@savvy-web/silk-effects 3.3.1 → 4.0.1

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.
Files changed (72) hide show
  1. package/changesets/changelog/getReleaseLine.js +1 -1
  2. package/changesets/constants.js +0 -18
  3. package/changesets/index.js +10 -24
  4. package/changesets/schemas/changeset.js +8 -4
  5. package/changesets/schemas/dependency-table.js +15 -4
  6. package/changesets/schemas/git.js +7 -2
  7. package/changesets/schemas/github.js +4 -4
  8. package/changesets/schemas/options.js +3 -3
  9. package/changesets/schemas/package-scope.js +2 -5
  10. package/changesets/schemas/primitives.js +2 -2
  11. package/changesets/schemas/release-plan.js +12 -8
  12. package/changesets/schemas/version-files.js +4 -4
  13. package/changesets/services/branch-analyzer.js +60 -191
  14. package/changesets/services/changelog.js +2 -15
  15. package/changesets/services/config-inspector.js +109 -75
  16. package/changesets/services/deps-regen.js +58 -32
  17. package/changesets/services/github.js +3 -16
  18. package/changesets/services/maintenance-reason.js +5 -1
  19. package/changesets/services/markdown.js +3 -16
  20. package/changesets/services/release-planner.js +4 -13
  21. package/changesets/utils/commit-parser.js +0 -18
  22. package/changesets/utils/dep-diff.js +0 -27
  23. package/changesets/utils/git.js +15 -50
  24. package/changesets/utils/issue-refs.js +0 -13
  25. package/changesets/utils/logger.js +0 -13
  26. package/changesets/utils/markdown-link.js +0 -14
  27. package/changesets/utils/publishability.js +11 -24
  28. package/changesets/utils/strip-frontmatter.js +0 -19
  29. package/changesets/utils/version-files.js +85 -51
  30. package/commitlint/config/schema.js +9 -5
  31. package/commitlint/detection/scopes.js +2 -7
  32. package/commitlint/formatter/messages.js +0 -5
  33. package/commitlint/hook/diagnostics/branch.js +12 -12
  34. package/commitlint/hook/diagnostics/open-issues.js +13 -7
  35. package/commitlint/hook/diagnostics/signing.js +30 -37
  36. package/commitlint/hook/envelope.js +2 -8
  37. package/commitlint/hook/silence-logger.js +14 -12
  38. package/index.d.ts +897 -932
  39. package/lint/cli/templates/markdownlint.gen.js +0 -9
  40. package/lint/handlers/PnpmWorkspace.js +26 -12
  41. package/lint/utils/Filter.js +0 -13
  42. package/lint/utils/Workspace.js +10 -6
  43. package/package.json +11 -10
  44. package/repos/index.js +3 -5
  45. package/repos/schemas/manifest.js +7 -13
  46. package/repos/schemas/reports.js +5 -1
  47. package/repos/services/config-store.js +6 -10
  48. package/repos/services/manager.js +52 -112
  49. package/schemas/CommentStyle.js +1 -1
  50. package/schemas/ResolvedTool.js +52 -17
  51. package/schemas/SectionBlock.js +1 -1
  52. package/schemas/SectionDefinition.js +2 -2
  53. package/schemas/TagStrategySchemas.js +1 -1
  54. package/schemas/ToolDefinition.js +1 -1
  55. package/schemas/ToolResults.js +1 -1
  56. package/schemas/VersioningSchemas.js +28 -9
  57. package/schemas/WorkspaceAnalysisSchemas.js +27 -17
  58. package/services/BiomeSchemaSync.js +7 -8
  59. package/services/ChangesetConfig.js +2 -2
  60. package/services/ChangesetConfigReader.js +7 -8
  61. package/services/ConfigDiscovery.js +5 -6
  62. package/services/ManagedSection.js +3 -4
  63. package/services/SilkPublishability.js +38 -15
  64. package/services/SilkWorkspaceAnalyzer.js +7 -9
  65. package/services/TagStrategy.js +3 -3
  66. package/services/ToolDiscovery.js +22 -21
  67. package/services/VersioningStrategy.js +2 -2
  68. package/tsdoc-metadata.json +1 -1
  69. package/turbo/schemas/DryRun.js +8 -14
  70. package/turbo/schemas/results.js +8 -8
  71. package/turbo/services/TurboInspector.js +30 -23
  72. package/utils/ToolCommand.js +38 -12
@@ -5,9 +5,8 @@ import { WorkspaceAnalysisError } from "../errors/WorkspaceAnalysisError.js";
5
5
  import { AnalyzedWorkspace, WorkspaceAnalysis } from "../schemas/WorkspaceAnalysisSchemas.js";
6
6
  import { TagStrategy } from "./TagStrategy.js";
7
7
  import { VersioningStrategy } from "./VersioningStrategy.js";
8
- import { Context, Effect, Layer, Option } from "effect";
9
- import { FileSystem } from "@effect/platform";
10
- import { PackageManagerDetector, TopologicalSorter, WorkspaceDiscovery } from "workspaces-effect";
8
+ import { Context, Effect, FileSystem, Layer, Option } from "effect";
9
+ import { DependencyGraph, PackageManagerDetector, WorkspaceDiscovery } from "@effected/workspaces";
11
10
 
12
11
  //#region src/services/SilkWorkspaceAnalyzer.ts
13
12
  /**
@@ -37,7 +36,7 @@ import { PackageManagerDetector, TopologicalSorter, WorkspaceDiscovery } from "w
37
36
  * @since 0.2.0
38
37
  * @public
39
38
  */
40
- var SilkWorkspaceAnalyzer = class extends Context.Tag("@savvy-web/silk-effects/SilkWorkspaceAnalyzer")() {};
39
+ var SilkWorkspaceAnalyzer = class extends Context.Service()("@savvy-web/silk-effects/SilkWorkspaceAnalyzer") {};
41
40
  /**
42
41
  * Read the raw package.json from disk as an untyped record.
43
42
  *
@@ -115,7 +114,6 @@ function computeReleaseStatus(pkgName, isPrivate, isPublishable, config) {
115
114
  const SilkWorkspaceAnalyzerLive = Layer.effect(SilkWorkspaceAnalyzer, Effect.gen(function* () {
116
115
  const fs = yield* FileSystem.FileSystem;
117
116
  const discovery = yield* WorkspaceDiscovery;
118
- const sorter = yield* TopologicalSorter;
119
117
  const pmDetector = yield* PackageManagerDetector;
120
118
  const configReader = yield* ChangesetConfigReader;
121
119
  const versioningStrategy = yield* VersioningStrategy;
@@ -125,11 +123,11 @@ const SilkWorkspaceAnalyzerLive = Layer.effect(SilkWorkspaceAnalyzer, Effect.gen
125
123
  root,
126
124
  reason: `Package manager detection failed: ${String(err)}`
127
125
  })));
128
- const packages = yield* discovery.listPackages(root).pipe(Effect.mapError((err) => new WorkspaceAnalysisError({
126
+ const packages = yield* discovery.listPackages().pipe(Effect.mapError((err) => new WorkspaceAnalysisError({
129
127
  root,
130
128
  reason: `Workspace discovery failed: ${String(err)}`
131
129
  })));
132
- const topoOrder = yield* sorter.sort().pipe(Effect.mapError((err) => new WorkspaceAnalysisError({
130
+ const topoOrder = yield* DependencyGraph.make({ packages }).sort().pipe(Effect.mapError((err) => new WorkspaceAnalysisError({
133
131
  root,
134
132
  reason: `Cyclic dependency detected: ${String(err)}`
135
133
  })));
@@ -199,8 +197,8 @@ const SilkWorkspaceAnalyzerLive = Layer.effect(SilkWorkspaceAnalyzer, Effect.gen
199
197
  root,
200
198
  runtime: pm.runtime,
201
199
  packageManager: {
202
- type: pm.type,
203
- version: pm.version
200
+ type: pm.name,
201
+ ...Option.isSome(pm.version) ? { version: pm.version.value } : {}
204
202
  },
205
203
  workspaces: analyzedWorkspaces,
206
204
  changesetConfig,
@@ -25,7 +25,7 @@ import { Context, Effect, Layer } from "effect";
25
25
  * @since 0.1.0
26
26
  * @public
27
27
  */
28
- var TagStrategy = class extends Context.Tag("@savvy-web/silk-effects/TagStrategy")() {};
28
+ var TagStrategy = class extends Context.Service()("@savvy-web/silk-effects/TagStrategy") {};
29
29
  /**
30
30
  * Live implementation of {@link TagStrategy} with no external dependencies.
31
31
  *
@@ -35,7 +35,7 @@ var TagStrategy = class extends Context.Tag("@savvy-web/silk-effects/TagStrategy
35
35
  * @since 0.1.0
36
36
  * @public
37
37
  */
38
- const TagStrategyLive = Layer.succeed(TagStrategy, TagStrategy.of({
38
+ const TagStrategyLive = Layer.succeed(TagStrategy, {
39
39
  determine: (versioningResult) => {
40
40
  if (versioningResult.type === "independent") return Effect.succeed("scoped");
41
41
  return Effect.succeed("single");
@@ -50,7 +50,7 @@ const TagStrategyLive = Layer.succeed(TagStrategy, TagStrategy.of({
50
50
  if (name.startsWith("@")) return Effect.succeed(`${name}@${version}`);
51
51
  return Effect.succeed(`${name}@${version}`);
52
52
  }
53
- }));
53
+ });
54
54
 
55
55
  //#endregion
56
56
  export { TagStrategy, TagStrategyLive };
@@ -2,8 +2,8 @@ import { ToolNotFoundError } from "../errors/ToolNotFoundError.js";
2
2
  import { ToolResolutionError } from "../errors/ToolResolutionError.js";
3
3
  import { ResolvedTool } from "../schemas/ResolvedTool.js";
4
4
  import { Context, Effect, Layer, Option, Ref } from "effect";
5
- import { Command, CommandExecutor } from "@effect/platform";
6
- import { PackageManagerDetector, WorkspaceRoot } from "workspaces-effect";
5
+ import { PackageManagerDetector, WorkspaceRoot } from "@effected/workspaces";
6
+ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
7
7
 
8
8
  //#region src/services/ToolDiscovery.ts
9
9
  /**
@@ -21,7 +21,7 @@ import { PackageManagerDetector, WorkspaceRoot } from "workspaces-effect";
21
21
  * );
22
22
  * }).pipe(
23
23
  * Effect.provide(ToolDiscoveryLive),
24
- * Effect.provide(NodeContext.layer),
24
+ * Effect.provide(NodeServices.layer),
25
25
  * )
26
26
  * );
27
27
  * ```
@@ -29,7 +29,7 @@ import { PackageManagerDetector, WorkspaceRoot } from "workspaces-effect";
29
29
  * @since 0.2.0
30
30
  * @public
31
31
  */
32
- var ToolDiscovery = class extends Context.Tag("@savvy-web/silk-effects/ToolDiscovery")() {};
32
+ var ToolDiscovery = class extends Context.Service()("@savvy-web/silk-effects/ToolDiscovery") {};
33
33
  /**
34
34
  * Build the PM exec prefix for running a local binary.
35
35
  */
@@ -62,14 +62,14 @@ function pmExecArgs(pmType, name) {
62
62
  /**
63
63
  * Run a command and return its stdout, or `Option.none()` on failure.
64
64
  */
65
- function tryString(cmd) {
66
- return Command.string(cmd).pipe(Effect.map((s) => Option.some(s.trim())), Effect.catchAll(() => Effect.succeed(Option.none())));
65
+ function tryString(spawner, cmd) {
66
+ return spawner.string(cmd).pipe(Effect.map((s) => Option.some(s.trim())), Effect.catch(() => Effect.succeed(Option.none())));
67
67
  }
68
68
  /**
69
69
  * Run a command and return true if it succeeds (exit code 0).
70
70
  */
71
- function tryExists(cmd) {
72
- return Command.exitCode(cmd).pipe(Effect.map((code) => code === 0), Effect.catchAll(() => Effect.succeed(false)));
71
+ function tryExists(spawner, cmd) {
72
+ return spawner.exitCode(cmd).pipe(Effect.map((code) => code === 0), Effect.catch(() => Effect.succeed(false)));
73
73
  }
74
74
  /**
75
75
  * Extract version from command output using a VersionExtractor.
@@ -98,45 +98,46 @@ function extractVersion(output, extractor) {
98
98
  * Live implementation of {@link ToolDiscovery}.
99
99
  *
100
100
  * @remarks
101
- * Requires `CommandExecutor` from `@effect/platform`, `PackageManagerDetector`
102
- * and `WorkspaceRoot` from `workspaces-effect`.
101
+ * Requires `ChildProcessSpawner` from `effect/unstable/process` (provide
102
+ * `NodeServices.layer` at the app edge), plus `PackageManagerDetector`
103
+ * and `WorkspaceRoot` from `@effected/workspaces`.
103
104
  *
104
105
  * @since 0.2.0
105
106
  * @public
106
107
  */
107
108
  const ToolDiscoveryLive = Layer.effect(ToolDiscovery, Effect.gen(function* () {
108
- const executor = yield* CommandExecutor.CommandExecutor;
109
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
109
110
  const wsRoot = yield* WorkspaceRoot;
110
111
  const pmDetector = yield* PackageManagerDetector;
111
112
  const cache = yield* Ref.make(/* @__PURE__ */ new Map());
112
113
  const resolve = (definition) => Effect.gen(function* () {
113
114
  const hit = (yield* Ref.get(cache)).get(definition.name);
114
115
  if (hit) return hit;
115
- const root = yield* wsRoot.find(process.cwd()).pipe(Effect.catchAll(() => Effect.fail(new ToolResolutionError({
116
+ const root = yield* wsRoot.find(process.cwd()).pipe(Effect.catch(() => Effect.fail(new ToolResolutionError({
116
117
  name: definition.name,
117
118
  reason: "Could not find workspace root"
118
119
  }))));
119
- const pmType = (yield* pmDetector.detect(root).pipe(Effect.catchAll(() => Effect.fail(new ToolResolutionError({
120
+ const pmType = (yield* pmDetector.detect(root).pipe(Effect.catch(() => Effect.fail(new ToolResolutionError({
120
121
  name: definition.name,
121
122
  reason: "Could not detect package manager"
122
- }))))).type;
123
- const globalExists = yield* Effect.provideService(tryExists(Command.make("sh", "-c", `command -v ${definition.name}`)), CommandExecutor.CommandExecutor, executor);
123
+ }))))).name;
124
+ const globalExists = yield* tryExists(spawner, ChildProcess.make("sh", ["-c", `command -v ${definition.name}`]));
124
125
  let globalVersion = Option.none();
125
126
  if (globalExists && definition.versionExtractor._tag !== "None") {
126
127
  const flag = definition.versionExtractor.flag;
127
- globalVersion = extractVersion(yield* Effect.provideService(tryString(Command.make(definition.name, flag)), CommandExecutor.CommandExecutor, executor), definition.versionExtractor);
128
+ globalVersion = extractVersion(yield* tryString(spawner, ChildProcess.make(definition.name, [flag])), definition.versionExtractor);
128
129
  }
129
130
  let localExists = false;
130
131
  let localVersion = Option.none();
131
132
  const [pmBin, ...pmArgs] = pmExecArgs(pmType, definition.name);
132
133
  if (definition.versionExtractor._tag !== "None") {
133
134
  const flag = definition.versionExtractor.flag;
134
- const localOutput = yield* Effect.provideService(tryString(Command.make(pmBin, ...pmArgs, flag)), CommandExecutor.CommandExecutor, executor);
135
+ const localOutput = yield* tryString(spawner, ChildProcess.make(pmBin, [...pmArgs, flag]));
135
136
  if (Option.isSome(localOutput)) {
136
137
  localExists = true;
137
138
  localVersion = extractVersion(localOutput, definition.versionExtractor);
138
139
  }
139
- } else localExists = yield* Effect.provideService(tryExists(Command.make(pmBin, ...pmArgs, "--version")), CommandExecutor.CommandExecutor, executor);
140
+ } else localExists = yield* tryExists(spawner, ChildProcess.make(pmBin, [...pmArgs, "--version"]));
140
141
  if (!globalExists && !localExists) return yield* Effect.fail(new ToolResolutionError({
141
142
  name: definition.name,
142
143
  reason: "Tool not found globally or locally"
@@ -209,15 +210,15 @@ const ToolDiscoveryLive = Layer.effect(ToolDiscovery, Effect.gen(function* () {
209
210
  reason: message ?? err.reason
210
211
  })));
211
212
  const isAvailable = (definition) => Effect.gen(function* () {
212
- if (yield* Effect.provideService(tryExists(Command.make("sh", "-c", `command -v ${definition.name}`)), CommandExecutor.CommandExecutor, executor)) return true;
213
+ if (yield* tryExists(spawner, ChildProcess.make("sh", ["-c", `command -v ${definition.name}`]))) return true;
213
214
  const rootResult = yield* wsRoot.find(process.cwd()).pipe(Effect.option);
214
215
  if (Option.isNone(rootResult)) return false;
215
216
  const pmResult = yield* pmDetector.detect(rootResult.value).pipe(Effect.option);
216
217
  if (Option.isNone(pmResult)) return false;
217
- const pmType = pmResult.value.type;
218
+ const pmType = pmResult.value.name;
218
219
  const probeFlag = definition.versionExtractor._tag !== "None" ? definition.versionExtractor.flag : "--version";
219
220
  const [pmBin, ...pmArgs] = pmExecArgs(pmType, definition.name);
220
- return yield* Effect.provideService(tryExists(Command.make(pmBin, ...pmArgs, probeFlag)), CommandExecutor.CommandExecutor, executor);
221
+ return yield* tryExists(spawner, ChildProcess.make(pmBin, [...pmArgs, probeFlag]));
221
222
  });
222
223
  return {
223
224
  resolve,
@@ -19,7 +19,7 @@ import { Context, Effect, Layer } from "effect";
19
19
  * }).pipe(
20
20
  * Effect.provide(VersioningStrategyLive),
21
21
  * Effect.provide(ChangesetConfigReaderLive),
22
- * Effect.provide(NodeContext.layer),
22
+ * Effect.provide(NodeServices.layer),
23
23
  * )
24
24
  * );
25
25
  * ```
@@ -27,7 +27,7 @@ import { Context, Effect, Layer } from "effect";
27
27
  * @since 0.1.0
28
28
  * @public
29
29
  */
30
- var VersioningStrategy = class extends Context.Tag("@savvy-web/silk-effects/VersioningStrategy")() {};
30
+ var VersioningStrategy = class extends Context.Service()("@savvy-web/silk-effects/VersioningStrategy") {};
31
31
  /**
32
32
  * Live implementation of {@link VersioningStrategy}.
33
33
  *
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.58.9"
8
+ "packageVersion": "7.58.10"
9
9
  }
10
10
  ]
11
11
  }
@@ -5,9 +5,9 @@ import { Schema } from "effect";
5
5
  const TurboCache = Schema.Struct({
6
6
  local: Schema.Boolean,
7
7
  remote: Schema.Boolean,
8
- status: Schema.Literal("HIT", "MISS"),
8
+ status: Schema.Literals(["HIT", "MISS"]),
9
9
  timeSaved: Schema.Number
10
- }).annotations({ identifier: "TurboCache" });
10
+ }).annotate({ identifier: "TurboCache" });
11
11
  /** turbo's environment-variable contribution block (per-task and global). */
12
12
  const TurboEnvVars = Schema.Struct({
13
13
  specified: Schema.Struct({
@@ -17,7 +17,7 @@ const TurboEnvVars = Schema.Struct({
17
17
  configured: Schema.Array(Schema.String),
18
18
  inferred: Schema.Array(Schema.String),
19
19
  passthrough: Schema.NullOr(Schema.Array(Schema.String))
20
- }).annotations({ identifier: "TurboEnvVars" });
20
+ }).annotate({ identifier: "TurboEnvVars" });
21
21
  /** A single task entry from `turbo run <task> --dry=json`. */
22
22
  const TurboDryTask = Schema.Struct({
23
23
  taskId: Schema.String,
@@ -25,33 +25,27 @@ const TurboDryTask = Schema.Struct({
25
25
  package: Schema.String,
26
26
  directory: Schema.String,
27
27
  hash: Schema.String,
28
- inputs: Schema.Record({
29
- key: Schema.String,
30
- value: Schema.String
31
- }),
28
+ inputs: Schema.Record(Schema.String, Schema.String),
32
29
  hashOfExternalDependencies: Schema.String,
33
30
  cache: TurboCache,
34
31
  dependencies: Schema.Array(Schema.String),
35
32
  dependents: Schema.Array(Schema.String),
36
33
  environmentVariables: TurboEnvVars
37
- }).annotations({ identifier: "TurboDryTask" });
34
+ }).annotate({ identifier: "TurboDryTask" });
38
35
  /** The `globalCacheInputs` block — the components of the global hash. */
39
36
  const TurboGlobalCacheInputs = Schema.Struct({
40
37
  rootKey: Schema.String,
41
- files: Schema.Record({
42
- key: Schema.String,
43
- value: Schema.String
44
- }),
38
+ files: Schema.Record(Schema.String, Schema.String),
45
39
  hashOfExternalDependencies: Schema.String,
46
40
  hashOfInternalDependencies: Schema.String,
47
41
  environmentVariables: TurboEnvVars
48
- }).annotations({ identifier: "TurboGlobalCacheInputs" });
42
+ }).annotate({ identifier: "TurboGlobalCacheInputs" });
49
43
  /** The subset of `turbo run <task> --dry=json` the Turbo namespace reads. */
50
44
  const TurboDryRun = Schema.Struct({
51
45
  turboVersion: Schema.String,
52
46
  globalCacheInputs: TurboGlobalCacheInputs,
53
47
  tasks: Schema.Array(TurboDryTask)
54
- }).annotations({ identifier: "TurboDryRun" });
48
+ }).annotate({ identifier: "TurboDryRun" });
55
49
 
56
50
  //#endregion
57
51
  export { TurboCache, TurboDryRun, TurboDryTask, TurboEnvVars, TurboGlobalCacheInputs };
@@ -6,9 +6,9 @@ const PackageCacheStatus = Schema.Struct({
6
6
  package: Schema.String,
7
7
  taskId: Schema.String,
8
8
  hash: Schema.String,
9
- status: Schema.Literal("HIT", "MISS"),
9
+ status: Schema.Literals(["HIT", "MISS"]),
10
10
  timeSaved: Schema.Number
11
- }).annotations({ identifier: "PackageCacheStatus" });
11
+ }).annotate({ identifier: "PackageCacheStatus" });
12
12
  /** The hash-contributor breakdown explaining a single MISS. */
13
13
  const MissExplanation = Schema.Struct({
14
14
  package: Schema.String,
@@ -18,7 +18,7 @@ const MissExplanation = Schema.Struct({
18
18
  hashedEnvVars: Schema.Array(Schema.String),
19
19
  externalDependenciesHash: Schema.String,
20
20
  dependsOn: Schema.Array(Schema.String)
21
- }).annotations({ identifier: "MissExplanation" });
21
+ }).annotate({ identifier: "MissExplanation" });
22
22
  /** Global-hash component summary (every task inherits this). */
23
23
  const GlobalHashSummary = Schema.Struct({
24
24
  rootKey: Schema.String,
@@ -26,7 +26,7 @@ const GlobalHashSummary = Schema.Struct({
26
26
  externalDependenciesHash: Schema.String,
27
27
  internalDependenciesHash: Schema.String,
28
28
  globalEnvVars: Schema.Array(Schema.String)
29
- }).annotations({ identifier: "GlobalHashSummary" });
29
+ }).annotate({ identifier: "GlobalHashSummary" });
30
30
  /** Result of `TurboInspector.diagnoseCache`. */
31
31
  const CacheDiagnosis = Schema.Struct({
32
32
  task: Schema.String,
@@ -36,26 +36,26 @@ const CacheDiagnosis = Schema.Struct({
36
36
  statuses: Schema.Array(PackageCacheStatus),
37
37
  explanations: Schema.Array(MissExplanation),
38
38
  global: GlobalHashSummary
39
- }).annotations({ identifier: "CacheDiagnosis" });
39
+ }).annotate({ identifier: "CacheDiagnosis" });
40
40
  /** One node in the task graph. */
41
41
  const GraphNode = Schema.Struct({
42
42
  taskId: Schema.String,
43
43
  package: Schema.String,
44
44
  dependsOn: Schema.Array(Schema.String)
45
- }).annotations({ identifier: "GraphNode" });
45
+ }).annotate({ identifier: "GraphNode" });
46
46
  /** Result of `TurboInspector.taskGraph`. */
47
47
  const TaskGraphResult = Schema.Struct({
48
48
  task: Schema.optional(Schema.String),
49
49
  nodeCount: Schema.Number,
50
50
  nodes: Schema.Array(GraphNode),
51
51
  criticalPath: Schema.Array(Schema.String)
52
- }).annotations({ identifier: "TaskGraphResult" });
52
+ }).annotate({ identifier: "TaskGraphResult" });
53
53
  /** Result of `TurboInspector.affected`. */
54
54
  const AffectedResult = Schema.Struct({
55
55
  base: Schema.String,
56
56
  packages: Schema.Array(Schema.String),
57
57
  dependents: Schema.Array(Schema.String)
58
- }).annotations({ identifier: "AffectedResult" });
58
+ }).annotate({ identifier: "AffectedResult" });
59
59
 
60
60
  //#endregion
61
61
  export { AffectedResult, CacheDiagnosis, GlobalHashSummary, GraphNode, MissExplanation, PackageCacheStatus, TaskGraphResult };
@@ -3,8 +3,9 @@ import { ToolDiscovery } from "../../services/ToolDiscovery.js";
3
3
  import { TurboDigest } from "../digest.js";
4
4
  import { DryRunParseError, NotATurboRepoError, TurboExecError, TurboNotInstalledError } from "../errors.js";
5
5
  import { TurboDryRun } from "../schemas/DryRun.js";
6
- import { Context, Effect, Layer, Schema } from "effect";
7
- import { Command, CommandExecutor, FileSystem } from "@effect/platform";
6
+ import { Context, Effect, FileSystem, Layer, Schema } from "effect";
7
+ import { Git } from "@effected/git";
8
+ import { ChildProcessSpawner } from "effect/unstable/process";
8
9
 
9
10
  //#region src/turbo/services/TurboInspector.ts
10
11
  /**
@@ -18,7 +19,7 @@ import { Command, CommandExecutor, FileSystem } from "@effect/platform";
18
19
  *
19
20
  * @since 0.7.0
20
21
  */
21
- var TurboInspector = class extends Context.Tag("@savvy-web/silk-effects/TurboInspector")() {};
22
+ var TurboInspector = class extends Context.Service()("@savvy-web/silk-effects/TurboInspector") {};
22
23
  const TURBO = ToolDefinition.make({ name: "turbo" });
23
24
  /** Default task used by {@link TurboInspector.taskGraph} and {@link TurboInspector.affected}. */
24
25
  const DEFAULT_BUILD_TASK = "build:dev";
@@ -29,41 +30,51 @@ const DEFAULT_AFFECTED_BASE = "main";
29
30
  *
30
31
  * @remarks
31
32
  * Requires {@link ToolDiscovery} to resolve the `turbo` binary, plus
32
- * `CommandExecutor` and `FileSystem` from `@effect/platform`. Mirrors
33
- * `ToolDiscoveryLive`: the executor is captured at layer construction and
34
- * discharged onto each command effect with `Effect.provideService`, keeping
35
- * the public method effects at `R = never`.
33
+ * `ChildProcessSpawner` and `FileSystem` from core (provide `NodeServices.layer`
34
+ * at the app edge) and `Git` from `@effected/git`. Mirrors `ToolDiscoveryLive`:
35
+ * the spawner is captured at layer construction and discharged onto each command
36
+ * effect with `Effect.provideService`, keeping the public method effects at
37
+ * `R = never`.
36
38
  *
37
39
  * @since 0.7.0
38
40
  */
39
41
  const TurboInspectorLive = Layer.effect(TurboInspector, Effect.gen(function* () {
40
42
  const discovery = yield* ToolDiscovery;
41
- const executor = yield* CommandExecutor.CommandExecutor;
43
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
42
44
  const fs = yield* FileSystem.FileSystem;
45
+ const git = yield* Git;
43
46
  const runTurbo = (cwd, args, env) => discovery.require(TURBO).pipe(Effect.mapError((e) => new TurboNotInstalledError({ reason: e.reason })), Effect.flatMap((resolved) => {
44
47
  const command = resolved.exec(...args).workingDirectory(cwd);
45
- return Effect.provideService((env ? command.env(env) : command).string(), CommandExecutor.CommandExecutor, executor).pipe(Effect.mapError((e) => new TurboExecError({
48
+ return Effect.provideService((env ? command.env(env) : command).string(), ChildProcessSpawner.ChildProcessSpawner, spawner).pipe(Effect.mapError((e) => new TurboExecError({
46
49
  args: [...args],
47
50
  reason: String(e)
48
51
  })));
49
52
  }));
50
- /** Run a read-only `git` command in `cwd` and return its non-empty output lines. */
51
- const runGit = (cwd, args) => Effect.provideService(Command.string(Command.workingDirectory(Command.make("git", ...args), cwd)), CommandExecutor.CommandExecutor, executor).pipe(Effect.mapError((e) => new TurboExecError({
52
- args: ["git", ...args],
53
- reason: String(e)
54
- })), Effect.map((stdout) => stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0)));
55
- const ensureTurboRepo = (cwd) => fs.exists(`${cwd}/turbo.json`).pipe(Effect.catchAll(() => Effect.succeed(false)), Effect.flatMap((ok) => ok ? Effect.void : Effect.fail(new NotATurboRepoError({ cwd }))));
53
+ /** The paths changed between `base` and HEAD, via `@effected/git`. */
54
+ const changedPaths = (cwd, base) => git.changedFiles(cwd, {
55
+ base,
56
+ head: "HEAD"
57
+ }).pipe(Effect.mapError((e) => new TurboExecError({
58
+ args: [
59
+ "git",
60
+ "diff",
61
+ "--name-only",
62
+ `${base}...HEAD`
63
+ ],
64
+ reason: e.message
65
+ })));
66
+ const ensureTurboRepo = (cwd) => fs.exists(`${cwd}/turbo.json`).pipe(Effect.catch(() => Effect.succeed(false)), Effect.flatMap((ok) => ok ? Effect.void : Effect.fail(new NotATurboRepoError({ cwd }))));
56
67
  const dryRun = (task, cwd, args, env) => ensureTurboRepo(cwd).pipe(Effect.flatMap(() => runTurbo(cwd, args, env)), Effect.flatMap((stdout) => Effect.try({
57
68
  try: () => JSON.parse(stdout),
58
69
  catch: (e) => new DryRunParseError({
59
70
  task,
60
71
  reason: `invalid JSON: ${String(e)}`
61
72
  })
62
- })), Effect.flatMap((json) => Schema.decodeUnknown(TurboDryRun)(json).pipe(Effect.mapError((e) => new DryRunParseError({
73
+ })), Effect.flatMap((json) => Schema.decodeUnknownEffect(TurboDryRun)(json).pipe(Effect.mapError((e) => new DryRunParseError({
63
74
  task,
64
75
  reason: String(e)
65
76
  })))));
66
- return TurboInspector.of({
77
+ return {
67
78
  diagnoseCache: (task, cwd) => dryRun(task, cwd, [
68
79
  "run",
69
80
  task,
@@ -80,11 +91,7 @@ const TurboInspectorLive = Layer.effect(TurboInspector, Effect.gen(function* ()
80
91
  affected: (cwd, base) => {
81
92
  const ref = base ?? DEFAULT_AFFECTED_BASE;
82
93
  return Effect.all({
83
- changedFiles: runGit(cwd, [
84
- "diff",
85
- "--name-only",
86
- `${ref}...HEAD`
87
- ]),
94
+ changedFiles: changedPaths(cwd, ref),
88
95
  dry: dryRun(`${DEFAULT_BUILD_TASK} --affected`, cwd, [
89
96
  "run",
90
97
  DEFAULT_BUILD_TASK,
@@ -93,7 +100,7 @@ const TurboInspectorLive = Layer.effect(TurboInspector, Effect.gen(function* ()
93
100
  ], { TURBO_SCM_BASE: ref })
94
101
  }).pipe(Effect.map(({ changedFiles, dry }) => TurboDigest.affected(ref, changedFiles, dry)));
95
102
  }
96
- });
103
+ };
97
104
  }));
98
105
 
99
106
  //#endregion
@@ -1,10 +1,26 @@
1
- import { Command } from "@effect/platform";
1
+ import { Effect, Stream } from "effect";
2
+ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
2
3
 
3
4
  //#region src/utils/ToolCommand.ts
4
5
  /**
5
- * Wraps `@effect/platform` `Command.Command` with instance method ergonomics.
6
+ * Rebuild a command with patched options.
6
7
  *
7
- * Use `yield* cmd.string()` instead of `yield* Command.string(cmd)`.
8
+ * @remarks
9
+ * `ChildProcess.Command` values are pure data, but core only ships setters for
10
+ * `cwd` and `env`. This helper rebuilds a `StandardCommand` with merged
11
+ * options, recursing into both sides of a `PipedCommand` the same way
12
+ * `ChildProcess.setCwd` does.
13
+ */
14
+ const patchOptions = (command, patch) => {
15
+ switch (command._tag) {
16
+ case "StandardCommand": return ChildProcess.make(command.command, command.args, patch(command.options));
17
+ case "PipedCommand": return ChildProcess.pipeTo(patchOptions(command.left, patch), patchOptions(command.right, patch), command.options);
18
+ }
19
+ };
20
+ /**
21
+ * Wraps `effect/unstable/process` `ChildProcess.Command` with instance method ergonomics.
22
+ *
23
+ * Use `yield* cmd.string()` instead of resolving the `ChildProcessSpawner` service by hand.
8
24
  *
9
25
  * @since 0.2.0
10
26
  * @public
@@ -14,26 +30,36 @@ var ToolCommand = class ToolCommand {
14
30
  constructor(command) {
15
31
  this.command = command;
16
32
  }
17
- string(encoding) {
18
- return Command.string(this.command, encoding);
33
+ string() {
34
+ return Effect.flatMap(ChildProcessSpawner.ChildProcessSpawner, (spawner) => spawner.string(this.command));
19
35
  }
20
36
  exitCode() {
21
- return Command.exitCode(this.command);
37
+ return Effect.flatMap(ChildProcessSpawner.ChildProcessSpawner, (spawner) => spawner.exitCode(this.command));
22
38
  }
23
- lines(encoding) {
24
- return Command.lines(this.command, encoding);
39
+ lines() {
40
+ return Effect.flatMap(ChildProcessSpawner.ChildProcessSpawner, (spawner) => spawner.lines(this.command));
25
41
  }
26
42
  stream() {
27
- return Command.stream(this.command);
43
+ return Stream.unwrap(Effect.map(ChildProcessSpawner.ChildProcessSpawner, (spawner) => spawner.streamString(this.command)));
28
44
  }
29
45
  env(environment) {
30
- return new ToolCommand(Command.env(this.command, environment));
46
+ return new ToolCommand(patchOptions(this.command, (options) => ({
47
+ ...options,
48
+ env: {
49
+ ...options.env,
50
+ ...environment
51
+ },
52
+ extendEnv: true
53
+ })));
31
54
  }
32
55
  workingDirectory(cwd) {
33
- return new ToolCommand(Command.workingDirectory(this.command, cwd));
56
+ return new ToolCommand(ChildProcess.setCwd(this.command, cwd));
34
57
  }
35
58
  stdin(input) {
36
- return new ToolCommand(Command.feed(this.command, input));
59
+ return new ToolCommand(patchOptions(this.command, (options) => ({
60
+ ...options,
61
+ stdin: Stream.succeed(new TextEncoder().encode(input))
62
+ })));
37
63
  }
38
64
  };
39
65