@savvy-web/silk 1.3.11 → 2.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.
@@ -3,10 +3,10 @@ import { ChangelogTransformer } from "../api/transformer.js";
3
3
  import { ConfigInspector } from "./config-inspector.js";
4
4
  import { deriveMaintenanceReason } from "./maintenance-reason.js";
5
5
  import { VersionFiles } from "../utils/version-files.js";
6
- import { read } from "../../../../../../../node_modules/.pnpm/@changesets_config@3.1.4/node_modules/@changesets/config/dist/changesets-config.esm.js";
7
- import applyReleasePlan from "../../../../../../../node_modules/.pnpm/@changesets_apply-release-plan@7.1.1/node_modules/@changesets/apply-release-plan/dist/changesets-apply-release-plan.esm.js";
8
- import getReleasePlan from "../../../../../../../node_modules/.pnpm/@changesets_get-release-plan@4.0.16/node_modules/@changesets/get-release-plan/dist/changesets-get-release-plan.esm.js";
9
6
  import { getPackages } from "../../../../../../../node_modules/.pnpm/@manypkg_get-packages@3.1.0/node_modules/@manypkg/get-packages/dist/manypkg-get-packages.js";
7
+ import { readConfig } from "../../../../../../../node_modules/.pnpm/@changesets_config@4.0.0-next.6/node_modules/@changesets/config/dist/index.js";
8
+ import { applyReleasePlan } from "../../../../../../../node_modules/.pnpm/@changesets_apply-release-plan@8.0.0-next.7/node_modules/@changesets/apply-release-plan/dist/index.js";
9
+ import { getReleasePlan } from "../../../../../../../node_modules/.pnpm/@changesets_get-release-plan@5.0.0-next.7/node_modules/@changesets/get-release-plan/dist/index.js";
10
10
  import { Context, Effect, Layer } from "effect";
11
11
  import { FileSystem } from "@effect/platform";
12
12
  import { dirname, isAbsolute, join, relative } from "node:path";
@@ -24,37 +24,21 @@ import { dirname, isAbsolute, join, relative } from "node:path";
24
24
  * (e.g. `getChangelogEntry`) is re-implemented.
25
25
  *
26
26
  */
27
- const V1_TOOLS = /* @__PURE__ */ new Set([
28
- "yarn",
29
- "bolt",
30
- "pnpm",
31
- "lerna",
32
- "root"
33
- ]);
27
+ const errMsg = (e) => e instanceof Error ? e.message : String(e);
34
28
  /**
35
- * Single workspace-discovery seam; swap to an Effect-native stack later here.
36
- *
37
- * Discovers with `@manypkg/get-packages@3.x` and adapts to the v1 shape:
38
- * `tool` collapses to its type string (tools unknown to v1 map to `"root"` —
39
- * the engine never reads `tool` at runtime, only `root.dir`), and
40
- * `rootDir`/`rootPackage` fold back into `root`.
29
+ * Read the changesets config, surfacing non-throwing `readConfig` errors as a
30
+ * thrown `Error` so callers inside `Effect.tryPromise` land on the existing
31
+ * `ReleasePlanError` mapping. Warnings are returned alongside the config so
32
+ * the caller can log them via the Effect runtime rather than console output.
41
33
  */
42
- const buildPackages = async (root) => {
43
- const { tool, rootDir, rootPackage, packages } = await getPackages(root);
44
- if (!rootPackage) throw new Error(`Workspace root has no package.json: ${rootDir}`);
34
+ async function loadConfig(root, packages) {
35
+ const configResult = await readConfig(root, packages);
36
+ if (configResult.config === void 0) throw new Error(`Invalid changeset config:\n${configResult.errors.join("\n")}`);
45
37
  return {
46
- tool: V1_TOOLS.has(tool.type) ? tool.type : "root",
47
- root: {
48
- dir: rootPackage.dir,
49
- packageJson: rootPackage.packageJson
50
- },
51
- packages: packages.map((p) => ({
52
- dir: p.dir,
53
- packageJson: p.packageJson
54
- }))
38
+ config: configResult.config,
39
+ warnings: configResult.warnings
55
40
  };
56
- };
57
- const errMsg = (e) => e instanceof Error ? e.message : String(e);
41
+ }
58
42
  /**
59
43
  * Base class for {@link ReleasePlanner}.
60
44
  *
@@ -74,7 +58,7 @@ function makeShape(inspector, fs) {
74
58
  })
75
59
  });
76
60
  const preview = (root) => previewEffect(root, fs);
77
- const apply = (root, options) => applyEffect(root, options?.dryRun ?? false, inspector, fs);
61
+ const apply = (root, options) => applyEffect(root, options?.dryRun ?? false, options?.changelogModules, inspector, fs);
78
62
  return {
79
63
  plan,
80
64
  preview,
@@ -132,19 +116,25 @@ function maintenanceReasons(plan, config) {
132
116
  function previewEffect(root, fs) {
133
117
  const program = Effect.gen(function* () {
134
118
  const [plan, packages] = yield* Effect.tryPromise({
135
- try: () => Promise.all([getReleasePlan(root), buildPackages(root)]),
119
+ try: () => Promise.all([getReleasePlan(root), getPackages(root)]),
136
120
  catch: (e) => new ReleasePlanError({
137
121
  phase: "preview",
138
122
  reason: errMsg(e)
139
123
  })
140
124
  });
141
- const config = yield* Effect.tryPromise({
142
- try: () => read(root, packages),
125
+ if (!packages.rootPackage) return yield* Effect.fail(new ReleasePlanError({
126
+ phase: "preview",
127
+ reason: `Workspace root has no package.json: ${root}`
128
+ }));
129
+ const rootPackage = packages.rootPackage;
130
+ const { config, warnings } = yield* Effect.tryPromise({
131
+ try: () => loadConfig(root, packages),
143
132
  catch: (e) => new ReleasePlanError({
144
133
  phase: "preview",
145
134
  reason: errMsg(e)
146
135
  })
147
136
  });
137
+ yield* Effect.forEach(warnings, (w) => Effect.logWarning(w));
148
138
  const reasonByName = maintenanceReasons(plan, config);
149
139
  const preMode = plan.preState ? plan.preState.mode : null;
150
140
  const changesets = plan.changesets.map((cs) => ({
@@ -163,7 +153,7 @@ function previewEffect(root, fs) {
163
153
  };
164
154
  const tempRoot = yield* fs.makeTempDirectoryScoped({ prefix: "silk-preview-" });
165
155
  const mapDir = (dir) => {
166
- const rel = relative(packages.root.dir, dir);
156
+ const rel = relative(packages.rootDir, dir);
167
157
  if (rel.startsWith("..") || isAbsolute(rel)) return Effect.fail(new ReleasePlanError({
168
158
  phase: "preview",
169
159
  reason: `Package directory is outside the workspace root: ${dir}`
@@ -173,13 +163,12 @@ function previewEffect(root, fs) {
173
163
  const tempDirs = yield* Effect.forEach(packages.packages, (p) => mapDir(p.dir));
174
164
  const tempPackages = {
175
165
  tool: packages.tool,
176
- root: {
177
- ...packages.root,
166
+ rootDir: tempRoot,
167
+ rootPackage: {
178
168
  dir: tempRoot,
179
- packageJson: structuredClone(packages.root.packageJson)
169
+ packageJson: structuredClone(rootPackage.packageJson)
180
170
  },
181
171
  packages: packages.packages.map((p, i) => ({
182
- ...p,
183
172
  dir: tempDirs[i],
184
173
  packageJson: structuredClone(p.packageJson)
185
174
  }))
@@ -196,7 +185,7 @@ function previewEffect(root, fs) {
196
185
  const realCl = join(p.dir, "CHANGELOG.md");
197
186
  if (yield* fs.exists(realCl)) yield* fs.copyFile(realCl, join(tDir, "CHANGELOG.md"));
198
187
  }
199
- const rootCl = join(packages.root.dir, "CHANGELOG.md");
188
+ const rootCl = join(packages.rootDir, "CHANGELOG.md");
200
189
  if (yield* fs.exists(rootCl)) yield* fs.copyFile(rootCl, join(tempRoot, "CHANGELOG.md"));
201
190
  yield* Effect.tryPromise({
202
191
  try: () => applyReleasePlan(plan, tempPackages, config, void 0, root),
@@ -207,7 +196,7 @@ function previewEffect(root, fs) {
207
196
  });
208
197
  const dirByName = /* @__PURE__ */ new Map();
209
198
  for (const p of tempPackages.packages) dirByName.set(p.packageJson.name, p.dir);
210
- if (tempPackages.root.packageJson.name) dirByName.set(tempPackages.root.packageJson.name, tempRoot);
199
+ if (tempPackages.rootPackage?.packageJson.name) dirByName.set(tempPackages.rootPackage.packageJson.name, tempRoot);
211
200
  const releases = [];
212
201
  for (const r of releasesToRender) {
213
202
  const dir = dirByName.get(r.name);
@@ -250,15 +239,17 @@ function previewEffect(root, fs) {
250
239
  function diskVersion(workspaceDir, fallback, fs) {
251
240
  return fs.readFileString(join(workspaceDir, "package.json")).pipe(Effect.flatMap((raw) => Effect.try(() => JSON.parse(raw).version ?? fallback)), Effect.orElseSucceed(() => fallback));
252
241
  }
253
- function applyEffect(root, dryRun, inspector, fs) {
242
+ function applyEffect(root, dryRun, changelogModules, inspector, fs) {
254
243
  return Effect.gen(function* () {
255
- const { plan, packages, config } = yield* Effect.tryPromise({
244
+ const { plan, packages, config, warnings } = yield* Effect.tryPromise({
256
245
  try: async () => {
257
- const [plan, packages] = await Promise.all([getReleasePlan(root), buildPackages(root)]);
246
+ const [plan, packages] = await Promise.all([getReleasePlan(root), getPackages(root)]);
247
+ const { config, warnings } = await loadConfig(root, packages);
258
248
  return {
259
249
  plan,
260
250
  packages,
261
- config: await read(root, packages)
251
+ config,
252
+ warnings
262
253
  };
263
254
  },
264
255
  catch: (e) => new ReleasePlanError({
@@ -266,6 +257,29 @@ function applyEffect(root, dryRun, inspector, fs) {
266
257
  reason: errMsg(e)
267
258
  })
268
259
  });
260
+ yield* Effect.forEach(warnings, (w) => Effect.logWarning(w));
261
+ let engineConfig = config;
262
+ if (changelogModules) {
263
+ engineConfig = {
264
+ ...config,
265
+ format: false
266
+ };
267
+ if (Array.isArray(config.changelog)) {
268
+ const configuredId = config.changelog[0];
269
+ const mapped = changelogModules[configuredId];
270
+ if (mapped === void 0) {
271
+ const supported = Object.keys(changelogModules).join(", ");
272
+ return yield* Effect.fail(new ReleasePlanError({
273
+ phase: "apply",
274
+ reason: `changelog id "${configuredId}" is not in changelogModules (supported: ${supported})`
275
+ }));
276
+ }
277
+ engineConfig = {
278
+ ...engineConfig,
279
+ changelog: [mapped, config.changelog[1]]
280
+ };
281
+ }
282
+ }
269
283
  const releases = plan.releases.filter((r) => r.type !== "none").map((r) => ({
270
284
  name: r.name,
271
285
  type: r.type,
@@ -278,10 +292,10 @@ function applyEffect(root, dryRun, inspector, fs) {
278
292
  const versionByPkgName = new Map(plan.releases.map((r) => [r.name, r.newVersion]));
279
293
  const nameByDir = /* @__PURE__ */ new Map();
280
294
  for (const p of packages.packages) nameByDir.set(p.dir, p.packageJson.name);
281
- if (packages.root.packageJson.name) nameByDir.set(packages.root.dir, packages.root.packageJson.name);
295
+ if (packages.rootPackage?.packageJson.name) nameByDir.set(packages.rootDir, packages.rootPackage.packageJson.name);
282
296
  touchedFiles = yield* Effect.tryPromise({
283
297
  try: async () => {
284
- const touched = await applyReleasePlan(plan, packages, config);
298
+ const touched = await applyReleasePlan(plan, packages, engineConfig);
285
299
  for (const f of touched) {
286
300
  if (!f.endsWith("CHANGELOG.md")) continue;
287
301
  const pkgName = nameByDir.get(dirname(f));
@@ -1,5 +1,5 @@
1
1
  const require_errors = require('../errors.cjs');
2
- const require_changesets_get_github_info_esm = require('../../../../../../../node_modules/.pnpm/@changesets_get-github-info@0.8.0/node_modules/@changesets/get-github-info/dist/changesets-get-github-info.esm.cjs');
2
+ const require_index = require('../../../../../../../node_modules/.pnpm/@changesets_get-github-info@1.0.0-next.3/node_modules/@changesets/get-github-info/dist/index.cjs');
3
3
  let effect = require("effect");
4
4
 
5
5
  //#region ../silk-effects/dist/dev/pkg/changesets/vendor/github-info.js
@@ -9,8 +9,14 @@ let effect = require("effect");
9
9
  * @remarks
10
10
  * Bridges the `\@changesets/get-github-info` package (which returns
11
11
  * promises) into the Effect ecosystem. The {@link getGitHubInfo}
12
- * function wraps the upstream `getInfo()` call in `Effect.tryPromise`,
13
- * mapping failures to {@link GitHubApiError}.
12
+ * function wraps the upstream `getCommitInfo()` call in `Effect.tryPromise`,
13
+ * adapting its structured `CommitInfo | undefined` return back to the
14
+ * legacy {@link GitHubCommitInfo} shape and mapping failures (including a
15
+ * `not found` result) to {@link GitHubApiError}.
16
+ *
17
+ * The upstream v1 package added a `.env` fallback: it reads
18
+ * `GITHUB_TOKEN` from `process.env` directly when no token is otherwise
19
+ * configured, so the caller does not need to plumb one through.
14
20
  *
15
21
  * The {@link GitHubCommitInfo} type is the only item from this module
16
22
  * that is part of the public API (re-exported from the package root).
@@ -24,9 +30,13 @@ let effect = require("effect");
24
30
  * Fetch GitHub info for a commit, wrapped in Effect.
25
31
  *
26
32
  * @remarks
27
- * Calls the upstream `getInfo()` from `\@changesets/get-github-info`
28
- * within `Effect.tryPromise`. Any thrown error is caught and mapped
29
- * to a {@link GitHubApiError} with the operation set to `"getInfo"`.
33
+ * Calls the upstream `getCommitInfo()` from `\@changesets/get-github-info`
34
+ * within `Effect.tryPromise`, adapting its structured `CommitInfo`
35
+ * return to the legacy {@link GitHubCommitInfo} shape. An `undefined`
36
+ * result (commit or repo not found) is treated as a thrown error so it
37
+ * is mapped to the same {@link GitHubApiError} failure channel. Any
38
+ * thrown error is caught and mapped to a {@link GitHubApiError} with
39
+ * the operation set to `"getCommitInfo"`.
30
40
  *
31
41
  * Requires a `GITHUB_TOKEN` environment variable to be set for
32
42
  * authenticated API access (the upstream library reads it directly).
@@ -39,13 +49,25 @@ let effect = require("effect");
39
49
  */
40
50
  function getGitHubInfo(params) {
41
51
  return effect.Effect.tryPromise({
42
- try: () => require_changesets_get_github_info_esm.getInfo({
43
- commit: params.commit,
44
- repo: params.repo
45
- }),
52
+ try: async () => {
53
+ const info = await require_index.getCommitInfo({
54
+ commit: params.commit,
55
+ repo: params.repo
56
+ });
57
+ if (info === void 0) throw new Error(`commit ${params.commit} not found in ${params.repo}`);
58
+ return {
59
+ user: info.author?.login ?? null,
60
+ pull: info.pull?.number ?? null,
61
+ links: {
62
+ commit: info.commit.markdownLink,
63
+ pull: info.pull?.markdownLink ?? null,
64
+ user: info.author?.markdownLink ?? null
65
+ }
66
+ };
67
+ },
46
68
  /* v8 ignore next 5 -- error mapping tested via GitHubService test layer */
47
69
  catch: (error) => new require_errors.GitHubApiError({
48
- operation: "getInfo",
70
+ operation: "getCommitInfo",
49
71
  reason: error instanceof Error ? error.message : String(error)
50
72
  })
51
73
  });
@@ -1,5 +1,5 @@
1
1
  import { GitHubApiError } from "../errors.js";
2
- import { getInfo } from "../../../../../../../node_modules/.pnpm/@changesets_get-github-info@0.8.0/node_modules/@changesets/get-github-info/dist/changesets-get-github-info.esm.js";
2
+ import { getCommitInfo } from "../../../../../../../node_modules/.pnpm/@changesets_get-github-info@1.0.0-next.3/node_modules/@changesets/get-github-info/dist/index.js";
3
3
  import { Effect } from "effect";
4
4
 
5
5
  //#region ../silk-effects/dist/dev/pkg/changesets/vendor/github-info.js
@@ -9,8 +9,14 @@ import { Effect } from "effect";
9
9
  * @remarks
10
10
  * Bridges the `\@changesets/get-github-info` package (which returns
11
11
  * promises) into the Effect ecosystem. The {@link getGitHubInfo}
12
- * function wraps the upstream `getInfo()` call in `Effect.tryPromise`,
13
- * mapping failures to {@link GitHubApiError}.
12
+ * function wraps the upstream `getCommitInfo()` call in `Effect.tryPromise`,
13
+ * adapting its structured `CommitInfo | undefined` return back to the
14
+ * legacy {@link GitHubCommitInfo} shape and mapping failures (including a
15
+ * `not found` result) to {@link GitHubApiError}.
16
+ *
17
+ * The upstream v1 package added a `.env` fallback: it reads
18
+ * `GITHUB_TOKEN` from `process.env` directly when no token is otherwise
19
+ * configured, so the caller does not need to plumb one through.
14
20
  *
15
21
  * The {@link GitHubCommitInfo} type is the only item from this module
16
22
  * that is part of the public API (re-exported from the package root).
@@ -24,9 +30,13 @@ import { Effect } from "effect";
24
30
  * Fetch GitHub info for a commit, wrapped in Effect.
25
31
  *
26
32
  * @remarks
27
- * Calls the upstream `getInfo()` from `\@changesets/get-github-info`
28
- * within `Effect.tryPromise`. Any thrown error is caught and mapped
29
- * to a {@link GitHubApiError} with the operation set to `"getInfo"`.
33
+ * Calls the upstream `getCommitInfo()` from `\@changesets/get-github-info`
34
+ * within `Effect.tryPromise`, adapting its structured `CommitInfo`
35
+ * return to the legacy {@link GitHubCommitInfo} shape. An `undefined`
36
+ * result (commit or repo not found) is treated as a thrown error so it
37
+ * is mapped to the same {@link GitHubApiError} failure channel. Any
38
+ * thrown error is caught and mapped to a {@link GitHubApiError} with
39
+ * the operation set to `"getCommitInfo"`.
30
40
  *
31
41
  * Requires a `GITHUB_TOKEN` environment variable to be set for
32
42
  * authenticated API access (the upstream library reads it directly).
@@ -39,13 +49,25 @@ import { Effect } from "effect";
39
49
  */
40
50
  function getGitHubInfo(params) {
41
51
  return Effect.tryPromise({
42
- try: () => getInfo({
43
- commit: params.commit,
44
- repo: params.repo
45
- }),
52
+ try: async () => {
53
+ const info = await getCommitInfo({
54
+ commit: params.commit,
55
+ repo: params.repo
56
+ });
57
+ if (info === void 0) throw new Error(`commit ${params.commit} not found in ${params.repo}`);
58
+ return {
59
+ user: info.author?.login ?? null,
60
+ pull: info.pull?.number ?? null,
61
+ links: {
62
+ commit: info.commit.markdownLink,
63
+ pull: info.pull?.markdownLink ?? null,
64
+ user: info.author?.markdownLink ?? null
65
+ }
66
+ };
67
+ },
46
68
  /* v8 ignore next 5 -- error mapping tested via GitHubService test layer */
47
69
  catch: (error) => new GitHubApiError({
48
- operation: "getInfo",
70
+ operation: "getCommitInfo",
49
71
  reason: error instanceof Error ? error.message : String(error)
50
72
  })
51
73
  });
@@ -30,7 +30,7 @@ const SnapshotConfig = effect.Schema.Struct({
30
30
  prereleaseTemplate: effect.Schema.optional(effect.Schema.String)
31
31
  });
32
32
  /**
33
- * Standard changesets configuration matching the `@changesets/config@3.1.1` spec.
33
+ * Standard changesets configuration matching the `@changesets/config@4.0.0-next.6` spec.
34
34
  *
35
35
  * @remarks
36
36
  * Represents the parsed `.changeset/config.json` file. All fields are optional
@@ -30,7 +30,7 @@ const SnapshotConfig = Schema.Struct({
30
30
  prereleaseTemplate: Schema.optional(Schema.String)
31
31
  });
32
32
  /**
33
- * Standard changesets configuration matching the `@changesets/config@3.1.1` spec.
33
+ * Standard changesets configuration matching the `@changesets/config@4.0.0-next.6` spec.
34
34
  *
35
35
  * @remarks
36
36
  * Represents the parsed `.changeset/config.json` file. All fields are optional