@effected/workspaces 0.26.0 → 0.28.0-next-20260927062352
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 +31 -10
- package/ConfigDependencySpec.js +170 -0
- package/PackedInstall.js +530 -51
- package/README.md +67 -18
- package/SourceBoundary.js +87 -3
- package/WorkspaceCatalogs.js +9 -4
- package/WorkspaceSnapshots.js +11 -5
- package/index.d.ts +217 -12
- package/index.js +2 -1
- package/internal/configDependencyFetch.js +146 -0
- package/internal/configDependencyResolution.js +83 -57
- package/internal/configDependencyShared.js +51 -0
- package/internal/configDependencySpecGrammar.js +15 -0
- package/internal/packedInstallPlan.js +73 -8
- package/internal/sourceText.js +32 -6
- package/package.json +4 -4
- package/testing.d.ts +484 -29
package/testing.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { Context, Duration, Effect, FileSystem, Layer, Option, Path, Schema } fr
|
|
|
3
3
|
import { GlobPattern } from "@effected/glob";
|
|
4
4
|
import { WorkspaceManifest } from "@effected/lockfiles";
|
|
5
5
|
import { Package } from "@effected/package-json";
|
|
6
|
+
import { CommandOutput } from "@effected/commands";
|
|
7
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
6
8
|
//#region src/LayerPolicy.d.ts
|
|
7
9
|
declare const LayerPolicyError_base: Schema.Class<LayerPolicyError, Schema.TaggedStruct<"LayerPolicyError", {
|
|
8
10
|
/** `read`: the file could not be read. `json`: it is not JSON. `decode`: it is not a `LayerPolicy`. */
|
|
@@ -883,12 +885,66 @@ export declare class WorkspaceDiscovery extends WorkspaceDiscovery_base {
|
|
|
883
885
|
type PackSource = "source" | {
|
|
884
886
|
readonly directory: string;
|
|
885
887
|
};
|
|
888
|
+
/**
|
|
889
|
+
* What decides the set of packages a packed install packs: the options
|
|
890
|
+
* {@link PackedInstall.closure} and {@link PackedInstall.run} share.
|
|
891
|
+
*
|
|
892
|
+
* @remarks
|
|
893
|
+
* The run's options satisfy this interface, so
|
|
894
|
+
* `PackedInstall.closure(options.carrier, options)` answers for exactly the
|
|
895
|
+
* run those options describe.
|
|
896
|
+
*
|
|
897
|
+
* @public
|
|
898
|
+
*/
|
|
899
|
+
interface PackedInstallClosureOptions {
|
|
900
|
+
/**
|
|
901
|
+
* The other workspace packages to pack and override; `"auto"` is the
|
|
902
|
+
* carrier's transitive runtime workspace dependencies (`dependencies`,
|
|
903
|
+
* `optionalDependencies`, `peerDependencies`). `PackedInstall.closure`
|
|
904
|
+
* defaults it to `"auto"`.
|
|
905
|
+
*/
|
|
906
|
+
readonly closure?: ReadonlyArray<string> | "auto" | undefined;
|
|
907
|
+
/**
|
|
908
|
+
* Packages from outside the workspace that replace registry resolution in
|
|
909
|
+
* every scratch consumer: package name to a local package directory or a
|
|
910
|
+
* `.tgz`, with or without a `file:` prefix.
|
|
911
|
+
*
|
|
912
|
+
* @remarks
|
|
913
|
+
* The dogfood case: a closure member imports a surface of a dependency
|
|
914
|
+
* that exists only in a sibling checkout's unreleased build, and the
|
|
915
|
+
* registry's copy lacks it. A directory is `npm pack`ed, so it must be
|
|
916
|
+
* publish-ready (for an effected-bundled package, its
|
|
917
|
+
* `dist/prod/npm/pkg`); a `.tgz` is used as it is. Either way the packed
|
|
918
|
+
* manifest must be named as its key, carry no specifier only a workspace
|
|
919
|
+
* could resolve, and it joins `PackedInstallResult.tarballs`. Each
|
|
920
|
+
* consumer steers the package to that tarball through the same override
|
|
921
|
+
* field as the closure (npm's and bun's `overrides`, Yarn's `resolutions`,
|
|
922
|
+
* pnpm's `overrides` in `pnpm-workspace.yaml`), so the carrier's
|
|
923
|
+
* transitive references resolve to it too, whatever range they ask for.
|
|
924
|
+
*
|
|
925
|
+
* A relative path resolves against the workspace root. A key naming the
|
|
926
|
+
* carrier or a closure member, a path that is neither a directory with a
|
|
927
|
+
* `package.json` nor a `.tgz` file, or a manifest named otherwise fails
|
|
928
|
+
* `InvalidOverride`. Entries here win over `workspaceOverrides` ones.
|
|
929
|
+
*/
|
|
930
|
+
readonly overrides?: Readonly<Record<string, string>> | undefined;
|
|
931
|
+
/**
|
|
932
|
+
* Also read the workspace root's `pnpm-workspace.yaml` and take every
|
|
933
|
+
* `overrides:` entry of the dogfood link shape,
|
|
934
|
+
* `"@scope/name": "file:<dir>"`, as if passed through `overrides`: the
|
|
935
|
+
* linked sibling builds the workspace itself installs. Entries that are not `file:`, or
|
|
936
|
+
* whose key carries a selector (`a>b`, `a@1`), are left out. A root with
|
|
937
|
+
* no readable `pnpm-workspace.yaml`, or one that is not YAML, fails
|
|
938
|
+
* `InvalidOverride`.
|
|
939
|
+
*/
|
|
940
|
+
readonly workspaceOverrides?: boolean | undefined;
|
|
941
|
+
}
|
|
886
942
|
/**
|
|
887
943
|
* Options for {@link PackedInstall.run}.
|
|
888
944
|
*
|
|
889
945
|
* @public
|
|
890
946
|
*/
|
|
891
|
-
interface PackedInstallOptions {
|
|
947
|
+
interface PackedInstallOptions extends PackedInstallClosureOptions {
|
|
892
948
|
/** The carrier: a direct dependency of each scratch consumer, beside only `consumerDependencies`. */
|
|
893
949
|
readonly carrier: string;
|
|
894
950
|
/** The other workspace packages to pack and override; `"auto"` is the carrier's transitive runtime workspace dependencies. */
|
|
@@ -906,8 +962,46 @@ interface PackedInstallOptions {
|
|
|
906
962
|
* published artifact; see {@link PackSource} for what `"source"` costs.
|
|
907
963
|
*/
|
|
908
964
|
readonly packFrom?: PackSource | undefined;
|
|
909
|
-
/**
|
|
965
|
+
/**
|
|
966
|
+
* Bin names every consumer must expose, executable, in `node_modules/.bin`.
|
|
967
|
+
*
|
|
968
|
+
* @remarks
|
|
969
|
+
* By default only the carrier may declare them. Whatever this lists, a
|
|
970
|
+
* packed package other than the carrier that declares one of the
|
|
971
|
+
* carrier's own bin names fails `BinConflict` before any install, unless
|
|
972
|
+
* `allowSharedBins` is set: under a flat layout (npm, bun,
|
|
973
|
+
* Yarn's `node-modules` linker) either package can take the `.bin` slot,
|
|
974
|
+
* so the bin check and every bin run could pass on the wrong package.
|
|
975
|
+
*
|
|
976
|
+
* The check compares packed packages only (the carrier, the closure and
|
|
977
|
+
* `overrides`), reading each manifest's `bin` field; it does not read
|
|
978
|
+
* `directories.bin`, and a dependency installed from the registry that
|
|
979
|
+
* declares the same bin name goes undetected.
|
|
980
|
+
*/
|
|
910
981
|
readonly bins: ReadonlyArray<string>;
|
|
982
|
+
/**
|
|
983
|
+
* Skip the `BinConflict` check: the front ends declare the carrier's bin
|
|
984
|
+
* names too, deliberately.
|
|
985
|
+
*
|
|
986
|
+
* @remarks
|
|
987
|
+
* A supported, permanent choice for a carrier whose front ends are also
|
|
988
|
+
* installed and run on their own, and a stopgap for one migrating to
|
|
989
|
+
* carrier-only bins. Its cost is provenance: under npm, bun and Yarn's
|
|
990
|
+
* `node-modules` linker, either package can take the `.bin` slot (npm and
|
|
991
|
+
* bun were observed to link the package whose name sorts first, which for
|
|
992
|
+
* a `cli` front end and a `plugin` carrier is the front end; Yarn 1 and 4
|
|
993
|
+
* were observed to keep the carrier's, the consumer's direct dependency),
|
|
994
|
+
* and the bin that runs then carries no distribution identity: no
|
|
995
|
+
* `--version` suffix.
|
|
996
|
+
* When every shared bin calls the same `main()`, nothing else changes.
|
|
997
|
+
*
|
|
998
|
+
* Every expected bin in `bins` is still verified present and executable,
|
|
999
|
+
* but that check, and `runBin`, can pass on the front end's bin. Prove the
|
|
1000
|
+
* carrier's own shim with `InstalledConsumer.runCarrierBin`, and read who
|
|
1001
|
+
* owns the slot with `InstalledConsumer.binProvenance`. Defaults to
|
|
1002
|
+
* `false`, so sharing is an explicit choice.
|
|
1003
|
+
*/
|
|
1004
|
+
readonly allowSharedBins?: boolean | undefined;
|
|
911
1005
|
/** The environment for every spawn: pass `process.env` from the test file. The parent manager's context is stripped. */
|
|
912
1006
|
readonly env: Readonly<Record<string, string | undefined>>;
|
|
913
1007
|
/**
|
|
@@ -922,19 +1016,26 @@ interface PackedInstallOptions {
|
|
|
922
1016
|
* while npm and bun hoist it and pass. That is a property of the consumer,
|
|
923
1017
|
* not of the pack.
|
|
924
1018
|
*
|
|
925
|
-
* An entry naming a packed package (the carrier
|
|
926
|
-
* written as that package's `file:` tarball,
|
|
927
|
-
* any range will do: the packed tarball always
|
|
928
|
-
* whose direct spec differs from its override
|
|
929
|
-
* written through unchanged would break every
|
|
1019
|
+
* An entry naming a packed package (the carrier, a closure member or an
|
|
1020
|
+
* `overrides` package) is written as that package's `file:` tarball,
|
|
1021
|
+
* whatever spec you pass, so any range will do: the packed tarball always
|
|
1022
|
+
* wins. npm fails an install whose direct spec differs from its override
|
|
1023
|
+
* (`EOVERRIDE`), so a range written through unchanged would break every
|
|
1024
|
+
* npm consumer.
|
|
930
1025
|
*/
|
|
931
1026
|
readonly consumerDependencies?: Readonly<Record<string, string>> | undefined;
|
|
932
1027
|
/** Ceiling on each install. Defaults to four minutes. Expiry fails `InstallFailed` with a message naming the manager and this duration. */
|
|
933
1028
|
readonly installTimeout?: Duration.Input | undefined;
|
|
1029
|
+
/**
|
|
1030
|
+
* Ceiling on each package's pack. Defaults to two minutes. Expiry fails
|
|
1031
|
+
* `PackFailed` with a message naming the package and this duration. Pass
|
|
1032
|
+
* the same value to {@link PackedInstall.timeoutBudget}.
|
|
1033
|
+
*/
|
|
1034
|
+
readonly packTimeout?: Duration.Input | undefined;
|
|
934
1035
|
}
|
|
935
1036
|
declare const PackedInstallError_base: Schema.Class<PackedInstallError, Schema.TaggedStruct<"PackedInstallError", {
|
|
936
1037
|
/** What failed. */
|
|
937
|
-
readonly reason: Schema.Literals<readonly ["UnsupportedPlatform", "NoManagerAvailable", "ManagerUnavailable", "UnknownPackage", "PackSourceMissing", "PackFailed", "UnresolvedProtocol", "InstallFailed", "MissingBin", "Discovery", "Io"]>;
|
|
1038
|
+
readonly reason: Schema.Literals<readonly ["UnsupportedPlatform", "NoManagerAvailable", "ManagerUnavailable", "UnknownPackage", "PackSourceMissing", "PackFailed", "UnresolvedProtocol", "InvalidOverride", "BinConflict", "InstallFailed", "MissingBin", "BinFailed", "UnownedBin", "Discovery", "Io"]>;
|
|
938
1039
|
/** One line, naming the package or manager involved. */
|
|
939
1040
|
readonly message: Schema.String;
|
|
940
1041
|
/** The package manager involved. */
|
|
@@ -952,6 +1053,44 @@ declare const PackedInstallError_base: Schema.Class<PackedInstallError, Schema.T
|
|
|
952
1053
|
* @public
|
|
953
1054
|
*/
|
|
954
1055
|
export declare class PackedInstallError extends PackedInstallError_base {}
|
|
1056
|
+
/**
|
|
1057
|
+
* Options for {@link InstalledConsumer.command}: how the bin's environment
|
|
1058
|
+
* and working directory are built.
|
|
1059
|
+
*
|
|
1060
|
+
* @public
|
|
1061
|
+
*/
|
|
1062
|
+
interface BinCommandOptions {
|
|
1063
|
+
/**
|
|
1064
|
+
* Variables layered over the environment the install ran under, such as an
|
|
1065
|
+
* `XDG_DATA_HOME` inside `PackedInstallResult.scratch`. A key set to
|
|
1066
|
+
* `undefined` removes that variable. They are applied after the parent
|
|
1067
|
+
* manager's context is stripped, so an explicit entry always wins: pass
|
|
1068
|
+
* `CI: "true"` to run a bin as if under CI.
|
|
1069
|
+
*/
|
|
1070
|
+
readonly env?: Readonly<Record<string, string | undefined>> | undefined;
|
|
1071
|
+
/** The working directory. Defaults to the consumer's `directory`. */
|
|
1072
|
+
readonly cwd?: string | undefined;
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Options for {@link InstalledConsumer.runBin}.
|
|
1076
|
+
*
|
|
1077
|
+
* @public
|
|
1078
|
+
*/
|
|
1079
|
+
interface RunBinOptions extends BinCommandOptions {
|
|
1080
|
+
/** Ceiling on the run. Defaults to one minute. Expiry fails `BinFailed` with a message naming the bin and this duration. */
|
|
1081
|
+
readonly timeout?: Duration.Input | undefined;
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Which installed package a `node_modules/.bin` symlink resolves into.
|
|
1085
|
+
*
|
|
1086
|
+
* @public
|
|
1087
|
+
*/
|
|
1088
|
+
interface BinProvenance {
|
|
1089
|
+
/** The `name` from the nearest `package.json` above the link's target. */
|
|
1090
|
+
readonly package: string;
|
|
1091
|
+
/** The link's target, realpath'd. */
|
|
1092
|
+
readonly target: string;
|
|
1093
|
+
}
|
|
955
1094
|
declare const InstalledConsumer_base: Schema.Class<InstalledConsumer, Schema.Struct<{
|
|
956
1095
|
/** The package manager that installed it. */
|
|
957
1096
|
readonly manager: Schema.Literals<readonly ["npm", "pnpm", "yarn", "bun"]>;
|
|
@@ -959,6 +1098,18 @@ declare const InstalledConsumer_base: Schema.Class<InstalledConsumer, Schema.Str
|
|
|
959
1098
|
readonly managerVersion: Schema.String;
|
|
960
1099
|
/** The consumer project directory (realpath'd). */
|
|
961
1100
|
readonly directory: Schema.String;
|
|
1101
|
+
/**
|
|
1102
|
+
* The scrubbed environment the install ran under, which {@link InstalledConsumer.runBin}
|
|
1103
|
+
* starts from. Redacted, so printing a consumer never prints a token.
|
|
1104
|
+
* `PackedInstall.run` always sets it; a hand-made consumer without it runs
|
|
1105
|
+
* its bins under only `RunBinOptions.env`.
|
|
1106
|
+
*/
|
|
1107
|
+
readonly env: Schema.optionalKey<Schema.Redacted<Schema.$Record<Schema.String, Schema.String>>>;
|
|
1108
|
+
/**
|
|
1109
|
+
* The carrier the consumer depends on, which {@link InstalledConsumer.carrierCommand}
|
|
1110
|
+
* resolves bins through. `PackedInstall.run` always sets it.
|
|
1111
|
+
*/
|
|
1112
|
+
readonly carrier: Schema.optionalKey<Schema.String>;
|
|
962
1113
|
}>, {}>;
|
|
963
1114
|
/**
|
|
964
1115
|
* One scratch project, outside the workspace, with the carrier installed.
|
|
@@ -968,6 +1119,134 @@ declare const InstalledConsumer_base: Schema.Class<InstalledConsumer, Schema.Str
|
|
|
968
1119
|
export declare class InstalledConsumer extends InstalledConsumer_base {
|
|
969
1120
|
/** The installed bin `name`, in `node_modules/.bin`. POSIX: `PackedInstall` runs only there. */
|
|
970
1121
|
binPath(name: string): string;
|
|
1122
|
+
/**
|
|
1123
|
+
* Which installed package the `node_modules/.bin` entry `name` resolves into.
|
|
1124
|
+
*
|
|
1125
|
+
* @remarks
|
|
1126
|
+
* Under a flat layout (npm, bun, and Yarn with the `node-modules` linker
|
|
1127
|
+
* this run configures), a hoisted bin of the same name from another package
|
|
1128
|
+
* can shadow the carrier's, and running it cannot tell you which one ran.
|
|
1129
|
+
* Those managers write each `.bin` entry as a symlink, so this reads the
|
|
1130
|
+
* link, realpaths its target, and walks up to the nearest `package.json`
|
|
1131
|
+
* with a string `name`, staying inside the consumer directory (itself
|
|
1132
|
+
* realpath'd first, so a `/var` alias of `/private/var` or a trailing
|
|
1133
|
+
* slash does not move the bound). A manifest that is valid JSON but not an
|
|
1134
|
+
* object, or has no string `name` (a `dist/package.json` carrying only
|
|
1135
|
+
* `type`), is passed over. Assert `package` is your carrier.
|
|
1136
|
+
*
|
|
1137
|
+
* `undefined` means one thing: the entry exists and is not a symlink.
|
|
1138
|
+
* pnpm writes `.bin` entries as shell shims, whose target this does not
|
|
1139
|
+
* parse; its isolated layout links only the consumer's direct dependencies
|
|
1140
|
+
* at the top level, so a shadowing bin needs a direct dependency there.
|
|
1141
|
+
*
|
|
1142
|
+
* An entry that does not exist, or a link whose target does not, fails
|
|
1143
|
+
* `MissingBin`. A link into no named package inside the consumer fails
|
|
1144
|
+
* `UnownedBin`, naming the target. Any other read failure, a
|
|
1145
|
+
* `package.json` that is not JSON included, fails `Io`.
|
|
1146
|
+
*
|
|
1147
|
+
* @param name - The bin, as named in `node_modules/.bin`.
|
|
1148
|
+
*/
|
|
1149
|
+
binProvenance(name: string): Effect.Effect<BinProvenance | undefined, PackedInstallError, FileSystem.FileSystem | Path.Path>;
|
|
1150
|
+
/**
|
|
1151
|
+
* The command {@link InstalledConsumer.runBin} spawns for the bin `name`,
|
|
1152
|
+
* for a caller that drives the child itself, such as `McpProbe.initialize`
|
|
1153
|
+
* from `@effected/mcp/testing`.
|
|
1154
|
+
*
|
|
1155
|
+
* @remarks
|
|
1156
|
+
* {@link InstalledConsumer.binPath}, from the consumer's directory, under
|
|
1157
|
+
* the install's scrubbed environment with `options.env` layered over it
|
|
1158
|
+
* after the scrub, and nothing inherited beyond that. `runBin` builds its
|
|
1159
|
+
* command here and then ignores stdin; this one leaves stdin as the
|
|
1160
|
+
* spawner's default pipe, so a probe can write to it. Spawn it inside the
|
|
1161
|
+
* scope that ran `PackedInstall.run`: the scratch directory is removed
|
|
1162
|
+
* when that scope closes.
|
|
1163
|
+
*
|
|
1164
|
+
* @example
|
|
1165
|
+
* ```ts
|
|
1166
|
+
* import { McpProbe } from "@effected/mcp/testing";
|
|
1167
|
+
* import type { InstalledConsumer } from "@effected/workspaces/testing";
|
|
1168
|
+
*
|
|
1169
|
+
* declare const consumer: InstalledConsumer;
|
|
1170
|
+
* declare const xdg: string;
|
|
1171
|
+
*
|
|
1172
|
+
* const probe = McpProbe.initialize(consumer.command("my-tool-mcp", [], { env: { XDG_DATA_HOME: xdg } }));
|
|
1173
|
+
* ```
|
|
1174
|
+
*
|
|
1175
|
+
* @param name - The bin, as named in `node_modules/.bin`.
|
|
1176
|
+
* @param args - Its arguments.
|
|
1177
|
+
* @param options - Extra environment and working directory.
|
|
1178
|
+
*/
|
|
1179
|
+
command(name: string, args?: ReadonlyArray<string>, options?: BinCommandOptions): ChildProcess.StandardCommand;
|
|
1180
|
+
/**
|
|
1181
|
+
* Run the installed bin `name` to completion and collect what it wrote.
|
|
1182
|
+
*
|
|
1183
|
+
* @remarks
|
|
1184
|
+
* Spawns {@link InstalledConsumer.command} with stdin ignored: the bin
|
|
1185
|
+
* from the consumer's directory, under the install's scrubbed environment
|
|
1186
|
+
* with `options.env` layered over it after the scrub, and nothing
|
|
1187
|
+
* inherited beyond that. A
|
|
1188
|
+
* non-zero exit is a result, read from `exitCode`, never a failure. A bin
|
|
1189
|
+
* that cannot spawn, outlives `options.timeout` or floods its output fails
|
|
1190
|
+
* `BinFailed` naming the manager and the bin. Run it inside the scope that
|
|
1191
|
+
* ran `PackedInstall.run`: the scratch directory is removed when that
|
|
1192
|
+
* scope closes.
|
|
1193
|
+
*
|
|
1194
|
+
* @param name - The bin, as named in `node_modules/.bin`.
|
|
1195
|
+
* @param args - Its arguments.
|
|
1196
|
+
* @param options - Extra environment, working directory and ceiling.
|
|
1197
|
+
*/
|
|
1198
|
+
runBin(name: string, args?: ReadonlyArray<string>, options?: RunBinOptions): Effect.Effect<CommandOutput, PackedInstallError, ChildProcessSpawner.ChildProcessSpawner>;
|
|
1199
|
+
/**
|
|
1200
|
+
* The command that runs the CARRIER's own bin `name`, whichever package
|
|
1201
|
+
* won the `node_modules/.bin` slot.
|
|
1202
|
+
*
|
|
1203
|
+
* @remarks
|
|
1204
|
+
* With `allowSharedBins`, a flat layout (npm, bun, Yarn's `node-modules`
|
|
1205
|
+
* linker) can link a front end's same-named bin into `.bin`, so
|
|
1206
|
+
* {@link InstalledConsumer.runBin} proves only that SOME package's bin runs.
|
|
1207
|
+
* This reads the carrier's installed `node_modules/<carrier>/package.json`,
|
|
1208
|
+
* takes `name` from its `bin` map (a `bin` string answers to the unscoped
|
|
1209
|
+
* package name), and returns `node <that file> ...args`: the carrier's shim
|
|
1210
|
+
* itself, run by the `node` on the environment's `PATH`.
|
|
1211
|
+
*
|
|
1212
|
+
* Running it through `node` has three limits. It assumes a Node script, so
|
|
1213
|
+
* a non-Node shim misruns. It drops any flags in the shim's shebang (a
|
|
1214
|
+
* `#!/usr/bin/env -S node --enable-source-maps` runs without them). And it
|
|
1215
|
+
* bypasses the file's executable bit, so under shared bins it does not
|
|
1216
|
+
* prove the carrier's shim is executable, only that it runs: the manager
|
|
1217
|
+
* that linked a front end's bin into the slot may never have made the
|
|
1218
|
+
* carrier's target executable. Environment and working directory are built as
|
|
1219
|
+
* {@link InstalledConsumer.command} builds them, stdin left open for a probe
|
|
1220
|
+
* such as `McpProbe.initialize`.
|
|
1221
|
+
*
|
|
1222
|
+
* A consumer with no `carrier`, a carrier that is not installed or does not
|
|
1223
|
+
* declare `name`, or a declared file that does not exist fails
|
|
1224
|
+
* `MissingBin`; a manifest that cannot be read or is not JSON fails `Io`.
|
|
1225
|
+
*
|
|
1226
|
+
* @param name - The bin, as the carrier's `bin` map names it.
|
|
1227
|
+
* @param args - Its arguments.
|
|
1228
|
+
* @param options - Extra environment and working directory.
|
|
1229
|
+
*/
|
|
1230
|
+
carrierCommand(name: string, args?: ReadonlyArray<string>, options?: BinCommandOptions): Effect.Effect<ChildProcess.StandardCommand, PackedInstallError, FileSystem.FileSystem | Path.Path>;
|
|
1231
|
+
/**
|
|
1232
|
+
* Run the carrier's own bin `name` to completion, whichever package won
|
|
1233
|
+
* the `node_modules/.bin` slot.
|
|
1234
|
+
*
|
|
1235
|
+
* @remarks
|
|
1236
|
+
* Spawns {@link InstalledConsumer.carrierCommand} with stdin ignored, and
|
|
1237
|
+
* reports as {@link InstalledConsumer.runBin} does: a non-zero exit is a
|
|
1238
|
+
* result, and a bin that cannot spawn or outlives `options.timeout` (one
|
|
1239
|
+
* minute by default) fails `BinFailed`. Use it beside `runBin` under
|
|
1240
|
+
* `allowSharedBins`: `runBin` proves what a user typing the bin name gets,
|
|
1241
|
+
* this proves the carrier's shim itself works, within `carrierCommand`'s
|
|
1242
|
+
* limits (a Node script, shebang flags dropped, executable bit not
|
|
1243
|
+
* checked).
|
|
1244
|
+
*
|
|
1245
|
+
* @param name - The bin, as the carrier's `bin` map names it.
|
|
1246
|
+
* @param args - Its arguments.
|
|
1247
|
+
* @param options - Extra environment, working directory and ceiling.
|
|
1248
|
+
*/
|
|
1249
|
+
runCarrierBin(name: string, args?: ReadonlyArray<string>, options?: RunBinOptions): Effect.Effect<CommandOutput, PackedInstallError, FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner>;
|
|
971
1250
|
}
|
|
972
1251
|
declare const PackedInstallResult_base: Schema.Class<PackedInstallResult, Schema.Struct<{
|
|
973
1252
|
/** One consumer per available manager, in the order requested. */
|
|
@@ -976,6 +1255,13 @@ declare const PackedInstallResult_base: Schema.Class<PackedInstallResult, Schema
|
|
|
976
1255
|
readonly unavailable: Schema.$Array<Schema.Literals<readonly ["npm", "pnpm", "yarn", "bun"]>>;
|
|
977
1256
|
/** Every packed package: name to absolute tarball path. */
|
|
978
1257
|
readonly tarballs: Schema.$Record<Schema.String, Schema.String>;
|
|
1258
|
+
/**
|
|
1259
|
+
* The scratch root (realpath'd) holding the tarballs and every consumer.
|
|
1260
|
+
* It is removed when the scope that ran `PackedInstall.run` closes, so a
|
|
1261
|
+
* directory made under it, such as an `XDG_DATA_HOME` for the bins, is
|
|
1262
|
+
* cleaned up with it.
|
|
1263
|
+
*/
|
|
1264
|
+
readonly scratch: Schema.String;
|
|
979
1265
|
}>, {}>;
|
|
980
1266
|
/**
|
|
981
1267
|
* What a packed install produced.
|
|
@@ -983,6 +1269,32 @@ declare const PackedInstallResult_base: Schema.Class<PackedInstallResult, Schema
|
|
|
983
1269
|
* @public
|
|
984
1270
|
*/
|
|
985
1271
|
export declare class PackedInstallResult extends PackedInstallResult_base {}
|
|
1272
|
+
/**
|
|
1273
|
+
* What {@link PackedInstall.timeoutBudget} adds up.
|
|
1274
|
+
*
|
|
1275
|
+
* @public
|
|
1276
|
+
*/
|
|
1277
|
+
interface PackedInstallBudget {
|
|
1278
|
+
/** The managers the run requests; one listed twice counts once. */
|
|
1279
|
+
readonly managers: ReadonlyArray<PackageManagerName>;
|
|
1280
|
+
/** The run's `installTimeout`. Defaults to four minutes, as the run does. */
|
|
1281
|
+
readonly installTimeout?: Duration.Input | undefined;
|
|
1282
|
+
/** The run's `packTimeout`. Defaults to two minutes, as the run does. */
|
|
1283
|
+
readonly packTimeout?: Duration.Input | undefined;
|
|
1284
|
+
/**
|
|
1285
|
+
* What the run packs: a count, or the names {@link PackedInstall.closure}
|
|
1286
|
+
* returns for the run's options, which are exactly the keys of
|
|
1287
|
+
* `PackedInstallResult.tarballs`.
|
|
1288
|
+
*/
|
|
1289
|
+
readonly packages: number | ReadonlyArray<string>;
|
|
1290
|
+
/**
|
|
1291
|
+
* What the test does with each consumer afterwards, such as its bin runs'
|
|
1292
|
+
* ceilings. Defaults to one minute: one {@link InstalledConsumer.runBin}
|
|
1293
|
+
* at its default ceiling. Add a minute per further `runBin`, and pass
|
|
1294
|
+
* `"0 seconds"` for a test that only installs.
|
|
1295
|
+
*/
|
|
1296
|
+
readonly perConsumer?: Duration.Input | undefined;
|
|
1297
|
+
}
|
|
986
1298
|
/**
|
|
987
1299
|
* Prove a carrier's bins reach a consumer that is not part of the workspace,
|
|
988
1300
|
* once per available package manager.
|
|
@@ -994,8 +1306,8 @@ export declare class PackedInstallResult extends PackedInstallResult_base {}
|
|
|
994
1306
|
* pin in the repo cannot refuse it), it writes a consumer whose only direct
|
|
995
1307
|
* dependency is the carrier tarball, steers the closure to its tarballs
|
|
996
1308
|
* through the manager's own override field, pins the probed version (as
|
|
997
|
-
* `packageManager`, or for pnpm as `devEngines.packageManager` with
|
|
998
|
-
* "ignore"`, since pnpm resolves a `packageManager` pin from the registry even
|
|
1309
|
+
* `packageManager`, or for pnpm as `devEngines.packageManager` with
|
|
1310
|
+
* `onFail: "ignore"`, since pnpm resolves a `packageManager` pin from the registry even
|
|
999
1311
|
* when it names the running version), installs with lifecycle scripts skipped,
|
|
1000
1312
|
* and checks every
|
|
1001
1313
|
* expected bin is present and executable. A packed manifest that still
|
|
@@ -1003,18 +1315,18 @@ export declare class PackedInstallResult extends PackedInstallResult_base {}
|
|
|
1003
1315
|
* fails `UnresolvedProtocol` before any install.
|
|
1004
1316
|
*
|
|
1005
1317
|
* It asserts nothing about what the bins DO: run them from the test through
|
|
1006
|
-
* `InstalledConsumer.
|
|
1007
|
-
* `@effected/mcp/testing`, inside the same
|
|
1008
|
-
*
|
|
1318
|
+
* `InstalledConsumer.runBin`, or for an MCP bin through `McpProbe` from
|
|
1319
|
+
* `@effected/mcp/testing` at `InstalledConsumer.binPath`, inside the same
|
|
1320
|
+
* scope, because the scratch directory (`PackedInstallResult.scratch`) is
|
|
1321
|
+
* removed when the scope closes. Size the test's outer timeout with
|
|
1322
|
+
* {@link PackedInstall.timeoutBudget}. POSIX only.
|
|
1009
1323
|
*
|
|
1010
1324
|
* @example
|
|
1011
1325
|
* ```ts
|
|
1012
1326
|
* import { NodeServices } from "@effect/platform-node";
|
|
1013
|
-
* import { Run } from "@effected/commands";
|
|
1014
1327
|
* import { Workspaces } from "@effected/workspaces";
|
|
1015
1328
|
* import { PackedInstall } from "@effected/workspaces/testing";
|
|
1016
1329
|
* import { Effect, Layer } from "effect";
|
|
1017
|
-
* import { ChildProcess } from "effect/unstable/process";
|
|
1018
1330
|
*
|
|
1019
1331
|
* const Live = Workspaces.layer({ cwd: "/repo" }).pipe(Layer.provideMerge(NodeServices.layer));
|
|
1020
1332
|
*
|
|
@@ -1028,12 +1340,11 @@ export declare class PackedInstallResult extends PackedInstallResult_base {}
|
|
|
1028
1340
|
* bins: ["my-tool"],
|
|
1029
1341
|
* env: process.env,
|
|
1030
1342
|
* });
|
|
1031
|
-
*
|
|
1343
|
+
* // Keep the tool's data inside the scratch root, removed with it.
|
|
1344
|
+
* const env = { XDG_DATA_HOME: `${result.scratch}/xdg` };
|
|
1032
1345
|
* for (const consumer of result.consumers) {
|
|
1033
|
-
* const
|
|
1034
|
-
*
|
|
1035
|
-
* );
|
|
1036
|
-
* console.log(consumer.manager, version.trim());
|
|
1346
|
+
* const { stdout, exitCode } = yield* consumer.runBin("my-tool", ["--version"], { env });
|
|
1347
|
+
* console.log(consumer.manager, exitCode, stdout.trim());
|
|
1037
1348
|
* }
|
|
1038
1349
|
* }).pipe(Effect.scoped, Effect.provide(Live));
|
|
1039
1350
|
* ```
|
|
@@ -1044,8 +1355,115 @@ export declare class PackedInstall {
|
|
|
1044
1355
|
private constructor();
|
|
1045
1356
|
/** `env` without undefined values or the parent manager's context: the environment the installs run under. Reuse it to run the installed bins. */
|
|
1046
1357
|
static readonly scrubEnv: (env: Readonly<Record<string, string | undefined>>) => Record<string, string>;
|
|
1358
|
+
/**
|
|
1359
|
+
* The ceiling a test's outer timeout should cover so that every one of the
|
|
1360
|
+
* run's own ceilings fires first, as a named `PackedInstallError`, rather
|
|
1361
|
+
* than the outer guard's `TimeoutError` that names nothing.
|
|
1362
|
+
*
|
|
1363
|
+
* @remarks
|
|
1364
|
+
* The worst case of the run's own ceilings, taken in sequence as the run
|
|
1365
|
+
* takes them: each manager's `--version` probe (30 seconds), install
|
|
1366
|
+
* (`installTimeout`) and `perConsumer` (one minute by default, one `runBin`
|
|
1367
|
+
* at its default ceiling), plus each package's pack
|
|
1368
|
+
* (`packTimeout`, two minutes by default) and manifest read (30 seconds),
|
|
1369
|
+
* plus 30 seconds for the untimed steps and one minute for cleanup:
|
|
1370
|
+
* removing the scratch root when the scope closes, and killing a child
|
|
1371
|
+
* whose ceiling interrupted it. It reads the same defaults the run does.
|
|
1372
|
+
* Pass `packages` as the names {@link PackedInstall.closure} returns to
|
|
1373
|
+
* size it before the run. A vitest test timeout must sit above it, since
|
|
1374
|
+
* vitest's own guard should not pre-empt the Effect's.
|
|
1375
|
+
*
|
|
1376
|
+
* @example
|
|
1377
|
+
* ```ts
|
|
1378
|
+
* import { PackedInstall } from "@effected/workspaces/testing";
|
|
1379
|
+
* import { Duration } from "effect";
|
|
1380
|
+
*
|
|
1381
|
+
* const budget = PackedInstall.timeoutBudget({
|
|
1382
|
+
* managers: ["npm", "pnpm", "yarn", "bun"],
|
|
1383
|
+
* installTimeout: "3 minutes",
|
|
1384
|
+
* packTimeout: "30 seconds",
|
|
1385
|
+
* packages: ["my-tool", "my-tool-cli", "my-tool-core"],
|
|
1386
|
+
* perConsumer: "2 minutes",
|
|
1387
|
+
* });
|
|
1388
|
+
* console.log(Duration.format(budget));
|
|
1389
|
+
* // => 26m 30s
|
|
1390
|
+
* ```
|
|
1391
|
+
*/
|
|
1392
|
+
static readonly timeoutBudget: (budget: PackedInstallBudget) => Duration.Duration;
|
|
1393
|
+
/**
|
|
1394
|
+
* The packages a run packs, in the order it packs them, without packing
|
|
1395
|
+
* anything: the carrier, the rest of the closure, then the `overrides`
|
|
1396
|
+
* packages by name.
|
|
1397
|
+
*
|
|
1398
|
+
* @remarks
|
|
1399
|
+
* The same implementation {@link PackedInstall.run} plans with, so for the
|
|
1400
|
+
* same workspace and options the result equals the keys of
|
|
1401
|
+
* `PackedInstallResult.tarballs`, in order. `options.closure` defaults to
|
|
1402
|
+
* `"auto"`, and the run's own options object is accepted as it is. It
|
|
1403
|
+
* fails as the run would before packing: `UnknownPackage`, `Discovery`,
|
|
1404
|
+
* `InvalidOverride` or `Io`. Use it to size
|
|
1405
|
+
* {@link PackedInstall.timeoutBudget} before the test is declared.
|
|
1406
|
+
*
|
|
1407
|
+
* @example
|
|
1408
|
+
* ```ts
|
|
1409
|
+
* import { NodeServices } from "@effect/platform-node";
|
|
1410
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
1411
|
+
* import { PackedInstall } from "@effected/workspaces/testing";
|
|
1412
|
+
* import { Effect, Layer } from "effect";
|
|
1413
|
+
*
|
|
1414
|
+
* const Live = Workspaces.layer({ cwd: "/repo" }).pipe(Layer.provideMerge(NodeServices.layer));
|
|
1415
|
+
* const packages = await Effect.runPromise(PackedInstall.closure("my-tool").pipe(Effect.provide(Live)));
|
|
1416
|
+
* const budget = PackedInstall.timeoutBudget({ managers: ["npm", "pnpm"], packages });
|
|
1417
|
+
* ```
|
|
1418
|
+
*
|
|
1419
|
+
* @param carrier - The carrier, as the run's `carrier`.
|
|
1420
|
+
* @param options - The closure and override options, as the run takes them.
|
|
1421
|
+
*/
|
|
1422
|
+
static readonly closure: (carrier: string, options?: PackedInstallClosureOptions) => Effect.Effect<ReadonlyArray<string>, PackedInstallError, WorkspaceDiscovery | FileSystem.FileSystem | Path.Path>;
|
|
1423
|
+
/**
|
|
1424
|
+
* {@link PackedInstall.timeoutBudget} for exactly the run `options`
|
|
1425
|
+
* describes, planned from the options themselves.
|
|
1426
|
+
*
|
|
1427
|
+
* @remarks
|
|
1428
|
+
* Plans the closure with {@link PackedInstall.closure} (the run's own
|
|
1429
|
+
* planner, `overrides` and `workspaceOverrides` included) and takes
|
|
1430
|
+
* `managers`, `installTimeout` and `packTimeout` from the same object, so
|
|
1431
|
+
* nothing is written twice. `perConsumer` is the one input a run's options
|
|
1432
|
+
* do not carry. It is an Effect because planning reads the workspace; in a
|
|
1433
|
+
* vitest file, await it at module evaluation, since a test's timeout is
|
|
1434
|
+
* fixed when the test is declared. Fails as `closure` does.
|
|
1435
|
+
*
|
|
1436
|
+
* @example
|
|
1437
|
+
* ```ts
|
|
1438
|
+
* import { NodeServices } from "@effect/platform-node";
|
|
1439
|
+
* import { Workspaces } from "@effected/workspaces";
|
|
1440
|
+
* import type { PackedInstallOptions } from "@effected/workspaces/testing";
|
|
1441
|
+
* import { PackedInstall } from "@effected/workspaces/testing";
|
|
1442
|
+
* import { Duration, Effect, Layer } from "effect";
|
|
1443
|
+
*
|
|
1444
|
+
* const Live = Workspaces.layer({ cwd: "/repo" }).pipe(Layer.provideMerge(NodeServices.layer));
|
|
1445
|
+
* const RUN: PackedInstallOptions = {
|
|
1446
|
+
* carrier: "my-tool",
|
|
1447
|
+
* closure: "auto",
|
|
1448
|
+
* managers: ["npm", "pnpm"],
|
|
1449
|
+
* bins: ["my-tool"],
|
|
1450
|
+
* env: process.env,
|
|
1451
|
+
* packTimeout: "30 seconds",
|
|
1452
|
+
* };
|
|
1453
|
+
* const budget = await Effect.runPromise(
|
|
1454
|
+
* PackedInstall.timeoutBudgetFor(RUN, { perConsumer: "1 minute" }).pipe(Effect.provide(Live)),
|
|
1455
|
+
* );
|
|
1456
|
+
* const TEST_TIMEOUT_MS = Duration.toMillis(budget) + 60_000;
|
|
1457
|
+
* ```
|
|
1458
|
+
*
|
|
1459
|
+
* @param options - The run's options, the same object passed to `run`.
|
|
1460
|
+
* @param extra - What the test does with each consumer afterwards.
|
|
1461
|
+
*/
|
|
1462
|
+
static readonly timeoutBudgetFor: (options: PackedInstallOptions, extra?: {
|
|
1463
|
+
readonly perConsumer?: Duration.Input | undefined;
|
|
1464
|
+
}) => Effect.Effect<Duration.Duration, PackedInstallError, WorkspaceDiscovery | FileSystem.FileSystem | Path.Path>;
|
|
1047
1465
|
/** Pack, then install under every available manager. */
|
|
1048
|
-
static readonly run: (options: PackedInstallOptions) => Effect.Effect<PackedInstallResult, PackedInstallError,
|
|
1466
|
+
static readonly run: (options: PackedInstallOptions) => Effect.Effect<PackedInstallResult, PackedInstallError, ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem | Path.Path | import("effect/Scope").Scope | WorkspaceDiscovery>;
|
|
1049
1467
|
}
|
|
1050
1468
|
//#endregion
|
|
1051
1469
|
//#region src/SourceBoundary.d.ts
|
|
@@ -1068,6 +1486,17 @@ export declare class PackedInstall {
|
|
|
1068
1486
|
* core's `Console` service. `{ forbidImports }` forbids an import whose specifier
|
|
1069
1487
|
* equals an entry, is a subpath of one, or starts with an entry's text before a
|
|
1070
1488
|
* trailing `*` (so `"node:*"` and `"@effect/platform*"` are prefixes).
|
|
1489
|
+
* `{ forbidTokens }` forbids each entry's exact text wherever it appears in
|
|
1490
|
+
* code (see {@link SourceBoundary.check} for how a token is matched).
|
|
1491
|
+
*
|
|
1492
|
+
* To confine a token to named files rather than forbid it everywhere, forbid
|
|
1493
|
+
* it and waive the rule for those files through
|
|
1494
|
+
* {@link ScanOptions.allowRules}: the `"forbidTokens"` key names the files, and
|
|
1495
|
+
* `SourceScan.waived` then reports every use inside them, so a
|
|
1496
|
+
* confinement that no longer matches anything shows up there. The house rule
|
|
1497
|
+
* "`process.env.__PACKAGE_VERSION__` only in `version.ts`" is
|
|
1498
|
+
* `rules: [{ forbidTokens: ["process.env.__PACKAGE_VERSION__"] }]` with
|
|
1499
|
+
* `allowRules: { forbidTokens: ["version.ts"] }`.
|
|
1071
1500
|
*
|
|
1072
1501
|
* `"node:*"` matches only the `node:` spelling: a bare built-in such as `"fs"`
|
|
1073
1502
|
* is not caught. To forbid both, spread Node's own list from the test file,
|
|
@@ -1080,6 +1509,8 @@ export declare class PackedInstall {
|
|
|
1080
1509
|
*/
|
|
1081
1510
|
type BoundaryRule = "process" | "node:process" | "stdout-write" | "console" | "console-stdout" | {
|
|
1082
1511
|
readonly forbidImports: ReadonlyArray<string>;
|
|
1512
|
+
} | {
|
|
1513
|
+
readonly forbidTokens: ReadonlyArray<string>;
|
|
1083
1514
|
};
|
|
1084
1515
|
/**
|
|
1085
1516
|
* Options for the `process` rule.
|
|
@@ -1090,7 +1521,9 @@ interface ReferenceOptions {
|
|
|
1090
1521
|
/**
|
|
1091
1522
|
* Exact tokens exempt from the `process` rule, each starting at the
|
|
1092
1523
|
* reference (`"process.env.MY_CONSTANT"`). `process.env.__PACKAGE_VERSION__`
|
|
1093
|
-
* is always exempt: the bundler substitutes it at build time.
|
|
1524
|
+
* is always exempt: the bundler substitutes it at build time. To hold such a
|
|
1525
|
+
* token to named files, forbid it with a `{ forbidTokens }` rule as well (see
|
|
1526
|
+
* {@link BoundaryRule}).
|
|
1094
1527
|
*/
|
|
1095
1528
|
readonly ignoreTokens?: ReadonlyArray<string> | undefined;
|
|
1096
1529
|
}
|
|
@@ -1117,8 +1550,8 @@ declare const Offence_base: Schema.Class<Offence, Schema.Struct<{
|
|
|
1117
1550
|
/** The 1-based column, in UTF-16 code units. */
|
|
1118
1551
|
readonly column: Schema.Number;
|
|
1119
1552
|
/** The rule broken. */
|
|
1120
|
-
readonly rule: Schema.Literals<readonly ["process", "node:process", "stdout-write", "console", "console-stdout", "forbidImports"]>;
|
|
1121
|
-
/** What matched: the identifier, the call,
|
|
1553
|
+
readonly rule: Schema.Literals<readonly ["process", "node:process", "stdout-write", "console", "console-stdout", "forbidImports", "forbidTokens"]>;
|
|
1554
|
+
/** What matched: the identifier, the call, the import specifier, or the token. */
|
|
1122
1555
|
readonly detail: Schema.String;
|
|
1123
1556
|
}>, {}>;
|
|
1124
1557
|
/**
|
|
@@ -1132,7 +1565,8 @@ export declare class Offence extends Offence_base {
|
|
|
1132
1565
|
}
|
|
1133
1566
|
/**
|
|
1134
1567
|
* The rule an {@link Offence} names: every string {@link BoundaryRule}, plus
|
|
1135
|
-
* `"forbidImports"` for any `{ forbidImports }` rule
|
|
1568
|
+
* `"forbidImports"` for any `{ forbidImports }` rule and `"forbidTokens"` for
|
|
1569
|
+
* any `{ forbidTokens }` rule. These are the keys of
|
|
1136
1570
|
* {@link ScanOptions.allowRules}.
|
|
1137
1571
|
*
|
|
1138
1572
|
* @public
|
|
@@ -1178,8 +1612,10 @@ interface ScanOptions extends ReferenceOptions {
|
|
|
1178
1612
|
* rule. A matching file is still checked against every other rule, and
|
|
1179
1613
|
* each offence a glob waives is reported in `SourceScan.waived`
|
|
1180
1614
|
* rather than dropped. The `"forbidImports"` key covers every
|
|
1181
|
-
* `{ forbidImports }` rule
|
|
1182
|
-
* rule,
|
|
1615
|
+
* `{ forbidImports }` rule, and the `"forbidTokens"` key every
|
|
1616
|
+
* `{ forbidTokens }` rule, which is how a token is confined to named files.
|
|
1617
|
+
* A file `allow` matches is exempt from every rule, so nothing in it is
|
|
1618
|
+
* waived.
|
|
1183
1619
|
*/
|
|
1184
1620
|
readonly allowRules?: { readonly [R in OffenceRule]?: ReadonlyArray<string> | undefined; } | undefined;
|
|
1185
1621
|
/**
|
|
@@ -1221,6 +1657,10 @@ interface ScanOptions extends ReferenceOptions {
|
|
|
1221
1657
|
*
|
|
1222
1658
|
* - JSX text reads as code;
|
|
1223
1659
|
*
|
|
1660
|
+
* - a variable named `yield` or `await` in a sloppy-mode script reads as the
|
|
1661
|
+
* keyword, so a `/` after it opens a regex (module and strict code reserve
|
|
1662
|
+
* both words);
|
|
1663
|
+
*
|
|
1224
1664
|
* - `forbidImports: ["node:*"]` does not catch a bare built-in such as
|
|
1225
1665
|
* `"fs"` (see {@link BoundaryRule}).
|
|
1226
1666
|
*
|
|
@@ -1250,7 +1690,22 @@ export declare class SourceBoundary {
|
|
|
1250
1690
|
static readonly importSpecifiers: (text: string) => ReadonlyArray<string>;
|
|
1251
1691
|
/** Whether `text` imports the Node built-in `module`, in either its `node:` or bare form. */
|
|
1252
1692
|
static readonly importsNode: (text: string, module: string) => boolean;
|
|
1253
|
-
/**
|
|
1693
|
+
/**
|
|
1694
|
+
* Every place `text` breaks one of `rules`, in source order, attributed to `file`.
|
|
1695
|
+
*
|
|
1696
|
+
* @remarks
|
|
1697
|
+
* A `{ forbidTokens }` entry matches its exact text in code only: comments,
|
|
1698
|
+
* strings, template text and regex bodies are blanked first, so a token that
|
|
1699
|
+
* itself contains a string literal never matches, and whitespace inside a
|
|
1700
|
+
* token must match byte for byte. A token that starts with an identifier
|
|
1701
|
+
* character does not match inside a longer identifier or after `#`, and one
|
|
1702
|
+
* that ends with one does not match when an identifier character follows.
|
|
1703
|
+
* A member access is still a match: `globalThis.process.env.X` contains the
|
|
1704
|
+
* token `process.env.X`. A token listed twice is reported once. A token with
|
|
1705
|
+
* no identifier character at either end (`"=>"`, `"?."`) has no edge guard,
|
|
1706
|
+
* so it matches inside longer punctuation, and consecutive matches of it can
|
|
1707
|
+
* overlap.
|
|
1708
|
+
*/
|
|
1254
1709
|
static readonly check: (file: string, text: string, rules: ReadonlyArray<BoundaryRule>, options?: ReferenceOptions) => ReadonlyArray<Offence>;
|
|
1255
1710
|
/**
|
|
1256
1711
|
* Check every source file under `root` against `rules`.
|
|
@@ -1378,5 +1833,5 @@ export declare class WorkspaceLayering {
|
|
|
1378
1833
|
static readonly checkWorkspace: (policy: LayerPolicy) => Effect.Effect<LayeringReport, WorkspaceDiscoveryFailure, WorkspaceDiscovery>;
|
|
1379
1834
|
}
|
|
1380
1835
|
//#endregion
|
|
1381
|
-
export type { BoundaryFixture, BoundaryRule, DependencyDiff, FindWorkspaceRootOptions, LayeringGraph, OffenceRule, PackSource, PackedInstallOptions, ReferenceOptions, ScanOptions, WorkspaceDiscoveryFailure, WorkspaceDiscoveryOptions, WorkspaceDiscoveryShape, WorkspaceLookupFailure, WorkspaceRootShape };
|
|
1836
|
+
export type { BinCommandOptions, BinProvenance, BoundaryFixture, BoundaryRule, DependencyDiff, FindWorkspaceRootOptions, LayeringGraph, OffenceRule, PackSource, PackedInstallBudget, PackedInstallClosureOptions, PackedInstallOptions, ReferenceOptions, RunBinOptions, ScanOptions, WorkspaceDiscoveryFailure, WorkspaceDiscoveryOptions, WorkspaceDiscoveryShape, WorkspaceLookupFailure, WorkspaceRootShape };
|
|
1382
1837
|
//# sourceMappingURL=testing.d.ts.map
|