@dbx-tools/projen 0.9.47 → 0.9.50

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.
@@ -1,141 +0,0 @@
1
- #!/usr/bin/env -S bun
2
- /** Batch package type-checks and run package-owned compile lifecycles. */
3
- import { existsSync, readFileSync } from "node:fs";
4
- import { availableParallelism } from "node:os";
5
- import { join, resolve } from "node:path";
6
- import * as exec from "@dbx-tools/core/exec";
7
- import { log } from "@dbx-tools/shared-core";
8
-
9
- const logger = log.logger("projen:compile");
10
-
11
- interface PackageManifest {
12
- readonly name?: string;
13
- readonly scripts?: Record<string, string>;
14
- readonly workspaces?: string[];
15
- }
16
-
17
- interface TaskManifest {
18
- readonly tasks?: {
19
- readonly "pre-compile"?: {
20
- readonly steps?: Array<{ readonly exec?: string; readonly execArgs?: string[] }>;
21
- };
22
- readonly compile?: {
23
- readonly steps?: Array<{ readonly exec?: string; readonly execArgs?: string[] }>;
24
- };
25
- readonly "post-compile"?: {
26
- readonly steps?: Array<{ readonly exec?: string; readonly execArgs?: string[] }>;
27
- };
28
- };
29
- }
30
-
31
- export interface CompilePlan {
32
- readonly typescriptConfigs: string[];
33
- readonly customPackages: Array<{ readonly name: string; readonly directory: string }>;
34
- }
35
-
36
- function readJson<T>(path: string): T {
37
- return JSON.parse(readFileSync(path, "utf8")) as T;
38
- }
39
-
40
- function plainTypeScriptBuild(steps: unknown): boolean {
41
- if (!Array.isArray(steps) || steps.length !== 1) return false;
42
- const step = steps[0] as { execArgs?: string[] };
43
- if (!step || typeof step !== "object") return false;
44
- return (
45
- Object.keys(step).length === 1 &&
46
- step.execArgs?.length === 2 &&
47
- step.execArgs[0] === "tsc" &&
48
- step.execArgs[1] === "--build"
49
- );
50
- }
51
-
52
- function hasSteps(steps: unknown): boolean {
53
- return Array.isArray(steps) && steps.length > 0;
54
- }
55
-
56
- /** Read the root's concrete workspace paths on every run, including newly added members. */
57
- export function compilePlan(root: string): CompilePlan {
58
- const workspace = readJson<PackageManifest>(join(root, "package.json"));
59
- const typescriptConfigs: string[] = [];
60
- const customPackages: CompilePlan["customPackages"] = [];
61
- for (const member of workspace.workspaces ?? []) {
62
- const directory = resolve(root, member);
63
- const manifest = readJson<PackageManifest>(join(directory, "package.json"));
64
- const script = manifest.scripts?.compile;
65
- if (!script) continue;
66
- const taskPath = join(directory, ".projen", "tasks.json");
67
- const tasks = existsSync(taskPath) ? readJson<TaskManifest>(taskPath).tasks : undefined;
68
- const plainCompile = plainTypeScriptBuild(tasks?.compile?.steps);
69
- if (
70
- script === "projen compile" &&
71
- plainCompile &&
72
- !hasSteps(tasks?.["pre-compile"]?.steps) &&
73
- !hasSteps(tasks?.["post-compile"]?.steps)
74
- ) {
75
- typescriptConfigs.push(join(directory, "tsconfig.json"));
76
- } else {
77
- customPackages.push({ name: manifest.name ?? member, directory });
78
- }
79
- }
80
- return { typescriptConfigs, customPackages };
81
- }
82
-
83
- const emoji = /[\p{Extended_Pictographic}\uFE0F\u200D]/gu;
84
-
85
- export async function main(): Promise<void> {
86
- const root = process.cwd();
87
- const plan = compilePlan(root);
88
- const jobs: Array<{ name: string; result: Promise<exec.ExecResult> }> = [];
89
- if (plan.typescriptConfigs.length > 0) {
90
- const batchCount = Math.min(4, availableParallelism(), plan.typescriptConfigs.length);
91
- const batches = Array.from({ length: batchCount }, () => [] as string[]);
92
- plan.typescriptConfigs.forEach((config, index) => batches[index % batchCount]!.push(config));
93
- logger.info(`type-checking ${plan.typescriptConfigs.length} workspaces in ${batchCount} batches`);
94
- for (const [index, batch] of batches.entries()) {
95
- jobs.push({
96
- name: `TypeScript batch ${index + 1}`,
97
- result: exec.spawn("bunx", ["tsc", "--build", ...batch], {
98
- cwd: root,
99
- stdin: "ignore",
100
- stdout: "inherit",
101
- stderr: "inherit",
102
- }),
103
- });
104
- }
105
- }
106
- for (const pkg of plan.customPackages) {
107
- jobs.push({
108
- name: pkg.name,
109
- result: compileCustomPackage(pkg),
110
- });
111
- }
112
- const results = await Promise.allSettled(jobs.map((job) => job.result));
113
- let failed = false;
114
- for (const [index, result] of results.entries()) {
115
- if (result.status === "rejected") {
116
- logger.error(`${jobs[index]!.name} compile failed:`, result.reason);
117
- failed = true;
118
- } else if (result.value.exitCode !== 0) {
119
- logger.error(`${jobs[index]!.name} compile exited with ${result.value.exitCode}`);
120
- failed = true;
121
- }
122
- }
123
- if (failed) process.exitCode = 1;
124
- }
125
-
126
- async function compileCustomPackage(pkg: {
127
- readonly name: string;
128
- readonly directory: string;
129
- }): Promise<exec.ExecResult> {
130
- let result: exec.ExecResult | undefined;
131
- for (const task of ["pre-compile", "compile", "post-compile"]) {
132
- result = await exec.spawn("bun", ["run", task], {
133
- cwd: pkg.directory,
134
- stdin: "ignore",
135
- stdout: (line) => logger.info(`${pkg.name}: ${line.replace(emoji, "")}`),
136
- stderr: (line) => logger.error(`${pkg.name}: ${line.replace(emoji, "")}`),
137
- });
138
- if (result.exitCode !== 0) return result;
139
- }
140
- return result!;
141
- }
@@ -1,466 +0,0 @@
1
- #!/usr/bin/env -S bun
2
- /** Idempotent npm archive publication for release recovery. */
3
- import { createHash } from "node:crypto";
4
- import {
5
- chmodSync,
6
- lstatSync,
7
- mkdtempSync,
8
- readFileSync,
9
- readdirSync,
10
- readlinkSync,
11
- rmSync,
12
- writeFileSync,
13
- } from "node:fs";
14
- import { tmpdir } from "node:os";
15
- import { join, relative, resolve } from "node:path";
16
- import * as exec from "@dbx-tools/core/exec";
17
- import * as projectUtils from "@dbx-tools/core/project-utils";
18
- import { log } from "@dbx-tools/shared-core";
19
- import { Command } from "commander";
20
- import { runTaskCommand } from "../../src/_task-command.ts";
21
-
22
- const DEFAULT_REGISTRY = "https://registry.npmjs.org";
23
- const logger = log.logger("projen:publish-npm");
24
-
25
- /** Registry and archive identity used to verify idempotent npm publication. */
26
- export interface NpmReleaseIdentity {
27
- readonly access?: "public" | "restricted";
28
- readonly contentDigest?: string;
29
- readonly integrity?: string;
30
- readonly name: string;
31
- readonly repository?: unknown;
32
- readonly version: string;
33
- }
34
-
35
- interface NpmArchiveManifest {
36
- readonly [key: string]: unknown;
37
- readonly dependencies?: Readonly<Record<string, string>>;
38
- readonly optionalDependencies?: Readonly<Record<string, string>>;
39
- readonly name?: string;
40
- readonly publishConfig?: { readonly access?: unknown };
41
- readonly repository?: unknown;
42
- readonly version?: string;
43
- }
44
-
45
- /** Transform applied to a packed npm manifest before archive verification. */
46
- export type NpmArchiveManifestTransform = (
47
- manifest: NpmArchiveManifest,
48
- ) => Readonly<Record<string, unknown>>;
49
-
50
- /** Entry-point fields projected from publishConfig into a packed manifest. */
51
- const PUBLISH_CONFIG_ENTRY_FIELDS = ["main", "types", "bin", "exports"] as const;
52
- const DEPENDENCY_FIELDS = [
53
- "dependencies",
54
- "devDependencies",
55
- "optionalDependencies",
56
- "peerDependencies",
57
- ] as const;
58
-
59
- /** Replace workspace and catalogue protocols with publishable registry ranges. */
60
- export function materializeWorkspaceManifest(
61
- source: Readonly<Record<string, unknown>>,
62
- workspace: Readonly<Record<string, unknown>>,
63
- ): Readonly<Record<string, unknown>> {
64
- const manifest = structuredClone(source) as Record<string, unknown>;
65
- const version = workspace.version;
66
- const catalog =
67
- workspace.catalog && typeof workspace.catalog === "object"
68
- ? (workspace.catalog as Readonly<Record<string, unknown>>)
69
- : {};
70
- if (typeof version !== "string" || !version) {
71
- throw new Error("Workspace manifest has no version");
72
- }
73
- for (const field of DEPENDENCY_FIELDS) {
74
- const dependencies = manifest[field];
75
- if (!dependencies || typeof dependencies !== "object") continue;
76
- manifest[field] = Object.fromEntries(
77
- Object.entries(dependencies).map(([name, value]) => [
78
- name,
79
- materializeDependency(name, value, version, catalog),
80
- ]),
81
- );
82
- }
83
- return manifest;
84
- }
85
-
86
- function materializeDependency(
87
- name: string,
88
- value: unknown,
89
- version: string,
90
- catalog: Readonly<Record<string, unknown>>,
91
- ): unknown {
92
- if (value === "catalog:") {
93
- const resolved = catalog[name];
94
- if (typeof resolved !== "string" || !resolved) {
95
- throw new Error(`Workspace catalog has no version for ${name}`);
96
- }
97
- return resolved;
98
- }
99
- if (typeof value !== "string" || !value.startsWith("workspace:")) return value;
100
- const selector = value.slice("workspace:".length);
101
- if (selector === "*" || !selector) return version;
102
- if (selector === "^" || selector === "~") return `${selector}${version}`;
103
- return selector;
104
- }
105
-
106
- /**
107
- * Fold compiled publish entry points onto an archive manifest without changing
108
- * the source checkout.
109
- */
110
- export function applyPublishConfig(
111
- source: Readonly<Record<string, unknown>>,
112
- ): Readonly<Record<string, unknown>> {
113
- const manifest = { ...source };
114
- const publishConfig = manifest.publishConfig as Record<string, unknown> | undefined;
115
- if (!publishConfig) return manifest;
116
- for (const field of PUBLISH_CONFIG_ENTRY_FIELDS) {
117
- if (field in publishConfig) manifest[field] = publishConfig[field];
118
- }
119
- return manifest;
120
- }
121
-
122
- function archiveFileEntries(directory: string, prefix = ""): string[] {
123
- return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
124
- const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
125
- return entry.isDirectory()
126
- ? archiveFileEntries(join(directory, entry.name), relativePath)
127
- : [relativePath];
128
- });
129
- }
130
-
131
- function normalizedRepository(value: unknown): string | undefined {
132
- const url =
133
- typeof value === "string"
134
- ? value
135
- : value && typeof value === "object" && "url" in value
136
- ? String(value.url)
137
- : undefined;
138
- return url
139
- ?.replace(/^git\+/, "")
140
- .replace(/\.git$/, "")
141
- .replace(/\/$/, "");
142
- }
143
-
144
- function registryUrl(registry: string, name: string, version: string): string {
145
- return `${registry.replace(/\/$/, "")}/${encodeURIComponent(name)}/${encodeURIComponent(version)}`;
146
- }
147
-
148
- /** Verify that registry metadata represents the exact locally packed release. */
149
- export function npmReleaseMatches(
150
- local: NpmReleaseIdentity,
151
- published: NpmReleaseIdentity | undefined,
152
- ): boolean {
153
- if (!published) return false;
154
- if (published.name !== local.name || published.version !== local.version) {
155
- throw new Error(`Published npm identity does not match ${local.name}@${local.version}`);
156
- }
157
- const localRepository = normalizedRepository(local.repository);
158
- const publishedRepository = normalizedRepository(published.repository);
159
- if (localRepository && publishedRepository && localRepository !== publishedRepository) {
160
- throw new Error(`Published npm repository does not match ${local.name}@${local.version}`);
161
- }
162
- if (
163
- local.contentDigest &&
164
- published.contentDigest &&
165
- local.contentDigest !== published.contentDigest
166
- ) {
167
- throw new Error(`Published npm content does not match ${local.name}@${local.version}`);
168
- }
169
- if (
170
- !(local.contentDigest && published.contentDigest) &&
171
- local.integrity &&
172
- published.integrity !== local.integrity
173
- ) {
174
- throw new Error(`Published npm integrity does not match ${local.name}@${local.version}`);
175
- }
176
- return true;
177
- }
178
-
179
- /** Read one published npm release identity, or `undefined` when it is absent. */
180
- export async function publishedNpmRelease(
181
- name: string,
182
- version: string,
183
- registry = process.env.NPM_CONFIG_REGISTRY ?? DEFAULT_REGISTRY,
184
- ): Promise<NpmReleaseIdentity | undefined> {
185
- const response = await fetch(registryUrl(registry, name, version), {
186
- headers: { accept: "application/json" },
187
- });
188
- if (response.status === 404) return undefined;
189
- if (!response.ok) {
190
- throw new Error(`npm registry lookup failed for ${name}@${version}: ${response.status}`);
191
- }
192
- const metadata = (await response.json()) as {
193
- dist?: { integrity?: string; tarball?: string };
194
- name?: string;
195
- repository?: unknown;
196
- version?: string;
197
- };
198
- if (!metadata.name || !metadata.version) {
199
- throw new Error(`npm registry returned an incomplete identity for ${name}@${version}`);
200
- }
201
- let contentDigest: string | undefined;
202
- if (metadata.dist?.tarball) {
203
- const archive = await fetch(metadata.dist.tarball);
204
- if (!archive.ok) {
205
- throw new Error(`npm tarball lookup failed for ${name}@${version}: ${archive.status}`);
206
- }
207
- const temp = mkdtempSync(join(tmpdir(), "projen-published-npm-"));
208
- const path = join(temp, "package.tgz");
209
- try {
210
- writeFileSync(path, Buffer.from(await archive.arrayBuffer()));
211
- contentDigest = npmArchiveContentDigest(path);
212
- } finally {
213
- rmSync(temp, { recursive: true, force: true });
214
- }
215
- }
216
- return {
217
- ...(contentDigest ? { contentDigest } : {}),
218
- integrity: metadata.dist?.integrity,
219
- name: metadata.name,
220
- repository: metadata.repository,
221
- version: metadata.version,
222
- };
223
- }
224
-
225
- function readNpmArchiveManifest(path: string): NpmArchiveManifest {
226
- const result = exec.spawnSync("tar", ["-xOf", path, "package/package.json"], {
227
- cwd: process.cwd(),
228
- stdout: "capture",
229
- stderr: "capture",
230
- stdin: "ignore",
231
- check: false,
232
- });
233
- if (result.exitCode !== 0 || !result.stdout) {
234
- throw new Error(`Cannot read npm package manifest from ${path}: ${result.stderr}`);
235
- }
236
- return JSON.parse(result.stdout) as NpmArchiveManifest;
237
- }
238
-
239
- /** Read and hash the release identity embedded in an npm archive. */
240
- export function readNpmArchiveIdentity(path: string): NpmReleaseIdentity {
241
- const manifest = readNpmArchiveManifest(path);
242
- if (!manifest.name || !manifest.version) {
243
- throw new Error(`npm archive has no package name or version: ${path}`);
244
- }
245
- const access = manifest.publishConfig?.access;
246
- if (access !== undefined && access !== "public" && access !== "restricted") {
247
- throw new Error(`npm archive has invalid publishConfig.access: ${String(access)}`);
248
- }
249
- return {
250
- ...(access ? { access } : {}),
251
- contentDigest: npmArchiveContentDigest(path),
252
- integrity: `sha512-${createHash("sha512").update(readFileSync(path)).digest("base64")}`,
253
- name: manifest.name,
254
- repository: manifest.repository,
255
- version: manifest.version,
256
- };
257
- }
258
-
259
- function orderNpmArchives(archives: readonly string[]): string[] {
260
- const byName = new Map(
261
- archives.map((archive) => {
262
- const manifest = readNpmArchiveManifest(archive);
263
- if (!manifest.name) throw new Error(`npm archive has no package name: ${archive}`);
264
- return [manifest.name, { archive, manifest }] as const;
265
- }),
266
- );
267
- if (byName.size !== archives.length) throw new Error("Release contains duplicate npm packages");
268
- const remaining = new Map(
269
- [...byName].map(([name, value]) => [
270
- name,
271
- new Set(
272
- [
273
- ...Object.keys(value.manifest.dependencies ?? {}),
274
- ...Object.keys(value.manifest.optionalDependencies ?? {}),
275
- ].filter((dependency) => byName.has(dependency)),
276
- ),
277
- ]),
278
- );
279
- const ordered: string[] = [];
280
- while (remaining.size > 0) {
281
- const ready = [...remaining]
282
- .filter(([, dependencies]) => dependencies.size === 0)
283
- .map(([name]) => name)
284
- .sort();
285
- if (ready.length === 0) {
286
- throw new Error(
287
- `Cyclic npm release dependencies: ${[...remaining.keys()].sort().join(", ")}`,
288
- );
289
- }
290
- for (const name of ready) {
291
- ordered.push(byName.get(name)!.archive);
292
- remaining.delete(name);
293
- }
294
- for (const dependencies of remaining.values()) {
295
- for (const name of ready) dependencies.delete(name);
296
- }
297
- }
298
- return ordered;
299
- }
300
-
301
- /** Hash paths, executable bits, symlink targets, and bytes while ignoring tar metadata. */
302
- export function npmArchiveContentDigest(path: string): string {
303
- const temp = mkdtempSync(join(tmpdir(), "projen-npm-content-"));
304
- try {
305
- exec.spawnSync("tar", ["-xzf", path, "-C", temp], {
306
- cwd: process.cwd(),
307
- stdout: "ignore",
308
- stderr: "capture",
309
- stdin: "ignore",
310
- check: true,
311
- });
312
- const hash = createHash("sha512");
313
- const visit = (directory: string): void => {
314
- for (const entry of readdirSync(directory, { withFileTypes: true }).sort((a, b) =>
315
- a.name.localeCompare(b.name),
316
- )) {
317
- const entryPath = join(directory, entry.name);
318
- const name = relative(temp, entryPath).split("\\").join("/");
319
- const stat = lstatSync(entryPath);
320
- hash.update(`${entry.isDirectory() ? "D" : entry.isSymbolicLink() ? "L" : "F"}\0`);
321
- hash.update(`${name}\0${stat.mode & 0o111}\0`);
322
- if (entry.isDirectory()) visit(entryPath);
323
- else if (entry.isSymbolicLink()) hash.update(readlinkSync(entryPath));
324
- else hash.update(readFileSync(entryPath));
325
- }
326
- };
327
- visit(temp);
328
- return `sha512-${hash.digest("base64")}`;
329
- } finally {
330
- rmSync(temp, { recursive: true, force: true });
331
- }
332
- }
333
-
334
- /** Pack one workspace package and optionally transform only its archived manifest. */
335
- export function packNpmPackage(
336
- directory: string,
337
- destination: string,
338
- path = process.env.PATH ?? "",
339
- transformManifest?: NpmArchiveManifestTransform,
340
- ): string {
341
- const executable = process.versions.bun ? process.execPath : "bun";
342
- const manifest = join(directory, "package.json");
343
- const originalManifest = readFileSync(manifest, "utf8");
344
- const workspaceRoot = projectUtils.root(directory) ?? directory;
345
- const workspaceManifest = JSON.parse(
346
- readFileSync(join(workspaceRoot, "package.json"), "utf8"),
347
- ) as Record<string, unknown>;
348
- const packedManifest = `${JSON.stringify(
349
- materializeWorkspaceManifest(
350
- JSON.parse(originalManifest) as Record<string, unknown>,
351
- workspaceManifest,
352
- ),
353
- null,
354
- 2,
355
- )}\n`;
356
- const manifestMode = lstatSync(manifest).mode & 0o777;
357
- const restoreManifestMode = (manifestMode & 0o200) === 0;
358
- const existingArchives = new Set(readdirSync(destination));
359
- if (restoreManifestMode) chmodSync(manifest, manifestMode | 0o200);
360
- try {
361
- writeFileSync(manifest, packedManifest);
362
- exec.spawnSync(
363
- executable,
364
- ["pm", "pack", "--destination", destination, "--ignore-scripts", "--quiet"],
365
- {
366
- cwd: directory,
367
- env: { ...process.env, PATH: path },
368
- stdout: "inherit",
369
- stderr: "inherit",
370
- stdin: "ignore",
371
- check: true,
372
- },
373
- );
374
- } finally {
375
- writeFileSync(manifest, originalManifest);
376
- if (restoreManifestMode) chmodSync(manifest, manifestMode);
377
- }
378
- const archives = readdirSync(destination).filter(
379
- (file) => file.endsWith(".tgz") && !existingArchives.has(file),
380
- );
381
- if (archives.length !== 1) {
382
- throw new Error(`Expected one packed npm archive, found ${archives.length}`);
383
- }
384
- const archive = join(destination, archives[0]);
385
- if (!transformManifest) return archive;
386
-
387
- const temp = mkdtempSync(join(tmpdir(), "projen-npm-project-"));
388
- try {
389
- exec.spawnSync("tar", ["-xzf", archive, "-C", temp], {
390
- cwd: process.cwd(),
391
- stdout: "ignore",
392
- stderr: "inherit",
393
- stdin: "ignore",
394
- check: true,
395
- });
396
- const manifestPath = join(temp, "package", "package.json");
397
- const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as NpmArchiveManifest;
398
- writeFileSync(manifestPath, `${JSON.stringify(transformManifest(manifest), null, 2)}\n`);
399
- rmSync(archive, { force: true });
400
- exec.spawnSync("tar", ["-czf", archive, "-C", temp, ...archiveFileEntries(temp)], {
401
- cwd: process.cwd(),
402
- stdout: "ignore",
403
- stderr: "inherit",
404
- stdin: "ignore",
405
- check: true,
406
- });
407
- return archive;
408
- } finally {
409
- rmSync(temp, { recursive: true, force: true });
410
- }
411
- }
412
-
413
- /** Publish dependency-ordered npm archives, skipping only exact registry matches. */
414
- export async function publishNpmArchives(options: {
415
- readonly directory: string;
416
- readonly dryRun?: boolean;
417
- readonly registry?: string;
418
- readonly version?: string;
419
- }): Promise<void> {
420
- const directory = resolve(options.directory);
421
- const archives = orderNpmArchives(
422
- readdirSync(directory)
423
- .filter((file) => file.endsWith(".tgz"))
424
- .sort()
425
- .map((file) => join(directory, file)),
426
- );
427
- if (archives.length === 0) throw new Error(`No npm archives found in ${directory}`);
428
-
429
- for (const archive of archives) {
430
- const local = readNpmArchiveIdentity(archive);
431
- if (options.version && local.version !== options.version) {
432
- throw new Error(
433
- `npm archive ${archive} carries ${local.version}, expected ${options.version}`,
434
- );
435
- }
436
- if (!options.dryRun) {
437
- const published = await publishedNpmRelease(local.name, local.version, options.registry);
438
- if (npmReleaseMatches(local, published)) {
439
- logger.info(`skip published ${local.name}@${local.version}`);
440
- continue;
441
- }
442
- }
443
- runTaskCommand(process.cwd(), "npm", [
444
- "publish",
445
- archive,
446
- ...(local.access ? ["--access", local.access] : []),
447
- ...(options.registry ? ["--registry", options.registry] : []),
448
- ...(options.dryRun ? ["--dry-run"] : []),
449
- ]);
450
- }
451
- }
452
-
453
- /** Parse npm archive publication options and execute the publisher. */
454
- export async function main(): Promise<void> {
455
- const program = new Command();
456
- program
457
- .requiredOption("--directory <path>", "Directory containing npm archives")
458
- .option("--version <version>", "Expected npm release version")
459
- .option("--registry <url>", "npm registry URL")
460
- .option("--dry-run", "Validate archives without publishing")
461
- .action(
462
- (options: { directory: string; dryRun?: boolean; registry?: string; version?: string }) =>
463
- publishNpmArchives(options),
464
- );
465
- await program.parseAsync();
466
- }