@effected/workspaces 0.3.1 → 0.4.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm](https://img.shields.io/npm/v/@effected%2Fworkspaces?label=npm&color=cb3837)](https://www.npmjs.com/package/@effected/workspaces)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-4caf50.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Node.js %3E%3D24.11.0](https://img.shields.io/badge/Node.js-%3E%3D24.11.0-5fa04e.svg)](https://nodejs.org/)
6
- [![TypeScript 6.0](https://img.shields.io/badge/TypeScript-6.0-3178c6.svg)](https://www.typescriptlang.org/)
6
+ [![TypeScript 7.0](https://img.shields.io/badge/TypeScript-7.0-3178c6.svg)](https://www.typescriptlang.org/)
7
7
 
8
8
  Monorepo workspace tooling for [Effect](https://effect.website) v4: find the workspace root, enumerate its packages, walk the dependency graph, detect the package manager, resolve pnpm catalogs, read the lockfile and work out which packages a git range touches. Every capability is a service you provide at the edge and swap in tests. Works with npm, pnpm, yarn Berry and bun.
9
9
 
@@ -151,7 +151,7 @@ const options = {
151
151
  path, // node:path satisfies SyncPath verbatim
152
152
  };
153
153
 
154
- const root = findWorkspaceRootSync(options);
154
+ const root = findWorkspaceRootSync(process.cwd(), options);
155
155
  const packages = root === null ? [] : getWorkspacePackagesSync(root, options);
156
156
  // root: the workspace root path, or null when none is found above the cwd
157
157
  // packages: the discovered workspace packages, empty when there is no root
@@ -180,12 +180,38 @@ const program = Effect.gen(function* () {
180
180
 
181
181
  `WorkspaceRootNotFoundError`, `WorkspaceDiscoveryError`, `WorkspacePatternError`, `PackageNotFoundError`, `WorkspaceManifestError`, `PackageManagerDetectionError`, `CatalogAssemblyError`, `LockfileReadError`, `CyclicDependencyError` and `ChangeDetectionError` each name one thing that can actually go wrong, and each method's error channel is narrowed to the ones it can produce. `CatalogAssemblyError` is defined in `@effected/npm`, beside the resolver contract that names it in its channel — import it from there. Change detection additionally surfaces `@effected/git`'s typed git errors, such as `NotARepositoryError`.
182
182
 
183
+ ## Testing
184
+
185
+ Every service here can be replaced with `Layer.succeed` and a hand-built value, and `WorkspaceDiscovery` ships that pattern ready-made: `WorkspaceDiscovery.layerTest(overrides)` provides an in-memory double where a test stubs only the methods it exercises. The defaults model an empty workspace, and the derived methods run over the effective `listPackages`, so stubbing that one method keeps `getPackage`, `importerMap` and `resolveFile` answering consistently:
186
+
187
+ ```ts
188
+ import { WorkspaceDiscovery, WorkspacePackage } from "@effected/workspaces";
189
+ import { Effect } from "effect";
190
+
191
+ // Bind to a const — layers memoize by reference.
192
+ const TestDiscovery = WorkspaceDiscovery.layerTest({
193
+ listPackages: () =>
194
+ Effect.succeed([
195
+ WorkspacePackage.make({
196
+ name: "@my-org/utils",
197
+ version: "1.0.0",
198
+ path: "/repo/packages/utils",
199
+ packageJsonPath: "/repo/packages/utils/package.json",
200
+ relativePath: "packages/utils",
201
+ }),
202
+ ]),
203
+ });
204
+ // program.pipe(Effect.provide(TestDiscovery))
205
+ ```
206
+
207
+ A name miss in the derived `getPackage` fails with the service's own typed `PackageNotFoundError`, exactly as the live implementation does. Two deliberate edges: `info()` has no honest default (a fabricated root path would leak into consumer path logic), so it dies with an explanatory defect unless stubbed, and the derived file-ownership methods assume POSIX paths, so pass your own `resolveFile` for win32 fixtures. `WorkspaceDiscovery.makeTest(overrides)` returns the bare service shape when you want the double without a layer.
208
+
183
209
  ## Features
184
210
 
185
211
  - `Workspaces.layer` / `Workspaces.layerWithGit` / `Workspaces.resolvers` — the composite layers, split on requirements rather than feature flags: a filesystem, a filesystem plus a subprocess, and the two `@effected/npm` resolver contracts.
186
212
  - `Workspaces.resolverLayer` / `Workspaces.resolveManifest` — the one-call manifest-resolution path: a fresh, unmemoized layer per call so root discovery follows your cwd, and one-shot resolution of a whole `Manifest` against the real workspace.
187
213
  - `WorkspaceRoot` — root discovery from a `cwd`, over `WORKSPACE_MARKERS`.
188
- - `WorkspaceDiscovery` — package enumeration with a bounded descent for segment-crossing `packages/**` patterns, plus per-package lookup.
214
+ - `WorkspaceDiscovery` — package enumeration with a bounded descent for segment-crossing `packages/**` patterns, per-package lookup and the `makeTest` / `layerTest` in-memory test doubles.
189
215
  - `WorkspacePackage` — a deliberately tolerant manifest model, so one member with an odd version cannot fail discovery for the whole repo. `manifestRecord` keeps the as-read `package.json` for tolerant access to fields outside the typed slice without a second read; `WorkspacePackage.manifest(pkg)` re-reads and is the opt-in bridge to `@effected/package-json`'s strict `Package`.
190
216
  - `DependencyGraph` — a value class over discovered packages: `levels()` for parallel build tiers, the flattened topological order, and `CyclicDependencyError` when there isn't one.
191
217
  - `PackageManagerDetector` — npm, pnpm, yarn or bun from lockfiles and the `packageManager` field.
@@ -304,6 +304,109 @@ var WorkspaceDiscovery = class WorkspaceDiscovery extends Context.Service()("@ef
304
304
  */
305
305
  static layer = (options) => Layer.effect(WorkspaceDiscovery, WorkspaceDiscovery.make(options));
306
306
  /**
307
+ * An in-memory test double of the service shape, with every method
308
+ * defaulted so a test stubs only what it exercises.
309
+ *
310
+ * @remarks
311
+ * The defaults model an **empty workspace**, and the derived methods run
312
+ * over the *effective* `listPackages` — the override when one is supplied —
313
+ * so stubbing only `listPackages` yields a consistent double:
314
+ *
315
+ * - `listPackages` — succeeds with `[]`.
316
+ * - `importerMap` — derived: the packages keyed by `relativePath`.
317
+ * - `getPackage` — derived: a name lookup that fails with the service's own
318
+ * typed {@link PackageNotFoundError} on a miss, exactly as the live
319
+ * implementation does.
320
+ * - `resolveFile` / `resolveFiles` — derived: longest-prefix ownership over
321
+ * `pkg.path`, POSIX-terminated (`"/"`); supply a win32 double explicitly
322
+ * if your fixture paths are win32.
323
+ * - `refresh` — a no-op (`Effect.void`); there is nothing memoized to drop.
324
+ * - `info` — **dies** with an explanatory defect. No honest default exists
325
+ * (a fabricated root path would leak into consumer path logic), so an
326
+ * unstubbed `info()` call is a test-wiring mistake and fails loudly as a
327
+ * defect rather than succeeding with a lie or failing with a dishonest
328
+ * typed error.
329
+ *
330
+ * @example
331
+ * ```ts
332
+ * import { WorkspaceDiscovery, WorkspacePackage } from "@effected/workspaces";
333
+ * import { Effect } from "effect";
334
+ *
335
+ * const double = WorkspaceDiscovery.makeTest({
336
+ * listPackages: () =>
337
+ * Effect.succeed([
338
+ * WorkspacePackage.make({
339
+ * name: "@my-org/utils",
340
+ * version: "1.0.0",
341
+ * path: "/repo/packages/utils",
342
+ * packageJsonPath: "/repo/packages/utils/package.json",
343
+ * relativePath: "packages/utils",
344
+ * }),
345
+ * ]),
346
+ * });
347
+ * // `getPackage`, `importerMap`, `resolveFile(s)` now answer consistently.
348
+ * ```
349
+ */
350
+ static makeTest = (overrides = {}) => {
351
+ const listPackages = overrides.listPackages ?? (() => Effect.succeed([]));
352
+ const ownerOf = (filePath, all) => {
353
+ let best;
354
+ let bestLength = 0;
355
+ for (const pkg of all) {
356
+ const prefix = pkg.path.endsWith("/") ? pkg.path : `${pkg.path}/`;
357
+ if (filePath.startsWith(prefix) && prefix.length > bestLength) {
358
+ best = pkg;
359
+ bestLength = prefix.length;
360
+ }
361
+ }
362
+ return Option.fromUndefinedOr(best);
363
+ };
364
+ return {
365
+ listPackages,
366
+ info: () => Effect.die(/* @__PURE__ */ new Error("WorkspaceDiscovery.makeTest: info() was called but not stubbed — no honest default WorkspaceInfo exists for a test double; pass an `info` override.")),
367
+ importerMap: () => Effect.map(listPackages(), (all) => new Map(all.map((pkg) => [pkg.relativePath, pkg]))),
368
+ getPackage: (name) => Effect.flatMap(listPackages(), (all) => {
369
+ const found = all.find((pkg) => pkg.name === name);
370
+ return found !== void 0 ? Effect.succeed(found) : Effect.fail(new PackageNotFoundError({
371
+ name,
372
+ available: all.map((pkg) => pkg.name)
373
+ }));
374
+ }),
375
+ resolveFile: (filePath) => Effect.map(listPackages(), (all) => ownerOf(filePath, all)),
376
+ resolveFiles: (filePaths) => Effect.map(listPackages(), (all) => {
377
+ const seen = /* @__PURE__ */ new Map();
378
+ for (const filePath of filePaths) {
379
+ const owner = ownerOf(filePath, all);
380
+ if (Option.isSome(owner)) seen.set(owner.value.name, owner.value);
381
+ }
382
+ return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
383
+ }),
384
+ refresh: () => Effect.void,
385
+ ...overrides
386
+ };
387
+ };
388
+ /**
389
+ * The test layer: {@link WorkspaceDiscovery.makeTest} behind
390
+ * `Layer.succeed`, so a suite provides only the methods it exercises.
391
+ *
392
+ * @remarks
393
+ * A parameterized layer factory mints a **fresh reference per call**, and
394
+ * layers memoize by reference — bind the result to a `const` and reuse it
395
+ * rather than calling `layerTest(...)` at each composition site.
396
+ *
397
+ * @example
398
+ * ```ts
399
+ * import { WorkspaceDiscovery } from "@effected/workspaces";
400
+ * import { Effect } from "effect";
401
+ *
402
+ * const TestDiscovery = WorkspaceDiscovery.layerTest({
403
+ * listPackages: () => Effect.succeed([]),
404
+ * });
405
+ * // program.pipe(Effect.provide(TestDiscovery))
406
+ * ```
407
+ */
408
+ static layerTest = (overrides = {}) => Layer.succeed(WorkspaceDiscovery, WorkspaceDiscovery.makeTest(overrides));
409
+ /**
307
410
  * The real implementation of `@effected/npm`'s `WorkspaceResolver` contract
308
411
  * — the one `@effected/package-json` declares but cannot fill.
309
412
  *
package/WorkspacesSync.js CHANGED
@@ -38,20 +38,28 @@ const isDirectory = (fileSystem, dir) => {
38
38
  /** Whether `dir` holds a `package.json`. */
39
39
  const isPackage = (options, dir) => options.fileSystem.exists(options.path.join(dir, "package.json"));
40
40
  /**
41
- * The nearest workspace root at or above `options.cwd`, or `null`.
41
+ * The nearest workspace root at or above `cwd`, or `null`.
42
42
  *
43
43
  * @remarks
44
44
  * **Synchronous.** The Effect surface is `WorkspaceRoot`; reach for this one
45
45
  * only where you genuinely cannot run an Effect — a Vitest config being the
46
46
  * motivating case. The file and path operations are the caller's
47
- * ({@link FindWorkspaceRootSyncOptions}); this module imports no `node:*` and
47
+ * ({@link WorkspacesSyncOptions}); this module imports no `node:*` and
48
48
  * assumes no posix.
49
49
  *
50
+ * The signature is path-first, options second — the same shape as
51
+ * {@link getWorkspacePackagesSync} and the rest of the kit's sync facades
52
+ * (`TsconfigLoaderSync.load(configPath, options)`). `cwd` is required and
53
+ * positional: the earlier options-bag form defaulted it to an ambient
54
+ * `process.cwd()` read, which both broke the symmetry with its sibling and
55
+ * was the module's one platform assumption.
56
+ *
50
57
  * Markers match the async service exactly: a `pnpm-workspace.yaml`, or a
51
58
  * `package.json` carrying a `workspaces` field.
52
59
  *
53
- * @param options - The consumer-supplied file and path operations, plus the
54
- * optional `cwd` to start from (defaulting to `process.cwd()`).
60
+ * @param cwd - Where to start the ascent (typically `process.cwd()`),
61
+ * matching the Effect layers' `{ cwd }` option.
62
+ * @param options - The consumer-supplied file and path operations.
55
63
  *
56
64
  * @example
57
65
  * ```ts
@@ -59,7 +67,7 @@ const isPackage = (options, dir) => options.fileSystem.exists(options.path.join(
59
67
  * import * as path from "node:path";
60
68
  * import { findWorkspaceRootSync } from "@effected/workspaces";
61
69
  *
62
- * const root = findWorkspaceRootSync({
70
+ * const root = findWorkspaceRootSync(process.cwd(), {
63
71
  * fileSystem: {
64
72
  * exists: existsSync,
65
73
  * readFile: (p) => readFileSync(p, "utf8"),
@@ -72,9 +80,9 @@ const isPackage = (options, dir) => options.fileSystem.exists(options.path.join(
72
80
  *
73
81
  * @public
74
82
  */
75
- const findWorkspaceRootSync = (options) => {
83
+ const findWorkspaceRootSync = (cwd, options) => {
76
84
  const { fileSystem, path } = options;
77
- let current = path.resolve(options.cwd ?? process.cwd());
85
+ let current = path.resolve(cwd);
78
86
  for (let depth = 0; depth < 32 * 8; depth++) {
79
87
  if (fileSystem.exists(path.join(current, "pnpm-workspace.yaml"))) return current;
80
88
  const manifest = readJson(fileSystem, path.join(current, "package.json"));
@@ -173,7 +181,7 @@ const readPackageSync = (options, directory, relativePath) => {
173
181
  * },
174
182
  * path,
175
183
  * };
176
- * const root = findWorkspaceRootSync(ops);
184
+ * const root = findWorkspaceRootSync(process.cwd(), ops);
177
185
  * const packages = root === null ? [] : getWorkspacePackagesSync(root, ops);
178
186
  * ```
179
187
  *
package/index.d.ts CHANGED
@@ -440,6 +440,72 @@ declare class WorkspaceDiscovery extends WorkspaceDiscovery_base {
440
440
  * rather than calling `layer(...)` at each composition site.
441
441
  */
442
442
  static readonly layer: (options?: WorkspaceDiscoveryOptions) => Layer.Layer<WorkspaceDiscovery, never, WorkspaceRoot | FileSystem.FileSystem | Path.Path>;
443
+ /**
444
+ * An in-memory test double of the service shape, with every method
445
+ * defaulted so a test stubs only what it exercises.
446
+ *
447
+ * @remarks
448
+ * The defaults model an **empty workspace**, and the derived methods run
449
+ * over the *effective* `listPackages` — the override when one is supplied —
450
+ * so stubbing only `listPackages` yields a consistent double:
451
+ *
452
+ * - `listPackages` — succeeds with `[]`.
453
+ * - `importerMap` — derived: the packages keyed by `relativePath`.
454
+ * - `getPackage` — derived: a name lookup that fails with the service's own
455
+ * typed {@link PackageNotFoundError} on a miss, exactly as the live
456
+ * implementation does.
457
+ * - `resolveFile` / `resolveFiles` — derived: longest-prefix ownership over
458
+ * `pkg.path`, POSIX-terminated (`"/"`); supply a win32 double explicitly
459
+ * if your fixture paths are win32.
460
+ * - `refresh` — a no-op (`Effect.void`); there is nothing memoized to drop.
461
+ * - `info` — **dies** with an explanatory defect. No honest default exists
462
+ * (a fabricated root path would leak into consumer path logic), so an
463
+ * unstubbed `info()` call is a test-wiring mistake and fails loudly as a
464
+ * defect rather than succeeding with a lie or failing with a dishonest
465
+ * typed error.
466
+ *
467
+ * @example
468
+ * ```ts
469
+ * import { WorkspaceDiscovery, WorkspacePackage } from "@effected/workspaces";
470
+ * import { Effect } from "effect";
471
+ *
472
+ * const double = WorkspaceDiscovery.makeTest({
473
+ * listPackages: () =>
474
+ * Effect.succeed([
475
+ * WorkspacePackage.make({
476
+ * name: "@my-org/utils",
477
+ * version: "1.0.0",
478
+ * path: "/repo/packages/utils",
479
+ * packageJsonPath: "/repo/packages/utils/package.json",
480
+ * relativePath: "packages/utils",
481
+ * }),
482
+ * ]),
483
+ * });
484
+ * // `getPackage`, `importerMap`, `resolveFile(s)` now answer consistently.
485
+ * ```
486
+ */
487
+ static readonly makeTest: (overrides?: Partial<WorkspaceDiscoveryShape>) => WorkspaceDiscoveryShape;
488
+ /**
489
+ * The test layer: {@link WorkspaceDiscovery.makeTest} behind
490
+ * `Layer.succeed`, so a suite provides only the methods it exercises.
491
+ *
492
+ * @remarks
493
+ * A parameterized layer factory mints a **fresh reference per call**, and
494
+ * layers memoize by reference — bind the result to a `const` and reuse it
495
+ * rather than calling `layerTest(...)` at each composition site.
496
+ *
497
+ * @example
498
+ * ```ts
499
+ * import { WorkspaceDiscovery } from "@effected/workspaces";
500
+ * import { Effect } from "effect";
501
+ *
502
+ * const TestDiscovery = WorkspaceDiscovery.layerTest({
503
+ * listPackages: () => Effect.succeed([]),
504
+ * });
505
+ * // program.pipe(Effect.provide(TestDiscovery))
506
+ * ```
507
+ */
508
+ static readonly layerTest: (overrides?: Partial<WorkspaceDiscoveryShape>) => Layer.Layer<WorkspaceDiscovery>;
443
509
  /**
444
510
  * The real implementation of `@effected/npm`'s `WorkspaceResolver` contract
445
511
  * — the one `@effected/package-json` declares but cannot fill.
@@ -1608,34 +1674,28 @@ interface WorkspacesSyncOptions {
1608
1674
  readonly path: SyncPath;
1609
1675
  }
1610
1676
  /**
1611
- * Options for {@link findWorkspaceRootSync}: the required consumer-supplied
1612
- * operations plus where to start the ascent.
1613
- *
1614
- * @public
1615
- */
1616
- interface FindWorkspaceRootSyncOptions extends WorkspacesSyncOptions {
1617
- /**
1618
- * Where to start the ascent, matching the Effect layers' `{ cwd }` option.
1619
- *
1620
- * @defaultValue `process.cwd()`, read lazily per call.
1621
- */
1622
- readonly cwd?: string;
1623
- }
1624
- /**
1625
- * The nearest workspace root at or above `options.cwd`, or `null`.
1677
+ * The nearest workspace root at or above `cwd`, or `null`.
1626
1678
  *
1627
1679
  * @remarks
1628
1680
  * **Synchronous.** The Effect surface is `WorkspaceRoot`; reach for this one
1629
1681
  * only where you genuinely cannot run an Effect — a Vitest config being the
1630
1682
  * motivating case. The file and path operations are the caller's
1631
- * ({@link FindWorkspaceRootSyncOptions}); this module imports no `node:*` and
1683
+ * ({@link WorkspacesSyncOptions}); this module imports no `node:*` and
1632
1684
  * assumes no posix.
1633
1685
  *
1686
+ * The signature is path-first, options second — the same shape as
1687
+ * {@link getWorkspacePackagesSync} and the rest of the kit's sync facades
1688
+ * (`TsconfigLoaderSync.load(configPath, options)`). `cwd` is required and
1689
+ * positional: the earlier options-bag form defaulted it to an ambient
1690
+ * `process.cwd()` read, which both broke the symmetry with its sibling and
1691
+ * was the module's one platform assumption.
1692
+ *
1634
1693
  * Markers match the async service exactly: a `pnpm-workspace.yaml`, or a
1635
1694
  * `package.json` carrying a `workspaces` field.
1636
1695
  *
1637
- * @param options - The consumer-supplied file and path operations, plus the
1638
- * optional `cwd` to start from (defaulting to `process.cwd()`).
1696
+ * @param cwd - Where to start the ascent (typically `process.cwd()`),
1697
+ * matching the Effect layers' `{ cwd }` option.
1698
+ * @param options - The consumer-supplied file and path operations.
1639
1699
  *
1640
1700
  * @example
1641
1701
  * ```ts
@@ -1643,7 +1703,7 @@ interface FindWorkspaceRootSyncOptions extends WorkspacesSyncOptions {
1643
1703
  * import * as path from "node:path";
1644
1704
  * import { findWorkspaceRootSync } from "@effected/workspaces";
1645
1705
  *
1646
- * const root = findWorkspaceRootSync({
1706
+ * const root = findWorkspaceRootSync(process.cwd(), {
1647
1707
  * fileSystem: {
1648
1708
  * exists: existsSync,
1649
1709
  * readFile: (p) => readFileSync(p, "utf8"),
@@ -1656,7 +1716,7 @@ interface FindWorkspaceRootSyncOptions extends WorkspacesSyncOptions {
1656
1716
  *
1657
1717
  * @public
1658
1718
  */
1659
- declare const findWorkspaceRootSync: (options: FindWorkspaceRootSyncOptions) => string | null;
1719
+ declare const findWorkspaceRootSync: (cwd: string, options: WorkspacesSyncOptions) => string | null;
1660
1720
  /**
1661
1721
  * Options for {@link getWorkspacePackagesSync}: the required consumer-supplied
1662
1722
  * operations plus the traversal bound.
@@ -1714,7 +1774,7 @@ interface GetWorkspacePackagesSyncOptions extends WorkspacesSyncOptions {
1714
1774
  * },
1715
1775
  * path,
1716
1776
  * };
1717
- * const root = findWorkspaceRootSync(ops);
1777
+ * const root = findWorkspaceRootSync(process.cwd(), ops);
1718
1778
  * const packages = root === null ? [] : getWorkspacePackagesSync(root, ops);
1719
1779
  * ```
1720
1780
  *
@@ -1722,5 +1782,5 @@ interface GetWorkspacePackagesSyncOptions extends WorkspacesSyncOptions {
1722
1782
  */
1723
1783
  declare const getWorkspacePackagesSync: (root: string, options: GetWorkspacePackagesSyncOptions) => ReadonlyArray<WorkspacePackage>;
1724
1784
  //#endregion
1725
- export { type CatalogAssemblyFailure, CatalogSet, ChangeDetectionError, type ChangeDetectionFailure, ChangeDetectionOptions, ChangeDetector, type ChangeDetectorShape, ConfigDependencyHooks, type ConfigDependencyHooksShape, CyclicDependencyError, type DependencyDiff, DependencyGraph, DetectedPackageManager, type FindWorkspaceRootSyncOptions, type GetWorkspacePackagesSyncOptions, 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 WorkspaceSnapshotAtFailure, type WorkspaceSnapshotWorktreeFailure, WorkspaceSnapshots, type WorkspaceSnapshotsOptions, type WorkspaceSnapshotsShape, WorkspaceStateSnapshot, Workspaces, type WorkspacesOptions, type WorkspacesServices, type WorkspacesSyncOptions, findWorkspaceRootSync, getWorkspacePackagesSync };
1785
+ export { type CatalogAssemblyFailure, CatalogSet, ChangeDetectionError, type ChangeDetectionFailure, ChangeDetectionOptions, ChangeDetector, type ChangeDetectorShape, ConfigDependencyHooks, type ConfigDependencyHooksShape, CyclicDependencyError, type DependencyDiff, DependencyGraph, DetectedPackageManager, type GetWorkspacePackagesSyncOptions, 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 WorkspaceSnapshotAtFailure, type WorkspaceSnapshotWorktreeFailure, WorkspaceSnapshots, type WorkspaceSnapshotsOptions, type WorkspaceSnapshotsShape, WorkspaceStateSnapshot, Workspaces, type WorkspacesOptions, type WorkspacesServices, type WorkspacesSyncOptions, findWorkspaceRootSync, getWorkspacePackagesSync };
1726
1786
  //# sourceMappingURL=index.d.ts.map
package/node-sync.d.ts CHANGED
@@ -106,7 +106,9 @@ declare const nodePath: SyncPath;
106
106
  /**
107
107
  * The complete Node-bound options bag for `findWorkspaceRootSync` and
108
108
  * `getWorkspacePackagesSync` — {@link nodeFileSystem} plus {@link nodePath}.
109
- * Spread it to add the per-call extras: `{ ...nodeSyncOps, cwd }`.
109
+ * Both helpers take their path positionally, so this bag usually passes
110
+ * through verbatim; spread it only to add `getWorkspacePackagesSync`'s
111
+ * traversal extras: `{ ...nodeSyncOps, maxDepth }`.
110
112
  *
111
113
  * @public
112
114
  */
package/node-sync.js CHANGED
@@ -21,7 +21,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
21
21
  * import { findWorkspaceRootSync, getWorkspacePackagesSync } from "@effected/workspaces";
22
22
  * import { nodeSyncOps } from "@effected/workspaces/node-sync";
23
23
  *
24
- * const root = findWorkspaceRootSync(nodeSyncOps);
24
+ * const root = findWorkspaceRootSync(process.cwd(), nodeSyncOps);
25
25
  * const packages = root === null ? [] : getWorkspacePackagesSync(root, nodeSyncOps);
26
26
  * ```
27
27
  *
@@ -54,7 +54,9 @@ const nodePath = path;
54
54
  /**
55
55
  * The complete Node-bound options bag for `findWorkspaceRootSync` and
56
56
  * `getWorkspacePackagesSync` — {@link nodeFileSystem} plus {@link nodePath}.
57
- * Spread it to add the per-call extras: `{ ...nodeSyncOps, cwd }`.
57
+ * Both helpers take their path positionally, so this bag usually passes
58
+ * through verbatim; spread it only to add `getWorkspacePackagesSync`'s
59
+ * traversal extras: `{ ...nodeSyncOps, maxDepth }`.
58
60
  *
59
61
  * @public
60
62
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/workspaces",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
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": [
@@ -35,30 +35,32 @@
35
35
  "exports": {
36
36
  ".": {
37
37
  "types": "./index.d.ts",
38
- "import": "./index.js"
38
+ "import": "./index.js",
39
+ "default": "./index.js"
39
40
  },
40
41
  "./node-sync": {
41
42
  "types": "./node-sync.d.ts",
42
- "import": "./node-sync.js"
43
+ "import": "./node-sync.js",
44
+ "default": "./node-sync.js"
43
45
  },
44
46
  "./package.json": "./package.json"
45
47
  },
46
48
  "dependencies": {
47
- "@effected/git": "0.4.0",
48
- "@effected/glob": "0.1.1",
49
- "@effected/lockfiles": "0.1.3",
50
- "@effected/npm": "0.2.0",
51
- "@effected/package-json": "0.3.0",
52
- "@effected/semver": "0.1.0",
53
- "@effected/walker": "0.2.1",
54
- "@effected/yaml": "0.3.0",
49
+ "@effected/git": "0.4.1",
50
+ "@effected/glob": "0.1.2",
51
+ "@effected/lockfiles": "0.1.5",
52
+ "@effected/npm": "0.2.1",
53
+ "@effected/package-json": "0.3.1",
54
+ "@effected/semver": "0.1.1",
55
+ "@effected/walker": "0.2.2",
56
+ "@effected/yaml": "0.4.0",
55
57
  "@pnpm/catalogs.config": "^1100.0.0",
56
58
  "@pnpm/catalogs.protocol-parser": "^1100.0.0",
57
59
  "@pnpm/catalogs.resolver": "^1100.0.0",
58
60
  "@pnpm/catalogs.types": "^1100.0.0"
59
61
  },
60
62
  "peerDependencies": {
61
- "effect": "4.0.0-beta.98"
63
+ "effect": "4.0.0-beta.99"
62
64
  },
63
65
  "engines": {
64
66
  "node": ">=24.11.0"
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.58.10"
8
+ "packageVersion": "7.58.11"
9
9
  }
10
10
  ]
11
11
  }