tegami 0.0.0

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.
@@ -0,0 +1,6 @@
1
+ import { n as LogGenerator } from "../types-DdIMewK9.mjs";
2
+
3
+ //#region src/generators/simple.d.ts
4
+ declare function simpleGenerator(): LogGenerator;
5
+ //#endregion
6
+ export { simpleGenerator };
@@ -0,0 +1,17 @@
1
+ //#region src/generators/simple.ts
2
+ function simpleGenerator() {
3
+ return { generate({ changelogs, version }) {
4
+ return [
5
+ `## ${version}`,
6
+ "",
7
+ ...changelogs.flatMap((entry) => [
8
+ `### ${entry.title}`,
9
+ "",
10
+ entry.content,
11
+ ""
12
+ ])
13
+ ].join("\n").trim();
14
+ } };
15
+ }
16
+ //#endregion
17
+ export { simpleGenerator };
@@ -0,0 +1,31 @@
1
+ import { _ as PackageOptions, a as TegamiOptions, b as PackageGraph, f as PackagePublishResult, g as DraftPlan, i as RegistryClient, m as PublishResult, n as LogGenerator, o as TegamiPlugin, p as PublishOptions, s as TegamiPluginOption, v as PackagePlan, x as WorkspacePackage } from "./types-DdIMewK9.mjs";
2
+
3
+ //#region src/changelog/create.d.ts
4
+ interface CreateChangelogOptions {
5
+ /** Start revision. Defaults to the latest reachable git tag, or all history if none exists. */
6
+ from?: string;
7
+ /** End revision. Defaults to HEAD. */
8
+ to?: string;
9
+ }
10
+ interface CreatedChangelog {
11
+ filename: string;
12
+ path: string;
13
+ packages: string[];
14
+ changes: number;
15
+ }
16
+ //#endregion
17
+ //#region src/index.d.ts
18
+ interface Tegami {
19
+ /** Create pending changelog files from git commit history. */
20
+ createChangelog(options?: CreateChangelogOptions): Promise<CreatedChangelog[]>;
21
+ /** Build an editable draft from pending changelog files. */
22
+ draft(): Promise<DraftPlan>;
23
+ /** Discover workspace packages and their dependency relationships. */
24
+ graph(): Promise<PackageGraph>;
25
+ /** Publish the current publish plan. */
26
+ publish(options?: PublishOptions): Promise<PublishResult>;
27
+ }
28
+ /** Create a Tegami project handle. */
29
+ declare function tegami(options?: TegamiOptions): Tegami;
30
+ //#endregion
31
+ export { type CreateChangelogOptions, type CreatedChangelog, type DraftPlan, type LogGenerator, type PackageGraph, type PackageOptions, type PackagePlan, type PackagePublishResult, type PublishOptions, type PublishResult, type RegistryClient, Tegami, type TegamiOptions, type TegamiPlugin, type TegamiPluginOption, type WorkspacePackage, tegami };
package/dist/index.mjs ADDED
@@ -0,0 +1,569 @@
1
+ import { r as isNodeError, t as PackageGraph } from "./workspace-B5_i21S0.mjs";
2
+ import { cargo } from "./providers/cargo.mjs";
3
+ import { a as planStoreSchema, i as changelogFrontmatterSchema, r as npm } from "./npm-BSE_dtB3.mjs";
4
+ import { simpleGenerator } from "./generators/simple.mjs";
5
+ import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
6
+ import path, { basename, dirname, join, resolve } from "node:path";
7
+ import { x } from "tinyexec";
8
+ import { inc } from "semver";
9
+ import { load } from "js-yaml";
10
+ import { fromMarkdown } from "mdast-util-from-markdown";
11
+ import { toMarkdown } from "mdast-util-to-markdown";
12
+ //#region src/changelog/create.ts
13
+ async function createChangelog(context, options = {}) {
14
+ const commits = await readConventionalCommits(context, options);
15
+ const groups = /* @__PURE__ */ new Map();
16
+ for (const commit of commits) {
17
+ const key = commit.packages.join("\0");
18
+ const group = groups.get(key);
19
+ if (group) group.push(commit);
20
+ else groups.set(key, [commit]);
21
+ }
22
+ const directory = join(context.cwd, context.changelogDir);
23
+ await mkdir(directory, { recursive: true });
24
+ const created = [];
25
+ const stamp = Date.now().toString(36);
26
+ for (const [key, changes] of groups) {
27
+ const packages = key ? key.split("\0") : [];
28
+ const filename = `changes-${stamp}-${slugify(packages.join("-") || "workspace")}.md`;
29
+ const path = join(directory, filename);
30
+ await writeFile(path, renderChangelog(packages, changes));
31
+ created.push({
32
+ filename,
33
+ path,
34
+ packages,
35
+ changes: changes.length
36
+ });
37
+ }
38
+ return created;
39
+ }
40
+ async function readConventionalCommits(context, options) {
41
+ const to = options.to ?? "HEAD";
42
+ const from = options.from ?? await latestTag(context.cwd);
43
+ const args = [
44
+ "log",
45
+ "--no-merges",
46
+ "--format=%H%x1f%s%x1f%b%x1e"
47
+ ];
48
+ if (from) args.push(`${from}..${to}`);
49
+ else if (to !== "HEAD") args.push(to);
50
+ const result = await x("git", args, { nodeOptions: { cwd: context.cwd } });
51
+ if (result.exitCode !== 0) throw new Error(`Unable to read git commits: ${commandOutput(result).trim()}`);
52
+ const changes = [];
53
+ for (const record of result.stdout.split("")) {
54
+ const [hash, subject, body = ""] = record.replace(/^\n+|\n+$/g, "").split("");
55
+ if (!hash || !subject) continue;
56
+ const change = parseConventionalCommit(context, hash, subject, body);
57
+ if (change) changes.push(change);
58
+ }
59
+ return changes;
60
+ }
61
+ async function latestTag(cwd) {
62
+ const result = await x("git", [
63
+ "describe",
64
+ "--tags",
65
+ "--abbrev=0"
66
+ ], { nodeOptions: { cwd } });
67
+ if (result.exitCode !== 0) return void 0;
68
+ return result.stdout.trim() || void 0;
69
+ }
70
+ function parseConventionalCommit(context, hash, subject, body) {
71
+ const match = /^(?<type>[a-zA-Z]+)(?:\((?<scope>[^)]+)\))?(?<breaking>!)?:\s*(?<title>.+)$/.exec(subject);
72
+ if (!match?.groups) return void 0;
73
+ const bump = commitTypeToBump(match.groups.type.toLowerCase(), Boolean(match.groups.breaking) || /^BREAKING(?:-| )CHANGE:/m.test(body));
74
+ if (!bump) return void 0;
75
+ return {
76
+ hash,
77
+ subject,
78
+ body: body.trim(),
79
+ packages: resolvePackages(context, match.groups.scope),
80
+ type: bump,
81
+ title: titleCase(match.groups.title)
82
+ };
83
+ }
84
+ function commitTypeToBump(type, breaking) {
85
+ if (breaking) return "major";
86
+ if (type === "feat") return "minor";
87
+ if (type === "fix" || type === "perf") return "patch";
88
+ }
89
+ function resolvePackages(context, scope) {
90
+ if (!scope) return [];
91
+ const packages = /* @__PURE__ */ new Set();
92
+ for (const item of scope.split(",")) {
93
+ const name = item.trim();
94
+ if (!name) continue;
95
+ if (context.graph.getByName(name).length > 0) {
96
+ packages.add(name);
97
+ continue;
98
+ }
99
+ const byShortName = context.graph.getPackages().filter((pkg) => pkg.name.split("/").at(-1) === name);
100
+ if (byShortName.length > 0) {
101
+ for (const pkg of byShortName) packages.add(pkg.name);
102
+ continue;
103
+ }
104
+ packages.add(name);
105
+ }
106
+ return Array.from(packages).sort();
107
+ }
108
+ function renderChangelog(packages, changes) {
109
+ return [
110
+ "---",
111
+ `packages: ${JSON.stringify(packages)}`,
112
+ "---",
113
+ "",
114
+ changes.map(renderChange).join("\n\n"),
115
+ ""
116
+ ].join("\n");
117
+ }
118
+ function renderChange(change) {
119
+ const heading = "#".repeat(change.type === "major" ? 1 : change.type === "minor" ? 2 : 3);
120
+ if (!change.body) return `${heading} ${change.title}`;
121
+ return `${heading} ${change.title}\n\n${change.body}`;
122
+ }
123
+ function titleCase(value) {
124
+ return value.charAt(0).toUpperCase() + value.slice(1);
125
+ }
126
+ function slugify(value) {
127
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
128
+ }
129
+ function commandOutput(result) {
130
+ return [result.stdout, result.stderr].filter(Boolean).join("\n");
131
+ }
132
+ //#endregion
133
+ //#region src/context.ts
134
+ async function createTegamiContext(options = {}) {
135
+ const cwd = resolve(options.cwd ?? process.cwd());
136
+ const graph = new PackageGraph();
137
+ const registryClients = /* @__PURE__ */ new Map();
138
+ const ctx = {
139
+ cwd,
140
+ changelogDir: options.changelogDir ?? ".tegami",
141
+ planPath: resolve(cwd, options.planPath ?? join(".tegami", "publish-plan.json")),
142
+ options,
143
+ plugins: resolvePlugins([
144
+ npm(options.npmClient),
145
+ cargo(),
146
+ ...options.plugins ?? []
147
+ ]),
148
+ graph,
149
+ getRegistryClient(pkgOrId) {
150
+ let client;
151
+ if (typeof pkgOrId === "string") client = registryClients.get(pkgOrId);
152
+ else for (const item of registryClients.values()) if (item.supports && item.supports(pkgOrId)) {
153
+ client = item;
154
+ break;
155
+ }
156
+ if (!client) {
157
+ const id = typeof pkgOrId === "string" ? pkgOrId : pkgOrId.manager;
158
+ throw new Error(`No registry client is available for ${id}.`);
159
+ }
160
+ return client;
161
+ }
162
+ };
163
+ for (const plugin of ctx.plugins) await plugin.init?.call(ctx);
164
+ for (const plugin of ctx.plugins) await plugin.resolve?.call(ctx);
165
+ for (const plugin of ctx.plugins) {
166
+ const clients = await plugin.createRegistryClient?.call(ctx);
167
+ if (!clients) continue;
168
+ for (const client of Array.isArray(clients) ? clients : [clients]) registryClients.set(client.id, client);
169
+ }
170
+ return ctx;
171
+ }
172
+ const PLUGIN_ORDER = {
173
+ pre: 0,
174
+ default: 1,
175
+ post: 2
176
+ };
177
+ function resolvePlugins(plugins = []) {
178
+ return plugins.flat(Infinity).sort((a, b) => PLUGIN_ORDER[a.enforce ?? "default"] - PLUGIN_ORDER[b.enforce ?? "default"]);
179
+ }
180
+ //#endregion
181
+ //#region src/utils/semver.ts
182
+ function maxBump(a, b) {
183
+ if (a === "major" || b === "major") return "major";
184
+ if (a === "minor" || b === "minor") return "minor";
185
+ return "patch";
186
+ }
187
+ function bumpVersion(version, type) {
188
+ const next = inc(version, type);
189
+ if (!next) throw new Error(`Invalid semver version: ${version}`);
190
+ return next;
191
+ }
192
+ //#endregion
193
+ //#region src/draft.ts
194
+ var DraftPlan = class {
195
+ changelogs;
196
+ packages;
197
+ context;
198
+ #created = false;
199
+ constructor(changelogs, packages, context) {
200
+ this.changelogs = changelogs;
201
+ this.packages = packages;
202
+ this.context = context;
203
+ }
204
+ getPackageIds() {
205
+ return Array.from(this.packages.keys());
206
+ }
207
+ getPackage(id) {
208
+ return this.packages.get(id);
209
+ }
210
+ setPackage(id, plan = {}) {
211
+ this.packages.set(id, {
212
+ ...plan,
213
+ changelogIds: plan.changelogIds ?? /* @__PURE__ */ new Set(),
214
+ publish: plan.publish ?? true,
215
+ type: plan.type ?? "patch"
216
+ });
217
+ }
218
+ deletePackage(id) {
219
+ return this.packages.delete(id);
220
+ }
221
+ getChangelogIds() {
222
+ return Array.from(this.changelogs.keys());
223
+ }
224
+ getChangelog(id) {
225
+ return this.changelogs.get(id);
226
+ }
227
+ setChangelog(id, entry) {
228
+ this.changelogs.set(id, entry);
229
+ }
230
+ deleteChangelog(id) {
231
+ return this.changelogs.delete(id);
232
+ }
233
+ /** Write the publish plan, update package versions, and consume changelog files. */
234
+ async createPublishPlan() {
235
+ this.assertEditable();
236
+ await this.assertPublishPlanFinished();
237
+ this.#created = true;
238
+ await this.applyVersionChanges();
239
+ const plan = {
240
+ id: `tegami-${Date.now().toString(36)}`,
241
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
242
+ changelogs: Object.fromEntries(Array.from(this.changelogs, ([id, entry]) => [id, {
243
+ filename: entry.filename,
244
+ subject: entry.subject,
245
+ packages: Array.from(entry.packages),
246
+ type: entry.type,
247
+ title: entry.title,
248
+ content: entry.content
249
+ }])),
250
+ packages: Object.fromEntries(this.packages)
251
+ };
252
+ await mkdir(dirname(this.context.planPath), { recursive: true });
253
+ await writeFile(this.context.planPath, planStoreSchema.encode(plan));
254
+ await this.removeConsumedChangelogs();
255
+ }
256
+ async assertPublishPlanFinished() {
257
+ const content = await readFile(this.context.planPath, "utf8").catch(() => void 0);
258
+ if (!content) return;
259
+ const parsed = planStoreSchema.safeDecode(content);
260
+ if (!parsed.success) return;
261
+ const status = await publishPlanStatus(this.context, parsed.data);
262
+ if (status.state === "success") return;
263
+ const message = `Publish plan already exists at ${this.context.planPath} and is ${status.state}. Publish it before creating a new plan.`;
264
+ throw new Error(status.error ? `${message}\n${status.error}` : message);
265
+ }
266
+ async applyVersionChanges() {
267
+ const { graph } = this.context;
268
+ const updatedPackages = /* @__PURE__ */ new Map();
269
+ const writes = [];
270
+ for (const [id, plan] of this.packages) {
271
+ const pkg = graph.get(id);
272
+ if (!pkg) continue;
273
+ updatedPackages.set(id, {
274
+ plan,
275
+ version: bumpVersion(pkg.version, plan.type)
276
+ });
277
+ }
278
+ for (const pkg of graph.getPackages()) {
279
+ const updated = updatedPackages.get(pkg.id);
280
+ for (const [id, updatedDep] of updatedPackages) {
281
+ const target = graph.get(id);
282
+ if (target) await pkg.updateDependency?.(target, updatedDep.version, this.context);
283
+ }
284
+ if (updated) {
285
+ pkg.setVersion?.(updated.version);
286
+ writes.push(this.appendChangelog(pkg, updated.plan));
287
+ }
288
+ const write = pkg.write?.();
289
+ if (write) writes.push(write);
290
+ }
291
+ await Promise.all(writes);
292
+ }
293
+ async removeConsumedChangelogs() {
294
+ const writes = [];
295
+ for (const entry of this.changelogs.values()) {
296
+ const file = path.resolve(this.context.cwd, this.context.changelogDir, entry.filename);
297
+ writes.push(rm(file, { force: true }));
298
+ }
299
+ await Promise.all(writes);
300
+ }
301
+ assertEditable() {
302
+ if (this.#created) throw new Error("This draft has already created a publish plan.");
303
+ }
304
+ async appendChangelog(pkg, plan) {
305
+ if (plan.changelogIds.size === 0) return;
306
+ const { generator = simpleGenerator() } = this.context.options;
307
+ const changelogs = [];
308
+ for (const id of plan.changelogIds) {
309
+ const entry = this.changelogs.get(id);
310
+ if (entry) changelogs.push(entry);
311
+ }
312
+ const generated = await generator.generate.call(this.context, {
313
+ packageName: pkg.name,
314
+ version: pkg.version,
315
+ changelogs
316
+ });
317
+ const path = join(pkg.path, "CHANGELOG.md");
318
+ const existing = await readFile(path, "utf8").catch(() => "");
319
+ await writeFile(path, `${generated.trim()}\n\n${existing}`.trimEnd() + "\n");
320
+ }
321
+ /** {@link createPublishPlan} but for `await using` syntax */
322
+ async [Symbol.asyncDispose]() {
323
+ return this.createPublishPlan();
324
+ }
325
+ };
326
+ async function publishPlanStatus(context, plan) {
327
+ for (const [id, pkgPlan] of Object.entries(plan.packages)) {
328
+ const pkg = context.graph.get(id);
329
+ if (!pkg || !pkgPlan.publish) continue;
330
+ if (!await context.getRegistryClient(pkg).packageVersionExists(pkg, pkg.version)) return { state: "pending" };
331
+ }
332
+ return { state: "success" };
333
+ }
334
+ function createDraftPlan(changelogs, context) {
335
+ const changelogMap = /* @__PURE__ */ new Map();
336
+ const byPackage = /* @__PURE__ */ new Map();
337
+ for (const entry of changelogs) {
338
+ changelogMap.set(entry.id, entry);
339
+ for (const requestedPackage of entry.packages) for (const pkg of context.graph.getByName(requestedPackage)) {
340
+ let entries = byPackage.get(pkg);
341
+ if (!entries) {
342
+ entries = [];
343
+ byPackage.set(pkg, entries);
344
+ }
345
+ entries.push(entry);
346
+ }
347
+ }
348
+ const packages = /* @__PURE__ */ new Map();
349
+ for (const [pkg, entries] of byPackage.entries()) {
350
+ const plan = createPackagePlan(pkg, entries, context);
351
+ if (plan) packages.set(pkg.id, plan);
352
+ }
353
+ return new DraftPlan(changelogMap, packages, context);
354
+ }
355
+ function createPackagePlan(pkg, entries, context) {
356
+ if (entries.length === 0) return null;
357
+ const packageOptions = context.options.packages?.[pkg.id] ?? context.options.packages?.[pkg.name] ?? {};
358
+ let type = entries[0].type;
359
+ const changelogIds = /* @__PURE__ */ new Set();
360
+ for (const entry of entries) {
361
+ changelogIds.add(entry.id);
362
+ type = maxBump(type, entry.type);
363
+ }
364
+ return {
365
+ type,
366
+ changelogIds,
367
+ distTag: packageOptions.distTag ?? pkg.distTag,
368
+ publish: packageOptions.publish ?? pkg.publish
369
+ };
370
+ }
371
+ //#endregion
372
+ //#region src/utils/frontmatter.ts
373
+ /**
374
+ * Inspired by https://github.com/jonschlinkert/gray-matter
375
+ */
376
+ const regex = /^---\r?\n(.+?)\r?\n---\r?\n?/s;
377
+ /**
378
+ * parse frontmatter, it supports only yaml format
379
+ */
380
+ function frontmatter(input) {
381
+ const output = {
382
+ matter: "",
383
+ data: {},
384
+ content: input
385
+ };
386
+ const match = regex.exec(input);
387
+ if (!match) return output;
388
+ output.matter = match[0];
389
+ output.content = input.slice(match[0].length);
390
+ output.data = load(match[1]) ?? {};
391
+ return output;
392
+ }
393
+ //#endregion
394
+ //#region src/changelog/parse.ts
395
+ async function readChangelogEntries(cwd, changelogDir) {
396
+ const directory = resolve(cwd, changelogDir);
397
+ const files = await readdir(directory).catch((error) => {
398
+ if (isNodeError(error) && error.code === "ENOENT") return [];
399
+ throw error;
400
+ });
401
+ const entries = [];
402
+ for (const file of files.sort()) {
403
+ if (!file.endsWith(".md")) continue;
404
+ const filePath = join(directory, file);
405
+ const content = await readFile(filePath, "utf8");
406
+ entries.push(...parseChangelogFile(filePath, content));
407
+ }
408
+ return entries;
409
+ }
410
+ /** Parse one changelog markdown file into release entries. */
411
+ function parseChangelogFile(file, content) {
412
+ const parsed = frontmatter(content);
413
+ const data = changelogFrontmatterSchema.parse(parsed.data);
414
+ const tree = fromMarkdown(parsed.content);
415
+ const entries = [];
416
+ for (const section of getHeadingSections(tree)) {
417
+ const type = headingToBump(section.heading.depth);
418
+ if (!type) continue;
419
+ const filename = basename(file);
420
+ entries.push({
421
+ id: `${filename}:${entries.length}`,
422
+ filename,
423
+ subject: data.subject,
424
+ packages: new Set(data.packages),
425
+ type,
426
+ title: headingText(section.heading),
427
+ content: sectionToMarkdown(section.children)
428
+ });
429
+ }
430
+ return entries;
431
+ }
432
+ function getHeadingSections(tree) {
433
+ const sections = [];
434
+ let current;
435
+ for (const child of tree.children) {
436
+ if (child.type === "heading") {
437
+ current = {
438
+ heading: child,
439
+ children: []
440
+ };
441
+ sections.push(current);
442
+ continue;
443
+ }
444
+ current?.children.push(child);
445
+ }
446
+ return sections;
447
+ }
448
+ function headingText(heading) {
449
+ return heading.children.map((child) => nodeText(child)).join("").trim();
450
+ }
451
+ function nodeText(node) {
452
+ if ("value" in node && typeof node.value === "string") return node.value;
453
+ if ("children" in node && Array.isArray(node.children)) return node.children.map((child) => nodeText(child)).join("");
454
+ return "";
455
+ }
456
+ function sectionToMarkdown(children) {
457
+ if (children.length === 0) return "";
458
+ return toMarkdown({
459
+ type: "root",
460
+ children
461
+ }, {
462
+ bullet: "-",
463
+ fence: "`"
464
+ }).trim();
465
+ }
466
+ function headingToBump(depth) {
467
+ if (depth === 1) return "major";
468
+ if (depth === 2) return "minor";
469
+ if (depth === 3) return "patch";
470
+ }
471
+ //#endregion
472
+ //#region src/publish.ts
473
+ async function publishFromPlan(context, plan, options) {
474
+ const packages = await publishStoredPlan(plan, context, options);
475
+ return {
476
+ planPath: context.planPath,
477
+ state: packages.some((pkg) => pkg.state === "failed") ? "failed" : "success",
478
+ packages,
479
+ _rawPlan: plan
480
+ };
481
+ }
482
+ async function publishStoredPlan(store, context, { dryRun = false }) {
483
+ const results = [];
484
+ for (const [id, plan] of Object.entries(store.packages)) {
485
+ if (!plan.publish) continue;
486
+ const pkg = context.graph.get(id);
487
+ if (!pkg) continue;
488
+ const changelogs = [];
489
+ for (const id of plan.changelogIds) {
490
+ const entry = store.changelogs[id];
491
+ if (entry) changelogs.push({
492
+ ...entry,
493
+ packages: new Set(entry.packages),
494
+ id
495
+ });
496
+ }
497
+ if (!dryRun) {
498
+ if (await context.getRegistryClient(pkg).packageVersionExists(pkg, pkg.version)) {
499
+ results.push({
500
+ id: pkg.id,
501
+ name: pkg.name,
502
+ version: pkg.version,
503
+ distTag: plan.distTag,
504
+ state: "success",
505
+ changelogs
506
+ });
507
+ continue;
508
+ }
509
+ }
510
+ try {
511
+ if (!dryRun) await context.getRegistryClient(pkg).publish(pkg, { distTag: plan.distTag });
512
+ results.push({
513
+ id: pkg.id,
514
+ name: pkg.name,
515
+ version: pkg.version,
516
+ distTag: plan.distTag,
517
+ state: "success",
518
+ changelogs
519
+ });
520
+ } catch (error) {
521
+ results.push({
522
+ id: pkg.id,
523
+ name: pkg.name,
524
+ version: pkg.version,
525
+ distTag: plan.distTag,
526
+ changelogs,
527
+ state: "failed",
528
+ error: error instanceof Error ? error.message : String(error)
529
+ });
530
+ }
531
+ }
532
+ return results;
533
+ }
534
+ //#endregion
535
+ //#region src/index.ts
536
+ /** Create a Tegami project handle. */
537
+ function tegami(options = {}) {
538
+ return {
539
+ async createChangelog(createOptions = {}) {
540
+ return createChangelog(await createTegamiContext(options), createOptions);
541
+ },
542
+ async draft() {
543
+ const context = await createTegamiContext(options);
544
+ let plan = createDraftPlan(await readChangelogEntries(context.cwd, context.changelogDir), context);
545
+ for (const plugin of context.plugins) plan = await plugin.initPlan?.call(context, plan) ?? plan;
546
+ return plan;
547
+ },
548
+ async graph() {
549
+ return (await createTegamiContext(options)).graph;
550
+ },
551
+ async publish(publishOptions = {}) {
552
+ const context = await createTegamiContext(options);
553
+ const parsed = await readFile(context.planPath, "utf8").then((content) => planStoreSchema.decode(content)).catch((error) => {
554
+ if (isNodeError(error) && error.code === "ENOENT") return void 0;
555
+ throw error;
556
+ });
557
+ if (parsed === void 0) throw new Error(`No publish plan found at ${context.planPath}.`);
558
+ let result = await publishFromPlan(context, parsed, publishOptions);
559
+ const publishCtx = {
560
+ ...context,
561
+ publishOptions
562
+ };
563
+ for (const plugin of context.plugins) result = await plugin.afterPublish?.call(publishCtx, result) ?? result;
564
+ return result;
565
+ }
566
+ };
567
+ }
568
+ //#endregion
569
+ export { tegami };