@treeseed/sdk 0.8.12 → 0.8.14

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,275 @@
1
+ function declaredEnvironments(resource, manifest) {
2
+ return resource.environments && resource.environments.length > 0 ? resource.environments : manifest.environments;
3
+ }
4
+ function selectedEnvironments(resource, manifest, selected) {
5
+ return declaredEnvironments(resource, manifest).filter((environment) => selected.includes(environment));
6
+ }
7
+ function ownership(seed, resourceKey) {
8
+ return {
9
+ seed: {
10
+ name: seed.name,
11
+ resourceKey,
12
+ version: seed.version
13
+ }
14
+ };
15
+ }
16
+ function seedOwnershipMetadata(seed, resourceKey) {
17
+ return ownership(seed, resourceKey);
18
+ }
19
+ function withMetadata(seed, resourceKey, metadata) {
20
+ return {
21
+ ...metadata ?? {},
22
+ ...ownership(seed, resourceKey)
23
+ };
24
+ }
25
+ function providerLaneEnvironments(provider, lane, manifest) {
26
+ const providerEnvironments = declaredEnvironments(provider, manifest);
27
+ const laneEnvironments = lane.environments && lane.environments.length > 0 ? lane.environments : providerEnvironments;
28
+ return laneEnvironments.filter((environment) => providerEnvironments.includes(environment));
29
+ }
30
+ function normalizeSeedResources(manifest, selected) {
31
+ const resources = [];
32
+ for (const team of manifest.resources.teams) {
33
+ resources.push({
34
+ kind: "team",
35
+ key: team.key,
36
+ label: team.displayName ?? team.name ?? team.slug,
37
+ environments: selectedEnvironments(team, manifest, selected),
38
+ payload: {
39
+ slug: team.slug,
40
+ name: team.name ?? team.slug,
41
+ displayName: team.displayName ?? team.name ?? team.slug,
42
+ logoUrl: team.logoUrl ?? null,
43
+ profileSummary: team.profileSummary ?? null,
44
+ metadata: withMetadata(manifest, team.key, team.metadata)
45
+ }
46
+ });
47
+ }
48
+ for (const host of manifest.resources.repositoryHosts) {
49
+ resources.push({
50
+ kind: "repositoryHost",
51
+ key: host.key,
52
+ label: `${host.provider}/${host.name}`,
53
+ environments: selectedEnvironments(host, manifest, selected),
54
+ payload: {
55
+ teamKey: host.team,
56
+ provider: host.provider,
57
+ name: host.name,
58
+ ownership: host.ownership ?? "treeseed_managed",
59
+ accountLabel: host.accountLabel ?? null,
60
+ organizationOrOwner: host.organizationOrOwner,
61
+ defaultVisibility: host.defaultVisibility ?? "private",
62
+ softwareRepositoryNameTemplate: host.softwareRepositoryNameTemplate ?? null,
63
+ contentRepositoryNameTemplate: host.contentRepositoryNameTemplate ?? null,
64
+ branchPolicy: host.branchPolicy ?? null,
65
+ workflowPolicy: host.workflowPolicy ?? null,
66
+ allowedProjectKinds: host.allowedProjectKinds ?? null,
67
+ status: host.status ?? "active",
68
+ credentialRef: host.credentialRef ?? null,
69
+ metadata: withMetadata(manifest, host.key, host.metadata)
70
+ }
71
+ });
72
+ }
73
+ for (const project of manifest.resources.projects) {
74
+ resources.push({
75
+ kind: "project",
76
+ key: project.key,
77
+ label: `${project.team.replace(/^team:/u, "")}/${project.slug}`,
78
+ environments: selectedEnvironments(project, manifest, selected),
79
+ payload: {
80
+ teamKey: project.team,
81
+ slug: project.slug,
82
+ name: project.name,
83
+ description: project.description ?? null,
84
+ kind: project.kind ?? null,
85
+ repository: project.repository,
86
+ metadata: withMetadata(manifest, project.key, project.metadata)
87
+ }
88
+ });
89
+ }
90
+ for (const repository of manifest.resources.hubRepositories) {
91
+ resources.push({
92
+ kind: "hubRepository",
93
+ key: repository.key,
94
+ label: `${repository.project.replace(/^project:/u, "")}/${repository.role}`,
95
+ environments: selectedEnvironments(repository, manifest, selected),
96
+ payload: {
97
+ projectKey: repository.project,
98
+ repositoryHostKey: repository.repositoryHost ?? null,
99
+ role: repository.role,
100
+ provider: repository.provider,
101
+ owner: repository.owner,
102
+ name: repository.name,
103
+ gitUrl: repository.gitUrl,
104
+ defaultBranch: repository.defaultBranch ?? null,
105
+ currentBranch: repository.currentBranch ?? repository.defaultBranch ?? null,
106
+ submodulePath: repository.submodulePath ?? null,
107
+ status: repository.status ?? "active",
108
+ accessPolicy: repository.accessPolicy ?? null,
109
+ releasePolicy: repository.releasePolicy ?? null,
110
+ publishPolicy: repository.publishPolicy ?? null,
111
+ metadata: withMetadata(manifest, repository.key, repository.metadata)
112
+ }
113
+ });
114
+ }
115
+ for (const provider of manifest.resources.capacityProviders) {
116
+ resources.push({
117
+ kind: "capacityProvider",
118
+ key: provider.key,
119
+ label: provider.name,
120
+ environments: selectedEnvironments(provider, manifest, selected),
121
+ payload: {
122
+ teamKey: provider.team,
123
+ name: provider.name,
124
+ kind: provider.kind ?? null,
125
+ provider: provider.provider,
126
+ billingScope: provider.billingScope ?? null,
127
+ monthlyCreditBudget: provider.monthlyCreditBudget ?? null,
128
+ dailyCreditBudget: provider.dailyCreditBudget ?? null,
129
+ maxConcurrentWorkdays: provider.maxConcurrentWorkdays ?? null,
130
+ maxConcurrentWorkers: provider.maxConcurrentWorkers ?? null,
131
+ capacityModel: provider.capacityModel ?? null,
132
+ registration: provider.registration ?? null,
133
+ metadata: withMetadata(manifest, provider.key, provider.metadata)
134
+ }
135
+ });
136
+ for (const lane of provider.lanes ?? []) {
137
+ resources.push({
138
+ kind: "capacityLane",
139
+ key: lane.key,
140
+ label: lane.name,
141
+ parentKey: provider.key,
142
+ environments: providerLaneEnvironments(provider, lane, manifest).filter((environment) => selected.includes(environment)),
143
+ payload: {
144
+ providerKey: provider.key,
145
+ name: lane.name,
146
+ businessModel: lane.businessModel ?? null,
147
+ modelFamily: lane.modelFamily ?? null,
148
+ modelClass: lane.modelClass ?? null,
149
+ regionPolicy: lane.regionPolicy ?? null,
150
+ unit: lane.unit ?? null,
151
+ scarcityLevel: lane.scarcityLevel ?? null,
152
+ hardLimits: lane.hardLimits ?? null,
153
+ routingPolicy: lane.routingPolicy ?? null,
154
+ metadata: withMetadata(manifest, lane.key, lane.metadata)
155
+ }
156
+ });
157
+ }
158
+ }
159
+ for (const grant of manifest.resources.capacityGrants) {
160
+ resources.push({
161
+ kind: "capacityGrant",
162
+ key: grant.key,
163
+ label: `${grant.provider.replace(/^capacity-provider:/u, "")} -> ${grant.project?.replace(/^project:/u, "") ?? grant.team.replace(/^team:/u, "")}`,
164
+ environments: selectedEnvironments(grant, manifest, selected),
165
+ payload: {
166
+ providerKey: grant.provider,
167
+ laneKey: grant.lane ?? null,
168
+ teamKey: grant.team,
169
+ projectKey: grant.project ?? null,
170
+ environment: grant.environment ?? null,
171
+ grantScope: grant.grantScope ?? null,
172
+ dailyCreditLimit: grant.dailyCreditLimit ?? null,
173
+ weeklyCreditLimit: grant.weeklyCreditLimit ?? null,
174
+ monthlyCreditLimit: grant.monthlyCreditLimit ?? null,
175
+ dailyUsdLimit: grant.dailyUsdLimit ?? null,
176
+ weeklyQuotaMinutes: grant.weeklyQuotaMinutes ?? null,
177
+ monthlyProviderUnits: grant.monthlyProviderUnits ?? null,
178
+ priorityWeight: grant.priorityWeight ?? null,
179
+ overflowPolicy: grant.overflowPolicy ?? null,
180
+ state: grant.state ?? null,
181
+ metadata: withMetadata(manifest, grant.key, grant.metadata)
182
+ }
183
+ });
184
+ }
185
+ for (const policy of manifest.resources.workPolicies) {
186
+ resources.push({
187
+ kind: "workPolicy",
188
+ key: policy.key,
189
+ label: `${policy.project.replace(/^project:[^/]+\//u, "")}/${policy.environment}`,
190
+ environments: selectedEnvironments(policy, manifest, selected),
191
+ payload: {
192
+ projectKey: policy.project,
193
+ environment: policy.environment,
194
+ enabled: policy.enabled ?? true,
195
+ startCron: policy.startCron ?? "0 9 * * 1-5",
196
+ durationMinutes: policy.durationMinutes ?? 480,
197
+ maxRunners: policy.maxRunners ?? 1,
198
+ maxWorkersPerRunner: policy.maxWorkersPerRunner ?? 4,
199
+ dailyCreditBudget: policy.dailyCreditBudget ?? null,
200
+ maxQueuedTasks: policy.maxQueuedTasks ?? null,
201
+ maxQueuedCredits: policy.maxQueuedCredits ?? null,
202
+ autoscale: policy.autoscale ?? null,
203
+ creditWeights: policy.creditWeights ?? null,
204
+ metadata: withMetadata(manifest, policy.key, policy.metadata)
205
+ }
206
+ });
207
+ }
208
+ for (const product of manifest.resources.products) {
209
+ resources.push({
210
+ kind: "product",
211
+ key: product.key,
212
+ label: `${product.kind}/${product.slug}`,
213
+ environments: selectedEnvironments(product, manifest, selected),
214
+ payload: {
215
+ teamKey: product.team,
216
+ kind: product.kind,
217
+ slug: product.slug,
218
+ title: product.title,
219
+ summary: product.summary ?? null,
220
+ visibility: product.visibility ?? "private",
221
+ listingEnabled: product.listingEnabled ?? false,
222
+ offerMode: product.offerMode ?? "private",
223
+ manifestKey: product.manifestKey ?? null,
224
+ artifactKey: product.artifactKey ?? null,
225
+ searchText: product.searchText ?? null,
226
+ metadata: withMetadata(manifest, product.key, product.metadata)
227
+ }
228
+ });
229
+ }
230
+ for (const artifact of manifest.resources.catalogArtifacts) {
231
+ resources.push({
232
+ kind: "catalogArtifact",
233
+ key: artifact.key,
234
+ label: `${artifact.product.replace(/^product:/u, "")}@${artifact.version}`,
235
+ environments: selectedEnvironments(artifact, manifest, selected),
236
+ payload: {
237
+ productKey: artifact.product,
238
+ version: artifact.version,
239
+ kind: artifact.kind,
240
+ contentKey: artifact.contentKey,
241
+ manifestKey: artifact.manifestKey ?? null,
242
+ publishedAt: artifact.publishedAt ?? null,
243
+ metadata: withMetadata(manifest, artifact.key, artifact.metadata)
244
+ }
245
+ });
246
+ }
247
+ return resources;
248
+ }
249
+ function resolveSelectedSeedEnvironments(manifest, requested) {
250
+ const errors = [];
251
+ const raw = requested ? requested.split(",").map((entry) => entry.trim()).filter(Boolean) : manifest.defaultEnvironments && manifest.defaultEnvironments.length > 0 ? manifest.defaultEnvironments : ["local"];
252
+ const environments = [];
253
+ for (const environment of raw) {
254
+ if (!["local", "staging", "prod"].includes(environment)) {
255
+ errors.push(`Unknown seed environment: ${environment}.`);
256
+ continue;
257
+ }
258
+ if (!manifest.environments.includes(environment)) {
259
+ errors.push(`Seed ${manifest.name} does not declare environment: ${environment}.`);
260
+ continue;
261
+ }
262
+ if (!environments.includes(environment)) {
263
+ environments.push(environment);
264
+ }
265
+ }
266
+ if (environments.length === 0 && errors.length === 0) {
267
+ errors.push("No seed environments selected.");
268
+ }
269
+ return { environments, errors };
270
+ }
271
+ export {
272
+ normalizeSeedResources,
273
+ resolveSelectedSeedEnvironments,
274
+ seedOwnershipMetadata
275
+ };
@@ -0,0 +1,10 @@
1
+ import type { SeedCurrentResource, SeedDiagnostic, SeedEnvironment, SeedManifest, SeedPlan } from './types.js';
2
+ export declare function createSeedPlan(input: {
3
+ manifest: SeedManifest;
4
+ manifestPath: string;
5
+ environments: SeedEnvironment[];
6
+ mode: SeedPlan['mode'];
7
+ diagnostics?: SeedDiagnostic[];
8
+ currentResources?: SeedCurrentResource[];
9
+ }): SeedPlan;
10
+ export declare function formatSeedPlan(plan: SeedPlan): string[];
@@ -0,0 +1,111 @@
1
+ import { normalizeSeedResources } from "./normalize.js";
2
+ const ACTION_TYPES = ["create", "update", "unchanged", "skip", "delete", "error"];
3
+ function emptySummary() {
4
+ return {
5
+ create: 0,
6
+ update: 0,
7
+ unchanged: 0,
8
+ skip: 0,
9
+ delete: 0,
10
+ error: 0
11
+ };
12
+ }
13
+ function stableJson(value) {
14
+ if (Array.isArray(value)) {
15
+ return `[${value.map(stableJson).join(",")}]`;
16
+ }
17
+ if (value && typeof value === "object") {
18
+ return `{${Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`).join(",")}}`;
19
+ }
20
+ return JSON.stringify(value);
21
+ }
22
+ function toAction(resource, currentByKey) {
23
+ if (resource.environments.length === 0) {
24
+ return {
25
+ ...resource,
26
+ action: "skip",
27
+ reason: "Resource does not target the selected environments."
28
+ };
29
+ }
30
+ const current = currentByKey.get(resource.key);
31
+ if (current) {
32
+ return {
33
+ ...resource,
34
+ action: stableJson(resource.payload) === stableJson(current.payload) ? "unchanged" : "update",
35
+ existing: current.existing ?? null
36
+ };
37
+ }
38
+ return {
39
+ ...resource,
40
+ action: "create"
41
+ };
42
+ }
43
+ function createSeedPlan(input) {
44
+ const currentByKey = new Map((input.currentResources ?? []).map((resource) => [resource.key, resource]));
45
+ const actions = normalizeSeedResources(input.manifest, input.environments).map((resource) => toAction(resource, currentByKey));
46
+ const summary = emptySummary();
47
+ for (const action of actions) {
48
+ summary[action.action] += 1;
49
+ }
50
+ for (const actionType of ACTION_TYPES) {
51
+ summary[actionType] += 0;
52
+ }
53
+ return {
54
+ ok: summary.error === 0 && !(input.diagnostics ?? []).some((diagnostic) => diagnostic.severity === "error"),
55
+ seed: input.manifest.name,
56
+ version: input.manifest.version,
57
+ mode: input.mode,
58
+ environments: input.environments,
59
+ summary,
60
+ actions,
61
+ diagnostics: input.diagnostics ?? [],
62
+ manifestPath: input.manifestPath
63
+ };
64
+ }
65
+ function actionKindLabel(action) {
66
+ switch (action.kind) {
67
+ case "repositoryHost":
68
+ return "repository host";
69
+ case "hubRepository":
70
+ return "hub repository";
71
+ case "capacityProvider":
72
+ return "capacity provider";
73
+ case "capacityLane":
74
+ return "lane";
75
+ case "capacityGrant":
76
+ return "grant";
77
+ case "workPolicy":
78
+ return "work policy";
79
+ case "catalogArtifact":
80
+ return "catalog artifact";
81
+ default:
82
+ return action.kind;
83
+ }
84
+ }
85
+ function formatSeedPlan(plan) {
86
+ const lines = [
87
+ `Seed: ${plan.seed}`,
88
+ `Environments: ${plan.environments.join(", ")}`,
89
+ ""
90
+ ];
91
+ for (const action of plan.actions) {
92
+ if (action.action === "skip") continue;
93
+ lines.push(`${action.action.toUpperCase()} ${actionKindLabel(action)} ${action.label}`);
94
+ }
95
+ if (lines[lines.length - 1] !== "") {
96
+ lines.push("");
97
+ }
98
+ lines.push(
99
+ "Summary:",
100
+ ` create: ${plan.summary.create}`,
101
+ ` update: ${plan.summary.update}`,
102
+ ` unchanged: ${plan.summary.unchanged}`,
103
+ ` skipped: ${plan.summary.skip}`,
104
+ ` errors: ${plan.summary.error}`
105
+ );
106
+ return lines;
107
+ }
108
+ export {
109
+ createSeedPlan,
110
+ formatSeedPlan
111
+ };
@@ -0,0 +1,2 @@
1
+ import { type SeedDiagnostic, type SeedManifest } from './types.js';
2
+ export declare function parseSeedManifest(value: unknown, diagnostics: SeedDiagnostic[]): SeedManifest | null;