@effected/workspaces 0.7.0 → 0.9.0
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/LockfileReader.js +70 -0
- package/PackageManagerName.js +83 -2
- package/Publishability.js +56 -4
- package/README.md +17 -2
- package/ReleaseTag.js +260 -0
- package/VersioningStrategy.js +122 -0
- package/WorkspaceCatalogs.js +66 -0
- package/WorkspaceSnapshots.js +0 -0
- package/Workspaces.js +215 -148
- package/index.d.ts +840 -14
- package/index.js +3 -1
- package/package.json +8 -7
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<{
|
|
@@ -1046,10 +1047,21 @@ declare class PackageManagerDetectionError extends PackageManagerDetectionError_
|
|
|
1046
1047
|
* @public
|
|
1047
1048
|
*/
|
|
1048
1049
|
type PackageManagerDetectionFailure = PackageManagerDetectionError | WorkspaceManifestError;
|
|
1049
|
-
|
|
1050
|
+
/**
|
|
1051
|
+
* The {@link PackageManagerDetector} service shape.
|
|
1052
|
+
*
|
|
1053
|
+
* @remarks
|
|
1054
|
+
* Exported so a consumer can type a bespoke double against the contract without
|
|
1055
|
+
* reaching into the class — the `WorkspaceDiscoveryShape` /
|
|
1056
|
+
* `PublishabilityDetectorShape` convention.
|
|
1057
|
+
*
|
|
1058
|
+
* @public
|
|
1059
|
+
*/
|
|
1060
|
+
interface PackageManagerDetectorShape {
|
|
1050
1061
|
/** Detect the package manager at a workspace root. */
|
|
1051
1062
|
readonly detect: (root: string) => Effect.Effect<DetectedPackageManager, PackageManagerDetectionFailure>;
|
|
1052
|
-
}
|
|
1063
|
+
}
|
|
1064
|
+
declare const PackageManagerDetector_base: Context.ServiceClass<PackageManagerDetector, "@effected/workspaces/PackageManagerDetector", PackageManagerDetectorShape>;
|
|
1053
1065
|
/**
|
|
1054
1066
|
* Detects which package manager owns a workspace root.
|
|
1055
1067
|
*
|
|
@@ -1098,6 +1110,50 @@ declare class PackageManagerDetector extends PackageManagerDetector_base {
|
|
|
1098
1110
|
}, never, FileSystem.FileSystem | Path.Path>;
|
|
1099
1111
|
/** The live layer. */
|
|
1100
1112
|
static readonly layer: Layer.Layer<PackageManagerDetector, never, FileSystem.FileSystem | Path.Path>;
|
|
1113
|
+
/**
|
|
1114
|
+
* The sanctioned in-memory double.
|
|
1115
|
+
*
|
|
1116
|
+
* @remarks
|
|
1117
|
+
* **`detect` has no honest default, so an unstubbed call dies** — the
|
|
1118
|
+
* `WorkspaceDiscovery.info` posture, for the same reason. A stand-in that
|
|
1119
|
+
* answered `"pnpm"` would hand a consumer a fact nothing established, and it
|
|
1120
|
+
* would contradict the very service it stands in for: the live detector's
|
|
1121
|
+
* defining property is that it [refuses to
|
|
1122
|
+
* guess](https://github.com/spencerbeggs/effected) when no evidence matches.
|
|
1123
|
+
* A double that guesses is worse than no double.
|
|
1124
|
+
*
|
|
1125
|
+
* Failing typed would be the subtler mistake: `PackageManagerDetectionError`
|
|
1126
|
+
* reads as a legitimate "no manager here" answer, so a consumer would branch
|
|
1127
|
+
* on it and proceed, never learning that the test simply forgot to stub.
|
|
1128
|
+
*
|
|
1129
|
+
* @param overrides - Members to supply; anything omitted dies on use.
|
|
1130
|
+
*
|
|
1131
|
+
* @example
|
|
1132
|
+
* ```ts
|
|
1133
|
+
* import { DetectedPackageManager, PackageManagerDetector } from "@effected/workspaces";
|
|
1134
|
+
* import { Effect, Option } from "effect";
|
|
1135
|
+
*
|
|
1136
|
+
* const TestDetector = PackageManagerDetector.layerTest({
|
|
1137
|
+
* detect: () =>
|
|
1138
|
+
* Effect.succeed(
|
|
1139
|
+
* DetectedPackageManager.make({ name: "pnpm", version: Option.none(), runtime: "node" }),
|
|
1140
|
+
* ),
|
|
1141
|
+
* });
|
|
1142
|
+
* ```
|
|
1143
|
+
*/
|
|
1144
|
+
static readonly makeTest: (overrides?: Partial<PackageManagerDetectorShape>) => PackageManagerDetectorShape;
|
|
1145
|
+
/**
|
|
1146
|
+
* {@link PackageManagerDetector.makeTest} behind `Layer.succeed`.
|
|
1147
|
+
*
|
|
1148
|
+
* @remarks
|
|
1149
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
1150
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
1151
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
1152
|
+
*
|
|
1153
|
+
* Pairs with `WorkspaceRoot.layerTest` and `WorkspaceDiscovery.layerTest` to
|
|
1154
|
+
* stand up the whole discovery path with no filesystem at all.
|
|
1155
|
+
*/
|
|
1156
|
+
static readonly layerTest: (overrides?: Partial<PackageManagerDetectorShape>) => Layer.Layer<PackageManagerDetector>;
|
|
1101
1157
|
}
|
|
1102
1158
|
//#endregion
|
|
1103
1159
|
//#region src/LockfileReader.d.ts
|
|
@@ -1212,6 +1268,65 @@ declare class LockfileReader extends LockfileReader_base {
|
|
|
1212
1268
|
* `const` and reuse it.
|
|
1213
1269
|
*/
|
|
1214
1270
|
static readonly layer: (options?: LockfileReaderOptions) => Layer.Layer<LockfileReader, never, WorkspaceRoot | PackageManagerDetector | WorkspaceDiscovery | FileSystem.FileSystem | Path.Path>;
|
|
1271
|
+
/**
|
|
1272
|
+
* A test double satisfying the full {@link LockfileReaderShape} with no
|
|
1273
|
+
* filesystem, root walk, or package-manager detection.
|
|
1274
|
+
*
|
|
1275
|
+
* @remarks
|
|
1276
|
+
* There is **no honest default lockfile**: an empty one that looks like a
|
|
1277
|
+
* legitimate answer is indistinguishable from "this workspace resolves
|
|
1278
|
+
* nothing" — the silent-empty failure class this package documents on the
|
|
1279
|
+
* live paths — so `read` **dies** with an instructive defect until stubbed.
|
|
1280
|
+
*
|
|
1281
|
+
* The one derivation mirrors `WorkspaceDiscovery.makeTest`'s
|
|
1282
|
+
* derived-from-the-primary rule: when a `read` override is supplied,
|
|
1283
|
+
* `resolvedVersion` answers as the live service does — the **first** entry
|
|
1284
|
+
* of `lockfile.packagesNamed(name)` in lockfile order, `Option.none()` on a
|
|
1285
|
+
* miss — so the two stay consistent by construction. `integrity` is **not**
|
|
1286
|
+
* derivable: the live method compares the lockfile against the workspace
|
|
1287
|
+
* manifests discovery enumerates, and the double has no discovery to ask, so
|
|
1288
|
+
* it dies unless stubbed.
|
|
1289
|
+
*
|
|
1290
|
+
* `refresh` defaults to `Effect.void` honestly: the live contract is "drop
|
|
1291
|
+
* the memoized read so the next call re-reads", and this double memoizes
|
|
1292
|
+
* nothing — every `read()` call re-invokes the override — so there is
|
|
1293
|
+
* nothing to drop and the no-op is truthful, the same reasoning as
|
|
1294
|
+
* `WorkspaceDiscovery.makeTest`'s `refresh`.
|
|
1295
|
+
*
|
|
1296
|
+
* @example
|
|
1297
|
+
* ```ts
|
|
1298
|
+
* import { Lockfile } from "@effected/lockfiles";
|
|
1299
|
+
* import { LockfileReader } from "@effected/workspaces";
|
|
1300
|
+
* import { Effect } from "effect";
|
|
1301
|
+
*
|
|
1302
|
+
* const double = LockfileReader.makeTest({
|
|
1303
|
+
* read: () =>
|
|
1304
|
+
* Effect.succeed(
|
|
1305
|
+
* Lockfile.make({ format: "pnpm", lockfileVersion: "9.0", packages: [], workspaceDependencies: [] }),
|
|
1306
|
+
* ),
|
|
1307
|
+
* });
|
|
1308
|
+
* // `resolvedVersion` now answers consistently from that lockfile.
|
|
1309
|
+
* ```
|
|
1310
|
+
*/
|
|
1311
|
+
static readonly makeTest: (overrides?: Partial<LockfileReaderShape>) => LockfileReaderShape;
|
|
1312
|
+
/**
|
|
1313
|
+
* The test layer: {@link LockfileReader.makeTest} behind `Layer.succeed`, so
|
|
1314
|
+
* a suite provides only the methods it exercises.
|
|
1315
|
+
*
|
|
1316
|
+
* @remarks
|
|
1317
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
1318
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
1319
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
1320
|
+
*
|
|
1321
|
+
* @example
|
|
1322
|
+
* ```ts
|
|
1323
|
+
* import { LockfileReader } from "@effected/workspaces";
|
|
1324
|
+
*
|
|
1325
|
+
* const TestLockfiles = LockfileReader.layerTest();
|
|
1326
|
+
* // program.pipe(Effect.provide(TestLockfiles)) — dies loudly if touched.
|
|
1327
|
+
* ```
|
|
1328
|
+
*/
|
|
1329
|
+
static readonly layerTest: (overrides?: Partial<LockfileReaderShape>) => Layer.Layer<LockfileReader>;
|
|
1215
1330
|
}
|
|
1216
1331
|
//#endregion
|
|
1217
1332
|
//#region src/Publishability.d.ts
|
|
@@ -1318,8 +1433,422 @@ declare const PublishabilityDetector_base: Context.ServiceClass<PublishabilityDe
|
|
|
1318
1433
|
* @public
|
|
1319
1434
|
*/
|
|
1320
1435
|
declare class PublishabilityDetector extends PublishabilityDetector_base {
|
|
1321
|
-
/**
|
|
1322
|
-
|
|
1436
|
+
/**
|
|
1437
|
+
* Standard npm publishing semantics, **as a value**. Pure — no filesystem,
|
|
1438
|
+
* no platform services.
|
|
1439
|
+
*
|
|
1440
|
+
* @remarks
|
|
1441
|
+
* Exposed as a shape and not only as a layer, because a consumer composing
|
|
1442
|
+
* *around* these rules cannot reach them through a layer without re-entering
|
|
1443
|
+
* the very tag it is replacing. `@savvy-web/silk-effects` had to write
|
|
1444
|
+
* `Effect.provide(PublishabilityDetector, PublishabilityDetector.layer)`
|
|
1445
|
+
* **inside its own implementation of that tag** to get at this function for
|
|
1446
|
+
* its pass-through branch; with the value exposed that becomes
|
|
1447
|
+
* `PublishabilityDetector.npm.detect(pkg)`.
|
|
1448
|
+
*
|
|
1449
|
+
* @example
|
|
1450
|
+
* ```ts
|
|
1451
|
+
* import { PublishabilityDetector } from "@effected/workspaces";
|
|
1452
|
+
* import { Effect, Layer } from "effect";
|
|
1453
|
+
*
|
|
1454
|
+
* // A policy that defers to npm semantics for everything it does not veto.
|
|
1455
|
+
* const withVeto = Layer.succeed(PublishabilityDetector, {
|
|
1456
|
+
* detect: (pkg) =>
|
|
1457
|
+
* pkg.name.endsWith("-private")
|
|
1458
|
+
* ? Effect.succeed([])
|
|
1459
|
+
* : PublishabilityDetector.npm.detect(pkg),
|
|
1460
|
+
* });
|
|
1461
|
+
* ```
|
|
1462
|
+
*/
|
|
1463
|
+
static readonly npm: PublishabilityDetectorShape;
|
|
1464
|
+
/** Nothing publishes. */
|
|
1465
|
+
static readonly none: PublishabilityDetectorShape;
|
|
1466
|
+
/**
|
|
1467
|
+
* {@link PublishabilityDetector.npm} as a layer.
|
|
1468
|
+
*
|
|
1469
|
+
* @remarks
|
|
1470
|
+
* Named for its policy rather than called `layer`, deliberately. **No
|
|
1471
|
+
* composite in this package provides a publishability detector**: a
|
|
1472
|
+
* `Workspaces.layer()` that quietly supplied npm semantics made the choice
|
|
1473
|
+
* invisible, and worse, made a naively-ordered override lose to it in
|
|
1474
|
+
* silence — `Layer.mergeAll(myDetector, Workspaces.layer())` resolved to the
|
|
1475
|
+
* default, because `mergeAll` is last-wins. For a service that decides
|
|
1476
|
+
* whether a package publishes and to which registry, that silent revert was
|
|
1477
|
+
* the worst available failure. The requirement now sits in `R`, so the
|
|
1478
|
+
* choice is made once, explicitly, and unmade wiring does not compile.
|
|
1479
|
+
*/
|
|
1480
|
+
static readonly layerNpm: Layer.Layer<PublishabilityDetector>;
|
|
1481
|
+
/**
|
|
1482
|
+
* {@link PublishabilityDetector.none} as a layer: a workspace where nothing
|
|
1483
|
+
* publishes.
|
|
1484
|
+
*
|
|
1485
|
+
* @remarks
|
|
1486
|
+
* For dry runs, and for a release tool whose configuration disables
|
|
1487
|
+
* publishing wholesale — silk's changeset `mode: "none"` is exactly this.
|
|
1488
|
+
*/
|
|
1489
|
+
static readonly layerNone: Layer.Layer<PublishabilityDetector>;
|
|
1490
|
+
}
|
|
1491
|
+
//#endregion
|
|
1492
|
+
//#region src/ReleaseTag.d.ts
|
|
1493
|
+
/**
|
|
1494
|
+
* Whether one shared tag names a whole release, or one tag names each package.
|
|
1495
|
+
*
|
|
1496
|
+
* @remarks
|
|
1497
|
+
* `single` is the shape of a single-package repo and of a monorepo whose
|
|
1498
|
+
* publishable packages all version in lockstep; `scoped` is the shape of
|
|
1499
|
+
* independent versioning, where a shared tag would be ambiguous.
|
|
1500
|
+
*
|
|
1501
|
+
* @public
|
|
1502
|
+
*/
|
|
1503
|
+
declare const TagStyle: Schema.Literals<readonly ["single", "scoped"]>;
|
|
1504
|
+
/**
|
|
1505
|
+
* The decoded type of {@link (TagStyle:variable)}: `"single" | "scoped"`.
|
|
1506
|
+
*
|
|
1507
|
+
* @public
|
|
1508
|
+
*/
|
|
1509
|
+
type TagStyle = typeof TagStyle.Type;
|
|
1510
|
+
/**
|
|
1511
|
+
* Formatting knobs for {@link ReleaseTag.single} and {@link ReleaseTag.scoped}.
|
|
1512
|
+
*
|
|
1513
|
+
* @public
|
|
1514
|
+
*/
|
|
1515
|
+
interface TagFormatOptions {
|
|
1516
|
+
/**
|
|
1517
|
+
* The prefix on the version segment.
|
|
1518
|
+
*
|
|
1519
|
+
* @remarks
|
|
1520
|
+
* Defaults to `""` uniformly, for both {@link ReleaseTag.single} and
|
|
1521
|
+
* {@link ReleaseTag.scoped} — strict SemVer, deliberately chosen. Pass
|
|
1522
|
+
* `"v"` for the GitHub release-tag convention (`v1.2.3`), which tools such
|
|
1523
|
+
* as `actions/checkout`'s ref resolution and third-party changelog
|
|
1524
|
+
* generators expect.
|
|
1525
|
+
*/
|
|
1526
|
+
readonly versionPrefix?: string;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Options for {@link TrackingTag.forVersion}.
|
|
1530
|
+
*
|
|
1531
|
+
* @public
|
|
1532
|
+
*/
|
|
1533
|
+
interface TrackingTagOptions {
|
|
1534
|
+
/**
|
|
1535
|
+
* Prefix the alias with a package name, for a monorepo that namespaces its
|
|
1536
|
+
* tags: `@scope/pkg@v1`. Omit it for a single-package repo's bare `v1`.
|
|
1537
|
+
*/
|
|
1538
|
+
readonly packageName?: string;
|
|
1539
|
+
/**
|
|
1540
|
+
* Which aliases to derive. `"both"` (the default) gives `v1` and `v1.2`;
|
|
1541
|
+
* `"major"` gives only the broad `v1`.
|
|
1542
|
+
*/
|
|
1543
|
+
readonly precision?: "major" | "both";
|
|
1544
|
+
/**
|
|
1545
|
+
* Derive aliases for a prerelease version too. **Off by default, and you
|
|
1546
|
+
* almost certainly want it off** — see {@link TrackingTag.forVersion}.
|
|
1547
|
+
*/
|
|
1548
|
+
readonly includePrerelease?: boolean;
|
|
1549
|
+
}
|
|
1550
|
+
declare const TrackingTag_base: Schema.Class<TrackingTag, Schema.Struct<{
|
|
1551
|
+
/** The tag string exactly as it appears in git. */
|
|
1552
|
+
readonly value: Schema.NonEmptyString;
|
|
1553
|
+
/** The package the alias namespaces; absent on a bare `v1`. */
|
|
1554
|
+
readonly packageName: Schema.optionalKey<Schema.NonEmptyString>;
|
|
1555
|
+
/** The major version the alias tracks. */
|
|
1556
|
+
readonly major: Schema.Int;
|
|
1557
|
+
/** The minor version, on a `v1.2`-precision alias; absent on `v1`. */
|
|
1558
|
+
readonly minor: Schema.optionalKey<Schema.Int>;
|
|
1559
|
+
}>, {}>;
|
|
1560
|
+
/**
|
|
1561
|
+
* A floating alias tag — `v1`, `v1.2` — that a repo re-points at its newest
|
|
1562
|
+
* matching release.
|
|
1563
|
+
*
|
|
1564
|
+
* @remarks
|
|
1565
|
+
* This is the GitHub Actions distribution convention: a consumer writes
|
|
1566
|
+
* `uses: owner/repo@v1` and receives whatever 1.x the repo last pointed `v1` at.
|
|
1567
|
+
*
|
|
1568
|
+
* **Deliberately not SemVer, and deliberately not a {@link (TagStyle:variable)}.**
|
|
1569
|
+
* A release tag names one immutable version; a tracking tag is an alias derived
|
|
1570
|
+
* *from* a version, carrying a truncated number that is not a version at all.
|
|
1571
|
+
* Folding it into `ReleaseTag` as a third style would put a mutable pointer and
|
|
1572
|
+
* an immutable name behind one type.
|
|
1573
|
+
*
|
|
1574
|
+
* Everything here is derivation, formatting and parsing. **Actually moving a
|
|
1575
|
+
* git tag is not this package's business** — a consumer does that through git,
|
|
1576
|
+
* and the deliberate omission is what keeps this module a pure leaf.
|
|
1577
|
+
*
|
|
1578
|
+
* @example
|
|
1579
|
+
* ```ts
|
|
1580
|
+
* import { TrackingTag } from "@effected/workspaces";
|
|
1581
|
+
*
|
|
1582
|
+
* TrackingTag.forVersion("1.2.3").map((t) => t.value); // ["v1", "v1.2"]
|
|
1583
|
+
* TrackingTag.forVersion("1.0.0-beta.3"); // [] — never float onto a beta
|
|
1584
|
+
* TrackingTag.forVersion("1.2.3", { packageName: "@acme/cli" });
|
|
1585
|
+
* // ["@acme/cli@v1", "@acme/cli@v1.2"]
|
|
1586
|
+
* ```
|
|
1587
|
+
*
|
|
1588
|
+
* @public
|
|
1589
|
+
*/
|
|
1590
|
+
declare class TrackingTag extends TrackingTag_base {
|
|
1591
|
+
/** Whether this alias tracks a whole major line, or one minor line inside it. */
|
|
1592
|
+
get precision(): "major" | "minor";
|
|
1593
|
+
/**
|
|
1594
|
+
* The tracking tags a release of `version` should be pointed at.
|
|
1595
|
+
*
|
|
1596
|
+
* @remarks
|
|
1597
|
+
* **A prerelease derives nothing.** Anyone depending on `owner/repo@v1` is
|
|
1598
|
+
* asking for the newest *stable* 1.x, so re-pointing that alias at
|
|
1599
|
+
* `1.0.0-beta.3` would ship a prerelease to every such consumer with no
|
|
1600
|
+
* signal at all. `includePrerelease` exists for callers who genuinely mean
|
|
1601
|
+
* it — a prerelease-only distribution channel — and should be rare.
|
|
1602
|
+
*
|
|
1603
|
+
* **Total, never throwing.** A version that is not `X.Y.Z` derives nothing
|
|
1604
|
+
* rather than failing: this is a query about a version, not a validation of
|
|
1605
|
+
* one, and `WorkspacePackage.version` is deliberately tolerant, so odd
|
|
1606
|
+
* versions reach here routinely.
|
|
1607
|
+
*
|
|
1608
|
+
* 0.x versions DO derive aliases. Floating `v0` across 0.x minors is a real
|
|
1609
|
+
* hazard, but which aliases to publish is the caller's policy, decided where
|
|
1610
|
+
* the tags are moved — not something a derivation should quietly withhold.
|
|
1611
|
+
*
|
|
1612
|
+
* @param version - The version being released.
|
|
1613
|
+
* @param options - Package prefix, precision and the prerelease override.
|
|
1614
|
+
* @returns The aliases, broadest first; empty when none apply.
|
|
1615
|
+
*/
|
|
1616
|
+
static forVersion(version: string, options?: TrackingTagOptions): ReadonlyArray<TrackingTag>;
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* What an arbitrary tag string denotes.
|
|
1620
|
+
*
|
|
1621
|
+
* @remarks
|
|
1622
|
+
* `unrecognized` is a real answer, not a failure: a repository's tags include
|
|
1623
|
+
* branches-turned-tags, `latest`, and whatever else humans wrote, and forcing
|
|
1624
|
+
* those into a bucket is exactly what a classifier must not do.
|
|
1625
|
+
*
|
|
1626
|
+
* @public
|
|
1627
|
+
*/
|
|
1628
|
+
type TagClassification = {
|
|
1629
|
+
readonly kind: "release";
|
|
1630
|
+
readonly tag: ReleaseTag;
|
|
1631
|
+
} | {
|
|
1632
|
+
readonly kind: "tracking";
|
|
1633
|
+
readonly tag: TrackingTag;
|
|
1634
|
+
} | {
|
|
1635
|
+
readonly kind: "unrecognized";
|
|
1636
|
+
};
|
|
1637
|
+
/**
|
|
1638
|
+
* Decide whether a tag string is a release tag, a tracking alias, or neither.
|
|
1639
|
+
*
|
|
1640
|
+
* @remarks
|
|
1641
|
+
* The two families are told apart by **segment count**, not by the `v` prefix:
|
|
1642
|
+
* three numeric segments is a version (so `1.0.0` and `v1.0.0` are both release
|
|
1643
|
+
* tags), while one or two segments is a truncated alias. The `v` *is* required
|
|
1644
|
+
* on an alias — a bare `1` is neither valid SemVer nor the tracking convention,
|
|
1645
|
+
* and accepting it would make this function guess.
|
|
1646
|
+
*
|
|
1647
|
+
* The package prefix splits at the **last** `@`, so a leading npm scope
|
|
1648
|
+
* survives: `@scope/pkg@1.0.0` is package `@scope/pkg` at version `1.0.0`.
|
|
1649
|
+
*
|
|
1650
|
+
* Round-tripping is a tested property: every tag {@link ReleaseTag} and
|
|
1651
|
+
* {@link TrackingTag} format classifies back to the family that produced it,
|
|
1652
|
+
* with its fields intact.
|
|
1653
|
+
*
|
|
1654
|
+
* @param tag - Any tag string.
|
|
1655
|
+
* @returns The classification.
|
|
1656
|
+
*
|
|
1657
|
+
* @public
|
|
1658
|
+
*/
|
|
1659
|
+
declare const classifyTag: (tag: string) => TagClassification;
|
|
1660
|
+
declare const ReleaseTag_base: Schema.Class<ReleaseTag, Schema.Struct<{
|
|
1661
|
+
/** The tag string exactly as it appears in git. */
|
|
1662
|
+
readonly value: Schema.NonEmptyString;
|
|
1663
|
+
/** The package the tag names; absent on a workspace-wide single tag. */
|
|
1664
|
+
readonly packageName: Schema.optionalKey<Schema.NonEmptyString>;
|
|
1665
|
+
/** The version the tag names, without any prefix. */
|
|
1666
|
+
readonly version: Schema.NonEmptyString;
|
|
1667
|
+
/** Which style produced it. */
|
|
1668
|
+
readonly style: Schema.Literals<readonly ["single", "scoped"]>;
|
|
1669
|
+
}>, {}>;
|
|
1670
|
+
/**
|
|
1671
|
+
* A git tag naming a release, and the parts it was built from.
|
|
1672
|
+
*
|
|
1673
|
+
* @remarks
|
|
1674
|
+
* `value` is the tag exactly as it appears in git; `version` stays **bare**
|
|
1675
|
+
* even when `value` carries a prefix, so a consumer comparing versions never
|
|
1676
|
+
* has to strip one back off.
|
|
1677
|
+
*
|
|
1678
|
+
* Formatting is **total**: there is no error channel, because the only failure
|
|
1679
|
+
* v3 modelled — an empty version — is caught by `Schema.NonEmptyString` when
|
|
1680
|
+
* the value is constructed. A bad version reaching these statics is developer
|
|
1681
|
+
* wiring rather than untrusted input, so it dies as a defect, the same posture
|
|
1682
|
+
* as an uncompilable glob literal in `WorkspacePackage.matchesDependency`.
|
|
1683
|
+
*
|
|
1684
|
+
* @example
|
|
1685
|
+
* ```ts
|
|
1686
|
+
* import { ReleaseTag } from "@effected/workspaces";
|
|
1687
|
+
*
|
|
1688
|
+
* ReleaseTag.single("1.2.3").value; // "1.2.3"
|
|
1689
|
+
* ReleaseTag.scoped("@acme/cli", "1.2.3").value; // "@acme/cli@1.2.3"
|
|
1690
|
+
* ReleaseTag.scoped("cli", "1.2.3").value; // "cli@1.2.3"
|
|
1691
|
+
* ReleaseTag.scoped("cli", "1.2.3", { versionPrefix: "v" }).value; // "cli@v1.2.3"
|
|
1692
|
+
* ```
|
|
1693
|
+
*
|
|
1694
|
+
* @public
|
|
1695
|
+
*/
|
|
1696
|
+
declare class ReleaseTag extends ReleaseTag_base {
|
|
1697
|
+
/**
|
|
1698
|
+
* One shared tag for a whole release: `1.2.3`.
|
|
1699
|
+
*
|
|
1700
|
+
* @param version - The version being released. Must not be empty.
|
|
1701
|
+
* @param options - Formatting overrides.
|
|
1702
|
+
*/
|
|
1703
|
+
static single(version: string, options?: TagFormatOptions): ReleaseTag;
|
|
1704
|
+
/**
|
|
1705
|
+
* A per-package tag: `<packageName>@<version>` — `@scope/pkg@1.2.3` for a
|
|
1706
|
+
* scoped name, `pkg@1.2.3` for an unscoped one, uniformly, unless
|
|
1707
|
+
* `options.versionPrefix` says otherwise.
|
|
1708
|
+
*
|
|
1709
|
+
* @param packageName - The package being released. Must not be empty.
|
|
1710
|
+
* @param version - The version being released. Must not be empty.
|
|
1711
|
+
* @param options - Formatting overrides.
|
|
1712
|
+
*/
|
|
1713
|
+
static scoped(packageName: string, version: string, options?: TagFormatOptions): ReleaseTag;
|
|
1714
|
+
}
|
|
1715
|
+
//#endregion
|
|
1716
|
+
//#region src/VersioningStrategy.d.ts
|
|
1717
|
+
/**
|
|
1718
|
+
* How a workspace assigns versions across its publishable packages.
|
|
1719
|
+
*
|
|
1720
|
+
* @remarks
|
|
1721
|
+
* - `single` — zero or one publishable package, so one tag names the release.
|
|
1722
|
+
* - `fixed-group` — every publishable package sits inside one group that
|
|
1723
|
+
* versions in lockstep, so one tag still names the release.
|
|
1724
|
+
* - `independent` — publishable packages version separately, so a shared tag
|
|
1725
|
+
* would be ambiguous and each package needs its own.
|
|
1726
|
+
*
|
|
1727
|
+
* @public
|
|
1728
|
+
*/
|
|
1729
|
+
declare const VersioningStrategyType: Schema.Literals<readonly ["single", "fixed-group", "independent"]>;
|
|
1730
|
+
/**
|
|
1731
|
+
* The decoded type of {@link (VersioningStrategyType:variable)}.
|
|
1732
|
+
*
|
|
1733
|
+
* @public
|
|
1734
|
+
*/
|
|
1735
|
+
type VersioningStrategyType = typeof VersioningStrategyType.Type;
|
|
1736
|
+
/**
|
|
1737
|
+
* Arguments to {@link VersioningStrategy.classify}.
|
|
1738
|
+
*
|
|
1739
|
+
* @public
|
|
1740
|
+
*/
|
|
1741
|
+
interface ClassifyOptions {
|
|
1742
|
+
/** Publishable package names, in any order. Duplicates are collapsed. */
|
|
1743
|
+
readonly packages: ReadonlyArray<string>;
|
|
1744
|
+
/**
|
|
1745
|
+
* Groups of packages that version in lockstep.
|
|
1746
|
+
*
|
|
1747
|
+
* @remarks
|
|
1748
|
+
* A **plain argument, deliberately.** Fixed groups are a release tool's
|
|
1749
|
+
* concept — changesets writes them to `.changeset/config.json` — and a
|
|
1750
|
+
* workspace-model package that read that file would be taking on one tool's
|
|
1751
|
+
* schema and one tool's release policy. The caller reads its own tool's
|
|
1752
|
+
* config and hands the groups in. Groups may name packages that are not
|
|
1753
|
+
* publishable, or do not exist; only whether some single group covers the
|
|
1754
|
+
* whole publishable set matters.
|
|
1755
|
+
*/
|
|
1756
|
+
readonly fixedGroups?: ReadonlyArray<ReadonlyArray<string>>;
|
|
1757
|
+
}
|
|
1758
|
+
/**
|
|
1759
|
+
* Arguments to {@link VersioningStrategy.detect}.
|
|
1760
|
+
*
|
|
1761
|
+
* @public
|
|
1762
|
+
*/
|
|
1763
|
+
interface VersioningDetectOptions {
|
|
1764
|
+
/** See {@link ClassifyOptions.fixedGroups}. Defaults to none. */
|
|
1765
|
+
readonly fixedGroups?: ReadonlyArray<ReadonlyArray<string>>;
|
|
1766
|
+
}
|
|
1767
|
+
/**
|
|
1768
|
+
* One entry in a release batch: which package went out, at which version.
|
|
1769
|
+
*
|
|
1770
|
+
* @remarks
|
|
1771
|
+
* Deliberately structural and minimal — a caller passes whatever it already
|
|
1772
|
+
* has (a publish result, a changeset plan row) without projecting it into a
|
|
1773
|
+
* package-specific type first.
|
|
1774
|
+
*
|
|
1775
|
+
* @public
|
|
1776
|
+
*/
|
|
1777
|
+
interface PackageRelease {
|
|
1778
|
+
/** The package that was released. */
|
|
1779
|
+
readonly name: string;
|
|
1780
|
+
/** The version it was released at. */
|
|
1781
|
+
readonly version: string;
|
|
1782
|
+
}
|
|
1783
|
+
declare const VersioningStrategy_base: Schema.Class<VersioningStrategy, Schema.Struct<{
|
|
1784
|
+
/** The classification. */
|
|
1785
|
+
readonly type: Schema.Literals<readonly ["single", "fixed-group", "independent"]>;
|
|
1786
|
+
/** The groups classification was performed against, as supplied. */
|
|
1787
|
+
readonly fixedGroups: Schema.$Array<Schema.$Array<Schema.String>>;
|
|
1788
|
+
/** The publishable package names, sorted and de-duplicated. */
|
|
1789
|
+
readonly publishablePackages: Schema.$Array<Schema.String>;
|
|
1790
|
+
}>, {}>;
|
|
1791
|
+
/**
|
|
1792
|
+
* How a workspace versions, and the tagging that follows from it.
|
|
1793
|
+
*
|
|
1794
|
+
* @remarks
|
|
1795
|
+
* Built either purely with {@link VersioningStrategy.classify}, or from a live
|
|
1796
|
+
* workspace with {@link VersioningStrategy.detect}.
|
|
1797
|
+
*
|
|
1798
|
+
* @example
|
|
1799
|
+
* ```ts
|
|
1800
|
+
* import { VersioningStrategy } from "@effected/workspaces";
|
|
1801
|
+
* import { Effect } from "effect";
|
|
1802
|
+
*
|
|
1803
|
+
* const program = Effect.gen(function* () {
|
|
1804
|
+
* const strategy = yield* VersioningStrategy.detect({ fixedGroups });
|
|
1805
|
+
* return strategy.tagsFor(released).map((tag) => tag.value);
|
|
1806
|
+
* });
|
|
1807
|
+
* ```
|
|
1808
|
+
*
|
|
1809
|
+
* @public
|
|
1810
|
+
*/
|
|
1811
|
+
declare class VersioningStrategy extends VersioningStrategy_base {
|
|
1812
|
+
/**
|
|
1813
|
+
* Whether a release needs one tag per package rather than one shared tag.
|
|
1814
|
+
*/
|
|
1815
|
+
get perPackageTags(): boolean;
|
|
1816
|
+
/** The tag style this strategy implies. */
|
|
1817
|
+
get tagStyle(): TagStyle;
|
|
1818
|
+
/**
|
|
1819
|
+
* Classify a workspace from its publishable package names and fixed groups.
|
|
1820
|
+
*
|
|
1821
|
+
* @remarks
|
|
1822
|
+
* Pure and total — no IO, no error channel. `packages` is sorted and
|
|
1823
|
+
* de-duplicated first, so a name listed twice cannot inflate a one-package
|
|
1824
|
+
* repo into an independent one.
|
|
1825
|
+
*/
|
|
1826
|
+
static classify(options: ClassifyOptions): VersioningStrategy;
|
|
1827
|
+
/**
|
|
1828
|
+
* Classify the ambient workspace: enumerate its packages, keep the ones the
|
|
1829
|
+
* {@link PublishabilityDetector} says publish somewhere, and classify those.
|
|
1830
|
+
*
|
|
1831
|
+
* @remarks
|
|
1832
|
+
* The publishability question is asked through the service precisely so a
|
|
1833
|
+
* consumer with its own rules — honouring a release tool's ignore list, say —
|
|
1834
|
+
* swaps the layer instead of filtering afterwards.
|
|
1835
|
+
*/
|
|
1836
|
+
static readonly detect: (options?: VersioningDetectOptions | undefined) => Effect.Effect<VersioningStrategy, WorkspaceDiscoveryFailure, PublishabilityDetector | WorkspaceDiscovery>;
|
|
1837
|
+
/**
|
|
1838
|
+
* The tags a release of `releases` produces under this strategy.
|
|
1839
|
+
*
|
|
1840
|
+
* @remarks
|
|
1841
|
+
* Under `independent` this is one {@link ReleaseTag} per release, in the
|
|
1842
|
+
* order given. Under `single` and `fixed-group` it is exactly one shared tag
|
|
1843
|
+
* carrying the **first** release's version — every release in a lockstep
|
|
1844
|
+
* batch shares a version by construction, so the choice is only visible on a
|
|
1845
|
+
* batch that should not exist. Whether a batch actually agreed is a property
|
|
1846
|
+
* of that batch rather than of the workspace, so it stays the caller's
|
|
1847
|
+
* one-line check rather than a field here.
|
|
1848
|
+
*
|
|
1849
|
+
* An empty batch produces no tags under either style.
|
|
1850
|
+
*/
|
|
1851
|
+
tagsFor(releases: ReadonlyArray<PackageRelease>, options?: TagFormatOptions): ReadonlyArray<ReleaseTag>;
|
|
1323
1852
|
}
|
|
1324
1853
|
//#endregion
|
|
1325
1854
|
//#region src/WorkspaceCatalogs.d.ts
|
|
@@ -1553,6 +2082,61 @@ declare class WorkspaceCatalogs extends WorkspaceCatalogs_base {
|
|
|
1553
2082
|
* Parameterized, so bind it to a `const` and reuse it.
|
|
1554
2083
|
*/
|
|
1555
2084
|
static readonly layerWithConfigDependencies: (options?: WorkspaceCatalogsOptions) => Layer.Layer<WorkspaceCatalogs, never, WorkspaceRoot | LockfileReader | FileSystem.FileSystem | Path.Path>;
|
|
2085
|
+
/**
|
|
2086
|
+
* A test double satisfying the full {@link WorkspaceCatalogsShape} with no
|
|
2087
|
+
* filesystem, lockfile read, or hook replay.
|
|
2088
|
+
*
|
|
2089
|
+
* @remarks
|
|
2090
|
+
* There is **no honest default catalog set**: an empty `CatalogSet` that
|
|
2091
|
+
* looks like a legitimate answer is the "every dependency looks newly added"
|
|
2092
|
+
* failure class the live assembler hard-fails to prevent, so every method
|
|
2093
|
+
* **dies** with an instructive defect until stubbed — a test-wiring mistake
|
|
2094
|
+
* fails loudly as a defect rather than succeeding with a lie or failing with
|
|
2095
|
+
* a dishonest typed error.
|
|
2096
|
+
*
|
|
2097
|
+
* The one derivation mirrors `WorkspaceDiscovery.makeTest`'s
|
|
2098
|
+
* derived-from-the-primary rule: when a `set` override is supplied,
|
|
2099
|
+
* `resolveSpecifier` answers from that `CatalogSet`'s own
|
|
2100
|
+
* {@link CatalogSet.resolveSpecifier} — exactly what the live service runs
|
|
2101
|
+
* over its assembled set — so the two stay consistent by construction.
|
|
2102
|
+
* `releaseAgeGate` and `importerVersions` are **not** derivable from a
|
|
2103
|
+
* catalog set (the gate comes from release-age keys and hook contributions,
|
|
2104
|
+
* the importer index from the lockfile's importer blocks — neither is in a
|
|
2105
|
+
* `CatalogSet`) and always die unless stubbed.
|
|
2106
|
+
*
|
|
2107
|
+
* @example
|
|
2108
|
+
* ```ts
|
|
2109
|
+
* import { CatalogSet, WorkspaceCatalogs } from "@effected/workspaces";
|
|
2110
|
+
* import { Effect } from "effect";
|
|
2111
|
+
*
|
|
2112
|
+
* const double = WorkspaceCatalogs.makeTest({
|
|
2113
|
+
* set: () => Effect.succeed(CatalogSet.fromCatalogs({ default: { effect: "4.0.0" } })),
|
|
2114
|
+
* });
|
|
2115
|
+
* // `resolveSpecifier` now answers consistently from that set.
|
|
2116
|
+
* ```
|
|
2117
|
+
*/
|
|
2118
|
+
static readonly makeTest: (overrides?: Partial<WorkspaceCatalogsShape>) => WorkspaceCatalogsShape;
|
|
2119
|
+
/**
|
|
2120
|
+
* The test layer: {@link WorkspaceCatalogs.makeTest} behind `Layer.succeed`,
|
|
2121
|
+
* so a suite provides only the methods it exercises.
|
|
2122
|
+
*
|
|
2123
|
+
* @remarks
|
|
2124
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
2125
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
2126
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
2127
|
+
*
|
|
2128
|
+
* @example
|
|
2129
|
+
* ```ts
|
|
2130
|
+
* import { CatalogSet, WorkspaceCatalogs } from "@effected/workspaces";
|
|
2131
|
+
* import { Effect } from "effect";
|
|
2132
|
+
*
|
|
2133
|
+
* const TestCatalogs = WorkspaceCatalogs.layerTest({
|
|
2134
|
+
* set: () => Effect.succeed(CatalogSet.empty()),
|
|
2135
|
+
* });
|
|
2136
|
+
* // program.pipe(Effect.provide(TestCatalogs))
|
|
2137
|
+
* ```
|
|
2138
|
+
*/
|
|
2139
|
+
static readonly layerTest: (overrides?: Partial<WorkspaceCatalogsShape>) => Layer.Layer<WorkspaceCatalogs>;
|
|
1556
2140
|
/**
|
|
1557
2141
|
* The real implementation of `@effected/npm`'s `CatalogResolver` contract —
|
|
1558
2142
|
* the one `@effected/package-json` declares but cannot fill.
|
|
@@ -1828,6 +2412,62 @@ declare class WorkspaceSnapshots extends WorkspaceSnapshots_base {
|
|
|
1828
2412
|
* `const` and reuse it, or layer memoization does not apply.
|
|
1829
2413
|
*/
|
|
1830
2414
|
static readonly layer: (options?: WorkspaceSnapshotsOptions) => Layer.Layer<WorkspaceSnapshots, never, Git | WorkspaceRoot | WorkspaceDiscovery | WorkspaceCatalogs>;
|
|
2415
|
+
/**
|
|
2416
|
+
* A test double satisfying the full {@link WorkspaceSnapshotsShape} with no
|
|
2417
|
+
* git, filesystem, discovery, or catalog assembly.
|
|
2418
|
+
*
|
|
2419
|
+
* @remarks
|
|
2420
|
+
* There are **no honest defaults and no derivations** here: `at(ref)` and
|
|
2421
|
+
* `worktree()` are two independent reads of two different sources (a git ref
|
|
2422
|
+
* vs. the live tree), so neither can be honestly derived from the other, and
|
|
2423
|
+
* a fabricated empty {@link WorkspaceStateSnapshot} on either side of a
|
|
2424
|
+
* before/after diff reads as every dependency newly added or removed — the
|
|
2425
|
+
* exact silent-empty failure class this package documents on the live paths.
|
|
2426
|
+
* Both methods therefore **die** with an instructive defect until stubbed; a
|
|
2427
|
+
* test-wiring mistake fails loudly as a defect rather than succeeding with a
|
|
2428
|
+
* lie.
|
|
2429
|
+
*
|
|
2430
|
+
* @example
|
|
2431
|
+
* ```ts
|
|
2432
|
+
* import { CatalogSet, WorkspaceSnapshots, WorkspaceStateSnapshot } from "@effected/workspaces";
|
|
2433
|
+
* import { Effect } from "effect";
|
|
2434
|
+
*
|
|
2435
|
+
* const empty = WorkspaceStateSnapshot.make({
|
|
2436
|
+
* packages: [],
|
|
2437
|
+
* catalogs: CatalogSet.empty(),
|
|
2438
|
+
* importerVersions: {},
|
|
2439
|
+
* });
|
|
2440
|
+
* const double = WorkspaceSnapshots.makeTest({
|
|
2441
|
+
* at: () => Effect.succeed(empty),
|
|
2442
|
+
* worktree: () => Effect.succeed(empty),
|
|
2443
|
+
* });
|
|
2444
|
+
* ```
|
|
2445
|
+
*/
|
|
2446
|
+
static readonly makeTest: (overrides?: Partial<WorkspaceSnapshotsShape>) => WorkspaceSnapshotsShape;
|
|
2447
|
+
/**
|
|
2448
|
+
* The test layer: {@link WorkspaceSnapshots.makeTest} behind `Layer.succeed`,
|
|
2449
|
+
* so a suite provides only the methods it exercises.
|
|
2450
|
+
*
|
|
2451
|
+
* @remarks
|
|
2452
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
2453
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
2454
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
2455
|
+
*
|
|
2456
|
+
* @example
|
|
2457
|
+
* ```ts
|
|
2458
|
+
* import { CatalogSet, WorkspaceSnapshots, WorkspaceStateSnapshot } from "@effected/workspaces";
|
|
2459
|
+
* import { Effect } from "effect";
|
|
2460
|
+
*
|
|
2461
|
+
* const TestSnapshots = WorkspaceSnapshots.layerTest({
|
|
2462
|
+
* worktree: () =>
|
|
2463
|
+
* Effect.succeed(
|
|
2464
|
+
* WorkspaceStateSnapshot.make({ packages: [], catalogs: CatalogSet.empty(), importerVersions: {} }),
|
|
2465
|
+
* ),
|
|
2466
|
+
* });
|
|
2467
|
+
* // program.pipe(Effect.provide(TestSnapshots))
|
|
2468
|
+
* ```
|
|
2469
|
+
*/
|
|
2470
|
+
static readonly layerTest: (overrides?: Partial<WorkspaceSnapshotsShape>) => Layer.Layer<WorkspaceSnapshots>;
|
|
1831
2471
|
}
|
|
1832
2472
|
//#endregion
|
|
1833
2473
|
//#region src/Workspaces.d.ts
|
|
@@ -1852,20 +2492,206 @@ interface WorkspacesOptions {
|
|
|
1852
2492
|
*
|
|
1853
2493
|
* @public
|
|
1854
2494
|
*/
|
|
1855
|
-
type WorkspacesServices = WorkspaceRoot | PackageManagerDetector | WorkspaceDiscovery | LockfileReader | WorkspaceCatalogs
|
|
2495
|
+
type WorkspacesServices = WorkspaceRoot | PackageManagerDetector | WorkspaceDiscovery | LockfileReader | WorkspaceCatalogs;
|
|
1856
2496
|
/**
|
|
1857
2497
|
* The composite layers.
|
|
1858
2498
|
*
|
|
1859
2499
|
* @public
|
|
1860
2500
|
*/
|
|
1861
|
-
declare
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
2501
|
+
declare class Workspaces {
|
|
2502
|
+
private constructor();
|
|
2503
|
+
/**
|
|
2504
|
+
* Every service that needs only a filesystem: root, package-manager
|
|
2505
|
+
* detection, discovery, lockfile reading, catalogs and publishability.
|
|
2506
|
+
*
|
|
2507
|
+
* @remarks
|
|
2508
|
+
* Requires core `FileSystem` and `Path`, which the consumer provides at the
|
|
2509
|
+
* edge (`@effect/platform-node`, `@effect/platform-bun`, or a test's
|
|
2510
|
+
* `FileSystem.layerNoop`).
|
|
2511
|
+
*
|
|
2512
|
+
* **Bind the result to a `const`.** This is a parameterized factory and
|
|
2513
|
+
* layers memoize by reference, so calling it twice builds everything twice.
|
|
2514
|
+
*
|
|
2515
|
+
* @example
|
|
2516
|
+
* ```ts
|
|
2517
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
2518
|
+
* import { Layer } from "effect";
|
|
2519
|
+
*
|
|
2520
|
+
* const WorkspacesLayer = Workspaces.layer();
|
|
2521
|
+
* const AppLayer = Layer.provide(WorkspacesLayer, PlatformLayer);
|
|
2522
|
+
* ```
|
|
2523
|
+
*/
|
|
2524
|
+
static readonly layer: (options?: WorkspacesOptions) => Layer.Layer<WorkspacesServices, never, FileSystem.FileSystem | Path.Path>;
|
|
2525
|
+
/**
|
|
2526
|
+
* The git-free composite, but with catalog assembly that **replays config
|
|
2527
|
+
* dependency `pnpmfile.cjs` hooks** —
|
|
2528
|
+
* {@link WorkspaceCatalogs.layerWithConfigDependencies} in place of the
|
|
2529
|
+
* default no-op catalogs layer.
|
|
2530
|
+
*
|
|
2531
|
+
* @remarks
|
|
2532
|
+
* Identical requirement set to {@link Workspaces.layer}; the only
|
|
2533
|
+
* difference is that config-dependency code is executed in process. Opt in
|
|
2534
|
+
* deliberately — the default {@link Workspaces.layer} never executes
|
|
2535
|
+
* config-dependency code.
|
|
2536
|
+
*
|
|
2537
|
+
* **Bind the result to a `const`.**
|
|
2538
|
+
*/
|
|
2539
|
+
static readonly layerWithConfigDependencies: (options?: WorkspacesOptions) => Layer.Layer<WorkspacesServices, never, FileSystem.FileSystem | Path.Path>;
|
|
2540
|
+
/**
|
|
2541
|
+
* The git-free composite plus {@link ChangeDetector} and
|
|
2542
|
+
* {@link WorkspaceSnapshots}, over `@effected/git`'s `Git` service.
|
|
2543
|
+
*
|
|
2544
|
+
* @remarks
|
|
2545
|
+
* The extra requirement is core's `ChildProcessSpawner` (behind `Git`),
|
|
2546
|
+
* which is why it is a separate layer rather than a flag: a consumer that
|
|
2547
|
+
* never detects changes or reads at a ref should not have to be able to
|
|
2548
|
+
* spawn a subprocess. The consumer provides `ChildProcessSpawner` once at
|
|
2549
|
+
* the edge (`@effect/platform-node`'s `NodeServices.layer`); a test
|
|
2550
|
+
* provides `Layer.succeed(Git, …)` and needs no repository on disk.
|
|
2551
|
+
*/
|
|
2552
|
+
static readonly layerWithGit: (options?: WorkspacesOptions) => Layer.Layer<WorkspacesServices | ChangeDetector | WorkspaceSnapshots | Git, never, FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner>;
|
|
2553
|
+
/**
|
|
2554
|
+
* This package's implementation of `@effected/commands`' `LocalExec`
|
|
2555
|
+
* contract: how to run a project-local binary here.
|
|
2556
|
+
*
|
|
2557
|
+
* @remarks
|
|
2558
|
+
* **An inverted contract, the `@effected/npm` `CatalogResolver`
|
|
2559
|
+
* precedent.** Tool discovery needs package-manager detection and
|
|
2560
|
+
* workspace-root resolution, both of which live here — but a direct edge
|
|
2561
|
+
* from `@effected/commands` to this package would make that boundary-tier
|
|
2562
|
+
* package integrated, and through the planned `npm` → `commands` edge
|
|
2563
|
+
* would drag `npm`, `lockfiles` (pure!) and `package-json` up a tier with
|
|
2564
|
+
* it. So `commands` declares the narrow contract and we ship the layer.
|
|
2565
|
+
*
|
|
2566
|
+
* **The argv knowledge is not duplicated.** `LocalExec.prefixes(name)` is
|
|
2567
|
+
* the one home of the four managers' `exec`/`dlx` prefixes; this layer
|
|
2568
|
+
* detects *which* manager owns the directory and asks `commands` what that
|
|
2569
|
+
* manager's argv looks like. Neither package reimplements the other's
|
|
2570
|
+
* half.
|
|
2571
|
+
*
|
|
2572
|
+
* **`None` is success.** Outside any workspace — and inside one whose
|
|
2573
|
+
* manager cannot be identified — the answer is `Option.none()`: "there is
|
|
2574
|
+
* no project-local way to run tools here" is an ordinary fact, not an
|
|
2575
|
+
* exceptional one, and a consumer running in a bare directory should not
|
|
2576
|
+
* have to catch an error to learn it. The contract's typed
|
|
2577
|
+
* `LocalExecError` is reserved for **mechanism** failure — a manifest that
|
|
2578
|
+
* exists but cannot be read or parsed, which means something is broken
|
|
2579
|
+
* rather than absent. That is npm's resolver convention, adopted
|
|
2580
|
+
* verbatim.
|
|
2581
|
+
*
|
|
2582
|
+
* `directory` is the resolved **workspace root**, not the caller's cwd: a
|
|
2583
|
+
* project-local launcher has to run where the workspace is.
|
|
2584
|
+
*
|
|
2585
|
+
* A consumer with no monorepo never needs this layer, and therefore never
|
|
2586
|
+
* installs this package — `LocalExec.layerNone` and `LocalExec.layerFor`
|
|
2587
|
+
* are one-liners in `@effected/commands`.
|
|
2588
|
+
*
|
|
2589
|
+
* **Bind the result to a `const`** — a parameterized layer factory mints a
|
|
2590
|
+
* fresh reference per call and layers memoize by reference.
|
|
2591
|
+
*
|
|
2592
|
+
* @example
|
|
2593
|
+
* ```ts
|
|
2594
|
+
* import { ToolDiscovery } from "@effected/commands";
|
|
2595
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
2596
|
+
* import { Layer } from "effect";
|
|
2597
|
+
*
|
|
2598
|
+
* const AppLayer = ToolDiscovery.layer.pipe(
|
|
2599
|
+
* Layer.provide(Workspaces.localExecLayer()),
|
|
2600
|
+
* Layer.provide(Workspaces.layer()),
|
|
2601
|
+
* Layer.provide(NodeServices.layer),
|
|
2602
|
+
* );
|
|
2603
|
+
* ```
|
|
2604
|
+
*/
|
|
2605
|
+
static readonly localExecLayer: (options?: {
|
|
2606
|
+
readonly cwd?: string;
|
|
2607
|
+
}) => Layer.Layer<LocalExec, never, PackageManagerDetector | WorkspaceRoot>;
|
|
2608
|
+
/**
|
|
2609
|
+
* Resolve every `catalog:` and `workspace:` specifier in one `Manifest`
|
|
2610
|
+
* against the real workspace, in one call — the 90% path. Decode stays at
|
|
2611
|
+
* the consumer's edge: build the `Manifest` with `Manifest.decode` (from
|
|
2612
|
+
* `@effected/npm`), hand it here, and get a new `Manifest` back with
|
|
2613
|
+
* concrete ranges; `toRecord()` returns to the wire shape.
|
|
2614
|
+
*
|
|
2615
|
+
* @remarks
|
|
2616
|
+
* Composes `manifest.resolve()` with a fresh {@link Workspaces.resolverLayer}
|
|
2617
|
+
* per call, so the workspace root is re-discovered from `options.cwd` (or
|
|
2618
|
+
* the current `process.cwd()`) on every invocation. Consumers processing
|
|
2619
|
+
* many manifests should check `manifest.needsResolution` first and skip
|
|
2620
|
+
* the call entirely when no dependency field carries a
|
|
2621
|
+
* `catalog:`/`workspace:` specifier — that predicate is pure and avoids
|
|
2622
|
+
* catalog assembly altogether.
|
|
2623
|
+
*
|
|
2624
|
+
* A specifier the workspace cannot answer fails typed as
|
|
2625
|
+
* `UnresolvedDependencyError`; assembly and mechanism failures surface as
|
|
2626
|
+
* `CatalogAssemblyError` / `DependencyResolutionError`.
|
|
2627
|
+
*
|
|
2628
|
+
* @example
|
|
2629
|
+
* ```ts
|
|
2630
|
+
* import { Manifest } from "@effected/npm";
|
|
2631
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
2632
|
+
* import { Effect } from "effect";
|
|
2633
|
+
*
|
|
2634
|
+
* const program = Effect.gen(function* () {
|
|
2635
|
+
* const manifest = yield* Manifest.decode({ dependencies: { effect: "catalog:" } });
|
|
2636
|
+
* const resolved = manifest.needsResolution ? yield* Workspaces.resolveManifest(manifest) : manifest;
|
|
2637
|
+
* return resolved.toRecord();
|
|
2638
|
+
* });
|
|
2639
|
+
* ```
|
|
2640
|
+
*/
|
|
2641
|
+
static readonly resolveManifest: (manifest: Manifest, options?: WorkspacesOptions) => Effect.Effect<Manifest, CatalogAssemblyError | DependencyResolutionError | UnresolvedDependencyError, FileSystem.FileSystem | Path.Path>;
|
|
2642
|
+
/**
|
|
2643
|
+
* The one-call resolver factory: {@link Workspaces.resolvers} pre-wired
|
|
2644
|
+
* over {@link Workspaces.layerWithConfigDependencies}, so the two
|
|
2645
|
+
* `@effected/npm` contracts (`CatalogResolver`, `WorkspaceResolver`) need
|
|
2646
|
+
* only a platform (`FileSystem` + `Path`) from the consumer.
|
|
2647
|
+
*
|
|
2648
|
+
* @remarks
|
|
2649
|
+
* This is deliberately a **parameterized layer function, and the fresh
|
|
2650
|
+
* layer per call is the feature**: layers memoize by reference, so each
|
|
2651
|
+
* call mints an unmemoized layer whose root discovery re-runs — including
|
|
2652
|
+
* a per-call `process.cwd()` read when `options.cwd` is omitted. A build
|
|
2653
|
+
* tool that changes directory between manifests gets a correct
|
|
2654
|
+
* re-discovery each time precisely because nothing is shared across
|
|
2655
|
+
* calls. When you *want* sharing, bind one call's result to a `const` and
|
|
2656
|
+
* provide that; the memoization rule is unchanged, this factory just
|
|
2657
|
+
* refuses to hide it.
|
|
2658
|
+
*
|
|
2659
|
+
* Catalog assembly replays config-dependency `pnpmfile` hooks (the
|
|
2660
|
+
* `layerWithConfigDependencies` path) — the semantics a real pnpm install
|
|
2661
|
+
* has. Compose {@link Workspaces.resolvers} with {@link Workspaces.layer}
|
|
2662
|
+
* yourself if config-dependency code must not run in process.
|
|
2663
|
+
*
|
|
2664
|
+
* @example
|
|
2665
|
+
* ```ts
|
|
2666
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
2667
|
+
* import { Effect } from "effect";
|
|
2668
|
+
*
|
|
2669
|
+
* const program = doSomethingWithResolvers.pipe(
|
|
2670
|
+
* Effect.provide(Workspaces.resolverLayer()),
|
|
2671
|
+
* );
|
|
2672
|
+
* ```
|
|
2673
|
+
*/
|
|
2674
|
+
static readonly resolverLayer: (options?: WorkspacesOptions) => Layer.Layer<CatalogResolver | WorkspaceResolver, never, FileSystem.FileSystem | Path.Path>;
|
|
2675
|
+
/**
|
|
2676
|
+
* The two `@effected/npm` resolver contracts, implemented for real.
|
|
2677
|
+
*
|
|
2678
|
+
* @remarks
|
|
2679
|
+
* Provide this alongside `@effected/package-json`'s `Package.resolve` and
|
|
2680
|
+
* a manifest's `catalog:` and `workspace:` specifiers resolve against the
|
|
2681
|
+
* actual workspace instead of the no-op layers' `Option.none()`.
|
|
2682
|
+
*
|
|
2683
|
+
* @example
|
|
2684
|
+
* ```ts
|
|
2685
|
+
* import { Package } from "@effected/package-json";
|
|
2686
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
2687
|
+
* import { Layer } from "effect";
|
|
2688
|
+
*
|
|
2689
|
+
* const WorkspacesLayer = Workspaces.layer();
|
|
2690
|
+
* const Resolvers = Workspaces.resolvers.pipe(Layer.provide(WorkspacesLayer));
|
|
2691
|
+
* ```
|
|
2692
|
+
*/
|
|
2693
|
+
static readonly resolvers: Layer.Layer<CatalogResolver | WorkspaceResolver, never, WorkspaceCatalogs | WorkspaceDiscovery>;
|
|
2694
|
+
}
|
|
1869
2695
|
//#endregion
|
|
1870
2696
|
//#region src/WorkspacesSync.d.ts
|
|
1871
2697
|
/**
|
|
@@ -2056,5 +2882,5 @@ interface GetWorkspacePackagesSyncOptions extends WorkspacesSyncOptions {
|
|
|
2056
2882
|
*/
|
|
2057
2883
|
declare const getWorkspacePackagesSync: (root: string, options: GetWorkspacePackagesSyncOptions) => ReadonlyArray<WorkspacePackage>;
|
|
2058
2884
|
//#endregion
|
|
2059
|
-
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 };
|
|
2885
|
+
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 };
|
|
2060
2886
|
//# sourceMappingURL=index.d.ts.map
|