@effected/workspaces 0.8.0 → 0.9.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.
package/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { CatalogAssemblyError, CatalogResolver, DependencyResolutionError, Manif
4
4
  import { GlobPattern } from "@effected/glob";
5
5
  import { Lockfile, LockfileFramingError, LockfileIntegrity, LockfileParseError, ResolvedPackage, WorkspaceManifest } from "@effected/lockfiles";
6
6
  import { Package } from "@effected/package-json";
7
+ import { LocalExec } from "@effected/commands";
7
8
  import { ChildProcessSpawner } from "effect/unstable/process";
8
9
  //#region src/WorkspacePackage.d.ts
9
10
  declare const PublishConfig_base: Schema.Class<PublishConfig, Schema.Struct<{
@@ -606,7 +607,10 @@ declare class WorkspaceDiscovery extends WorkspaceDiscovery_base {
606
607
  * (a fabricated root path would leak into consumer path logic), so an
607
608
  * unstubbed `info()` call is a test-wiring mistake and fails loudly as a
608
609
  * defect rather than succeeding with a lie or failing with a dishonest
609
- * typed error.
610
+ * typed error. A defect is not absorbed by `Effect.catch` or any
611
+ * typed-error handler — deliberately, so code under test with a
612
+ * best-effort `catch` cannot make the mandatory stub look optional; the
613
+ * unstubbed call still fails the test.
610
614
  *
611
615
  * @example
612
616
  * ```ts
@@ -1046,10 +1050,21 @@ declare class PackageManagerDetectionError extends PackageManagerDetectionError_
1046
1050
  * @public
1047
1051
  */
1048
1052
  type PackageManagerDetectionFailure = PackageManagerDetectionError | WorkspaceManifestError;
1049
- declare const PackageManagerDetector_base: Context.ServiceClass<PackageManagerDetector, "@effected/workspaces/PackageManagerDetector", {
1053
+ /**
1054
+ * The {@link PackageManagerDetector} service shape.
1055
+ *
1056
+ * @remarks
1057
+ * Exported so a consumer can type a bespoke double against the contract without
1058
+ * reaching into the class — the `WorkspaceDiscoveryShape` /
1059
+ * `PublishabilityDetectorShape` convention.
1060
+ *
1061
+ * @public
1062
+ */
1063
+ interface PackageManagerDetectorShape {
1050
1064
  /** Detect the package manager at a workspace root. */
1051
1065
  readonly detect: (root: string) => Effect.Effect<DetectedPackageManager, PackageManagerDetectionFailure>;
1052
- }>;
1066
+ }
1067
+ declare const PackageManagerDetector_base: Context.ServiceClass<PackageManagerDetector, "@effected/workspaces/PackageManagerDetector", PackageManagerDetectorShape>;
1053
1068
  /**
1054
1069
  * Detects which package manager owns a workspace root.
1055
1070
  *
@@ -1098,6 +1113,55 @@ declare class PackageManagerDetector extends PackageManagerDetector_base {
1098
1113
  }, never, FileSystem.FileSystem | Path.Path>;
1099
1114
  /** The live layer. */
1100
1115
  static readonly layer: Layer.Layer<PackageManagerDetector, never, FileSystem.FileSystem | Path.Path>;
1116
+ /**
1117
+ * The sanctioned in-memory double.
1118
+ *
1119
+ * @remarks
1120
+ * **`detect` has no honest default, so an unstubbed call dies** — the
1121
+ * `WorkspaceDiscovery.info` posture, for the same reason. A stand-in that
1122
+ * answered `"pnpm"` would hand a consumer a fact nothing established, and it
1123
+ * would contradict the very service it stands in for: the live detector's
1124
+ * defining property is that it [refuses to
1125
+ * guess](https://github.com/spencerbeggs/effected) when no evidence matches.
1126
+ * A double that guesses is worse than no double.
1127
+ *
1128
+ * Failing typed would be the subtler mistake: `PackageManagerDetectionError`
1129
+ * reads as a legitimate "no manager here" answer, so a consumer would branch
1130
+ * on it and proceed, never learning that the test simply forgot to stub.
1131
+ *
1132
+ * The defect is also not absorbed by `Effect.catch` or any typed-error
1133
+ * handler — deliberately, so code under test with a best-effort `catch`
1134
+ * around detection cannot make the mandatory stub look optional; the
1135
+ * unstubbed call still fails the test.
1136
+ *
1137
+ * @param overrides - Members to supply; anything omitted dies on use.
1138
+ *
1139
+ * @example
1140
+ * ```ts
1141
+ * import { DetectedPackageManager, PackageManagerDetector } from "@effected/workspaces";
1142
+ * import { Effect, Option } from "effect";
1143
+ *
1144
+ * const TestDetector = PackageManagerDetector.layerTest({
1145
+ * detect: () =>
1146
+ * Effect.succeed(
1147
+ * DetectedPackageManager.make({ name: "pnpm", version: Option.none(), runtime: "node" }),
1148
+ * ),
1149
+ * });
1150
+ * ```
1151
+ */
1152
+ static readonly makeTest: (overrides?: Partial<PackageManagerDetectorShape>) => PackageManagerDetectorShape;
1153
+ /**
1154
+ * {@link PackageManagerDetector.makeTest} behind `Layer.succeed`.
1155
+ *
1156
+ * @remarks
1157
+ * A parameterized layer factory mints a **fresh reference per call**, and
1158
+ * layers memoize by reference — bind the result to a `const` and reuse it
1159
+ * rather than calling `layerTest(...)` at each composition site.
1160
+ *
1161
+ * Pairs with `WorkspaceRoot.layerTest` and `WorkspaceDiscovery.layerTest` to
1162
+ * stand up the whole discovery path with no filesystem at all.
1163
+ */
1164
+ static readonly layerTest: (overrides?: Partial<PackageManagerDetectorShape>) => Layer.Layer<PackageManagerDetector>;
1101
1165
  }
1102
1166
  //#endregion
1103
1167
  //#region src/LockfileReader.d.ts
@@ -1377,8 +1441,429 @@ declare const PublishabilityDetector_base: Context.ServiceClass<PublishabilityDe
1377
1441
  * @public
1378
1442
  */
1379
1443
  declare class PublishabilityDetector extends PublishabilityDetector_base {
1380
- /** Standard npm publishing semantics. Pure — no filesystem, no platform services. */
1381
- static readonly layer: Layer.Layer<PublishabilityDetector>;
1444
+ /**
1445
+ * Standard npm publishing semantics, **as a value**. Pure — no filesystem,
1446
+ * no platform services.
1447
+ *
1448
+ * @remarks
1449
+ * Exposed as a shape and not only as a layer, because a consumer composing
1450
+ * *around* these rules cannot reach them through a layer without re-entering
1451
+ * the very tag it is replacing. `@savvy-web/silk-effects` had to write
1452
+ * `Effect.provide(PublishabilityDetector, PublishabilityDetector.layer)`
1453
+ * **inside its own implementation of that tag** to get at this function for
1454
+ * its pass-through branch; with the value exposed that becomes
1455
+ * `PublishabilityDetector.npm.detect(pkg)`.
1456
+ *
1457
+ * @example
1458
+ * ```ts
1459
+ * import { PublishabilityDetector } from "@effected/workspaces";
1460
+ * import { Effect, Layer } from "effect";
1461
+ *
1462
+ * // A policy that defers to npm semantics for everything it does not veto.
1463
+ * const withVeto = Layer.succeed(PublishabilityDetector, {
1464
+ * detect: (pkg) =>
1465
+ * pkg.name.endsWith("-private")
1466
+ * ? Effect.succeed([])
1467
+ * : PublishabilityDetector.npm.detect(pkg),
1468
+ * });
1469
+ * ```
1470
+ */
1471
+ static readonly npm: PublishabilityDetectorShape;
1472
+ /** Nothing publishes. */
1473
+ static readonly none: PublishabilityDetectorShape;
1474
+ /**
1475
+ * {@link PublishabilityDetector.npm} as a layer.
1476
+ *
1477
+ * @remarks
1478
+ * Named for its policy rather than called `layer`, deliberately. **No
1479
+ * composite in this package provides a publishability detector**: a
1480
+ * `Workspaces.layer()` that quietly supplied npm semantics made the choice
1481
+ * invisible, and worse, made a naively-ordered override lose to it in
1482
+ * silence — `Layer.mergeAll(myDetector, Workspaces.layer())` resolved to the
1483
+ * default, because `mergeAll` is last-wins. For a service that decides
1484
+ * whether a package publishes and to which registry, that silent revert was
1485
+ * the worst available failure.
1486
+ *
1487
+ * The composites do not *require* a detector either — nothing inside them
1488
+ * asks a publishability question, so their `R` stays `FileSystem | Path`.
1489
+ * The requirement instead surfaces in the `R` of each operation that asks
1490
+ * (`VersioningStrategy.detect`, e.g.): a program that asks and never wires
1491
+ * a detector fails to compile where that operation's `R` must close — which
1492
+ * can be far from the layer-wiring site — and a program that never asks
1493
+ * never supplies a publish policy at all.
1494
+ */
1495
+ static readonly layerNpm: Layer.Layer<PublishabilityDetector>;
1496
+ /**
1497
+ * {@link PublishabilityDetector.none} as a layer: a workspace where nothing
1498
+ * publishes.
1499
+ *
1500
+ * @remarks
1501
+ * For dry runs, and for a release tool whose configuration disables
1502
+ * publishing wholesale — silk's changeset `mode: "none"` is exactly this.
1503
+ */
1504
+ static readonly layerNone: Layer.Layer<PublishabilityDetector>;
1505
+ }
1506
+ //#endregion
1507
+ //#region src/ReleaseTag.d.ts
1508
+ /**
1509
+ * Whether one shared tag names a whole release, or one tag names each package.
1510
+ *
1511
+ * @remarks
1512
+ * `single` is the shape of a single-package repo and of a monorepo whose
1513
+ * publishable packages all version in lockstep; `scoped` is the shape of
1514
+ * independent versioning, where a shared tag would be ambiguous.
1515
+ *
1516
+ * @public
1517
+ */
1518
+ declare const TagStyle: Schema.Literals<readonly ["single", "scoped"]>;
1519
+ /**
1520
+ * The decoded type of {@link (TagStyle:variable)}: `"single" | "scoped"`.
1521
+ *
1522
+ * @public
1523
+ */
1524
+ type TagStyle = typeof TagStyle.Type;
1525
+ /**
1526
+ * Formatting knobs for {@link ReleaseTag.single} and {@link ReleaseTag.scoped}.
1527
+ *
1528
+ * @public
1529
+ */
1530
+ interface TagFormatOptions {
1531
+ /**
1532
+ * The prefix on the version segment.
1533
+ *
1534
+ * @remarks
1535
+ * Defaults to `""` uniformly, for both {@link ReleaseTag.single} and
1536
+ * {@link ReleaseTag.scoped} — strict SemVer, deliberately chosen. Pass
1537
+ * `"v"` for the GitHub release-tag convention (`v1.2.3`), which tools such
1538
+ * as `actions/checkout`'s ref resolution and third-party changelog
1539
+ * generators expect.
1540
+ */
1541
+ readonly versionPrefix?: string;
1542
+ }
1543
+ /**
1544
+ * Options for {@link TrackingTag.forVersion}.
1545
+ *
1546
+ * @public
1547
+ */
1548
+ interface TrackingTagOptions {
1549
+ /**
1550
+ * Prefix the alias with a package name, for a monorepo that namespaces its
1551
+ * tags: `@scope/pkg@v1`. Omit it for a single-package repo's bare `v1`.
1552
+ */
1553
+ readonly packageName?: string;
1554
+ /**
1555
+ * Which aliases to derive. `"both"` (the default) gives `v1` and `v1.2`;
1556
+ * `"major"` gives only the broad `v1`.
1557
+ */
1558
+ readonly precision?: "major" | "both";
1559
+ /**
1560
+ * Derive aliases for a prerelease version too. **Off by default, and you
1561
+ * almost certainly want it off** — see {@link TrackingTag.forVersion}.
1562
+ */
1563
+ readonly includePrerelease?: boolean;
1564
+ }
1565
+ declare const TrackingTag_base: Schema.Class<TrackingTag, Schema.Struct<{
1566
+ /** The tag string exactly as it appears in git. */
1567
+ readonly value: Schema.NonEmptyString;
1568
+ /** The package the alias namespaces; absent on a bare `v1`. */
1569
+ readonly packageName: Schema.optionalKey<Schema.NonEmptyString>;
1570
+ /** The major version the alias tracks. */
1571
+ readonly major: Schema.Int;
1572
+ /** The minor version, on a `v1.2`-precision alias; absent on `v1`. */
1573
+ readonly minor: Schema.optionalKey<Schema.Int>;
1574
+ }>, {}>;
1575
+ /**
1576
+ * A floating alias tag — `v1`, `v1.2` — that a repo re-points at its newest
1577
+ * matching release.
1578
+ *
1579
+ * @remarks
1580
+ * This is the GitHub Actions distribution convention: a consumer writes
1581
+ * `uses: owner/repo@v1` and receives whatever 1.x the repo last pointed `v1` at.
1582
+ *
1583
+ * **Deliberately not SemVer, and deliberately not a {@link (TagStyle:variable)}.**
1584
+ * A release tag names one immutable version; a tracking tag is an alias derived
1585
+ * *from* a version, carrying a truncated number that is not a version at all.
1586
+ * Folding it into `ReleaseTag` as a third style would put a mutable pointer and
1587
+ * an immutable name behind one type.
1588
+ *
1589
+ * Everything here is derivation, formatting and parsing. **Actually moving a
1590
+ * git tag is not this package's business** — a consumer does that through git,
1591
+ * and the deliberate omission is what keeps this module a pure leaf.
1592
+ *
1593
+ * @example
1594
+ * ```ts
1595
+ * import { TrackingTag } from "@effected/workspaces";
1596
+ *
1597
+ * TrackingTag.forVersion("1.2.3").map((t) => t.value); // ["v1", "v1.2"]
1598
+ * TrackingTag.forVersion("1.0.0-beta.3"); // [] — never float onto a beta
1599
+ * TrackingTag.forVersion("1.2.3", { packageName: "@acme/cli" });
1600
+ * // ["@acme/cli@v1", "@acme/cli@v1.2"]
1601
+ * ```
1602
+ *
1603
+ * @public
1604
+ */
1605
+ declare class TrackingTag extends TrackingTag_base {
1606
+ /** Whether this alias tracks a whole major line, or one minor line inside it. */
1607
+ get precision(): "major" | "minor";
1608
+ /**
1609
+ * The tracking tags a release of `version` should be pointed at.
1610
+ *
1611
+ * @remarks
1612
+ * **A prerelease derives nothing.** Anyone depending on `owner/repo@v1` is
1613
+ * asking for the newest *stable* 1.x, so re-pointing that alias at
1614
+ * `1.0.0-beta.3` would ship a prerelease to every such consumer with no
1615
+ * signal at all. `includePrerelease` exists for callers who genuinely mean
1616
+ * it — a prerelease-only distribution channel — and should be rare.
1617
+ *
1618
+ * **Total, never throwing.** A version that is not `X.Y.Z` derives nothing
1619
+ * rather than failing: this is a query about a version, not a validation of
1620
+ * one, and `WorkspacePackage.version` is deliberately tolerant, so odd
1621
+ * versions reach here routinely.
1622
+ *
1623
+ * 0.x versions DO derive aliases. Floating `v0` across 0.x minors is a real
1624
+ * hazard, but which aliases to publish is the caller's policy, decided where
1625
+ * the tags are moved — not something a derivation should quietly withhold.
1626
+ *
1627
+ * @param version - The version being released.
1628
+ * @param options - Package prefix, precision and the prerelease override.
1629
+ * @returns The aliases, broadest first; empty when none apply.
1630
+ */
1631
+ static forVersion(version: string, options?: TrackingTagOptions): ReadonlyArray<TrackingTag>;
1632
+ }
1633
+ /**
1634
+ * What an arbitrary tag string denotes.
1635
+ *
1636
+ * @remarks
1637
+ * `unrecognized` is a real answer, not a failure: a repository's tags include
1638
+ * branches-turned-tags, `latest`, and whatever else humans wrote, and forcing
1639
+ * those into a bucket is exactly what a classifier must not do.
1640
+ *
1641
+ * @public
1642
+ */
1643
+ type TagClassification = {
1644
+ readonly kind: "release";
1645
+ readonly tag: ReleaseTag;
1646
+ } | {
1647
+ readonly kind: "tracking";
1648
+ readonly tag: TrackingTag;
1649
+ } | {
1650
+ readonly kind: "unrecognized";
1651
+ };
1652
+ /**
1653
+ * Decide whether a tag string is a release tag, a tracking alias, or neither.
1654
+ *
1655
+ * @remarks
1656
+ * The two families are told apart by **segment count**, not by the `v` prefix:
1657
+ * three numeric segments is a version (so `1.0.0` and `v1.0.0` are both release
1658
+ * tags), while one or two segments is a truncated alias. The `v` *is* required
1659
+ * on an alias — a bare `1` is neither valid SemVer nor the tracking convention,
1660
+ * and accepting it would make this function guess.
1661
+ *
1662
+ * The package prefix splits at the **last** `@`, so a leading npm scope
1663
+ * survives: `@scope/pkg@1.0.0` is package `@scope/pkg` at version `1.0.0`.
1664
+ *
1665
+ * Round-tripping is a tested property: every tag {@link ReleaseTag} and
1666
+ * {@link TrackingTag} format classifies back to the family that produced it,
1667
+ * with its fields intact.
1668
+ *
1669
+ * @param tag - Any tag string.
1670
+ * @returns The classification.
1671
+ *
1672
+ * @public
1673
+ */
1674
+ declare const classifyTag: (tag: string) => TagClassification;
1675
+ declare const ReleaseTag_base: Schema.Class<ReleaseTag, Schema.Struct<{
1676
+ /** The tag string exactly as it appears in git. */
1677
+ readonly value: Schema.NonEmptyString;
1678
+ /** The package the tag names; absent on a workspace-wide single tag. */
1679
+ readonly packageName: Schema.optionalKey<Schema.NonEmptyString>;
1680
+ /** The version the tag names, without any prefix. */
1681
+ readonly version: Schema.NonEmptyString;
1682
+ /** Which style produced it. */
1683
+ readonly style: Schema.Literals<readonly ["single", "scoped"]>;
1684
+ }>, {}>;
1685
+ /**
1686
+ * A git tag naming a release, and the parts it was built from.
1687
+ *
1688
+ * @remarks
1689
+ * `value` is the tag exactly as it appears in git; `version` stays **bare**
1690
+ * even when `value` carries a prefix, so a consumer comparing versions never
1691
+ * has to strip one back off.
1692
+ *
1693
+ * Formatting is **total**: there is no error channel, because the only failure
1694
+ * v3 modelled — an empty version — is caught by `Schema.NonEmptyString` when
1695
+ * the value is constructed. A bad version reaching these statics is developer
1696
+ * wiring rather than untrusted input, so it dies as a defect, the same posture
1697
+ * as an uncompilable glob literal in `WorkspacePackage.matchesDependency`.
1698
+ *
1699
+ * @example
1700
+ * ```ts
1701
+ * import { ReleaseTag } from "@effected/workspaces";
1702
+ *
1703
+ * ReleaseTag.single("1.2.3").value; // "1.2.3"
1704
+ * ReleaseTag.scoped("@acme/cli", "1.2.3").value; // "@acme/cli@1.2.3"
1705
+ * ReleaseTag.scoped("cli", "1.2.3").value; // "cli@1.2.3"
1706
+ * ReleaseTag.scoped("cli", "1.2.3", { versionPrefix: "v" }).value; // "cli@v1.2.3"
1707
+ * ```
1708
+ *
1709
+ * @public
1710
+ */
1711
+ declare class ReleaseTag extends ReleaseTag_base {
1712
+ /**
1713
+ * One shared tag for a whole release: `1.2.3`.
1714
+ *
1715
+ * @param version - The version being released. Must not be empty.
1716
+ * @param options - Formatting overrides.
1717
+ */
1718
+ static single(version: string, options?: TagFormatOptions): ReleaseTag;
1719
+ /**
1720
+ * A per-package tag: `<packageName>@<version>` — `@scope/pkg@1.2.3` for a
1721
+ * scoped name, `pkg@1.2.3` for an unscoped one, uniformly, unless
1722
+ * `options.versionPrefix` says otherwise.
1723
+ *
1724
+ * @param packageName - The package being released. Must not be empty.
1725
+ * @param version - The version being released. Must not be empty.
1726
+ * @param options - Formatting overrides.
1727
+ */
1728
+ static scoped(packageName: string, version: string, options?: TagFormatOptions): ReleaseTag;
1729
+ }
1730
+ //#endregion
1731
+ //#region src/VersioningStrategy.d.ts
1732
+ /**
1733
+ * How a workspace assigns versions across its publishable packages.
1734
+ *
1735
+ * @remarks
1736
+ * - `single` — zero or one publishable package, so one tag names the release.
1737
+ * - `fixed-group` — every publishable package sits inside one group that
1738
+ * versions in lockstep, so one tag still names the release.
1739
+ * - `independent` — publishable packages version separately, so a shared tag
1740
+ * would be ambiguous and each package needs its own.
1741
+ *
1742
+ * @public
1743
+ */
1744
+ declare const VersioningStrategyType: Schema.Literals<readonly ["single", "fixed-group", "independent"]>;
1745
+ /**
1746
+ * The decoded type of {@link (VersioningStrategyType:variable)}.
1747
+ *
1748
+ * @public
1749
+ */
1750
+ type VersioningStrategyType = typeof VersioningStrategyType.Type;
1751
+ /**
1752
+ * Arguments to {@link VersioningStrategy.classify}.
1753
+ *
1754
+ * @public
1755
+ */
1756
+ interface ClassifyOptions {
1757
+ /** Publishable package names, in any order. Duplicates are collapsed. */
1758
+ readonly packages: ReadonlyArray<string>;
1759
+ /**
1760
+ * Groups of packages that version in lockstep.
1761
+ *
1762
+ * @remarks
1763
+ * A **plain argument, deliberately.** Fixed groups are a release tool's
1764
+ * concept — changesets writes them to `.changeset/config.json` — and a
1765
+ * workspace-model package that read that file would be taking on one tool's
1766
+ * schema and one tool's release policy. The caller reads its own tool's
1767
+ * config and hands the groups in. Groups may name packages that are not
1768
+ * publishable, or do not exist; only whether some single group covers the
1769
+ * whole publishable set matters.
1770
+ */
1771
+ readonly fixedGroups?: ReadonlyArray<ReadonlyArray<string>>;
1772
+ }
1773
+ /**
1774
+ * Arguments to {@link VersioningStrategy.detect}.
1775
+ *
1776
+ * @public
1777
+ */
1778
+ interface VersioningDetectOptions {
1779
+ /** See {@link ClassifyOptions.fixedGroups}. Defaults to none. */
1780
+ readonly fixedGroups?: ReadonlyArray<ReadonlyArray<string>>;
1781
+ }
1782
+ /**
1783
+ * One entry in a release batch: which package went out, at which version.
1784
+ *
1785
+ * @remarks
1786
+ * Deliberately structural and minimal — a caller passes whatever it already
1787
+ * has (a publish result, a changeset plan row) without projecting it into a
1788
+ * package-specific type first.
1789
+ *
1790
+ * @public
1791
+ */
1792
+ interface PackageRelease {
1793
+ /** The package that was released. */
1794
+ readonly name: string;
1795
+ /** The version it was released at. */
1796
+ readonly version: string;
1797
+ }
1798
+ declare const VersioningStrategy_base: Schema.Class<VersioningStrategy, Schema.Struct<{
1799
+ /** The classification. */
1800
+ readonly type: Schema.Literals<readonly ["single", "fixed-group", "independent"]>;
1801
+ /** The groups classification was performed against, as supplied. */
1802
+ readonly fixedGroups: Schema.$Array<Schema.$Array<Schema.String>>;
1803
+ /** The publishable package names, sorted and de-duplicated. */
1804
+ readonly publishablePackages: Schema.$Array<Schema.String>;
1805
+ }>, {}>;
1806
+ /**
1807
+ * How a workspace versions, and the tagging that follows from it.
1808
+ *
1809
+ * @remarks
1810
+ * Built either purely with {@link VersioningStrategy.classify}, or from a live
1811
+ * workspace with {@link VersioningStrategy.detect}.
1812
+ *
1813
+ * @example
1814
+ * ```ts
1815
+ * import { VersioningStrategy } from "@effected/workspaces";
1816
+ * import { Effect } from "effect";
1817
+ *
1818
+ * const program = Effect.gen(function* () {
1819
+ * const strategy = yield* VersioningStrategy.detect({ fixedGroups });
1820
+ * return strategy.tagsFor(released).map((tag) => tag.value);
1821
+ * });
1822
+ * ```
1823
+ *
1824
+ * @public
1825
+ */
1826
+ declare class VersioningStrategy extends VersioningStrategy_base {
1827
+ /**
1828
+ * Whether a release needs one tag per package rather than one shared tag.
1829
+ */
1830
+ get perPackageTags(): boolean;
1831
+ /** The tag style this strategy implies. */
1832
+ get tagStyle(): TagStyle;
1833
+ /**
1834
+ * Classify a workspace from its publishable package names and fixed groups.
1835
+ *
1836
+ * @remarks
1837
+ * Pure and total — no IO, no error channel. `packages` is sorted and
1838
+ * de-duplicated first, so a name listed twice cannot inflate a one-package
1839
+ * repo into an independent one.
1840
+ */
1841
+ static classify(options: ClassifyOptions): VersioningStrategy;
1842
+ /**
1843
+ * Classify the ambient workspace: enumerate its packages, keep the ones the
1844
+ * {@link PublishabilityDetector} says publish somewhere, and classify those.
1845
+ *
1846
+ * @remarks
1847
+ * The publishability question is asked through the service precisely so a
1848
+ * consumer with its own rules — honouring a release tool's ignore list, say —
1849
+ * swaps the layer instead of filtering afterwards.
1850
+ */
1851
+ static readonly detect: (options?: VersioningDetectOptions | undefined) => Effect.Effect<VersioningStrategy, WorkspaceDiscoveryFailure, PublishabilityDetector | WorkspaceDiscovery>;
1852
+ /**
1853
+ * The tags a release of `releases` produces under this strategy.
1854
+ *
1855
+ * @remarks
1856
+ * Under `independent` this is one {@link ReleaseTag} per release, in the
1857
+ * order given. Under `single` and `fixed-group` it is exactly one shared tag
1858
+ * carrying the **first** release's version — every release in a lockstep
1859
+ * batch shares a version by construction, so the choice is only visible on a
1860
+ * batch that should not exist. Whether a batch actually agreed is a property
1861
+ * of that batch rather than of the workspace, so it stays the caller's
1862
+ * one-line check rather than a field here.
1863
+ *
1864
+ * An empty batch produces no tags under either style.
1865
+ */
1866
+ tagsFor(releases: ReadonlyArray<PackageRelease>, options?: TagFormatOptions): ReadonlyArray<ReleaseTag>;
1382
1867
  }
1383
1868
  //#endregion
1384
1869
  //#region src/WorkspaceCatalogs.d.ts
@@ -1957,6 +2442,13 @@ declare class WorkspaceSnapshots extends WorkspaceSnapshots_base {
1957
2442
  * test-wiring mistake fails loudly as a defect rather than succeeding with a
1958
2443
  * lie.
1959
2444
  *
2445
+ * **A defect is not absorbed by `Effect.catch` or any typed-error handler**,
2446
+ * and that is the point: code under test with a best-effort `catch` around
2447
+ * its snapshot reads cannot make a mandatory stub look optional — the
2448
+ * unstubbed call still fails the test instead of quietly taking the catch
2449
+ * branch. Only defect-level combinators (`Effect.catchDefect`,
2450
+ * `Effect.exit`) would see it.
2451
+ *
1960
2452
  * @example
1961
2453
  * ```ts
1962
2454
  * import { CatalogSet, WorkspaceSnapshots, WorkspaceStateSnapshot } from "@effected/workspaces";
@@ -2022,20 +2514,217 @@ interface WorkspacesOptions {
2022
2514
  *
2023
2515
  * @public
2024
2516
  */
2025
- type WorkspacesServices = WorkspaceRoot | PackageManagerDetector | WorkspaceDiscovery | LockfileReader | WorkspaceCatalogs | PublishabilityDetector;
2517
+ type WorkspacesServices = WorkspaceRoot | PackageManagerDetector | WorkspaceDiscovery | LockfileReader | WorkspaceCatalogs;
2026
2518
  /**
2027
2519
  * The composite layers.
2028
2520
  *
2029
2521
  * @public
2030
2522
  */
2031
- declare const Workspaces: {
2032
- readonly layer: (options?: WorkspacesOptions) => Layer.Layer<WorkspacesServices, never, FileSystem.FileSystem | Path.Path>;
2033
- readonly layerWithConfigDependencies: (options?: WorkspacesOptions) => Layer.Layer<WorkspacesServices, never, FileSystem.FileSystem | Path.Path>;
2034
- readonly layerWithGit: (options?: WorkspacesOptions) => Layer.Layer<WorkspacesServices | ChangeDetector | WorkspaceSnapshots | Git, never, FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner>;
2035
- readonly resolveManifest: (manifest: Manifest, options?: WorkspacesOptions) => Effect.Effect<Manifest, CatalogAssemblyError | DependencyResolutionError | UnresolvedDependencyError, FileSystem.FileSystem | Path.Path>;
2036
- readonly resolverLayer: (options?: WorkspacesOptions) => Layer.Layer<CatalogResolver | WorkspaceResolver, never, FileSystem.FileSystem | Path.Path>;
2037
- readonly resolvers: Layer.Layer<CatalogResolver | WorkspaceResolver, never, WorkspaceCatalogs | WorkspaceDiscovery>;
2038
- };
2523
+ declare class Workspaces {
2524
+ private constructor();
2525
+ /**
2526
+ * Every service that needs only a filesystem: root, package-manager
2527
+ * detection, discovery, lockfile reading and catalogs.
2528
+ *
2529
+ * @remarks
2530
+ * Requires core `FileSystem` and `Path`, which the consumer provides at the
2531
+ * edge (`@effect/platform-node`, `@effect/platform-bun`, or a test's
2532
+ * `FileSystem.layerNoop`).
2533
+ *
2534
+ * **`PublishabilityDetector` is neither provided nor required here.** The
2535
+ * composite used to bake in npm semantics, which a naively-ordered override
2536
+ * silently lost to; now it supplies no default, and — because nothing inside
2537
+ * the composite asks a publishability question — it does not require one in
2538
+ * `R` either. The requirement surfaces in the `R` of each operation that
2539
+ * asks (`VersioningStrategy.detect`, e.g.), so a program that asks and never
2540
+ * wires a detector fails to compile at that operation, and a program that
2541
+ * never asks never supplies a publish policy. Wire one explicitly where
2542
+ * needed: `Layer.mergeAll(Workspaces.layer(), PublishabilityDetector.layerNpm)`.
2543
+ *
2544
+ * **Bind the result to a `const`.** This is a parameterized factory and
2545
+ * layers memoize by reference, so calling it twice builds everything twice.
2546
+ *
2547
+ * @example
2548
+ * ```ts
2549
+ * import { Workspaces } from "@effected/workspaces";
2550
+ * import { Layer } from "effect";
2551
+ *
2552
+ * const WorkspacesLayer = Workspaces.layer();
2553
+ * const AppLayer = Layer.provide(WorkspacesLayer, PlatformLayer);
2554
+ * ```
2555
+ */
2556
+ static readonly layer: (options?: WorkspacesOptions) => Layer.Layer<WorkspacesServices, never, FileSystem.FileSystem | Path.Path>;
2557
+ /**
2558
+ * The git-free composite, but with catalog assembly that **replays config
2559
+ * dependency `pnpmfile.cjs` hooks** —
2560
+ * {@link WorkspaceCatalogs.layerWithConfigDependencies} in place of the
2561
+ * default no-op catalogs layer.
2562
+ *
2563
+ * @remarks
2564
+ * Identical requirement set to {@link Workspaces.layer}; the only
2565
+ * difference is that config-dependency code is executed in process. Opt in
2566
+ * deliberately — the default {@link Workspaces.layer} never executes
2567
+ * config-dependency code.
2568
+ *
2569
+ * **Bind the result to a `const`.**
2570
+ */
2571
+ static readonly layerWithConfigDependencies: (options?: WorkspacesOptions) => Layer.Layer<WorkspacesServices, never, FileSystem.FileSystem | Path.Path>;
2572
+ /**
2573
+ * The git-free composite plus {@link ChangeDetector} and
2574
+ * {@link WorkspaceSnapshots}, over `@effected/git`'s `Git` service.
2575
+ *
2576
+ * @remarks
2577
+ * The extra requirement is core's `ChildProcessSpawner` (behind `Git`),
2578
+ * which is why it is a separate layer rather than a flag: a consumer that
2579
+ * never detects changes or reads at a ref should not have to be able to
2580
+ * spawn a subprocess. The consumer provides `ChildProcessSpawner` once at
2581
+ * the edge (`@effect/platform-node`'s `NodeServices.layer`); a test
2582
+ * provides `Layer.succeed(Git, …)` and needs no repository on disk.
2583
+ */
2584
+ static readonly layerWithGit: (options?: WorkspacesOptions) => Layer.Layer<WorkspacesServices | ChangeDetector | WorkspaceSnapshots | Git, never, FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner>;
2585
+ /**
2586
+ * This package's implementation of `@effected/commands`' `LocalExec`
2587
+ * contract: how to run a project-local binary here.
2588
+ *
2589
+ * @remarks
2590
+ * **An inverted contract, the `@effected/npm` `CatalogResolver`
2591
+ * precedent.** Tool discovery needs package-manager detection and
2592
+ * workspace-root resolution, both of which live here — but a direct edge
2593
+ * from `@effected/commands` to this package would make that boundary-tier
2594
+ * package integrated, and through the planned `npm` → `commands` edge
2595
+ * would drag `npm`, `lockfiles` (pure!) and `package-json` up a tier with
2596
+ * it. So `commands` declares the narrow contract and we ship the layer.
2597
+ *
2598
+ * **The argv knowledge is not duplicated.** `LocalExec.prefixes(name)` is
2599
+ * the one home of the four managers' `exec`/`dlx`/script-runner prefixes;
2600
+ * this layer
2601
+ * detects *which* manager owns the directory and asks `commands` what that
2602
+ * manager's argv looks like. Neither package reimplements the other's
2603
+ * half.
2604
+ *
2605
+ * **`None` is success.** Outside any workspace — and inside one whose
2606
+ * manager cannot be identified — the answer is `Option.none()`: "there is
2607
+ * no project-local way to run tools here" is an ordinary fact, not an
2608
+ * exceptional one, and a consumer running in a bare directory should not
2609
+ * have to catch an error to learn it. The contract's typed
2610
+ * `LocalExecError` is reserved for **mechanism** failure — a manifest that
2611
+ * exists but cannot be read or parsed, which means something is broken
2612
+ * rather than absent. That is npm's resolver convention, adopted
2613
+ * verbatim.
2614
+ *
2615
+ * `directory` is the resolved **workspace root**, not the caller's cwd: a
2616
+ * project-local launcher has to run where the workspace is.
2617
+ *
2618
+ * A consumer with no monorepo never needs this layer, and therefore never
2619
+ * installs this package — `LocalExec.layerNone` and `LocalExec.layerFor`
2620
+ * are one-liners in `@effected/commands`.
2621
+ *
2622
+ * **Bind the result to a `const`** — a parameterized layer factory mints a
2623
+ * fresh reference per call and layers memoize by reference.
2624
+ *
2625
+ * @example
2626
+ * ```ts
2627
+ * import { ToolDiscovery } from "@effected/commands";
2628
+ * import { Workspaces } from "@effected/workspaces";
2629
+ * import { Layer } from "effect";
2630
+ *
2631
+ * const AppLayer = ToolDiscovery.layer.pipe(
2632
+ * Layer.provide(Workspaces.localExecLayer()),
2633
+ * Layer.provide(Workspaces.layer()),
2634
+ * Layer.provide(NodeServices.layer),
2635
+ * );
2636
+ * ```
2637
+ */
2638
+ static readonly localExecLayer: (options?: {
2639
+ readonly cwd?: string;
2640
+ }) => Layer.Layer<LocalExec, never, PackageManagerDetector | WorkspaceRoot>;
2641
+ /**
2642
+ * Resolve every `catalog:` and `workspace:` specifier in one `Manifest`
2643
+ * against the real workspace, in one call — the 90% path. Decode stays at
2644
+ * the consumer's edge: build the `Manifest` with `Manifest.decode` (from
2645
+ * `@effected/npm`), hand it here, and get a new `Manifest` back with
2646
+ * concrete ranges; `toRecord()` returns to the wire shape.
2647
+ *
2648
+ * @remarks
2649
+ * Composes `manifest.resolve()` with a fresh {@link Workspaces.resolverLayer}
2650
+ * per call, so the workspace root is re-discovered from `options.cwd` (or
2651
+ * the current `process.cwd()`) on every invocation. Consumers processing
2652
+ * many manifests should check `manifest.needsResolution` first and skip
2653
+ * the call entirely when no dependency field carries a
2654
+ * `catalog:`/`workspace:` specifier — that predicate is pure and avoids
2655
+ * catalog assembly altogether.
2656
+ *
2657
+ * A specifier the workspace cannot answer fails typed as
2658
+ * `UnresolvedDependencyError`; assembly and mechanism failures surface as
2659
+ * `CatalogAssemblyError` / `DependencyResolutionError`.
2660
+ *
2661
+ * @example
2662
+ * ```ts
2663
+ * import { Manifest } from "@effected/npm";
2664
+ * import { Workspaces } from "@effected/workspaces";
2665
+ * import { Effect } from "effect";
2666
+ *
2667
+ * const program = Effect.gen(function* () {
2668
+ * const manifest = yield* Manifest.decode({ dependencies: { effect: "catalog:" } });
2669
+ * const resolved = manifest.needsResolution ? yield* Workspaces.resolveManifest(manifest) : manifest;
2670
+ * return resolved.toRecord();
2671
+ * });
2672
+ * ```
2673
+ */
2674
+ static readonly resolveManifest: (manifest: Manifest, options?: WorkspacesOptions) => Effect.Effect<Manifest, CatalogAssemblyError | DependencyResolutionError | UnresolvedDependencyError, FileSystem.FileSystem | Path.Path>;
2675
+ /**
2676
+ * The one-call resolver factory: {@link Workspaces.resolvers} pre-wired
2677
+ * over {@link Workspaces.layerWithConfigDependencies}, so the two
2678
+ * `@effected/npm` contracts (`CatalogResolver`, `WorkspaceResolver`) need
2679
+ * only a platform (`FileSystem` + `Path`) from the consumer.
2680
+ *
2681
+ * @remarks
2682
+ * This is deliberately a **parameterized layer function, and the fresh
2683
+ * layer per call is the feature**: layers memoize by reference, so each
2684
+ * call mints an unmemoized layer whose root discovery re-runs — including
2685
+ * a per-call `process.cwd()` read when `options.cwd` is omitted. A build
2686
+ * tool that changes directory between manifests gets a correct
2687
+ * re-discovery each time precisely because nothing is shared across
2688
+ * calls. When you *want* sharing, bind one call's result to a `const` and
2689
+ * provide that; the memoization rule is unchanged, this factory just
2690
+ * refuses to hide it.
2691
+ *
2692
+ * Catalog assembly replays config-dependency `pnpmfile` hooks (the
2693
+ * `layerWithConfigDependencies` path) — the semantics a real pnpm install
2694
+ * has. Compose {@link Workspaces.resolvers} with {@link Workspaces.layer}
2695
+ * yourself if config-dependency code must not run in process.
2696
+ *
2697
+ * @example
2698
+ * ```ts
2699
+ * import { Workspaces } from "@effected/workspaces";
2700
+ * import { Effect } from "effect";
2701
+ *
2702
+ * const program = doSomethingWithResolvers.pipe(
2703
+ * Effect.provide(Workspaces.resolverLayer()),
2704
+ * );
2705
+ * ```
2706
+ */
2707
+ static readonly resolverLayer: (options?: WorkspacesOptions) => Layer.Layer<CatalogResolver | WorkspaceResolver, never, FileSystem.FileSystem | Path.Path>;
2708
+ /**
2709
+ * The two `@effected/npm` resolver contracts, implemented for real.
2710
+ *
2711
+ * @remarks
2712
+ * Provide this alongside `@effected/package-json`'s `Package.resolve` and
2713
+ * a manifest's `catalog:` and `workspace:` specifiers resolve against the
2714
+ * actual workspace instead of the no-op layers' `Option.none()`.
2715
+ *
2716
+ * @example
2717
+ * ```ts
2718
+ * import { Package } from "@effected/package-json";
2719
+ * import { Workspaces } from "@effected/workspaces";
2720
+ * import { Layer } from "effect";
2721
+ *
2722
+ * const WorkspacesLayer = Workspaces.layer();
2723
+ * const Resolvers = Workspaces.resolvers.pipe(Layer.provide(WorkspacesLayer));
2724
+ * ```
2725
+ */
2726
+ static readonly resolvers: Layer.Layer<CatalogResolver | WorkspaceResolver, never, WorkspaceCatalogs | WorkspaceDiscovery>;
2727
+ }
2039
2728
  //#endregion
2040
2729
  //#region src/WorkspacesSync.d.ts
2041
2730
  /**
@@ -2226,5 +2915,5 @@ interface GetWorkspacePackagesSyncOptions extends WorkspacesSyncOptions {
2226
2915
  */
2227
2916
  declare const getWorkspacePackagesSync: (root: string, options: GetWorkspacePackagesSyncOptions) => ReadonlyArray<WorkspacePackage>;
2228
2917
  //#endregion
2229
- export { type CatalogAssemblyFailure, CatalogSet, ChangeDetectionError, type ChangeDetectionFailure, ChangeDetectionOptions, ChangeDetector, type ChangeDetectorShape, ConfigDependencyHooks, type ConfigDependencyHooksShape, CyclicDependencyError, type DependencyDiff, DependencyGraph, DetectedPackageManager, type FindWorkspaceRootOptions, type GetWorkspacePackagesSyncOptions, type HookInjection, type ImporterVersions, LockfileReadError, type LockfileReadFailure, LockfileReader, type LockfileReaderOptions, type LockfileReaderShape, PackageManagerDetectionError, type PackageManagerDetectionFailure, PackageManagerDetector, PackageManagerName, PackageNotFoundError, PackageStateSnapshot, PublishConfig, PublishTarget, PublishabilityDetector, type PublishabilityDetectorShape, type SyncFileSystem, type SyncPath, WORKSPACE_MARKERS, WorkspaceCatalogs, type WorkspaceCatalogsOptions, type WorkspaceCatalogsShape, WorkspaceDiscovery, WorkspaceDiscoveryError, type WorkspaceDiscoveryFailure, type WorkspaceDiscoveryOptions, type WorkspaceDiscoveryShape, WorkspaceInfo, type WorkspaceLookupFailure, WorkspaceManifestError, WorkspacePackage, WorkspacePatternError, WorkspaceRoot, WorkspaceRootNotFoundError, type WorkspaceRootShape, type WorkspaceSnapshotAtFailure, type WorkspaceSnapshotWorktreeFailure, WorkspaceSnapshots, type WorkspaceSnapshotsOptions, type WorkspaceSnapshotsShape, WorkspaceStateSnapshot, Workspaces, type WorkspacesOptions, type WorkspacesServices, type WorkspacesSyncOptions, findWorkspaceRootSync, getWorkspacePackagesSync };
2918
+ export { type CatalogAssemblyFailure, CatalogSet, ChangeDetectionError, type ChangeDetectionFailure, ChangeDetectionOptions, ChangeDetector, type ChangeDetectorShape, type ClassifyOptions, ConfigDependencyHooks, type ConfigDependencyHooksShape, CyclicDependencyError, type DependencyDiff, DependencyGraph, DetectedPackageManager, type FindWorkspaceRootOptions, type GetWorkspacePackagesSyncOptions, type HookInjection, type ImporterVersions, LockfileReadError, type LockfileReadFailure, LockfileReader, type LockfileReaderOptions, type LockfileReaderShape, PackageManagerDetectionError, type PackageManagerDetectionFailure, PackageManagerDetector, type PackageManagerDetectorShape, PackageManagerName, PackageNotFoundError, type PackageRelease, PackageStateSnapshot, PublishConfig, PublishTarget, PublishabilityDetector, type PublishabilityDetectorShape, ReleaseTag, type SyncFileSystem, type SyncPath, type TagClassification, type TagFormatOptions, TagStyle, TrackingTag, type TrackingTagOptions, type VersioningDetectOptions, VersioningStrategy, VersioningStrategyType, WORKSPACE_MARKERS, WorkspaceCatalogs, type WorkspaceCatalogsOptions, type WorkspaceCatalogsShape, WorkspaceDiscovery, WorkspaceDiscoveryError, type WorkspaceDiscoveryFailure, type WorkspaceDiscoveryOptions, type WorkspaceDiscoveryShape, WorkspaceInfo, type WorkspaceLookupFailure, WorkspaceManifestError, WorkspacePackage, WorkspacePatternError, WorkspaceRoot, WorkspaceRootNotFoundError, type WorkspaceRootShape, type WorkspaceSnapshotAtFailure, type WorkspaceSnapshotWorktreeFailure, WorkspaceSnapshots, type WorkspaceSnapshotsOptions, type WorkspaceSnapshotsShape, WorkspaceStateSnapshot, Workspaces, type WorkspacesOptions, type WorkspacesServices, type WorkspacesSyncOptions, classifyTag, findWorkspaceRootSync, getWorkspacePackagesSync };
2230
2919
  //# sourceMappingURL=index.d.ts.map