@pnpm/releasing.commands 1100.7.0 → 1100.7.1
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 +45 -0
- package/lib/change/index.d.ts +42 -0
- package/lib/change/index.js +248 -0
- package/lib/deploy/createDeployFiles.d.ts +26 -0
- package/lib/deploy/createDeployFiles.js +255 -0
- package/lib/deploy/deploy.d.ts +12 -0
- package/lib/deploy/deploy.js +398 -0
- package/lib/deploy/deployHook.d.ts +2 -0
- package/lib/deploy/deployHook.js +15 -0
- package/lib/deploy/index.d.ts +2 -0
- package/lib/deploy/index.js +3 -0
- package/lib/index.d.ts +7 -0
- package/lib/lane/index.d.ts +24 -0
- package/lib/lane/index.js +157 -0
- package/lib/pack-app/index.d.ts +2 -0
- package/lib/pack-app/index.js +3 -0
- package/lib/pack-app/packApp.d.ts +22 -0
- package/lib/pack-app/packApp.js +558 -0
- package/lib/publish/FailedToPublishError.d.ts +22 -0
- package/lib/publish/FailedToPublishError.js +40 -0
- package/lib/publish/batchPublish.d.ts +20 -0
- package/lib/publish/batchPublish.js +201 -0
- package/lib/publish/displayError.d.ts +1 -0
- package/lib/publish/displayError.js +23 -0
- package/lib/publish/executeTokenHelper.d.ts +4 -0
- package/lib/publish/executeTokenHelper.js +17 -0
- package/lib/publish/extractManifestFromPacked.d.ts +19 -0
- package/lib/publish/extractManifestFromPacked.js +110 -0
- package/lib/publish/index.d.ts +3 -0
- package/lib/publish/index.js +4 -0
- package/lib/publish/oidc/authToken.d.ts +73 -0
- package/lib/publish/oidc/authToken.js +97 -0
- package/lib/publish/oidc/idToken.d.ts +76 -0
- package/lib/publish/oidc/idToken.js +90 -0
- package/lib/publish/oidc/provenance.d.ts +73 -0
- package/lib/publish/oidc/provenance.js +91 -0
- package/lib/publish/otp.d.ts +38 -0
- package/lib/publish/otp.js +41 -0
- package/lib/publish/otpEnv.d.ts +7 -0
- package/lib/publish/otpEnv.js +5 -0
- package/lib/publish/pack.d.ts +34 -0
- package/lib/publish/pack.js +408 -0
- package/lib/publish/previousChangelog.d.ts +25 -0
- package/lib/publish/previousChangelog.js +194 -0
- package/lib/publish/publish.d.ts +40 -0
- package/lib/publish/publish.js +256 -0
- package/lib/publish/publishPackedPkg.d.ts +79 -0
- package/lib/publish/publishPackedPkg.js +298 -0
- package/lib/publish/recursivePublish.d.ts +18 -0
- package/lib/publish/recursivePublish.js +132 -0
- package/lib/publish/registryConfigKeys.d.ts +30 -0
- package/lib/publish/registryConfigKeys.js +50 -0
- package/lib/publish/utils/shared-context.d.ts +7 -0
- package/lib/publish/utils/shared-context.js +20 -0
- package/lib/resolveUnpublishedDirs.d.ts +10 -0
- package/lib/resolveUnpublishedDirs.js +14 -0
- package/lib/stage/approve.d.ts +2 -0
- package/lib/stage/approve.js +14 -0
- package/lib/stage/context.d.ts +14 -0
- package/lib/stage/context.js +25 -0
- package/lib/stage/download.d.ts +2 -0
- package/lib/stage/download.js +32 -0
- package/lib/stage/errors.d.ts +15 -0
- package/lib/stage/errors.js +17 -0
- package/lib/stage/help.d.ts +1 -0
- package/lib/stage/help.js +84 -0
- package/lib/stage/index.d.ts +13 -0
- package/lib/stage/index.js +57 -0
- package/lib/stage/list.d.ts +2 -0
- package/lib/stage/list.js +49 -0
- package/lib/stage/parsing.d.ts +6 -0
- package/lib/stage/parsing.js +27 -0
- package/lib/stage/publish.d.ts +7 -0
- package/lib/stage/publish.js +38 -0
- package/lib/stage/reject.d.ts +2 -0
- package/lib/stage/reject.js +16 -0
- package/lib/stage/rendering.d.ts +11 -0
- package/lib/stage/rendering.js +50 -0
- package/lib/stage/request.d.ts +29 -0
- package/lib/stage/request.js +110 -0
- package/lib/stage/types.d.ts +38 -0
- package/lib/stage/types.js +3 -0
- package/lib/stage/view.d.ts +2 -0
- package/lib/stage/view.js +14 -0
- package/lib/tarball/index.d.ts +2 -0
- package/lib/tarball/index.js +3 -0
- package/lib/tarball/publishSummary.d.ts +47 -0
- package/lib/tarball/publishSummary.js +36 -0
- package/lib/tarball/safeTarballFilename.d.ts +8 -0
- package/lib/tarball/safeTarballFilename.js +21 -0
- package/lib/tarball/summarizeTarball.d.ts +12 -0
- package/lib/tarball/summarizeTarball.js +84 -0
- package/lib/version/index.d.ts +35 -0
- package/lib/version/index.js +360 -0
- package/package.json +42 -42
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { pickRegistryForPackage } from '@pnpm/config.pick-registry-for-package';
|
|
3
|
+
import { createResolver } from '@pnpm/installing.client';
|
|
4
|
+
import { logger } from '@pnpm/logger';
|
|
5
|
+
import { sortFilteredProjects } from '@pnpm/workspace.projects-sorter';
|
|
6
|
+
import pFilter from 'p-filter';
|
|
7
|
+
import { pick } from 'ramda';
|
|
8
|
+
import { writeJsonFile } from 'write-json-file';
|
|
9
|
+
import { batchPublishPackages } from './batchPublish.js';
|
|
10
|
+
import { publish } from './publish.js';
|
|
11
|
+
export async function recursivePublish(opts) {
|
|
12
|
+
const pkgs = Object.values(opts.selectedProjectsGraph).map((wsPkg) => wsPkg.package);
|
|
13
|
+
const { resolve } = createResolver({
|
|
14
|
+
...opts,
|
|
15
|
+
configByUri: opts.configByUri,
|
|
16
|
+
retry: {
|
|
17
|
+
factor: opts.fetchRetryFactor,
|
|
18
|
+
maxTimeout: opts.fetchRetryMaxtimeout,
|
|
19
|
+
minTimeout: opts.fetchRetryMintimeout,
|
|
20
|
+
retries: opts.fetchRetries,
|
|
21
|
+
},
|
|
22
|
+
timeout: opts.fetchTimeout,
|
|
23
|
+
});
|
|
24
|
+
const pkgsToPublish = await pFilter(pkgs, async (pkg) => {
|
|
25
|
+
if (!pkg.manifest.name || !pkg.manifest.version || pkg.manifest.private)
|
|
26
|
+
return false;
|
|
27
|
+
if (opts.force)
|
|
28
|
+
return true;
|
|
29
|
+
return !(await isAlreadyPublished({
|
|
30
|
+
dir: pkg.rootDir,
|
|
31
|
+
lockfileDir: opts.lockfileDir ?? pkg.rootDir,
|
|
32
|
+
registries: opts.registries,
|
|
33
|
+
resolve,
|
|
34
|
+
}, pkg.manifest.name, pkg.manifest.version));
|
|
35
|
+
});
|
|
36
|
+
const publishedPkgDirs = new Set(pkgsToPublish.map(({ rootDir }) => rootDir));
|
|
37
|
+
const publishedPackages = [];
|
|
38
|
+
if (publishedPkgDirs.size === 0) {
|
|
39
|
+
logger.info({
|
|
40
|
+
message: 'There are no new packages that should be published',
|
|
41
|
+
prefix: opts.dir,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const appendedArgs = [];
|
|
46
|
+
if (opts.cliOptions['access']) {
|
|
47
|
+
appendedArgs.push(`--access=${opts.cliOptions['access']}`);
|
|
48
|
+
}
|
|
49
|
+
if (opts.dryRun) {
|
|
50
|
+
appendedArgs.push('--dry-run');
|
|
51
|
+
}
|
|
52
|
+
if (opts.force) {
|
|
53
|
+
appendedArgs.push('--force');
|
|
54
|
+
}
|
|
55
|
+
if (opts.cliOptions['otp']) {
|
|
56
|
+
appendedArgs.push(`--otp=${opts.cliOptions['otp']}`);
|
|
57
|
+
}
|
|
58
|
+
const chunks = sortFilteredProjects(opts);
|
|
59
|
+
const tag = opts.tag ?? 'latest';
|
|
60
|
+
if (opts.batch) {
|
|
61
|
+
const sortedPkgs = chunks
|
|
62
|
+
.flat()
|
|
63
|
+
.filter((pkgDir) => publishedPkgDirs.has(pkgDir))
|
|
64
|
+
.map((pkgDir) => opts.selectedProjectsGraph[pkgDir].package);
|
|
65
|
+
publishedPackages.push(...await batchPublishPackages(sortedPkgs, { ...opts, tag }));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const commandArgs = opts.stage ? ['stage', 'publish'] : ['publish'];
|
|
69
|
+
for (const chunk of chunks) {
|
|
70
|
+
// We can't run publish concurrently due to the npm CLI asking for OTP.
|
|
71
|
+
// NOTE: If we solve the OTP issue, we still need to limit packages concurrency.
|
|
72
|
+
// Otherwise, publishing will consume too much resources.
|
|
73
|
+
// See related issue: https://github.com/pnpm/pnpm/issues/6968
|
|
74
|
+
for (const pkgDir of chunk) {
|
|
75
|
+
if (!publishedPkgDirs.has(pkgDir))
|
|
76
|
+
continue;
|
|
77
|
+
const pkg = opts.selectedProjectsGraph[pkgDir].package;
|
|
78
|
+
const registry = pkg.manifest.publishConfig?.registry ?? pickRegistryForPackage(opts.registries, pkg.manifest.name);
|
|
79
|
+
// eslint-disable-next-line no-await-in-loop
|
|
80
|
+
const publishResult = await publish({
|
|
81
|
+
...opts,
|
|
82
|
+
dir: pkg.rootDir,
|
|
83
|
+
argv: {
|
|
84
|
+
original: [
|
|
85
|
+
...commandArgs,
|
|
86
|
+
'--tag',
|
|
87
|
+
tag,
|
|
88
|
+
'--registry',
|
|
89
|
+
registry,
|
|
90
|
+
...appendedArgs,
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
gitChecks: false,
|
|
94
|
+
recursive: false,
|
|
95
|
+
}, [pkg.rootDir]);
|
|
96
|
+
if (publishResult?.publishSummary != null) {
|
|
97
|
+
publishedPackages.push(publishResult.publishSummary);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Fallback for paths that don't produce a full PublishSummary (e.g. dry run via the
|
|
101
|
+
// legacy npm-CLI bridge, or future call sites that bypass publishPackedPkg).
|
|
102
|
+
const publishedManifest = publishResult?.publishedManifest ?? publishResult?.manifest;
|
|
103
|
+
if (publishedManifest != null) {
|
|
104
|
+
publishedPackages.push(pick(['name', 'version'], publishedManifest));
|
|
105
|
+
}
|
|
106
|
+
else if (publishResult?.exitCode) {
|
|
107
|
+
return { exitCode: publishResult.exitCode, publishedPackages };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (opts.reportSummary) {
|
|
115
|
+
await writeJsonFile(path.join(opts.lockfileDir ?? opts.dir, 'pnpm-publish-summary.json'), { publishedPackages });
|
|
116
|
+
}
|
|
117
|
+
return { exitCode: 0, publishedPackages };
|
|
118
|
+
}
|
|
119
|
+
async function isAlreadyPublished(opts, pkgName, pkgVersion) {
|
|
120
|
+
try {
|
|
121
|
+
await opts.resolve({ alias: pkgName, bareSpecifier: pkgVersion }, {
|
|
122
|
+
lockfileDir: opts.lockfileDir,
|
|
123
|
+
preferredVersions: {},
|
|
124
|
+
projectDir: opts.dir,
|
|
125
|
+
});
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
catch (err) { // eslint-disable-line
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=recursivePublish.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocols currently supported.
|
|
3
|
+
*/
|
|
4
|
+
type SupportedRegistryScheme = 'http' | 'https';
|
|
5
|
+
/**
|
|
6
|
+
* A registry URL that has been normalized to match its corresponding {@link RegistryConfigKey}.
|
|
7
|
+
*/
|
|
8
|
+
export type NormalizedRegistryUrl = `${SupportedRegistryScheme}://${string}/`;
|
|
9
|
+
/**
|
|
10
|
+
* A config key of a registry url is a key on the `.npmrc` file. This key starts with
|
|
11
|
+
* a "//" prefix followed by a hostname and the rest of the URI and ends with a "/".
|
|
12
|
+
* They usually specify authentication information.
|
|
13
|
+
*/
|
|
14
|
+
export type RegistryConfigKey = `//${string}/`;
|
|
15
|
+
export interface SupportedRegistryUrlInfo {
|
|
16
|
+
normalizedUrl: NormalizedRegistryUrl;
|
|
17
|
+
longestConfigKey: RegistryConfigKey;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* If the {@link registryUrl} is an HTTP or an HTTPS registry url, return the longest
|
|
21
|
+
* {@link RegistryConfigKey} that corresponds to the registry url and a {@link NormalizedRegistryUrl}
|
|
22
|
+
* that matches it.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseSupportedRegistryUrl(registryUrl: string): SupportedRegistryUrlInfo | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Generate all {@link RegistryConfigKey} of the same hostname from the longest to the shortest,
|
|
27
|
+
* including {@link longest} itself.
|
|
28
|
+
*/
|
|
29
|
+
export declare function allRegistryConfigKeys(longest: RegistryConfigKey): Generator<RegistryConfigKey, void, void>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import normalizeRegistryUrl from 'normalize-registry-url';
|
|
2
|
+
/**
|
|
3
|
+
* If {@link text} starts with {@link oldPrefix}, replace it with {@link newPrefix}.
|
|
4
|
+
* Otherwise, return `undefined`.
|
|
5
|
+
*/
|
|
6
|
+
const replacePrefix = (text, oldPrefix, newPrefix) => text.startsWith(oldPrefix)
|
|
7
|
+
? text.replace(oldPrefix, newPrefix)
|
|
8
|
+
: undefined;
|
|
9
|
+
/**
|
|
10
|
+
* If {@link text} already ends with {@link suffix}, return it.
|
|
11
|
+
* Otherwise, append {@link suffix} to {@link text} and return it.
|
|
12
|
+
*/
|
|
13
|
+
const ensureSuffix = (text, suffix) => text.endsWith(suffix) ? text : `${text}${suffix}`;
|
|
14
|
+
/**
|
|
15
|
+
* If the {@link registryUrl} is an HTTP or an HTTPS registry url, return the longest
|
|
16
|
+
* {@link RegistryConfigKey} that corresponds to the registry url and a {@link NormalizedRegistryUrl}
|
|
17
|
+
* that matches it.
|
|
18
|
+
*/
|
|
19
|
+
export function parseSupportedRegistryUrl(registryUrl) {
|
|
20
|
+
registryUrl = normalizeRegistryUrl(registryUrl);
|
|
21
|
+
const keyPrefix = replacePrefix(registryUrl, 'http://', '//') ?? replacePrefix(registryUrl, 'https://', '//');
|
|
22
|
+
if (!keyPrefix)
|
|
23
|
+
return undefined;
|
|
24
|
+
const normalizedUrl = ensureSuffix(registryUrl, '/');
|
|
25
|
+
const longestConfigKey = ensureSuffix(keyPrefix, '/');
|
|
26
|
+
return { normalizedUrl, longestConfigKey };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* This value is used for termination check in {@link allRegistryConfigKeys} only.
|
|
30
|
+
* It is not actually a valid {@link RegistryConfigKey}.
|
|
31
|
+
*/
|
|
32
|
+
const EMPTY_REGISTRY_CONFIG_KEY = '///';
|
|
33
|
+
/**
|
|
34
|
+
* Generate all {@link RegistryConfigKey} of the same hostname from the longest to the shortest,
|
|
35
|
+
* including {@link longest} itself.
|
|
36
|
+
*/
|
|
37
|
+
export function* allRegistryConfigKeys(longest) {
|
|
38
|
+
if (!longest.startsWith('//')) {
|
|
39
|
+
throw new RangeError(`The string ${JSON.stringify(longest)} is not a valid registry config key`);
|
|
40
|
+
}
|
|
41
|
+
if (longest === EMPTY_REGISTRY_CONFIG_KEY) {
|
|
42
|
+
throw new RangeError('Registry config key cannot be without hostname');
|
|
43
|
+
}
|
|
44
|
+
if (longest.length <= EMPTY_REGISTRY_CONFIG_KEY.length)
|
|
45
|
+
return;
|
|
46
|
+
yield longest;
|
|
47
|
+
const next = longest.replace(/[^/]*\/$/, '');
|
|
48
|
+
yield* allRegistryConfigKeys(next);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=registryConfigKeys.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AuthTokenContext } from '../oidc/authToken.js';
|
|
2
|
+
import type { IdTokenContext } from '../oidc/idToken.js';
|
|
3
|
+
import type { ProvenanceContext } from '../oidc/provenance.js';
|
|
4
|
+
import type { OtpContext } from '../otp.js';
|
|
5
|
+
type SharedContext = AuthTokenContext & IdTokenContext & ProvenanceContext & OtpContext;
|
|
6
|
+
export declare const SHARED_CONTEXT: SharedContext;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import readline from 'node:readline';
|
|
2
|
+
import { input } from '@inquirer/prompts';
|
|
3
|
+
import { globalInfo, globalWarn } from '@pnpm/logger';
|
|
4
|
+
import { fetch } from '@pnpm/network.fetch';
|
|
5
|
+
import ciInfo from 'ci-info';
|
|
6
|
+
import { publish as _publish } from 'libnpmpublish';
|
|
7
|
+
const publish = _publish;
|
|
8
|
+
export const SHARED_CONTEXT = {
|
|
9
|
+
Date,
|
|
10
|
+
createReadlineInterface: readline.createInterface.bind(null, { input: process.stdin }),
|
|
11
|
+
ciInfo,
|
|
12
|
+
enquirer: { input },
|
|
13
|
+
fetch,
|
|
14
|
+
globalInfo,
|
|
15
|
+
globalWarn,
|
|
16
|
+
process,
|
|
17
|
+
publish,
|
|
18
|
+
setTimeout,
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=shared-context.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReleasePlan } from '@pnpm/releasing.versioning';
|
|
2
|
+
import { type PreviousChangelogOptions } from './publish/previousChangelog.js';
|
|
3
|
+
export type CheckVersionPublished = (pkgName: string, version: string) => Promise<boolean>;
|
|
4
|
+
export type UnpublishedProbeOptions = PreviousChangelogOptions & {
|
|
5
|
+
/** Overridable for tests; production probes the registry. */
|
|
6
|
+
checkVersionPublished?: CheckVersionPublished;
|
|
7
|
+
networkConcurrency?: number;
|
|
8
|
+
};
|
|
9
|
+
/** The releases in `plan` whose current version the registry does not have — {@link assembleReleasePlan}'s `unpublishedDirs`. */
|
|
10
|
+
export declare function resolveUnpublishedDirs(plan: ReleasePlan, opts: UnpublishedProbeOptions): Promise<Set<string>>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import pLimit from 'p-limit';
|
|
2
|
+
import { createVersionPublishedChecker } from './publish/previousChangelog.js';
|
|
3
|
+
const DEFAULT_NETWORK_CONCURRENCY = 16;
|
|
4
|
+
/** The releases in `plan` whose current version the registry does not have — {@link assembleReleasePlan}'s `unpublishedDirs`. */
|
|
5
|
+
export async function resolveUnpublishedDirs(plan, opts) {
|
|
6
|
+
const checkVersionPublished = opts.checkVersionPublished ?? createVersionPublishedChecker(opts);
|
|
7
|
+
const limit = pLimit(opts.networkConcurrency ?? DEFAULT_NETWORK_CONCURRENCY);
|
|
8
|
+
const probed = await Promise.all(plan.releases.map((release) => limit(async () => ({
|
|
9
|
+
dir: release.dir,
|
|
10
|
+
published: await checkVersionPublished(release.name, release.currentVersion),
|
|
11
|
+
}))));
|
|
12
|
+
return new Set(probed.filter(({ published }) => !published).map(({ dir }) => dir));
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=resolveUnpublishedDirs.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createStageContext } from './context.js';
|
|
2
|
+
import { requireStageId } from './parsing.js';
|
|
3
|
+
import { stageRequestWithOtp } from './request.js';
|
|
4
|
+
export async function stageApprove(opts, params) {
|
|
5
|
+
const stageId = requireStageId(params, 'approve');
|
|
6
|
+
const context = createStageContext(opts);
|
|
7
|
+
await stageRequestWithOtp(context, {
|
|
8
|
+
url: new URL(`-/stage/${stageId}/approve`, context.registry).href,
|
|
9
|
+
init: { method: 'POST' },
|
|
10
|
+
action: `approve staged package ${stageId}`,
|
|
11
|
+
});
|
|
12
|
+
return `Staged package ${stageId} approved and published successfully.`;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=approve.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createFetchFromRegistry } from '@pnpm/network.fetch';
|
|
2
|
+
import type { StageOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Shared per-subcommand request context. Created once at the entry of each stage
|
|
5
|
+
* subcommand so that paginated calls (e.g. `stageList`) reuse a single
|
|
6
|
+
* `fetchFromRegistry` instance and a precomputed auth header.
|
|
7
|
+
*/
|
|
8
|
+
export interface StageContext {
|
|
9
|
+
opts: StageOptions;
|
|
10
|
+
registry: string;
|
|
11
|
+
authHeaderValue: string | undefined;
|
|
12
|
+
fetchFromRegistry: ReturnType<typeof createFetchFromRegistry>;
|
|
13
|
+
}
|
|
14
|
+
export declare function createStageContext(opts: StageOptions, packageName?: string): StageContext;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { pickRegistryForPackage } from '@pnpm/config.pick-registry-for-package';
|
|
2
|
+
import { createGetAuthHeaderByURI } from '@pnpm/network.auth-header';
|
|
3
|
+
import { createFetchFromRegistry } from '@pnpm/network.fetch';
|
|
4
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org/';
|
|
5
|
+
export function createStageContext(opts, packageName) {
|
|
6
|
+
const registry = getStageRegistry(opts, packageName);
|
|
7
|
+
const getAuthHeaderByUri = createGetAuthHeaderByURI(opts.configByUri ?? {});
|
|
8
|
+
return {
|
|
9
|
+
opts,
|
|
10
|
+
registry,
|
|
11
|
+
authHeaderValue: packageName ? getAuthHeaderByUri(registry, { pkgName: packageName }) : getAuthHeaderByUri(registry),
|
|
12
|
+
fetchFromRegistry: createFetchFromRegistry(opts),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function getStageRegistry(opts, packageName) {
|
|
16
|
+
const registries = getRegistries(opts);
|
|
17
|
+
const registry = packageName
|
|
18
|
+
? pickRegistryForPackage(registries, packageName)
|
|
19
|
+
: registries.default;
|
|
20
|
+
return registry.endsWith('/') ? registry : `${registry}/`;
|
|
21
|
+
}
|
|
22
|
+
function getRegistries(opts) {
|
|
23
|
+
return opts.registries ?? { default: opts.registry ?? DEFAULT_REGISTRY };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { PnpmError } from '@pnpm/error';
|
|
4
|
+
import { createTarballFilename } from '../tarball/safeTarballFilename.js';
|
|
5
|
+
import { summarizeTarball } from '../tarball/summarizeTarball.js';
|
|
6
|
+
import { createStageContext } from './context.js';
|
|
7
|
+
import { requireStageId } from './parsing.js';
|
|
8
|
+
import { renderTarballSummary } from './rendering.js';
|
|
9
|
+
import { stageRequest } from './request.js';
|
|
10
|
+
export async function stageDownload(opts, params) {
|
|
11
|
+
const stageId = requireStageId(params, 'download');
|
|
12
|
+
const context = createStageContext(opts);
|
|
13
|
+
const response = await stageRequest(context, {
|
|
14
|
+
url: new URL(`-/stage/${stageId}/tarball`, context.registry).href,
|
|
15
|
+
init: { method: 'GET' },
|
|
16
|
+
action: `download staged package ${stageId}`,
|
|
17
|
+
});
|
|
18
|
+
const tarballData = Buffer.from(await response.arrayBuffer());
|
|
19
|
+
const summary = await summarizeTarball(tarballData);
|
|
20
|
+
const filename = createTarballFilename({ name: summary.name, version: summary.version, suffix: stageId });
|
|
21
|
+
const downloadedSummary = { ...summary, filename };
|
|
22
|
+
const downloadDir = path.resolve(opts.dir ?? process.cwd());
|
|
23
|
+
const outputPath = path.resolve(downloadDir, filename);
|
|
24
|
+
if (path.dirname(outputPath) !== downloadDir) {
|
|
25
|
+
throw new PnpmError('INVALID_TARBALL_FILENAME', `Invalid tarball filename "${filename}".`);
|
|
26
|
+
}
|
|
27
|
+
await fs.writeFile(outputPath, tarballData);
|
|
28
|
+
if (opts.json)
|
|
29
|
+
return JSON.stringify({ [summary.name]: downloadedSummary }, null, 2);
|
|
30
|
+
return `${renderTarballSummary(downloadedSummary)}\n${filename}`;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PnpmError } from '@pnpm/error';
|
|
2
|
+
interface StageRegistryErrorProperties {
|
|
3
|
+
readonly action: string;
|
|
4
|
+
readonly status: number;
|
|
5
|
+
readonly statusText: string;
|
|
6
|
+
readonly text: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class StageRegistryError extends PnpmError implements StageRegistryErrorProperties {
|
|
9
|
+
readonly action: string;
|
|
10
|
+
readonly status: number;
|
|
11
|
+
readonly statusText: string;
|
|
12
|
+
readonly text: string;
|
|
13
|
+
constructor(opts: StageRegistryErrorProperties);
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PnpmError } from '@pnpm/error';
|
|
2
|
+
export class StageRegistryError extends PnpmError {
|
|
3
|
+
action;
|
|
4
|
+
status;
|
|
5
|
+
statusText;
|
|
6
|
+
text;
|
|
7
|
+
constructor(opts) {
|
|
8
|
+
const statusDisplay = opts.statusText ? `${opts.status} ${opts.statusText}` : opts.status.toString();
|
|
9
|
+
const trimmedText = opts.text.trim();
|
|
10
|
+
super('STAGE_REGISTRY_ERROR', `Failed to ${opts.action} (status ${statusDisplay})${trimmedText ? `: ${trimmedText}` : ''}`);
|
|
11
|
+
this.action = opts.action;
|
|
12
|
+
this.status = opts.status;
|
|
13
|
+
this.statusText = opts.statusText;
|
|
14
|
+
this.text = opts.text;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function help(): string;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { FILTERING } from '@pnpm/cli.common-cli-options-help';
|
|
2
|
+
import { docsUrl } from '@pnpm/cli.utils';
|
|
3
|
+
import { renderHelp } from 'render-help';
|
|
4
|
+
export function help() {
|
|
5
|
+
return renderHelp({
|
|
6
|
+
description: 'Stage packages for publishing, deferring proof-of-presence (2FA) to a later point in time.',
|
|
7
|
+
descriptionLists: [
|
|
8
|
+
{
|
|
9
|
+
title: 'Subcommands',
|
|
10
|
+
list: [
|
|
11
|
+
{
|
|
12
|
+
description: 'Stage a package for publishing.',
|
|
13
|
+
name: 'publish',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
description: 'List all staged package versions.',
|
|
17
|
+
name: 'list',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
description: 'View details of a specific staged package.',
|
|
21
|
+
name: 'view',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
description: 'Approve a staged package, publishing it to the npm registry.',
|
|
25
|
+
name: 'approve',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
description: 'Reject a staged package, removing it from the registry.',
|
|
29
|
+
name: 'reject',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
description: 'Download the tarball of a staged package for inspection.',
|
|
33
|
+
name: 'download',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
title: 'Options',
|
|
39
|
+
list: [
|
|
40
|
+
{
|
|
41
|
+
description: 'The base URL of the npm registry.',
|
|
42
|
+
name: '--registry <url>',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
description: 'Show information in JSON format for list, view, publish, and download.',
|
|
46
|
+
name: '--json',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
description: 'Registers the staged package with the given tag. By default, the "latest" tag is used.',
|
|
50
|
+
name: '--tag <tag>',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
description: 'Tells the registry whether the staged package should be public or restricted.',
|
|
54
|
+
name: '--access <public|restricted>',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
description: 'Does everything stage publish would do except uploading to the registry.',
|
|
58
|
+
name: '--dry-run',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
description: 'One-time password for approve and reject.',
|
|
62
|
+
name: '--otp',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
description: 'Stage all publishable packages from the workspace.',
|
|
66
|
+
name: '--recursive',
|
|
67
|
+
shortAlias: '-r',
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
FILTERING,
|
|
72
|
+
],
|
|
73
|
+
url: docsUrl('stage'),
|
|
74
|
+
usages: [
|
|
75
|
+
'pnpm stage publish [<tarball>|<dir>] [--tag <tag>] [--access <public|restricted>] [options]',
|
|
76
|
+
'pnpm stage list [<package-spec>]',
|
|
77
|
+
'pnpm stage view <stage-id>',
|
|
78
|
+
'pnpm stage approve <stage-id>',
|
|
79
|
+
'pnpm stage reject <stage-id>',
|
|
80
|
+
'pnpm stage download <stage-id>',
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=help.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { help } from './help.js';
|
|
2
|
+
import { type StageOptions } from './types.js';
|
|
3
|
+
export { help };
|
|
4
|
+
export declare function rcOptionsTypes(): Record<string, unknown>;
|
|
5
|
+
export declare function cliOptionsTypes(): Record<string, unknown>;
|
|
6
|
+
export declare const commandNames: string[];
|
|
7
|
+
export declare const completion: (_cliOpts: Record<string, unknown>, params: string[]) => Promise<Array<{
|
|
8
|
+
name: string;
|
|
9
|
+
}>>;
|
|
10
|
+
export declare function handler(opts: StageOptions, params: string[]): Promise<{
|
|
11
|
+
exitCode?: number;
|
|
12
|
+
output?: string;
|
|
13
|
+
} | string | undefined>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { types as allTypes } from '@pnpm/config.reader';
|
|
2
|
+
import { PnpmError } from '@pnpm/error';
|
|
3
|
+
import { pick } from 'ramda';
|
|
4
|
+
import * as publishCommand from '../publish/publish.js';
|
|
5
|
+
import { stageApprove } from './approve.js';
|
|
6
|
+
import { stageDownload } from './download.js';
|
|
7
|
+
import { help } from './help.js';
|
|
8
|
+
import { stageList } from './list.js';
|
|
9
|
+
import { stagePublish } from './publish.js';
|
|
10
|
+
import { stageReject } from './reject.js';
|
|
11
|
+
import { STAGE_SUBCOMMANDS } from './types.js';
|
|
12
|
+
import { stageView } from './view.js';
|
|
13
|
+
export { help };
|
|
14
|
+
export function rcOptionsTypes() {
|
|
15
|
+
return {
|
|
16
|
+
...publishCommand.rcOptionsTypes(),
|
|
17
|
+
...pick([
|
|
18
|
+
'registry',
|
|
19
|
+
], allTypes),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function cliOptionsTypes() {
|
|
23
|
+
return publishCommand.cliOptionsTypes();
|
|
24
|
+
}
|
|
25
|
+
export const commandNames = ['stage'];
|
|
26
|
+
export const completion = async (_cliOpts, params) => {
|
|
27
|
+
if (params.length > 0)
|
|
28
|
+
return [];
|
|
29
|
+
return STAGE_SUBCOMMANDS.map((name) => ({ name }));
|
|
30
|
+
};
|
|
31
|
+
export async function handler(opts, params) {
|
|
32
|
+
const subcommand = params[0];
|
|
33
|
+
const subcommandParams = params.slice(1);
|
|
34
|
+
switch (subcommand) {
|
|
35
|
+
case 'publish':
|
|
36
|
+
return stagePublish(opts, subcommandParams);
|
|
37
|
+
case 'list':
|
|
38
|
+
return stageList(opts, subcommandParams);
|
|
39
|
+
case 'view':
|
|
40
|
+
return stageView(opts, subcommandParams);
|
|
41
|
+
case 'approve':
|
|
42
|
+
return stageApprove(opts, subcommandParams);
|
|
43
|
+
case 'reject':
|
|
44
|
+
return stageReject(opts, subcommandParams);
|
|
45
|
+
case 'download':
|
|
46
|
+
return stageDownload(opts, subcommandParams);
|
|
47
|
+
case undefined:
|
|
48
|
+
throw new PnpmError('STAGE_SUBCOMMAND_REQUIRED', 'Stage subcommand is required', {
|
|
49
|
+
hint: `Use one of: ${STAGE_SUBCOMMANDS.join(', ')}`,
|
|
50
|
+
});
|
|
51
|
+
default:
|
|
52
|
+
throw new PnpmError('STAGE_UNKNOWN_SUBCOMMAND', `Unknown stage subcommand "${subcommand}"`, {
|
|
53
|
+
hint: `Use one of: ${STAGE_SUBCOMMANDS.join(', ')}`,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { PnpmError } from '@pnpm/error';
|
|
2
|
+
import { createStageContext } from './context.js';
|
|
3
|
+
import { parseStagePackageSpec } from './parsing.js';
|
|
4
|
+
import { renderStageItem } from './rendering.js';
|
|
5
|
+
import { stageJsonRequest } from './request.js';
|
|
6
|
+
const PER_PAGE = 100;
|
|
7
|
+
// Fail-safe bound on the pagination loop, so a registry that keeps answering
|
|
8
|
+
// full pages with an inflated `total` cannot drive it forever.
|
|
9
|
+
const MAX_PAGES = 1000;
|
|
10
|
+
export async function stageList(opts, params) {
|
|
11
|
+
const packageFilter = parsePackageFilter(params[0]);
|
|
12
|
+
const context = createStageContext(opts, packageFilter);
|
|
13
|
+
const items = [];
|
|
14
|
+
let page = 0;
|
|
15
|
+
while (true) {
|
|
16
|
+
const url = new URL('-/stage', context.registry);
|
|
17
|
+
url.searchParams.set('page', page.toString());
|
|
18
|
+
url.searchParams.set('perPage', PER_PAGE.toString());
|
|
19
|
+
if (packageFilter) {
|
|
20
|
+
url.searchParams.set('package', packageFilter);
|
|
21
|
+
}
|
|
22
|
+
// eslint-disable-next-line no-await-in-loop
|
|
23
|
+
const res = await stageJsonRequest(context, { url: url.href, action: 'list staged packages' });
|
|
24
|
+
items.push(...res.items);
|
|
25
|
+
if (items.length >= res.total || res.items.length < PER_PAGE)
|
|
26
|
+
break;
|
|
27
|
+
page++;
|
|
28
|
+
if (page >= MAX_PAGES)
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
if (opts.json)
|
|
32
|
+
return JSON.stringify(items, null, 2);
|
|
33
|
+
if (items.length === 0) {
|
|
34
|
+
return packageFilter
|
|
35
|
+
? `No staged versions of package name "${packageFilter}".`
|
|
36
|
+
: 'No staged packages found.';
|
|
37
|
+
}
|
|
38
|
+
return items.map(renderStageItem).join('\n\n');
|
|
39
|
+
}
|
|
40
|
+
function parsePackageFilter(rawSpec) {
|
|
41
|
+
if (!rawSpec)
|
|
42
|
+
return undefined;
|
|
43
|
+
const spec = parseStagePackageSpec(rawSpec);
|
|
44
|
+
if (spec.rawSpec !== '' && spec.rawSpec !== '*') {
|
|
45
|
+
throw new PnpmError('STAGE_VERSION_SPECIFIER_UNSUPPORTED', 'Version specifiers are not supported for listing staged packages');
|
|
46
|
+
}
|
|
47
|
+
return spec.name;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PnpmError } from '@pnpm/error';
|
|
2
|
+
import npa from '@pnpm/npm-package-arg';
|
|
3
|
+
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4
|
+
export function parseStagePackageSpec(rawSpec) {
|
|
5
|
+
let spec;
|
|
6
|
+
try {
|
|
7
|
+
spec = npa(rawSpec);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
throw new PnpmError('INVALID_PACKAGE_SPEC', `Invalid package spec: ${rawSpec}`);
|
|
11
|
+
}
|
|
12
|
+
if (!spec.name) {
|
|
13
|
+
throw new PnpmError('INVALID_PACKAGE_SPEC', `Invalid package spec: ${rawSpec}`);
|
|
14
|
+
}
|
|
15
|
+
return { name: spec.name, rawSpec: spec.rawSpec };
|
|
16
|
+
}
|
|
17
|
+
export function requireStageId(params, subcommand) {
|
|
18
|
+
if (!params[0]) {
|
|
19
|
+
throw new PnpmError('STAGE_ID_REQUIRED', `Missing required <stage-id> for "pnpm stage ${subcommand}"`);
|
|
20
|
+
}
|
|
21
|
+
const stageId = params[0];
|
|
22
|
+
if (!UUID_REGEX.test(stageId)) {
|
|
23
|
+
throw new PnpmError('INVALID_STAGE_ID', 'stage-id must be a valid UUID');
|
|
24
|
+
}
|
|
25
|
+
return stageId;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=parsing.js.map
|