@effected/workspaces 0.28.0-next-20260927071821 → 0.29.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/ConfigDependencyHooks.js +13 -1
- package/LockfileReader.js +7 -2
- package/PeerCheck.js +449 -36
- package/README.md +20 -5
- package/WorkspaceCatalogs.js +2 -1
- package/WorkspaceDiscovery.js +4 -3
- package/WorkspaceSnapshots.js +5 -4
- package/WorkspaceStateSnapshot.js +47 -30
- package/Workspaces.js +6 -2
- package/WorkspacesSync.js +11 -1
- package/index.d.ts +972 -721
- package/internal/configDependencyFetch.js +39 -6
- package/internal/configDependencyResolution.js +1 -0
- package/internal/layerRoot.js +14 -0
- package/package.json +4 -4
- package/testing.d.ts +5 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { carries, manifestVersion, messageOf, sideLabel } from "./configDependencyShared.js";
|
|
2
2
|
import { splitConfigDependencySpec } from "./configDependencySpecGrammar.js";
|
|
3
|
-
import { Duration, Effect } from "effect";
|
|
3
|
+
import { Duration, Effect, Predicate } from "effect";
|
|
4
4
|
import { IntegrityHash } from "@effected/npm";
|
|
5
|
+
import { Yaml } from "@effected/yaml";
|
|
5
6
|
import { Run } from "@effected/commands";
|
|
6
7
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
7
|
-
import { mkdtemp, readFile, realpath, rm, writeFile } from "node:fs/promises";
|
|
8
|
+
import { copyFile, mkdtemp, readFile, realpath, rm, writeFile } from "node:fs/promises";
|
|
8
9
|
import { tmpdir } from "node:os";
|
|
9
10
|
import { basename, dirname, join } from "node:path";
|
|
10
11
|
|
|
@@ -47,12 +48,36 @@ const expectedIntegrity = (request) => Effect.gen(function* () {
|
|
|
47
48
|
if (integrity === void 0) return yield* Effect.fail(fetchFailure("integrityUnavailable", `config dependency ${key} must be fetched, but ${sideLabel(side)} records no integrity for it (no inline integrity in pnpm-workspace.yaml and no pnpm-lock.yaml entry for that version), so a fetched copy could not be verified and nothing was fetched.`));
|
|
48
49
|
return integrity;
|
|
49
50
|
});
|
|
51
|
+
/** The {@link RegistrySettings} of a parsed `pnpm-workspace.yaml` document. */
|
|
52
|
+
const registrySettingsOf = (document) => {
|
|
53
|
+
if (!Predicate.isObject(document)) return { registries: {} };
|
|
54
|
+
const registries = {};
|
|
55
|
+
if (Predicate.isObject(document.registries)) {
|
|
56
|
+
for (const [scope, url] of Object.entries(document.registries)) if (typeof url === "string") registries[scope] = url;
|
|
57
|
+
}
|
|
58
|
+
return typeof document.registry === "string" ? {
|
|
59
|
+
registry: document.registry,
|
|
60
|
+
registries
|
|
61
|
+
} : { registries };
|
|
62
|
+
};
|
|
50
63
|
/**
|
|
51
|
-
* The scratch workspace's `pnpm-workspace.yaml
|
|
64
|
+
* The scratch workspace's `pnpm-workspace.yaml`: the one config dependency,
|
|
65
|
+
* plus the declaring workspace's registry settings. Every key and scalar is a
|
|
52
66
|
* JSON-quoted string, which YAML reads verbatim, so no declared text can
|
|
53
67
|
* change the document's structure.
|
|
54
68
|
*/
|
|
55
|
-
const scratchWorkspaceYaml = (name, version
|
|
69
|
+
const scratchWorkspaceYaml = (name, version, settings = { registries: {} }) => {
|
|
70
|
+
const lines = ["configDependencies:", ` ${JSON.stringify(name)}: ${JSON.stringify(version)}`];
|
|
71
|
+
if (settings.registry !== void 0) lines.push(`registry: ${JSON.stringify(settings.registry)}`);
|
|
72
|
+
const scopes = Object.entries(settings.registries);
|
|
73
|
+
if (scopes.length > 0) {
|
|
74
|
+
lines.push("registries:");
|
|
75
|
+
for (const [scope, url] of scopes) lines.push(` ${JSON.stringify(scope)}: ${JSON.stringify(url)}`);
|
|
76
|
+
}
|
|
77
|
+
return `${lines.join("\n")}\n`;
|
|
78
|
+
};
|
|
79
|
+
/** Whether `error` is a node filesystem "no such file" failure. */
|
|
80
|
+
const isNotFound = (error) => Predicate.isObject(error) && error.code === "ENOENT";
|
|
56
81
|
/**
|
|
57
82
|
* The scratch workspace's `pnpm-lock.yaml`: the env preamble pnpm 11 and 12
|
|
58
83
|
* write for a lone config dependency, pinned to `integrity`, followed by the
|
|
@@ -123,14 +148,22 @@ const makeFetchConfigDependency = (spawner) => (request) => Effect.scoped(Effect
|
|
|
123
148
|
catch: (cause) => failed(`${what} failed: ${messageOf(cause)}`, cause)
|
|
124
149
|
});
|
|
125
150
|
const integrity = yield* expectedIntegrity(request);
|
|
151
|
+
const rootYaml = yield* io("reading the workspace pnpm-workspace.yaml", () => readFile(join(request.root, "pnpm-workspace.yaml"), "utf8").catch((error) => {
|
|
152
|
+
if (isNotFound(error)) return void 0;
|
|
153
|
+
throw error;
|
|
154
|
+
}));
|
|
155
|
+
const settings = rootYaml === void 0 ? registrySettingsOf(void 0) : registrySettingsOf(yield* Yaml.parse(rootYaml).pipe(Effect.mapError((cause) => failed(`reading the workspace pnpm-workspace.yaml failed: ${messageOf(cause)}`, cause))));
|
|
126
156
|
const scratch = yield* Effect.acquireRelease(io("creating a scratch workspace", () => mkdtemp(join(tmpdir(), "effected-config-dependency-"))), (dir) => Effect.promise(() => rm(dir, {
|
|
127
157
|
recursive: true,
|
|
128
158
|
force: true
|
|
129
159
|
}).catch(() => void 0)));
|
|
130
160
|
const lockfile = scratchLockfile(name, version, integrity);
|
|
131
161
|
yield* io("writing the scratch workspace", async () => {
|
|
132
|
-
await writeFile(join(scratch, "pnpm-workspace.yaml"), scratchWorkspaceYaml(name, version));
|
|
162
|
+
await writeFile(join(scratch, "pnpm-workspace.yaml"), scratchWorkspaceYaml(name, version, settings));
|
|
133
163
|
await writeFile(join(scratch, "pnpm-lock.yaml"), lockfile);
|
|
164
|
+
await copyFile(join(request.root, ".npmrc"), join(scratch, ".npmrc")).catch((error) => {
|
|
165
|
+
if (!isNotFound(error)) throw error;
|
|
166
|
+
});
|
|
134
167
|
});
|
|
135
168
|
const command = ChildProcess.make("pnpm", fetchArgs(scratch, request.stores), { cwd: scratch });
|
|
136
169
|
yield* Run.text(command, { timeout: FETCH_TIMEOUT }).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.mapError((cause) => failed(`fetching ${key} with \`pnpm ${fetchArgs("<scratch>", request.stores).join(" ")}\` failed: ${messageOf(cause)}`, cause)));
|
|
@@ -143,4 +176,4 @@ const makeFetchConfigDependency = (spawner) => (request) => Effect.scoped(Effect
|
|
|
143
176
|
}));
|
|
144
177
|
|
|
145
178
|
//#endregion
|
|
146
|
-
export { expectedIntegrity, fetchArgs, makeFetchConfigDependency, storeDirArgument };
|
|
179
|
+
export { expectedIntegrity, fetchArgs, makeFetchConfigDependency, registrySettingsOf, scratchWorkspaceYaml, storeDirArgument };
|
|
@@ -266,6 +266,7 @@ const resolveDirectory = (root, entry, stores, locks, options) => Effect.gen(fun
|
|
|
266
266
|
if (options.fetch === void 0) return yield* Effect.fail(notInstalled(Option.none()));
|
|
267
267
|
return {
|
|
268
268
|
dir: yield* options.fetch({
|
|
269
|
+
root,
|
|
269
270
|
name,
|
|
270
271
|
version: declared,
|
|
271
272
|
spec: entry.spec,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/layerRoot.ts
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the workspace root a layer's options name. `Effect.suspend` so the
|
|
6
|
+
* ambient `process.cwd()` is read at first use, not at layer construction.
|
|
7
|
+
*/
|
|
8
|
+
const findLayerRoot = (roots, options) => Effect.suspend(() => {
|
|
9
|
+
const stopAt = options?.stopAt;
|
|
10
|
+
return roots.find(options?.cwd ?? process.cwd(), stopAt === void 0 ? void 0 : { stopAt });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
export { findLayerRoot };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/workspaces",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.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": [
|
|
@@ -55,9 +55,9 @@
|
|
|
55
55
|
"@effected/git": "^0.17.0",
|
|
56
56
|
"@effected/glob": "^0.8.0",
|
|
57
57
|
"@effected/jsonc": "^0.13.0",
|
|
58
|
-
"@effected/lockfiles": "^0.
|
|
59
|
-
"@effected/npm": "^0.
|
|
60
|
-
"@effected/package-json": "^0.18.
|
|
58
|
+
"@effected/lockfiles": "^0.13.0",
|
|
59
|
+
"@effected/npm": "^0.18.0",
|
|
60
|
+
"@effected/package-json": "^0.18.1",
|
|
61
61
|
"@effected/semver": "^0.9.0",
|
|
62
62
|
"@effected/walker": "^0.12.0",
|
|
63
63
|
"@effected/yaml": "^0.17.0",
|
package/testing.d.ts
CHANGED
|
@@ -652,6 +652,11 @@ interface WorkspaceDiscoveryOptions {
|
|
|
652
652
|
* directory the caller names, which a single layer-level ceiling cannot
|
|
653
653
|
* sensibly bound, so they ascend unbounded as before.
|
|
654
654
|
*
|
|
655
|
+
* `LockfileReader`, `WorkspaceCatalogs` and `WorkspaceSnapshots` each
|
|
656
|
+
* take the same option; give them the same
|
|
657
|
+
* value when wiring by hand, or let a `Workspaces.*` composite forward one
|
|
658
|
+
* `stopAt` to all of them, so no service adopts a root another refused.
|
|
659
|
+
*
|
|
655
660
|
* @defaultValue no ceiling — the ascent runs to the filesystem root.
|
|
656
661
|
*/
|
|
657
662
|
readonly stopAt?: string | undefined;
|