tegami 1.1.1 → 1.1.3

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,22 +1,372 @@
1
1
  import { c as somePromise, i as isNodeError, n as execFailure, r as handlePluginError, s as joinPath } from "./error-BhMYq9iW.js";
2
2
  import { t as _accessExpressionAsString } from "./_accessExpressionAsString-QhbUZzwv.js";
3
3
  import { n as _validateReport, t as _createStandardSchema } from "./_createStandardSchema-BGQyz-uA.js";
4
+ import { a as parseChangelogFile, i as parsePublishLock, n as validateChangelogStore, r as validatePackageStore } from "./draft-CzUiQasJ.js";
4
5
  import { t as _assertGuard } from "./_assertGuard-BBn2NbSz.js";
5
6
  import { n as WorkspacePackage } from "./graph-BmXTJZxx.js";
6
- import { a as parseChangelogFile, i as parsePublishLock, n as validateChangelogStore, r as validatePackageStore } from "./draft-DsxZOCOb.js";
7
7
  import fs, { readFile, writeFile } from "node:fs/promises";
8
8
  import path, { join } from "node:path";
9
9
  import { x } from "tinyexec";
10
10
  import * as semver$1 from "semver";
11
- import { parse as parse$1 } from "yaml";
12
- import { glob } from "tinyglobby";
11
+ import { parseDocument } from "yaml";
13
12
  import { detect } from "package-manager-detector";
14
13
  import { tmpdir } from "node:os";
15
14
  import { intro, note, outro } from "@clack/prompts";
16
- //#region src/providers/npm/schema.ts
15
+ import { glob } from "tinyglobby";
16
+ //#region src/plans/publish.ts
17
+ async function initPublishPlan(context, options) {
18
+ let lock;
19
+ try {
20
+ lock = parsePublishLock(await fs.readFile(context.lockPath, "utf8"));
21
+ } catch {
22
+ return;
23
+ }
24
+ let data;
25
+ const packages = /* @__PURE__ */ new Map();
26
+ const changelogs = /* @__PURE__ */ new Map();
27
+ while (data = lock.read("core:changelogs")) {
28
+ const validated = validateChangelogStore(data);
29
+ if (!validated.success) continue;
30
+ const entry = validated.data;
31
+ const parsed = parseChangelogFile(entry.filename, entry.content);
32
+ if (!parsed) continue;
33
+ changelogs.set(parsed.id, parsed);
34
+ }
35
+ while (data = lock.read("core:packages")) {
36
+ const validated = validatePackageStore(data);
37
+ if (!validated.success) continue;
38
+ const parsed = validated.data;
39
+ if (!context.graph.get(parsed.id)) continue;
40
+ const pkgChangelogs = [];
41
+ for (const id of parsed.changelogIds ?? []) {
42
+ const entry = changelogs.get(id);
43
+ if (entry) pkgChangelogs.push(entry);
44
+ }
45
+ packages.set(parsed.id, {
46
+ changelogs: pkgChangelogs,
47
+ updated: parsed.updated
48
+ });
49
+ }
50
+ const plan = {
51
+ options,
52
+ changelogs,
53
+ packages
54
+ };
55
+ for (const plugin of context.plugins) await handlePluginError(plugin, "initPublishPlan", () => plugin.initPublishPlan?.call(context, {
56
+ lock,
57
+ plan
58
+ }));
59
+ return plan;
60
+ }
61
+ function resolvePublishTargets(plan) {
62
+ /** the iteration order = publish order */
63
+ const orderedMap = /* @__PURE__ */ new Map();
64
+ /** package id -> true while scanning hard wait, false while scanning optional wait */
65
+ const stack = /* @__PURE__ */ new Map();
66
+ function scan(id) {
67
+ const preflight = plan.packages.get(id)?.preflight;
68
+ if (!preflight || !preflight.shouldPublish) return;
69
+ switch (stack.get(id)) {
70
+ case true: throw new Error(`circular reference of deps: ${[...stack.keys(), id].join(" -> ")}`);
71
+ case false: return;
72
+ }
73
+ if (orderedMap.has(id)) return;
74
+ let split = false;
75
+ if (preflight.wait) {
76
+ stack.set(id, true);
77
+ split ||= preflight.wait.length > 0;
78
+ for (const dep of preflight.wait) scan(dep);
79
+ }
80
+ if (preflight.optionalWait) {
81
+ stack.set(id, false);
82
+ split ||= preflight.optionalWait.length > 0;
83
+ for (const dep of preflight.optionalWait) scan(dep);
84
+ }
85
+ stack.delete(id);
86
+ orderedMap.set(id, { split });
87
+ }
88
+ for (const id of plan.packages.keys()) scan(id);
89
+ return orderedMap;
90
+ }
91
+ async function runPublishPlan(context, plan) {
92
+ const { dryRun = false, unstable_maxChunk = 5 } = plan.options;
93
+ for (const plugin of context.plugins) await handlePluginError(plugin, "beforePublishAll", () => plugin.beforePublishAll?.call(context, { plan }));
94
+ async function publish(pkg) {
95
+ if (dryRun) return { type: "published" };
96
+ try {
97
+ for (const plugin of context.plugins) if (await handlePluginError(plugin, "willPublish", () => plugin.willPublish?.call(context, { pkg })) === false) return { type: "skipped" };
98
+ for (const plugin of context.plugins) {
99
+ const publishResult = await handlePluginError(plugin, "publish", () => plugin.publish?.call(context, {
100
+ pkg,
101
+ plan
102
+ }));
103
+ if (publishResult) return publishResult;
104
+ }
105
+ } catch (e) {
106
+ return {
107
+ type: "failed",
108
+ error: e instanceof Error ? e.message : String(e)
109
+ };
110
+ }
111
+ return {
112
+ type: "failed",
113
+ error: `There is no plugin to publish package "${pkg.id}", please make sure the package has a supported provider plugin.`
114
+ };
115
+ }
116
+ let promises = [];
117
+ for (const [id, { split }] of resolvePublishTargets(plan)) {
118
+ const pkg = context.graph.get(id);
119
+ const packagePlan = plan.packages.get(pkg.id);
120
+ if (!pkg || !packagePlan) continue;
121
+ if (split || promises.length >= unstable_maxChunk) {
122
+ await Promise.all(promises);
123
+ promises = [];
124
+ }
125
+ promises.push(publish(pkg).then(async (result) => {
126
+ packagePlan.publishResult = result;
127
+ if (result.type === "skipped") return;
128
+ for (const plugin of context.plugins) await handlePluginError(plugin, "afterPublish", () => plugin.afterPublish?.call(context, {
129
+ pkg,
130
+ plan
131
+ }));
132
+ }));
133
+ }
134
+ await Promise.all(promises);
135
+ for (const packagePlan of plan.packages.values()) packagePlan.publishResult ??= { type: "skipped" };
136
+ for (const plugin of context.plugins) await handlePluginError(plugin, "afterPublishAll", () => plugin.afterPublishAll?.call(context, { plan }));
137
+ }
138
+ async function runPreflights(context, plan) {
139
+ const { graph } = context;
140
+ await Promise.all(Array.from(plan.packages, async ([id, packagePlan]) => {
141
+ const pkg = graph.get(id);
142
+ packagePlan.preflight = { shouldPublish: false };
143
+ if (!packagePlan.updated) return;
144
+ for (const plugin of context.plugins) {
145
+ const res = await handlePluginError(plugin, "publishPreflight", () => plugin.publishPreflight?.call(context, {
146
+ pkg,
147
+ plan
148
+ }));
149
+ if (res) {
150
+ packagePlan.preflight = res;
151
+ break;
152
+ }
153
+ }
154
+ }));
155
+ if (plan.options.packages?.length) {
156
+ const only = /* @__PURE__ */ new Set();
157
+ function addOnly(id) {
158
+ if (only.has(id)) return;
159
+ const preflight = plan.packages.get(id)?.preflight;
160
+ if (!preflight) return;
161
+ only.add(id);
162
+ if (preflight.wait) for (const dep of preflight.wait) addOnly(dep);
163
+ if (preflight.optionalWait) for (const dep of preflight.optionalWait) addOnly(dep);
164
+ }
165
+ for (const name of plan.options.packages) for (const pkg of graph.getByName(name)) addOnly(pkg.id);
166
+ for (const [id, packagePlan] of plan.packages) {
167
+ if (only.has(id)) continue;
168
+ packagePlan.preflight.shouldPublish = false;
169
+ }
170
+ }
171
+ for (const plugin of context.plugins) await handlePluginError(plugin, "afterPreflight", () => plugin.afterPreflight?.call(context, { plan }));
172
+ }
173
+ async function publishPlanStatus(plan, context) {
174
+ for (const plugin of context.plugins) {
175
+ const status = await handlePluginError(plugin, "resolvePlanStatus", () => plugin.resolvePlanStatus?.call(context, { plan }));
176
+ if (Array.isArray(status) && await somePromise(status, (v) => v === "pending")) return "pending";
177
+ if (status === "pending") return "pending";
178
+ }
179
+ return "success";
180
+ }
181
+ //#endregion
182
+ //#region src/providers/npm/cli.ts
183
+ const PLACEHOLDER_VERSION = "0.0.0-tegami-trusted-publish-setup";
184
+ const PLACEHOLDER_DIST_TAG = "temp";
185
+ const PROJECT_FLAG = {
186
+ gitlab: "--project",
187
+ github: "--repo"
188
+ };
189
+ function registerNpmCli(cli, options) {
190
+ cli.command("npm pretrust", { description: "publish empty placeholder packages and configure npm trusted publishing for new packages" }).option("dry-run", {
191
+ type: "boolean",
192
+ description: "show packages that would be configured without publishing"
193
+ }).action(async ({ context, values }) => {
194
+ intro("npm pretrust");
195
+ if (!context.graph.getPackages().some((pkg) => pkg instanceof NpmPackage)) throw new Error("No npm packages found in the workspace.");
196
+ const dryRun = values["dry-run"] ?? false;
197
+ let repo;
198
+ if (options.provider === "github") {
199
+ if (!context.github?.repo) throw new Error("The GitHub plugin must be configured with `repo` specified.");
200
+ repo = context.github.repo;
201
+ } else if (options.provider === "gitlab") {
202
+ if (!context.gitlab?.repo) throw new Error("The GitLab plugin must be configured with `repo` specified.");
203
+ repo = context.gitlab.repo;
204
+ } else throw new Error(`Invalid npm trusted publishing provider: ${options.provider}`);
205
+ const targets = await resolvePretrustTargets(context);
206
+ if (targets.length === 0) {
207
+ outro("All publishable packages in the publish lock already exist on npm.");
208
+ return;
209
+ }
210
+ const prepareLines = ["Make sure to run login command first, it will publish empty packages."];
211
+ for (const pkg of targets) prepareLines.push(`${pkg.name}: will publish a placeholder under dist-tag "${PLACEHOLDER_DIST_TAG}", then configure trusted publishing.`);
212
+ note(prepareLines.join("\n"), dryRun ? "Dry run" : "Configure trusted publishing");
213
+ const lines = [];
214
+ let lock;
215
+ if (!dryRun) try {
216
+ lock = parsePublishLock(await fs.readFile(context.lockPath, "utf8"));
217
+ } catch {}
218
+ for (const pkg of targets) {
219
+ if (dryRun) {
220
+ lines.push(`would configure ${pkg.name} (placeholder ${PLACEHOLDER_VERSION}@${PLACEHOLDER_DIST_TAG})`);
221
+ continue;
222
+ }
223
+ await publishPlaceholder(pkg);
224
+ await npmTrust(context, pkg, options, repo);
225
+ lines.push(`configured ${pkg.name} (placeholder ${PLACEHOLDER_VERSION}@${PLACEHOLDER_DIST_TAG})`);
226
+ lock?.write("npm:mark-latest", { id: pkg.id });
227
+ }
228
+ if (lock) await fs.writeFile(context.lockPath, lock.serialize());
229
+ note(lines.join("\n"), "Result");
230
+ outro(dryRun ? "Dry run complete. Re-run without --dry-run to publish placeholders and configure trusted publishing." : "Trusted publishing configured. CI can now publish real package versions with OIDC.");
231
+ });
232
+ }
233
+ async function resolvePretrustTargets(context) {
234
+ const plan = await initPublishPlan(context, {});
235
+ if (!plan) throw new Error(`No publish lock found at ${context.lockPath}. Run "tegami version" before configuring trusted publishing.`);
236
+ await runPreflights(context, plan);
237
+ return (await Promise.all(Array.from(plan.packages, async ([id, { preflight }]) => {
238
+ if (!preflight?.shouldPublish) return;
239
+ const pkg = context.graph.get(id);
240
+ if (!pkg || !(pkg instanceof NpmPackage)) return;
241
+ if (await isPackageOnRegistry(pkg.name, pkg.getRegistry())) return;
242
+ return pkg;
243
+ }))).filter((pkg) => pkg !== void 0);
244
+ }
245
+ async function isPackageOnRegistry(name, registry = "https://registry.npmjs.org") {
246
+ const response = await fetch(joinPath(registry, name), { headers: { Accept: "application/json" } });
247
+ if (response.status === 404) return false;
248
+ if (!response.ok) throw new Error(`Unable to check whether ${name} exists on the npm registry${registry ? ` "${registry}"` : ""}.`);
249
+ return true;
250
+ }
251
+ async function publishPlaceholder(pkg) {
252
+ const registry = pkg.getRegistry();
253
+ const access = pkg.manifest.publishConfig?.access;
254
+ const dir = await fs.mkdtemp(join(tmpdir(), "tegami-npm-placeholder-"));
255
+ try {
256
+ await fs.writeFile(join(dir, "package.json"), `${JSON.stringify({
257
+ name: pkg.name,
258
+ version: PLACEHOLDER_VERSION,
259
+ description: "Placeholder published by Tegami for npm trusted publishing setup."
260
+ }, null, 2)}\n`);
261
+ await fs.writeFile(join(dir, "README.md"), `# Placeholder package
262
+
263
+ This empty package was published by [Tegami](https://tegami.fuma-nama.dev) to configure npm trusted publishing.
264
+
265
+ The real package contents will be published via CI with OIDC.
266
+ `);
267
+ const args = [
268
+ "publish",
269
+ "--tag",
270
+ PLACEHOLDER_DIST_TAG,
271
+ "--ignore-scripts",
272
+ "--registry",
273
+ registry
274
+ ];
275
+ if (access) args.push("--access", access);
276
+ const result = await x("npm", args, { nodeOptions: {
277
+ cwd: dir,
278
+ stdio: "inherit"
279
+ } });
280
+ if (result.exitCode !== 0) {
281
+ const hint = result.stderr.includes("EOTP") ? " Complete npm 2FA in the terminal, or publish with an OTP-capable session." : "";
282
+ throw execFailure(`Failed to publish placeholder ${pkg.name}@${PLACEHOLDER_VERSION} with dist-tag "${PLACEHOLDER_DIST_TAG}".${hint}`, result);
283
+ }
284
+ } finally {
285
+ await fs.rm(dir, {
286
+ recursive: true,
287
+ force: true
288
+ });
289
+ }
290
+ }
291
+ async function npmTrust(context, pkg, options, repo) {
292
+ const result = await x("npm", [
293
+ "trust",
294
+ options.provider,
295
+ pkg.name,
296
+ PROJECT_FLAG[options.provider],
297
+ repo,
298
+ "--file",
299
+ options.workflow,
300
+ "--allow-publish",
301
+ "-y",
302
+ "--registry",
303
+ pkg.getRegistry()
304
+ ], { nodeOptions: {
305
+ cwd: context.cwd,
306
+ stdio: "inherit"
307
+ } });
308
+ if (result.exitCode !== 0) {
309
+ const hint = result.stderr.includes("EOTP") ? " Complete npm 2FA in the terminal when prompted." : "";
310
+ throw execFailure(`Failed to configure trusted publishing for ${pkg.name}.${hint}`, result);
311
+ }
312
+ }
313
+ //#endregion
314
+ //#region src/providers/npm/graph.ts
315
+ var NpmPackage = class extends WorkspacePackage {
316
+ path;
317
+ manifest;
318
+ manager = "npm";
319
+ dependencies;
320
+ constructor(path, manifest) {
321
+ super();
322
+ this.path = path;
323
+ this.manifest = manifest;
324
+ }
325
+ get name() {
326
+ return this.manifest.name;
327
+ }
328
+ get version() {
329
+ return this.manifest.version;
330
+ }
331
+ async write() {
332
+ await writeFile(path.join(this.path, "package.json"), `${JSON.stringify(this.manifest, null, 2)}\n`);
333
+ }
334
+ initDraft() {
335
+ const defaults = super.initDraft();
336
+ defaults.npm = { distTag: this.manifest.publishConfig?.tag };
337
+ return defaults;
338
+ }
339
+ getRegistry() {
340
+ return this.manifest.publishConfig?.registry ?? "https://registry.npmjs.org";
341
+ }
342
+ configureDraft({ draft }) {
343
+ super.configureDraft({ draft });
344
+ const { distTag = this.group?.options?.npm?.distTag } = this.options.npm ?? {};
345
+ if (distTag) {
346
+ draft.npm ??= {};
347
+ draft.npm.distTag = distTag;
348
+ } else if (draft.prerelease) {
349
+ draft.npm ??= {};
350
+ draft.npm.distTag ??= draft.prerelease;
351
+ }
352
+ }
353
+ listDependencies(graph) {
354
+ return this.dependencies ??= listDependencies(graph, this);
355
+ }
356
+ };
17
357
  const assertPnpmWorkspace = (() => {
18
- const _io0 = (input) => void 0 === input.packages || Array.isArray(input.packages) && input.packages.every((elem) => "string" === typeof elem);
19
- const _ao0 = (input, _path, _exceptionable = true) => void 0 === input.packages || (Array.isArray(input.packages) || _assertGuard(_exceptionable, {
358
+ const _io0 = (input) => (void 0 === input.packages || Array.isArray(input.packages) && input.packages.every((elem) => "string" === typeof elem)) && (void 0 === input.catalog || "object" === typeof input.catalog && null !== input.catalog && false === Array.isArray(input.catalog) && _io1(input.catalog)) && (void 0 === input.catalogs || "object" === typeof input.catalogs && null !== input.catalogs && false === Array.isArray(input.catalogs) && _io2(input.catalogs));
359
+ const _io1 = (input) => Object.keys(input).every((key) => {
360
+ const value = input[key];
361
+ if (void 0 === value) return true;
362
+ return "string" === typeof value;
363
+ });
364
+ const _io2 = (input) => Object.keys(input).every((key) => {
365
+ const value = input[key];
366
+ if (void 0 === value) return true;
367
+ return "object" === typeof value && null !== value && false === Array.isArray(value) && _io1(value);
368
+ });
369
+ const _ao0 = (input, _path, _exceptionable = true) => (void 0 === input.packages || (Array.isArray(input.packages) || _assertGuard(_exceptionable, {
20
370
  method: "typia.createAssert",
21
371
  path: _path + ".packages",
22
372
  expected: "(Array<string> | undefined)",
@@ -31,7 +381,52 @@ const assertPnpmWorkspace = (() => {
31
381
  path: _path + ".packages",
32
382
  expected: "(Array<string> | undefined)",
33
383
  value: input.packages
34
- }, _errorFactory);
384
+ }, _errorFactory)) && (void 0 === input.catalog || ("object" === typeof input.catalog && null !== input.catalog && false === Array.isArray(input.catalog) || _assertGuard(_exceptionable, {
385
+ method: "typia.createAssert",
386
+ path: _path + ".catalog",
387
+ expected: "(Record<string, string> | undefined)",
388
+ value: input.catalog
389
+ }, _errorFactory)) && _ao1(input.catalog, _path + ".catalog", _exceptionable) || _assertGuard(_exceptionable, {
390
+ method: "typia.createAssert",
391
+ path: _path + ".catalog",
392
+ expected: "(Record<string, string> | undefined)",
393
+ value: input.catalog
394
+ }, _errorFactory)) && (void 0 === input.catalogs || ("object" === typeof input.catalogs && null !== input.catalogs && false === Array.isArray(input.catalogs) || _assertGuard(_exceptionable, {
395
+ method: "typia.createAssert",
396
+ path: _path + ".catalogs",
397
+ expected: "(Record<string, Record<string, string>> | undefined)",
398
+ value: input.catalogs
399
+ }, _errorFactory)) && _ao2(input.catalogs, _path + ".catalogs", _exceptionable) || _assertGuard(_exceptionable, {
400
+ method: "typia.createAssert",
401
+ path: _path + ".catalogs",
402
+ expected: "(Record<string, Record<string, string>> | undefined)",
403
+ value: input.catalogs
404
+ }, _errorFactory));
405
+ const _ao1 = (input, _path, _exceptionable = true) => false === _exceptionable || Object.keys(input).every((key) => {
406
+ const value = input[key];
407
+ if (void 0 === value) return true;
408
+ return "string" === typeof value || _assertGuard(_exceptionable, {
409
+ method: "typia.createAssert",
410
+ path: _path + _accessExpressionAsString(key),
411
+ expected: "string",
412
+ value
413
+ }, _errorFactory);
414
+ });
415
+ const _ao2 = (input, _path, _exceptionable = true) => false === _exceptionable || Object.keys(input).every((key) => {
416
+ const value = input[key];
417
+ if (void 0 === value) return true;
418
+ return ("object" === typeof value && null !== value && false === Array.isArray(value) || _assertGuard(_exceptionable, {
419
+ method: "typia.createAssert",
420
+ path: _path + _accessExpressionAsString(key),
421
+ expected: "Record<string, string>",
422
+ value
423
+ }, _errorFactory)) && _ao1(value, _path + _accessExpressionAsString(key), _exceptionable) || _assertGuard(_exceptionable, {
424
+ method: "typia.createAssert",
425
+ path: _path + _accessExpressionAsString(key),
426
+ expected: "Record<string, string>",
427
+ value
428
+ }, _errorFactory);
429
+ });
35
430
  const __is = (input) => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
36
431
  let _errorFactory;
37
432
  return (input, errorFactory) => {
@@ -53,13 +448,19 @@ const assertPnpmWorkspace = (() => {
53
448
  };
54
449
  })();
55
450
  const assertPackageManifest = (() => {
56
- const _io0 = (input) => "string" === typeof input.name && (void 0 === input.version || "string" === typeof input.version) && (void 0 === input["private"] || "boolean" === typeof input["private"]) && (void 0 === input.publishConfig || "object" === typeof input.publishConfig && null !== input.publishConfig && false === Array.isArray(input.publishConfig) && _io1(input.publishConfig)) && (void 0 === input.scripts || "object" === typeof input.scripts && null !== input.scripts && false === Array.isArray(input.scripts) && _io2(input.scripts)) && (void 0 === input.workspaces || Array.isArray(input.workspaces) && input.workspaces.every((elem) => "string" === typeof elem)) && (void 0 === input.dependencies || "object" === typeof input.dependencies && null !== input.dependencies && false === Array.isArray(input.dependencies) && _io2(input.dependencies)) && (void 0 === input.devDependencies || "object" === typeof input.devDependencies && null !== input.devDependencies && false === Array.isArray(input.devDependencies) && _io2(input.devDependencies)) && (void 0 === input.peerDependencies || "object" === typeof input.peerDependencies && null !== input.peerDependencies && false === Array.isArray(input.peerDependencies) && _io2(input.peerDependencies)) && (void 0 === input.optionalDependencies || "object" === typeof input.optionalDependencies && null !== input.optionalDependencies && false === Array.isArray(input.optionalDependencies) && _io2(input.optionalDependencies));
451
+ const _io0 = (input) => "string" === typeof input.name && (void 0 === input.version || "string" === typeof input.version) && (void 0 === input["private"] || "boolean" === typeof input["private"]) && (void 0 === input.publishConfig || "object" === typeof input.publishConfig && null !== input.publishConfig && false === Array.isArray(input.publishConfig) && _io1(input.publishConfig)) && (void 0 === input.scripts || "object" === typeof input.scripts && null !== input.scripts && false === Array.isArray(input.scripts) && _io2(input.scripts)) && null !== input.workspaces && (void 0 === input.workspaces || Array.isArray(input.workspaces) && input.workspaces.every((elem) => "string" === typeof elem) || "object" === typeof input.workspaces && null !== input.workspaces && false === Array.isArray(input.workspaces) && _io3(input.workspaces)) && (void 0 === input.catalog || "object" === typeof input.catalog && null !== input.catalog && false === Array.isArray(input.catalog) && _io2(input.catalog)) && (void 0 === input.catalogs || "object" === typeof input.catalogs && null !== input.catalogs && false === Array.isArray(input.catalogs) && _io4(input.catalogs)) && (void 0 === input.dependencies || "object" === typeof input.dependencies && null !== input.dependencies && false === Array.isArray(input.dependencies) && _io2(input.dependencies)) && (void 0 === input.devDependencies || "object" === typeof input.devDependencies && null !== input.devDependencies && false === Array.isArray(input.devDependencies) && _io2(input.devDependencies)) && (void 0 === input.peerDependencies || "object" === typeof input.peerDependencies && null !== input.peerDependencies && false === Array.isArray(input.peerDependencies) && _io2(input.peerDependencies)) && (void 0 === input.optionalDependencies || "object" === typeof input.optionalDependencies && null !== input.optionalDependencies && false === Array.isArray(input.optionalDependencies) && _io2(input.optionalDependencies));
57
452
  const _io1 = (input) => (void 0 === input.access || "public" === input.access || "restricted" === input.access) && (void 0 === input.registry || "string" === typeof input.registry) && (void 0 === input.tag || "string" === typeof input.tag);
58
453
  const _io2 = (input) => Object.keys(input).every((key) => {
59
454
  const value = input[key];
60
455
  if (void 0 === value) return true;
61
456
  return "string" === typeof value;
62
457
  });
458
+ const _io3 = (input) => (void 0 === input.packages || Array.isArray(input.packages) && input.packages.every((elem) => "string" === typeof elem)) && (void 0 === input.catalog || "object" === typeof input.catalog && null !== input.catalog && false === Array.isArray(input.catalog) && _io2(input.catalog)) && (void 0 === input.catalogs || "object" === typeof input.catalogs && null !== input.catalogs && false === Array.isArray(input.catalogs) && _io4(input.catalogs));
459
+ const _io4 = (input) => Object.keys(input).every((key) => {
460
+ const value = input[key];
461
+ if (void 0 === value) return true;
462
+ return "object" === typeof value && null !== value && false === Array.isArray(value) && _io2(value);
463
+ });
63
464
  const _ao0 = (input, _path, _exceptionable = true) => ("string" === typeof input.name || _assertGuard(_exceptionable, {
64
465
  method: "typia.createAssert",
65
466
  path: _path + ".name",
@@ -95,21 +496,46 @@ const assertPackageManifest = (() => {
95
496
  path: _path + ".scripts",
96
497
  expected: "(Record<string, string> | undefined)",
97
498
  value: input.scripts
98
- }, _errorFactory)) && (void 0 === input.workspaces || (Array.isArray(input.workspaces) || _assertGuard(_exceptionable, {
499
+ }, _errorFactory)) && (null !== input.workspaces || _assertGuard(_exceptionable, {
99
500
  method: "typia.createAssert",
100
501
  path: _path + ".workspaces",
101
- expected: "(Array<string> | undefined)",
502
+ expected: "(Array<string> | BunWorkspaces | undefined)",
102
503
  value: input.workspaces
103
- }, _errorFactory)) && input.workspaces.every((elem, _index2) => "string" === typeof elem || _assertGuard(_exceptionable, {
504
+ }, _errorFactory)) && (void 0 === input.workspaces || Array.isArray(input.workspaces) && input.workspaces.every((elem, _index3) => "string" === typeof elem || _assertGuard(_exceptionable, {
104
505
  method: "typia.createAssert",
105
- path: _path + ".workspaces[" + _index2 + "]",
506
+ path: _path + ".workspaces[" + _index3 + "]",
106
507
  expected: "string",
107
508
  value: elem
108
- }, _errorFactory)) || _assertGuard(_exceptionable, {
509
+ }, _errorFactory)) || "object" === typeof input.workspaces && null !== input.workspaces && false === Array.isArray(input.workspaces) && _ao3(input.workspaces, _path + ".workspaces", _exceptionable) || _assertGuard(_exceptionable, {
109
510
  method: "typia.createAssert",
110
511
  path: _path + ".workspaces",
111
- expected: "(Array<string> | undefined)",
512
+ expected: "(Array<string> | BunWorkspaces | undefined)",
513
+ value: input.workspaces
514
+ }, _errorFactory) || _assertGuard(_exceptionable, {
515
+ method: "typia.createAssert",
516
+ path: _path + ".workspaces",
517
+ expected: "(Array<string> | BunWorkspaces | undefined)",
112
518
  value: input.workspaces
519
+ }, _errorFactory)) && (void 0 === input.catalog || ("object" === typeof input.catalog && null !== input.catalog && false === Array.isArray(input.catalog) || _assertGuard(_exceptionable, {
520
+ method: "typia.createAssert",
521
+ path: _path + ".catalog",
522
+ expected: "(Record<string, string> | undefined)",
523
+ value: input.catalog
524
+ }, _errorFactory)) && _ao2(input.catalog, _path + ".catalog", _exceptionable) || _assertGuard(_exceptionable, {
525
+ method: "typia.createAssert",
526
+ path: _path + ".catalog",
527
+ expected: "(Record<string, string> | undefined)",
528
+ value: input.catalog
529
+ }, _errorFactory)) && (void 0 === input.catalogs || ("object" === typeof input.catalogs && null !== input.catalogs && false === Array.isArray(input.catalogs) || _assertGuard(_exceptionable, {
530
+ method: "typia.createAssert",
531
+ path: _path + ".catalogs",
532
+ expected: "(Record<string, Record<string, string>> | undefined)",
533
+ value: input.catalogs
534
+ }, _errorFactory)) && _ao4(input.catalogs, _path + ".catalogs", _exceptionable) || _assertGuard(_exceptionable, {
535
+ method: "typia.createAssert",
536
+ path: _path + ".catalogs",
537
+ expected: "(Record<string, Record<string, string>> | undefined)",
538
+ value: input.catalogs
113
539
  }, _errorFactory)) && (void 0 === input.dependencies || ("object" === typeof input.dependencies && null !== input.dependencies && false === Array.isArray(input.dependencies) || _assertGuard(_exceptionable, {
114
540
  method: "typia.createAssert",
115
541
  path: _path + ".dependencies",
@@ -177,376 +603,363 @@ const assertPackageManifest = (() => {
177
603
  value
178
604
  }, _errorFactory);
179
605
  });
180
- const __is = (input) => "object" === typeof input && null !== input && _io0(input);
181
- let _errorFactory;
182
- return (input, errorFactory) => {
183
- if (false === __is(input)) {
184
- _errorFactory = errorFactory;
185
- ((input, _path, _exceptionable = true) => ("object" === typeof input && null !== input || _assertGuard(true, {
186
- method: "typia.createAssert",
187
- path: _path + "",
188
- expected: "PackageManifest",
189
- value: input
190
- }, _errorFactory)) && _ao0(input, _path + "", true) || _assertGuard(true, {
191
- method: "typia.createAssert",
192
- path: _path + "",
193
- expected: "PackageManifest",
194
- value: input
195
- }, _errorFactory))(input, "$input", true);
196
- }
197
- return input;
198
- };
199
- })();
200
- //#endregion
201
- //#region src/plans/publish.ts
202
- async function initPublishPlan(context, options) {
203
- let lock;
204
- try {
205
- lock = parsePublishLock(await fs.readFile(context.lockPath, "utf8"));
206
- } catch {
207
- return;
208
- }
209
- let data;
210
- const packages = /* @__PURE__ */ new Map();
211
- const changelogs = /* @__PURE__ */ new Map();
212
- while (data = lock.read("core:changelogs")) {
213
- const validated = validateChangelogStore(data);
214
- if (!validated.success) continue;
215
- const entry = validated.data;
216
- const parsed = parseChangelogFile(entry.filename, entry.content);
217
- if (!parsed) continue;
218
- changelogs.set(parsed.id, parsed);
219
- }
220
- while (data = lock.read("core:packages")) {
221
- const validated = validatePackageStore(data);
222
- if (!validated.success) continue;
223
- const parsed = validated.data;
224
- if (!context.graph.get(parsed.id)) continue;
225
- const pkgChangelogs = [];
226
- for (const id of parsed.changelogIds ?? []) {
227
- const entry = changelogs.get(id);
228
- if (entry) pkgChangelogs.push(entry);
229
- }
230
- packages.set(parsed.id, {
231
- changelogs: pkgChangelogs,
232
- updated: parsed.updated
233
- });
234
- }
235
- const plan = {
236
- options,
237
- changelogs,
238
- packages
239
- };
240
- for (const plugin of context.plugins) await handlePluginError(plugin, "initPublishPlan", () => plugin.initPublishPlan?.call(context, {
241
- lock,
242
- plan
243
- }));
244
- return plan;
245
- }
246
- function resolvePublishTargets(plan) {
247
- const ordered = [];
248
- const scanned = /* @__PURE__ */ new Set();
249
- function scan(id, stack) {
250
- const preflight = plan.packages.get(id)?.preflight;
251
- if (!preflight || !preflight.shouldPublish) return;
252
- if (stack.has(id)) throw new Error(`circular reference of deps: ${[...stack, id].join(" -> ")}`);
253
- if (scanned.has(id)) return;
254
- if (preflight.wait) {
255
- stack.add(id);
256
- for (const dep of preflight.wait) scan(dep, stack);
257
- stack.delete(id);
258
- }
259
- ordered.push(id);
260
- scanned.add(id);
261
- }
262
- const stack = /* @__PURE__ */ new Set();
263
- for (const id of plan.packages.keys()) scan(id, stack);
264
- return ordered;
265
- }
266
- async function runPublishPlan(context, plan) {
267
- const { dryRun = false } = plan.options;
268
- const onPublishResult = async (pkg, publishResult) => {
269
- const packagePlan = plan.packages.get(pkg.id);
270
- if (!packagePlan) return;
271
- packagePlan.publishResult = publishResult;
272
- if (publishResult.type === "skipped") return;
273
- for (const plugin of context.plugins) await handlePluginError(plugin, "afterPublish", () => plugin.afterPublish?.call(context, {
274
- pkg,
275
- plan
276
- }));
277
- };
278
- publishLoop: for (const id of resolvePublishTargets(plan)) {
279
- const pkg = context.graph.get(id);
280
- if (dryRun) {
281
- await onPublishResult(pkg, { type: "published" });
282
- continue;
283
- }
284
- for (const plugin of context.plugins) if (await handlePluginError(plugin, "willPublish", () => plugin.willPublish?.call(context, { pkg })) === false) continue publishLoop;
285
- for (const plugin of context.plugins) {
286
- const publishResult = await handlePluginError(plugin, "publish", () => plugin.publish?.call(context, {
287
- pkg,
288
- plan
289
- }));
290
- if (publishResult) {
291
- await onPublishResult(pkg, publishResult);
292
- continue publishLoop;
293
- }
294
- }
295
- await onPublishResult(pkg, {
296
- type: "failed",
297
- error: `There is no plugin to publish package "${pkg.id}", please make sure the package has a supported provider plugin.`
298
- });
299
- }
300
- for (const packagePlan of plan.packages.values()) packagePlan.publishResult ??= { type: "skipped" };
301
- for (const plugin of context.plugins) await handlePluginError(plugin, "afterPublishAll", () => plugin.afterPublishAll?.call(context, { plan }));
302
- }
303
- async function runPreflights(context, plan) {
304
- const promises = [];
305
- for (const [id, packagePlan] of plan.packages) {
306
- const pkg = context.graph.get(id);
307
- packagePlan.preflight = { shouldPublish: false };
308
- if (!packagePlan.updated) continue;
309
- promises.push((async () => {
310
- for (const plugin of context.plugins) {
311
- const res = await handlePluginError(plugin, "publishPreflight", () => plugin.publishPreflight?.call(context, {
312
- pkg,
313
- plan
314
- }));
315
- if (res) {
316
- packagePlan.preflight = res;
317
- break;
318
- }
319
- }
320
- })());
321
- }
322
- await Promise.all(promises);
323
- for (const plugin of context.plugins) await handlePluginError(plugin, "afterPreflight", () => plugin.afterPreflight?.call(context, { plan }));
324
- }
325
- async function publishPlanStatus(plan, context) {
326
- for (const plugin of context.plugins) {
327
- const status = await handlePluginError(plugin, "resolvePlanStatus", () => plugin.resolvePlanStatus?.call(context, { plan }));
328
- if (Array.isArray(status) && await somePromise(status, (v) => v === "pending")) return "pending";
329
- if (status === "pending") return "pending";
330
- }
331
- return "success";
332
- }
333
- //#endregion
334
- //#region src/providers/npm/cli.ts
335
- const PLACEHOLDER_VERSION = "0.0.0-tegami-trusted-publish-setup";
336
- const PLACEHOLDER_DIST_TAG = "temp";
337
- const PROJECT_FLAG = {
338
- gitlab: "--project",
339
- github: "--repo"
340
- };
341
- function registerNpmCli(cli, options) {
342
- cli.command("npm pretrust", { description: "publish empty placeholder packages and configure npm trusted publishing for new packages" }).option("dry-run", {
343
- type: "boolean",
344
- description: "show packages that would be configured without publishing"
345
- }).action(async ({ context, values }) => {
346
- intro("npm pretrust");
347
- if (!context.graph.getPackages().some((pkg) => pkg instanceof NpmPackage)) throw new Error("No npm packages found in the workspace.");
348
- const dryRun = values["dry-run"] ?? false;
349
- let repo;
350
- if (options.provider === "github") {
351
- if (!context.github?.repo) throw new Error("The GitHub plugin must be configured with `repo` specified.");
352
- repo = context.github.repo;
353
- } else if (options.provider === "gitlab") {
354
- if (!context.gitlab?.repo) throw new Error("The GitLab plugin must be configured with `repo` specified.");
355
- repo = context.gitlab.repo;
356
- } else throw new Error(`Invalid npm trusted publishing provider: ${options.provider}`);
357
- const targets = await resolvePretrustTargets(context);
358
- if (targets.length === 0) {
359
- outro("All publishable packages in the publish lock already exist on npm.");
360
- return;
361
- }
362
- const prepareLines = ["Make sure to run login command first, it will publish empty packages."];
363
- for (const pkg of targets) prepareLines.push(`${pkg.name}: will publish a placeholder under dist-tag "${PLACEHOLDER_DIST_TAG}", then configure trusted publishing.`);
364
- note(prepareLines.join("\n"), dryRun ? "Dry run" : "Configure trusted publishing");
365
- const lines = [];
366
- let lock;
367
- if (!dryRun) try {
368
- lock = parsePublishLock(await fs.readFile(context.lockPath, "utf8"));
369
- } catch {}
370
- for (const pkg of targets) {
371
- if (dryRun) {
372
- lines.push(`would configure ${pkg.name} (placeholder ${PLACEHOLDER_VERSION}@${PLACEHOLDER_DIST_TAG})`);
373
- continue;
374
- }
375
- await publishPlaceholder(pkg);
376
- await npmTrust(context, pkg, options, repo);
377
- lines.push(`configured ${pkg.name} (placeholder ${PLACEHOLDER_VERSION}@${PLACEHOLDER_DIST_TAG})`);
378
- lock?.write("npm:mark-latest", { id: pkg.id });
379
- }
380
- if (lock) await fs.writeFile(context.lockPath, lock.serialize());
381
- note(lines.join("\n"), "Result");
382
- outro(dryRun ? "Dry run complete. Re-run without --dry-run to publish placeholders and configure trusted publishing." : "Trusted publishing configured. CI can now publish real package versions with OIDC.");
606
+ const _ao3 = (input, _path, _exceptionable = true) => (void 0 === input.packages || (Array.isArray(input.packages) || _assertGuard(_exceptionable, {
607
+ method: "typia.createAssert",
608
+ path: _path + ".packages",
609
+ expected: "(Array<string> | undefined)",
610
+ value: input.packages
611
+ }, _errorFactory)) && input.packages.every((elem, _index4) => "string" === typeof elem || _assertGuard(_exceptionable, {
612
+ method: "typia.createAssert",
613
+ path: _path + ".packages[" + _index4 + "]",
614
+ expected: "string",
615
+ value: elem
616
+ }, _errorFactory)) || _assertGuard(_exceptionable, {
617
+ method: "typia.createAssert",
618
+ path: _path + ".packages",
619
+ expected: "(Array<string> | undefined)",
620
+ value: input.packages
621
+ }, _errorFactory)) && (void 0 === input.catalog || ("object" === typeof input.catalog && null !== input.catalog && false === Array.isArray(input.catalog) || _assertGuard(_exceptionable, {
622
+ method: "typia.createAssert",
623
+ path: _path + ".catalog",
624
+ expected: "(Record<string, string> | undefined)",
625
+ value: input.catalog
626
+ }, _errorFactory)) && _ao2(input.catalog, _path + ".catalog", _exceptionable) || _assertGuard(_exceptionable, {
627
+ method: "typia.createAssert",
628
+ path: _path + ".catalog",
629
+ expected: "(Record<string, string> | undefined)",
630
+ value: input.catalog
631
+ }, _errorFactory)) && (void 0 === input.catalogs || ("object" === typeof input.catalogs && null !== input.catalogs && false === Array.isArray(input.catalogs) || _assertGuard(_exceptionable, {
632
+ method: "typia.createAssert",
633
+ path: _path + ".catalogs",
634
+ expected: "(Record<string, Record<string, string>> | undefined)",
635
+ value: input.catalogs
636
+ }, _errorFactory)) && _ao4(input.catalogs, _path + ".catalogs", _exceptionable) || _assertGuard(_exceptionable, {
637
+ method: "typia.createAssert",
638
+ path: _path + ".catalogs",
639
+ expected: "(Record<string, Record<string, string>> | undefined)",
640
+ value: input.catalogs
641
+ }, _errorFactory));
642
+ const _ao4 = (input, _path, _exceptionable = true) => false === _exceptionable || Object.keys(input).every((key) => {
643
+ const value = input[key];
644
+ if (void 0 === value) return true;
645
+ return ("object" === typeof value && null !== value && false === Array.isArray(value) || _assertGuard(_exceptionable, {
646
+ method: "typia.createAssert",
647
+ path: _path + _accessExpressionAsString(key),
648
+ expected: "Record<string, string>",
649
+ value
650
+ }, _errorFactory)) && _ao2(value, _path + _accessExpressionAsString(key), _exceptionable) || _assertGuard(_exceptionable, {
651
+ method: "typia.createAssert",
652
+ path: _path + _accessExpressionAsString(key),
653
+ expected: "Record<string, string>",
654
+ value
655
+ }, _errorFactory);
383
656
  });
384
- }
385
- async function resolvePretrustTargets(context) {
386
- const plan = await initPublishPlan(context, {});
387
- if (!plan) throw new Error(`No publish lock found at ${context.lockPath}. Run "tegami version" before configuring trusted publishing.`);
388
- await runPreflights(context, plan);
389
- return (await Promise.all(Array.from(plan.packages, async ([id, { preflight }]) => {
390
- if (!preflight?.shouldPublish) return;
391
- const pkg = context.graph.get(id);
392
- if (!pkg || !(pkg instanceof NpmPackage)) return;
393
- if (await isPackageOnRegistry(pkg.name, pkg.getRegistry())) return;
394
- return pkg;
395
- }))).filter((pkg) => pkg !== void 0);
396
- }
397
- async function isPackageOnRegistry(name, registry = "https://registry.npmjs.org") {
398
- const response = await fetch(joinPath(registry, name), { headers: { Accept: "application/json" } });
399
- if (response.status === 404) return false;
400
- if (!response.ok) throw new Error(`Unable to check whether ${name} exists on the npm registry${registry ? ` "${registry}"` : ""}.`);
401
- return true;
402
- }
403
- async function publishPlaceholder(pkg) {
404
- const registry = pkg.getRegistry();
405
- const access = pkg.manifest.publishConfig?.access;
406
- const dir = await fs.mkdtemp(join(tmpdir(), "tegami-npm-placeholder-"));
407
- try {
408
- await fs.writeFile(join(dir, "package.json"), `${JSON.stringify({
409
- name: pkg.name,
410
- version: PLACEHOLDER_VERSION,
411
- description: "Placeholder published by Tegami for npm trusted publishing setup."
412
- }, null, 2)}\n`);
413
- await fs.writeFile(join(dir, "README.md"), `# Placeholder package
414
-
415
- This empty package was published by [Tegami](https://tegami.fuma-nama.dev) to configure npm trusted publishing.
416
-
417
- The real package contents will be published via CI with OIDC.
418
- `);
419
- const args = [
420
- "publish",
421
- "--tag",
422
- PLACEHOLDER_DIST_TAG,
423
- "--ignore-scripts",
424
- "--registry",
425
- registry
426
- ];
427
- if (access) args.push("--access", access);
428
- const result = await x("npm", args, { nodeOptions: {
429
- cwd: dir,
430
- stdio: "inherit"
431
- } });
432
- if (result.exitCode !== 0) {
433
- const hint = result.stderr.includes("EOTP") ? " Complete npm 2FA in the terminal, or publish with an OTP-capable session." : "";
434
- throw execFailure(`Failed to publish placeholder ${pkg.name}@${PLACEHOLDER_VERSION} with dist-tag "${PLACEHOLDER_DIST_TAG}".${hint}`, result);
657
+ const __is = (input) => "object" === typeof input && null !== input && _io0(input);
658
+ let _errorFactory;
659
+ return (input, errorFactory) => {
660
+ if (false === __is(input)) {
661
+ _errorFactory = errorFactory;
662
+ ((input, _path, _exceptionable = true) => ("object" === typeof input && null !== input || _assertGuard(true, {
663
+ method: "typia.createAssert",
664
+ path: _path + "",
665
+ expected: "PackageManifest",
666
+ value: input
667
+ }, _errorFactory)) && _ao0(input, _path + "", true) || _assertGuard(true, {
668
+ method: "typia.createAssert",
669
+ path: _path + "",
670
+ expected: "PackageManifest",
671
+ value: input
672
+ }, _errorFactory))(input, "$input", true);
435
673
  }
436
- } finally {
437
- await fs.rm(dir, {
438
- recursive: true,
439
- force: true
440
- });
441
- }
442
- }
443
- async function npmTrust(context, pkg, options, repo) {
444
- const result = await x("npm", [
445
- "trust",
446
- options.provider,
447
- pkg.name,
448
- PROJECT_FLAG[options.provider],
449
- repo,
450
- "--file",
451
- options.workflow,
452
- "--allow-publish",
453
- "-y",
454
- "--registry",
455
- pkg.getRegistry()
456
- ], { nodeOptions: {
457
- cwd: context.cwd,
458
- stdio: "inherit"
459
- } });
460
- if (result.exitCode !== 0) {
461
- const hint = result.stderr.includes("EOTP") ? " Complete npm 2FA in the terminal when prompted." : "";
462
- throw execFailure(`Failed to configure trusted publishing for ${pkg.name}.${hint}`, result);
463
- }
464
- }
465
- //#endregion
466
- //#region src/providers/npm.ts
674
+ return input;
675
+ };
676
+ })();
467
677
  const DEP_FIELDS = [
468
678
  "dependencies",
469
679
  "devDependencies",
470
680
  "peerDependencies",
471
681
  "optionalDependencies"
472
682
  ];
473
- var NpmPackage = class extends WorkspacePackage {
474
- path;
475
- manifest;
476
- manager = "npm";
477
- constructor(path, manifest) {
478
- super();
479
- this.path = path;
480
- this.manifest = manifest;
683
+ function listDependencies(graph, pkg) {
684
+ const out = [];
685
+ for (const field of DEP_FIELDS) {
686
+ const table = pkg.manifest[field];
687
+ if (!table) continue;
688
+ for (const [name, raw] of Object.entries(table)) out.push(resolveDependency(graph, pkg, field, name, raw, (range) => {
689
+ table[name] = range;
690
+ }));
481
691
  }
482
- get name() {
483
- return this.manifest.name;
692
+ return out;
693
+ }
694
+ function parseDependencySpec(raw) {
695
+ if (raw.startsWith("workspace:")) {
696
+ const body = raw.slice(10);
697
+ if (!body) return {
698
+ protocol: "workspace",
699
+ range: "*"
700
+ };
701
+ if (body.startsWith(".") || body.startsWith("/")) return {
702
+ protocol: "workspace",
703
+ range: body,
704
+ path: body
705
+ };
706
+ const separator = body.lastIndexOf("@");
707
+ if (separator > 0) return {
708
+ protocol: "workspace",
709
+ packageName: body.slice(0, separator),
710
+ range: body.slice(separator + 1) || "*"
711
+ };
712
+ if (separator === 0) return {
713
+ protocol: "workspace",
714
+ packageName: body,
715
+ range: "*"
716
+ };
717
+ return {
718
+ protocol: "workspace",
719
+ range: body
720
+ };
484
721
  }
485
- get version() {
486
- return this.manifest.version;
722
+ if (raw.startsWith("file:")) return {
723
+ protocol: "file",
724
+ path: raw.slice(5)
725
+ };
726
+ if (raw.startsWith("portal:")) return {
727
+ protocol: "portal",
728
+ path: raw.slice(7)
729
+ };
730
+ if (raw === "catalog:") return {
731
+ protocol: "catalog",
732
+ catalogName: "default"
733
+ };
734
+ if (raw.startsWith("catalog:")) return {
735
+ protocol: "catalog",
736
+ catalogName: raw.slice(8).trim() || "default"
737
+ };
738
+ if (raw.startsWith("npm:")) {
739
+ const spec = raw.slice(4);
740
+ const separator = spec.lastIndexOf("@");
741
+ if (separator > 0) return {
742
+ protocol: "npm",
743
+ alias: spec.slice(0, separator),
744
+ range: spec.slice(separator + 1)
745
+ };
487
746
  }
488
- async write() {
489
- await writeFile(path.join(this.path, "package.json"), `${JSON.stringify(this.manifest, null, 2)}\n`);
747
+ return { range: raw };
748
+ }
749
+ function formatDependencySpec(spec, range) {
750
+ switch (spec.protocol) {
751
+ case "workspace":
752
+ if (spec.path) return `workspace:${spec.path}`;
753
+ if (spec.packageName) {
754
+ const next = range ?? spec.range;
755
+ return `workspace:${spec.packageName}@${next}`;
756
+ }
757
+ return `workspace:${range ?? spec.range}`;
758
+ case "file": return `file:${spec.path}`;
759
+ case "portal": return `portal:${spec.path}`;
760
+ case "catalog": return spec.catalogName === "default" ? "catalog:" : `catalog:${spec.catalogName}`;
761
+ case "npm": return `npm:${spec.alias}@${range ?? spec.range}`;
762
+ default: return range ?? spec.range;
490
763
  }
491
- initDraft() {
492
- const defaults = super.initDraft();
493
- defaults.npm = { distTag: this.manifest.publishConfig?.tag };
494
- return defaults;
764
+ }
765
+ async function resolveNpmGraph(cwd, client) {
766
+ const packages = /* @__PURE__ */ new Map();
767
+ const packagesByPath = /* @__PURE__ */ new Map();
768
+ const catalogSources = [];
769
+ function addPackage(packagePath, manifest) {
770
+ const pkg = new NpmPackage(packagePath, manifest);
771
+ packages.set(pkg.name, pkg);
772
+ packagesByPath.set(packagePath, pkg);
495
773
  }
496
- getRegistry() {
497
- return this.manifest.publishConfig?.registry ?? "https://registry.npmjs.org";
774
+ const patterns = [];
775
+ const rootManifest = await readManifest(cwd).catch(() => void 0);
776
+ if (rootManifest) {
777
+ catalogSources.push(createRootCatalogSource(rootManifest));
778
+ if (rootManifest.name) addPackage(cwd, rootManifest);
779
+ if (Array.isArray(rootManifest.workspaces)) patterns.push(...rootManifest.workspaces);
780
+ else if (rootManifest.workspaces?.packages) patterns.push(...rootManifest.workspaces.packages);
498
781
  }
499
- configureDraft({ draft }) {
500
- super.configureDraft({ draft });
501
- const { distTag = this.group?.options?.npm?.distTag } = this.options.npm ?? {};
502
- if (distTag) {
503
- draft.npm ??= {};
504
- draft.npm.distTag = distTag;
505
- } else if (draft.prerelease) {
506
- draft.npm ??= {};
507
- draft.npm.distTag ??= draft.prerelease;
782
+ if (client === "pnpm") {
783
+ const pnpmWorkspacePath = path.join(cwd, "pnpm-workspace.yaml");
784
+ const content = await readFile(pnpmWorkspacePath, "utf8").catch((error) => {
785
+ if (isNodeError(error) && error.code === "ENOENT") return void 0;
786
+ throw error;
787
+ });
788
+ if (content) {
789
+ const doc = parseDocument(content);
790
+ const data = assertPnpmWorkspace(doc.toJSON());
791
+ catalogSources.push(createPnpmCatalogSource(pnpmWorkspacePath, doc));
792
+ patterns.push(...data.packages ?? []);
508
793
  }
794
+ } else if (client === "yarn") {
795
+ const yarnCatalog = await readYarnCatalog(cwd);
796
+ if (yarnCatalog) catalogSources.push(yarnCatalog);
509
797
  }
510
- };
511
- function parseDependencySpec(context, dependent, name, range) {
512
- const { graph } = context;
513
- if (range.startsWith("workspace:")) return {
514
- range: range.slice(10),
515
- linked: graph.get(`npm:${name}`),
516
- protocol: "workspace"
798
+ if (patterns?.length) {
799
+ const candidatePaths = await expandWorkspacePatterns(cwd, patterns);
800
+ await Promise.all(candidatePaths.map(async (packagePath) => {
801
+ const manifest = await readManifest(packagePath).catch(() => void 0);
802
+ if (!manifest) return;
803
+ addPackage(packagePath, manifest);
804
+ }));
805
+ }
806
+ return {
807
+ root: cwd,
808
+ packages,
809
+ packagesByPath,
810
+ catalogs: catalogSources
517
811
  };
518
- if (range.startsWith("file:")) {
519
- let target = path.resolve(dependent.path, range.slice(5));
520
- if (path.basename(target) === "package.json") target = path.dirname(target);
521
- return {
522
- protocol: "file",
523
- raw: range,
524
- linked: graph.getPackages().find((pkg) => pkg instanceof NpmPackage && pkg.path === target)
525
- };
812
+ }
813
+ function resolveDependency(graph, dependent, field, name, raw, write) {
814
+ const spec = parseDependencySpec(raw);
815
+ const linked = resolveLinkedPackage(graph, dependent, name, spec);
816
+ let range;
817
+ let customSetRange;
818
+ switch (spec.protocol) {
819
+ case "catalog":
820
+ for (const source of graph.catalogs) {
821
+ const resolved = source.resolve(name, spec.catalogName);
822
+ if (!resolved) continue;
823
+ range = resolved;
824
+ customSetRange = (v) => source.setRange(name, spec.catalogName, v);
825
+ break;
826
+ }
827
+ break;
828
+ case "workspace":
829
+ range = semver$1.validRange(spec.range) ? spec.range : void 0;
830
+ break;
831
+ case "npm":
832
+ case void 0:
833
+ range = spec.range;
834
+ break;
526
835
  }
527
- if (range.startsWith("npm:")) {
528
- const spec = range.slice(4);
529
- const separator = spec.lastIndexOf("@");
530
- if (separator <= 0) return;
531
- const alias = spec.slice(0, separator);
532
- return {
533
- alias,
534
- linked: graph.get(`npm:${alias}`),
535
- range: spec.slice(separator + 1),
536
- protocol: "npm"
537
- };
836
+ return {
837
+ field,
838
+ name,
839
+ spec,
840
+ linked,
841
+ range,
842
+ setRange(nextRange) {
843
+ if (customSetRange) {
844
+ customSetRange(nextRange);
845
+ return;
846
+ }
847
+ write(formatDependencySpec(spec, nextRange));
848
+ }
849
+ };
850
+ }
851
+ function resolveLinkedPackage(graph, dependent, name, spec) {
852
+ switch (spec.protocol) {
853
+ case "workspace":
854
+ if (spec.path) return findPackageByPath(graph, dependent.path, spec.path);
855
+ return graph.packages.get(spec.packageName || name);
856
+ case "file":
857
+ case "portal": return findPackageByPath(graph, dependent.path, spec.path);
858
+ case "npm": return graph.packages.get(spec.alias);
859
+ default: return graph.packages.get(name);
538
860
  }
861
+ }
862
+ function findPackageByPath(graph, from, target) {
863
+ let absolute = path.resolve(from, target);
864
+ if (path.basename(absolute) === "package.json") absolute = path.dirname(absolute);
865
+ return graph.packagesByPath.get(absolute);
866
+ }
867
+ async function expandWorkspacePatterns(cwd, patterns) {
868
+ if (patterns.length === 0) return [];
869
+ return (await glob(patterns, {
870
+ absolute: true,
871
+ cwd,
872
+ ignore: ["**/node_modules/**", "**/dist/**"],
873
+ onlyDirectories: true,
874
+ onlyFiles: false
875
+ })).map((item) => item.endsWith(path.sep) ? item.slice(0, -1) : item);
876
+ }
877
+ async function readManifest(packagePath) {
878
+ const content = await readFile(path.join(packagePath, "package.json"), "utf8");
879
+ const parsed = JSON.parse(content);
880
+ assertPackageManifest(parsed);
881
+ return parsed;
882
+ }
883
+ function createPnpmCatalogSource(filePath, doc) {
884
+ return {
885
+ resolve(name, catalogName) {
886
+ const value = doc.getIn(catalogName === "default" ? ["catalog", name] : [
887
+ "catalogs",
888
+ catalogName,
889
+ name
890
+ ]);
891
+ return typeof value === "string" ? value : void 0;
892
+ },
893
+ setRange(name, catalogName, range) {
894
+ doc.setIn(catalogName === "default" ? ["catalog", name] : [
895
+ "catalogs",
896
+ catalogName,
897
+ name
898
+ ], range);
899
+ },
900
+ async write() {
901
+ const output = doc.toString();
902
+ await writeFile(filePath, output.endsWith("\n") ? output : `${output}\n`);
903
+ }
904
+ };
905
+ }
906
+ function createRootCatalogSource(manifest) {
539
907
  return {
540
- linked: graph.get(`npm:${name}`),
541
- range
908
+ resolve(name, catalogName) {
909
+ const fromWorkspaces = typeof manifest.workspaces === "object" && !Array.isArray(manifest.workspaces) ? catalogName === "default" ? manifest.workspaces.catalog?.[name] : manifest.workspaces.catalogs?.[catalogName]?.[name] : void 0;
910
+ if (fromWorkspaces) return fromWorkspaces;
911
+ if (catalogName === "default") return manifest.catalog?.[name];
912
+ return manifest.catalogs?.[catalogName]?.[name];
913
+ },
914
+ setRange(name, catalogName, range) {
915
+ if (typeof manifest.workspaces === "object" && !Array.isArray(manifest.workspaces)) if (catalogName === "default") {
916
+ manifest.workspaces.catalog ??= {};
917
+ manifest.workspaces.catalog[name] = range;
918
+ } else {
919
+ manifest.workspaces.catalogs ??= {};
920
+ manifest.workspaces.catalogs[catalogName] ??= {};
921
+ manifest.workspaces.catalogs[catalogName][name] = range;
922
+ }
923
+ else if (catalogName === "default") {
924
+ manifest.catalog ??= {};
925
+ manifest.catalog[name] = range;
926
+ } else {
927
+ manifest.catalogs ??= {};
928
+ manifest.catalogs[catalogName] ??= {};
929
+ manifest.catalogs[catalogName][name] = range;
930
+ }
931
+ }
542
932
  };
543
933
  }
544
- function formatDependencySpec(spec) {
545
- if (spec.protocol === "workspace") return `workspace:${spec.range}`;
546
- if (spec.protocol === "file") return spec.raw;
547
- if (spec.protocol === "npm") return `npm:${spec.alias}@${spec.range}`;
548
- return spec.range;
934
+ async function readYarnCatalog(cwd) {
935
+ const filePath = path.join(cwd, ".yarnrc.yml");
936
+ const content = await readFile(filePath, "utf8").catch(() => void 0);
937
+ if (!content) return;
938
+ const doc = parseDocument(content);
939
+ return {
940
+ resolve(name, catalogName) {
941
+ const value = doc.getIn(catalogName === "default" ? ["catalog", name] : [
942
+ "catalogs",
943
+ catalogName,
944
+ name
945
+ ]);
946
+ return typeof value === "string" ? value : void 0;
947
+ },
948
+ setRange(name, catalogName, range) {
949
+ doc.setIn(catalogName === "default" ? ["catalog", name] : [
950
+ "catalogs",
951
+ catalogName,
952
+ name
953
+ ], range);
954
+ },
955
+ async write() {
956
+ const output = doc.toString();
957
+ await writeFile(filePath, output.endsWith("\n") ? output : `${output}\n`);
958
+ }
959
+ };
549
960
  }
961
+ //#endregion
962
+ //#region src/providers/npm.ts
550
963
  const validateNpmPackageLock = (() => {
551
964
  const _io0 = (input) => "string" === typeof input.id && (void 0 === input.distTag || "string" === typeof input.distTag);
552
965
  const _vo0 = (input, _path, _exceptionable = true) => ["string" === typeof input.id || _report(_exceptionable, {
@@ -650,12 +1063,21 @@ function npm({ client: defaultClient, onBreakPeerDep = "set", updateLockFile = t
650
1063
  this.npm = { client };
651
1064
  },
652
1065
  async resolve() {
653
- await discoverNpmPackages(this.cwd, (pkg) => this.graph.add(pkg));
654
- active = this.graph.getPackages().some((pkg) => pkg instanceof NpmPackage);
1066
+ if (!this.npm) return;
1067
+ const graph = await resolveNpmGraph(this.cwd, this.npm.client);
1068
+ if (graph.packages.size === 0) return;
1069
+ this.npm.graph = graph;
1070
+ for (const pkg of graph.packages.values()) this.graph.add(pkg);
1071
+ active = true;
655
1072
  },
656
1073
  async publishPreflight({ pkg }) {
657
- if (!(pkg instanceof NpmPackage)) return;
658
- return { shouldPublish: pkg.version !== void 0 && pkg.manifest.private !== true };
1074
+ if (!(pkg instanceof NpmPackage) || !this.npm?.graph) return;
1075
+ const optionalWait = [];
1076
+ for (const { linked } of pkg.listDependencies(this.npm.graph)) if (linked) optionalWait.push(linked.id);
1077
+ return {
1078
+ shouldPublish: pkg.version !== void 0 && pkg.manifest.private !== true,
1079
+ optionalWait: optionalWait.length > 0 ? optionalWait : void 0
1080
+ };
659
1081
  },
660
1082
  resolvePlanStatus({ plan }) {
661
1083
  if (!active) return;
@@ -720,50 +1142,35 @@ function npm({ client: defaultClient, onBreakPeerDep = "set", updateLockFile = t
720
1142
  plan.addPolicy(depsPolicy(this, getBumpDepType));
721
1143
  },
722
1144
  async applyDraft(draft) {
723
- if (!active) return;
724
- const { graph } = this;
1145
+ if (!active || !this.npm?.graph) return;
1146
+ const npmGraph = this.npm.graph;
725
1147
  const writes = [];
726
- for (const pkg of graph.getPackages()) {
727
- if (!(pkg instanceof NpmPackage)) continue;
1148
+ for (const pkg of npmGraph.packages.values()) {
728
1149
  const bumped = draft.getPackageDraft(pkg.id)?.bumpVersion(pkg);
729
1150
  if (bumped) pkg.manifest.version = bumped;
730
1151
  }
731
- for (const pkg of graph.getPackages()) {
732
- if (!(pkg instanceof NpmPackage)) continue;
733
- for (const field of DEP_FIELDS) {
734
- const dependencies = pkg.manifest[field];
735
- if (!dependencies) continue;
736
- for (const [k, v] of Object.entries(dependencies)) {
737
- const spec = parseDependencySpec(this, pkg, k, v);
738
- if (!spec?.linked || spec.protocol === "workspace" || spec.protocol === "file") continue;
739
- if (!semver$1.validRange(spec.range)) continue;
740
- if (!spec.linked.version || semver$1.satisfies(spec.linked.version, spec.range)) continue;
741
- let updatedRange;
742
- const isPeer = field === "peerDependencies";
743
- if (isPeer && onBreakPeerDep === "ignore") continue;
744
- if (isPeer && onBreakPeerDep === "set") updatedRange = spec.linked.version;
745
- else if (isPeer && onBreakPeerDep === "error") throw new Error(`[Tegami] the version of "${spec.linked.name}" is beyond its peer dependency constraint "${v}" in package "${pkg.name}", please update the constraint to satisfy.`);
746
- else if (spec.range.startsWith("^")) updatedRange = `^${spec.linked.version}`;
747
- else if (spec.range.startsWith("~")) updatedRange = `~${spec.linked.version}`;
748
- else updatedRange = spec.linked.version;
749
- dependencies[k] = formatDependencySpec({
750
- ...spec,
751
- range: updatedRange
752
- });
753
- }
1152
+ for (const pkg of npmGraph.packages.values()) {
1153
+ for (const dep of pkg.listDependencies(npmGraph)) {
1154
+ if (!dep.linked || !dep.range || !dep.setRange) continue;
1155
+ if (!dep.linked.version || semver$1.satisfies(dep.linked.version, dep.range)) continue;
1156
+ const isPeer = dep.field === "peerDependencies";
1157
+ if (isPeer && onBreakPeerDep === "ignore") continue;
1158
+ let updatedRange;
1159
+ if (isPeer && onBreakPeerDep === "set") updatedRange = dep.linked.version;
1160
+ else if (isPeer && onBreakPeerDep === "error") throw new Error(`[Tegami] the version of "${dep.linked.name}" is beyond its peer dependency constraint "${formatDependencySpec(dep.spec)}" in package "${pkg.name}", please update the constraint to satisfy.`);
1161
+ else if (dep.range.startsWith("^")) updatedRange = `^${dep.linked.version}`;
1162
+ else if (dep.range.startsWith("~")) updatedRange = `~${dep.linked.version}`;
1163
+ else updatedRange = dep.linked.version;
1164
+ dep.setRange(updatedRange);
754
1165
  }
755
1166
  writes.push(pkg.write());
756
1167
  }
1168
+ for (const source of npmGraph.catalogs) writes.push(source.write?.());
757
1169
  await Promise.all(writes);
758
1170
  },
759
1171
  async applyCliDraft() {
760
1172
  if (!active || !updateLockFile) return;
761
- let args;
762
- if (client === "npm") args = ["ci"];
763
- else if (client === "yarn") args = ["install", "--immutable"];
764
- else if (client === "bun") args = ["install", "--frozen-lockfile"];
765
- else args = ["install", "--frozen-lockfile"];
766
- const result = await x(client, args, { nodeOptions: { cwd: this.cwd } });
1173
+ const result = await x(client, ["install"], { nodeOptions: { cwd: this.cwd } });
767
1174
  if (result.exitCode !== 0) throw execFailure("Failed to update lockfile.", result);
768
1175
  },
769
1176
  initCli(cli) {
@@ -773,42 +1180,36 @@ function npm({ client: defaultClient, onBreakPeerDep = "set", updateLockFile = t
773
1180
  };
774
1181
  }
775
1182
  function depsPolicy(context, getBumpDepType) {
776
- const { graph } = context;
1183
+ const npmGraph = context.npm?.graph;
1184
+ if (!npmGraph) throw new Error("npm graph is missing");
777
1185
  const dependentMap = /* @__PURE__ */ new Map();
778
- for (const pkg of graph.getPackages()) {
779
- if (!(pkg instanceof NpmPackage)) continue;
780
- for (const kind of DEP_FIELDS) {
781
- const dependencies = pkg.manifest[kind];
782
- if (!dependencies) continue;
783
- for (const [name, range] of Object.entries(dependencies)) {
784
- const spec = parseDependencySpec(context, pkg, name, range);
785
- if (!spec?.linked) continue;
786
- const refs = dependentMap.get(spec.linked.id);
787
- if (refs) refs.push({
788
- dependent: pkg,
789
- kind,
790
- name,
791
- spec
792
- });
793
- else dependentMap.set(spec.linked.id, [{
794
- dependent: pkg,
795
- kind,
796
- name,
797
- spec
798
- }]);
799
- }
800
- }
1186
+ for (const pkg of npmGraph.packages.values()) for (const resolved of pkg.listDependencies(npmGraph)) {
1187
+ if (!resolved.linked) continue;
1188
+ const refs = dependentMap.get(resolved.linked.id);
1189
+ const ref = {
1190
+ dependent: pkg,
1191
+ kind: resolved.field,
1192
+ name: resolved.name,
1193
+ spec: resolved.spec,
1194
+ resolved
1195
+ };
1196
+ if (refs) refs.push(ref);
1197
+ else dependentMap.set(resolved.linked.id, [ref]);
801
1198
  }
802
- function needsUpdate(spec, target) {
803
- if (spec.linked && spec.protocol === "workspace") switch (spec.range) {
804
- case "":
805
- case "*": return true;
806
- case "^":
807
- case "~": return !semver$1.satisfies(target, `${spec.range}${spec.linked.version}`);
1199
+ function needsDependencyUpdate(resolved, target) {
1200
+ if (!resolved.linked) return false;
1201
+ switch (resolved.spec.protocol) {
1202
+ case "file":
1203
+ case "portal": return true;
1204
+ case "workspace": switch (resolved.spec.range) {
1205
+ case "":
1206
+ case "*": return true;
1207
+ case "^":
1208
+ case "~": return !semver$1.satisfies(target, `${resolved.spec.range}${resolved.linked.version ?? "0.0.0"}`);
1209
+ }
808
1210
  }
809
- if (spec.linked && spec.protocol === "file") return true;
810
- if (spec.protocol === "file" || !semver$1.validRange(spec.range)) return false;
811
- return !semver$1.satisfies(target, spec.range);
1211
+ if (!resolved.range || !semver$1.validRange(resolved.range)) return false;
1212
+ return !semver$1.satisfies(target, resolved.range);
812
1213
  }
813
1214
  return {
814
1215
  id: "npm:deps",
@@ -820,7 +1221,7 @@ function depsPolicy(context, getBumpDepType) {
820
1221
  if (!bumped) return;
821
1222
  for (const dep of deps) {
822
1223
  if (pkg.group?.options.syncBump && dep.dependent.group === pkg.group) continue;
823
- if (!needsUpdate(dep.spec, bumped)) continue;
1224
+ if (!needsDependencyUpdate(dep.resolved, bumped)) continue;
824
1225
  const bumpType = getBumpDepType(dep);
825
1226
  if (bumpType === false) continue;
826
1227
  this.bumpPackage(dep.dependent, {
@@ -888,42 +1289,5 @@ async function isPackagePublished(name, version, registry) {
888
1289
  if (!response.ok) throw new Error(`Unable to validate ${name}@${version} against the npm registry${registry ? ` "${registry}"` : ""}.`);
889
1290
  return true;
890
1291
  }
891
- async function discoverNpmPackages(cwd, add) {
892
- const rootManifest = await readManifest(cwd).catch(() => void 0);
893
- const pnpmPatterns = await readFile(path.join(cwd, "pnpm-workspace.yaml"), "utf8").then((content) => assertPnpmWorkspace(parse$1(content))).catch((error) => {
894
- if (isNodeError(error) && error.code === "ENOENT") return void 0;
895
- throw error;
896
- });
897
- if (rootManifest?.name) add(new NpmPackage(cwd, rootManifest));
898
- const patterns = pnpmPatterns?.packages ?? rootManifest?.workspaces;
899
- if (!patterns || patterns.length === 0) return;
900
- const candidatePaths = await expandWorkspacePatterns(cwd, patterns);
901
- const manifests = await Promise.all(candidatePaths.map((path) => readManifest(path).then((manifest) => ({
902
- path,
903
- manifest
904
- })).catch(() => void 0)));
905
- for (const entry of manifests) {
906
- if (!entry) continue;
907
- add(new NpmPackage(entry.path, entry.manifest));
908
- }
909
- }
910
- async function expandWorkspacePatterns(cwd, patterns) {
911
- if (patterns.length === 0) return [];
912
- return (await glob(patterns, {
913
- absolute: true,
914
- cwd,
915
- ignore: ["**/node_modules/**", "**/dist/**"],
916
- onlyDirectories: true,
917
- onlyFiles: false
918
- })).map((item) => {
919
- return item.endsWith(path.sep) ? item.slice(0, -1) : item;
920
- });
921
- }
922
- async function readManifest(packagePath) {
923
- const content = await readFile(path.join(packagePath, "package.json"), "utf8");
924
- const parsed = JSON.parse(content);
925
- assertPackageManifest(parsed);
926
- return parsed;
927
- }
928
1292
  //#endregion
929
- export { runPreflights as a, publishPlanStatus as i, npm as n, runPublishPlan as o, initPublishPlan as r, NpmPackage as t };
1293
+ export { runPreflights as a, publishPlanStatus as i, NpmPackage as n, runPublishPlan as o, initPublishPlan as r, npm as t };