@savvy-web/silk 2.2.2 → 2.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changesets-changelog.cjs +90 -14
- package/changesets-changelog.d.cts +19 -0
- package/changesets-changelog.d.ts +19 -0
- package/changesets-changelog.js +91 -14
- package/changesets-markdownlint.cjs +90 -14
- package/changesets-markdownlint.d.cts +19 -0
- package/changesets-markdownlint.d.ts +19 -0
- package/changesets-markdownlint.js +91 -14
- package/package.json +4 -4
- package/tsconfig/node/dist/tsconfig.tsbuildinfo +1 -1
package/changesets-changelog.cjs
CHANGED
|
@@ -380,10 +380,27 @@ const ChangesetConfigLive = effect.Layer.effect(ChangesetConfig, effect.Effect.g
|
|
|
380
380
|
fixed: (root) => read(root).pipe(effect.Effect.map(effect.Option.match({
|
|
381
381
|
onNone: () => [],
|
|
382
382
|
onSome: (cfg) => cfg.fixed ?? []
|
|
383
|
-
})))
|
|
383
|
+
}))),
|
|
384
|
+
refresh: () => effect.Effect.sync(() => cache.clear())
|
|
384
385
|
};
|
|
385
386
|
}));
|
|
386
387
|
//#endregion
|
|
388
|
+
//#region ../silk-effects/dist/dev/pkg/utils/TrailingSlash.js
|
|
389
|
+
/**
|
|
390
|
+
* Trim trailing slashes from a string.
|
|
391
|
+
*
|
|
392
|
+
* @remarks
|
|
393
|
+
* Trims trailing slashes with an index scan rather than `/\/+$/`. That regex is
|
|
394
|
+
* unanchored at the start, so the engine retries the match from every position
|
|
395
|
+
* and degrades to O(n²) on a string of many slashes (CodeQL `js/polynomial-redos`).
|
|
396
|
+
* Only a trailing run of slashes is removed; interior slash runs are untouched.
|
|
397
|
+
*/
|
|
398
|
+
const trimTrailingSlashes = (s) => {
|
|
399
|
+
let end = s.length;
|
|
400
|
+
while (end > 0 && s[end - 1] === "/") end -= 1;
|
|
401
|
+
return s.slice(0, end);
|
|
402
|
+
};
|
|
403
|
+
//#endregion
|
|
387
404
|
//#region ../../node_modules/.pnpm/workspaces-effect@2.0.3_@effect+platform@0.96.2_effect@3.21.4__effect@3.21.4/node_modules/workspaces-effect/errors/CatalogAssemblyError.js
|
|
388
405
|
/**
|
|
389
406
|
* Base constant for {@link CatalogAssemblyError}.
|
|
@@ -10868,18 +10885,16 @@ var SilkPublishability = class {
|
|
|
10868
10885
|
* normalizes to `.`, matching how a root-directory target is recorded.
|
|
10869
10886
|
*
|
|
10870
10887
|
* @remarks
|
|
10871
|
-
* Trailing
|
|
10872
|
-
*
|
|
10873
|
-
*
|
|
10874
|
-
* `js/polynomial-redos`). `dir` reaches
|
|
10875
|
-
* the bundler writes but a consumer
|
|
10888
|
+
* Trailing-slash trimming is delegated to the shared `trimTrailingSlashes`
|
|
10889
|
+
* index-scan helper rather than `/\/+$/`: that regex is unanchored at the
|
|
10890
|
+
* start, so the engine retries the match from every position and degrades to
|
|
10891
|
+
* O(n²) on a path of many slashes (CodeQL `js/polynomial-redos`). `dir` reaches
|
|
10892
|
+
* here from `dist/prod/targets.json`, which the bundler writes but a consumer
|
|
10893
|
+
* repo could hand-edit.
|
|
10876
10894
|
*/
|
|
10877
10895
|
const normalizeDir = (dir) => {
|
|
10878
10896
|
const slashed = dir.replaceAll("\\", "/");
|
|
10879
|
-
const
|
|
10880
|
-
let end = withoutPrefix.length;
|
|
10881
|
-
while (end > 0 && withoutPrefix[end - 1] === "/") end -= 1;
|
|
10882
|
-
const normalized = withoutPrefix.slice(0, end);
|
|
10897
|
+
const normalized = trimTrailingSlashes(slashed.startsWith("./") ? slashed.slice(2) : slashed);
|
|
10883
10898
|
return normalized === "" ? "." : normalized;
|
|
10884
10899
|
};
|
|
10885
10900
|
/** True when a built target directory's package.json is `private: true`. Missing/unreadable/malformed → false. */
|
|
@@ -38499,9 +38514,14 @@ function makeShape$3(reader, discovery, fs) {
|
|
|
38499
38514
|
const inspected = yield* inspect(cwd);
|
|
38500
38515
|
return paths.map((p) => classifyOne(inspected, p));
|
|
38501
38516
|
});
|
|
38517
|
+
const refresh = () => effect.Effect.gen(function* () {
|
|
38518
|
+
cache.clear();
|
|
38519
|
+
yield* discovery.refresh();
|
|
38520
|
+
});
|
|
38502
38521
|
return {
|
|
38503
38522
|
inspect,
|
|
38504
|
-
classify
|
|
38523
|
+
classify,
|
|
38524
|
+
refresh
|
|
38505
38525
|
};
|
|
38506
38526
|
}
|
|
38507
38527
|
/**
|
|
@@ -38571,7 +38591,8 @@ const ConfigInspectorLive = effect.Layer.effect(ConfigInspector, effect.Effect.g
|
|
|
38571
38591
|
function makeConfigInspectorTest(fixed) {
|
|
38572
38592
|
return effect.Layer.succeed(ConfigInspector, {
|
|
38573
38593
|
inspect: () => effect.Effect.succeed(fixed),
|
|
38574
|
-
classify: (_cwd, paths) => effect.Effect.succeed(paths.map((p) => classifyOne(fixed, p)))
|
|
38594
|
+
classify: (_cwd, paths) => effect.Effect.succeed(paths.map((p) => classifyOne(fixed, p))),
|
|
38595
|
+
refresh: () => effect.Effect.void
|
|
38575
38596
|
});
|
|
38576
38597
|
}
|
|
38577
38598
|
//#endregion
|
|
@@ -39085,6 +39106,48 @@ function gitMergeBase(cwd, base) {
|
|
|
39085
39106
|
}
|
|
39086
39107
|
});
|
|
39087
39108
|
}
|
|
39109
|
+
/**
|
|
39110
|
+
* List the basenames of `.changeset/*.md` files tracked at `ref` (e.g. the
|
|
39111
|
+
* merge base), via `git ls-tree -r --name-only`. Used by `DepsRegen.plan()`
|
|
39112
|
+
* to protect changesets authored by already-merged PRs from being deleted by
|
|
39113
|
+
* an unrelated branch's regen run (#258).
|
|
39114
|
+
*
|
|
39115
|
+
* @remarks
|
|
39116
|
+
* Deliberately tolerant, unlike {@link gitMergeBase}: `cwd` may not be a git
|
|
39117
|
+
* repository at all (many `DepsRegen` unit tests pass synthetic refs like
|
|
39118
|
+
* `"BEFORE"`/`"AFTER"` against a bare tmpdir), and an unresolvable ref is a
|
|
39119
|
+
* plausible caller mistake rather than a fatal condition. Either failure mode
|
|
39120
|
+
* resolves to an empty set — "nothing protected" — rather than propagating a
|
|
39121
|
+
* {@link GitError}, so a missing/invalid git context degrades the
|
|
39122
|
+
* authorship filter to a no-op instead of blocking the whole plan.
|
|
39123
|
+
*
|
|
39124
|
+
* @internal
|
|
39125
|
+
*/
|
|
39126
|
+
function gitListChangesetFilesAtRef(cwd, ref) {
|
|
39127
|
+
return effect.Effect.sync(() => {
|
|
39128
|
+
try {
|
|
39129
|
+
const out = (0, node_child_process.execFileSync)("git", [
|
|
39130
|
+
"ls-tree",
|
|
39131
|
+
"-r",
|
|
39132
|
+
"--name-only",
|
|
39133
|
+
ref,
|
|
39134
|
+
"--",
|
|
39135
|
+
".changeset"
|
|
39136
|
+
], {
|
|
39137
|
+
cwd,
|
|
39138
|
+
encoding: "utf8",
|
|
39139
|
+
stdio: [
|
|
39140
|
+
"ignore",
|
|
39141
|
+
"pipe",
|
|
39142
|
+
"pipe"
|
|
39143
|
+
]
|
|
39144
|
+
});
|
|
39145
|
+
return new Set(out.split(/\r?\n/).filter((line) => line.trim().length > 0).map((path) => (0, node_path.basename)(path)));
|
|
39146
|
+
} catch {
|
|
39147
|
+
return /* @__PURE__ */ new Set();
|
|
39148
|
+
}
|
|
39149
|
+
});
|
|
39150
|
+
}
|
|
39088
39151
|
//#endregion
|
|
39089
39152
|
//#region ../silk-effects/dist/dev/pkg/changesets/utils/publishability.js
|
|
39090
39153
|
/**
|
|
@@ -39149,6 +39212,15 @@ function listPublishablePackageNames(packages, root) {
|
|
|
39149
39212
|
* first, then deleting the stale pure-dependency ones (so an interrupted
|
|
39150
39213
|
* run loses nothing and is safely re-runnable).
|
|
39151
39214
|
*
|
|
39215
|
+
* A pure dependency changeset is only ever a delete candidate when BOTH:
|
|
39216
|
+
* its package is in scope AND actually produced a fresh diff this run
|
|
39217
|
+
* (present in {@link RegenPlan.toWrite}), and the file was authored on
|
|
39218
|
+
* this branch rather than already committed at the merge-base ref. Either
|
|
39219
|
+
* gap silently destroyed a still-relevant release note before this was
|
|
39220
|
+
* fixed (#258) — a devDependency-only diff drops all its rows and was
|
|
39221
|
+
* never rewritten, and a changeset an earlier, already-merged PR
|
|
39222
|
+
* committed was swept away by an unrelated branch's regen run.
|
|
39223
|
+
*
|
|
39152
39224
|
* This is the single source of truth for regen/detect: the CLI commands
|
|
39153
39225
|
* and MCP tools are thin adapters over this service.
|
|
39154
39226
|
*
|
|
@@ -39368,7 +39440,8 @@ function makeShape$1(pit, inspector, discovery, detector, config, fs) {
|
|
|
39368
39440
|
const plan = (options) => effect.Effect.gen(function* () {
|
|
39369
39441
|
const resolvedCwd = (0, node_path.resolve)(options.cwd);
|
|
39370
39442
|
const changesetDir = (0, node_path.join)(resolvedCwd, ".changeset");
|
|
39371
|
-
yield*
|
|
39443
|
+
yield* inspector.refresh();
|
|
39444
|
+
yield* config.refresh();
|
|
39372
39445
|
let fromRef = options.from;
|
|
39373
39446
|
if (!fromRef) {
|
|
39374
39447
|
let baseBranch = options.base;
|
|
@@ -39405,7 +39478,10 @@ function makeShape$1(pit, inspector, discovery, detector, config, fs) {
|
|
|
39405
39478
|
}
|
|
39406
39479
|
const existingPure = yield* findPureDependencyChangesets(fs, changesetDir);
|
|
39407
39480
|
const skippedMixed = yield* findMixedDependencyChangesets(fs, changesetDir);
|
|
39408
|
-
const
|
|
39481
|
+
const rewrittenPackages = new Set(resolved.map((d) => d.package));
|
|
39482
|
+
const atMergeBase = yield* gitListChangesetFilesAtRef(resolvedCwd, fromRef);
|
|
39483
|
+
const authoredOnBranch = (file) => !atMergeBase.has((0, node_path.basename)(file));
|
|
39484
|
+
const toDelete = existingPure.filter((p) => inScopeFor(p.package) && rewrittenPackages.has(p.package) && authoredOnBranch(p.file));
|
|
39409
39485
|
const chosenFilenames = /* @__PURE__ */ new Set();
|
|
39410
39486
|
const toWrite = [];
|
|
39411
39487
|
for (const diff of resolved) {
|
|
@@ -7333,6 +7333,17 @@ interface ConfigInspectorShape {
|
|
|
7333
7333
|
* input path, in the same order
|
|
7334
7334
|
*/
|
|
7335
7335
|
readonly classify: (cwd: string, paths: ReadonlyArray<string>) => Effect.Effect<ReadonlyArray<Classification>, ConfigurationError>;
|
|
7336
|
+
/**
|
|
7337
|
+
* Drop the cached {@link InspectedConfig} for every previously-inspected
|
|
7338
|
+
* root, and refresh the underlying `WorkspaceDiscovery` snapshot. Callers
|
|
7339
|
+
* that hold this service across multiple logical operations in a single
|
|
7340
|
+
* process (e.g. a long-lived MCP server) must call this before an
|
|
7341
|
+
* operation that needs to observe on-disk edits made since the last
|
|
7342
|
+
* `inspect`/`classify` call — the cache never expires on its own.
|
|
7343
|
+
*
|
|
7344
|
+
* @returns An Effect that clears the cache and succeeds with `void`.
|
|
7345
|
+
*/
|
|
7346
|
+
readonly refresh: () => Effect.Effect<void>;
|
|
7336
7347
|
}
|
|
7337
7348
|
/**
|
|
7338
7349
|
* Base class for {@link ConfigInspector}.
|
|
@@ -8028,6 +8039,14 @@ declare const ChangesetConfig_base: Context.TagClass<ChangesetConfig, "@savvy-we
|
|
|
8028
8039
|
readonly ignorePatterns: (root: string) => Effect.Effect<ReadonlyArray<string>>;
|
|
8029
8040
|
readonly isIgnored: (name: string, root: string) => Effect.Effect<boolean>;
|
|
8030
8041
|
readonly fixed: (root: string) => Effect.Effect<ReadonlyArray<ReadonlyArray<string>>>;
|
|
8042
|
+
/**
|
|
8043
|
+
* Drop the cached read for every previously-read root. Callers that hold
|
|
8044
|
+
* this service across multiple logical operations in a single process
|
|
8045
|
+
* (e.g. a long-lived MCP server) must call this before an operation that
|
|
8046
|
+
* needs to observe an on-disk edit made since the last accessor call —
|
|
8047
|
+
* the cache never expires on its own.
|
|
8048
|
+
*/
|
|
8049
|
+
readonly refresh: () => Effect.Effect<void>;
|
|
8031
8050
|
}>;
|
|
8032
8051
|
/**
|
|
8033
8052
|
* Accessor service over a workspace root's `.changeset/config.json`.
|
|
@@ -7333,6 +7333,17 @@ interface ConfigInspectorShape {
|
|
|
7333
7333
|
* input path, in the same order
|
|
7334
7334
|
*/
|
|
7335
7335
|
readonly classify: (cwd: string, paths: ReadonlyArray<string>) => Effect.Effect<ReadonlyArray<Classification>, ConfigurationError>;
|
|
7336
|
+
/**
|
|
7337
|
+
* Drop the cached {@link InspectedConfig} for every previously-inspected
|
|
7338
|
+
* root, and refresh the underlying `WorkspaceDiscovery` snapshot. Callers
|
|
7339
|
+
* that hold this service across multiple logical operations in a single
|
|
7340
|
+
* process (e.g. a long-lived MCP server) must call this before an
|
|
7341
|
+
* operation that needs to observe on-disk edits made since the last
|
|
7342
|
+
* `inspect`/`classify` call — the cache never expires on its own.
|
|
7343
|
+
*
|
|
7344
|
+
* @returns An Effect that clears the cache and succeeds with `void`.
|
|
7345
|
+
*/
|
|
7346
|
+
readonly refresh: () => Effect.Effect<void>;
|
|
7336
7347
|
}
|
|
7337
7348
|
/**
|
|
7338
7349
|
* Base class for {@link ConfigInspector}.
|
|
@@ -8028,6 +8039,14 @@ declare const ChangesetConfig_base: Context.TagClass<ChangesetConfig, "@savvy-we
|
|
|
8028
8039
|
readonly ignorePatterns: (root: string) => Effect.Effect<ReadonlyArray<string>>;
|
|
8029
8040
|
readonly isIgnored: (name: string, root: string) => Effect.Effect<boolean>;
|
|
8030
8041
|
readonly fixed: (root: string) => Effect.Effect<ReadonlyArray<ReadonlyArray<string>>>;
|
|
8042
|
+
/**
|
|
8043
|
+
* Drop the cached read for every previously-read root. Callers that hold
|
|
8044
|
+
* this service across multiple logical operations in a single process
|
|
8045
|
+
* (e.g. a long-lived MCP server) must call this before an operation that
|
|
8046
|
+
* needs to observe an on-disk edit made since the last accessor call —
|
|
8047
|
+
* the cache never expires on its own.
|
|
8048
|
+
*/
|
|
8049
|
+
readonly refresh: () => Effect.Effect<void>;
|
|
8031
8050
|
}>;
|
|
8032
8051
|
/**
|
|
8033
8052
|
* Accessor service over a workspace root's `.changeset/config.json`.
|
package/changesets-changelog.js
CHANGED
|
@@ -394,10 +394,28 @@ const ChangesetConfigLive = Layer.effect(ChangesetConfig, Effect.gen(function* (
|
|
|
394
394
|
fixed: (root) => read(root).pipe(Effect.map(Option.match({
|
|
395
395
|
onNone: () => [],
|
|
396
396
|
onSome: (cfg) => cfg.fixed ?? []
|
|
397
|
-
})))
|
|
397
|
+
}))),
|
|
398
|
+
refresh: () => Effect.sync(() => cache.clear())
|
|
398
399
|
};
|
|
399
400
|
}));
|
|
400
401
|
|
|
402
|
+
//#endregion
|
|
403
|
+
//#region ../silk-effects/dist/dev/pkg/utils/TrailingSlash.js
|
|
404
|
+
/**
|
|
405
|
+
* Trim trailing slashes from a string.
|
|
406
|
+
*
|
|
407
|
+
* @remarks
|
|
408
|
+
* Trims trailing slashes with an index scan rather than `/\/+$/`. That regex is
|
|
409
|
+
* unanchored at the start, so the engine retries the match from every position
|
|
410
|
+
* and degrades to O(n²) on a string of many slashes (CodeQL `js/polynomial-redos`).
|
|
411
|
+
* Only a trailing run of slashes is removed; interior slash runs are untouched.
|
|
412
|
+
*/
|
|
413
|
+
const trimTrailingSlashes = (s) => {
|
|
414
|
+
let end = s.length;
|
|
415
|
+
while (end > 0 && s[end - 1] === "/") end -= 1;
|
|
416
|
+
return s.slice(0, end);
|
|
417
|
+
};
|
|
418
|
+
|
|
401
419
|
//#endregion
|
|
402
420
|
//#region ../../node_modules/.pnpm/workspaces-effect@2.0.3_@effect+platform@0.96.2_effect@3.21.4__effect@3.21.4/node_modules/workspaces-effect/errors/CatalogAssemblyError.js
|
|
403
421
|
/**
|
|
@@ -11064,18 +11082,16 @@ var SilkPublishability = class {
|
|
|
11064
11082
|
* normalizes to `.`, matching how a root-directory target is recorded.
|
|
11065
11083
|
*
|
|
11066
11084
|
* @remarks
|
|
11067
|
-
* Trailing
|
|
11068
|
-
*
|
|
11069
|
-
*
|
|
11070
|
-
* `js/polynomial-redos`). `dir` reaches
|
|
11071
|
-
* the bundler writes but a consumer
|
|
11085
|
+
* Trailing-slash trimming is delegated to the shared `trimTrailingSlashes`
|
|
11086
|
+
* index-scan helper rather than `/\/+$/`: that regex is unanchored at the
|
|
11087
|
+
* start, so the engine retries the match from every position and degrades to
|
|
11088
|
+
* O(n²) on a path of many slashes (CodeQL `js/polynomial-redos`). `dir` reaches
|
|
11089
|
+
* here from `dist/prod/targets.json`, which the bundler writes but a consumer
|
|
11090
|
+
* repo could hand-edit.
|
|
11072
11091
|
*/
|
|
11073
11092
|
const normalizeDir = (dir) => {
|
|
11074
11093
|
const slashed = dir.replaceAll("\\", "/");
|
|
11075
|
-
const
|
|
11076
|
-
let end = withoutPrefix.length;
|
|
11077
|
-
while (end > 0 && withoutPrefix[end - 1] === "/") end -= 1;
|
|
11078
|
-
const normalized = withoutPrefix.slice(0, end);
|
|
11094
|
+
const normalized = trimTrailingSlashes(slashed.startsWith("./") ? slashed.slice(2) : slashed);
|
|
11079
11095
|
return normalized === "" ? "." : normalized;
|
|
11080
11096
|
};
|
|
11081
11097
|
/** True when a built target directory's package.json is `private: true`. Missing/unreadable/malformed → false. */
|
|
@@ -38943,9 +38959,14 @@ function makeShape$3(reader, discovery, fs) {
|
|
|
38943
38959
|
const inspected = yield* inspect(cwd);
|
|
38944
38960
|
return paths.map((p) => classifyOne(inspected, p));
|
|
38945
38961
|
});
|
|
38962
|
+
const refresh = () => Effect.gen(function* () {
|
|
38963
|
+
cache.clear();
|
|
38964
|
+
yield* discovery.refresh();
|
|
38965
|
+
});
|
|
38946
38966
|
return {
|
|
38947
38967
|
inspect,
|
|
38948
|
-
classify
|
|
38968
|
+
classify,
|
|
38969
|
+
refresh
|
|
38949
38970
|
};
|
|
38950
38971
|
}
|
|
38951
38972
|
/**
|
|
@@ -39015,7 +39036,8 @@ const ConfigInspectorLive = Layer.effect(ConfigInspector, Effect.gen(function* (
|
|
|
39015
39036
|
function makeConfigInspectorTest(fixed) {
|
|
39016
39037
|
return Layer.succeed(ConfigInspector, {
|
|
39017
39038
|
inspect: () => Effect.succeed(fixed),
|
|
39018
|
-
classify: (_cwd, paths) => Effect.succeed(paths.map((p) => classifyOne(fixed, p)))
|
|
39039
|
+
classify: (_cwd, paths) => Effect.succeed(paths.map((p) => classifyOne(fixed, p))),
|
|
39040
|
+
refresh: () => Effect.void
|
|
39019
39041
|
});
|
|
39020
39042
|
}
|
|
39021
39043
|
|
|
@@ -39533,6 +39555,48 @@ function gitMergeBase(cwd, base) {
|
|
|
39533
39555
|
}
|
|
39534
39556
|
});
|
|
39535
39557
|
}
|
|
39558
|
+
/**
|
|
39559
|
+
* List the basenames of `.changeset/*.md` files tracked at `ref` (e.g. the
|
|
39560
|
+
* merge base), via `git ls-tree -r --name-only`. Used by `DepsRegen.plan()`
|
|
39561
|
+
* to protect changesets authored by already-merged PRs from being deleted by
|
|
39562
|
+
* an unrelated branch's regen run (#258).
|
|
39563
|
+
*
|
|
39564
|
+
* @remarks
|
|
39565
|
+
* Deliberately tolerant, unlike {@link gitMergeBase}: `cwd` may not be a git
|
|
39566
|
+
* repository at all (many `DepsRegen` unit tests pass synthetic refs like
|
|
39567
|
+
* `"BEFORE"`/`"AFTER"` against a bare tmpdir), and an unresolvable ref is a
|
|
39568
|
+
* plausible caller mistake rather than a fatal condition. Either failure mode
|
|
39569
|
+
* resolves to an empty set — "nothing protected" — rather than propagating a
|
|
39570
|
+
* {@link GitError}, so a missing/invalid git context degrades the
|
|
39571
|
+
* authorship filter to a no-op instead of blocking the whole plan.
|
|
39572
|
+
*
|
|
39573
|
+
* @internal
|
|
39574
|
+
*/
|
|
39575
|
+
function gitListChangesetFilesAtRef(cwd, ref) {
|
|
39576
|
+
return Effect.sync(() => {
|
|
39577
|
+
try {
|
|
39578
|
+
const out = execFileSync("git", [
|
|
39579
|
+
"ls-tree",
|
|
39580
|
+
"-r",
|
|
39581
|
+
"--name-only",
|
|
39582
|
+
ref,
|
|
39583
|
+
"--",
|
|
39584
|
+
".changeset"
|
|
39585
|
+
], {
|
|
39586
|
+
cwd,
|
|
39587
|
+
encoding: "utf8",
|
|
39588
|
+
stdio: [
|
|
39589
|
+
"ignore",
|
|
39590
|
+
"pipe",
|
|
39591
|
+
"pipe"
|
|
39592
|
+
]
|
|
39593
|
+
});
|
|
39594
|
+
return new Set(out.split(/\r?\n/).filter((line) => line.trim().length > 0).map((path) => basename(path)));
|
|
39595
|
+
} catch {
|
|
39596
|
+
return /* @__PURE__ */ new Set();
|
|
39597
|
+
}
|
|
39598
|
+
});
|
|
39599
|
+
}
|
|
39536
39600
|
|
|
39537
39601
|
//#endregion
|
|
39538
39602
|
//#region ../silk-effects/dist/dev/pkg/changesets/utils/publishability.js
|
|
@@ -39599,6 +39663,15 @@ function listPublishablePackageNames(packages, root) {
|
|
|
39599
39663
|
* first, then deleting the stale pure-dependency ones (so an interrupted
|
|
39600
39664
|
* run loses nothing and is safely re-runnable).
|
|
39601
39665
|
*
|
|
39666
|
+
* A pure dependency changeset is only ever a delete candidate when BOTH:
|
|
39667
|
+
* its package is in scope AND actually produced a fresh diff this run
|
|
39668
|
+
* (present in {@link RegenPlan.toWrite}), and the file was authored on
|
|
39669
|
+
* this branch rather than already committed at the merge-base ref. Either
|
|
39670
|
+
* gap silently destroyed a still-relevant release note before this was
|
|
39671
|
+
* fixed (#258) — a devDependency-only diff drops all its rows and was
|
|
39672
|
+
* never rewritten, and a changeset an earlier, already-merged PR
|
|
39673
|
+
* committed was swept away by an unrelated branch's regen run.
|
|
39674
|
+
*
|
|
39602
39675
|
* This is the single source of truth for regen/detect: the CLI commands
|
|
39603
39676
|
* and MCP tools are thin adapters over this service.
|
|
39604
39677
|
*
|
|
@@ -39818,7 +39891,8 @@ function makeShape$1(pit, inspector, discovery, detector, config, fs) {
|
|
|
39818
39891
|
const plan = (options) => Effect.gen(function* () {
|
|
39819
39892
|
const resolvedCwd = resolve(options.cwd);
|
|
39820
39893
|
const changesetDir = join(resolvedCwd, ".changeset");
|
|
39821
|
-
yield*
|
|
39894
|
+
yield* inspector.refresh();
|
|
39895
|
+
yield* config.refresh();
|
|
39822
39896
|
let fromRef = options.from;
|
|
39823
39897
|
if (!fromRef) {
|
|
39824
39898
|
let baseBranch = options.base;
|
|
@@ -39855,7 +39929,10 @@ function makeShape$1(pit, inspector, discovery, detector, config, fs) {
|
|
|
39855
39929
|
}
|
|
39856
39930
|
const existingPure = yield* findPureDependencyChangesets(fs, changesetDir);
|
|
39857
39931
|
const skippedMixed = yield* findMixedDependencyChangesets(fs, changesetDir);
|
|
39858
|
-
const
|
|
39932
|
+
const rewrittenPackages = new Set(resolved.map((d) => d.package));
|
|
39933
|
+
const atMergeBase = yield* gitListChangesetFilesAtRef(resolvedCwd, fromRef);
|
|
39934
|
+
const authoredOnBranch = (file) => !atMergeBase.has(basename(file));
|
|
39935
|
+
const toDelete = existingPure.filter((p) => inScopeFor(p.package) && rewrittenPackages.has(p.package) && authoredOnBranch(p.file));
|
|
39859
39936
|
const chosenFilenames = /* @__PURE__ */ new Set();
|
|
39860
39937
|
const toWrite = [];
|
|
39861
39938
|
for (const diff of resolved) {
|
|
@@ -384,10 +384,27 @@ const ChangesetConfigLive = effect.Layer.effect(ChangesetConfig, effect.Effect.g
|
|
|
384
384
|
fixed: (root) => read(root).pipe(effect.Effect.map(effect.Option.match({
|
|
385
385
|
onNone: () => [],
|
|
386
386
|
onSome: (cfg) => cfg.fixed ?? []
|
|
387
|
-
})))
|
|
387
|
+
}))),
|
|
388
|
+
refresh: () => effect.Effect.sync(() => cache.clear())
|
|
388
389
|
};
|
|
389
390
|
}));
|
|
390
391
|
//#endregion
|
|
392
|
+
//#region ../silk-effects/dist/dev/pkg/utils/TrailingSlash.js
|
|
393
|
+
/**
|
|
394
|
+
* Trim trailing slashes from a string.
|
|
395
|
+
*
|
|
396
|
+
* @remarks
|
|
397
|
+
* Trims trailing slashes with an index scan rather than `/\/+$/`. That regex is
|
|
398
|
+
* unanchored at the start, so the engine retries the match from every position
|
|
399
|
+
* and degrades to O(n²) on a string of many slashes (CodeQL `js/polynomial-redos`).
|
|
400
|
+
* Only a trailing run of slashes is removed; interior slash runs are untouched.
|
|
401
|
+
*/
|
|
402
|
+
const trimTrailingSlashes = (s) => {
|
|
403
|
+
let end = s.length;
|
|
404
|
+
while (end > 0 && s[end - 1] === "/") end -= 1;
|
|
405
|
+
return s.slice(0, end);
|
|
406
|
+
};
|
|
407
|
+
//#endregion
|
|
391
408
|
//#region ../../node_modules/.pnpm/workspaces-effect@2.0.3_@effect+platform@0.96.2_effect@3.21.4__effect@3.21.4/node_modules/workspaces-effect/errors/CatalogAssemblyError.js
|
|
392
409
|
/**
|
|
393
410
|
* Base constant for {@link CatalogAssemblyError}.
|
|
@@ -10872,18 +10889,16 @@ var SilkPublishability = class {
|
|
|
10872
10889
|
* normalizes to `.`, matching how a root-directory target is recorded.
|
|
10873
10890
|
*
|
|
10874
10891
|
* @remarks
|
|
10875
|
-
* Trailing
|
|
10876
|
-
*
|
|
10877
|
-
*
|
|
10878
|
-
* `js/polynomial-redos`). `dir` reaches
|
|
10879
|
-
* the bundler writes but a consumer
|
|
10892
|
+
* Trailing-slash trimming is delegated to the shared `trimTrailingSlashes`
|
|
10893
|
+
* index-scan helper rather than `/\/+$/`: that regex is unanchored at the
|
|
10894
|
+
* start, so the engine retries the match from every position and degrades to
|
|
10895
|
+
* O(n²) on a path of many slashes (CodeQL `js/polynomial-redos`). `dir` reaches
|
|
10896
|
+
* here from `dist/prod/targets.json`, which the bundler writes but a consumer
|
|
10897
|
+
* repo could hand-edit.
|
|
10880
10898
|
*/
|
|
10881
10899
|
const normalizeDir = (dir) => {
|
|
10882
10900
|
const slashed = dir.replaceAll("\\", "/");
|
|
10883
|
-
const
|
|
10884
|
-
let end = withoutPrefix.length;
|
|
10885
|
-
while (end > 0 && withoutPrefix[end - 1] === "/") end -= 1;
|
|
10886
|
-
const normalized = withoutPrefix.slice(0, end);
|
|
10901
|
+
const normalized = trimTrailingSlashes(slashed.startsWith("./") ? slashed.slice(2) : slashed);
|
|
10887
10902
|
return normalized === "" ? "." : normalized;
|
|
10888
10903
|
};
|
|
10889
10904
|
/** True when a built target directory's package.json is `private: true`. Missing/unreadable/malformed → false. */
|
|
@@ -38503,9 +38518,14 @@ function makeShape$3(reader, discovery, fs) {
|
|
|
38503
38518
|
const inspected = yield* inspect(cwd);
|
|
38504
38519
|
return paths.map((p) => classifyOne(inspected, p));
|
|
38505
38520
|
});
|
|
38521
|
+
const refresh = () => effect.Effect.gen(function* () {
|
|
38522
|
+
cache.clear();
|
|
38523
|
+
yield* discovery.refresh();
|
|
38524
|
+
});
|
|
38506
38525
|
return {
|
|
38507
38526
|
inspect,
|
|
38508
|
-
classify
|
|
38527
|
+
classify,
|
|
38528
|
+
refresh
|
|
38509
38529
|
};
|
|
38510
38530
|
}
|
|
38511
38531
|
/**
|
|
@@ -38575,7 +38595,8 @@ const ConfigInspectorLive = effect.Layer.effect(ConfigInspector, effect.Effect.g
|
|
|
38575
38595
|
function makeConfigInspectorTest(fixed) {
|
|
38576
38596
|
return effect.Layer.succeed(ConfigInspector, {
|
|
38577
38597
|
inspect: () => effect.Effect.succeed(fixed),
|
|
38578
|
-
classify: (_cwd, paths) => effect.Effect.succeed(paths.map((p) => classifyOne(fixed, p)))
|
|
38598
|
+
classify: (_cwd, paths) => effect.Effect.succeed(paths.map((p) => classifyOne(fixed, p))),
|
|
38599
|
+
refresh: () => effect.Effect.void
|
|
38579
38600
|
});
|
|
38580
38601
|
}
|
|
38581
38602
|
//#endregion
|
|
@@ -39089,6 +39110,48 @@ function gitMergeBase(cwd, base) {
|
|
|
39089
39110
|
}
|
|
39090
39111
|
});
|
|
39091
39112
|
}
|
|
39113
|
+
/**
|
|
39114
|
+
* List the basenames of `.changeset/*.md` files tracked at `ref` (e.g. the
|
|
39115
|
+
* merge base), via `git ls-tree -r --name-only`. Used by `DepsRegen.plan()`
|
|
39116
|
+
* to protect changesets authored by already-merged PRs from being deleted by
|
|
39117
|
+
* an unrelated branch's regen run (#258).
|
|
39118
|
+
*
|
|
39119
|
+
* @remarks
|
|
39120
|
+
* Deliberately tolerant, unlike {@link gitMergeBase}: `cwd` may not be a git
|
|
39121
|
+
* repository at all (many `DepsRegen` unit tests pass synthetic refs like
|
|
39122
|
+
* `"BEFORE"`/`"AFTER"` against a bare tmpdir), and an unresolvable ref is a
|
|
39123
|
+
* plausible caller mistake rather than a fatal condition. Either failure mode
|
|
39124
|
+
* resolves to an empty set — "nothing protected" — rather than propagating a
|
|
39125
|
+
* {@link GitError}, so a missing/invalid git context degrades the
|
|
39126
|
+
* authorship filter to a no-op instead of blocking the whole plan.
|
|
39127
|
+
*
|
|
39128
|
+
* @internal
|
|
39129
|
+
*/
|
|
39130
|
+
function gitListChangesetFilesAtRef(cwd, ref) {
|
|
39131
|
+
return effect.Effect.sync(() => {
|
|
39132
|
+
try {
|
|
39133
|
+
const out = (0, node_child_process.execFileSync)("git", [
|
|
39134
|
+
"ls-tree",
|
|
39135
|
+
"-r",
|
|
39136
|
+
"--name-only",
|
|
39137
|
+
ref,
|
|
39138
|
+
"--",
|
|
39139
|
+
".changeset"
|
|
39140
|
+
], {
|
|
39141
|
+
cwd,
|
|
39142
|
+
encoding: "utf8",
|
|
39143
|
+
stdio: [
|
|
39144
|
+
"ignore",
|
|
39145
|
+
"pipe",
|
|
39146
|
+
"pipe"
|
|
39147
|
+
]
|
|
39148
|
+
});
|
|
39149
|
+
return new Set(out.split(/\r?\n/).filter((line) => line.trim().length > 0).map((path) => (0, node_path.basename)(path)));
|
|
39150
|
+
} catch {
|
|
39151
|
+
return /* @__PURE__ */ new Set();
|
|
39152
|
+
}
|
|
39153
|
+
});
|
|
39154
|
+
}
|
|
39092
39155
|
//#endregion
|
|
39093
39156
|
//#region ../silk-effects/dist/dev/pkg/changesets/utils/publishability.js
|
|
39094
39157
|
/**
|
|
@@ -39153,6 +39216,15 @@ function listPublishablePackageNames(packages, root) {
|
|
|
39153
39216
|
* first, then deleting the stale pure-dependency ones (so an interrupted
|
|
39154
39217
|
* run loses nothing and is safely re-runnable).
|
|
39155
39218
|
*
|
|
39219
|
+
* A pure dependency changeset is only ever a delete candidate when BOTH:
|
|
39220
|
+
* its package is in scope AND actually produced a fresh diff this run
|
|
39221
|
+
* (present in {@link RegenPlan.toWrite}), and the file was authored on
|
|
39222
|
+
* this branch rather than already committed at the merge-base ref. Either
|
|
39223
|
+
* gap silently destroyed a still-relevant release note before this was
|
|
39224
|
+
* fixed (#258) — a devDependency-only diff drops all its rows and was
|
|
39225
|
+
* never rewritten, and a changeset an earlier, already-merged PR
|
|
39226
|
+
* committed was swept away by an unrelated branch's regen run.
|
|
39227
|
+
*
|
|
39156
39228
|
* This is the single source of truth for regen/detect: the CLI commands
|
|
39157
39229
|
* and MCP tools are thin adapters over this service.
|
|
39158
39230
|
*
|
|
@@ -39372,7 +39444,8 @@ function makeShape$1(pit, inspector, discovery, detector, config, fs) {
|
|
|
39372
39444
|
const plan = (options) => effect.Effect.gen(function* () {
|
|
39373
39445
|
const resolvedCwd = (0, node_path.resolve)(options.cwd);
|
|
39374
39446
|
const changesetDir = (0, node_path.join)(resolvedCwd, ".changeset");
|
|
39375
|
-
yield*
|
|
39447
|
+
yield* inspector.refresh();
|
|
39448
|
+
yield* config.refresh();
|
|
39376
39449
|
let fromRef = options.from;
|
|
39377
39450
|
if (!fromRef) {
|
|
39378
39451
|
let baseBranch = options.base;
|
|
@@ -39409,7 +39482,10 @@ function makeShape$1(pit, inspector, discovery, detector, config, fs) {
|
|
|
39409
39482
|
}
|
|
39410
39483
|
const existingPure = yield* findPureDependencyChangesets(fs, changesetDir);
|
|
39411
39484
|
const skippedMixed = yield* findMixedDependencyChangesets(fs, changesetDir);
|
|
39412
|
-
const
|
|
39485
|
+
const rewrittenPackages = new Set(resolved.map((d) => d.package));
|
|
39486
|
+
const atMergeBase = yield* gitListChangesetFilesAtRef(resolvedCwd, fromRef);
|
|
39487
|
+
const authoredOnBranch = (file) => !atMergeBase.has((0, node_path.basename)(file));
|
|
39488
|
+
const toDelete = existingPure.filter((p) => inScopeFor(p.package) && rewrittenPackages.has(p.package) && authoredOnBranch(p.file));
|
|
39413
39489
|
const chosenFilenames = /* @__PURE__ */ new Set();
|
|
39414
39490
|
const toWrite = [];
|
|
39415
39491
|
for (const diff of resolved) {
|
|
@@ -7333,6 +7333,17 @@ interface ConfigInspectorShape {
|
|
|
7333
7333
|
* input path, in the same order
|
|
7334
7334
|
*/
|
|
7335
7335
|
readonly classify: (cwd: string, paths: ReadonlyArray<string>) => Effect.Effect<ReadonlyArray<Classification>, ConfigurationError>;
|
|
7336
|
+
/**
|
|
7337
|
+
* Drop the cached {@link InspectedConfig} for every previously-inspected
|
|
7338
|
+
* root, and refresh the underlying `WorkspaceDiscovery` snapshot. Callers
|
|
7339
|
+
* that hold this service across multiple logical operations in a single
|
|
7340
|
+
* process (e.g. a long-lived MCP server) must call this before an
|
|
7341
|
+
* operation that needs to observe on-disk edits made since the last
|
|
7342
|
+
* `inspect`/`classify` call — the cache never expires on its own.
|
|
7343
|
+
*
|
|
7344
|
+
* @returns An Effect that clears the cache and succeeds with `void`.
|
|
7345
|
+
*/
|
|
7346
|
+
readonly refresh: () => Effect.Effect<void>;
|
|
7336
7347
|
}
|
|
7337
7348
|
/**
|
|
7338
7349
|
* Base class for {@link ConfigInspector}.
|
|
@@ -8028,6 +8039,14 @@ declare const ChangesetConfig_base: Context.TagClass<ChangesetConfig, "@savvy-we
|
|
|
8028
8039
|
readonly ignorePatterns: (root: string) => Effect.Effect<ReadonlyArray<string>>;
|
|
8029
8040
|
readonly isIgnored: (name: string, root: string) => Effect.Effect<boolean>;
|
|
8030
8041
|
readonly fixed: (root: string) => Effect.Effect<ReadonlyArray<ReadonlyArray<string>>>;
|
|
8042
|
+
/**
|
|
8043
|
+
* Drop the cached read for every previously-read root. Callers that hold
|
|
8044
|
+
* this service across multiple logical operations in a single process
|
|
8045
|
+
* (e.g. a long-lived MCP server) must call this before an operation that
|
|
8046
|
+
* needs to observe an on-disk edit made since the last accessor call —
|
|
8047
|
+
* the cache never expires on its own.
|
|
8048
|
+
*/
|
|
8049
|
+
readonly refresh: () => Effect.Effect<void>;
|
|
8031
8050
|
}>;
|
|
8032
8051
|
/**
|
|
8033
8052
|
* Accessor service over a workspace root's `.changeset/config.json`.
|
|
@@ -7333,6 +7333,17 @@ interface ConfigInspectorShape {
|
|
|
7333
7333
|
* input path, in the same order
|
|
7334
7334
|
*/
|
|
7335
7335
|
readonly classify: (cwd: string, paths: ReadonlyArray<string>) => Effect.Effect<ReadonlyArray<Classification>, ConfigurationError>;
|
|
7336
|
+
/**
|
|
7337
|
+
* Drop the cached {@link InspectedConfig} for every previously-inspected
|
|
7338
|
+
* root, and refresh the underlying `WorkspaceDiscovery` snapshot. Callers
|
|
7339
|
+
* that hold this service across multiple logical operations in a single
|
|
7340
|
+
* process (e.g. a long-lived MCP server) must call this before an
|
|
7341
|
+
* operation that needs to observe on-disk edits made since the last
|
|
7342
|
+
* `inspect`/`classify` call — the cache never expires on its own.
|
|
7343
|
+
*
|
|
7344
|
+
* @returns An Effect that clears the cache and succeeds with `void`.
|
|
7345
|
+
*/
|
|
7346
|
+
readonly refresh: () => Effect.Effect<void>;
|
|
7336
7347
|
}
|
|
7337
7348
|
/**
|
|
7338
7349
|
* Base class for {@link ConfigInspector}.
|
|
@@ -8028,6 +8039,14 @@ declare const ChangesetConfig_base: Context.TagClass<ChangesetConfig, "@savvy-we
|
|
|
8028
8039
|
readonly ignorePatterns: (root: string) => Effect.Effect<ReadonlyArray<string>>;
|
|
8029
8040
|
readonly isIgnored: (name: string, root: string) => Effect.Effect<boolean>;
|
|
8030
8041
|
readonly fixed: (root: string) => Effect.Effect<ReadonlyArray<ReadonlyArray<string>>>;
|
|
8042
|
+
/**
|
|
8043
|
+
* Drop the cached read for every previously-read root. Callers that hold
|
|
8044
|
+
* this service across multiple logical operations in a single process
|
|
8045
|
+
* (e.g. a long-lived MCP server) must call this before an operation that
|
|
8046
|
+
* needs to observe an on-disk edit made since the last accessor call —
|
|
8047
|
+
* the cache never expires on its own.
|
|
8048
|
+
*/
|
|
8049
|
+
readonly refresh: () => Effect.Effect<void>;
|
|
8031
8050
|
}>;
|
|
8032
8051
|
/**
|
|
8033
8052
|
* Accessor service over a workspace root's `.changeset/config.json`.
|