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