@savvy-web/silk 1.3.5 → 1.3.7

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.
@@ -43578,7 +43578,7 @@ const handle = {
43578
43578
  * @import {Join} from 'mdast-util-to-markdown'
43579
43579
  */
43580
43580
  /** @type {Array<Join>} */
43581
- const join$7 = [joinDefaults];
43581
+ const join$8 = [joinDefaults];
43582
43582
  /** @type {Join} */
43583
43583
  function joinDefaults(left, right, parent, state) {
43584
43584
  if (right.type === "code" && formatCodeAsIndented(right, state) && (left.type === "list" || left.type === right.type && formatCodeAsIndented(left, state))) return false;
@@ -46423,7 +46423,7 @@ function toMarkdown(tree, options) {
46423
46423
  handle: void 0,
46424
46424
  indentLines,
46425
46425
  indexStack: [],
46426
- join: [...join$7],
46426
+ join: [...join$8],
46427
46427
  options: {},
46428
46428
  safe: safeBound,
46429
46429
  stack: [],
@@ -59761,6 +59761,16 @@ const DependencyActionSchema = effect.Schema.Literal("added", "updated", "remove
59761
59761
  */
59762
59762
  const DependencyTableTypeSchema = effect.Schema.Literal("dependency", "devDependency", "peerDependency", "optionalDependency", "workspace", "config");
59763
59763
  /**
59764
+ * The canonical accepted-value pattern for a dependency-table From/To cell:
59765
+ * the em-dash sentinel (U+2014), a bare/`~`/`^` semver, or — as a last-resort
59766
+ * fallback when a `catalog:`/`workspace:` specifier could not be resolved to a
59767
+ * concrete version — a pnpm protocol string. Non-overlapping alternatives keep
59768
+ * this free of polynomial backtracking (CodeQL).
59769
+ *
59770
+ * @public
59771
+ */
59772
+ const VERSION_RE = /^(\u2014|[~^]?\d+\.\d+\.\d+(?:[-+.][\w.+-]+)?|(?:catalog|workspace|npm|jsr|file|link|portal):[^\s|]+)$/;
59773
+ /**
59764
59774
  * Version string or em dash (U+2014) sentinel for added/removed entries.
59765
59775
  *
59766
59776
  * @remarks
@@ -59786,7 +59796,7 @@ const DependencyTableTypeSchema = effect.Schema.Literal("dependency", "devDepend
59786
59796
  *
59787
59797
  * @public
59788
59798
  */
59789
- const VersionOrEmptySchema = effect.Schema.String.pipe(effect.Schema.pattern(/^(\u2014|[~^]?\d+\.\d+\.\d+(?:[-+.][\w.+-]*)?)$/));
59799
+ const VersionOrEmptySchema = effect.Schema.String.pipe(effect.Schema.pattern(VERSION_RE));
59790
59800
  /**
59791
59801
  * Schema for a single dependency table row.
59792
59802
  *
@@ -61634,6 +61644,38 @@ const ContentStructureRule$1 = lintRule("remark-lint:changeset-content-structure
61634
61644
  });
61635
61645
  });
61636
61646
  //#endregion
61647
+ //#region ../silk-effects/dist/dev/pkg/changesets/remark/rules/dependency-table-format.js
61648
+ /** @internal */
61649
+ const EM_DASH$3 = "—";
61650
+ const DependencyTableFormatRule$1 = lintRule("remark-lint:changeset-dependency-table-format", (tree, file) => {
61651
+ visit(tree, "heading", (node, index) => {
61652
+ if (node.depth !== 2) return;
61653
+ if (toString(node).toLowerCase() !== "dependencies") return;
61654
+ if (index === void 0) return;
61655
+ const content = [];
61656
+ for (let i = index + 1; i < tree.children.length; i++) {
61657
+ const child = tree.children[i];
61658
+ if (child.type === "heading") break;
61659
+ content.push(child);
61660
+ }
61661
+ const tables = content.filter((n) => n.type === "table");
61662
+ if (tables.length === 0) {
61663
+ file.message(`Dependencies section must contain a table, not a list or paragraph. See: ${RULE_DOCS.CSH005}`, node);
61664
+ return;
61665
+ }
61666
+ const table = tables[0];
61667
+ try {
61668
+ const rows = parseDependencyTable(table);
61669
+ for (const row of rows) {
61670
+ if (row.action === "added" && row.from !== EM_DASH$3) file.message(`'from' must be '\u2014' when action is 'added' (got '${row.from}'). See: ${RULE_DOCS.CSH005}`, table);
61671
+ if (row.action === "removed" && row.to !== EM_DASH$3) file.message(`'to' must be '\u2014' when action is 'removed' (got '${row.to}'). See: ${RULE_DOCS.CSH005}`, table);
61672
+ }
61673
+ } catch (error) {
61674
+ file.message(`${error instanceof Error ? error.message : String(error)}. See: ${RULE_DOCS.CSH005}`, table);
61675
+ }
61676
+ });
61677
+ });
61678
+ //#endregion
61637
61679
  //#region ../silk-effects/dist/dev/pkg/changesets/remark/rules/heading-hierarchy.js
61638
61680
  const HeadingHierarchyRule$1 = lintRule("remark-lint:changeset-heading-hierarchy", (tree, file) => {
61639
61681
  let prevDepth = 0;
@@ -61730,7 +61772,7 @@ function stripFrontmatter(content) {
61730
61772
  /**
61731
61773
  * Class-based API wrapper for changeset linting.
61732
61774
  *
61733
- * Provides a static class interface that runs all remark-lint rules
61775
+ * Provides a static class interface that runs all five remark-lint rules
61734
61776
  * against changeset markdown files and returns structured diagnostics.
61735
61777
  *
61736
61778
  * @internal
@@ -61738,9 +61780,9 @@ function stripFrontmatter(content) {
61738
61780
  /**
61739
61781
  * Static class for linting changeset markdown files.
61740
61782
  *
61741
- * Runs the four remark-lint rules (heading-hierarchy, required-sections,
61742
- * content-structure, uncategorized-content) against changeset markdown
61743
- * and returns structured {@link LintMessage} diagnostics.
61783
+ * Runs the five remark-lint rules (heading-hierarchy, required-sections,
61784
+ * content-structure, dependency-table-format, uncategorized-content) against
61785
+ * changeset markdown and returns structured {@link LintMessage} diagnostics.
61744
61786
  *
61745
61787
  * @remarks
61746
61788
  * This class implements the pre-validation layer of the three-layer
@@ -61826,7 +61868,7 @@ var ChangesetLinter = class ChangesetLinter {
61826
61868
  *
61827
61869
  * @remarks
61828
61870
  * Reads the file synchronously, strips YAML frontmatter, and runs all
61829
- * four lint rules. The file path is preserved in each returned
61871
+ * five lint rules. The file path is preserved in each returned
61830
61872
  * {@link LintMessage} for error reporting.
61831
61873
  *
61832
61874
  * @param filePath - Absolute or relative path to the changeset `.md` file
@@ -61840,7 +61882,7 @@ var ChangesetLinter = class ChangesetLinter {
61840
61882
  * Validate a markdown string directly.
61841
61883
  *
61842
61884
  * @remarks
61843
- * Strips YAML frontmatter (if present) and runs all four lint rules
61885
+ * Strips YAML frontmatter (if present) and runs all five lint rules
61844
61886
  * against the remaining content. This method is useful for validating
61845
61887
  * changeset content that is already in memory, such as in test suites
61846
61888
  * or editor integrations.
@@ -61852,7 +61894,7 @@ var ChangesetLinter = class ChangesetLinter {
61852
61894
  */
61853
61895
  static validateContent(content, filePath = "<input>") {
61854
61896
  const body = stripFrontmatter(content);
61855
- return unified().use(remarkParse).use(remarkStringify).use(HeadingHierarchyRule$1).use(RequiredSectionsRule$1).use(ContentStructureRule$1).use(UncategorizedContentRule$1).processSync(body).messages.map((msg) => ({
61897
+ return unified().use(remarkParse).use(remarkGfm).use(remarkStringify).use(HeadingHierarchyRule$1).use(RequiredSectionsRule$1).use(ContentStructureRule$1).use(DependencyTableFormatRule$1).use(UncategorizedContentRule$1).processSync(body).messages.map((msg) => ({
61856
61898
  file: filePath,
61857
61899
  /* v8 ignore next 3 -- ruleId/line/column fallbacks; remark-lint always provides these */
61858
61900
  rule: msg.ruleId ?? msg.source ?? "unknown",
@@ -77373,7 +77415,7 @@ function configErrorFromParseError(parseError, configPath) {
77373
77415
  * Each shape carries a private cache keyed by absolute project dir so
77374
77416
  * repeat `inspect`/`classify` calls reuse the materialized state.
77375
77417
  */
77376
- function makeShape$3(reader, discovery, fs) {
77418
+ function makeShape$4(reader, discovery, fs) {
77377
77419
  const cache = /* @__PURE__ */ new Map();
77378
77420
  const inspect = (cwd) => effect.Effect.gen(function* () {
77379
77421
  const projectDir = (0, node_path.resolve)(cwd);
@@ -77503,7 +77545,7 @@ function classifyOne(inspected, path) {
77503
77545
  * @public
77504
77546
  */
77505
77547
  const ConfigInspectorLive = effect.Layer.effect(ConfigInspector, effect.Effect.gen(function* () {
77506
- return makeShape$3(yield* ChangesetConfigReader, yield* WorkspaceDiscovery, yield* _effect_platform.FileSystem.FileSystem);
77548
+ return makeShape$4(yield* ChangesetConfigReader, yield* WorkspaceDiscovery, yield* _effect_platform.FileSystem.FileSystem);
77507
77549
  }));
77508
77550
  /**
77509
77551
  * Test factory — build a {@link ConfigInspector} that returns a fixed
@@ -77726,7 +77768,7 @@ function resolveBaseBranch(opts) {
77726
77768
  effect.Effect.catchAll(() => effect.Effect.succeed(opts.configBaseBranch))
77727
77769
  );
77728
77770
  }
77729
- function makeShape$2(inspector) {
77771
+ function makeShape$3(inspector) {
77730
77772
  const analyzeBranch = (cwd, opts) => effect.Effect.gen(function* () {
77731
77773
  const inspected = yield* inspector.inspect(cwd);
77732
77774
  const baseBranch = yield* resolveBaseBranch({
@@ -77796,7 +77838,7 @@ function makeShape$2(inspector) {
77796
77838
  * @public
77797
77839
  */
77798
77840
  const BranchAnalyzerLive = effect.Layer.effect(BranchAnalyzer, effect.Effect.gen(function* () {
77799
- return makeShape$2(yield* ConfigInspector);
77841
+ return makeShape$3(yield* ConfigInspector);
77800
77842
  }));
77801
77843
  /**
77802
77844
  * Test factory — build a {@link BranchAnalyzer} that returns a fixed
@@ -77853,6 +77895,714 @@ const ChangelogServiceBase = effect.Context.Tag("ChangelogService")();
77853
77895
  */
77854
77896
  var ChangelogService = class extends ChangelogServiceBase {};
77855
77897
  //#endregion
77898
+ //#region ../silk-effects/dist/dev/pkg/changesets/utils/dep-diff.js
77899
+ const EM_DASH$2 = "—";
77900
+ const DEP_TYPE_MAP = [
77901
+ ["dependencies", "dependency"],
77902
+ ["devDependencies", "devDependency"],
77903
+ ["peerDependencies", "peerDependency"],
77904
+ ["optionalDependencies", "optionalDependency"]
77905
+ ];
77906
+ function diffOneRecord(before, after, type) {
77907
+ const rows = [];
77908
+ const seen = /* @__PURE__ */ new Set();
77909
+ for (const [name, beforeVersion] of Object.entries(before)) {
77910
+ seen.add(name);
77911
+ const afterVersion = after[name];
77912
+ if (afterVersion === void 0) rows.push({
77913
+ dependency: name,
77914
+ type,
77915
+ action: "removed",
77916
+ from: beforeVersion,
77917
+ to: EM_DASH$2
77918
+ });
77919
+ else if (afterVersion !== beforeVersion) rows.push({
77920
+ dependency: name,
77921
+ type,
77922
+ action: "updated",
77923
+ from: beforeVersion,
77924
+ to: afterVersion
77925
+ });
77926
+ }
77927
+ for (const [name, afterVersion] of Object.entries(after)) {
77928
+ if (seen.has(name)) continue;
77929
+ rows.push({
77930
+ dependency: name,
77931
+ type,
77932
+ action: "added",
77933
+ from: EM_DASH$2,
77934
+ to: afterVersion
77935
+ });
77936
+ }
77937
+ return rows;
77938
+ }
77939
+ /**
77940
+ * Diff two workspace snapshots and return per-package dependency-table rows.
77941
+ *
77942
+ * @param before - Snapshot at the older ref (typically the merge base). Pass
77943
+ * `null` for workspace packages that did not exist at the older ref — every
77944
+ * declared dep is then reported as `"added"`.
77945
+ * @param after - Snapshot at the newer ref (typically the working tree).
77946
+ * @returns One {@link WorkspaceDependencyDiff} entry per workspace package
77947
+ * that has at least one row. Packages with no changes are omitted.
77948
+ *
77949
+ * @public
77950
+ */
77951
+ function computeWorkspaceDependencyDiffs(beforeSnapshots, afterSnapshots) {
77952
+ const beforeByName = new Map(beforeSnapshots.map((s) => [s.name, s]));
77953
+ const result = [];
77954
+ for (const after of afterSnapshots) {
77955
+ const before = beforeByName.get(after.name);
77956
+ const rows = [];
77957
+ for (const [field, type] of DEP_TYPE_MAP) {
77958
+ const beforeRecord = before?.[field] ?? {};
77959
+ const afterRecord = after[field];
77960
+ rows.push(...diffOneRecord(beforeRecord, afterRecord, type));
77961
+ }
77962
+ if (rows.length > 0) result.push({
77963
+ package: after.name,
77964
+ relativePath: after.relativePath,
77965
+ rows: sortDependencyRows(rows)
77966
+ });
77967
+ }
77968
+ return result;
77969
+ }
77970
+ //#endregion
77971
+ //#region ../silk-effects/dist/dev/pkg/changesets/utils/publishability.js
77972
+ /**
77973
+ * Publishability helpers for the changeset CLI commands.
77974
+ *
77975
+ * @remarks
77976
+ * Provides `listPublishablePackageNames`, a convenience wrapper around
77977
+ * {@link SilkPublishability} that returns a `Set<string>` of
77978
+ * publishable package names. Used by the `deps detect` and `deps regen`
77979
+ * commands to filter out workspace packages whose dependency changes
77980
+ * would never reach a release.
77981
+ *
77982
+ */
77983
+ /**
77984
+ * Compute the set of currently-publishable workspace package names.
77985
+ *
77986
+ * @remarks
77987
+ * Uses the currently-active {@link SilkPublishability} — wire the
77988
+ * {@link SilkPublishabilityDetectorLive} layer to get silk semantics.
77989
+ *
77990
+ * @param packages - The workspace packages to evaluate
77991
+ * @returns An Effect yielding a `Set` of publishable package names
77992
+ *
77993
+ * @public
77994
+ */
77995
+ function listPublishablePackageNames(packages) {
77996
+ return effect.Effect.gen(function* () {
77997
+ const detector = yield* PublishabilityDetector;
77998
+ const names = /* @__PURE__ */ new Set();
77999
+ for (const pkg of packages) if ((yield* detector.detect(pkg, pkg.path)).length > 0) names.add(pkg.name);
78000
+ return names;
78001
+ });
78002
+ }
78003
+ //#endregion
78004
+ //#region ../silk-effects/dist/dev/pkg/changesets/utils/worktree-snapshot.js
78005
+ /**
78006
+ * Shared helpers for `deps detect` and `deps regen` — both need to read
78007
+ * workspace package snapshots from the live working tree (the "after"
78008
+ * side of a dep diff that isn't pinned to a git ref) and to resolve the
78009
+ * merge-base for the default `--from` ref.
78010
+ *
78011
+ * @remarks
78012
+ * `WorkspaceSnapshotReader` covers the git-ref side via `git show`. This
78013
+ * module is the working-tree counterpart — staged and unstaged
78014
+ * `package.json` edits show up here, matching `analyze-branch`'s
78015
+ * coverage of the working tree.
78016
+ *
78017
+ * @internal
78018
+ */
78019
+ /**
78020
+ * Run `git merge-base <base> HEAD`, returning the SHA. Errors propagate
78021
+ * as {@link GitError}.
78022
+ *
78023
+ * @internal
78024
+ */
78025
+ function gitMergeBase(cwd, base) {
78026
+ return effect.Effect.try({
78027
+ try: () => (0, node_child_process.execFileSync)("git", [
78028
+ "merge-base",
78029
+ base,
78030
+ "HEAD"
78031
+ ], {
78032
+ cwd,
78033
+ encoding: "utf8",
78034
+ stdio: [
78035
+ "ignore",
78036
+ "pipe",
78037
+ "pipe"
78038
+ ]
78039
+ }).trim(),
78040
+ catch: (error) => {
78041
+ const stderr = error.stderr;
78042
+ const text = typeof stderr === "string" ? stderr : stderr?.toString() ?? "";
78043
+ return new GitError$1({
78044
+ command: `git merge-base ${base} HEAD`,
78045
+ cwd,
78046
+ reason: text.trim() || (error.message ?? String(error))
78047
+ });
78048
+ }
78049
+ });
78050
+ }
78051
+ /**
78052
+ * Normalize a pnpm-workspace.yaml glob entry for filesystem expansion.
78053
+ *
78054
+ * `packages/**` collapses to `packages/*` (retains the wildcard so the
78055
+ * caller hits the directory-listing path); `packages/*` and a literal
78056
+ * `packages/foo` are passed through unchanged.
78057
+ *
78058
+ * Returning the literal-path form for `packages/**` (i.e., `"packages"`)
78059
+ * would route the caller through the "no wildcards" branch and silently
78060
+ * skip every child workspace.
78061
+ *
78062
+ * @internal
78063
+ */
78064
+ function normalizeWorkspaceGlob(glob) {
78065
+ return glob.replace(/\/\*\*$/, "/*");
78066
+ }
78067
+ /**
78068
+ * Read every workspace package's `package.json` from the live working
78069
+ * tree, returning {@link WorkspaceSnapshot} entries matching the shape
78070
+ * `WorkspaceSnapshotReader.snapshotAt` produces for git refs.
78071
+ *
78072
+ * @remarks
78073
+ * Falls back to root-only when `pnpm-workspace.yaml` is missing or
78074
+ * unparseable. Uses `node:fs.readdirSync` for directory expansion
78075
+ * (portable across platforms — `execFileSync("ls")` is not).
78076
+ *
78077
+ * @internal
78078
+ */
78079
+ function snapshotFromWorktree(cwd) {
78080
+ const snapshots = [];
78081
+ const dirs = /* @__PURE__ */ new Set([cwd]);
78082
+ for (const dir of expandWorkspaceDirs(cwd)) dirs.add(dir);
78083
+ for (const dir of dirs) try {
78084
+ const pkgJson = JSON.parse((0, node_fs.readFileSync)((0, node_path.join)(dir, "package.json"), "utf8"));
78085
+ if (!pkgJson.name) continue;
78086
+ const rel = dir === cwd ? "." : dir.slice(cwd.length + 1);
78087
+ snapshots.push({
78088
+ name: pkgJson.name,
78089
+ relativePath: rel,
78090
+ version: pkgJson.version ?? "0.0.0",
78091
+ dependencies: pkgJson.dependencies ?? {},
78092
+ devDependencies: pkgJson.devDependencies ?? {},
78093
+ peerDependencies: pkgJson.peerDependencies ?? {},
78094
+ optionalDependencies: pkgJson.optionalDependencies ?? {}
78095
+ });
78096
+ } catch {}
78097
+ return snapshots;
78098
+ }
78099
+ function expandWorkspaceDirs(cwd) {
78100
+ let yaml;
78101
+ try {
78102
+ yaml = (0, node_fs.readFileSync)((0, node_path.join)(cwd, "pnpm-workspace.yaml"), "utf8");
78103
+ } catch {
78104
+ return [];
78105
+ }
78106
+ const dirs = [];
78107
+ const lines = yaml.split(/\r?\n/);
78108
+ let inPackagesBlock = false;
78109
+ for (const line of lines) {
78110
+ if (/^\s*#/.test(line)) continue;
78111
+ if (/^\s*packages\s*:\s*$/.test(line)) {
78112
+ inPackagesBlock = true;
78113
+ continue;
78114
+ }
78115
+ if (!inPackagesBlock) continue;
78116
+ const m = line.match(/^\s+-\s+["']?(.+?)["']?\s*$/);
78117
+ if (m) {
78118
+ const glob = normalizeWorkspaceGlob(m[1]);
78119
+ if (glob.includes("*") || glob.includes("?")) {
78120
+ const prefix = glob.includes("/") ? glob.slice(0, glob.lastIndexOf("/") + 1) : "";
78121
+ let entries = [];
78122
+ try {
78123
+ entries = (0, node_fs.readdirSync)((0, node_path.join)(cwd, prefix || "."));
78124
+ } catch {
78125
+ continue;
78126
+ }
78127
+ const regex = new RegExp(`^${glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]")}$`);
78128
+ for (const entry of entries) {
78129
+ const candidate = prefix ? `${prefix}${entry}` : entry;
78130
+ if (regex.test(candidate)) dirs.push((0, node_path.join)(cwd, candidate));
78131
+ }
78132
+ } else dirs.push((0, node_path.join)(cwd, glob));
78133
+ } else if (line.length > 0 && !line.startsWith(" ") && !line.startsWith(" ")) inPackagesBlock = false;
78134
+ }
78135
+ return dirs;
78136
+ }
78137
+ //#endregion
78138
+ //#region ../silk-effects/dist/dev/pkg/changesets/services/workspace-snapshot.js
78139
+ /**
78140
+ * @internal
78141
+ */
78142
+ const WorkspaceSnapshotReaderBase = effect.Context.Tag("WorkspaceSnapshotReader")();
78143
+ /**
78144
+ * Effect service tag for {@link WorkspaceSnapshotReaderShape}.
78145
+ *
78146
+ * @public
78147
+ */
78148
+ var WorkspaceSnapshotReader = class extends WorkspaceSnapshotReaderBase {};
78149
+ function runGitShow(cwd, ref, path) {
78150
+ return effect.Effect.try({
78151
+ try: () => (0, node_child_process.execFileSync)("git", ["show", `${ref}:${path}`], {
78152
+ cwd,
78153
+ encoding: "utf8",
78154
+ stdio: [
78155
+ "ignore",
78156
+ "pipe",
78157
+ "pipe"
78158
+ ]
78159
+ }),
78160
+ catch: (error) => {
78161
+ const stderr = error.stderr;
78162
+ const text = typeof stderr === "string" ? stderr : stderr?.toString() ?? "";
78163
+ if (/exists on disk, but not in|does not exist|unknown revision|bad object/.test(text)) return new GitError$1({
78164
+ command: `git show ${ref}:${path}`,
78165
+ cwd,
78166
+ reason: "PATH_NOT_AT_REF"
78167
+ });
78168
+ return new GitError$1({
78169
+ command: `git show ${ref}:${path}`,
78170
+ cwd,
78171
+ reason: text.trim() || (error.message ?? String(error))
78172
+ });
78173
+ }
78174
+ }).pipe(effect.Effect.catchTag("GitError", (err) => err.reason === "PATH_NOT_AT_REF" ? effect.Effect.succeed(null) : effect.Effect.fail(err)));
78175
+ }
78176
+ /**
78177
+ * Parse a minimal `pnpm-workspace.yaml` (`packages:` list only). Tolerant
78178
+ * of comments and varied indentation; rejects on missing `packages:` key.
78179
+ */
78180
+ function parseWorkspaceGlobs(yamlText) {
78181
+ const lines = yamlText.split(/\r?\n/);
78182
+ const globs = [];
78183
+ let inPackagesBlock = false;
78184
+ for (const line of lines) {
78185
+ if (/^\s*#/.test(line)) continue;
78186
+ if (/^\s*packages\s*:\s*$/.test(line)) {
78187
+ inPackagesBlock = true;
78188
+ continue;
78189
+ }
78190
+ if (inPackagesBlock) {
78191
+ const match = line.match(/^\s+-\s+["']?(.+?)["']?\s*$/);
78192
+ if (match) {
78193
+ globs.push(match[1]);
78194
+ continue;
78195
+ }
78196
+ if (line.length > 0 && !line.startsWith(" ") && !line.startsWith(" ")) inPackagesBlock = false;
78197
+ }
78198
+ }
78199
+ return globs;
78200
+ }
78201
+ function toSnapshot(pkg, relativePath) {
78202
+ if (!pkg.name) return null;
78203
+ return {
78204
+ name: pkg.name,
78205
+ relativePath,
78206
+ version: pkg.version ?? "0.0.0",
78207
+ dependencies: pkg.dependencies ?? {},
78208
+ devDependencies: pkg.devDependencies ?? {},
78209
+ peerDependencies: pkg.peerDependencies ?? {},
78210
+ optionalDependencies: pkg.optionalDependencies ?? {}
78211
+ };
78212
+ }
78213
+ /**
78214
+ * Expand a workspace glob like `packages/*` or `apps/web` against the
78215
+ * directories present at the given git ref. We can't `globSync` here
78216
+ * (the directories may not be on disk at this ref); instead we use
78217
+ * `git ls-tree` to enumerate paths.
78218
+ */
78219
+ function expandGlobAtRef(cwd, ref, glob) {
78220
+ return effect.Effect.gen(function* () {
78221
+ const cleanGlob = glob.replace(/\/\*\*$/, "/*");
78222
+ if (!cleanGlob.includes("*") && !cleanGlob.includes("?")) return [cleanGlob];
78223
+ const prefix = cleanGlob.includes("/") ? cleanGlob.slice(0, cleanGlob.lastIndexOf("/") + 1) : "";
78224
+ const entries = (yield* effect.Effect.try({
78225
+ try: () => (0, node_child_process.execFileSync)("git", [
78226
+ "ls-tree",
78227
+ "--name-only",
78228
+ ref,
78229
+ prefix
78230
+ ], {
78231
+ cwd,
78232
+ encoding: "utf8",
78233
+ stdio: [
78234
+ "ignore",
78235
+ "pipe",
78236
+ "pipe"
78237
+ ]
78238
+ }),
78239
+ catch: (error) => {
78240
+ const stderr = error.stderr;
78241
+ const text = typeof stderr === "string" ? stderr : stderr?.toString() ?? "";
78242
+ return new GitError$1({
78243
+ command: `git ls-tree ${ref} ${prefix}`,
78244
+ cwd,
78245
+ reason: text.trim() || (error.message ?? String(error))
78246
+ });
78247
+ }
78248
+ })).split(/\r?\n/).map((s) => s.trim()).filter((s) => s.length > 0);
78249
+ const regex = new RegExp(`^${cleanGlob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]")}$`);
78250
+ return entries.filter((e) => regex.test(e));
78251
+ });
78252
+ }
78253
+ function makeShape$2() {
78254
+ const cache = /* @__PURE__ */ new Map();
78255
+ const snapshotAt = (cwd, ref) => effect.Effect.gen(function* () {
78256
+ const cacheKey = `${cwd}::${ref}`;
78257
+ const cached = cache.get(cacheKey);
78258
+ if (cached) return cached;
78259
+ const wsYaml = yield* runGitShow(cwd, ref, "pnpm-workspace.yaml");
78260
+ const globs = wsYaml ? parseWorkspaceGlobs(wsYaml) : [];
78261
+ const dirs = [];
78262
+ for (const glob of globs) {
78263
+ const expanded = yield* expandGlobAtRef(cwd, ref, glob);
78264
+ for (const d of expanded) if (!dirs.includes(d)) dirs.push(d);
78265
+ }
78266
+ if (!dirs.includes(".")) dirs.unshift(".");
78267
+ const snapshots = [];
78268
+ for (const dir of dirs) {
78269
+ const pkgText = yield* runGitShow(cwd, ref, dir === "." ? "package.json" : `${dir}/package.json`);
78270
+ if (!pkgText) continue;
78271
+ let parsed;
78272
+ try {
78273
+ parsed = JSON.parse(pkgText);
78274
+ } catch {
78275
+ continue;
78276
+ }
78277
+ const snap = toSnapshot(parsed, dir);
78278
+ if (snap) snapshots.push(snap);
78279
+ }
78280
+ cache.set(cacheKey, snapshots);
78281
+ return snapshots;
78282
+ });
78283
+ return { snapshotAt };
78284
+ }
78285
+ /**
78286
+ * Production layer for {@link WorkspaceSnapshotReader}.
78287
+ *
78288
+ * @public
78289
+ */
78290
+ const WorkspaceSnapshotReaderLive = effect.Layer.succeed(WorkspaceSnapshotReader, makeShape$2());
78291
+ //#endregion
78292
+ //#region ../silk-effects/dist/dev/pkg/changesets/services/deps-regen.js
78293
+ /**
78294
+ * `Changesets.DepsRegen` service — lift the `deps regen` / `deps detect`
78295
+ * orchestration out of the CLI into a `Context.Tag` service with a
78296
+ * `plan()` / `execute()` split.
78297
+ *
78298
+ * @remarks
78299
+ * `plan()` computes the cumulative dependency diff (merge-base → working
78300
+ * tree by default, or between two explicit refs), resolves `catalog:` /
78301
+ * `workspace:` specifiers to concrete versions, drops `devDependency`
78302
+ * rows (unless `includeDevDeps`), and returns a complete {@link RegenPlan}
78303
+ * (target filenames + stale-changeset deletes) WITHOUT touching the
78304
+ * filesystem. `execute()` applies a plan — writing the fresh changesets
78305
+ * first, then deleting the stale pure-dependency ones (so an interrupted
78306
+ * run loses nothing and is safely re-runnable).
78307
+ *
78308
+ * This is the single source of truth for regen/detect: the CLI commands
78309
+ * and MCP tools are thin adapters over this service.
78310
+ *
78311
+ * @see {@link DepsRegen} for the service tag
78312
+ * @see {@link DepsRegenLive} for the production layer
78313
+ *
78314
+ */
78315
+ /** The em-dash sentinel (U+2014) used for added ("from") / removed ("to") cells. */
78316
+ const EM_DASH$1 = "—";
78317
+ /** Whether a From/To cell holds a pnpm protocol specifier that could resolve to a version. */
78318
+ const isProtocol = (v) => /^(?:catalog|workspace|npm|jsr|file|link|portal):/.test(v);
78319
+ /**
78320
+ * Resolve protocol From/To cells to concrete versions (raw-string fallback
78321
+ * when unresolved or on resolver error), leave em-dash sentinels untouched,
78322
+ * then optionally drop `devDependency` rows, and re-sort.
78323
+ *
78324
+ * @param diff - One workspace package's dependency-table rows.
78325
+ * @param keepDevDeps - When `true`, retain `devDependency` rows; otherwise
78326
+ * drop them unconditionally (the regen default).
78327
+ * @returns An Effect yielding the transformed {@link WorkspaceDependencyDiff}.
78328
+ *
78329
+ * @public
78330
+ */
78331
+ const resolveDiffRows = (diff, keepDevDeps = false) => effect.Effect.gen(function* () {
78332
+ const resolver = yield* CatalogResolver;
78333
+ const resolveCell = (dep, value) => isProtocol(value) ? resolver.resolveSpecifier(dep, value).pipe(effect.Effect.map((opt) => effect.Option.getOrElse(opt, () => value)), effect.Effect.catchAll((error) => effect.Effect.logWarning(`DepsRegen: catalog resolution failed for "${dep}" (${value}); keeping raw specifier: ${String(error)}`).pipe(effect.Effect.as(value)))) : effect.Effect.succeed(value);
78334
+ const rows = [];
78335
+ for (const row of diff.rows) {
78336
+ if (!keepDevDeps && row.type === "devDependency") continue;
78337
+ const from = row.from === EM_DASH$1 ? EM_DASH$1 : yield* resolveCell(row.dependency, row.from);
78338
+ const to = row.to === EM_DASH$1 ? EM_DASH$1 : yield* resolveCell(row.dependency, row.to);
78339
+ rows.push({
78340
+ ...row,
78341
+ from,
78342
+ to
78343
+ });
78344
+ }
78345
+ return {
78346
+ ...diff,
78347
+ rows: sortDependencyRows(rows)
78348
+ };
78349
+ });
78350
+ const ADJECTIVES = [
78351
+ "brave",
78352
+ "clever",
78353
+ "swift",
78354
+ "silver",
78355
+ "lucky",
78356
+ "happy",
78357
+ "calm",
78358
+ "bright",
78359
+ "quiet",
78360
+ "wild"
78361
+ ];
78362
+ const NOUNS = [
78363
+ "dogs",
78364
+ "cats",
78365
+ "wolves",
78366
+ "foxes",
78367
+ "cups",
78368
+ "ships",
78369
+ "trees",
78370
+ "owls",
78371
+ "cranes",
78372
+ "hills"
78373
+ ];
78374
+ const VERBS = [
78375
+ "laugh",
78376
+ "dream",
78377
+ "fly",
78378
+ "sing",
78379
+ "dance",
78380
+ "wander",
78381
+ "soar",
78382
+ "rest",
78383
+ "leap",
78384
+ "ponder"
78385
+ ];
78386
+ function pickRandomTriplet() {
78387
+ return `${ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)]}-${NOUNS[Math.floor(Math.random() * NOUNS.length)]}-${VERBS[Math.floor(Math.random() * VERBS.length)]}`;
78388
+ }
78389
+ /**
78390
+ * Pick a `<adjective>-<noun>-<verb>` filename slug that does not collide
78391
+ * with an existing `.changeset/*.md` OR with a slug already claimed
78392
+ * earlier in the same {@link RegenPlan.toWrite} computation. `plan()`
78393
+ * never writes to disk, so `existsSync` alone cannot see slugs chosen
78394
+ * moments earlier in the same call — the `chosen` set closes that gap.
78395
+ * The triplet space is 1,000 combinations, so a busy repo can plausibly
78396
+ * exhaust it across runs; fall back to a timestamp suffix after 20
78397
+ * unlucky picks.
78398
+ *
78399
+ * @param changesetDir - Directory checked via `existsSync` for on-disk collisions.
78400
+ * @param chosen - Basenames (without extension) already picked within this plan;
78401
+ * the picked candidate is added to this set before returning.
78402
+ * @internal
78403
+ */
78404
+ function randomFilename(changesetDir, chosen) {
78405
+ for (let i = 0; i < 20; i++) {
78406
+ const candidate = pickRandomTriplet();
78407
+ if (!chosen.has(candidate) && !(0, node_fs.existsSync)((0, node_path.join)(changesetDir, `${candidate}.md`))) {
78408
+ chosen.add(candidate);
78409
+ return candidate;
78410
+ }
78411
+ }
78412
+ let attempt = 0;
78413
+ let fallback = `${pickRandomTriplet()}-${Date.now()}`;
78414
+ while (chosen.has(fallback) || (0, node_fs.existsSync)((0, node_path.join)(changesetDir, `${fallback}.md`))) fallback = `${pickRandomTriplet()}-${Date.now()}-${++attempt}`;
78415
+ chosen.add(fallback);
78416
+ return fallback;
78417
+ }
78418
+ /**
78419
+ * Strict detection of "pure dependency changesets" per the documented
78420
+ * rules: single-package frontmatter, single `## Dependencies` heading,
78421
+ * no other body content beyond that section.
78422
+ *
78423
+ * @param content - Raw `.changeset/*.md` file contents.
78424
+ * @returns `{ isPure, package }` — `isPure` is `true` only for a
78425
+ * single-package, Dependencies-only changeset; `package` is the sole
78426
+ * frontmatter package name (or `null` when not pure).
78427
+ *
78428
+ * @public
78429
+ */
78430
+ function isPureDependencyChangeset(content) {
78431
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
78432
+ if (!fmMatch) return {
78433
+ isPure: false,
78434
+ package: null
78435
+ };
78436
+ const frontmatter = fmMatch[1];
78437
+ const body = (fmMatch[2] ?? "").trim();
78438
+ const fmLines = frontmatter.split(/\r?\n/).filter((l) => l.trim().length > 0 && !/^\s*#/.test(l));
78439
+ if (fmLines.length !== 1) return {
78440
+ isPure: false,
78441
+ package: null
78442
+ };
78443
+ const pkgMatch = fmLines[0].match(/^\s*["']?([^"':\s]+)["']?\s*:\s*([a-z]+)\s*$/);
78444
+ if (!pkgMatch) return {
78445
+ isPure: false,
78446
+ package: null
78447
+ };
78448
+ const pkg = pkgMatch[1];
78449
+ const bodyTrimmed = body.replace(/^\s+/, "");
78450
+ if (!/^## Dependencies\b/.test(bodyTrimmed)) return {
78451
+ isPure: false,
78452
+ package: null
78453
+ };
78454
+ if ((bodyTrimmed.match(/^## /gm) ?? []).length !== 1) return {
78455
+ isPure: false,
78456
+ package: null
78457
+ };
78458
+ if (/^# /m.test(bodyTrimmed)) return {
78459
+ isPure: false,
78460
+ package: null
78461
+ };
78462
+ return {
78463
+ isPure: true,
78464
+ package: pkg
78465
+ };
78466
+ }
78467
+ function listChangesetFiles(changesetDir) {
78468
+ if (!(0, node_fs.existsSync)(changesetDir)) return [];
78469
+ return (0, node_fs.readdirSync)(changesetDir).filter((f) => f.endsWith(".md") && f !== "README.md").map((f) => (0, node_path.join)(changesetDir, f));
78470
+ }
78471
+ function findPureDependencyChangesets(changesetDir) {
78472
+ const result = [];
78473
+ for (const file of listChangesetFiles(changesetDir)) {
78474
+ let content;
78475
+ try {
78476
+ content = (0, node_fs.readFileSync)(file, "utf8");
78477
+ } catch {
78478
+ continue;
78479
+ }
78480
+ const detection = isPureDependencyChangeset(content);
78481
+ if (detection.isPure && detection.package) result.push({
78482
+ file,
78483
+ package: detection.package
78484
+ });
78485
+ }
78486
+ return result;
78487
+ }
78488
+ function findMixedDependencyChangesets(changesetDir) {
78489
+ const result = [];
78490
+ for (const file of listChangesetFiles(changesetDir)) {
78491
+ let content;
78492
+ try {
78493
+ content = (0, node_fs.readFileSync)(file, "utf8");
78494
+ } catch {
78495
+ continue;
78496
+ }
78497
+ if (/^## Dependencies\b/m.test(content) && !isPureDependencyChangeset(content).isPure) result.push(file);
78498
+ }
78499
+ return result;
78500
+ }
78501
+ /**
78502
+ * Render a single-package, patch-bump changeset for a diff whose rows have
78503
+ * already been resolved/filtered by {@link resolveDiffRows}.
78504
+ */
78505
+ function renderChangesetContent(diff) {
78506
+ return `${`---\n"${diff.package}": patch\n---`}\n\n## Dependencies\n\n${serializeDependencyTableToMarkdown([...diff.rows])}\n`;
78507
+ }
78508
+ /**
78509
+ * @internal
78510
+ */
78511
+ const DepsRegenBase = effect.Context.Tag("Changesets/DepsRegen")();
78512
+ /**
78513
+ * Effect service tag for {@link DepsRegenShape}.
78514
+ *
78515
+ * @example
78516
+ * ```typescript
78517
+ * import { Effect } from "effect";
78518
+ * import { Changesets } from "@savvy-web/silk-effects";
78519
+ *
78520
+ * const program = Effect.gen(function* () {
78521
+ * const svc = yield* Changesets.DepsRegen;
78522
+ * const plan = yield* svc.plan({ cwd: process.cwd() });
78523
+ * return yield* svc.execute(plan);
78524
+ * });
78525
+ * ```
78526
+ *
78527
+ * @public
78528
+ */
78529
+ var DepsRegen = class extends DepsRegenBase {};
78530
+ /**
78531
+ * Build a {@link DepsRegenShape} that closes over already-resolved service
78532
+ * implementations, keeping the public `plan`/`execute` signatures
78533
+ * requirement-free (`R = never`).
78534
+ */
78535
+ function makeShape$1(reader, inspector, discovery, resolver, detector) {
78536
+ const provideResolver = effect.Layer.succeed(CatalogResolver, resolver);
78537
+ const provideDetector = effect.Layer.succeed(PublishabilityDetector, detector);
78538
+ const plan = (options) => effect.Effect.gen(function* () {
78539
+ const resolvedCwd = (0, node_path.resolve)(options.cwd);
78540
+ const changesetDir = (0, node_path.join)(resolvedCwd, ".changeset");
78541
+ let fromRef = options.from;
78542
+ if (!fromRef) {
78543
+ let baseBranch = options.base;
78544
+ if (!baseBranch) baseBranch = (yield* inspector.inspect(resolvedCwd).pipe(effect.Effect.catchTag("ConfigurationError", () => effect.Effect.succeed({ baseBranch: "main" })))).baseBranch;
78545
+ fromRef = yield* gitMergeBase(resolvedCwd, baseBranch);
78546
+ }
78547
+ const rawDiffs = computeWorkspaceDependencyDiffs(yield* reader.snapshotAt(resolvedCwd, fromRef), options.to ? yield* reader.snapshotAt(resolvedCwd, options.to) : snapshotFromWorktree(resolvedCwd));
78548
+ const targetPkg = options.package;
78549
+ const publishable = yield* listPublishablePackageNames(yield* discovery.listPackages(resolvedCwd)).pipe(effect.Effect.provide(provideDetector));
78550
+ const keepDevDeps = options.includeDevDeps === true;
78551
+ const scoped = targetPkg ? rawDiffs.filter((d) => d.package === targetPkg) : rawDiffs.filter((d) => publishable.has(d.package));
78552
+ const resolved = [];
78553
+ for (const diff of scoped) {
78554
+ const next = yield* resolveDiffRows(diff, keepDevDeps).pipe(effect.Effect.provide(provideResolver));
78555
+ if (next.rows.length > 0) resolved.push(next);
78556
+ }
78557
+ const existingPure = findPureDependencyChangesets(changesetDir);
78558
+ const skippedMixed = findMixedDependencyChangesets(changesetDir);
78559
+ const toDelete = targetPkg ? existingPure.filter((p) => p.package === targetPkg) : existingPure.filter((p) => publishable.has(p.package));
78560
+ const chosenFilenames = /* @__PURE__ */ new Set();
78561
+ return {
78562
+ toDelete,
78563
+ toWrite: resolved.map((diff) => ({
78564
+ file: (0, node_path.join)(changesetDir, `${randomFilename(changesetDir, chosenFilenames)}.md`),
78565
+ package: diff.package,
78566
+ diff
78567
+ })),
78568
+ skippedMixed
78569
+ };
78570
+ });
78571
+ const execute = (plan) => effect.Effect.sync(() => {
78572
+ const deleted = [];
78573
+ const written = [];
78574
+ for (const entry of plan.toWrite) {
78575
+ (0, node_fs.writeFileSync)(entry.file, renderChangesetContent(entry.diff));
78576
+ written.push(entry.file);
78577
+ }
78578
+ for (const entry of plan.toDelete) try {
78579
+ (0, node_fs.unlinkSync)(entry.file);
78580
+ deleted.push(entry.file);
78581
+ } catch {}
78582
+ return {
78583
+ deleted,
78584
+ written,
78585
+ skippedMixed: plan.skippedMixed
78586
+ };
78587
+ });
78588
+ return {
78589
+ plan,
78590
+ execute
78591
+ };
78592
+ }
78593
+ /**
78594
+ * Live layer for {@link DepsRegen}.
78595
+ *
78596
+ * Requires {@link WorkspaceSnapshotReader}, {@link ConfigInspector},
78597
+ * `WorkspaceDiscovery`, `CatalogResolver`, and `PublishabilityDetector`
78598
+ * (the last three from `workspaces-effect`).
78599
+ *
78600
+ * @public
78601
+ */
78602
+ const DepsRegenLive = effect.Layer.effect(DepsRegen, effect.Effect.gen(function* () {
78603
+ return makeShape$1(yield* WorkspaceSnapshotReader, yield* ConfigInspector, yield* WorkspaceDiscovery, yield* CatalogResolver, yield* PublishabilityDetector);
78604
+ }));
78605
+ //#endregion
77856
78606
  //#region ../silk-effects/dist/dev/pkg/changesets/utils/jsonpath.js
77857
78607
  /**
77858
78608
  * Tokenize a JSONPath string into segments.
@@ -102968,7 +103718,7 @@ const ReleasePlannerBase = effect.Context.Tag("ReleasePlanner")();
102968
103718
  /** Effect service tag for the release planner. @public */
102969
103719
  var ReleasePlanner = class extends ReleasePlannerBase {};
102970
103720
  /** Build the service shape over a resolved {@link ConfigInspector}. */
102971
- function makeShape$1(inspector) {
103721
+ function makeShape(inspector) {
102972
103722
  const plan = (root) => effect.Effect.tryPromise({
102973
103723
  try: () => getReleasePlan(root),
102974
103724
  catch: (e) => new ReleasePlanError({
@@ -102992,7 +103742,7 @@ function makeShape$1(inspector) {
102992
103742
  }
102993
103743
  /** Production layer. Requires {@link ConfigInspector} (used by `apply`). @public */
102994
103744
  const ReleasePlannerLive = effect.Layer.effect(ReleasePlanner, effect.Effect.gen(function* () {
102995
- return makeShape$1(yield* ConfigInspector);
103745
+ return makeShape(yield* ConfigInspector);
102996
103746
  }));
102997
103747
  /**
102998
103748
  * Test factory — supply fixed results for any subset of methods. Unsupplied
@@ -103177,160 +103927,6 @@ function applyEffect(root, dryRun, inspector) {
103177
103927
  });
103178
103928
  }
103179
103929
  //#endregion
103180
- //#region ../silk-effects/dist/dev/pkg/changesets/services/workspace-snapshot.js
103181
- /**
103182
- * @internal
103183
- */
103184
- const WorkspaceSnapshotReaderBase = effect.Context.Tag("WorkspaceSnapshotReader")();
103185
- /**
103186
- * Effect service tag for {@link WorkspaceSnapshotReaderShape}.
103187
- *
103188
- * @public
103189
- */
103190
- var WorkspaceSnapshotReader = class extends WorkspaceSnapshotReaderBase {};
103191
- function runGitShow(cwd, ref, path) {
103192
- return effect.Effect.try({
103193
- try: () => (0, node_child_process.execFileSync)("git", ["show", `${ref}:${path}`], {
103194
- cwd,
103195
- encoding: "utf8",
103196
- stdio: [
103197
- "ignore",
103198
- "pipe",
103199
- "pipe"
103200
- ]
103201
- }),
103202
- catch: (error) => {
103203
- const stderr = error.stderr;
103204
- const text = typeof stderr === "string" ? stderr : stderr?.toString() ?? "";
103205
- if (/exists on disk, but not in|does not exist|unknown revision|bad object/.test(text)) return new GitError$1({
103206
- command: `git show ${ref}:${path}`,
103207
- cwd,
103208
- reason: "PATH_NOT_AT_REF"
103209
- });
103210
- return new GitError$1({
103211
- command: `git show ${ref}:${path}`,
103212
- cwd,
103213
- reason: text.trim() || (error.message ?? String(error))
103214
- });
103215
- }
103216
- }).pipe(effect.Effect.catchTag("GitError", (err) => err.reason === "PATH_NOT_AT_REF" ? effect.Effect.succeed(null) : effect.Effect.fail(err)));
103217
- }
103218
- /**
103219
- * Parse a minimal `pnpm-workspace.yaml` (`packages:` list only). Tolerant
103220
- * of comments and varied indentation; rejects on missing `packages:` key.
103221
- */
103222
- function parseWorkspaceGlobs(yamlText) {
103223
- const lines = yamlText.split(/\r?\n/);
103224
- const globs = [];
103225
- let inPackagesBlock = false;
103226
- for (const line of lines) {
103227
- if (/^\s*#/.test(line)) continue;
103228
- if (/^\s*packages\s*:\s*$/.test(line)) {
103229
- inPackagesBlock = true;
103230
- continue;
103231
- }
103232
- if (inPackagesBlock) {
103233
- const match = line.match(/^\s+-\s+["']?(.+?)["']?\s*$/);
103234
- if (match) {
103235
- globs.push(match[1]);
103236
- continue;
103237
- }
103238
- if (line.length > 0 && !line.startsWith(" ") && !line.startsWith(" ")) inPackagesBlock = false;
103239
- }
103240
- }
103241
- return globs;
103242
- }
103243
- function toSnapshot(pkg, relativePath) {
103244
- if (!pkg.name) return null;
103245
- return {
103246
- name: pkg.name,
103247
- relativePath,
103248
- version: pkg.version ?? "0.0.0",
103249
- dependencies: pkg.dependencies ?? {},
103250
- devDependencies: pkg.devDependencies ?? {},
103251
- peerDependencies: pkg.peerDependencies ?? {},
103252
- optionalDependencies: pkg.optionalDependencies ?? {}
103253
- };
103254
- }
103255
- /**
103256
- * Expand a workspace glob like `packages/*` or `apps/web` against the
103257
- * directories present at the given git ref. We can't `globSync` here
103258
- * (the directories may not be on disk at this ref); instead we use
103259
- * `git ls-tree` to enumerate paths.
103260
- */
103261
- function expandGlobAtRef(cwd, ref, glob) {
103262
- return effect.Effect.gen(function* () {
103263
- const cleanGlob = glob.replace(/\/\*\*$/, "/*");
103264
- if (!cleanGlob.includes("*") && !cleanGlob.includes("?")) return [cleanGlob];
103265
- const prefix = cleanGlob.includes("/") ? cleanGlob.slice(0, cleanGlob.lastIndexOf("/") + 1) : "";
103266
- const entries = (yield* effect.Effect.try({
103267
- try: () => (0, node_child_process.execFileSync)("git", [
103268
- "ls-tree",
103269
- "--name-only",
103270
- ref,
103271
- prefix
103272
- ], {
103273
- cwd,
103274
- encoding: "utf8",
103275
- stdio: [
103276
- "ignore",
103277
- "pipe",
103278
- "pipe"
103279
- ]
103280
- }),
103281
- catch: (error) => {
103282
- const stderr = error.stderr;
103283
- const text = typeof stderr === "string" ? stderr : stderr?.toString() ?? "";
103284
- return new GitError$1({
103285
- command: `git ls-tree ${ref} ${prefix}`,
103286
- cwd,
103287
- reason: text.trim() || (error.message ?? String(error))
103288
- });
103289
- }
103290
- })).split(/\r?\n/).map((s) => s.trim()).filter((s) => s.length > 0);
103291
- const regex = new RegExp(`^${cleanGlob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]")}$`);
103292
- return entries.filter((e) => regex.test(e));
103293
- });
103294
- }
103295
- function makeShape() {
103296
- const cache = /* @__PURE__ */ new Map();
103297
- const snapshotAt = (cwd, ref) => effect.Effect.gen(function* () {
103298
- const cacheKey = `${cwd}::${ref}`;
103299
- const cached = cache.get(cacheKey);
103300
- if (cached) return cached;
103301
- const wsYaml = yield* runGitShow(cwd, ref, "pnpm-workspace.yaml");
103302
- const globs = wsYaml ? parseWorkspaceGlobs(wsYaml) : [];
103303
- const dirs = [];
103304
- for (const glob of globs) {
103305
- const expanded = yield* expandGlobAtRef(cwd, ref, glob);
103306
- for (const d of expanded) if (!dirs.includes(d)) dirs.push(d);
103307
- }
103308
- if (!dirs.includes(".")) dirs.unshift(".");
103309
- const snapshots = [];
103310
- for (const dir of dirs) {
103311
- const pkgText = yield* runGitShow(cwd, ref, dir === "." ? "package.json" : `${dir}/package.json`);
103312
- if (!pkgText) continue;
103313
- let parsed;
103314
- try {
103315
- parsed = JSON.parse(pkgText);
103316
- } catch {
103317
- continue;
103318
- }
103319
- const snap = toSnapshot(parsed, dir);
103320
- if (snap) snapshots.push(snap);
103321
- }
103322
- cache.set(cacheKey, snapshots);
103323
- return snapshots;
103324
- });
103325
- return { snapshotAt };
103326
- }
103327
- /**
103328
- * Production layer for {@link WorkspaceSnapshotReader}.
103329
- *
103330
- * @public
103331
- */
103332
- const WorkspaceSnapshotReaderLive = effect.Layer.succeed(WorkspaceSnapshotReader, makeShape());
103333
- //#endregion
103334
103930
  //#region ../silk-effects/dist/dev/pkg/changesets/categories/types.js
103335
103931
  /**
103336
103932
  * Section category schema and type definitions.
@@ -103647,246 +104243,6 @@ const AppliedReleaseSchema = effect.Schema.Struct({
103647
104243
  versionFileUpdates: effect.Schema.Array(VersionFileUpdateRecordSchema)
103648
104244
  }).annotations({ identifier: "AppliedRelease" });
103649
104245
  //#endregion
103650
- //#region ../silk-effects/dist/dev/pkg/changesets/utils/dep-diff.js
103651
- const EM_DASH$2 = "—";
103652
- const DEP_TYPE_MAP = [
103653
- ["dependencies", "dependency"],
103654
- ["devDependencies", "devDependency"],
103655
- ["peerDependencies", "peerDependency"],
103656
- ["optionalDependencies", "optionalDependency"]
103657
- ];
103658
- function diffOneRecord(before, after, type) {
103659
- const rows = [];
103660
- const seen = /* @__PURE__ */ new Set();
103661
- for (const [name, beforeVersion] of Object.entries(before)) {
103662
- seen.add(name);
103663
- const afterVersion = after[name];
103664
- if (afterVersion === void 0) rows.push({
103665
- dependency: name,
103666
- type,
103667
- action: "removed",
103668
- from: beforeVersion,
103669
- to: EM_DASH$2
103670
- });
103671
- else if (afterVersion !== beforeVersion) rows.push({
103672
- dependency: name,
103673
- type,
103674
- action: "updated",
103675
- from: beforeVersion,
103676
- to: afterVersion
103677
- });
103678
- }
103679
- for (const [name, afterVersion] of Object.entries(after)) {
103680
- if (seen.has(name)) continue;
103681
- rows.push({
103682
- dependency: name,
103683
- type,
103684
- action: "added",
103685
- from: EM_DASH$2,
103686
- to: afterVersion
103687
- });
103688
- }
103689
- return rows;
103690
- }
103691
- /**
103692
- * Diff two workspace snapshots and return per-package dependency-table rows.
103693
- *
103694
- * @param before - Snapshot at the older ref (typically the merge base). Pass
103695
- * `null` for workspace packages that did not exist at the older ref — every
103696
- * declared dep is then reported as `"added"`.
103697
- * @param after - Snapshot at the newer ref (typically the working tree).
103698
- * @returns One {@link WorkspaceDependencyDiff} entry per workspace package
103699
- * that has at least one row. Packages with no changes are omitted.
103700
- *
103701
- * @public
103702
- */
103703
- function computeWorkspaceDependencyDiffs(beforeSnapshots, afterSnapshots) {
103704
- const beforeByName = new Map(beforeSnapshots.map((s) => [s.name, s]));
103705
- const result = [];
103706
- for (const after of afterSnapshots) {
103707
- const before = beforeByName.get(after.name);
103708
- const rows = [];
103709
- for (const [field, type] of DEP_TYPE_MAP) {
103710
- const beforeRecord = before?.[field] ?? {};
103711
- const afterRecord = after[field];
103712
- rows.push(...diffOneRecord(beforeRecord, afterRecord, type));
103713
- }
103714
- if (rows.length > 0) result.push({
103715
- package: after.name,
103716
- relativePath: after.relativePath,
103717
- rows: sortDependencyRows(rows)
103718
- });
103719
- }
103720
- return result;
103721
- }
103722
- //#endregion
103723
- //#region ../silk-effects/dist/dev/pkg/changesets/utils/publishability.js
103724
- /**
103725
- * Publishability helpers for the changeset CLI commands.
103726
- *
103727
- * @remarks
103728
- * Provides `listPublishablePackageNames`, a convenience wrapper around
103729
- * {@link SilkPublishability} that returns a `Set<string>` of
103730
- * publishable package names. Used by the `deps detect` and `deps regen`
103731
- * commands to filter out workspace packages whose dependency changes
103732
- * would never reach a release.
103733
- *
103734
- */
103735
- /**
103736
- * Compute the set of currently-publishable workspace package names.
103737
- *
103738
- * @remarks
103739
- * Uses the currently-active {@link SilkPublishability} — wire the
103740
- * {@link SilkPublishabilityDetectorLive} layer to get silk semantics.
103741
- *
103742
- * @param packages - The workspace packages to evaluate
103743
- * @returns An Effect yielding a `Set` of publishable package names
103744
- *
103745
- * @public
103746
- */
103747
- function listPublishablePackageNames(packages) {
103748
- return effect.Effect.gen(function* () {
103749
- const detector = yield* PublishabilityDetector;
103750
- const names = /* @__PURE__ */ new Set();
103751
- for (const pkg of packages) if ((yield* detector.detect(pkg, pkg.path)).length > 0) names.add(pkg.name);
103752
- return names;
103753
- });
103754
- }
103755
- //#endregion
103756
- //#region ../silk-effects/dist/dev/pkg/changesets/utils/worktree-snapshot.js
103757
- /**
103758
- * Shared helpers for `deps detect` and `deps regen` — both need to read
103759
- * workspace package snapshots from the live working tree (the "after"
103760
- * side of a dep diff that isn't pinned to a git ref) and to resolve the
103761
- * merge-base for the default `--from` ref.
103762
- *
103763
- * @remarks
103764
- * `WorkspaceSnapshotReader` covers the git-ref side via `git show`. This
103765
- * module is the working-tree counterpart — staged and unstaged
103766
- * `package.json` edits show up here, matching `analyze-branch`'s
103767
- * coverage of the working tree.
103768
- *
103769
- * @internal
103770
- */
103771
- /**
103772
- * Run `git merge-base <base> HEAD`, returning the SHA. Errors propagate
103773
- * as {@link GitError}.
103774
- *
103775
- * @internal
103776
- */
103777
- function gitMergeBase(cwd, base) {
103778
- return effect.Effect.try({
103779
- try: () => (0, node_child_process.execFileSync)("git", [
103780
- "merge-base",
103781
- base,
103782
- "HEAD"
103783
- ], {
103784
- cwd,
103785
- encoding: "utf8",
103786
- stdio: [
103787
- "ignore",
103788
- "pipe",
103789
- "pipe"
103790
- ]
103791
- }).trim(),
103792
- catch: (error) => {
103793
- const stderr = error.stderr;
103794
- const text = typeof stderr === "string" ? stderr : stderr?.toString() ?? "";
103795
- return new GitError$1({
103796
- command: `git merge-base ${base} HEAD`,
103797
- cwd,
103798
- reason: text.trim() || (error.message ?? String(error))
103799
- });
103800
- }
103801
- });
103802
- }
103803
- /**
103804
- * Normalize a pnpm-workspace.yaml glob entry for filesystem expansion.
103805
- *
103806
- * `packages/**` collapses to `packages/*` (retains the wildcard so the
103807
- * caller hits the directory-listing path); `packages/*` and a literal
103808
- * `packages/foo` are passed through unchanged.
103809
- *
103810
- * Returning the literal-path form for `packages/**` (i.e., `"packages"`)
103811
- * would route the caller through the "no wildcards" branch and silently
103812
- * skip every child workspace.
103813
- *
103814
- * @internal
103815
- */
103816
- function normalizeWorkspaceGlob(glob) {
103817
- return glob.replace(/\/\*\*$/, "/*");
103818
- }
103819
- /**
103820
- * Read every workspace package's `package.json` from the live working
103821
- * tree, returning {@link WorkspaceSnapshot} entries matching the shape
103822
- * `WorkspaceSnapshotReader.snapshotAt` produces for git refs.
103823
- *
103824
- * @remarks
103825
- * Falls back to root-only when `pnpm-workspace.yaml` is missing or
103826
- * unparseable. Uses `node:fs.readdirSync` for directory expansion
103827
- * (portable across platforms — `execFileSync("ls")` is not).
103828
- *
103829
- * @internal
103830
- */
103831
- function snapshotFromWorktree(cwd) {
103832
- const snapshots = [];
103833
- const dirs = /* @__PURE__ */ new Set([cwd]);
103834
- for (const dir of expandWorkspaceDirs(cwd)) dirs.add(dir);
103835
- for (const dir of dirs) try {
103836
- const pkgJson = JSON.parse((0, node_fs.readFileSync)((0, node_path.join)(dir, "package.json"), "utf8"));
103837
- if (!pkgJson.name) continue;
103838
- const rel = dir === cwd ? "." : dir.slice(cwd.length + 1);
103839
- snapshots.push({
103840
- name: pkgJson.name,
103841
- relativePath: rel,
103842
- version: pkgJson.version ?? "0.0.0",
103843
- dependencies: pkgJson.dependencies ?? {},
103844
- devDependencies: pkgJson.devDependencies ?? {},
103845
- peerDependencies: pkgJson.peerDependencies ?? {},
103846
- optionalDependencies: pkgJson.optionalDependencies ?? {}
103847
- });
103848
- } catch {}
103849
- return snapshots;
103850
- }
103851
- function expandWorkspaceDirs(cwd) {
103852
- let yaml;
103853
- try {
103854
- yaml = (0, node_fs.readFileSync)((0, node_path.join)(cwd, "pnpm-workspace.yaml"), "utf8");
103855
- } catch {
103856
- return [];
103857
- }
103858
- const dirs = [];
103859
- const lines = yaml.split(/\r?\n/);
103860
- let inPackagesBlock = false;
103861
- for (const line of lines) {
103862
- if (/^\s*#/.test(line)) continue;
103863
- if (/^\s*packages\s*:\s*$/.test(line)) {
103864
- inPackagesBlock = true;
103865
- continue;
103866
- }
103867
- if (!inPackagesBlock) continue;
103868
- const m = line.match(/^\s+-\s+["']?(.+?)["']?\s*$/);
103869
- if (m) {
103870
- const glob = normalizeWorkspaceGlob(m[1]);
103871
- if (glob.includes("*") || glob.includes("?")) {
103872
- const prefix = glob.includes("/") ? glob.slice(0, glob.lastIndexOf("/") + 1) : "";
103873
- let entries = [];
103874
- try {
103875
- entries = (0, node_fs.readdirSync)((0, node_path.join)(cwd, prefix || "."));
103876
- } catch {
103877
- continue;
103878
- }
103879
- const regex = new RegExp(`^${glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]")}$`);
103880
- for (const entry of entries) {
103881
- const candidate = prefix ? `${prefix}${entry}` : entry;
103882
- if (regex.test(candidate)) dirs.push((0, node_path.join)(cwd, candidate));
103883
- }
103884
- } else dirs.push((0, node_path.join)(cwd, glob));
103885
- } else if (line.length > 0 && !line.startsWith(" ") && !line.startsWith(" ")) inPackagesBlock = false;
103886
- }
103887
- return dirs;
103888
- }
103889
- //#endregion
103890
104246
  //#region ../silk-effects/dist/dev/pkg/changesets/markdownlint/rules/utils.js
103891
104247
  /**
103892
104248
  * Get the heading level (1-6) from an `atxHeading` token.
@@ -104007,7 +104363,7 @@ const ContentStructureRule = {
104007
104363
  };
104008
104364
  //#endregion
104009
104365
  //#region ../silk-effects/dist/dev/pkg/changesets/markdownlint/rules/dependency-table-format.js
104010
- const EM_DASH$1 = "—";
104366
+ const EM_DASH = "—";
104011
104367
  const VALID_TYPES = /* @__PURE__ */ new Set([
104012
104368
  "dependency",
104013
104369
  "devDependency",
@@ -104028,7 +104384,6 @@ const EXPECTED_HEADERS = [
104028
104384
  "from",
104029
104385
  "to"
104030
104386
  ];
104031
- const VERSION_RE = /^(\u2014|[~^]?\d+\.\d+\.\d+(?:[-+.][\w.+-]*)?)$/;
104032
104387
  /**
104033
104388
  * Extract text from a `tableHeader` or `tableData` cell token.
104034
104389
  *
@@ -104060,7 +104415,7 @@ function getRowCells(row) {
104060
104415
  *
104061
104416
  * @public
104062
104417
  */
104063
- const DependencyTableFormatRule$1 = {
104418
+ const DependencyTableFormatRule = {
104064
104419
  names: ["changeset-dependency-table-format", "CSH005"],
104065
104420
  description: "Dependencies section must contain a valid dependency table",
104066
104421
  tags: ["changeset"],
@@ -104158,11 +104513,11 @@ const DependencyTableFormatRule$1 = {
104158
104513
  lineNumber: row.startLine,
104159
104514
  detail: `Invalid 'to' value '${to}'. Must be a semver string or em dash (\u2014). See: ${RULE_DOCS.CSH005}`
104160
104515
  });
104161
- if (action === "added" && from !== EM_DASH$1) onError({
104516
+ if (action === "added" && from !== EM_DASH) onError({
104162
104517
  lineNumber: row.startLine,
104163
104518
  detail: `'from' must be '\u2014' when action is 'added' (got '${from}'). See: ${RULE_DOCS.CSH005}`
104164
104519
  });
104165
- if (action === "removed" && to !== EM_DASH$1) onError({
104520
+ if (action === "removed" && to !== EM_DASH) onError({
104166
104521
  lineNumber: row.startLine,
104167
104522
  detail: `'to' must be '\u2014' when action is 'removed' (got '${to}'). See: ${RULE_DOCS.CSH005}`
104168
104523
  });
@@ -104342,7 +104697,7 @@ const SilkChangesetsRules = [
104342
104697
  RequiredSectionsRule,
104343
104698
  ContentStructureRule,
104344
104699
  UncategorizedContentRule,
104345
- DependencyTableFormatRule$1
104700
+ DependencyTableFormatRule
104346
104701
  ];
104347
104702
  //#endregion
104348
104703
  //#region ../silk-effects/dist/dev/pkg/changesets/remark/plugins/aggregate-dependency-tables.js
@@ -104387,38 +104742,6 @@ const AggregateDependencyTablesPlugin = () => {
104387
104742
  };
104388
104743
  };
104389
104744
  //#endregion
104390
- //#region ../silk-effects/dist/dev/pkg/changesets/remark/rules/dependency-table-format.js
104391
- /** @internal */
104392
- const EM_DASH = "—";
104393
- const DependencyTableFormatRule = lintRule("remark-lint:changeset-dependency-table-format", (tree, file) => {
104394
- visit(tree, "heading", (node, index) => {
104395
- if (node.depth !== 2) return;
104396
- if (toString(node).toLowerCase() !== "dependencies") return;
104397
- if (index === void 0) return;
104398
- const content = [];
104399
- for (let i = index + 1; i < tree.children.length; i++) {
104400
- const child = tree.children[i];
104401
- if (child.type === "heading") break;
104402
- content.push(child);
104403
- }
104404
- const tables = content.filter((n) => n.type === "table");
104405
- if (tables.length === 0) {
104406
- file.message(`Dependencies section must contain a table, not a list or paragraph. See: ${RULE_DOCS.CSH005}`, node);
104407
- return;
104408
- }
104409
- const table = tables[0];
104410
- try {
104411
- const rows = parseDependencyTable(table);
104412
- for (const row of rows) {
104413
- if (row.action === "added" && row.from !== EM_DASH) file.message(`'from' must be '\u2014' when action is 'added' (got '${row.from}'). See: ${RULE_DOCS.CSH005}`, table);
104414
- if (row.action === "removed" && row.to !== EM_DASH) file.message(`'to' must be '\u2014' when action is 'removed' (got '${row.to}'). See: ${RULE_DOCS.CSH005}`, table);
104415
- }
104416
- } catch (error) {
104417
- file.message(`${error instanceof Error ? error.message : String(error)}. See: ${RULE_DOCS.CSH005}`, table);
104418
- }
104419
- });
104420
- });
104421
- //#endregion
104422
104745
  //#region ../silk-effects/dist/dev/pkg/changesets/remark/presets.js
104423
104746
  /**
104424
104747
  * Remark preset collections for changeset lint rules and transform plugins.
@@ -104472,7 +104795,7 @@ const SilkChangesetPreset = [
104472
104795
  RequiredSectionsRule$1,
104473
104796
  ContentStructureRule$1,
104474
104797
  UncategorizedContentRule$1,
104475
- DependencyTableFormatRule
104798
+ DependencyTableFormatRule$1
104476
104799
  ];
104477
104800
  /**
104478
104801
  * Ordered array of all transform plugins in the correct execution order.
@@ -104568,12 +104891,15 @@ const changelogFunctions = (/* @__PURE__ */ __exportAll({
104568
104891
  DeduplicateItemsPlugin: () => DeduplicateItemsPlugin,
104569
104892
  DependencyActionSchema: () => DependencyActionSchema,
104570
104893
  DependencyTable: () => DependencyTable,
104571
- DependencyTableFormatRule: () => DependencyTableFormatRule,
104894
+ DependencyTableFormatRule: () => DependencyTableFormatRule$1,
104572
104895
  DependencyTableRowSchema: () => DependencyTableRowSchema,
104573
104896
  DependencyTableSchema: () => DependencyTableSchema,
104574
104897
  DependencyTableTypeSchema: () => DependencyTableTypeSchema,
104575
104898
  DependencyTypeSchema: () => DependencyTypeSchema,
104576
104899
  DependencyUpdateSchema: () => DependencyUpdateSchema,
104900
+ DepsRegen: () => DepsRegen,
104901
+ DepsRegenBase: () => DepsRegenBase,
104902
+ DepsRegenLive: () => DepsRegenLive,
104577
104903
  FileStatusSchema: () => FileStatusSchema,
104578
104904
  GitError: () => GitError$1,
104579
104905
  GitErrorBase: () => GitErrorBase,
@@ -104597,7 +104923,7 @@ const changelogFunctions = (/* @__PURE__ */ __exportAll({
104597
104923
  MarkdownService: () => MarkdownService,
104598
104924
  MarkdownServiceBase: () => MarkdownServiceBase,
104599
104925
  MarkdownlintContentStructureRule: () => ContentStructureRule,
104600
- MarkdownlintDependencyTableFormatRule: () => DependencyTableFormatRule$1,
104926
+ MarkdownlintDependencyTableFormatRule: () => DependencyTableFormatRule,
104601
104927
  MarkdownlintHeadingHierarchyRule: () => HeadingHierarchyRule,
104602
104928
  MarkdownlintRequiredSectionsRule: () => RequiredSectionsRule,
104603
104929
  MarkdownlintUncategorizedContentRule: () => UncategorizedContentRule,
@@ -104626,6 +104952,7 @@ const changelogFunctions = (/* @__PURE__ */ __exportAll({
104626
104952
  UncategorizedContentRule: () => UncategorizedContentRule$1,
104627
104953
  UrlOrMarkdownLinkSchema: () => UrlOrMarkdownLinkSchema,
104628
104954
  UsernameSchema: () => UsernameSchema,
104955
+ VERSION_RE: () => VERSION_RE,
104629
104956
  VersionFileConfigSchema: () => VersionFileConfigSchema,
104630
104957
  VersionFileError: () => VersionFileError,
104631
104958
  VersionFileErrorBase: () => VersionFileErrorBase,
@@ -104640,11 +104967,13 @@ const changelogFunctions = (/* @__PURE__ */ __exportAll({
104640
104967
  changelogFunctions: () => changelogFunctions$1,
104641
104968
  computeWorkspaceDependencyDiffs: () => computeWorkspaceDependencyDiffs,
104642
104969
  gitMergeBase: () => gitMergeBase,
104970
+ isPureDependencyChangeset: () => isPureDependencyChangeset,
104643
104971
  listPublishablePackageNames: () => listPublishablePackageNames,
104644
104972
  makeBranchAnalyzerTest: () => makeBranchAnalyzerTest,
104645
104973
  makeConfigInspectorTest: () => makeConfigInspectorTest,
104646
104974
  makeGitHubTest: () => makeGitHubTest,
104647
104975
  makeReleasePlannerTest: () => makeReleasePlannerTest,
104976
+ resolveDiffRows: () => resolveDiffRows,
104648
104977
  serializeDependencyTableToMarkdown: () => serializeDependencyTableToMarkdown,
104649
104978
  snapshotFromWorktree: () => snapshotFromWorktree
104650
104979
  })).changelogFunctions;