@effected/workspaces 0.23.0 → 0.24.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/ConfigDependencyHooks.js +208 -170
- package/PeerCheck.js +27 -20
- package/WorkspaceCatalogs.js +62 -57
- package/WorkspaceSnapshots.js +37 -7
- package/WorkspaceStateSnapshot.js +41 -17
- package/Workspaces.js +55 -22
- package/index.d.ts +301 -81
- package/internal/configDependencyResolution.js +307 -0
- package/internal/patterns.js +2 -1
- package/internal/peerPatterns.js +35 -0
- package/internal/workspaceYaml.js +44 -0
- package/package.json +2 -2
package/index.d.ts
CHANGED
|
@@ -894,11 +894,13 @@ export declare class ChangeDetector extends ChangeDetector_base {
|
|
|
894
894
|
* replayed hook returns `{ allowedVersions, ignoreMissing, allowAny }` intact,
|
|
895
895
|
* needing no reshaping.
|
|
896
896
|
*
|
|
897
|
-
*
|
|
898
|
-
* `
|
|
899
|
-
*
|
|
900
|
-
*
|
|
901
|
-
* on
|
|
897
|
+
* All three axes are consumed by `PeerCheck`, each established by measurement
|
|
898
|
+
* against `pnpm peers check` with a firing control. `ignoreMissing` and
|
|
899
|
+
* `allowAny` are lists of **peer-name patterns** in pnpm's `@pnpm/matcher`
|
|
900
|
+
* grammar (`*` wildcard, leading `!` negation) — not `parent>peer` keys, which
|
|
901
|
+
* match nothing on those two axes. `ignoreMissing` hides only a required peer
|
|
902
|
+
* that resolved to nothing; `allowAny` hides only a peer that resolved outside
|
|
903
|
+
* its wanted range; the two never cross.
|
|
902
904
|
*
|
|
903
905
|
* `allowedVersions` keys come in two spellings in the wild, and both must be
|
|
904
906
|
* handled: parent-versioned (`"@effect/ai-anthropic@4.0.0-rc.109>effect"`, as
|
|
@@ -910,9 +912,9 @@ export declare class ChangeDetector extends ChangeDetector_base {
|
|
|
910
912
|
interface PeerDependencyRules {
|
|
911
913
|
/** `parent>peer` → the peer version or range the rule permits. */
|
|
912
914
|
readonly allowedVersions: Readonly<Record<string, string>>;
|
|
913
|
-
/** Peer
|
|
915
|
+
/** Peer-name patterns whose absence pnpm does not report (a required peer that resolved to nothing). */
|
|
914
916
|
readonly ignoreMissing: ReadonlyArray<string>;
|
|
915
|
-
/** Peer
|
|
917
|
+
/** Peer-name patterns for which any resolved version is accepted. */
|
|
916
918
|
readonly allowAny: ReadonlyArray<string>;
|
|
917
919
|
}
|
|
918
920
|
/**
|
|
@@ -930,6 +932,34 @@ interface PeerDependencyRules {
|
|
|
930
932
|
* @public
|
|
931
933
|
*/
|
|
932
934
|
export declare const NoPeerDependencyRules: PeerDependencyRules;
|
|
935
|
+
/**
|
|
936
|
+
* Where a replayed config dependency's declared version was found: the
|
|
937
|
+
* `node_modules/.pnpm-config` copy, the pnpm store's `links/` tree, or a
|
|
938
|
+
* {@link ConfigDependencyHooks.layerFrom} entry.
|
|
939
|
+
*
|
|
940
|
+
* @public
|
|
941
|
+
*/
|
|
942
|
+
type HookReplaySource = "installed" | "store" | "supplied";
|
|
943
|
+
/**
|
|
944
|
+
* Which version of a config dependency a replay actually loaded, and where
|
|
945
|
+
* that version came from.
|
|
946
|
+
*
|
|
947
|
+
* @remarks
|
|
948
|
+
* Recorded per dependency so a consumer diffing two refs can tell not just
|
|
949
|
+
* WHAT the hooks injected but which pinned version injected it — the
|
|
950
|
+
* evidence that a "range moved" row came from a config-dependency bump and
|
|
951
|
+
* not from a hook edit. `installed` is the `node_modules/.pnpm-config` copy,
|
|
952
|
+
* `store` the pnpm store's `links/` tree, `supplied` a
|
|
953
|
+
* {@link ConfigDependencyHooks.layerFrom} entry.
|
|
954
|
+
*
|
|
955
|
+
* @public
|
|
956
|
+
*/
|
|
957
|
+
interface HookReplay {
|
|
958
|
+
/** The declared version that was resolved and replayed. */
|
|
959
|
+
readonly version: string;
|
|
960
|
+
/** Which resolution rung answered. */
|
|
961
|
+
readonly source: HookReplaySource;
|
|
962
|
+
}
|
|
933
963
|
/**
|
|
934
964
|
* The result of replaying a workspace's `configDependencies` hooks: the catalogs
|
|
935
965
|
* the hooks yield, and the release-age gate contribution they leave on the
|
|
@@ -967,6 +997,13 @@ interface HookInjection {
|
|
|
967
997
|
* overwrites for pnpm too — so do not "repair" it into a merger.
|
|
968
998
|
*/
|
|
969
999
|
readonly peerDependencyRules: PeerDependencyRules;
|
|
1000
|
+
/**
|
|
1001
|
+
* Which version each declared config dependency was replayed from, keyed
|
|
1002
|
+
* by name — every dependency the layer resolved, including one that ships
|
|
1003
|
+
* no pnpmfile (resolved, contributed nothing). `{}` under
|
|
1004
|
+
* {@link ConfigDependencyHooks.layerNoop}, which resolves nothing.
|
|
1005
|
+
*/
|
|
1006
|
+
readonly replays: Readonly<Record<string, HookReplay>>;
|
|
970
1007
|
}
|
|
971
1008
|
/**
|
|
972
1009
|
* The {@link ConfigDependencyHooks} service shape.
|
|
@@ -1000,9 +1037,12 @@ interface ConfigDependencyHooksShape {
|
|
|
1000
1037
|
* `hooks`-source `CatalogAssemblyError`, never a silent skip.
|
|
1001
1038
|
*
|
|
1002
1039
|
* @param root - The workspace root; config dependencies resolve under
|
|
1003
|
-
* `<root>/node_modules/.pnpm-config/<name
|
|
1040
|
+
* `<root>/node_modules/.pnpm-config/<name>` when that holds the declared
|
|
1041
|
+
* version, else through the pnpm store.
|
|
1004
1042
|
* @param configDependencies - The `configDependencies` map (name →
|
|
1005
|
-
* version
|
|
1043
|
+
* `<version>+<integrity>`, or a bare `<version>`) declared in the
|
|
1044
|
+
* `pnpm-workspace.yaml` being read — the live one, or the one at a ref.
|
|
1045
|
+
* The replaying layers load the pnpmfile of the DECLARED version.
|
|
1006
1046
|
* @param seed - The inline catalogs, as `catalog name → dependency → range`.
|
|
1007
1047
|
* @param rules - The workspace file's `peerDependencyRules`, seeded into the
|
|
1008
1048
|
* threaded config so hooks merge onto them rather than replacing them.
|
|
@@ -1018,12 +1058,21 @@ declare const ConfigDependencyHooks_base: Context.ServiceClass<ConfigDependencyH
|
|
|
1018
1058
|
* assembly.
|
|
1019
1059
|
*
|
|
1020
1060
|
* @remarks
|
|
1021
|
-
* A contract-only service: it declares the shape and ships
|
|
1061
|
+
* A contract-only service: it declares the shape and ships layers, never a
|
|
1022
1062
|
* baked-in default. {@link ConfigDependencyHooks.layerNoop} executes no
|
|
1023
1063
|
* config-dependency code (it returns the seed untouched) and is what the default
|
|
1024
1064
|
* {@link WorkspaceCatalogs} layer wires; {@link ConfigDependencyHooks.layerLive}
|
|
1025
|
-
* dynamically imports each
|
|
1026
|
-
* explicit `WorkspaceCatalogs.layerWithConfigDependencies` opt-in
|
|
1065
|
+
* dynamically imports each pnpmfile and replays it, and is wired only by the
|
|
1066
|
+
* explicit `WorkspaceCatalogs.layerWithConfigDependencies` opt-in;
|
|
1067
|
+
* {@link ConfigDependencyHooks.layerSubprocess} is its bundler-safe twin; and
|
|
1068
|
+
* {@link ConfigDependencyHooks.layerFrom} is the hermetic test seam that
|
|
1069
|
+
* replays caller-supplied files with no resolution.
|
|
1070
|
+
*
|
|
1071
|
+
* Every replaying layer replays the pnpmfile of the version each
|
|
1072
|
+
* `configDependencies` entry **declares** — `layerLive` and `layerSubprocess`
|
|
1073
|
+
* resolve it through `node_modules/.pnpm-config` and then the pnpm store,
|
|
1074
|
+
* failing closed when neither holds that version — so a snapshot read at a
|
|
1075
|
+
* past ref replays that ref's pinned hook, not today's.
|
|
1027
1076
|
*
|
|
1028
1077
|
* @public
|
|
1029
1078
|
*/
|
|
@@ -1035,22 +1084,76 @@ export declare class ConfigDependencyHooks extends ConfigDependencyHooks_base {
|
|
|
1035
1084
|
*/
|
|
1036
1085
|
static readonly layerNoop: Layer.Layer<ConfigDependencyHooks>;
|
|
1037
1086
|
/**
|
|
1038
|
-
* The live layer:
|
|
1039
|
-
* (in process, no subprocess)
|
|
1040
|
-
* seed, in declaration order.
|
|
1041
|
-
*
|
|
1042
|
-
*
|
|
1087
|
+
* The live layer: resolves each config dependency to the pnpmfile of its
|
|
1088
|
+
* **declared** version, dynamically imports it (in process, no subprocess)
|
|
1089
|
+
* and replays its `updateConfig` hook over the seed, in declaration order.
|
|
1090
|
+
* A dependency that ships no pnpmfile contributes nothing; a dependency
|
|
1091
|
+
* whose declared version is installed nowhere, or whose file fails to load
|
|
1092
|
+
* or replay, fails typed with a `hooks`-source `CatalogAssemblyError`, never
|
|
1093
|
+
* a silent skip.
|
|
1043
1094
|
*
|
|
1044
1095
|
* @remarks
|
|
1045
|
-
*
|
|
1096
|
+
* Resolution is a ladder, fail-closed, with no registry fetch ever:
|
|
1097
|
+
* `<root>/node_modules/.pnpm-config/<name>` when its `package.json`
|
|
1098
|
+
* `version` equals the declared version (the text before the first `+` of
|
|
1099
|
+
* the `configDependencies` value), else the pnpm store's
|
|
1100
|
+
* `links/<name>/<declared>/*\/node_modules/<name>` (the store is located
|
|
1101
|
+
* from `node_modules/.modules.yaml`, the realpath of any `.pnpm-config`
|
|
1102
|
+
* entry, then `$PNPM_HOME` / `$XDG_DATA_HOME` / the platform default), else
|
|
1103
|
+
* a typed error naming the package, the declared version, what is
|
|
1104
|
+
* installed, the stores searched, and the remediation
|
|
1105
|
+
* (`pnpm add --config <name>@<version>` in a throwaway workspace). In the
|
|
1106
|
+
* chosen directory the first of `pnpmfile.mjs`, `pnpmfile.cjs`,
|
|
1107
|
+
* `pnpmfile.js` present in one directory listing is loaded; a listed but
|
|
1108
|
+
* unreadable file fails typed at import time, never as "ships no hook".
|
|
1109
|
+
*
|
|
1110
|
+
* Runtime-coupled by design, not node-exclusive. The `import()` loads
|
|
1046
1111
|
* **and executes** a config dependency's pnpmfile in-process — code execution,
|
|
1047
|
-
* not IO, so no `FileSystem` / `Path` service abstracts it
|
|
1048
|
-
*
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1051
|
-
* `
|
|
1112
|
+
* not IO, so no `FileSystem` / `Path` service abstracts it — and the ladder
|
|
1113
|
+
* reads the real disk through `node:fs` for the same reason: the store it
|
|
1114
|
+
* searches is real even when the caller's `FileSystem` is virtual. Only ever
|
|
1115
|
+
* wired by `WorkspaceCatalogs.layerWithConfigDependencies` and the
|
|
1116
|
+
* `Workspaces` composites of the same name.
|
|
1052
1117
|
*/
|
|
1053
1118
|
static readonly layerLive: Layer.Layer<ConfigDependencyHooks>;
|
|
1119
|
+
/**
|
|
1120
|
+
* The hermetic layer: replays caller-supplied pnpmfiles, keyed
|
|
1121
|
+
* `"<name>@<version>"` → absolute path, with **no resolution** — it consults
|
|
1122
|
+
* neither `node_modules/.pnpm-config` nor the pnpm store, and never the
|
|
1123
|
+
* effect `FileSystem` service, so it works unchanged under an
|
|
1124
|
+
* `@effected/memfs` volume.
|
|
1125
|
+
*
|
|
1126
|
+
* @remarks
|
|
1127
|
+
* The test seam for anything that replays hooks: a suite can pin what
|
|
1128
|
+
* `at(ref)` replays for a ref declaring `plugin@1.0.0` versus one declaring
|
|
1129
|
+
* `plugin@2.0.0` by mapping both keys to fixture files, with no store on
|
|
1130
|
+
* disk. The version half of the key is the declared version — the text
|
|
1131
|
+
* before the first `+` of the `configDependencies` value — so a
|
|
1132
|
+
* `<version>+<integrity>` spec and a bare `<version>` spec look up the same
|
|
1133
|
+
* entry.
|
|
1134
|
+
*
|
|
1135
|
+
* Same typed semantics as {@link ConfigDependencyHooks.layerLive} past the
|
|
1136
|
+
* lookup: the same in-process `import()`, the same hook-locator shapes, the
|
|
1137
|
+
* same tolerant threading, the same `hooks`-source attribution. A declared
|
|
1138
|
+
* `(name, version)` with no entry fails closed, the same shape as an
|
|
1139
|
+
* uninstalled version under `layerLive`; an empty `configDependencies`
|
|
1140
|
+
* returns the seed untouched. A `..` segment in a name is refused before
|
|
1141
|
+
* any lookup.
|
|
1142
|
+
*
|
|
1143
|
+
* @param entries - `"<name>@<version>"` → the absolute path of the pnpmfile
|
|
1144
|
+
* to replay for that declared version.
|
|
1145
|
+
*
|
|
1146
|
+
* @example
|
|
1147
|
+
* ```ts
|
|
1148
|
+
* import { ConfigDependencyHooks } from "@effected/workspaces";
|
|
1149
|
+
*
|
|
1150
|
+
* const hooks = ConfigDependencyHooks.layerFrom({
|
|
1151
|
+
* "@scope/plugin@1.0.0": "/fixtures/plugin-1/pnpmfile.mjs",
|
|
1152
|
+
* "@scope/plugin@2.0.0": "/fixtures/plugin-2/pnpmfile.mjs",
|
|
1153
|
+
* });
|
|
1154
|
+
* ```
|
|
1155
|
+
*/
|
|
1156
|
+
static readonly layerFrom: (entries: Readonly<Record<string, string>>) => Layer.Layer<ConfigDependencyHooks>;
|
|
1054
1157
|
/**
|
|
1055
1158
|
* The subprocess layer: replays each config dependency's pnpmfile in a `node`
|
|
1056
1159
|
* child process instead of an in-process dynamic `import()`, with identical
|
|
@@ -1064,24 +1167,26 @@ export declare class ConfigDependencyHooks extends ConfigDependencyHooks_base {
|
|
|
1064
1167
|
* consumer, a GitHub Action above all, the in-process replay is unreachable.
|
|
1065
1168
|
* This layer keeps every computed load out of the bundle graph: the replay
|
|
1066
1169
|
* program is a **static** string constant passed via argv
|
|
1067
|
-
* (`node --input-type=module -e <script> <
|
|
1170
|
+
* (`node --input-type=module -e <script> <seed> <rules> <entries>`), and the
|
|
1068
1171
|
* child process performs the computed imports where no bundler rewrote them.
|
|
1069
1172
|
* A subprocess also keeps config-dependency code out of the consumer's own
|
|
1070
1173
|
* process.
|
|
1071
1174
|
*
|
|
1072
|
-
* The
|
|
1073
|
-
*
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1076
|
-
*
|
|
1077
|
-
*
|
|
1078
|
-
*
|
|
1079
|
-
*
|
|
1080
|
-
*
|
|
1081
|
-
*
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1175
|
+
* The declared-version resolution is the PARENT's: the same ladder as
|
|
1176
|
+
* `layerLive` (`.pnpm-config` when it holds the declared version, else the
|
|
1177
|
+
* pnpm store, else a typed fail-closed error) runs here, and the child
|
|
1178
|
+
* receives the resolved `[name, fileUrl]` pairs as one JSON argv
|
|
1179
|
+
* argument. An empty `configDependencies`, or one whose dependencies all
|
|
1180
|
+
* ship no pnpmfile, returns the seed without spawning anything; a `..` path
|
|
1181
|
+
* segment in a dependency name fails typed **before** any spawn; a
|
|
1182
|
+
* load failure in the child — a syntax error, a throwing top level, an
|
|
1183
|
+
* `ERR_MODULE_NOT_FOUND` for a module the pnpmfile itself imports — and a
|
|
1184
|
+
* hook that throws when called are serialized back per-name and surface
|
|
1185
|
+
* typed as a `hooks`-source `CatalogAssemblyError` naming that dependency.
|
|
1186
|
+
* A hook's returned *data* stays tolerantly threaded (last well-formed write
|
|
1187
|
+
* wins), never fatal. Spawn and transport failures — `node` absent, a
|
|
1188
|
+
* non-zero exit without a result payload, unparseable output — fail typed
|
|
1189
|
+
* too, never a defect and never a silent skip.
|
|
1085
1190
|
*
|
|
1086
1191
|
* Two bounds this layer imposes that `layerLive` cannot: the replay is
|
|
1087
1192
|
* given thirty seconds (a pnpmfile that loops or awaits forever fails typed
|
|
@@ -1829,9 +1934,9 @@ export declare class LockfileReader extends LockfileReader_base {
|
|
|
1829
1934
|
* so pnpm's post-hoc suppression could not be replicated and some reported
|
|
1830
1935
|
* rows may be ones pnpm hides. **Omitting the option always produces this**,
|
|
1831
1936
|
* because "nobody looked" and "I looked and there are none" are different
|
|
1832
|
-
* facts. Supplied rules
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
1937
|
+
* facts. Supplied rules never produce it: all three axes —
|
|
1938
|
+
* `allowedVersions`, `ignoreMissing` and `allowAny` — are applied with
|
|
1939
|
+
* pnpm's measured semantics.
|
|
1835
1940
|
* - `"unresolvedEdge"` — some instance records an edge this model could not
|
|
1836
1941
|
* name (`ResolvedPackage.unresolvedEdges`), so a peer that edge satisfies
|
|
1837
1942
|
* cannot be verified either way.
|
|
@@ -1853,10 +1958,11 @@ type UnverifiedReason = "peerRulesNotApplied" | "unresolvedEdge";
|
|
|
1853
1958
|
* different results, because a gate must be able to tell "clean" from
|
|
1854
1959
|
* "unchecked".
|
|
1855
1960
|
*
|
|
1856
|
-
* **
|
|
1857
|
-
*
|
|
1858
|
-
*
|
|
1859
|
-
*
|
|
1961
|
+
* **All three axes of the supplied rules are applied**, each with the
|
|
1962
|
+
* semantics measured against pnpm (see {@link PeerCheck.run}): `allowedVersions`
|
|
1963
|
+
* permits a resolved version by range, `ignoreMissing` hides a required peer
|
|
1964
|
+
* nothing resolved for, and `allowAny` hides a peer that resolved outside its
|
|
1965
|
+
* range. The two list axes are name patterns, never `parent>peer` keys.
|
|
1860
1966
|
*
|
|
1861
1967
|
* @public
|
|
1862
1968
|
*/
|
|
@@ -2736,10 +2842,8 @@ interface WorkspaceCatalogsShape {
|
|
|
2736
2842
|
*
|
|
2737
2843
|
* A consumer needs these to avoid **reporting** what pnpm suppresses: pnpm
|
|
2738
2844
|
* computes the same peer violations and then hides the ones a rule allows,
|
|
2739
|
-
* so a checker without them reports findings pnpm calls clean.
|
|
2740
|
-
*
|
|
2741
|
-
* `ignoreMissing` and `allowAny` are carried but unmeasured — no kit code
|
|
2742
|
-
* acts on them today.
|
|
2845
|
+
* so a checker without them reports findings pnpm calls clean. `PeerCheck`
|
|
2846
|
+
* applies all three axes.
|
|
2743
2847
|
*/
|
|
2744
2848
|
readonly peerDependencyRules: () => Effect.Effect<PeerDependencyRules, CatalogAssemblyFailure>;
|
|
2745
2849
|
/**
|
|
@@ -2755,6 +2859,18 @@ interface WorkspaceCatalogsShape {
|
|
|
2755
2859
|
* nothing rather than failing.
|
|
2756
2860
|
*/
|
|
2757
2861
|
readonly importerVersions: () => Effect.Effect<ImporterVersions, CatalogAssemblyFailure>;
|
|
2862
|
+
/**
|
|
2863
|
+
* Which version each declared config dependency was replayed from, keyed
|
|
2864
|
+
* by name — off the same single memoized assemble pass as `set()`.
|
|
2865
|
+
*
|
|
2866
|
+
* @remarks
|
|
2867
|
+
* Every dependency the hooks layer resolved when a `pnpm-workspace.yaml`
|
|
2868
|
+
* was read — `{}` when it declares no config dependencies or under the
|
|
2869
|
+
* no-op layer, which resolves nothing. Config dependencies are a pnpm
|
|
2870
|
+
* feature, so the bun / `package.json` path yields `{}` too, exactly as
|
|
2871
|
+
* `importerVersions` does.
|
|
2872
|
+
*/
|
|
2873
|
+
readonly hookReplays: () => Effect.Effect<Readonly<Record<string, HookReplay>>, CatalogAssemblyFailure>;
|
|
2758
2874
|
/**
|
|
2759
2875
|
* Discard the memoized assembly so the **next** read re-assembles — the same
|
|
2760
2876
|
* single read and hook replay as the first, over the workspace as it stands
|
|
@@ -2818,6 +2934,26 @@ declare const WorkspaceCatalogs_base: Context.ServiceClass<WorkspaceCatalogs, "@
|
|
|
2818
2934
|
export declare class WorkspaceCatalogs extends WorkspaceCatalogs_base {
|
|
2819
2935
|
/** Builds the service. */
|
|
2820
2936
|
static readonly make: (options?: WorkspaceCatalogsOptions) => Effect.Effect<WorkspaceCatalogsShape, never, WorkspaceRoot | LockfileReader | ConfigDependencyHooks | FileSystem.FileSystem | Path.Path>;
|
|
2937
|
+
/**
|
|
2938
|
+
* The service over an explicit {@link ConfigDependencyHooks} layer — the
|
|
2939
|
+
* one builder every `layer*` static and the `Workspaces` composites
|
|
2940
|
+
* delegate to, so the hooks layer is provided to {@link WorkspaceCatalogs.make}
|
|
2941
|
+
* in exactly one place.
|
|
2942
|
+
*
|
|
2943
|
+
* @remarks
|
|
2944
|
+
* Reach for it when the hooks layer is chosen elsewhere — a composite that
|
|
2945
|
+
* must hand ONE hooks reference to both this service and
|
|
2946
|
+
* `WorkspaceSnapshots` (layers memoize by reference, so two references
|
|
2947
|
+
* would replay twice and could run two policies), or a test wiring
|
|
2948
|
+
* {@link ConfigDependencyHooks.layerFrom}. The three `layer*` statics are
|
|
2949
|
+
* this with the hooks layer fixed. `R` is the hooks layer's own
|
|
2950
|
+
* requirement (the subprocess variant's `ChildProcessSpawner`), threaded
|
|
2951
|
+
* through. Parameterized, so bind it to a `const` and reuse it.
|
|
2952
|
+
*
|
|
2953
|
+
* @param hooks - The {@link ConfigDependencyHooks} layer to replay through.
|
|
2954
|
+
* @param options - The same options the other layer statics take.
|
|
2955
|
+
*/
|
|
2956
|
+
static readonly layerWithHooks: <R = never>(hooks: Layer.Layer<ConfigDependencyHooks, never, R>, options?: WorkspaceCatalogsOptions) => Layer.Layer<WorkspaceCatalogs, never, WorkspaceRoot | LockfileReader | FileSystem.FileSystem | Path.Path | R>;
|
|
2821
2957
|
/**
|
|
2822
2958
|
* The live layer — the default, which **never executes config-dependency
|
|
2823
2959
|
* code**: it wires {@link ConfigDependencyHooks.layerNoop}, whose hooks return
|
|
@@ -2875,9 +3011,10 @@ export declare class WorkspaceCatalogs extends WorkspaceCatalogs_base {
|
|
|
2875
3011
|
* `resolveSpecifier` answers from that `CatalogSet`'s own
|
|
2876
3012
|
* {@link CatalogSet.resolveSpecifier} — exactly what the live service runs
|
|
2877
3013
|
* over its assembled set — so the two stay consistent by construction.
|
|
2878
|
-
* `releaseAgeGate` and `
|
|
2879
|
-
* catalog set (the gate comes from release-age keys and
|
|
2880
|
-
* the importer index from the lockfile's importer
|
|
3014
|
+
* `releaseAgeGate`, `importerVersions` and `hookReplays` are **not**
|
|
3015
|
+
* derivable from a catalog set (the gate comes from release-age keys and
|
|
3016
|
+
* hook contributions, the importer index from the lockfile's importer
|
|
3017
|
+
* blocks, the replay record from the hooks layer — none is in a
|
|
2881
3018
|
* `CatalogSet`) and always die unless stubbed.
|
|
2882
3019
|
*
|
|
2883
3020
|
* `refresh` defaults to `Effect.void` honestly: the double holds no memo,
|
|
@@ -3009,6 +3146,23 @@ declare const WorkspaceStateSnapshot_base: Schema.Class<WorkspaceStateSnapshot,
|
|
|
3009
3146
|
* importer versions; bun and npm yield an empty index.
|
|
3010
3147
|
*/
|
|
3011
3148
|
readonly importerVersions: Schema.optionalKey<Schema.$Record<Schema.String, Schema.$Record<Schema.String, Schema.String>>>;
|
|
3149
|
+
/**
|
|
3150
|
+
* Which version each declared config dependency's hook was replayed from at
|
|
3151
|
+
* this moment, keyed by name.
|
|
3152
|
+
*
|
|
3153
|
+
* @remarks
|
|
3154
|
+
* Versions only — WHERE this machine found each pnpmfile (the `source` of a
|
|
3155
|
+
* live `HookReplay`) is machine-local provenance, not part of "what the
|
|
3156
|
+
* workspace looked like then", so it stays on the live `HookInjection`
|
|
3157
|
+
* diagnostic and off this serializable value. Every fresh read sets the
|
|
3158
|
+
* field: `{}` under the no-op layer, which resolves nothing, when the file
|
|
3159
|
+
* declares no config dependencies, or on the bun / `package.json` path,
|
|
3160
|
+
* where config dependencies do not exist. Absent only when decoding a
|
|
3161
|
+
* snapshot serialized before the field existed, so stored values stay
|
|
3162
|
+
* readable. Carried through `withSeededCatalogs` and `crossSeed`
|
|
3163
|
+
* unchanged, like `importerVersions`.
|
|
3164
|
+
*/
|
|
3165
|
+
readonly hookReplays: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
|
|
3012
3166
|
/**
|
|
3013
3167
|
* Catalogs supplied from OUTSIDE this moment, consulted only when
|
|
3014
3168
|
* `catalogs` cannot answer.
|
|
@@ -3022,11 +3176,14 @@ declare const WorkspaceStateSnapshot_base: Schema.Class<WorkspaceStateSnapshot,
|
|
|
3022
3176
|
* always wins and both halves stay readable.
|
|
3023
3177
|
*
|
|
3024
3178
|
* The motivating case is a catalog injected by a config-dependency
|
|
3025
|
-
* `pnpmfile` hook. It is recorded in no committed catalog source, so
|
|
3026
|
-
* `
|
|
3027
|
-
* see it, and a `catalog:` specifier
|
|
3028
|
-
* sides of a diff. Seeding the live
|
|
3029
|
-
* set,
|
|
3179
|
+
* `pnpmfile` hook. It is recorded in no committed catalog source, so under
|
|
3180
|
+
* a non-replaying `ConfigDependencyHooks` layer (`layerNoop`, the default)
|
|
3181
|
+
* `WorkspaceSnapshots.at(ref)` cannot see it, and a `catalog:` specifier
|
|
3182
|
+
* against it resolves to nothing on BOTH sides of a diff. Seeding the live
|
|
3183
|
+
* hook-injected set, or the other side's set, restores a declared RANGE
|
|
3184
|
+
* without executing any historical code. Under a replaying layer `at(ref)`
|
|
3185
|
+
* replays the ref's own pinned hook and the seed answers only what neither
|
|
3186
|
+
* source declared.
|
|
3030
3187
|
*
|
|
3031
3188
|
* Defaults to absent, which makes the seed inert — exactly the behavior of
|
|
3032
3189
|
* every snapshot captured before this field existed.
|
|
@@ -3147,8 +3304,8 @@ export declare class WorkspaceStateSnapshot extends WorkspaceStateSnapshot_base
|
|
|
3147
3304
|
* value, and an accumulating seed would make precedence depend on call
|
|
3148
3305
|
* order). {@link WorkspaceStateSnapshot.crossSeed} is the deliberate
|
|
3149
3306
|
* exception: it composes the two seeds explicitly, precisely because a bare
|
|
3150
|
-
* replace would discard a layer-level seed. `catalogs`, `packages` and `
|
|
3151
|
-
* unchanged, so what the ref declared is still exactly what it declared.
|
|
3307
|
+
* replace would discard a layer-level seed. `catalogs`, `packages`, `importerVersions` and `hookReplays` are
|
|
3308
|
+
* carried through unchanged, so what the ref declared is still exactly what it declared.
|
|
3152
3309
|
*
|
|
3153
3310
|
* The two seeds worth reaching for: the LIVE hook-injected catalog set (from
|
|
3154
3311
|
* a `WorkspaceCatalogs` built by one of the config-dependency layers), or the
|
|
@@ -3186,16 +3343,19 @@ export declare class WorkspaceStateSnapshot extends WorkspaceStateSnapshot_base
|
|
|
3186
3343
|
* missing it, at strictly lower precedence than that side's own catalogs, so
|
|
3187
3344
|
* a genuine change between the refs still reads as a change.
|
|
3188
3345
|
*
|
|
3189
|
-
* **
|
|
3190
|
-
* change made purely by bumping the config dependency BETWEEN the two
|
|
3191
|
-
*
|
|
3192
|
-
*
|
|
3193
|
-
* the
|
|
3194
|
-
*
|
|
3195
|
-
*
|
|
3196
|
-
*
|
|
3197
|
-
*
|
|
3198
|
-
*
|
|
3346
|
+
* **What cross-seeding can and cannot see depends on the hooks layer.** A
|
|
3347
|
+
* range change made purely by bumping the config dependency BETWEEN the two
|
|
3348
|
+
* refs is detected when the snapshots were read under a replaying
|
|
3349
|
+
* `ConfigDependencyHooks` layer: `WorkspaceSnapshots.at(ref)` replays each
|
|
3350
|
+
* ref's hook at the version that ref's `configDependencies` declares, so
|
|
3351
|
+
* each side's OWN catalogs carry that side's injected range and the seed
|
|
3352
|
+
* never gets a say. Under `layerNoop` the case stays suppressed — neither
|
|
3353
|
+
* committed source declares the catalog, so each side falls back to the
|
|
3354
|
+
* other's value and the two agree by construction; there, diff
|
|
3355
|
+
* `configDependencies` in `pnpm-workspace.yaml` directly, the only committed
|
|
3356
|
+
* evidence that the injection changed. Cross-seeding is harmless under a
|
|
3357
|
+
* replaying layer (own catalogs take precedence) and still worthwhile under
|
|
3358
|
+
* the no-op one, which is why it stays.
|
|
3199
3359
|
*
|
|
3200
3360
|
* **The seeding relationship is symmetric; the RETURN ORDER is not.** Each
|
|
3201
3361
|
* snapshot is seeded with the other's catalogs, so neither argument is
|
|
@@ -3258,15 +3418,15 @@ export declare class WorkspaceStateSnapshot extends WorkspaceStateSnapshot_base
|
|
|
3258
3418
|
//#region src/WorkspaceSnapshots.d.ts
|
|
3259
3419
|
/**
|
|
3260
3420
|
* Every failure `WorkspaceSnapshots.at` can surface: git's own typed
|
|
3261
|
-
* errors, a catalog-assembly failure from the inline config source
|
|
3262
|
-
* or an unfindable workspace root.
|
|
3421
|
+
* errors, a catalog-assembly failure from the inline config source or the
|
|
3422
|
+
* config-dependency hook replay at the ref, or an unfindable workspace root.
|
|
3263
3423
|
*
|
|
3264
3424
|
* @remarks
|
|
3265
3425
|
* Narrow by design. `at` reads through git and never enumerates the live
|
|
3266
3426
|
* filesystem, so no `WorkspaceDiscoveryError` / `WorkspacePatternError` appears —
|
|
3267
3427
|
* and a malformed *lockfile* at the ref degrades to no catalogs (a lockfile is a
|
|
3268
|
-
* record, not a source of truth), so only the inline source
|
|
3269
|
-
* `CatalogAssemblyError`.
|
|
3428
|
+
* record, not a source of truth), so only the inline source and a
|
|
3429
|
+
* `hooks`-source replay failure raise `CatalogAssemblyError`.
|
|
3270
3430
|
*
|
|
3271
3431
|
* @public
|
|
3272
3432
|
*/
|
|
@@ -3309,10 +3469,13 @@ interface WorkspaceSnapshotsOptions {
|
|
|
3309
3469
|
* remembering to call `withSeededCatalogs` at each site — and a forgotten
|
|
3310
3470
|
* call is a silently missing diff row, not a type error.
|
|
3311
3471
|
*
|
|
3312
|
-
* **This executes nothing.** The caller supplies the set
|
|
3313
|
-
*
|
|
3314
|
-
*
|
|
3315
|
-
*
|
|
3472
|
+
* **This executes nothing by itself.** The caller supplies the set, and
|
|
3473
|
+
* `at(ref)` still never fetches. Whether `at(ref)` replays that ref's
|
|
3474
|
+
* config-dependency hooks is decided by the {@link ConfigDependencyHooks}
|
|
3475
|
+
* layer in scope, not by this option: under a replaying layer the ref's own
|
|
3476
|
+
* replay answers first and the seed fills only what neither source declared;
|
|
3477
|
+
* under `layerNoop` the seed is the only way a hook-injected catalog reaches
|
|
3478
|
+
* the ref side at all.
|
|
3316
3479
|
*
|
|
3317
3480
|
* Applied to `worktree()` too, for symmetry. There it is usually inert: a
|
|
3318
3481
|
* live set assembled under a config-dependency layer already contains the
|
|
@@ -3330,7 +3493,18 @@ interface WorkspaceSnapshotsOptions {
|
|
|
3330
3493
|
* @public
|
|
3331
3494
|
*/
|
|
3332
3495
|
interface WorkspaceSnapshotsShape {
|
|
3333
|
-
/**
|
|
3496
|
+
/**
|
|
3497
|
+
* The workspace state at a git ref, read with no checkout. Cached per
|
|
3498
|
+
* `(root, ref)`.
|
|
3499
|
+
*
|
|
3500
|
+
* @remarks
|
|
3501
|
+
* The ref's `pnpm-workspace.yaml` `configDependencies` are replayed through
|
|
3502
|
+
* the {@link ConfigDependencyHooks} layer in scope at the versions THAT ref
|
|
3503
|
+
* declares (a replaying layer resolves them via `.pnpm-config` or the pnpm
|
|
3504
|
+
* store and fails closed when a declared version is installed nowhere;
|
|
3505
|
+
* `layerNoop` executes nothing). The injected catalogs merge above the
|
|
3506
|
+
* ref's lockfile and inline sources, exactly as the live assembler does.
|
|
3507
|
+
*/
|
|
3334
3508
|
readonly at: (ref: string) => Effect.Effect<WorkspaceStateSnapshot, WorkspaceSnapshotAtFailure>;
|
|
3335
3509
|
/** The live workspace state, over discovery and catalog assembly. Uncached. */
|
|
3336
3510
|
readonly worktree: () => Effect.Effect<WorkspaceStateSnapshot, WorkspaceSnapshotWorktreeFailure>;
|
|
@@ -3362,8 +3536,17 @@ declare const WorkspaceSnapshots_base: Context.ServiceClass<WorkspaceSnapshots,
|
|
|
3362
3536
|
* @public
|
|
3363
3537
|
*/
|
|
3364
3538
|
export declare class WorkspaceSnapshots extends WorkspaceSnapshots_base {
|
|
3365
|
-
/**
|
|
3366
|
-
|
|
3539
|
+
/**
|
|
3540
|
+
* Builds the service over `Git`, {@link WorkspaceRoot}, {@link WorkspaceDiscovery},
|
|
3541
|
+
* {@link WorkspaceCatalogs} and {@link ConfigDependencyHooks}.
|
|
3542
|
+
*
|
|
3543
|
+
* @remarks
|
|
3544
|
+
* `ConfigDependencyHooks` is what `at(ref)` replays a ref's
|
|
3545
|
+
* `configDependencies` through — at the versions THAT ref declares, which is
|
|
3546
|
+
* why the composites hand the same hooks layer to this service and to
|
|
3547
|
+
* `WorkspaceCatalogs`. Under `layerNoop` the ref read executes nothing.
|
|
3548
|
+
*/
|
|
3549
|
+
static readonly make: (options?: WorkspaceSnapshotsOptions) => Effect.Effect<WorkspaceSnapshotsShape, never, Git | WorkspaceRoot | WorkspaceDiscovery | WorkspaceCatalogs | ConfigDependencyHooks>;
|
|
3367
3550
|
/**
|
|
3368
3551
|
* The live layer.
|
|
3369
3552
|
*
|
|
@@ -3371,7 +3554,7 @@ export declare class WorkspaceSnapshots extends WorkspaceSnapshots_base {
|
|
|
3371
3554
|
* Parameterized, so it mints a fresh reference per call — bind it to a
|
|
3372
3555
|
* `const` and reuse it, or layer memoization does not apply.
|
|
3373
3556
|
*/
|
|
3374
|
-
static readonly layer: (options?: WorkspaceSnapshotsOptions) => Layer.Layer<WorkspaceSnapshots, never, Git | WorkspaceRoot | WorkspaceDiscovery | WorkspaceCatalogs>;
|
|
3557
|
+
static readonly layer: (options?: WorkspaceSnapshotsOptions) => Layer.Layer<WorkspaceSnapshots, never, Git | WorkspaceRoot | WorkspaceDiscovery | WorkspaceCatalogs | ConfigDependencyHooks>;
|
|
3375
3558
|
/**
|
|
3376
3559
|
* A test double satisfying the full {@link WorkspaceSnapshotsShape} with no
|
|
3377
3560
|
* git, filesystem, discovery, or catalog assembly.
|
|
@@ -3630,6 +3813,43 @@ export declare class Workspaces {
|
|
|
3630
3813
|
* ```
|
|
3631
3814
|
*/
|
|
3632
3815
|
static readonly layerWithGitAndConfigDependenciesSubprocess: (options?: WorkspacesGitOptions) => Layer.Layer<WorkspacesServices | ChangeDetector | WorkspaceSnapshots | Git, never, FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner>;
|
|
3816
|
+
/**
|
|
3817
|
+
* The git composite over a **caller-supplied** hooks layer — the same graph
|
|
3818
|
+
* as {@link Workspaces.layerWithGit} and its two config-dependency
|
|
3819
|
+
* variants, with the `ConfigDependencyHooks` policy chosen by the caller.
|
|
3820
|
+
*
|
|
3821
|
+
* @remarks
|
|
3822
|
+
* The three fixed git composites are this function applied to
|
|
3823
|
+
* {@link ConfigDependencyHooks.layerNoop}, {@link ConfigDependencyHooks.layerLive}
|
|
3824
|
+
* and {@link ConfigDependencyHooks.layerSubprocess}. It exists for the fourth
|
|
3825
|
+
* policy: {@link ConfigDependencyHooks.layerFrom}, the hermetic seam, which a
|
|
3826
|
+
* snapshot-backed test otherwise cannot reach without rebuilding the whole
|
|
3827
|
+
* git graph by hand — the copy `layerWithGitAndConfigDependencies` was added
|
|
3828
|
+
* to delete. The ONE hooks reference is handed to both `WorkspaceCatalogs`
|
|
3829
|
+
* and `WorkspaceSnapshots`, so `at(ref)` and `worktree()` cannot drift on
|
|
3830
|
+
* which policy they replay. A hooks layer carrying its own requirement (the
|
|
3831
|
+
* subprocess variant's `ChildProcessSpawner`) threads it through to the
|
|
3832
|
+
* composite's `R` unchanged.
|
|
3833
|
+
*
|
|
3834
|
+
* **Bind the result to a `const`.**
|
|
3835
|
+
*
|
|
3836
|
+
* @param hooks - The `ConfigDependencyHooks` layer to wire on both sides.
|
|
3837
|
+
* @param options - The composite options, as for {@link Workspaces.layerWithGit}.
|
|
3838
|
+
*
|
|
3839
|
+
* @example
|
|
3840
|
+
* ```ts
|
|
3841
|
+
* import { ConfigDependencyHooks, Workspaces } from "@effected/workspaces";
|
|
3842
|
+
*
|
|
3843
|
+
* // A test pinning what at(ref) replays for two declared plugin versions.
|
|
3844
|
+
* const KitLayer = Workspaces.layerWithGitAndHooks(
|
|
3845
|
+
* ConfigDependencyHooks.layerFrom({
|
|
3846
|
+
* "@scope/plugin@1.0.0": "/fixtures/plugin-1/pnpmfile.mjs",
|
|
3847
|
+
* "@scope/plugin@2.0.0": "/fixtures/plugin-2/pnpmfile.mjs",
|
|
3848
|
+
* }),
|
|
3849
|
+
* );
|
|
3850
|
+
* ```
|
|
3851
|
+
*/
|
|
3852
|
+
static readonly layerWithGitAndHooks: <R = never>(hooks: Layer.Layer<ConfigDependencyHooks, never, R>, options?: WorkspacesGitOptions) => Layer.Layer<WorkspacesServices | ChangeDetector | WorkspaceSnapshots | Git, never, FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner | R>;
|
|
3633
3853
|
/**
|
|
3634
3854
|
* This package's implementation of `@effected/commands`' `LocalExec`
|
|
3635
3855
|
* contract: how to run a project-local binary here.
|
|
@@ -4092,5 +4312,5 @@ interface GetWorkspacePackagesSyncOptions extends WorkspacesSyncOptions {
|
|
|
4092
4312
|
*/
|
|
4093
4313
|
export declare const getWorkspacePackagesSync: (root: string, options: GetWorkspacePackagesSyncOptions) => ReadonlyArray<WorkspacePackage>;
|
|
4094
4314
|
//#endregion
|
|
4095
|
-
export type { CatalogAssemblyFailure, ChangeDetectionFailure, ChangeDetectorShape, ClassifyOptions, ConfigDependencyHooksShape, DependencyDiff, DuplicateCheckOptions, FindWorkspaceRootOptions, GetWorkspacePackagesSyncOptions, HookInjection, ImporterVersions, LockfileReadFailure, LockfileReaderOptions, LockfileReaderShape, PackageManagerDetectionFailure, PackageManagerDetectorShape, PackageRelease, PeerCheckOptions, PeerDependencyRules, PublishabilityDetectorShape, SyncDirectoryEntry, SyncFileSystem, SyncPath, TagClassification, TagFormatOptions, TrackingTagOptions, UnverifiedReason, VersioningDetectOptions, WorkspaceCatalogsOptions, WorkspaceCatalogsShape, WorkspaceDiscoveryFailure, WorkspaceDiscoveryOptions, WorkspaceDiscoveryShape, WorkspaceDiscoverySkip, WorkspaceDiscoverySkipKind, WorkspaceLookupFailure, WorkspaceRootShape, WorkspaceSnapshotAtFailure, WorkspaceSnapshotWorktreeFailure, WorkspaceSnapshotsOptions, WorkspaceSnapshotsShape, WorkspacesGitOptions, WorkspacesOptions, WorkspacesServices, WorkspacesSyncOptions };
|
|
4315
|
+
export type { CatalogAssemblyFailure, ChangeDetectionFailure, ChangeDetectorShape, ClassifyOptions, ConfigDependencyHooksShape, DependencyDiff, DuplicateCheckOptions, FindWorkspaceRootOptions, GetWorkspacePackagesSyncOptions, HookInjection, HookReplay, HookReplaySource, ImporterVersions, LockfileReadFailure, LockfileReaderOptions, LockfileReaderShape, PackageManagerDetectionFailure, PackageManagerDetectorShape, PackageRelease, PeerCheckOptions, PeerDependencyRules, PublishabilityDetectorShape, SyncDirectoryEntry, SyncFileSystem, SyncPath, TagClassification, TagFormatOptions, TrackingTagOptions, UnverifiedReason, VersioningDetectOptions, WorkspaceCatalogsOptions, WorkspaceCatalogsShape, WorkspaceDiscoveryFailure, WorkspaceDiscoveryOptions, WorkspaceDiscoveryShape, WorkspaceDiscoverySkip, WorkspaceDiscoverySkipKind, WorkspaceLookupFailure, WorkspaceRootShape, WorkspaceSnapshotAtFailure, WorkspaceSnapshotWorktreeFailure, WorkspaceSnapshotsOptions, WorkspaceSnapshotsShape, WorkspacesGitOptions, WorkspacesOptions, WorkspacesServices, WorkspacesSyncOptions };
|
|
4096
4316
|
//# sourceMappingURL=index.d.ts.map
|