tegami 0.0.1 → 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/checks-CKBRRjgQ.js +100 -0
- package/dist/cli/{index.d.mts → index.d.ts} +2 -2
- package/dist/cli/{index.mjs → index.js} +78 -55
- package/dist/error-DBK-9uBa.js +23 -0
- package/dist/generators/{simple.d.mts → simple.d.ts} +1 -1
- package/dist/generators/{simple.mjs → simple.js} +3 -3
- package/dist/graph-CUgwuRW5.js +107 -0
- package/dist/index-Da6P6gSc.d.ts +467 -0
- package/dist/index.d.ts +2 -0
- package/dist/{index.mjs → index.js} +182 -194
- package/dist/plugins/{git.d.mts → git.d.ts} +1 -1
- package/dist/plugins/git.js +84 -0
- package/dist/plugins/{github.d.mts → github.d.ts} +23 -9
- package/dist/plugins/github.js +187 -0
- package/dist/providers/cargo.d.ts +2 -0
- package/dist/providers/{cargo.mjs → cargo.js} +110 -52
- package/dist/providers/npm.d.ts +2 -0
- package/dist/providers/npm.js +288 -0
- package/dist/schemas-Cc4h6bq5.js +41 -0
- package/dist/semver-mWK2Khi2.js +33 -0
- package/package.json +22 -22
- package/dist/context-CC36jtz1.d.mts +0 -297
- package/dist/exec-hOS852t5.mjs +0 -12
- package/dist/index-DJ7YxxdM.d.mts +0 -35
- package/dist/index.d.mts +0 -3
- package/dist/npm-BpFlXy8I.mjs +0 -281
- package/dist/plugins/git.mjs +0 -99
- package/dist/plugins/github.mjs +0 -146
- package/dist/providers/cargo.d.mts +0 -32
- package/dist/providers/npm.d.mts +0 -2
- package/dist/providers/npm.mjs +0 -2
- package/dist/semver-FSWx3_34.mjs +0 -35
- package/dist/workspace-B5_i21S0.mjs +0 -63
- /package/dist/{constants-B9qjNfvr.mjs → constants-B9qjNfvr.js} +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { n as handlePluginError } from "./error-DBK-9uBa.js";
|
|
2
|
+
import { n as jsonCodec } from "./schemas-Cc4h6bq5.js";
|
|
3
|
+
import z$1 from "zod";
|
|
4
|
+
import { readFile } from "fs/promises";
|
|
5
|
+
//#region src/plans/store.ts
|
|
6
|
+
const packagePlanStoreSchema = z$1.object({
|
|
7
|
+
type: z$1.enum([
|
|
8
|
+
"major",
|
|
9
|
+
"minor",
|
|
10
|
+
"patch"
|
|
11
|
+
]).optional(),
|
|
12
|
+
changelogIds: z$1.array(z$1.string()).optional(),
|
|
13
|
+
bumpReasons: z$1.array(z$1.string()).optional(),
|
|
14
|
+
npm: z$1.object({ distTag: z$1.string().optional() }).optional(),
|
|
15
|
+
publish: z$1.boolean()
|
|
16
|
+
});
|
|
17
|
+
/** the persisted plan data for actual publishing */
|
|
18
|
+
const planStoreSchema = jsonCodec(z$1.object({
|
|
19
|
+
id: z$1.string(),
|
|
20
|
+
createdAt: z$1.iso.datetime(),
|
|
21
|
+
version: z$1.literal("0.0.0").default("0.0.0"),
|
|
22
|
+
/** release note entries */
|
|
23
|
+
changelogs: z$1.record(z$1.string(), z$1.object({
|
|
24
|
+
filename: z$1.string(),
|
|
25
|
+
subject: z$1.string().optional(),
|
|
26
|
+
packages: z$1.array(z$1.string()),
|
|
27
|
+
type: z$1.enum([
|
|
28
|
+
"major",
|
|
29
|
+
"minor",
|
|
30
|
+
"patch"
|
|
31
|
+
]),
|
|
32
|
+
title: z$1.string(),
|
|
33
|
+
content: z$1.string()
|
|
34
|
+
})),
|
|
35
|
+
/** package id -> package info */
|
|
36
|
+
packages: z$1.record(z$1.string(), packagePlanStoreSchema)
|
|
37
|
+
}));
|
|
38
|
+
function createPlanStore(draft, context) {
|
|
39
|
+
const store = {
|
|
40
|
+
version: "0.0.0",
|
|
41
|
+
id: `tegami-${Date.now().toString(36)}`,
|
|
42
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
43
|
+
changelogs: {},
|
|
44
|
+
packages: {}
|
|
45
|
+
};
|
|
46
|
+
for (const entry of draft.getChangelogs()) store.changelogs[entry.id] = {
|
|
47
|
+
filename: entry.filename,
|
|
48
|
+
subject: entry.subject,
|
|
49
|
+
packages: Array.from(entry.packages),
|
|
50
|
+
type: entry.type,
|
|
51
|
+
title: entry.title,
|
|
52
|
+
content: entry.content
|
|
53
|
+
};
|
|
54
|
+
for (const pkg of context.graph.getPackages()) {
|
|
55
|
+
const plan = draft.getPackagePlan(pkg.id);
|
|
56
|
+
if (plan) store.packages[pkg.id] = {
|
|
57
|
+
publish: plan.publish ?? false,
|
|
58
|
+
type: plan.type,
|
|
59
|
+
npm: plan.npm,
|
|
60
|
+
changelogIds: plan.changelogs?.map((entry) => entry.id),
|
|
61
|
+
bumpReasons: plan.bumpReasons ? Array.from(plan.bumpReasons) : void 0
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return planStoreSchema.encode(store);
|
|
65
|
+
}
|
|
66
|
+
async function readPlanStore(context) {
|
|
67
|
+
try {
|
|
68
|
+
return planStoreSchema.decode(await readFile(context.planPath, "utf8"));
|
|
69
|
+
} catch {}
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/plans/checks.ts
|
|
73
|
+
async function publishPlanStatus(store, context) {
|
|
74
|
+
async function defaultStatus() {
|
|
75
|
+
try {
|
|
76
|
+
await Promise.all(Object.entries(store.packages).map(async ([id, plan]) => {
|
|
77
|
+
const pkg = context.graph.get(id);
|
|
78
|
+
if (!pkg || !plan.publish) return;
|
|
79
|
+
if (!await context.getRegistryClient(pkg).isPackagePublished(pkg)) throw "pending";
|
|
80
|
+
}));
|
|
81
|
+
return { state: "success" };
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (err === "pending") return { state: "pending" };
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let status = await defaultStatus();
|
|
88
|
+
for (const plugin of context.plugins) {
|
|
89
|
+
const resolved = await handlePluginError(plugin, "resolvePlanStatus", () => plugin.resolvePlanStatus?.call(context, status, { plan: store }));
|
|
90
|
+
if (resolved) status = resolved;
|
|
91
|
+
}
|
|
92
|
+
return status;
|
|
93
|
+
}
|
|
94
|
+
async function assertPublishPlanFinished(context) {
|
|
95
|
+
const store = await readPlanStore(context);
|
|
96
|
+
if (!store) return;
|
|
97
|
+
if ((await publishPlanStatus(store, context)).state === "pending") throw new Error(`Publish plan already exists at ${context.planPath} and is pending. Publish it before applying a new plan.`);
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { readPlanStore as i, publishPlanStatus as n, createPlanStore as r, assertPublishPlanFinished as t };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as Tegami } from "../index-DJ7YxxdM.mjs";
|
|
1
|
+
import { C as PublishResult, a as Awaitable, t as Tegami, w as DraftPlan } from "../index-Da6P6gSc.js";
|
|
3
2
|
import { Command } from "commander";
|
|
4
3
|
|
|
5
4
|
//#region src/cli/index.d.ts
|
|
6
5
|
interface TegamiCLIOptions {
|
|
6
|
+
/** create a custom draft plan, it must not be applied */
|
|
7
7
|
version?: () => Awaitable<DraftPlan>;
|
|
8
8
|
publish?: () => Awaitable<PublishResult>;
|
|
9
9
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as handlePluginError } from "../error-DBK-9uBa.js";
|
|
2
|
+
import { t as assertPublishPlanFinished } from "../checks-CKBRRjgQ.js";
|
|
3
|
+
import { t as isCI } from "../constants-B9qjNfvr.js";
|
|
2
4
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
-
import { join
|
|
5
|
+
import { join } from "node:path";
|
|
4
6
|
import { confirm, intro, isCancel, multiselect, note, outro, select, spinner, text } from "@clack/prompts";
|
|
5
7
|
import { Command } from "commander";
|
|
6
8
|
//#region src/cli/index.ts
|
|
@@ -11,24 +13,34 @@ var CancelledError = class extends Error {
|
|
|
11
13
|
};
|
|
12
14
|
function createCli(tegami, options = {}) {
|
|
13
15
|
const program = new Command();
|
|
14
|
-
program.name("tegami").description("
|
|
15
|
-
...commandOptions,
|
|
16
|
-
cli: options
|
|
17
|
-
})));
|
|
18
|
-
program.command("version").description("create a publish plan and update package versions").action((commandOptions) => runAction(tegami, () => versionPackages(tegami, {
|
|
19
|
-
...commandOptions,
|
|
20
|
-
cli: options
|
|
21
|
-
})));
|
|
22
|
-
program.command("publish").description("publish packages from the current publish plan").option("--dry-run", "validate the publish plan without publishing packages").action((commandOptions) => runAction(tegami, () => publishPackages(tegami, {
|
|
16
|
+
program.name("tegami").description("create changelogs").action((commandOptions) => runAction(tegami, () => createChangelogs(tegami, {
|
|
23
17
|
...commandOptions,
|
|
24
18
|
cli: options
|
|
25
19
|
})));
|
|
20
|
+
program.command("version").description("draft and apply a publish plan").action((commandOptions) => runAction(tegami, async () => {
|
|
21
|
+
await versionPackages(tegami, {
|
|
22
|
+
...commandOptions,
|
|
23
|
+
cli: options
|
|
24
|
+
});
|
|
25
|
+
}));
|
|
26
|
+
program.command("ci").description("version and publish packages").action(() => runAction(tegami, async () => {
|
|
27
|
+
if (await versionPackages(tegami, { cli: options })) return;
|
|
28
|
+
await publishPackages(tegami, { cli: options });
|
|
29
|
+
}));
|
|
30
|
+
program.command("publish").description("publish packages from the applied publish plan").option("--dry-run", "validate the publish plan without publishing packages").action((commandOptions) => runAction(tegami, async () => {
|
|
31
|
+
await publishPackages(tegami, {
|
|
32
|
+
...commandOptions,
|
|
33
|
+
cli: options
|
|
34
|
+
});
|
|
35
|
+
}));
|
|
36
|
+
program.command("cleanup").description("remove the publish plan after all packages have been published").action(() => runAction(tegami, () => runCleanup(tegami)));
|
|
26
37
|
return program;
|
|
27
38
|
}
|
|
28
39
|
async function createChangelogs(tegami, _options) {
|
|
40
|
+
const context = await tegami._internal.context();
|
|
41
|
+
await assertPublishPlanFinished(context);
|
|
29
42
|
intro("Create changelogs");
|
|
30
|
-
const
|
|
31
|
-
const packages = graph.getPackages();
|
|
43
|
+
const packages = context.graph.getPackages();
|
|
32
44
|
let selectedPackages = [];
|
|
33
45
|
if (isCI()) selectedPackages = [];
|
|
34
46
|
else {
|
|
@@ -92,11 +104,10 @@ async function createChangelogs(tegami, _options) {
|
|
|
92
104
|
});
|
|
93
105
|
if (isCancel(message)) throw new CancelledError();
|
|
94
106
|
const filename = changelogFilename();
|
|
95
|
-
const directory = resolve(cwd, changelogDir);
|
|
96
107
|
const s = spinner();
|
|
97
108
|
s.start("Creating changelog");
|
|
98
|
-
await mkdir(
|
|
99
|
-
await writeFile(join(
|
|
109
|
+
await mkdir(context.changelogDir, { recursive: true });
|
|
110
|
+
await writeFile(join(context.changelogDir, filename), renderManualChangelog(selectedPackages, type, message.trim()));
|
|
100
111
|
s.stop("Created changelog file");
|
|
101
112
|
note(`${filename}\n${selectedPackages.join(", ")}: ${type}`, "Created changelog");
|
|
102
113
|
outro("Changelog ready.");
|
|
@@ -104,36 +115,35 @@ async function createChangelogs(tegami, _options) {
|
|
|
104
115
|
async function versionPackages(tegami, options) {
|
|
105
116
|
intro("Version Packages");
|
|
106
117
|
const { version: customVersion } = options.cli;
|
|
118
|
+
const context = await tegami._internal.context();
|
|
107
119
|
const draft = customVersion ? await customVersion() : await tegami.draft();
|
|
108
|
-
|
|
109
|
-
|
|
120
|
+
if (!draft.canApply()) throw new Error(`The draft plan from custom "version" hook must not be applied`);
|
|
121
|
+
for (const plugin of context.plugins) await handlePluginError(plugin, "cli.publishPlanCreated", () => plugin.cli?.publishPlanCreated?.call(context, draft));
|
|
122
|
+
if (!draft.hasPending()) {
|
|
110
123
|
note("No pending changelog entries matched workspace packages.", "Nothing to version");
|
|
111
124
|
outro("No versions changed.");
|
|
112
|
-
return;
|
|
125
|
+
return false;
|
|
113
126
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
s.start("Updating package versions");
|
|
121
|
-
try {
|
|
122
|
-
await draft.createPublishPlan();
|
|
123
|
-
} catch (error) {
|
|
124
|
-
s.stop("Failed to create publish plan");
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
s.stop("Package versions updated");
|
|
127
|
+
const planEntries = [];
|
|
128
|
+
for (const pkg of context.graph.getPackages()) {
|
|
129
|
+
const plan = draft.getPackagePlan(pkg.id);
|
|
130
|
+
if (!plan?.type) continue;
|
|
131
|
+
planEntries.push(`${pkg.id}: ${plan.type} (${plan.changelogs?.length ?? 0} changelogs)`);
|
|
132
|
+
if (plan.bumpReasons) for (const reason of plan.bumpReasons) planEntries.push(` - ${reason}`);
|
|
128
133
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
note(planEntries.join("\n"), "Release plan");
|
|
135
|
+
const s = spinner();
|
|
136
|
+
s.start("Updating package versions");
|
|
137
|
+
try {
|
|
138
|
+
await draft.applyPlan();
|
|
132
139
|
} catch (error) {
|
|
133
|
-
|
|
134
|
-
throw
|
|
140
|
+
s.stop("Failed to apply publish plan");
|
|
141
|
+
throw error;
|
|
135
142
|
}
|
|
136
|
-
|
|
143
|
+
s.stop("Package versions updated");
|
|
144
|
+
for (const plugin of context.plugins) await handlePluginError(plugin, "cli.publishPlanApplied", () => plugin.cli?.publishPlanApplied?.call(context, draft));
|
|
145
|
+
outro("Publish plan applied.");
|
|
146
|
+
return true;
|
|
137
147
|
}
|
|
138
148
|
async function publishPackages(tegami, options) {
|
|
139
149
|
const dryRun = options.dryRun ?? false;
|
|
@@ -143,22 +153,41 @@ async function publishPackages(tegami, options) {
|
|
|
143
153
|
s.start(dryRun ? "Validating publish plan" : "Publishing packages");
|
|
144
154
|
const result = customPublish ? await customPublish() : await tegami.publish({ dryRun });
|
|
145
155
|
if (result.state === "skipped") {
|
|
156
|
+
const { planPath } = await tegami._internal.context();
|
|
146
157
|
s.stop(dryRun ? "No publish plan to validate" : "Nothing to publish");
|
|
147
|
-
outro(`No publishable packages were found in ${
|
|
148
|
-
return;
|
|
158
|
+
outro(`No publishable packages were found in ${planPath}.`);
|
|
159
|
+
return false;
|
|
149
160
|
}
|
|
150
161
|
s.stop(dryRun ? "Publish plan validated" : "Publish complete");
|
|
151
162
|
note(result.packages.map((pkg) => {
|
|
152
|
-
const tag = pkg.distTag ? ` (${pkg.distTag})` : "";
|
|
163
|
+
const tag = pkg.npm?.distTag ? ` (${pkg.npm.distTag})` : "";
|
|
153
164
|
const suffix = pkg.state === "failed" && pkg.error ? `: ${pkg.error}` : "";
|
|
154
165
|
return `${pkg.state === "success" ? "success" : "failed"} ${pkg.name}@${pkg.version}${tag}${suffix}`;
|
|
155
166
|
}).join("\n"), dryRun ? "Publish dry run" : "Publish result");
|
|
156
167
|
if (result.state === "failed") {
|
|
157
168
|
process.exitCode = 1;
|
|
158
169
|
outro("Some packages failed to publish.");
|
|
159
|
-
return;
|
|
170
|
+
return false;
|
|
160
171
|
}
|
|
161
172
|
outro(dryRun ? "Publish plan is valid." : "Packages published.");
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
async function runCleanup(tegami) {
|
|
176
|
+
intro("Cleanup publish plan");
|
|
177
|
+
const s = spinner();
|
|
178
|
+
s.start("Checking publish plan status");
|
|
179
|
+
const result = await tegami.cleanup();
|
|
180
|
+
const { planPath } = await tegami._internal.context();
|
|
181
|
+
s.stop(result.state === "removed" ? "Publish plan removed" : "Publish plan kept");
|
|
182
|
+
if (result.state === "removed") {
|
|
183
|
+
outro(`Removed ${planPath}.`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (result.reason === "missing") {
|
|
187
|
+
outro(`No publish plan found at ${planPath}.`);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
outro(`Publish plan at ${planPath} is still pending. Publish it before cleanup.`);
|
|
162
191
|
}
|
|
163
192
|
function renderManualChangelog(packages, type, message) {
|
|
164
193
|
const heading = "#".repeat(type === "major" ? 1 : type === "minor" ? 2 : 3);
|
|
@@ -178,21 +207,15 @@ function changelogFilename() {
|
|
|
178
207
|
async function runAction(tegami, action) {
|
|
179
208
|
try {
|
|
180
209
|
const context = await tegami._internal.context();
|
|
181
|
-
for (const plugin of context.plugins)
|
|
182
|
-
await plugin.cli?.init?.call(context);
|
|
183
|
-
} catch (error) {
|
|
184
|
-
const details = error instanceof Error ? error.message : String(error);
|
|
185
|
-
throw new Error(`Plugin "${plugin.name}" failed during cli.init:\n${details}`, { cause: error });
|
|
186
|
-
}
|
|
210
|
+
for (const plugin of context.plugins) await handlePluginError(plugin, "cli.init", () => plugin.cli?.init?.call(context));
|
|
187
211
|
await action();
|
|
188
212
|
} catch (error) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
213
|
+
if (error instanceof CancelledError) outro(error.message);
|
|
214
|
+
else {
|
|
215
|
+
note(error instanceof Error ? error.message : String(error), "Error");
|
|
216
|
+
outro("Command failed.");
|
|
193
217
|
}
|
|
194
|
-
|
|
195
|
-
outro("Command failed.");
|
|
218
|
+
process.exit(1);
|
|
196
219
|
}
|
|
197
220
|
}
|
|
198
221
|
//#endregion
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/utils/error.ts
|
|
2
|
+
function commandOutput(result) {
|
|
3
|
+
return [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
4
|
+
}
|
|
5
|
+
function execFailure(context, result) {
|
|
6
|
+
const lines = [context, `(exit ${result.exitCode})`];
|
|
7
|
+
const output = commandOutput(result);
|
|
8
|
+
if (output) lines.push(output);
|
|
9
|
+
return new Error(lines.join("\n"));
|
|
10
|
+
}
|
|
11
|
+
function isNodeError(error) {
|
|
12
|
+
return error instanceof Error && "code" in error;
|
|
13
|
+
}
|
|
14
|
+
async function handlePluginError(plugin, hookName, callback) {
|
|
15
|
+
try {
|
|
16
|
+
return await callback();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
19
|
+
throw new Error(`Plugin "${plugin.name}" failed during ${hookName}:\n${details}`, { cause: error });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { handlePluginError as n, isNodeError as r, execFailure as t };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as formatPackageVersion } from "../semver-mWK2Khi2.js";
|
|
2
2
|
//#region src/generators/simple.ts
|
|
3
3
|
function simpleGenerator() {
|
|
4
|
-
return { generate({ changelogs, version, packageName,
|
|
4
|
+
return { generate({ changelogs, version, packageName, plan }) {
|
|
5
5
|
return [
|
|
6
|
-
`## ${formatPackageVersion(packageName, version, distTag)}`,
|
|
6
|
+
`## ${formatPackageVersion(packageName, version, plan.npm?.distTag)}`,
|
|
7
7
|
"",
|
|
8
8
|
...changelogs.flatMap((entry) => [
|
|
9
9
|
`### ${entry.title}`,
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { t as bumpVersion } from "./semver-mWK2Khi2.js";
|
|
2
|
+
//#region src/graph.ts
|
|
3
|
+
/** Package discovered in the workspace. */
|
|
4
|
+
var WorkspacePackage = class {
|
|
5
|
+
get id() {
|
|
6
|
+
return `${this.manager}:${this.name}`;
|
|
7
|
+
}
|
|
8
|
+
opts = {};
|
|
9
|
+
/** note: this will only be available after package graph is resolved */
|
|
10
|
+
getPackageOptions() {
|
|
11
|
+
return this.opts;
|
|
12
|
+
}
|
|
13
|
+
setPackageOptions(options) {
|
|
14
|
+
this.opts = options;
|
|
15
|
+
}
|
|
16
|
+
/** create an empty draft plan. */
|
|
17
|
+
onPlan(_context) {
|
|
18
|
+
const { publish, prerelease, npm } = this.opts;
|
|
19
|
+
return {
|
|
20
|
+
publish,
|
|
21
|
+
prerelease,
|
|
22
|
+
npm: npm ? { distTag: npm.distTag } : void 0,
|
|
23
|
+
bumpVersion(pkg) {
|
|
24
|
+
return bumpVersion(pkg.version, this.type, this.prerelease);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/** Dependency graph for discovered workspace packages. */
|
|
30
|
+
var PackageGraph = class {
|
|
31
|
+
packages = /* @__PURE__ */ new Map();
|
|
32
|
+
groups = /* @__PURE__ */ new Map();
|
|
33
|
+
constructor(packages = []) {
|
|
34
|
+
for (const pkg of packages) this.add(pkg);
|
|
35
|
+
}
|
|
36
|
+
getPackages() {
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const pkg of this.packages.values()) out.push(pkg.value);
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
/** Get a package by exact id. */
|
|
42
|
+
get(id) {
|
|
43
|
+
return this.packages.get(id)?.value;
|
|
44
|
+
}
|
|
45
|
+
/** Get packages by id, `group:name`, or every package matching a name. */
|
|
46
|
+
getByName(nameOrId) {
|
|
47
|
+
const exact = this.packages.get(nameOrId);
|
|
48
|
+
if (exact) return [exact.value];
|
|
49
|
+
if (nameOrId.startsWith("group:")) return this.getGroup(nameOrId.slice(6))?.packages ?? [];
|
|
50
|
+
const out = [];
|
|
51
|
+
for (const { value } of this.packages.values()) if (value.name === nameOrId) out.push(value);
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
/** scan package into graph, if the package id already exists, replace the existing one in graph */
|
|
55
|
+
add(pkg) {
|
|
56
|
+
this.delete(pkg.id);
|
|
57
|
+
this.packages.set(pkg.id, { value: pkg });
|
|
58
|
+
}
|
|
59
|
+
delete(id) {
|
|
60
|
+
this.packages.delete(id);
|
|
61
|
+
for (const group of this.groups.values()) {
|
|
62
|
+
const index = group.packages.findIndex((pkg) => pkg.id === id);
|
|
63
|
+
if (index >= 0) group.packages.splice(index, 1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
getPackageGroup(pkgId) {
|
|
67
|
+
return this.packages.get(pkgId)?.group;
|
|
68
|
+
}
|
|
69
|
+
getGroups() {
|
|
70
|
+
return Array.from(this.groups.values());
|
|
71
|
+
}
|
|
72
|
+
getGroup(name) {
|
|
73
|
+
return this.groups.get(name);
|
|
74
|
+
}
|
|
75
|
+
registerGroup(name, options) {
|
|
76
|
+
const existing = this.groups.get(name);
|
|
77
|
+
if (existing) {
|
|
78
|
+
existing.options = options;
|
|
79
|
+
return existing;
|
|
80
|
+
}
|
|
81
|
+
const group = {
|
|
82
|
+
name,
|
|
83
|
+
options,
|
|
84
|
+
packages: []
|
|
85
|
+
};
|
|
86
|
+
this.groups.set(name, group);
|
|
87
|
+
return group;
|
|
88
|
+
}
|
|
89
|
+
addGroupMember(groupId, id) {
|
|
90
|
+
const group = this.groups.get(groupId);
|
|
91
|
+
const pkg = this.packages.get(id);
|
|
92
|
+
if (!group || !pkg || pkg.group) return;
|
|
93
|
+
pkg.group = group;
|
|
94
|
+
group.packages.push(pkg.value);
|
|
95
|
+
}
|
|
96
|
+
removeGroupMember(group, id) {
|
|
97
|
+
const entry = this.groups.get(group);
|
|
98
|
+
if (!entry) return;
|
|
99
|
+
const index = entry.packages.findIndex((pkg) => pkg.id === id);
|
|
100
|
+
if (index >= 0) entry.packages.splice(index, 1);
|
|
101
|
+
}
|
|
102
|
+
unregisterGroup(name) {
|
|
103
|
+
this.groups.delete(name);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
107
|
+
export { WorkspacePackage as n, PackageGraph as t };
|