@effected/workspaces 0.7.0 → 0.8.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/README.md +17 -2
- package/WorkspaceCatalogs.js +66 -0
- package/WorkspaceSnapshots.js +0 -0
- package/index.d.ts +170 -0
- package/package.json +6 -6
package/LockfileReader.js
CHANGED
|
@@ -27,6 +27,8 @@ var LockfileReadError = class extends Schema.TaggedErrorClass()("LockfileReadErr
|
|
|
27
27
|
return `Cannot read ${this.format} lockfile at ${this.lockfilePath}`;
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
|
+
/** A defect naming the unstubbed test-double method — a test-wiring mistake, not a typed failure. */
|
|
31
|
+
const unstubbed = (method) => Effect.die(/* @__PURE__ */ new Error(`LockfileReader.makeTest: ${method}() was called but not stubbed — pass a \`${method}\` override.`));
|
|
30
32
|
/**
|
|
31
33
|
* Reads and parses the workspace's lockfile.
|
|
32
34
|
*
|
|
@@ -112,6 +114,74 @@ var LockfileReader = class LockfileReader extends Context.Service()("@effected/w
|
|
|
112
114
|
* `const` and reuse it.
|
|
113
115
|
*/
|
|
114
116
|
static layer = (options) => Layer.effect(LockfileReader, LockfileReader.make(options));
|
|
117
|
+
/**
|
|
118
|
+
* A test double satisfying the full {@link LockfileReaderShape} with no
|
|
119
|
+
* filesystem, root walk, or package-manager detection.
|
|
120
|
+
*
|
|
121
|
+
* @remarks
|
|
122
|
+
* There is **no honest default lockfile**: an empty one that looks like a
|
|
123
|
+
* legitimate answer is indistinguishable from "this workspace resolves
|
|
124
|
+
* nothing" — the silent-empty failure class this package documents on the
|
|
125
|
+
* live paths — so `read` **dies** with an instructive defect until stubbed.
|
|
126
|
+
*
|
|
127
|
+
* The one derivation mirrors `WorkspaceDiscovery.makeTest`'s
|
|
128
|
+
* derived-from-the-primary rule: when a `read` override is supplied,
|
|
129
|
+
* `resolvedVersion` answers as the live service does — the **first** entry
|
|
130
|
+
* of `lockfile.packagesNamed(name)` in lockfile order, `Option.none()` on a
|
|
131
|
+
* miss — so the two stay consistent by construction. `integrity` is **not**
|
|
132
|
+
* derivable: the live method compares the lockfile against the workspace
|
|
133
|
+
* manifests discovery enumerates, and the double has no discovery to ask, so
|
|
134
|
+
* it dies unless stubbed.
|
|
135
|
+
*
|
|
136
|
+
* `refresh` defaults to `Effect.void` honestly: the live contract is "drop
|
|
137
|
+
* the memoized read so the next call re-reads", and this double memoizes
|
|
138
|
+
* nothing — every `read()` call re-invokes the override — so there is
|
|
139
|
+
* nothing to drop and the no-op is truthful, the same reasoning as
|
|
140
|
+
* `WorkspaceDiscovery.makeTest`'s `refresh`.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* import { Lockfile } from "@effected/lockfiles";
|
|
145
|
+
* import { LockfileReader } from "@effected/workspaces";
|
|
146
|
+
* import { Effect } from "effect";
|
|
147
|
+
*
|
|
148
|
+
* const double = LockfileReader.makeTest({
|
|
149
|
+
* read: () =>
|
|
150
|
+
* Effect.succeed(
|
|
151
|
+
* Lockfile.make({ format: "pnpm", lockfileVersion: "9.0", packages: [], workspaceDependencies: [] }),
|
|
152
|
+
* ),
|
|
153
|
+
* });
|
|
154
|
+
* // `resolvedVersion` now answers consistently from that lockfile.
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
static makeTest = (overrides = {}) => {
|
|
158
|
+
const read = overrides.read;
|
|
159
|
+
return {
|
|
160
|
+
read: () => unstubbed("read"),
|
|
161
|
+
resolvedVersion: read !== void 0 ? (packageName) => Effect.map(read(), (lockfile) => Option.fromUndefinedOr(lockfile.packagesNamed(packageName)[0])) : () => unstubbed("resolvedVersion"),
|
|
162
|
+
integrity: () => unstubbed("integrity"),
|
|
163
|
+
refresh: () => Effect.void,
|
|
164
|
+
...overrides
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* The test layer: {@link LockfileReader.makeTest} behind `Layer.succeed`, so
|
|
169
|
+
* a suite provides only the methods it exercises.
|
|
170
|
+
*
|
|
171
|
+
* @remarks
|
|
172
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
173
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
174
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* import { LockfileReader } from "@effected/workspaces";
|
|
179
|
+
*
|
|
180
|
+
* const TestLockfiles = LockfileReader.layerTest();
|
|
181
|
+
* // program.pipe(Effect.provide(TestLockfiles)) — dies loudly if touched.
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
static layerTest = (overrides = {}) => Layer.succeed(LockfileReader, LockfileReader.makeTest(overrides));
|
|
115
185
|
};
|
|
116
186
|
|
|
117
187
|
//#endregion
|
package/README.md
CHANGED
|
@@ -134,7 +134,21 @@ A specifier the workspace cannot answer fails typed as `UnresolvedDependencyErro
|
|
|
134
134
|
|
|
135
135
|
## The synchronous escape hatch
|
|
136
136
|
|
|
137
|
-
Vitest's config-time project discovery cannot await. Two functions exist for exactly that case, and they run synchronously over file and path operations you supply
|
|
137
|
+
Vitest's config-time project discovery cannot await. Two functions exist for exactly that case, and they run synchronously over file and path operations you supply. On Node you do not have to write them: the `@effected/workspaces/node-sync` subpath exports `nodeSyncOps`, the ready-made `node:fs` and `node:path` bindings, so adopting the sync path is one extra import.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { findWorkspaceRootSync, getWorkspacePackagesSync } from "@effected/workspaces";
|
|
141
|
+
import { nodeSyncOps } from "@effected/workspaces/node-sync";
|
|
142
|
+
|
|
143
|
+
const root = findWorkspaceRootSync(process.cwd(), nodeSyncOps);
|
|
144
|
+
const packages = root === null ? [] : getWorkspacePackagesSync(root, nodeSyncOps);
|
|
145
|
+
// root: the workspace root path, or null when none is found above the cwd
|
|
146
|
+
// packages: the discovered workspace packages, empty when there is no root
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Both entry points take their path positionally, so the bag usually passes through verbatim; spread it to add `getWorkspacePackagesSync`'s traversal extras — `{ ...nodeSyncOps, maxDepth }`. The bindings are a separate subpath deliberately: the main entry imports nothing platform-shaped, and re-exporting them from it would drag `node:*` into every consumer, including the ones supplying their own operations. `nodePath` is the running platform's `node:path`, so on Windows the paths handed back are win32 paths.
|
|
150
|
+
|
|
151
|
+
Write the operations yourself when Node's built-ins are not the platform you mean — a Bun or Deno binding, a test fake, or `node:path/win32` to pin a dialect rather than follow the running platform. Each one is a one-liner:
|
|
138
152
|
|
|
139
153
|
```ts
|
|
140
154
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
@@ -157,7 +171,7 @@ const packages = root === null ? [] : getWorkspacePackagesSync(root, options);
|
|
|
157
171
|
// packages: the discovered workspace packages, empty when there is no root
|
|
158
172
|
```
|
|
159
173
|
|
|
160
|
-
Windows correctness is the operations you pass
|
|
174
|
+
Windows correctness is therefore the operations you pass, and nothing else. Both entry points drive one traversal state machine (the same dequeue order, depth rule, visit budget and `node_modules` prune), so the sync and Effect surfaces can never disagree about what a pattern means. The one deliberate difference is at a bound: the Effect enumerator fails typed, the sync one truncates. Prefer the Effect API everywhere you can run one.
|
|
161
175
|
|
|
162
176
|
## Error handling
|
|
163
177
|
|
|
@@ -220,6 +234,7 @@ A name miss in the derived `getPackage` fails with the service's own typed `Pack
|
|
|
220
234
|
- `ChangeDetector` — git-range change detection over `@effected/git`'s `Git` service; swap the layer to mock it with no repository.
|
|
221
235
|
- `PublishabilityDetector` — whether a package publishes and to where, as a `PublishTarget` (registry, directory, access, provenance). The default layer implements npm's semantics; swap the layer if yours differ.
|
|
222
236
|
- `findWorkspaceRootSync` / `getWorkspacePackagesSync` — the synchronous escape hatch for config-time callers that cannot await, over file and path operations you supply.
|
|
237
|
+
- `@effected/workspaces/node-sync` — a second entry point holding the Node bindings for those operations (`nodeFileSystem`, `nodePath` and the `nodeSyncOps` bag), kept off the main entry so `node:*` never reaches a consumer that supplies its own.
|
|
223
238
|
|
|
224
239
|
## License
|
|
225
240
|
|
package/WorkspaceCatalogs.js
CHANGED
|
@@ -283,6 +283,8 @@ const validatePnpmWorkspaceCatalogs = (document) => {
|
|
|
283
283
|
catalogs: "catalogs"
|
|
284
284
|
}));
|
|
285
285
|
};
|
|
286
|
+
/** A defect naming the unstubbed test-double method — a test-wiring mistake, not a typed failure. */
|
|
287
|
+
const unstubbed = (method) => Effect.die(/* @__PURE__ */ new Error(`WorkspaceCatalogs.makeTest: ${method}() was called but not stubbed — pass a \`${method}\` override.`));
|
|
286
288
|
/**
|
|
287
289
|
* Assembles a workspace's catalogs, package-manager-aware.
|
|
288
290
|
*
|
|
@@ -426,6 +428,70 @@ var WorkspaceCatalogs = class WorkspaceCatalogs extends Context.Service()("@effe
|
|
|
426
428
|
*/
|
|
427
429
|
static layerWithConfigDependencies = (options) => Layer.effect(WorkspaceCatalogs, WorkspaceCatalogs.make(options)).pipe(Layer.provide(ConfigDependencyHooks.layerLive));
|
|
428
430
|
/**
|
|
431
|
+
* A test double satisfying the full {@link WorkspaceCatalogsShape} with no
|
|
432
|
+
* filesystem, lockfile read, or hook replay.
|
|
433
|
+
*
|
|
434
|
+
* @remarks
|
|
435
|
+
* There is **no honest default catalog set**: an empty `CatalogSet` that
|
|
436
|
+
* looks like a legitimate answer is the "every dependency looks newly added"
|
|
437
|
+
* failure class the live assembler hard-fails to prevent, so every method
|
|
438
|
+
* **dies** with an instructive defect until stubbed — a test-wiring mistake
|
|
439
|
+
* fails loudly as a defect rather than succeeding with a lie or failing with
|
|
440
|
+
* a dishonest typed error.
|
|
441
|
+
*
|
|
442
|
+
* The one derivation mirrors `WorkspaceDiscovery.makeTest`'s
|
|
443
|
+
* derived-from-the-primary rule: when a `set` override is supplied,
|
|
444
|
+
* `resolveSpecifier` answers from that `CatalogSet`'s own
|
|
445
|
+
* {@link CatalogSet.resolveSpecifier} — exactly what the live service runs
|
|
446
|
+
* over its assembled set — so the two stay consistent by construction.
|
|
447
|
+
* `releaseAgeGate` and `importerVersions` are **not** derivable from a
|
|
448
|
+
* catalog set (the gate comes from release-age keys and hook contributions,
|
|
449
|
+
* the importer index from the lockfile's importer blocks — neither is in a
|
|
450
|
+
* `CatalogSet`) and always die unless stubbed.
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* ```ts
|
|
454
|
+
* import { CatalogSet, WorkspaceCatalogs } from "@effected/workspaces";
|
|
455
|
+
* import { Effect } from "effect";
|
|
456
|
+
*
|
|
457
|
+
* const double = WorkspaceCatalogs.makeTest({
|
|
458
|
+
* set: () => Effect.succeed(CatalogSet.fromCatalogs({ default: { effect: "4.0.0" } })),
|
|
459
|
+
* });
|
|
460
|
+
* // `resolveSpecifier` now answers consistently from that set.
|
|
461
|
+
* ```
|
|
462
|
+
*/
|
|
463
|
+
static makeTest = (overrides = {}) => {
|
|
464
|
+
const set = overrides.set;
|
|
465
|
+
return {
|
|
466
|
+
set: () => unstubbed("set"),
|
|
467
|
+
resolveSpecifier: set !== void 0 ? (dependency, specifier) => Effect.map(set(), (catalogs) => catalogs.resolveSpecifier(dependency, specifier)) : () => unstubbed("resolveSpecifier"),
|
|
468
|
+
releaseAgeGate: () => unstubbed("releaseAgeGate"),
|
|
469
|
+
importerVersions: () => unstubbed("importerVersions"),
|
|
470
|
+
...overrides
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
/**
|
|
474
|
+
* The test layer: {@link WorkspaceCatalogs.makeTest} behind `Layer.succeed`,
|
|
475
|
+
* so a suite provides only the methods it exercises.
|
|
476
|
+
*
|
|
477
|
+
* @remarks
|
|
478
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
479
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
480
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* ```ts
|
|
484
|
+
* import { CatalogSet, WorkspaceCatalogs } from "@effected/workspaces";
|
|
485
|
+
* import { Effect } from "effect";
|
|
486
|
+
*
|
|
487
|
+
* const TestCatalogs = WorkspaceCatalogs.layerTest({
|
|
488
|
+
* set: () => Effect.succeed(CatalogSet.empty()),
|
|
489
|
+
* });
|
|
490
|
+
* // program.pipe(Effect.provide(TestCatalogs))
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
static layerTest = (overrides = {}) => Layer.succeed(WorkspaceCatalogs, WorkspaceCatalogs.makeTest(overrides));
|
|
494
|
+
/**
|
|
429
495
|
* The real implementation of `@effected/npm`'s `CatalogResolver` contract —
|
|
430
496
|
* the one `@effected/package-json` declares but cannot fill.
|
|
431
497
|
*
|
package/WorkspaceSnapshots.js
CHANGED
|
Binary file
|
package/index.d.ts
CHANGED
|
@@ -1212,6 +1212,65 @@ declare class LockfileReader extends LockfileReader_base {
|
|
|
1212
1212
|
* `const` and reuse it.
|
|
1213
1213
|
*/
|
|
1214
1214
|
static readonly layer: (options?: LockfileReaderOptions) => Layer.Layer<LockfileReader, never, WorkspaceRoot | PackageManagerDetector | WorkspaceDiscovery | FileSystem.FileSystem | Path.Path>;
|
|
1215
|
+
/**
|
|
1216
|
+
* A test double satisfying the full {@link LockfileReaderShape} with no
|
|
1217
|
+
* filesystem, root walk, or package-manager detection.
|
|
1218
|
+
*
|
|
1219
|
+
* @remarks
|
|
1220
|
+
* There is **no honest default lockfile**: an empty one that looks like a
|
|
1221
|
+
* legitimate answer is indistinguishable from "this workspace resolves
|
|
1222
|
+
* nothing" — the silent-empty failure class this package documents on the
|
|
1223
|
+
* live paths — so `read` **dies** with an instructive defect until stubbed.
|
|
1224
|
+
*
|
|
1225
|
+
* The one derivation mirrors `WorkspaceDiscovery.makeTest`'s
|
|
1226
|
+
* derived-from-the-primary rule: when a `read` override is supplied,
|
|
1227
|
+
* `resolvedVersion` answers as the live service does — the **first** entry
|
|
1228
|
+
* of `lockfile.packagesNamed(name)` in lockfile order, `Option.none()` on a
|
|
1229
|
+
* miss — so the two stay consistent by construction. `integrity` is **not**
|
|
1230
|
+
* derivable: the live method compares the lockfile against the workspace
|
|
1231
|
+
* manifests discovery enumerates, and the double has no discovery to ask, so
|
|
1232
|
+
* it dies unless stubbed.
|
|
1233
|
+
*
|
|
1234
|
+
* `refresh` defaults to `Effect.void` honestly: the live contract is "drop
|
|
1235
|
+
* the memoized read so the next call re-reads", and this double memoizes
|
|
1236
|
+
* nothing — every `read()` call re-invokes the override — so there is
|
|
1237
|
+
* nothing to drop and the no-op is truthful, the same reasoning as
|
|
1238
|
+
* `WorkspaceDiscovery.makeTest`'s `refresh`.
|
|
1239
|
+
*
|
|
1240
|
+
* @example
|
|
1241
|
+
* ```ts
|
|
1242
|
+
* import { Lockfile } from "@effected/lockfiles";
|
|
1243
|
+
* import { LockfileReader } from "@effected/workspaces";
|
|
1244
|
+
* import { Effect } from "effect";
|
|
1245
|
+
*
|
|
1246
|
+
* const double = LockfileReader.makeTest({
|
|
1247
|
+
* read: () =>
|
|
1248
|
+
* Effect.succeed(
|
|
1249
|
+
* Lockfile.make({ format: "pnpm", lockfileVersion: "9.0", packages: [], workspaceDependencies: [] }),
|
|
1250
|
+
* ),
|
|
1251
|
+
* });
|
|
1252
|
+
* // `resolvedVersion` now answers consistently from that lockfile.
|
|
1253
|
+
* ```
|
|
1254
|
+
*/
|
|
1255
|
+
static readonly makeTest: (overrides?: Partial<LockfileReaderShape>) => LockfileReaderShape;
|
|
1256
|
+
/**
|
|
1257
|
+
* The test layer: {@link LockfileReader.makeTest} behind `Layer.succeed`, so
|
|
1258
|
+
* a suite provides only the methods it exercises.
|
|
1259
|
+
*
|
|
1260
|
+
* @remarks
|
|
1261
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
1262
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
1263
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
1264
|
+
*
|
|
1265
|
+
* @example
|
|
1266
|
+
* ```ts
|
|
1267
|
+
* import { LockfileReader } from "@effected/workspaces";
|
|
1268
|
+
*
|
|
1269
|
+
* const TestLockfiles = LockfileReader.layerTest();
|
|
1270
|
+
* // program.pipe(Effect.provide(TestLockfiles)) — dies loudly if touched.
|
|
1271
|
+
* ```
|
|
1272
|
+
*/
|
|
1273
|
+
static readonly layerTest: (overrides?: Partial<LockfileReaderShape>) => Layer.Layer<LockfileReader>;
|
|
1215
1274
|
}
|
|
1216
1275
|
//#endregion
|
|
1217
1276
|
//#region src/Publishability.d.ts
|
|
@@ -1553,6 +1612,61 @@ declare class WorkspaceCatalogs extends WorkspaceCatalogs_base {
|
|
|
1553
1612
|
* Parameterized, so bind it to a `const` and reuse it.
|
|
1554
1613
|
*/
|
|
1555
1614
|
static readonly layerWithConfigDependencies: (options?: WorkspaceCatalogsOptions) => Layer.Layer<WorkspaceCatalogs, never, WorkspaceRoot | LockfileReader | FileSystem.FileSystem | Path.Path>;
|
|
1615
|
+
/**
|
|
1616
|
+
* A test double satisfying the full {@link WorkspaceCatalogsShape} with no
|
|
1617
|
+
* filesystem, lockfile read, or hook replay.
|
|
1618
|
+
*
|
|
1619
|
+
* @remarks
|
|
1620
|
+
* There is **no honest default catalog set**: an empty `CatalogSet` that
|
|
1621
|
+
* looks like a legitimate answer is the "every dependency looks newly added"
|
|
1622
|
+
* failure class the live assembler hard-fails to prevent, so every method
|
|
1623
|
+
* **dies** with an instructive defect until stubbed — a test-wiring mistake
|
|
1624
|
+
* fails loudly as a defect rather than succeeding with a lie or failing with
|
|
1625
|
+
* a dishonest typed error.
|
|
1626
|
+
*
|
|
1627
|
+
* The one derivation mirrors `WorkspaceDiscovery.makeTest`'s
|
|
1628
|
+
* derived-from-the-primary rule: when a `set` override is supplied,
|
|
1629
|
+
* `resolveSpecifier` answers from that `CatalogSet`'s own
|
|
1630
|
+
* {@link CatalogSet.resolveSpecifier} — exactly what the live service runs
|
|
1631
|
+
* over its assembled set — so the two stay consistent by construction.
|
|
1632
|
+
* `releaseAgeGate` and `importerVersions` are **not** derivable from a
|
|
1633
|
+
* catalog set (the gate comes from release-age keys and hook contributions,
|
|
1634
|
+
* the importer index from the lockfile's importer blocks — neither is in a
|
|
1635
|
+
* `CatalogSet`) and always die unless stubbed.
|
|
1636
|
+
*
|
|
1637
|
+
* @example
|
|
1638
|
+
* ```ts
|
|
1639
|
+
* import { CatalogSet, WorkspaceCatalogs } from "@effected/workspaces";
|
|
1640
|
+
* import { Effect } from "effect";
|
|
1641
|
+
*
|
|
1642
|
+
* const double = WorkspaceCatalogs.makeTest({
|
|
1643
|
+
* set: () => Effect.succeed(CatalogSet.fromCatalogs({ default: { effect: "4.0.0" } })),
|
|
1644
|
+
* });
|
|
1645
|
+
* // `resolveSpecifier` now answers consistently from that set.
|
|
1646
|
+
* ```
|
|
1647
|
+
*/
|
|
1648
|
+
static readonly makeTest: (overrides?: Partial<WorkspaceCatalogsShape>) => WorkspaceCatalogsShape;
|
|
1649
|
+
/**
|
|
1650
|
+
* The test layer: {@link WorkspaceCatalogs.makeTest} behind `Layer.succeed`,
|
|
1651
|
+
* so a suite provides only the methods it exercises.
|
|
1652
|
+
*
|
|
1653
|
+
* @remarks
|
|
1654
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
1655
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
1656
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
1657
|
+
*
|
|
1658
|
+
* @example
|
|
1659
|
+
* ```ts
|
|
1660
|
+
* import { CatalogSet, WorkspaceCatalogs } from "@effected/workspaces";
|
|
1661
|
+
* import { Effect } from "effect";
|
|
1662
|
+
*
|
|
1663
|
+
* const TestCatalogs = WorkspaceCatalogs.layerTest({
|
|
1664
|
+
* set: () => Effect.succeed(CatalogSet.empty()),
|
|
1665
|
+
* });
|
|
1666
|
+
* // program.pipe(Effect.provide(TestCatalogs))
|
|
1667
|
+
* ```
|
|
1668
|
+
*/
|
|
1669
|
+
static readonly layerTest: (overrides?: Partial<WorkspaceCatalogsShape>) => Layer.Layer<WorkspaceCatalogs>;
|
|
1556
1670
|
/**
|
|
1557
1671
|
* The real implementation of `@effected/npm`'s `CatalogResolver` contract —
|
|
1558
1672
|
* the one `@effected/package-json` declares but cannot fill.
|
|
@@ -1828,6 +1942,62 @@ declare class WorkspaceSnapshots extends WorkspaceSnapshots_base {
|
|
|
1828
1942
|
* `const` and reuse it, or layer memoization does not apply.
|
|
1829
1943
|
*/
|
|
1830
1944
|
static readonly layer: (options?: WorkspaceSnapshotsOptions) => Layer.Layer<WorkspaceSnapshots, never, Git | WorkspaceRoot | WorkspaceDiscovery | WorkspaceCatalogs>;
|
|
1945
|
+
/**
|
|
1946
|
+
* A test double satisfying the full {@link WorkspaceSnapshotsShape} with no
|
|
1947
|
+
* git, filesystem, discovery, or catalog assembly.
|
|
1948
|
+
*
|
|
1949
|
+
* @remarks
|
|
1950
|
+
* There are **no honest defaults and no derivations** here: `at(ref)` and
|
|
1951
|
+
* `worktree()` are two independent reads of two different sources (a git ref
|
|
1952
|
+
* vs. the live tree), so neither can be honestly derived from the other, and
|
|
1953
|
+
* a fabricated empty {@link WorkspaceStateSnapshot} on either side of a
|
|
1954
|
+
* before/after diff reads as every dependency newly added or removed — the
|
|
1955
|
+
* exact silent-empty failure class this package documents on the live paths.
|
|
1956
|
+
* Both methods therefore **die** with an instructive defect until stubbed; a
|
|
1957
|
+
* test-wiring mistake fails loudly as a defect rather than succeeding with a
|
|
1958
|
+
* lie.
|
|
1959
|
+
*
|
|
1960
|
+
* @example
|
|
1961
|
+
* ```ts
|
|
1962
|
+
* import { CatalogSet, WorkspaceSnapshots, WorkspaceStateSnapshot } from "@effected/workspaces";
|
|
1963
|
+
* import { Effect } from "effect";
|
|
1964
|
+
*
|
|
1965
|
+
* const empty = WorkspaceStateSnapshot.make({
|
|
1966
|
+
* packages: [],
|
|
1967
|
+
* catalogs: CatalogSet.empty(),
|
|
1968
|
+
* importerVersions: {},
|
|
1969
|
+
* });
|
|
1970
|
+
* const double = WorkspaceSnapshots.makeTest({
|
|
1971
|
+
* at: () => Effect.succeed(empty),
|
|
1972
|
+
* worktree: () => Effect.succeed(empty),
|
|
1973
|
+
* });
|
|
1974
|
+
* ```
|
|
1975
|
+
*/
|
|
1976
|
+
static readonly makeTest: (overrides?: Partial<WorkspaceSnapshotsShape>) => WorkspaceSnapshotsShape;
|
|
1977
|
+
/**
|
|
1978
|
+
* The test layer: {@link WorkspaceSnapshots.makeTest} behind `Layer.succeed`,
|
|
1979
|
+
* so a suite provides only the methods it exercises.
|
|
1980
|
+
*
|
|
1981
|
+
* @remarks
|
|
1982
|
+
* A parameterized layer factory mints a **fresh reference per call**, and
|
|
1983
|
+
* layers memoize by reference — bind the result to a `const` and reuse it
|
|
1984
|
+
* rather than calling `layerTest(...)` at each composition site.
|
|
1985
|
+
*
|
|
1986
|
+
* @example
|
|
1987
|
+
* ```ts
|
|
1988
|
+
* import { CatalogSet, WorkspaceSnapshots, WorkspaceStateSnapshot } from "@effected/workspaces";
|
|
1989
|
+
* import { Effect } from "effect";
|
|
1990
|
+
*
|
|
1991
|
+
* const TestSnapshots = WorkspaceSnapshots.layerTest({
|
|
1992
|
+
* worktree: () =>
|
|
1993
|
+
* Effect.succeed(
|
|
1994
|
+
* WorkspaceStateSnapshot.make({ packages: [], catalogs: CatalogSet.empty(), importerVersions: {} }),
|
|
1995
|
+
* ),
|
|
1996
|
+
* });
|
|
1997
|
+
* // program.pipe(Effect.provide(TestSnapshots))
|
|
1998
|
+
* ```
|
|
1999
|
+
*/
|
|
2000
|
+
static readonly layerTest: (overrides?: Partial<WorkspaceSnapshotsShape>) => Layer.Layer<WorkspaceSnapshots>;
|
|
1831
2001
|
}
|
|
1832
2002
|
//#endregion
|
|
1833
2003
|
//#region src/Workspaces.d.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/workspaces",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Monorepo workspace tooling as Effect services — root discovery, package enumeration, the dependency graph, package-manager detection, pnpm catalog resolution, lockfile IO and git-based change detection.",
|
|
6
6
|
"keywords": [
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
"./package.json": "./package.json"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@effected/git": "~0.
|
|
49
|
+
"@effected/git": "~0.5.0",
|
|
50
50
|
"@effected/glob": "~0.2.1",
|
|
51
|
-
"@effected/lockfiles": "~0.
|
|
52
|
-
"@effected/npm": "~0.
|
|
53
|
-
"@effected/package-json": "~0.5.
|
|
51
|
+
"@effected/lockfiles": "~0.2.0",
|
|
52
|
+
"@effected/npm": "~0.4.0",
|
|
53
|
+
"@effected/package-json": "~0.5.2",
|
|
54
54
|
"@effected/semver": "~0.2.1",
|
|
55
55
|
"@effected/walker": "~0.3.2",
|
|
56
|
-
"@effected/yaml": "~0.
|
|
56
|
+
"@effected/yaml": "~0.6.0",
|
|
57
57
|
"@pnpm/catalogs.config": "^1100.0.0",
|
|
58
58
|
"@pnpm/catalogs.protocol-parser": "^1100.0.0",
|
|
59
59
|
"@pnpm/catalogs.resolver": "^1100.0.0",
|