monoship 1.8.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/dist/cli.mjs ADDED
@@ -0,0 +1,597 @@
1
+ #!/usr/bin/env node
2
+ import { cac } from "cac";
3
+ import path from "node:path";
4
+ import fs from "node:fs";
5
+ import fg from "fast-glob";
6
+ import consola from "consola";
7
+ import libnpmpack from "libnpmpack";
8
+ import { publish } from "libnpmpublish";
9
+ import { execFile } from "node:child_process";
10
+ import { readFile, unlink, writeFile } from "node:fs/promises";
11
+ import { promisify } from "node:util";
12
+ import semver from "semver";
13
+ import hapic, { hasOwnProperty, isClientError } from "hapic";
14
+ //#region src/core/error.ts
15
+ var BaseError = class extends Error {
16
+ code;
17
+ statusCode;
18
+ constructor(message, options) {
19
+ super(message);
20
+ this.code = options.code;
21
+ this.statusCode = options.statusCode;
22
+ }
23
+ };
24
+ //#endregion
25
+ //#region src/core/filesystem/node.ts
26
+ var NodeFileSystem = class {
27
+ async readFile(filePath) {
28
+ return fs.promises.readFile(filePath, { encoding: "utf-8" });
29
+ }
30
+ async writeFile(filePath, content) {
31
+ await fs.promises.writeFile(filePath, content, { encoding: "utf-8" });
32
+ }
33
+ async glob(patterns, options = {}) {
34
+ return fg(patterns, {
35
+ ignore: options.ignore || ["node_modules/**"],
36
+ cwd: options.cwd,
37
+ absolute: true,
38
+ onlyDirectories: true
39
+ });
40
+ }
41
+ };
42
+ //#endregion
43
+ //#region src/core/logger/consola.ts
44
+ var ConsolaLogger = class {
45
+ info(message) {
46
+ consola.info(message);
47
+ }
48
+ success(message) {
49
+ consola.success(message);
50
+ }
51
+ warn(message) {
52
+ consola.warn(message);
53
+ }
54
+ error(message) {
55
+ consola.error(message);
56
+ }
57
+ };
58
+ //#endregion
59
+ //#region src/core/logger/noop.ts
60
+ var NoopLogger = class {
61
+ info(_message) {}
62
+ success(_message) {}
63
+ warn(_message) {}
64
+ error(_message) {}
65
+ };
66
+ //#endregion
67
+ //#region src/core/publisher/error.ts
68
+ var PublishError = class extends BaseError {
69
+ constructor(message, options) {
70
+ super(message, {
71
+ code: "EPUBLISH",
72
+ statusCode: 500
73
+ });
74
+ if (options?.cause) this.cause = options.cause;
75
+ }
76
+ };
77
+ //#endregion
78
+ //#region src/utils/object.ts
79
+ function isObject(input) {
80
+ return typeof input === "object" && input !== null && !Array.isArray(input);
81
+ }
82
+ function isError(input) {
83
+ return isObject(input) && typeof input.message === "string";
84
+ }
85
+ //#endregion
86
+ //#region src/core/publisher/npm.ts
87
+ var NpmPublisher = class {
88
+ /**
89
+ * Publish a package using libnpmpack + libnpmpublish.
90
+ *
91
+ * @returns `true` if published, `false` if the version already exists.
92
+ * @throws {PublishError} On non-conflict failures (network errors, auth failures, etc.).
93
+ */
94
+ async publish(packagePath, manifest, options) {
95
+ try {
96
+ await publish(manifest, await libnpmpack(packagePath), options);
97
+ return true;
98
+ } catch (e) {
99
+ if (this.isNpmJsVersionConflict(e) || this.isNpmPkgGitHubVersionConflict(e)) return false;
100
+ const cause = isError(e) ? e : void 0;
101
+ throw new PublishError(cause?.message || "libnpmpublish failed with an unknown error", { cause });
102
+ }
103
+ }
104
+ /**
105
+ * Determines whether an exception represents a version conflict
106
+ * when publishing to the npmjs.org registry.
107
+ */
108
+ isNpmJsVersionConflict(ex) {
109
+ if (!isObject(ex)) return false;
110
+ if ("code" in ex && ex.code === "EPUBLISHCONFLICT") return true;
111
+ return "code" in ex && ex.code === "E403" && typeof ex.message === "string" && ex.message.includes("You cannot publish over the previously published versions");
112
+ }
113
+ /**
114
+ * Determines whether an exception represents a version conflict
115
+ * when publishing to GitHub Packages (npm.pkg.github.com).
116
+ */
117
+ isNpmPkgGitHubVersionConflict(ex) {
118
+ if (!isObject(ex)) return false;
119
+ if ("code" in ex && ex.code === "E409") return true;
120
+ if ("body" in ex && isObject(ex.body) && ex.body.error === "Cannot publish over existing version") return true;
121
+ return typeof ex.message === "string" && ex.message.startsWith("409 Conflict - PUT https://npm.pkg.github.com");
122
+ }
123
+ };
124
+ //#endregion
125
+ //#region src/core/publisher/npm-cli.ts
126
+ const execFileAsync$1 = promisify(execFile);
127
+ const AUTH_TOKEN_PATTERN = /^(\/\/.+)\/:_authToken$/;
128
+ /**
129
+ * Extract the auth token entry from publish options.
130
+ *
131
+ * When `registry` is provided, prefers the entry whose scope matches
132
+ * the target registry (host + path). Falls back to the first match
133
+ * if no registry-specific entry is found.
134
+ */
135
+ function parseAuthTokenEntry(options, registry) {
136
+ let registryScope;
137
+ if (registry) try {
138
+ const url = new URL(registry);
139
+ registryScope = `//${url.host}${url.pathname.replace(/\/$/, "")}`;
140
+ } catch {}
141
+ let fallback;
142
+ const keys = Object.keys(options);
143
+ for (const key of keys) {
144
+ const match = AUTH_TOKEN_PATTERN.exec(key);
145
+ const registryPath = match?.[1];
146
+ if (match && registryPath && options[key]) {
147
+ const entry = {
148
+ key,
149
+ token: options[key],
150
+ registryPath
151
+ };
152
+ if (registryScope && registryPath === registryScope) return entry;
153
+ if (!fallback) fallback = entry;
154
+ }
155
+ }
156
+ return fallback;
157
+ }
158
+ var NpmCliPublisher = class {
159
+ execFn;
160
+ readFileFn;
161
+ writeFileFn;
162
+ unlinkFn;
163
+ constructor(options = {}) {
164
+ this.execFn = options.execFn || execFileAsync$1;
165
+ this.readFileFn = options.readFileFn || ((fp, enc) => readFile(fp, enc));
166
+ this.writeFileFn = options.writeFileFn || ((fp, content, enc) => writeFile(fp, content, enc));
167
+ this.unlinkFn = options.unlinkFn || ((fp) => unlink(fp));
168
+ }
169
+ /**
170
+ * Publish a package by shelling out to `npm publish`.
171
+ *
172
+ * Writes a temporary `.npmrc` for auth when a token is present and
173
+ * restores/removes it after the command completes (even on failure).
174
+ *
175
+ * @returns `true` if published, `false` if the version already exists.
176
+ * @throws {PublishError} On non-conflict failures (network errors, auth failures, etc.).
177
+ */
178
+ async publish(packagePath, _manifest, options) {
179
+ const args = ["publish"];
180
+ const authEntry = parseAuthTokenEntry(options, options.registry);
181
+ if (options.registry) args.push("--registry", options.registry);
182
+ else if (authEntry) args.push("--registry", `https:${authEntry.registryPath}`);
183
+ if (options.access) args.push("--access", options.access);
184
+ if (options.tag) args.push("--tag", options.tag);
185
+ const env = { ...process.env };
186
+ let npmrcPath;
187
+ let existingNpmrc;
188
+ if (authEntry) {
189
+ env.NODE_AUTH_TOKEN = authEntry.token;
190
+ const registryUrl = options.registry || `https:${authEntry.registryPath}`;
191
+ let npmrcContent;
192
+ try {
193
+ const url = new URL(registryUrl);
194
+ const registryPath = url.pathname.replace(/\/$/, "");
195
+ npmrcContent = `//${url.host}${registryPath}/:_authToken=\${NODE_AUTH_TOKEN}\n`;
196
+ } catch {
197
+ throw new PublishError(`Invalid registry URL: ${registryUrl}`);
198
+ }
199
+ npmrcPath = path.join(packagePath, ".npmrc");
200
+ try {
201
+ existingNpmrc = await this.readFileFn(npmrcPath, "utf-8");
202
+ } catch {}
203
+ const finalContent = existingNpmrc ? `${existingNpmrc.trimEnd()}\n${npmrcContent}` : npmrcContent;
204
+ await this.writeFileFn(npmrcPath, finalContent, "utf-8");
205
+ }
206
+ try {
207
+ await this.execFn("npm", args, {
208
+ cwd: packagePath,
209
+ env
210
+ });
211
+ return true;
212
+ } catch (e) {
213
+ if (this.isVersionConflict(e)) return false;
214
+ const cause = isError(e) ? e : void 0;
215
+ throw new PublishError(cause?.message || "npm publish failed with an unknown error", { cause });
216
+ } finally {
217
+ if (npmrcPath) if (typeof existingNpmrc === "string") await this.writeFileFn(npmrcPath, existingNpmrc, "utf-8");
218
+ else try {
219
+ await this.unlinkFn(npmrcPath);
220
+ } catch {}
221
+ }
222
+ }
223
+ isVersionConflict(e) {
224
+ if (!isObject(e)) return false;
225
+ let stderr = "";
226
+ if (typeof e.stderr === "string") stderr = e.stderr;
227
+ const message = isError(e) ? e.message : "";
228
+ const combined = `${stderr} ${message}`;
229
+ return combined.includes("EPUBLISHCONFLICT") || combined.includes("You cannot publish over the previously published versions") || combined.includes("Cannot publish over existing version") || combined.includes("409 Conflict");
230
+ }
231
+ };
232
+ //#endregion
233
+ //#region src/core/publisher/resolve.ts
234
+ const execFileAsync = promisify(execFile);
235
+ const NPM_MIN_VERSION = "10.0.0";
236
+ async function resolvePublisher(options = {}) {
237
+ const execFn = options.execFn || execFileAsync;
238
+ try {
239
+ const { stdout } = await execFn("npm", ["--version"], {
240
+ cwd: process.cwd(),
241
+ env: process.env
242
+ });
243
+ const version = stdout.trim();
244
+ if (semver.gte(version, NPM_MIN_VERSION)) return new NpmCliPublisher({ execFn });
245
+ } catch {}
246
+ return new NpmPublisher();
247
+ }
248
+ //#endregion
249
+ //#region src/core/registry-client/error.ts
250
+ var RegistryError = class extends BaseError {
251
+ constructor(message, statusCode) {
252
+ super(message, {
253
+ code: `E${statusCode}`,
254
+ statusCode
255
+ });
256
+ }
257
+ };
258
+ /**
259
+ * Duck-type check for RegistryError shape.
260
+ *
261
+ * Validates the error has `message` (string), `statusCode` (number),
262
+ * and `code` (string) — matching the BaseError contract.
263
+ *
264
+ * @param input The value to check.
265
+ * @returns `true` if the value has the RegistryError shape.
266
+ */
267
+ function isRegistryError(input) {
268
+ return isObject(input) && typeof input.message === "string" && typeof input.statusCode === "number" && typeof input.code === "string";
269
+ }
270
+ //#endregion
271
+ //#region src/core/registry-client/hapic.ts
272
+ var HapicRegistryClient = class {
273
+ async getPackument(name, options) {
274
+ const path = encodeURIComponent(name).replace(/^%40/, "@");
275
+ const headers = { ACCEPT: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*" };
276
+ if (options.token) headers.AUTHORIZATION = `Bearer ${options.token}`;
277
+ try {
278
+ return (await hapic.get(new URL(path, options.registry || "https://registry.npmjs.org/").toString(), { headers })).data;
279
+ } catch (e) {
280
+ if (isClientError(e)) throw new RegistryError(e.message, e.statusCode || 500);
281
+ throw new RegistryError(`Registry request failed for ${name}`, 500);
282
+ }
283
+ }
284
+ };
285
+ //#endregion
286
+ //#region src/core/token-provider/chain.ts
287
+ var ChainTokenProvider = class {
288
+ providers;
289
+ constructor(providers) {
290
+ this.providers = providers;
291
+ }
292
+ async getToken(packageName, registry) {
293
+ for (const provider of this.providers) {
294
+ const token = await provider.getToken(packageName, registry);
295
+ if (token) return token;
296
+ }
297
+ }
298
+ };
299
+ //#endregion
300
+ //#region src/core/token-provider/env.ts
301
+ var EnvTokenProvider = class {
302
+ async getToken(_packageName, _registry) {
303
+ return process.env.NODE_AUTH_TOKEN || void 0;
304
+ }
305
+ };
306
+ //#endregion
307
+ //#region src/core/token-provider/memory.ts
308
+ var MemoryTokenProvider = class {
309
+ token;
310
+ constructor(token) {
311
+ this.token = token;
312
+ }
313
+ async getToken(_packageName, _registry) {
314
+ return this.token;
315
+ }
316
+ };
317
+ //#endregion
318
+ //#region src/core/token-provider/oidc.ts
319
+ var OidcTokenProvider = class {
320
+ requestUrl;
321
+ requestToken;
322
+ fetchFn;
323
+ maxRetries;
324
+ retryDelayMs;
325
+ tokenCache;
326
+ constructor(options) {
327
+ this.requestUrl = options.requestUrl;
328
+ this.requestToken = options.requestToken;
329
+ this.fetchFn = options.fetchFn ?? globalThis.fetch;
330
+ this.maxRetries = options.maxRetries ?? 2;
331
+ this.retryDelayMs = options.retryDelayMs ?? 1e3;
332
+ this.tokenCache = /* @__PURE__ */ new Map();
333
+ }
334
+ async getToken(packageName, registry) {
335
+ const cacheKey = `${packageName}@${registry}`;
336
+ const cached = this.tokenCache.get(cacheKey);
337
+ if (cached) return cached;
338
+ const audience = `npm:${new URL(registry).host}`;
339
+ const separator = this.requestUrl.includes("?") ? "&" : "?";
340
+ const oidcUrl = `${this.requestUrl}${separator}audience=${encodeURIComponent(audience)}`;
341
+ const oidcResponse = await this.fetchWithRetry(oidcUrl, { headers: { Authorization: `Bearer ${this.requestToken}` } });
342
+ if (!oidcResponse.ok) throw new Error(`Failed to fetch OIDC token from GitHub: ${oidcResponse.status} ${oidcResponse.statusText}`);
343
+ const idToken = (await oidcResponse.json()).value;
344
+ if (!idToken) throw new Error("OIDC response did not contain a valid token");
345
+ const encodedName = encodeURIComponent(packageName).replace(/^%40/, "@");
346
+ const exchangeUrl = new URL(`/-/npm/v1/oidc/token/exchange/package/${encodedName}`, registry).toString();
347
+ const exchangeResponse = await this.fetchWithRetry(exchangeUrl, {
348
+ method: "POST",
349
+ headers: { Authorization: `Bearer ${idToken}` }
350
+ });
351
+ if (!exchangeResponse.ok) throw new Error(`Failed to exchange OIDC token with npm registry: ${exchangeResponse.status} ${exchangeResponse.statusText}`);
352
+ const { token } = await exchangeResponse.json();
353
+ if (!token) throw new Error("Token exchange response did not contain a valid token");
354
+ this.tokenCache.set(cacheKey, token);
355
+ return token;
356
+ }
357
+ async fetchWithRetry(url, init) {
358
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) try {
359
+ const response = await this.fetchFn(url, init);
360
+ if (response.ok || response.status < 500) return response;
361
+ if (attempt < this.maxRetries) {
362
+ await this.delay(this.retryDelayMs * (attempt + 1));
363
+ continue;
364
+ }
365
+ return response;
366
+ } catch (e) {
367
+ if (attempt >= this.maxRetries) throw e;
368
+ await this.delay(this.retryDelayMs * (attempt + 1));
369
+ }
370
+ throw new Error("Unreachable");
371
+ }
372
+ delay(ms) {
373
+ return new Promise((resolve) => {
374
+ setTimeout(resolve, ms);
375
+ });
376
+ }
377
+ };
378
+ //#endregion
379
+ //#region src/package.ts
380
+ async function isPackagePublished(pkg, registryClient, options) {
381
+ const { name, version } = pkg.content;
382
+ if (!name || !version) throw new Error(`Name or version attribute is missing in ${pkg.path}`);
383
+ try {
384
+ const { versions } = await registryClient.getPackument(name, options);
385
+ if (typeof versions === "undefined" || typeof versions[version] === "undefined") return false;
386
+ } catch (e) {
387
+ if (isRegistryError(e) && e.statusCode === 404) return false;
388
+ throw e;
389
+ }
390
+ return true;
391
+ }
392
+ async function publishPackage(pkg, publisher, options) {
393
+ let pkgPath;
394
+ if (path.isAbsolute(pkg.path)) pkgPath = pkg.path;
395
+ else pkgPath = path.resolve(pkg.path);
396
+ const publishOptions = { ...pkg.content.publishConfig || {} };
397
+ if (options.token && options.token.length > 0) {
398
+ const registry = options.registry || "https://registry.npmjs.org/";
399
+ const url = new URL(registry);
400
+ const registryPath = url.pathname.replace(/\/$/, "");
401
+ publishOptions[`//${url.host}${registryPath}/:_authToken`] = options.token;
402
+ }
403
+ return publisher.publish(pkgPath, pkg.content, publishOptions);
404
+ }
405
+ function isPackagePublishable(pkg) {
406
+ return !!pkg.content.name && !pkg.content.private && !!pkg.content.version;
407
+ }
408
+ //#endregion
409
+ //#region src/package-dependency.ts
410
+ function updatePackagesDependencies(packages) {
411
+ const pkgDir = {};
412
+ for (const package_ of packages) pkgDir[package_.content.name] = package_;
413
+ for (const pkg of packages) {
414
+ if (pkg.content.dependencies) updatePackageDependenciesByType(pkg, "dependencies", pkgDir);
415
+ if (pkg.content.devDependencies) updatePackageDependenciesByType(pkg, "devDependencies", pkgDir);
416
+ if (pkg.content.peerDependencies) updatePackageDependenciesByType(pkg, "peerDependencies", pkgDir);
417
+ }
418
+ }
419
+ function isDependencyWorkspaceProtocolValue(value) {
420
+ return value.substring(0, 10) === "workspace:";
421
+ }
422
+ function normalizeDependencyVersionValue(input, pkgVersion) {
423
+ if (input.length === 1) {
424
+ if (input === "~" || input === "^") return input + pkgVersion;
425
+ return pkgVersion;
426
+ }
427
+ const firstCharacter = input.substring(0, 1);
428
+ if (firstCharacter === "*" || firstCharacter === "~" || firstCharacter === "^") {
429
+ if (semver.valid(input.substring(1))) {
430
+ if (firstCharacter === "~" || firstCharacter === "^") return firstCharacter + pkgVersion;
431
+ return pkgVersion;
432
+ }
433
+ return pkgVersion;
434
+ }
435
+ return pkgVersion;
436
+ }
437
+ function updatePackageDependenciesByType(pkg, depType, pkgDir) {
438
+ const dependencies = pkg.content[depType];
439
+ if (!dependencies) return;
440
+ const keys = Object.keys(dependencies);
441
+ for (const key of keys) {
442
+ const value = dependencies[key];
443
+ if (!value || !isDependencyWorkspaceProtocolValue(value)) continue;
444
+ if (!hasOwnProperty(pkgDir, key)) {
445
+ pkg.valid = false;
446
+ continue;
447
+ }
448
+ const depPkg = pkgDir[key];
449
+ if (!depPkg) {
450
+ pkg.valid = false;
451
+ continue;
452
+ }
453
+ dependencies[key] = normalizeDependencyVersionValue(value.substring(10), depPkg.content.version);
454
+ pkg.modified = true;
455
+ }
456
+ }
457
+ //#endregion
458
+ //#region src/module.ts
459
+ function resolveTokenProvider$1(options) {
460
+ if (options.tokenProvider) return options.tokenProvider;
461
+ if (options.token) return new MemoryTokenProvider(options.token);
462
+ return new EnvTokenProvider();
463
+ }
464
+ async function readWorkspacePackages(workspace, cwd, fileSystem) {
465
+ const directories = await fileSystem.glob(workspace, {
466
+ cwd,
467
+ ignore: ["node_modules/**"]
468
+ });
469
+ const pkgs = [];
470
+ for (const directory of directories) try {
471
+ const raw = await fileSystem.readFile(path.posix.join(directory, "package.json"));
472
+ const content = JSON.parse(raw);
473
+ pkgs.push({
474
+ path: directory,
475
+ content
476
+ });
477
+ } catch {}
478
+ return pkgs;
479
+ }
480
+ async function publish$1(options = {}) {
481
+ const cwd = options.cwd || process.cwd();
482
+ const registry = options.registry || "https://registry.npmjs.org/";
483
+ const rootPackage = options.rootPackage ?? true;
484
+ const fileSystem = options.fileSystem ?? new NodeFileSystem();
485
+ const registryClient = options.registryClient ?? new HapicRegistryClient();
486
+ const publisher = options.publisher ?? await resolvePublisher();
487
+ const tokenProvider = resolveTokenProvider$1(options);
488
+ const logger = options.logger ?? new NoopLogger();
489
+ const raw = await fileSystem.readFile(path.posix.join(cwd, "package.json"));
490
+ let pkg;
491
+ try {
492
+ pkg = JSON.parse(raw);
493
+ } catch {
494
+ throw new Error(`Invalid JSON in package.json at ${path.posix.join(cwd, "package.json")}`);
495
+ }
496
+ const packages = [];
497
+ if (!Array.isArray(pkg.workspaces) && !rootPackage) return [];
498
+ if (rootPackage) packages.push({
499
+ path: cwd,
500
+ content: pkg
501
+ });
502
+ if (Array.isArray(pkg.workspaces)) packages.push(...await readWorkspacePackages(pkg.workspaces, cwd, fileSystem));
503
+ updatePackagesDependencies(packages);
504
+ const unpublishedPackages = [];
505
+ for (const p of packages) {
506
+ if (!isPackagePublishable(p)) continue;
507
+ if (p.valid === false) {
508
+ logger.warn(`Skipping ${p.content.name}: unresolved workspace dependencies`);
509
+ continue;
510
+ }
511
+ const token = await tokenProvider.getToken(p.content.name, registry);
512
+ if (await isPackagePublished(p, registryClient, {
513
+ registry,
514
+ token
515
+ })) continue;
516
+ if (p.modified && !options.dryRun) try {
517
+ await fileSystem.writeFile(path.posix.join(p.path, "package.json"), JSON.stringify(p.content));
518
+ } catch (e) {
519
+ const message = isError(e) ? e.message : String(e);
520
+ logger.warn(`Failed to write package.json for ${p.content.name}: ${message}`);
521
+ continue;
522
+ }
523
+ unpublishedPackages.push({
524
+ pkg: p,
525
+ token
526
+ });
527
+ }
528
+ if (unpublishedPackages.length === 0) return [];
529
+ if (options.dryRun) return unpublishedPackages.map((item) => item.pkg);
530
+ for (const { pkg: p, token } of unpublishedPackages) p.published = await publishPackage(p, publisher, {
531
+ token,
532
+ registry
533
+ });
534
+ return unpublishedPackages.map((item) => item.pkg).filter((p) => !!p.published);
535
+ }
536
+ //#endregion
537
+ //#region src/cli.ts
538
+ function isValidUrl(input) {
539
+ try {
540
+ return !!new URL(input).protocol;
541
+ } catch {
542
+ return false;
543
+ }
544
+ }
545
+ function resolveTokenProvider(token) {
546
+ if (token && token.length > 0) return new MemoryTokenProvider(token);
547
+ const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
548
+ const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
549
+ if (requestUrl && requestToken) return new ChainTokenProvider([new OidcTokenProvider({
550
+ requestUrl,
551
+ requestToken
552
+ }), new EnvTokenProvider()]);
553
+ return new EnvTokenProvider();
554
+ }
555
+ const logger = new ConsolaLogger();
556
+ const cli = cac();
557
+ cli.command("", "Publish workspace packages").option("--token <token>", "Token for registry", { default: process.env.NODE_AUTH_TOKEN }).option("--registry <registry>", "Registry url", { default: "https://registry.npmjs.org/" }).option("--root <root>", "Root directory", { default: process.cwd() }).option("--rootPackage", "Also consider the root package for publishing").option("--dryRun", "Show what would be published without actually publishing").action(async (options) => {
558
+ try {
559
+ if (!isValidUrl(options.registry)) {
560
+ logger.error(`Invalid registry URL: ${options.registry}`);
561
+ process.exit(2);
562
+ }
563
+ const tokenProvider = resolveTokenProvider(options.token);
564
+ if (options.token) process.env.NODE_AUTH_TOKEN = options.token;
565
+ const publisher = await resolvePublisher();
566
+ const packages = await publish$1({
567
+ registry: options.registry,
568
+ cwd: options.root,
569
+ rootPackage: options.rootPackage ?? true,
570
+ dryRun: options.dryRun,
571
+ fileSystem: new NodeFileSystem(),
572
+ registryClient: new HapicRegistryClient(),
573
+ publisher,
574
+ tokenProvider,
575
+ logger
576
+ });
577
+ if (packages.length === 0) logger.info("No packages need to be published.");
578
+ else if (options.dryRun) {
579
+ logger.info("Dry run — the following packages would be published:");
580
+ for (const pkg of packages) logger.info(` ${pkg.content.name}@${pkg.content.version} → ${options.registry}`);
581
+ } else {
582
+ for (const pkg of packages) logger.success(`Published ${pkg.content.name}@${pkg.content.version}`);
583
+ logger.info(`Published ${packages.length} package(s).`);
584
+ }
585
+ process.exit(0);
586
+ } catch (e) {
587
+ if (isError(e)) {
588
+ logger.error(e.message);
589
+ if (process.env.DEBUG) logger.error(e.stack || "");
590
+ } else logger.error("An unknown error occurred.");
591
+ process.exit(1);
592
+ }
593
+ });
594
+ cli.help();
595
+ cli.parse();
596
+ //#endregion
597
+ export {};