@pnpm/releasing.commands 1100.2.0 → 1100.2.2

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.
@@ -12,11 +12,6 @@ import { safeExeca as execa } from 'execa';
12
12
  import { renderHelp } from 'render-help';
13
13
  /** Minimum Node.js version that supports `node --build-sea`. */
14
14
  const MIN_BUILDER_VERSION = { major: 25, minor: 5 };
15
- // Range to download when the running Node is too old. Constrained to the
16
- // current major so we don't silently jump majors across releases, and pinned
17
- // above MIN_BUILDER_VERSION.minor so older point releases (e.g. 25.0.x) that
18
- // don't support `--build-sea` aren't picked.
19
- const DEFAULT_BUILDER_SPEC = `>=${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}.0 <${MIN_BUILDER_VERSION.major + 1}.0.0`;
20
15
  // Target OS names match `process.platform`. That keeps the CLI surface
21
16
  // consistent with pnpm's own `--os` flag (which also takes platform constants)
22
17
  // and with `supportedArchitectures.os` in pnpm-workspace.yaml.
@@ -44,16 +39,17 @@ export function help() {
44
39
  description: 'Pack a CommonJS entry file into a standalone executable for one or more target platforms.\n\n' +
45
40
  'The executable embeds a Node.js binary via the Node.js Single Executable Applications API.\n' +
46
41
  `Requires Node.js v${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}+ to perform ` +
47
- 'the injection. The running Node.js is used when it is new enough; otherwise, the ' +
48
- `latest Node.js v${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}+ in the ` +
49
- `v${MIN_BUILDER_VERSION.major}.x line is downloaded automatically.\n\n` +
42
+ 'the injection. SEA blobs are not compatible across Node.js minor releases, so the ' +
43
+ 'builder Node.js must match the embedded runtime version exactly. The running Node.js ' +
44
+ 'is used when it already matches; otherwise a host-arch Node.js of the embedded runtime ' +
45
+ 'version is downloaded automatically.\n\n' +
50
46
  'Defaults for --entry, --target, --runtime, --output-dir, and --output-name can be ' +
51
47
  'set in the package.json under "pnpm.app". CLI flags override the config; --target entirely ' +
52
48
  'replaces the configured list so you can narrow it at invocation time.',
53
49
  url: docsUrl('pack-app'),
54
50
  usages: [
55
51
  'pnpm pack-app --entry dist/index.cjs --target linux-x64 --target win32-x64',
56
- 'pnpm pack-app --entry dist/index.cjs --target linux-x64-musl --runtime node@22',
52
+ `pnpm pack-app --entry dist/index.cjs --target linux-x64-musl --runtime node@${MIN_BUILDER_VERSION.major}`,
57
53
  ],
58
54
  descriptionLists: [
59
55
  {
@@ -70,7 +66,8 @@ export function help() {
70
66
  },
71
67
  {
72
68
  description: 'Runtime to embed in the output executables, as a "<name>@<version>" spec ' +
73
- '(e.g. "node@22", "node@22.0.0", "node@lts"). Only "node" is supported today. ' +
69
+ `(e.g. "node@${MIN_BUILDER_VERSION.major}", "node@${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}.0"). ` +
70
+ `Only "node" is supported today, and the version must be >= v${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor} (the minimum that supports --build-sea). ` +
74
71
  'Defaults to the running Node.js version.',
75
72
  name: '--runtime',
76
73
  },
@@ -126,8 +123,17 @@ export async function handler(opts, params) {
126
123
  const outputName = validateOutputName(opts.outputName ?? project.app?.outputName ?? deriveOutputNameFromPackage(project, opts.dir));
127
124
  const fetch = createFetchFromRegistry(opts);
128
125
  const buildRoot = path.join(opts.pnpmHomeDir, 'pack-app');
129
- const builderBin = await resolveBuilderBinary({ fetch, nodeDownloadMirrors: opts.nodeDownloadMirrors, buildRoot });
126
+ // Resolve the embedded target version first so the builder can be pinned to
127
+ // the same version. SEA blobs carry no version header and the serialized
128
+ // format has changed across Node.js minor releases (e.g. v25.7 added a
129
+ // ModuleFormat byte for ESM entry points), so a blob produced by a builder
130
+ // of a different version than the embedded runtime will fail deserialization
131
+ // at startup with an opaque native assertion.
130
132
  const resolvedTargetVersion = await resolveVersion(fetch, requestedNodeSpec, opts.nodeDownloadMirrors);
133
+ const builderBin = await resolveBuilderBinary({
134
+ buildRoot,
135
+ targetVersion: resolvedTargetVersion,
136
+ });
131
137
  const results = [];
132
138
  for (const target of targets) {
133
139
  // eslint-disable-next-line no-await-in-loop
@@ -175,17 +181,26 @@ export async function handler(opts, params) {
175
181
  return `Built ${targets.length} executable${targets.length === 1 ? '' : 's'}:\n${results.join('\n')}`;
176
182
  }
177
183
  /**
178
- * Returns a Node.js binary that supports `--build-sea`. Prefers the running
179
- * interpreter to avoid a download; falls back to downloading Node.js v25.
184
+ * Returns a Node.js binary that supports `--build-sea` AND produces a SEA
185
+ * blob the embedded runtime can deserialize. The second constraint forces the
186
+ * builder to match the target runtime version exactly: blobs are versioned by
187
+ * the writer's internal struct layout with no header, and Node bumps that
188
+ * layout in minor releases (e.g. v25.7 added a ModuleFormat byte for ESM
189
+ * entries), so a cross-version blob crashes at startup.
190
+ *
191
+ * Prefers the running interpreter when it already matches the target version;
192
+ * otherwise downloads the target version for the host platform.
180
193
  */
181
194
  async function resolveBuilderBinary(ctx) {
182
- if (runningNodeCanBuildSea()) {
195
+ if (runningNodeCanBuildSea() && process.version === `v${ctx.targetVersion}`) {
183
196
  return process.execPath;
184
197
  }
185
- const version = await resolveVersion(ctx.fetch, DEFAULT_BUILDER_SPEC, ctx.nodeDownloadMirrors);
198
+ if (!builderVersionCanBuildSea(ctx.targetVersion)) {
199
+ throw new PnpmError('PACK_APP_RUNTIME_TOO_OLD', `The embedded runtime "node@${ctx.targetVersion}" is older than Node.js v${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}, which is the minimum version that supports --build-sea.`, { hint: `Pass --runtime node@${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}.0 (or newer) or set "pnpm.app.runtime" in package.json.` });
200
+ }
186
201
  return ensureNodeRuntime({
187
202
  buildRoot: ctx.buildRoot,
188
- version,
203
+ version: ctx.targetVersion,
189
204
  platform: process.platform,
190
205
  arch: process.arch,
191
206
  // Pin libc to the host's. Otherwise a caller that had set
@@ -201,7 +216,10 @@ function hostLinuxLibc() {
201
216
  return family === 'musl' ? 'musl' : 'glibc';
202
217
  }
203
218
  function runningNodeCanBuildSea() {
204
- const [majorStr, minorStr] = process.version.slice(1).split('.');
219
+ return builderVersionCanBuildSea(process.version.slice(1));
220
+ }
221
+ function builderVersionCanBuildSea(version) {
222
+ const [majorStr, minorStr] = version.split('.');
205
223
  const major = Number(majorStr);
206
224
  const minor = Number(minorStr);
207
225
  return (major > MIN_BUILDER_VERSION.major ||
@@ -286,7 +304,7 @@ const RUNTIME_PATTERN = /^(node)@(.+)$/;
286
304
  function parseRuntime(spec) {
287
305
  const match = RUNTIME_PATTERN.exec(spec);
288
306
  if (!match) {
289
- throw new PnpmError('PACK_APP_INVALID_RUNTIME', `Invalid runtime "${spec}". Expected format: <name>@<version> (supported runtimes: ${SUPPORTED_RUNTIMES.join(', ')}; e.g. "node@22.0.0", "node@lts").`);
307
+ throw new PnpmError('PACK_APP_INVALID_RUNTIME', `Invalid runtime "${spec}". Expected format: <name>@<version> (supported runtimes: ${SUPPORTED_RUNTIMES.join(', ')}; e.g. "node@${MIN_BUILDER_VERSION.major}.${MIN_BUILDER_VERSION.minor}.0").`);
290
308
  }
291
309
  return { name: match[1], version: match[2] };
292
310
  }
@@ -1,9 +1,9 @@
1
1
  import { PnpmError } from '@pnpm/error';
2
2
  import type { ExportedManifest } from '@pnpm/releasing.exportable-manifest';
3
- declare const TARBALL_SUFFIXES: readonly [".tar.gz", ".tgz"];
4
- export type TarballSuffix = typeof TARBALL_SUFFIXES[number];
3
+ declare const TARBALL_SUFFIXES: readonly ['.tar.gz', '.tgz'];
4
+ export type TarballSuffix = (typeof TARBALL_SUFFIXES)[number];
5
5
  export type TarballPath = `${string}${TarballSuffix}`;
6
- export declare const isTarballPath: (path: string) => path is `${string}.tar.gz` | `${string}.tgz`;
6
+ export declare const isTarballPath: (path: string) => path is TarballPath;
7
7
  export declare function extractManifestFromPacked<Output = ExportedManifest>(tarballPath: TarballPath): Promise<Output>;
8
8
  export declare class PublishArchiveMissingManifestError extends PnpmError {
9
9
  readonly tarballPath: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/releasing.commands",
3
- "version": "1100.2.0",
3
+ "version": "1100.2.2",
4
4
  "description": "Commands for deploy, pack, and publish",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -48,33 +48,33 @@
48
48
  "write-json-file": "^7.0.0",
49
49
  "write-yaml-file": "^6.0.0",
50
50
  "@pnpm/bins.resolver": "1100.0.1",
51
- "@pnpm/catalogs.types": "1100.0.0",
52
51
  "@pnpm/cli.utils": "1101.0.0",
53
52
  "@pnpm/cli.common-cli-options-help": "1100.0.0",
54
53
  "@pnpm/config.pick-registry-for-package": "1100.0.1",
55
- "@pnpm/config.reader": "1101.1.1",
56
54
  "@pnpm/deps.path": "1100.0.1",
57
- "@pnpm/engine.runtime.commands": "1100.0.4",
55
+ "@pnpm/catalogs.types": "1100.0.0",
56
+ "@pnpm/config.reader": "1101.1.1",
58
57
  "@pnpm/constants": "1100.0.0",
59
- "@pnpm/engine.runtime.node-resolver": "1101.0.0",
60
- "@pnpm/error": "1100.0.0",
61
- "@pnpm/exec.lifecycle": "1100.0.4",
58
+ "@pnpm/engine.runtime.commands": "1100.0.5",
62
59
  "@pnpm/exec.pnpm-cli-runner": "1100.0.0",
63
60
  "@pnpm/fetching.directory-fetcher": "1100.0.4",
61
+ "@pnpm/exec.lifecycle": "1100.0.4",
64
62
  "@pnpm/fs.indexed-pkg-importer": "1100.0.3",
65
- "@pnpm/fs.packlist": "1100.0.0",
66
- "@pnpm/installing.client": "1100.0.4",
67
63
  "@pnpm/fs.is-empty-dir-or-nothing": "1100.0.0",
68
- "@pnpm/installing.commands": "1100.1.2",
64
+ "@pnpm/fs.packlist": "1100.0.0",
65
+ "@pnpm/engine.runtime.node-resolver": "1101.0.0",
66
+ "@pnpm/installing.commands": "1100.1.3",
67
+ "@pnpm/installing.client": "1100.0.5",
68
+ "@pnpm/error": "1100.0.0",
69
+ "@pnpm/lockfile.fs": "1100.0.3",
69
70
  "@pnpm/lockfile.types": "1100.0.2",
70
71
  "@pnpm/network.fetch": "1100.0.1",
72
+ "@pnpm/network.web-auth": "1101.0.0",
71
73
  "@pnpm/network.git-utils": "1100.0.0",
72
- "@pnpm/lockfile.fs": "1100.0.3",
73
74
  "@pnpm/releasing.exportable-manifest": "1100.0.2",
74
- "@pnpm/network.web-auth": "1101.0.0",
75
75
  "@pnpm/resolving.resolver-base": "1100.1.0",
76
- "@pnpm/types": "1101.0.0",
77
76
  "@pnpm/workspace.projects-filter": "1100.0.4",
77
+ "@pnpm/types": "1101.0.0",
78
78
  "@pnpm/workspace.projects-sorter": "1100.0.1"
79
79
  },
80
80
  "peerDependencies": {
@@ -99,14 +99,14 @@
99
99
  "tar": "^7.5.10",
100
100
  "write-yaml-file": "^6.0.0",
101
101
  "@pnpm/assert-project": "1100.0.3",
102
- "@pnpm/logger": "1100.0.0",
103
102
  "@pnpm/catalogs.config": "1100.0.0",
104
103
  "@pnpm/prepare": "1100.0.3",
104
+ "@pnpm/logger": "1100.0.0",
105
105
  "@pnpm/hooks.pnpmfile": "1100.0.3",
106
- "@pnpm/testing.command-defaults": "1100.0.1",
106
+ "@pnpm/releasing.commands": "1100.2.2",
107
107
  "@pnpm/test-ipc-server": "1100.0.0",
108
- "@pnpm/releasing.commands": "1100.2.0",
109
- "@pnpm/test-fixtures": "1100.0.0"
108
+ "@pnpm/test-fixtures": "1100.0.0",
109
+ "@pnpm/testing.command-defaults": "1100.0.1"
110
110
  },
111
111
  "engines": {
112
112
  "node": ">=22.13"