pnpm 12.14.0 → 12.15.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 +189 -0
- package/dist/node_modules/.pnpm-workspace-state-v1.json +12 -14
- package/dist/node_modules/undici/lib/dispatcher/agent.js +6 -2
- package/dist/node_modules/undici/lib/dispatcher/client-h1.js +0 -2
- package/dist/node_modules/undici/lib/dispatcher/client-h2.js +0 -4
- package/dist/node_modules/undici/lib/dispatcher/client.js +3 -2
- package/dist/node_modules/undici/lib/dispatcher/dispatcher-base.js +9 -0
- package/dist/node_modules/undici/lib/web/eventsource/eventsource-stream.js +91 -10
- package/dist/node_modules/undici/lib/web/eventsource/eventsource.js +4 -1
- package/dist/node_modules/undici/lib/web/fetch/index.js +7 -9
- package/dist/node_modules/undici/lib/web/websocket/receiver.js +14 -0
- package/dist/node_modules/undici/package.json +1 -1
- package/package.json +16 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,194 @@
|
|
|
1
1
|
# pnpm
|
|
2
2
|
|
|
3
|
+
## 12.15.0
|
|
4
|
+
|
|
5
|
+
This release lets `pnpm-workspace.yaml` delegate settings to other files and config dependencies with `include`, adds `lockfile.integrityTarget: tar` and tar-registry support, accepts zstd-compressed registry responses, and honors `SOURCE_DATE_EPOCH` in `pnpm deploy` and `pnpm install`. It also rejects registry metadata that names a different package than the one requested and speeds up workspace installs and macOS file linking.
|
|
6
|
+
|
|
7
|
+
### Minor Changes
|
|
8
|
+
|
|
9
|
+
- `pnpm-workspace.yaml` can now delegate settings to other YAML files and to config dependencies through a new `include` field. Each source may define only the settings the root manifest delegates to it. Every setting has one owner. Commands that change settings write each one to the file that owns it. A relative path in an included file is relative to that file. See [Splitting Settings Across Files](https://pnpm.io/config-includes).
|
|
10
|
+
|
|
11
|
+
A config dependency supplies its settings through the YAML file named in the `pnpm.config` field of its `package.json`. The new `bootConfig` setting sets the release-age and trust policy pnpm uses to install config dependencies and the pnpm version from `packageManager`.
|
|
12
|
+
|
|
13
|
+
- Added the `lockfile.integrityTarget` setting. With `integrityTarget: tar`, `pnpm-lock.yaml` pins the hash of every package's uncompressed tar, so the lockfile no longer changes when a registry recompresses its tarballs or serves them as tars. pnpm learns a version's tar hash from the store or by downloading it once. `pnpm audit signatures` ties the tar hash to the signed `.tgz` integrity.
|
|
14
|
+
|
|
15
|
+
```yaml title="pnpm-workspace.yaml"
|
|
16
|
+
lockfile:
|
|
17
|
+
integrityTarget: tar
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- pnpm can now install from a registry that serves uncompressed tars with zstd or gzip content coding. `pnpm-lock.yaml` records the hash of each tar. Declare such a registry with `serverType: pnpr` in `registries` to keep tarball URLs out of the lockfile. See [Tar registries](https://pnpm.io/registries#tar-registries).
|
|
21
|
+
|
|
22
|
+
pnpm now decodes the content coding of tarball downloads itself. It rejects a zstd response whose window is larger than 8 MiB.
|
|
23
|
+
|
|
24
|
+
Added the `maxTarballUnpackedSize` setting. It caps the bytes the files of one package may unpack to, 4 GiB by default.
|
|
25
|
+
|
|
26
|
+
- Registry revisions now always pin the hash of the uncompressed tar, the original included. pnpm records an original selected through the revision protocol, for example with a `+r0` specifier, as `revision: 0` in the lockfile and fetches it from the registry's digest route. pnpm checks a revision's tar whether the registry sends it as a tar, gzip, or zstd. Revision metadata must list revision 0 and mark the selected revision. pnpm rejects incomplete revision metadata with `ERR_PNPM_MALFORMED_METADATA`.
|
|
27
|
+
|
|
28
|
+
- pnpm now accepts zstd-compressed responses. Registries that serve zstd send package metadata in fewer bytes than with gzip, most of all the full metadata that settings such as `minimumReleaseAge` and `trustPolicy` read.
|
|
29
|
+
|
|
30
|
+
- `pnpm deploy` and `pnpm install` now use the `SOURCE_DATE_EPOCH` environment variable as an upper bound for timestamps in `node_modules/.modules.yaml` and `node_modules/.pnpm-workspace-state-v1.json`. With an equivalently prepared deploy target and an epoch no later than the timestamps that would otherwise be recorded, deploying the same project twice produces identical files [#17171](https://github.com/pnpm/pnpm/issues/17171).
|
|
31
|
+
|
|
32
|
+
- `pnpm update --interactive` now highlights the row under the cursor in cyan while keeping the colors of its version changes.
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
#### Security
|
|
37
|
+
|
|
38
|
+
- pnpm now fails with `ERR_PNPM_REGISTRY_NAME_MISMATCH` if a registry returns metadata for a package other than the one requested. Before, pnpm installed the package under the name in the metadata, so `allowBuilds` rules for the requested package did not apply to it.
|
|
39
|
+
|
|
40
|
+
- `minimumReleaseAgeExcludePrune` and `trustPolicyExcludePrune` now keep exclusions for resolved configuration dependencies and their transitive dependencies [#17142](https://github.com/pnpm/pnpm/issues/17142).
|
|
41
|
+
|
|
42
|
+
#### Resolving dependencies
|
|
43
|
+
|
|
44
|
+
- When `pnpm install` resolves a dependency again, it now reuses a version that the install already resolved for the same package, as a fresh install does. Before, it kept the version the lockfile pinned and added a second copy of the package. A locked version still stays when nothing else in the install prefers another version. To pin a transitive dependency on purpose, use `overrides` [#17084](https://github.com/pnpm/pnpm/issues/17084).
|
|
45
|
+
|
|
46
|
+
- When another workspace project adds a different version of a package, a project's auto-installed peer on that package now moves to the version a fresh install picks. It used to stay on its locked version while any other project still accepted that version [#17058](https://github.com/pnpm/pnpm/issues/17058).
|
|
47
|
+
|
|
48
|
+
- A package that lists the same dependency in `dependencies` and as an optional peer now gets the peer bound when a project adds a provider for it later. The lockfile now records every peer a package declares, including one its own dependency supplies [#17056](https://github.com/pnpm/pnpm/issues/17056).
|
|
49
|
+
|
|
50
|
+
- `pnpm install --prod` and `pnpm deploy --prod` no longer drop a dependency that a package declares both as an optional peer and as its own dependency, when the package resolved it as its own dependency and a dev dependency provides the same package under another alias.
|
|
51
|
+
|
|
52
|
+
- `pnpm install` with an existing lockfile and a second `pnpm dedupe` now keep optional peer bindings that point into a dependency cycle or back into the dependent package. Before, the second run dropped the optional peer binding from the lockfile snapshot key.
|
|
53
|
+
|
|
54
|
+
- `pnpm install` and `pnpm dedupe` now preserve own-dependency and peer-dependency bindings when reusing a lockfile whose peer suffixes were hashed by `peersSuffixMaxLength`.
|
|
55
|
+
|
|
56
|
+
Upgrading a direct dependency in one workspace project now refreshes matching transitive pins in sibling projects during the same `pnpm install` pass.
|
|
57
|
+
|
|
58
|
+
- With `resolvePeersFromWorkspaceRoot: false`, a workspace project no longer binds a peer to a version that only another project's dependencies provide. pnpm now auto-installs a version that satisfies the peer range for that project. Before, the project got a version outside the range, with no peer warning.
|
|
59
|
+
|
|
60
|
+
- `pnpm install`, `pnpm add`, and `pnpm update` now reuse the lockfile's resolution for dependencies that take part in a dependency cycle, such as `mlly` and `pkg-types`. They re-resolved those packages and everything that depends on them from the registry before [pnpm/tasks#130](https://github.com/pnpm/tasks/issues/130).
|
|
61
|
+
|
|
62
|
+
- pnpm now measures a `pnpm.overrides` entry written as a `workspace:` path, such as `workspace:./packages/foo`, from the directory holding `pnpm-workspace.yaml`. It used to be measured from each package the override rewrote. The install then linked a directory that does not exist, or failed if workspace packages were injected [#17010](https://github.com/pnpm/pnpm/issues/17010).
|
|
63
|
+
|
|
64
|
+
- `pnpm install` now removes config dependencies that are no longer declared, including when the entire `configDependencies` setting is removed. Frozen installs reject a lockfile that still records removed config dependencies.
|
|
65
|
+
|
|
66
|
+
- `pnpm install --update-checksums` now re-resolves every locked package and records the integrity the registry reports now.
|
|
67
|
+
|
|
68
|
+
- `pnpm add` and `pnpm install` now accept `gist:<id>` dependencies and GitHub Gist URLs, and install them from the gist's archive. `pnpm pkg fix` normalizes a `gist:` repository the way `npm pkg fix` does, `pnpm licenses` links a gist package to its gist page, and `pnpm sbom` reports the repository of a package whose `repository` field is a `gist:` shorthand.
|
|
69
|
+
|
|
70
|
+
- `pnpm add` now reads the package name correctly from a tarball URL under a registry URL that has a path, such as `https://example.com/npm/`, and installs a registry tarball URL with a malformed percent escape as a plain tarball URL. Before, the registry path became part of the name, so a scoped package failed with `ERR_PNPM_INVALID_PACKAGE_NAME`.
|
|
71
|
+
|
|
72
|
+
- With `minimumReleaseAge` set, pnpm now revalidates a package's cached full metadata with a single ETag request so an unchanged registry answers with `304`, even when the registry does not list a publish time for every version. Previously, pnpm downloaded that metadata again whenever the cached copy was older than `minimumReleaseAge`. With `resolutionMode` set to `lowest-direct` or `time-based`, pnpm also reuses that metadata across every workspace project that depends on the package.
|
|
73
|
+
|
|
74
|
+
- When pnpm revalidates cached registry metadata, it now sends `If-Modified-Since` when the registry's modification time is a date without a time, and no longer crashes when the modification time is before 1970.
|
|
75
|
+
|
|
76
|
+
- Added protocol versioning between the pnpm client and pnpr server, verified applied resolution policies in resolution responses, and preserved structured error codes across the wire.
|
|
77
|
+
|
|
78
|
+
#### Performance
|
|
79
|
+
|
|
80
|
+
- Sped up `pnpm install` on macOS. On a 12-core Mac, installing 1352 packages from a warm store took 1.3 seconds, down from 2.2. pnpm now writes package files into `node_modules` with 4 threads on macOS, whatever the number of CPU cores. Set the `PNPM_MACOS_LINK_THREADS` environment variable to use a different number of threads, or to `0` to write files with the threads pnpm uses for the rest of the install.
|
|
81
|
+
|
|
82
|
+
- `pnpm install` is faster and uses less memory in large workspaces with `sharedWorkspaceLockfile: false`. Each project's install no longer reads the manifests of every other workspace project again. Installs that wait for their workspace dependencies now count toward `workspaceConcurrency` [#16966](https://github.com/pnpm/pnpm/issues/16966).
|
|
83
|
+
|
|
84
|
+
- Sped up workspace project discovery, dependency resolution, lockfile up-to-date checks, and peer dependency checks in large workspaces [#16839](https://github.com/pnpm/pnpm/issues/16839) [pnpm/tasks#9](https://github.com/pnpm/tasks/issues/9). On a workspace with 6,833 projects, the lockfile up-to-date check uses about 1 second less CPU time. The peer dependency check at the end of an install now checks linked workspace projects against the manifests already in memory, without reading each `package.json` again.
|
|
85
|
+
|
|
86
|
+
- Sped up bin linking and build planning during `pnpm install`. Package binary targets and shebang runtimes are now precalculated during tarball extraction and served in `pnpr` registry metadata alongside verified `requiresBuild`, tarball size, and file-count metadata.
|
|
87
|
+
|
|
88
|
+
- Lowered peak memory when pnpm downloads many packuments, such as during lockfile verification with `trustPolicy` on Linux. In one 1,800-entry workspace, a lockfile update now peaks at 1.8 to 2.0 GB. It peaked at 2.6 to 3.7 GB before [#17020](https://github.com/pnpm/pnpm/issues/17020).
|
|
89
|
+
|
|
90
|
+
- `pnpm install` in a Cargo workspace skips crates that an earlier install already unpacked into the store. Before, a repeat install processed every crate in `Cargo.lock` again. `pnpm install --force` unpacks every crate again [pnpm/tasks#69](https://github.com/pnpm/tasks/issues/69).
|
|
91
|
+
|
|
92
|
+
#### Linking node_modules and workspaces
|
|
93
|
+
|
|
94
|
+
- Concurrent installs that import the same package into one project now all complete. One of them used to fail with `ENOTEMPTY` when its copy of the package collided with the copy another install had already placed [pnpm/tasks#48](https://github.com/pnpm/tasks/issues/48).
|
|
95
|
+
|
|
96
|
+
- `pnpm install` now succeeds when `node_modules` is a symlink to an existing directory. Dependency hoisting previously rejected a symlinked `node_modules` as an invalid destination [#17157](https://github.com/pnpm/pnpm/issues/17157).
|
|
97
|
+
|
|
98
|
+
- A filtered first install under the isolated linker now links every workspace project by name into `node_modules/.pnpm/node_modules`, not only the selected ones [pnpm/tasks#155](https://github.com/pnpm/tasks/issues/155).
|
|
99
|
+
|
|
100
|
+
- With `nodeLinker: hoisted`, a filtered install such as `pnpm --filter app add <pkg>` keeps the root `node_modules` links and bins of the workspace projects it did not select. A project with no dependencies of its own lost them until the next full install.
|
|
101
|
+
|
|
102
|
+
- `pnpm install --filter` with `nodeLinker: hoisted` and `--prod` or `--dev` now preserves previously installed dependency groups for unselected workspace projects [pnpm/tasks#164](https://github.com/pnpm/tasks/issues/164). Repeat filtered installs also re-resolve when a transitive injected workspace dependency or a linked dependency's version changes.
|
|
103
|
+
|
|
104
|
+
- With `sharedWorkspaceLockfile: false`, `pnpm install` now runs the workspace root's pnpmfile for every project, after the project's own pnpmfile. Before, it ran only the pnpmfile in the project's directory.
|
|
105
|
+
|
|
106
|
+
- `pnpm install` and `pnpm peers check` now detect peer dependency issues and report linked package versions in linked projects that use `package.json5` or `package.yaml` [pnpm/tasks#172](https://github.com/pnpm/tasks/issues/172).
|
|
107
|
+
|
|
108
|
+
- `pnpm install` and `pnpm peers check` no longer report an unmet peer when a workspace package's peer is provided by a remote tarball URL. pnpm now compares the peer range with the version in the tarball's manifest [#17037](https://github.com/pnpm/pnpm/issues/17037).
|
|
109
|
+
|
|
110
|
+
- `pnpm prune` now removes extraneous direct packages from `node_modules` when `package.json` and `pnpm-lock.yaml` are already in sync. `pnpm prune` also honors `--filter`, `--recursive`, `sharedWorkspaceLockfile: false`, and the `trustLockfile` setting and `--trust-lockfile` flag. With the lockfile trusted, `pnpm prune --prod` works offline when every package is in the store [#16999](https://github.com/pnpm/pnpm/issues/16999).
|
|
111
|
+
|
|
112
|
+
#### Injected workspace packages
|
|
113
|
+
|
|
114
|
+
- `pnpm install --frozen-lockfile` now fails with `ERR_PNPM_OUTDATED_LOCKFILE` when a local directory dependency or injected workspace package changes a dependency to a different tarball URL or git commit [#17129](https://github.com/pnpm/pnpm/issues/17129). It also fails when an injected workspace package changes a dependency's named-registry range, such as `work:^2.0.0`, to one the locked version no longer satisfies.
|
|
115
|
+
|
|
116
|
+
- `pnpm install --frozen-lockfile` now accepts the lockfile it wrote for a `file:` dependency on a directory without a `package.json`. It also accepts the lockfile for an injected workspace package that bundles a dependency in `bundledDependencies`, lists a dependency in both `dependencies` and `optionalDependencies`, or publishes from a `publishConfig.directory` whose `package.json` lists other dependencies than the project's own. Before, these failed with `ERR_PNPM_OUTDATED_LOCKFILE`.
|
|
117
|
+
|
|
118
|
+
- The lockfile now records a `catalog:` peer dependency of an injected workspace package as written in its `package.json`. It recorded the catalog's range before, so editing the catalog changed the consumer's lockfile entry [pnpm/tasks#153](https://github.com/pnpm/tasks/issues/153).
|
|
119
|
+
|
|
120
|
+
- `pnpm install` now resolves a git, tarball, or tag dependency of an injected workspace package again when its spec changes. Before, it kept the old version.
|
|
121
|
+
|
|
122
|
+
- Repeat installs and `verifyDepsBeforeRun` now re-check projects that inject a modified workspace package, including when `sharedWorkspaceLockfile` is `false` [#17007](https://github.com/pnpm/pnpm/issues/17007), and accept aliased `workspace:` ranges on linked dependencies.
|
|
123
|
+
|
|
124
|
+
- An injected workspace package that was linked now becomes a copy when the project that depends on it starts providing one of its peers at a version the package's own workspace context lacks. The link used to stay until that dependency was updated, so the package could not find the peer the project provides [#17057](https://github.com/pnpm/pnpm/issues/17057).
|
|
125
|
+
|
|
126
|
+
- An injected workspace package now keeps only the files its manifest lists after a lifecycle script or a script named in `syncInjectedDepsAfterScripts` rebuilds it. Before, these refreshes copied every file of the package directory into the injected copy, including files that `files` or `.npmignore` leave out. With `deployAllFiles: true` the copy still gets every file.
|
|
127
|
+
|
|
128
|
+
- A script listed in `syncInjectedDepsAfterScripts` now keeps a package's links to its own files as links in the injected copies, the way `pnpm install` writes them, and updates those copies when `sharedWorkspaceLockfile` is `false`. Before, a directory link the script added became a real directory or went missing with `deployAllFiles`, and with `sharedWorkspaceLockfile: false` the copies kept the files from the last install.
|
|
129
|
+
|
|
130
|
+
#### Running scripts and checking dependencies
|
|
131
|
+
|
|
132
|
+
- Pressing Ctrl+C during `pnpm -r run` or `pnpm --filter <pkg> run` no longer reports `ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL`. pnpm now ends with the interrupted script's status, such as 130 for a script that SIGINT ended on Linux or macOS [#16964](https://github.com/pnpm/pnpm/issues/16964).
|
|
133
|
+
|
|
134
|
+
- With `enableGlobalVirtualStore`, a Node.js resolve hook that a script registers now sees the importer as `context.parentURL` after pnpm's `NODE_PATH` fallback fails to find a bare specifier. Hooks that resolve tsconfig `paths` aliases from the importer failed with `ERR_MODULE_NOT_FOUND` [#16979](https://github.com/pnpm/pnpm/issues/16979).
|
|
135
|
+
|
|
136
|
+
- `pnpm run`, `pnpm exec`, and `pnpm restart` now pass the install flags they were given, such as `--lockfile-dir`, `--modules-dir`, `--store-dir`, `--offline`, and `--ignore-scripts`, to the `pnpm install` that `verifyDepsBeforeRun: install` starts. When that install leaves the dependencies out of date, pnpm now prints a warning.
|
|
137
|
+
|
|
138
|
+
- A repeated `pnpm install --filter` now finishes without reinstalling when the selected projects are up to date, and so does `pnpm install` once filtered commands have installed every project. After a filtered install, `pnpm run` and `pnpm exec` now install a selected project whose dependencies changed in between [#17011](https://github.com/pnpm/pnpm/issues/17011), require the workspace root when a filtered install with `pruneDirectDependencies: true` skipped it [pnpm/tasks#181](https://github.com/pnpm/tasks/issues/181), and preserve the recorded ignored build scripts of unselected projects.
|
|
139
|
+
|
|
140
|
+
- The check that runs before a script now holds the workspace projects that the selected project depends on, including with `sharedWorkspaceLockfile: false` and for workspace dependencies declared with `link:`. `--filter=<project>...` now selects `link:` dependencies as well. In a workspace that has no `package.json` at its root, `pnpm run` and `pnpm exec` no longer reinstall before every command when the dependencies are up to date.
|
|
141
|
+
|
|
142
|
+
- `verifyDepsBeforeRun` and repeat `pnpm install` runs now apply `overrides` before comparing a modified `package.json` with `pnpm-lock.yaml` [#17127](https://github.com/pnpm/pnpm/issues/17127). `pnpm install` with `optimisticRepeatInstall` now re-runs when `configDependencies` changed, `verifyDepsBeforeRun` reports `node_modules` out of date when `useGitBranchLockfile` is enabled and the branch lockfile is missing, and in a single-project repository both checks record a successful lockfile check so later runs skip it until the lockfile changes [#16993](https://github.com/pnpm/pnpm/issues/16993).
|
|
143
|
+
|
|
144
|
+
- `pnpm install` and `verifyDepsBeforeRun` now compare manifests, lockfiles, and patch files by content hash. They notice an edit that kept the file's modification time, as `cp -p`, `rsync -a`, or an archive extraction leave it, and a patch file that was copied or touched without changing its content no longer marks dependencies as outdated [#16988](https://github.com/pnpm/pnpm/issues/16988).
|
|
145
|
+
|
|
146
|
+
#### Deploying, packing, and other commands
|
|
147
|
+
|
|
148
|
+
- `pnpm deploy` now deploys `file:` dependencies that live outside the workspace directory. It failed with "Refusing to deploy unsafe lockfile path" [#16983](https://github.com/pnpm/pnpm/issues/16983).
|
|
149
|
+
|
|
150
|
+
- `pnpm deploy` now records the peer dependencies of the workspace packages it copies into the deployed lockfile. Before, pnpm rejected that lockfile with `--frozen-lockfile` when a deployed workspace package declared a peer and `injectWorkspacePackages` was off [#17093](https://github.com/pnpm/pnpm/issues/17093).
|
|
151
|
+
|
|
152
|
+
- `pnpm install --frozen-lockfile` and `verifyDepsBeforeRun` now succeed inside a directory created by `pnpm deploy`. `pnpm deploy` writes self-contained workspace and lockfile settings, prunes unused `patchedDependencies`, and preserves workspace-level dependency resolutions on deployed directory dependencies.
|
|
153
|
+
|
|
154
|
+
- On macOS, `pnpm deploy` no longer writes a second, unused copy of every package to `node_modules/.pnpm/@` in the deploy directory [#17116](https://github.com/pnpm/pnpm/issues/17116).
|
|
155
|
+
|
|
156
|
+
- `pnpm patch-commit` and `pnpm install` now apply a patch that renames or moves a file of the patched package. Such a patch failed with "rename/copy operations in patches are not yet supported".
|
|
157
|
+
|
|
158
|
+
- In workspaces with `sharedWorkspaceLockfile: false`, `pnpm patch`, `pnpm patch-commit`, and `pnpm patch-remove` now resolve patch files and `patchedDependencies` entries at the workspace root. `pnpm install` checks configured patches across all workspace projects before reporting unused patches.
|
|
159
|
+
|
|
160
|
+
- `pnpm pack` and `pnpm publish` no longer include editor swap files such as `.*.swp`, macOS `._*` files, `.lock-wscript`, `.wafpickle-*`, `build/config.gypi`, or `archived-packages/` in the tarball. Naming one of these files in `files` still includes it [#16955](https://github.com/pnpm/pnpm/issues/16955).
|
|
161
|
+
|
|
162
|
+
- `pnpm pack` and `pnpm publish` now support extended glob patterns and brace ranges such as `part{1..3}.js` and `part{a..c}.js`, including descending ranges, steps, and zero padding, in `files`, `.npmignore`, and `.gitignore`. Explicitly listed internal directory symlinks are preserved below excluded directories, entry points honor nested ignore files, ignore negations can re-include default-excluded logs and backup files, and nested lockfiles follow the package's file selection rules.
|
|
163
|
+
|
|
164
|
+
- Trusted publishing with OIDC now works with registries whose URL has a path, such as `https://registry.example/npm/private/`. pnpm previously sent the token exchange request to the root of the registry host.
|
|
165
|
+
|
|
166
|
+
- `pnpm deprecate` and `pnpm unpublish` now work for packages whose registry metadata is larger than 10 MiB, such as `typescript`. They accept up to 50 MiB, the same limit as `pnpm owner`.
|
|
167
|
+
|
|
168
|
+
- `pnpm owner ls` now lists the owners of a package on the npm registry. It reported every package as not found.
|
|
169
|
+
|
|
170
|
+
- `pnpm licenses list --prod` now includes optional dependencies, including transitive ones. Pass `--no-optional` to leave them out. `pnpm licenses list` also reports no licenses for workspace projects not recorded in the lockfile, errors when `pnpm-lock.yaml` is missing, and formats empty results as the requested text or JSON [pnpm/tasks#174](https://github.com/pnpm/tasks/issues/174).
|
|
171
|
+
|
|
172
|
+
- `pnpm why` now reads each selected workspace project's lockfile and modules directory when `sharedWorkspaceLockfile` is `false` [pnpm/tasks#169](https://github.com/pnpm/tasks/issues/169).
|
|
173
|
+
|
|
174
|
+
- `pnpm pkg fix` now converts a `repository` string such as `github:pnpm/example` to the object form, as `npm pkg fix` does. It also normalizes a GitHub, GitLab, or Bitbucket repository URL the way `npm pkg fix` does [#16980](https://github.com/pnpm/pnpm/issues/16980).
|
|
175
|
+
|
|
176
|
+
#### Configuration and setting up pnpm
|
|
177
|
+
|
|
178
|
+
- Registry credentials and TLS settings passed with `--config`, such as `--config.//registry.example.com/:_authToken=<token>`, are now applied. They take precedence over the same settings in `.npmrc` files.
|
|
179
|
+
|
|
180
|
+
- pnpm now reads only the settings it needs to decide whether to switch to the pnpm version a project pins. A setting outside them can no longer fail that decision or change its outcome. For example, `pnpm --version` no longer fails in a pinned project whose `pnpm-workspace.yaml` has a setting this pnpm cannot read. Projects that pin neither pnpm nor a runtime skip this check without reading any settings.
|
|
181
|
+
|
|
182
|
+
- When pnpm switches to the version a project pins, an option only the pinned pnpm knows now reaches that pnpm when it comes immediately before the command name, as in `pnpm --new-flag install`. When switching for `pnpm --version` or an unrecognized command line, pnpm also honors `--color`, `--no-color`, `--reporter`, and `--loglevel` wherever they appear on the command line.
|
|
183
|
+
|
|
184
|
+
- `pnpm self-update` now updates the project's pnpm pin when Homebrew installed pnpm or Corepack runs it, leaving the global pnpm alone [#17001](https://github.com/pnpm/pnpm/issues/17001). Run inside a workspace package, `pnpm self-update` updates the pin in the workspace root's manifest, including in `package.yaml` or `package.json5`.
|
|
185
|
+
|
|
186
|
+
- `pnpm setup` now completes when Windows registry environment values cannot be read or written. It prints PowerShell commands to configure `PNPM_HOME` and `PATH` without registry access [#17167](https://github.com/pnpm/pnpm/issues/17167).
|
|
187
|
+
|
|
188
|
+
- `pnpm setup` now succeeds when it cannot tell which shell configuration file to update, for example in a container where `SHELL` is not set. It installs pnpm and prints the `PNPM_HOME` and `PATH` lines to add yourself. Previously it failed with `ERR_PNPM_UNKNOWN_SHELL`, and so did the installer script at get.pnpm.io [#16973](https://github.com/pnpm/pnpm/issues/16973).
|
|
189
|
+
|
|
190
|
+
- In StackBlitz WebContainers, `pnpm` no longer crashes intermittently with `Parking not supported on this platform`, and running a command that does not exist now reports that the command was not found. Previously it sometimes failed with a generic error.
|
|
191
|
+
|
|
3
192
|
## 12.14.0
|
|
4
193
|
|
|
5
194
|
This release adds the `sideEffectsCache.exclude` setting, stops `pnpm install` from waiting on the daily update check, and keeps pnpm from aborting with a core dump when the reader of its output exits.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"lastValidatedTimestamp":
|
|
2
|
+
"lastValidatedTimestamp": 1792577863758,
|
|
3
3
|
"projects": {
|
|
4
4
|
"/home/runner/_work/pnpm/pnpm/pnpm/npm/pnpm/temp-deploy": {
|
|
5
5
|
"name": "pacquet",
|
|
6
|
-
"version": "12.
|
|
6
|
+
"version": "12.15.0",
|
|
7
7
|
"hasModulesDir": true
|
|
8
8
|
}
|
|
9
9
|
},
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
"default": {
|
|
25
25
|
"@babel/core": "^7.29.7",
|
|
26
26
|
"@babel/plugin-transform-explicit-resource-management": "^7.29.7",
|
|
27
|
-
"@commitlint/cli": "^21.2.
|
|
28
|
-
"@commitlint/config-conventional": "^21.2.
|
|
29
|
-
"@commitlint/prompt-cli": "^21.2.
|
|
27
|
+
"@commitlint/cli": "^21.2.4",
|
|
28
|
+
"@commitlint/config-conventional": "^21.2.4",
|
|
29
|
+
"@commitlint/prompt-cli": "^21.2.4",
|
|
30
30
|
"@cyclonedx/cyclonedx-library": "10.3.0",
|
|
31
31
|
"@emnapi/wasi-threads": "2.2.0",
|
|
32
32
|
"@eslint/js": "^10.0.1",
|
|
33
|
-
"@inquirer/prompts": "^8.7.
|
|
33
|
+
"@inquirer/prompts": "^8.7.4",
|
|
34
34
|
"@jest/globals": "30.4.1",
|
|
35
35
|
"@npm/types": "^2.1.0",
|
|
36
36
|
"@openpgp/web-stream-tools": "0.3.1",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"@types/yarnpkg__lockfile": "^1.1.9",
|
|
89
89
|
"@types/yauzl": "^3.4.0",
|
|
90
90
|
"@types/zkochan__table": "npm:@types/table@6.3.2",
|
|
91
|
-
"@typescript-eslint/utils": "^8.
|
|
91
|
+
"@typescript-eslint/utils": "^8.72.0",
|
|
92
92
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
93
93
|
"@webcontainer/api": "^1.6.4",
|
|
94
94
|
"@yarnpkg/core": "4.9.1",
|
|
@@ -135,10 +135,10 @@
|
|
|
135
135
|
"enhanced-resolve": "5.26.0",
|
|
136
136
|
"esbuild": "^0.28.2",
|
|
137
137
|
"escape-string-regexp": "^5.0.0",
|
|
138
|
-
"eslint": "^10.
|
|
138
|
+
"eslint": "^10.13.0",
|
|
139
139
|
"eslint-plugin-import-x": "^4.17.1",
|
|
140
140
|
"eslint-plugin-jest": "^29.17.0",
|
|
141
|
-
"eslint-plugin-n": "^18.
|
|
141
|
+
"eslint-plugin-n": "^18.5.0",
|
|
142
142
|
"eslint-plugin-promise": "^7.3.0",
|
|
143
143
|
"eslint-plugin-regexp": "^3.1.1",
|
|
144
144
|
"eslint-plugin-simple-import-sort": "^14.0.0",
|
|
@@ -183,7 +183,7 @@
|
|
|
183
183
|
"memfs": "^4.80.0",
|
|
184
184
|
"memoize": "^11.0.0",
|
|
185
185
|
"micromatch": "^4.0.8",
|
|
186
|
-
"msgpackr": "2.1.
|
|
186
|
+
"msgpackr": "2.1.2",
|
|
187
187
|
"nm-prune": "^5.0.0",
|
|
188
188
|
"node-gyp": "^12.3.0",
|
|
189
189
|
"normalize-newline": "5.0.0",
|
|
@@ -201,13 +201,12 @@
|
|
|
201
201
|
"p-limit": "^7.3.3",
|
|
202
202
|
"p-queue": "^10.0.0",
|
|
203
203
|
"parse-json": "^8.3.0",
|
|
204
|
-
"parse-npm-tarball-url": "^5.0.0",
|
|
205
204
|
"path-absolute": "^2.0.0",
|
|
206
205
|
"path-exists": "^5.0.0",
|
|
207
206
|
"path-name": "^1.0.0",
|
|
208
207
|
"path-temp": "^3.0.0",
|
|
209
208
|
"pidtree": "^1.0.0",
|
|
210
|
-
"playwright-core": "^1.64.
|
|
209
|
+
"playwright-core": "^1.64.1",
|
|
211
210
|
"preferred-pm": "^5.0.1",
|
|
212
211
|
"pretty-bytes": "^7.2.0",
|
|
213
212
|
"pretty-ms": "^10.0.0",
|
|
@@ -261,7 +260,7 @@
|
|
|
261
260
|
"tree-kill": "^1.2.2",
|
|
262
261
|
"ts-jest-resolver": "2.0.1",
|
|
263
262
|
"typescript": "6.0.3",
|
|
264
|
-
"typescript-eslint": "^8.
|
|
263
|
+
"typescript-eslint": "^8.72.0",
|
|
265
264
|
"uid-number": "0.0.6",
|
|
266
265
|
"undici": "^7.29.1",
|
|
267
266
|
"unified": "^11.0.5",
|
|
@@ -310,7 +309,6 @@
|
|
|
310
309
|
"is-subdir",
|
|
311
310
|
"make-empty-dir",
|
|
312
311
|
"normalize-registry-url",
|
|
313
|
-
"parse-npm-tarball-url@5.0.0",
|
|
314
312
|
"path-absolute",
|
|
315
313
|
"path-temp",
|
|
316
314
|
"pnpm",
|
|
@@ -23,7 +23,7 @@ function defaultFactory (origin, opts) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
class Agent extends DispatcherBase {
|
|
26
|
-
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
|
|
26
|
+
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, tls, ...options } = {}) {
|
|
27
27
|
if (typeof factory !== 'function') {
|
|
28
28
|
throw new InvalidArgumentError('factory must be a function.')
|
|
29
29
|
}
|
|
@@ -42,11 +42,15 @@ class Agent extends DispatcherBase {
|
|
|
42
42
|
connect = { ...connect }
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
if (tls && typeof tls !== 'function') {
|
|
46
|
+
tls = { ...tls }
|
|
47
|
+
}
|
|
48
|
+
|
|
45
49
|
this[kInterceptors] = options.interceptors?.Agent && Array.isArray(options.interceptors.Agent)
|
|
46
50
|
? options.interceptors.Agent
|
|
47
51
|
: [createRedirectInterceptor({ maxRedirections })]
|
|
48
52
|
|
|
49
|
-
this[kOptions] = { ...util.deepClone(options), connect }
|
|
53
|
+
this[kOptions] = { ...util.deepClone(options), connect, tls }
|
|
50
54
|
this[kOptions].interceptors = options.interceptors
|
|
51
55
|
? { ...options.interceptors }
|
|
52
56
|
: undefined
|
|
@@ -727,8 +727,6 @@ async function connectH1 (client, socket) {
|
|
|
727
727
|
socket[kParser] = new Parser(client, socket, llhttpInstance)
|
|
728
728
|
|
|
729
729
|
addListener(socket, 'error', function (err) {
|
|
730
|
-
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
|
|
731
|
-
|
|
732
730
|
const parser = this[kParser]
|
|
733
731
|
|
|
734
732
|
// On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
|
|
@@ -136,8 +136,6 @@ async function connectH2 (client, socket) {
|
|
|
136
136
|
socket[kHTTP2Session] = session
|
|
137
137
|
|
|
138
138
|
util.addListener(socket, 'error', function (err) {
|
|
139
|
-
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
|
|
140
|
-
|
|
141
139
|
this[kError] = err
|
|
142
140
|
|
|
143
141
|
this[kClient][kOnError](err)
|
|
@@ -211,8 +209,6 @@ function resumeH2 (client) {
|
|
|
211
209
|
}
|
|
212
210
|
|
|
213
211
|
function onHttp2SessionError (err) {
|
|
214
|
-
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
|
|
215
|
-
|
|
216
212
|
this[kSocket][kError] = err
|
|
217
213
|
this[kClient][kOnError](err)
|
|
218
214
|
}
|
|
@@ -107,9 +107,10 @@ class Client extends DispatcherBase {
|
|
|
107
107
|
// h2
|
|
108
108
|
maxConcurrentStreams,
|
|
109
109
|
allowH2,
|
|
110
|
-
webSocket
|
|
110
|
+
webSocket,
|
|
111
|
+
eventSource
|
|
111
112
|
} = {}) {
|
|
112
|
-
super({ webSocket })
|
|
113
|
+
super({ webSocket, eventSource })
|
|
113
114
|
|
|
114
115
|
if (keepAlive !== undefined) {
|
|
115
116
|
throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const buffer = require('node:buffer')
|
|
3
4
|
const Dispatcher = require('./dispatcher')
|
|
4
5
|
const {
|
|
5
6
|
ClientDestroyedError,
|
|
@@ -12,6 +13,7 @@ const kOnDestroyed = Symbol('onDestroyed')
|
|
|
12
13
|
const kOnClosed = Symbol('onClosed')
|
|
13
14
|
const kInterceptedDispatch = Symbol('Intercepted Dispatch')
|
|
14
15
|
const kWebSocketOptions = Symbol('webSocketOptions')
|
|
16
|
+
const kEventSourceOptions = Symbol('eventSourceOptions')
|
|
15
17
|
|
|
16
18
|
class DispatcherBase extends Dispatcher {
|
|
17
19
|
constructor (opts) {
|
|
@@ -22,6 +24,7 @@ class DispatcherBase extends Dispatcher {
|
|
|
22
24
|
this[kClosed] = false
|
|
23
25
|
this[kOnClosed] = []
|
|
24
26
|
this[kWebSocketOptions] = opts?.webSocket ?? {}
|
|
27
|
+
this[kEventSourceOptions] = opts?.eventSource ?? {}
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
get webSocketOptions () {
|
|
@@ -31,6 +34,12 @@ class DispatcherBase extends Dispatcher {
|
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
get eventSourceOptions () {
|
|
38
|
+
return {
|
|
39
|
+
maxEventSize: this[kEventSourceOptions].maxEventSize ?? buffer.kStringMaxLength
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
get destroyed () {
|
|
35
44
|
return this[kDestroyed]
|
|
36
45
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
+
const buffer = require('node:buffer')
|
|
2
3
|
const { Transform } = require('node:stream')
|
|
3
4
|
const { isASCIINumber, isValidLastEventId } = require('./util')
|
|
4
5
|
|
|
@@ -23,11 +24,19 @@ const COLON = 0x3A
|
|
|
23
24
|
*/
|
|
24
25
|
const SPACE = 0x20
|
|
25
26
|
|
|
27
|
+
const defaultMaxEventSize = buffer.kStringMaxLength
|
|
28
|
+
|
|
26
29
|
const DATA = Buffer.from('data')
|
|
27
30
|
const EVENT = Buffer.from('event')
|
|
28
31
|
const ID = Buffer.from('id')
|
|
29
32
|
const RETRY = Buffer.from('retry')
|
|
30
33
|
|
|
34
|
+
// Each data field creates a string node, and each unconsumed input chunk keeps
|
|
35
|
+
// a Buffer wrapper. The absolute ceiling protects the large default byte limit.
|
|
36
|
+
const minParserItems = 1024
|
|
37
|
+
const maxParserItems = 131072
|
|
38
|
+
const bytesPerParserItem = 128
|
|
39
|
+
|
|
31
40
|
function isASCIINumberBytes (buffer, start) {
|
|
32
41
|
if (start >= buffer.length) {
|
|
33
42
|
return false
|
|
@@ -66,6 +75,12 @@ function isFieldName (line, length, field) {
|
|
|
66
75
|
return true
|
|
67
76
|
}
|
|
68
77
|
|
|
78
|
+
function createMaxEventSizeExceededError () {
|
|
79
|
+
const error = new Error('EventSource message size exceeded')
|
|
80
|
+
error.aborted = false
|
|
81
|
+
return error
|
|
82
|
+
}
|
|
83
|
+
|
|
69
84
|
/**
|
|
70
85
|
* @typedef {object} EventSourceStreamEvent
|
|
71
86
|
* @type {object}
|
|
@@ -114,6 +129,13 @@ class EventSourceStream extends Transform {
|
|
|
114
129
|
pos = 0
|
|
115
130
|
lineChunkIndex = 0
|
|
116
131
|
linePos = 0
|
|
132
|
+
lineLength = 0
|
|
133
|
+
eventSize = 0
|
|
134
|
+
eventFieldSizes = { data: 0, event: 0, id: 0, retry: 0 }
|
|
135
|
+
dataLineCount = 0
|
|
136
|
+
maxEventSize
|
|
137
|
+
maxDataLines
|
|
138
|
+
maxBufferedChunks
|
|
117
139
|
|
|
118
140
|
event = {
|
|
119
141
|
data: undefined,
|
|
@@ -124,6 +146,7 @@ class EventSourceStream extends Transform {
|
|
|
124
146
|
|
|
125
147
|
/**
|
|
126
148
|
* @param {object} options
|
|
149
|
+
* @param {number} [options.maxEventSize]
|
|
127
150
|
* @param {eventSourceSettings} options.eventSourceSettings
|
|
128
151
|
* @param {Function} [options.push]
|
|
129
152
|
*/
|
|
@@ -135,6 +158,12 @@ class EventSourceStream extends Transform {
|
|
|
135
158
|
super(options)
|
|
136
159
|
|
|
137
160
|
this.state = options.eventSourceSettings || {}
|
|
161
|
+
this.maxEventSize = options.maxEventSize ?? defaultMaxEventSize
|
|
162
|
+
const parserItemLimit = this.maxEventSize > 0
|
|
163
|
+
? Math.min(maxParserItems, Math.max(minParserItems, Math.floor(this.maxEventSize / bytesPerParserItem)))
|
|
164
|
+
: 0
|
|
165
|
+
this.maxDataLines = parserItemLimit
|
|
166
|
+
this.maxBufferedChunks = parserItemLimit
|
|
138
167
|
if (options.push) {
|
|
139
168
|
this.push = options.push
|
|
140
169
|
}
|
|
@@ -152,6 +181,14 @@ class EventSourceStream extends Transform {
|
|
|
152
181
|
return
|
|
153
182
|
}
|
|
154
183
|
|
|
184
|
+
// A leading line ending can immediately release the currently buffered
|
|
185
|
+
// line, even when it uses the final allowed chunk slot.
|
|
186
|
+
if (this.maxEventSize > 0 &&
|
|
187
|
+
this.chunks.length >= this.maxBufferedChunks &&
|
|
188
|
+
chunk[0] !== LF && chunk[0] !== CR) {
|
|
189
|
+
callback(createMaxEventSizeExceededError())
|
|
190
|
+
return
|
|
191
|
+
}
|
|
155
192
|
this.chunks.push(chunk)
|
|
156
193
|
|
|
157
194
|
// Strip leading byte-order-mark if we opened the stream and started
|
|
@@ -230,7 +267,12 @@ class EventSourceStream extends Transform {
|
|
|
230
267
|
|
|
231
268
|
// In any case, we can process the line as we reached an
|
|
232
269
|
// end-of-line character
|
|
233
|
-
|
|
270
|
+
try {
|
|
271
|
+
this.parseLine(this.readLine(), this.event)
|
|
272
|
+
} catch (error) {
|
|
273
|
+
callback(error)
|
|
274
|
+
return
|
|
275
|
+
}
|
|
234
276
|
this.consumeCurrentByte()
|
|
235
277
|
// A line was processed and this could be the end of the event. We need
|
|
236
278
|
// to check if the next line is empty to determine if the event is
|
|
@@ -239,15 +281,28 @@ class EventSourceStream extends Transform {
|
|
|
239
281
|
continue
|
|
240
282
|
}
|
|
241
283
|
|
|
284
|
+
// readLine() is only called after CR or LF. Reject an unfinished line
|
|
285
|
+
// before it can retain an unbounded number of decoded chunks. The seven
|
|
286
|
+
// extra bytes allow the longest field prefix ("event: " or "retry: ").
|
|
287
|
+
if (this.maxEventSize > 0 && ++this.lineLength > this.maxEventSize + 7) {
|
|
288
|
+
callback(createMaxEventSizeExceededError())
|
|
289
|
+
return
|
|
290
|
+
}
|
|
242
291
|
this.advanceCursor()
|
|
243
292
|
}
|
|
244
293
|
|
|
245
294
|
callback()
|
|
246
295
|
}
|
|
247
296
|
|
|
297
|
+
_destroy (error, callback) {
|
|
298
|
+
this.chunks.length = 0
|
|
299
|
+
this.clearEvent()
|
|
300
|
+
super._destroy(error, callback)
|
|
301
|
+
}
|
|
302
|
+
|
|
248
303
|
/**
|
|
249
304
|
* @param {Buffer} line
|
|
250
|
-
* @param {
|
|
305
|
+
* @param {EventSourceStreamEvent} event
|
|
251
306
|
*/
|
|
252
307
|
parseLine (line, event) {
|
|
253
308
|
// If the line is empty (a blank line)
|
|
@@ -281,6 +336,13 @@ class EventSourceStream extends Transform {
|
|
|
281
336
|
}
|
|
282
337
|
|
|
283
338
|
if (isFieldName(line, fieldLength, DATA)) {
|
|
339
|
+
if (this.maxEventSize > 0 && this.dataLineCount >= this.maxDataLines) {
|
|
340
|
+
throw createMaxEventSizeExceededError()
|
|
341
|
+
}
|
|
342
|
+
const valueBytes = line.length - valueStart
|
|
343
|
+
const dataSize = this.eventFieldSizes.data + (event.data === undefined ? 0 : 1) + valueBytes
|
|
344
|
+
this.setEventFieldSize('data', dataSize)
|
|
345
|
+
|
|
284
346
|
const value = line.toString('utf8', valueStart)
|
|
285
347
|
|
|
286
348
|
if (event.data === undefined) {
|
|
@@ -288,11 +350,13 @@ class EventSourceStream extends Transform {
|
|
|
288
350
|
} else {
|
|
289
351
|
event.data += `\n${value}`
|
|
290
352
|
}
|
|
353
|
+
this.dataLineCount++
|
|
291
354
|
return
|
|
292
355
|
}
|
|
293
356
|
|
|
294
357
|
if (isFieldName(line, fieldLength, RETRY)) {
|
|
295
358
|
if (isASCIINumberBytes(line, valueStart)) {
|
|
359
|
+
this.setEventFieldSize('retry', line.length - valueStart)
|
|
296
360
|
event.retry = line.toString('utf8', valueStart)
|
|
297
361
|
}
|
|
298
362
|
return
|
|
@@ -300,20 +364,29 @@ class EventSourceStream extends Transform {
|
|
|
300
364
|
|
|
301
365
|
if (isFieldName(line, fieldLength, ID)) {
|
|
302
366
|
if (isValidLastEventIdBytes(line, valueStart)) {
|
|
367
|
+
this.setEventFieldSize('id', line.length - valueStart)
|
|
303
368
|
event.id = line.toString('utf8', valueStart)
|
|
304
369
|
}
|
|
305
370
|
return
|
|
306
371
|
}
|
|
307
372
|
|
|
308
373
|
if (isFieldName(line, fieldLength, EVENT)) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
event.event = value
|
|
374
|
+
if (valueStart < line.length) {
|
|
375
|
+
this.setEventFieldSize('event', line.length - valueStart)
|
|
376
|
+
event.event = line.toString('utf8', valueStart)
|
|
313
377
|
}
|
|
314
378
|
}
|
|
315
379
|
}
|
|
316
380
|
|
|
381
|
+
setEventFieldSize (field, size) {
|
|
382
|
+
const eventSize = this.eventSize - this.eventFieldSizes[field] + size
|
|
383
|
+
if (this.maxEventSize > 0 && eventSize > this.maxEventSize) {
|
|
384
|
+
throw createMaxEventSizeExceededError()
|
|
385
|
+
}
|
|
386
|
+
this.eventSize = eventSize
|
|
387
|
+
this.eventFieldSizes[field] = size
|
|
388
|
+
}
|
|
389
|
+
|
|
317
390
|
/**
|
|
318
391
|
* @param {EventSourceStreamEvent} event
|
|
319
392
|
*/
|
|
@@ -344,6 +417,12 @@ class EventSourceStream extends Transform {
|
|
|
344
417
|
this.event.event = undefined
|
|
345
418
|
this.event.id = undefined
|
|
346
419
|
this.event.retry = undefined
|
|
420
|
+
this.eventSize = 0
|
|
421
|
+
this.dataLineCount = 0
|
|
422
|
+
this.eventFieldSizes.data = 0
|
|
423
|
+
this.eventFieldSizes.event = 0
|
|
424
|
+
this.eventFieldSizes.id = 0
|
|
425
|
+
this.eventFieldSizes.retry = 0
|
|
347
426
|
}
|
|
348
427
|
|
|
349
428
|
hasPendingEvent () {
|
|
@@ -379,14 +458,16 @@ class EventSourceStream extends Transform {
|
|
|
379
458
|
syncLineStartToCursor () {
|
|
380
459
|
this.lineChunkIndex = this.chunkIndex
|
|
381
460
|
this.linePos = this.pos
|
|
461
|
+
this.lineLength = 0
|
|
382
462
|
this.dropConsumedChunks()
|
|
383
463
|
}
|
|
384
464
|
|
|
385
465
|
dropConsumedChunks () {
|
|
386
|
-
|
|
387
|
-
this.
|
|
388
|
-
this.
|
|
389
|
-
this.
|
|
466
|
+
if (this.lineChunkIndex > 0) {
|
|
467
|
+
const consumed = this.lineChunkIndex
|
|
468
|
+
this.chunks.splice(0, consumed)
|
|
469
|
+
this.lineChunkIndex = 0
|
|
470
|
+
this.chunkIndex -= consumed
|
|
390
471
|
}
|
|
391
472
|
|
|
392
473
|
if (this.chunkIndex === this.chunks.length) {
|
|
@@ -11,6 +11,7 @@ const { isNetworkError } = require('../fetch/response')
|
|
|
11
11
|
const { delay } = require('./util')
|
|
12
12
|
const { kEnumerableProperty } = require('../../core/util')
|
|
13
13
|
const { environmentSettingsObject } = require('../fetch/util')
|
|
14
|
+
const { getGlobalDispatcher } = require('../../global')
|
|
14
15
|
|
|
15
16
|
let experimentalWarned = false
|
|
16
17
|
|
|
@@ -292,6 +293,7 @@ class EventSource extends EventTarget {
|
|
|
292
293
|
|
|
293
294
|
const eventSourceStream = new EventSourceStream({
|
|
294
295
|
eventSourceSettings: this.#state,
|
|
296
|
+
maxEventSize: this.#dispatcher.eventSourceOptions?.maxEventSize,
|
|
295
297
|
push: (event) => {
|
|
296
298
|
this.dispatchEvent(createFastMessageEvent(
|
|
297
299
|
event.type,
|
|
@@ -470,7 +472,8 @@ webidl.converters.EventSourceInitDict = webidl.dictionaryConverter([
|
|
|
470
472
|
},
|
|
471
473
|
{
|
|
472
474
|
key: 'dispatcher', // undici only
|
|
473
|
-
converter: webidl.converters.any
|
|
475
|
+
converter: webidl.converters.any,
|
|
476
|
+
defaultValue: () => getGlobalDispatcher()
|
|
474
477
|
}
|
|
475
478
|
])
|
|
476
479
|
|
|
@@ -1950,8 +1950,10 @@ async function httpNetworkFetch (
|
|
|
1950
1950
|
// 19. Run these steps in parallel:
|
|
1951
1951
|
|
|
1952
1952
|
// 1. Run these steps, but abort when fetchParams is canceled:
|
|
1953
|
-
fetchParams.controller.
|
|
1954
|
-
|
|
1953
|
+
if (!fetchParams.controller.resume) {
|
|
1954
|
+
fetchParams.controller.on('terminated', onAborted)
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1955
1957
|
fetchParams.controller.resume = async () => {
|
|
1956
1958
|
// 1. While true
|
|
1957
1959
|
while (true) {
|
|
@@ -2120,8 +2122,6 @@ async function httpNetworkFetch (
|
|
|
2120
2122
|
}
|
|
2121
2123
|
location = headersList.get('location', true)
|
|
2122
2124
|
|
|
2123
|
-
this.body = new Readable({ read: resume })
|
|
2124
|
-
|
|
2125
2125
|
const decoders = []
|
|
2126
2126
|
|
|
2127
2127
|
const willFollow = location && request.redirect === 'follow' &&
|
|
@@ -2139,7 +2139,7 @@ async function httpNetworkFetch (
|
|
|
2139
2139
|
// CVE fix similar to urllib3 (GHSA-gm62-xv2j-4w53) and curl (CVE-2022-32206).
|
|
2140
2140
|
const maxContentEncodings = 5
|
|
2141
2141
|
if (codings.length > maxContentEncodings) {
|
|
2142
|
-
|
|
2142
|
+
this.abort(new Error(`too many content-encodings in response: ${codings.length}, maximum allowed is ${maxContentEncodings}`))
|
|
2143
2143
|
return true
|
|
2144
2144
|
}
|
|
2145
2145
|
|
|
@@ -2172,6 +2172,8 @@ async function httpNetworkFetch (
|
|
|
2172
2172
|
}
|
|
2173
2173
|
}
|
|
2174
2174
|
|
|
2175
|
+
this.body = new Readable({ read: resume })
|
|
2176
|
+
|
|
2175
2177
|
const onError = this.onError.bind(this)
|
|
2176
2178
|
|
|
2177
2179
|
resolve({
|
|
@@ -2218,10 +2220,6 @@ async function httpNetworkFetch (
|
|
|
2218
2220
|
fetchParams.controller.off('terminated', this.abort)
|
|
2219
2221
|
}
|
|
2220
2222
|
|
|
2221
|
-
if (fetchParams.controller.onAborted) {
|
|
2222
|
-
fetchParams.controller.off('terminated', fetchParams.controller.onAborted)
|
|
2223
|
-
}
|
|
2224
|
-
|
|
2225
2223
|
fetchParams.controller.ended = true
|
|
2226
2224
|
|
|
2227
2225
|
this.body.push(null)
|
|
@@ -249,6 +249,20 @@ class ByteParser extends Writable {
|
|
|
249
249
|
if (isControlFrame(this.#info.opcode)) {
|
|
250
250
|
this.#loop = this.parseControlFrame(body)
|
|
251
251
|
this.#state = parserStates.INFO
|
|
252
|
+
|
|
253
|
+
// If the automatic PONG/CLOSE reply filled the socket's write buffer,
|
|
254
|
+
// stop parsing and resume on drain so a PING flood cannot grow the
|
|
255
|
+
// outbound queue without bound.
|
|
256
|
+
const socket = this.ws[kResponse].socket
|
|
257
|
+
if (this.#loop && socket.writableNeedDrain) {
|
|
258
|
+
this.#loop = false
|
|
259
|
+
socket.once('drain', () => {
|
|
260
|
+
if (this.destroyed) return
|
|
261
|
+
this.#loop = true
|
|
262
|
+
this.run(callback)
|
|
263
|
+
})
|
|
264
|
+
return
|
|
265
|
+
}
|
|
252
266
|
} else {
|
|
253
267
|
if (!this.#info.compressed) {
|
|
254
268
|
if (!this.writeFragments(body)) {
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"./dist/node_modules/node-gyp/gyp/gyp"
|
|
10
10
|
]
|
|
11
11
|
},
|
|
12
|
-
"version": "12.
|
|
12
|
+
"version": "12.15.0",
|
|
13
13
|
"description": "Fast, disk space efficient package manager",
|
|
14
14
|
"keywords": [
|
|
15
15
|
"pnpm",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"type": "module",
|
|
34
34
|
"engines": {
|
|
35
|
-
"node": ">=18
|
|
35
|
+
"node": ">=18"
|
|
36
36
|
},
|
|
37
37
|
"files": [
|
|
38
38
|
"pnpm",
|
|
@@ -46,20 +46,20 @@
|
|
|
46
46
|
"THIRD-PARTY-NOTICES.md"
|
|
47
47
|
],
|
|
48
48
|
"optionalDependencies": {
|
|
49
|
-
"@pnpm/exe.win32-x64": "12.
|
|
50
|
-
"@pnpm/exe.win32-arm64": "12.
|
|
51
|
-
"@pnpm/exe.darwin-x64": "12.
|
|
52
|
-
"@pnpm/exe.darwin-arm64": "12.
|
|
53
|
-
"@pnpm/exe.linux-x64": "12.
|
|
54
|
-
"@pnpm/exe.linux-arm64": "12.
|
|
55
|
-
"@pnpm/exe.linux-riscv64": "12.
|
|
56
|
-
"@pnpm/exe.linux-ppc64": "12.
|
|
57
|
-
"@pnpm/exe.linux-s390x": "12.
|
|
58
|
-
"@pnpm/exe.linux-x64-musl": "12.
|
|
59
|
-
"@pnpm/exe.linux-arm64-musl": "12.
|
|
60
|
-
"@pnpm/exe.freebsd-x64": "12.
|
|
61
|
-
"@pnpm/exe.android-arm64": "12.
|
|
62
|
-
"@pnpm/exe.android-x64": "12.
|
|
49
|
+
"@pnpm/exe.win32-x64": "12.15.0",
|
|
50
|
+
"@pnpm/exe.win32-arm64": "12.15.0",
|
|
51
|
+
"@pnpm/exe.darwin-x64": "12.15.0",
|
|
52
|
+
"@pnpm/exe.darwin-arm64": "12.15.0",
|
|
53
|
+
"@pnpm/exe.linux-x64": "12.15.0",
|
|
54
|
+
"@pnpm/exe.linux-arm64": "12.15.0",
|
|
55
|
+
"@pnpm/exe.linux-riscv64": "12.15.0",
|
|
56
|
+
"@pnpm/exe.linux-ppc64": "12.15.0",
|
|
57
|
+
"@pnpm/exe.linux-s390x": "12.15.0",
|
|
58
|
+
"@pnpm/exe.linux-x64-musl": "12.15.0",
|
|
59
|
+
"@pnpm/exe.linux-arm64-musl": "12.15.0",
|
|
60
|
+
"@pnpm/exe.freebsd-x64": "12.15.0",
|
|
61
|
+
"@pnpm/exe.android-arm64": "12.15.0",
|
|
62
|
+
"@pnpm/exe.android-x64": "12.15.0"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
65
|
"preinstall": "node install.js",
|