@effected/workspaces 0.31.0 → 0.32.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/PackedInstall.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { WorkspaceDiscovery } from "./WorkspaceDiscovery.js";
2
2
  import { PackageManagerName } from "./PackageManagerName.js";
3
3
  import { binConflict, binTargetOf, closureOf, consumerFiles, fileOverridesOf, installArgs, overridePath, readPackedManifest, scrubEnv, versionOf } from "./internal/packedInstallPlan.js";
4
- import { Duration, Effect, FileSystem, Option, Path, Redacted, Result, Schema } from "effect";
4
+ import { Config, Duration, Effect, FileSystem, Option, Path, Redacted, Result, Schema, Stream } from "effect";
5
5
  import { Yaml } from "@effected/yaml";
6
6
  import { Run } from "@effected/commands";
7
7
  import { ChildProcess } from "effect/process";
@@ -182,7 +182,9 @@ var InstalledConsumer = class extends Schema.Class("InstalledConsumer")({
182
182
  * Run the installed bin `name` to completion and collect what it wrote.
183
183
  *
184
184
  * @remarks
185
- * Spawns {@link InstalledConsumer.command} with stdin ignored: the bin
185
+ * Spawns {@link InstalledConsumer.command} with `options.stdin` as its
186
+ * input (the null device when omitted, so a bin never waits on an open
187
+ * pipe): the bin
186
188
  * from the consumer's directory, under the install's scrubbed environment
187
189
  * with `options.env` layered over it after the scrub, and nothing
188
190
  * inherited beyond that. A
@@ -194,10 +196,10 @@ var InstalledConsumer = class extends Schema.Class("InstalledConsumer")({
194
196
  *
195
197
  * @param name - The bin, as named in `node_modules/.bin`.
196
198
  * @param args - Its arguments.
197
- * @param options - Extra environment, working directory and ceiling.
199
+ * @param options - Extra environment, working directory, ceiling and stdin.
198
200
  */
199
201
  runBin(name, args = [], options = {}) {
200
- return collectBin(this.command(name, args, options), this.manager, name, options.timeout);
202
+ return collectBin(this.command(name, args, options), this.manager, name, options);
201
203
  }
202
204
  /**
203
205
  * The command that runs the CARRIER's own bin `name`, whichever package
@@ -271,7 +273,8 @@ var InstalledConsumer = class extends Schema.Class("InstalledConsumer")({
271
273
  * the `node_modules/.bin` slot.
272
274
  *
273
275
  * @remarks
274
- * Spawns {@link InstalledConsumer.carrierCommand} with stdin ignored, and
276
+ * Spawns {@link InstalledConsumer.carrierCommand} with `options.stdin` as
277
+ * its input (the null device when omitted), and
275
278
  * reports as {@link InstalledConsumer.runBin} does: a non-zero exit is a
276
279
  * result, and a bin that cannot spawn or outlives `options.timeout` (one
277
280
  * minute by default) fails `BinFailed`. Use it beside `runBin` under
@@ -282,11 +285,11 @@ var InstalledConsumer = class extends Schema.Class("InstalledConsumer")({
282
285
  *
283
286
  * @param name - The bin, as the carrier's `bin` map names it.
284
287
  * @param args - Its arguments.
285
- * @param options - Extra environment, working directory and ceiling.
288
+ * @param options - Extra environment, working directory, ceiling and stdin.
286
289
  */
287
290
  runCarrierBin(name, args = [], options = {}) {
288
291
  const manager = this.manager;
289
- return this.carrierCommand(name, args, options).pipe(Effect.flatMap((command) => collectBin(command, manager, name, options.timeout)));
292
+ return this.carrierCommand(name, args, options).pipe(Effect.flatMap((command) => collectBin(command, manager, name, options)));
290
293
  }
291
294
  };
292
295
  /** The install's scrubbed environment with the caller's entries layered on after the scrub: a deliberate CI=true must survive it. */
@@ -296,14 +299,24 @@ const layeredEnv = (consumer, options) => {
296
299
  else env[key] = value;
297
300
  return env;
298
301
  };
299
- /** Run a bin's command to completion, stdin ignored, mapping a failed spawn or an expired ceiling to `BinFailed`. */
300
- const collectBin = (command, manager, name, timeout = DEFAULT_BIN_TIMEOUT) => Run.collect(ChildProcess.make(command.command, command.args, {
301
- ...command.options,
302
- stdin: "ignore"
303
- }), { timeout }).pipe(Effect.mapError((cause) => failure("BinFailed", cause._tag === "CommandFailedError" && cause.kind === "timeout" ? `${manager}: ${name} timed out after ${describeDuration(timeout)}` : `${manager}: ${name} could not run`, {
304
- manager,
305
- cause
306
- })));
302
+ /** A bin's stdin as `ChildProcess` takes it: absent or `""` is the null device (immediate end of input), anything else is written and closed. */
303
+ const stdinOf = (stdin) => {
304
+ if (stdin === void 0 || stdin === "") return "ignore";
305
+ if (typeof stdin === "string") return Stream.make(new TextEncoder().encode(stdin));
306
+ if (stdin instanceof Uint8Array) return Stream.make(stdin);
307
+ return stdin;
308
+ };
309
+ /** Run a bin's command to completion with `options.stdin` as its input (the null device when omitted), mapping a failed spawn or an expired ceiling to `BinFailed`. */
310
+ const collectBin = (command, manager, name, options) => {
311
+ const timeout = options.timeout ?? DEFAULT_BIN_TIMEOUT;
312
+ return Run.collect(ChildProcess.make(command.command, command.args, {
313
+ ...command.options,
314
+ stdin: stdinOf(options.stdin)
315
+ }), { timeout }).pipe(Effect.mapError((cause) => failure("BinFailed", cause._tag === "CommandFailedError" && cause.kind === "timeout" ? `${manager}: ${name} timed out after ${describeDuration(timeout)}` : `${manager}: ${name} could not run`, {
316
+ manager,
317
+ cause
318
+ })));
319
+ };
307
320
  /**
308
321
  * What a packed install produced.
309
322
  *
@@ -594,6 +607,113 @@ var PackedInstall = class PackedInstall {
594
607
  ...options.packTimeout === void 0 ? {} : { packTimeout: options.packTimeout },
595
608
  ...extra.perConsumer === void 0 ? {} : { perConsumer: extra.perConsumer }
596
609
  })));
610
+ /**
611
+ * Whether the pack source {@link PackedInstall.run} would read is on disk,
612
+ * before any package manager is spawned.
613
+ *
614
+ * @remarks
615
+ * Plans the closure as the run does (`PackedInstall.closure`'s planner) and
616
+ * checks the `package.json` of each closure package under `packFrom`
617
+ * (default `{ directory: "dist/prod/npm/pkg" }`), the same file whose
618
+ * absence makes the run fail `PackSourceMissing`. Under `packFrom: "source"`
619
+ * the packer builds nothing of its own, so nothing is checked and the
620
+ * result is ready. A package a bundler's dev build produced is no
621
+ * substitute: the prod output exists only after the prod build, which a
622
+ * test job's usual `build:dev` pre-step does not run. Pair it with
623
+ * {@link PackedInstall.gate} to skip locally and fail under CI.
624
+ *
625
+ * Fails as `closure` does, plus `Io` when a path cannot be inspected.
626
+ *
627
+ * @example
628
+ * ```ts
629
+ * import { NodeServices } from "@effect/platform-node";
630
+ * import { Workspaces } from "@effected/workspaces";
631
+ * import { PackedInstall } from "@effected/workspaces/testing";
632
+ * import { Effect, Layer } from "effect";
633
+ *
634
+ * const Live = Workspaces.layer({ cwd: "/repo" }).pipe(Layer.provideMerge(NodeServices.layer));
635
+ * const preflight = await Effect.runPromise(
636
+ * PackedInstall.preflight({ carrier: "my-tool", closure: "auto" }).pipe(Effect.provide(Live)),
637
+ * );
638
+ * console.log(preflight.ready, preflight.missing);
639
+ * ```
640
+ *
641
+ * @param options - The run's carrier, closure, overrides and pack source.
642
+ */
643
+ static preflight = Effect.fn("PackedInstall.preflight")(function* (options) {
644
+ const fs = yield* FileSystem.FileSystem;
645
+ const path = yield* Path.Path;
646
+ const plan = yield* planClosure(options.carrier, options);
647
+ const source = options.packFrom ?? DEFAULT_PACK_FROM;
648
+ if (source === "source") return {
649
+ ready: true,
650
+ missing: []
651
+ };
652
+ const missing = [];
653
+ for (const pkg of plan.closure) {
654
+ const manifest = path.join(pkg.path, source.directory, "package.json");
655
+ if (!(yield* fs.exists(manifest).pipe(Effect.mapError((cause) => failure("Io", `could not inspect ${manifest}`, { cause }))))) missing.push(manifest);
656
+ }
657
+ return {
658
+ ready: missing.length === 0,
659
+ missing
660
+ };
661
+ });
662
+ /**
663
+ * Turns a {@link PackedInstallPreflight} into the suite's decision: run,
664
+ * skip locally, or fail under CI.
665
+ *
666
+ * @remarks
667
+ * `CI` is read through `Config`, so the ambient `ConfigProvider` decides —
668
+ * the process environment at a test file's top level, and whatever a test
669
+ * provides in a test; nothing here reads `process`. `CI` counts as set when
670
+ * it is present and not `""`, `"0"` or `"false"` (case-insensitive), and an
671
+ * unreadable `CI` counts as unset. Ready is always `"run"`;
672
+ * missing is `"skip"` off CI and `"fail"` on it, so a CI job that never ran
673
+ * the prod build cannot pass by silently skipping the proof. The message
674
+ * names every missing path. The test job must run the prod build
675
+ * (`build:prod` for a `@savvy-web/bundler` package) before the packed-install
676
+ * tests: a dev build leaves `dist/prod` absent.
677
+ *
678
+ * @example
679
+ * ```ts
680
+ * import { assert, describe, it } from "@effect/vitest";
681
+ * import { PackedInstall } from "@effected/workspaces/testing";
682
+ * import { Effect } from "effect";
683
+ *
684
+ * declare const preflight: { readonly ready: boolean; readonly missing: ReadonlyArray<string> };
685
+ * const gate = Effect.runSync(PackedInstall.gate(preflight));
686
+ *
687
+ * describe.runIf(gate.action === "run")("packed install", () => {
688
+ * it("installs", () => {});
689
+ * });
690
+ * describe.runIf(gate.action === "fail")("packed install prod build", () => {
691
+ * it("exists", () => assert.fail(gate.message));
692
+ * });
693
+ * ```
694
+ *
695
+ * @param preflight - What {@link PackedInstall.preflight} answered.
696
+ */
697
+ static gate = (preflight) => Effect.gen(function* () {
698
+ if (preflight.ready) return {
699
+ action: "run",
700
+ message: ""
701
+ };
702
+ const ci = yield* Config.option(Config.String("CI")).pipe(Effect.orElseSucceed(() => Option.none()));
703
+ const underCi = Option.isSome(ci) && ![
704
+ "",
705
+ "0",
706
+ "false"
707
+ ].includes(ci.value.toLowerCase());
708
+ const message = `the pack source is missing: ${preflight.missing.join(", ")}; run the prod build (build:prod) before the packed-install tests`;
709
+ return underCi ? {
710
+ action: "fail",
711
+ message: `${message}; it must not be skipped under CI`
712
+ } : {
713
+ action: "skip",
714
+ message
715
+ };
716
+ });
597
717
  /** Pack, then install under every available manager. */
598
718
  static run = Effect.fn("PackedInstall.run")(function* (options) {
599
719
  const fs = yield* FileSystem.FileSystem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/workspaces",
3
- "version": "0.31.0",
3
+ "version": "0.32.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": [
@@ -54,13 +54,13 @@
54
54
  "@effected/commands": "^0.11.0",
55
55
  "@effected/git": "^0.20.0",
56
56
  "@effected/glob": "^0.10.0",
57
- "@effected/jsonc": "^0.15.0",
57
+ "@effected/jsonc": "^0.15.1",
58
58
  "@effected/lockfiles": "^0.15.0",
59
59
  "@effected/npm": "^0.20.0",
60
60
  "@effected/package-json": "^0.20.0",
61
61
  "@effected/semver": "^0.11.0",
62
62
  "@effected/walker": "^0.15.0",
63
- "@effected/yaml": "^0.19.0",
63
+ "@effected/yaml": "^0.19.1",
64
64
  "@pnpm/catalogs.config": "^1100.0.5",
65
65
  "@pnpm/catalogs.protocol-parser": "^1100.0.0",
66
66
  "@pnpm/catalogs.resolver": "^1100.0.0",
package/testing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DependencyField, WorkspaceResolver } from "@effected/npm";
2
- import { Context, Duration, Effect, FileSystem, Layer, Option, Path, Schema } from "effect";
2
+ import { Context, Duration, Effect, FileSystem, Layer, Option, Path, PlatformError, Schema, Stream } from "effect";
3
3
  import { GlobPattern } from "@effected/glob";
4
4
  import { WorkspaceManifest } from "@effected/lockfiles";
5
5
  import { Package } from "@effected/package-json";
@@ -1030,7 +1030,12 @@ interface PackedInstallOptions extends PackedInstallClosureOptions {
1030
1030
  * `false`, so sharing is an explicit choice.
1031
1031
  */
1032
1032
  readonly allowSharedBins?: boolean | undefined;
1033
- /** The environment for every spawn: pass `process.env` from the test file. The parent manager's context is stripped. */
1033
+ /**
1034
+ * The environment for every spawn: pass `process.env` from the test file, the edge that supplies the host
1035
+ * environment. It is taken whole because the parent manager's context is stripped by prefix (`npm_*`,
1036
+ * `pnpm_config_*`, `yarn_*`), which `Config`, reading keys by name, cannot enumerate; read single keys such as
1037
+ * `CI` through `Config`.
1038
+ */
1034
1039
  readonly env: Readonly<Record<string, string | undefined>>;
1035
1040
  /**
1036
1041
  * Extra consumer dependencies, name to range or `file:` tarball spec.
@@ -1107,6 +1112,22 @@ interface BinCommandOptions {
1107
1112
  interface RunBinOptions extends BinCommandOptions {
1108
1113
  /** Ceiling on the run. Defaults to one minute. Expiry fails `BinFailed` with a message naming the bin and this duration. */
1109
1114
  readonly timeout?: Duration.Input | undefined;
1115
+ /**
1116
+ * What to write to the bin's stdin, which is then closed: a string (UTF-8
1117
+ * encoded), bytes, or a byte stream for input that must arrive in pieces,
1118
+ * such as a framed LSP session.
1119
+ *
1120
+ * @remarks
1121
+ * Omitted or `""`, the bin's stdin is the null device: it reads end of
1122
+ * input immediately, never an open pipe, so a bin that reads stdin exits
1123
+ * instead of hanging until the ceiling. Empty bytes and an empty stream
1124
+ * are sent as given: the bin sees an ended, empty input. A bin that answers
1125
+ * only after the input is complete is driven by this option; one that must
1126
+ * be interleaved with its output is driven through
1127
+ * {@link InstalledConsumer.command} or {@link InstalledConsumer.carrierCommand},
1128
+ * which leave stdin open.
1129
+ */
1130
+ readonly stdin?: string | Uint8Array | Stream.Stream<Uint8Array, PlatformError.PlatformError> | undefined;
1110
1131
  }
1111
1132
  /**
1112
1133
  * Which installed package a `node_modules/.bin` symlink resolves into.
@@ -1209,7 +1230,9 @@ export declare class InstalledConsumer extends InstalledConsumer_base {
1209
1230
  * Run the installed bin `name` to completion and collect what it wrote.
1210
1231
  *
1211
1232
  * @remarks
1212
- * Spawns {@link InstalledConsumer.command} with stdin ignored: the bin
1233
+ * Spawns {@link InstalledConsumer.command} with `options.stdin` as its
1234
+ * input (the null device when omitted, so a bin never waits on an open
1235
+ * pipe): the bin
1213
1236
  * from the consumer's directory, under the install's scrubbed environment
1214
1237
  * with `options.env` layered over it after the scrub, and nothing
1215
1238
  * inherited beyond that. A
@@ -1221,7 +1244,7 @@ export declare class InstalledConsumer extends InstalledConsumer_base {
1221
1244
  *
1222
1245
  * @param name - The bin, as named in `node_modules/.bin`.
1223
1246
  * @param args - Its arguments.
1224
- * @param options - Extra environment, working directory and ceiling.
1247
+ * @param options - Extra environment, working directory, ceiling and stdin.
1225
1248
  */
1226
1249
  runBin(name: string, args?: ReadonlyArray<string>, options?: RunBinOptions): Effect.Effect<CommandOutput, PackedInstallError, ChildProcessSpawner.ChildProcessSpawner>;
1227
1250
  /**
@@ -1261,7 +1284,8 @@ export declare class InstalledConsumer extends InstalledConsumer_base {
1261
1284
  * the `node_modules/.bin` slot.
1262
1285
  *
1263
1286
  * @remarks
1264
- * Spawns {@link InstalledConsumer.carrierCommand} with stdin ignored, and
1287
+ * Spawns {@link InstalledConsumer.carrierCommand} with `options.stdin` as
1288
+ * its input (the null device when omitted), and
1265
1289
  * reports as {@link InstalledConsumer.runBin} does: a non-zero exit is a
1266
1290
  * result, and a bin that cannot spawn or outlives `options.timeout` (one
1267
1291
  * minute by default) fails `BinFailed`. Use it beside `runBin` under
@@ -1272,7 +1296,7 @@ export declare class InstalledConsumer extends InstalledConsumer_base {
1272
1296
  *
1273
1297
  * @param name - The bin, as the carrier's `bin` map names it.
1274
1298
  * @param args - Its arguments.
1275
- * @param options - Extra environment, working directory and ceiling.
1299
+ * @param options - Extra environment, working directory, ceiling and stdin.
1276
1300
  */
1277
1301
  runCarrierBin(name: string, args?: ReadonlyArray<string>, options?: RunBinOptions): Effect.Effect<CommandOutput, PackedInstallError, FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner>;
1278
1302
  }
@@ -1323,6 +1347,43 @@ interface PackedInstallBudget {
1323
1347
  */
1324
1348
  readonly perConsumer?: Duration.Input | undefined;
1325
1349
  }
1350
+ /**
1351
+ * Options for {@link PackedInstall.preflight}: the run's closure and pack
1352
+ * source, so the check answers for exactly the run those options describe.
1353
+ *
1354
+ * @public
1355
+ */
1356
+ interface PackedInstallPreflightOptions extends PackedInstallClosureOptions {
1357
+ /** The carrier, as the run's `carrier`. */
1358
+ readonly carrier: string;
1359
+ /** Where the run packs from; defaults as the run's does, to `{ directory: "dist/prod/npm/pkg" }`. */
1360
+ readonly packFrom?: PackSource | undefined;
1361
+ }
1362
+ /**
1363
+ * Whether the pack source a run needs is on disk.
1364
+ *
1365
+ * @public
1366
+ */
1367
+ interface PackedInstallPreflight {
1368
+ /** `true` when every package the run packs has its pack source in place. */
1369
+ readonly ready: boolean;
1370
+ /** The absolute `package.json` paths that do not exist, in pack order; empty exactly when `ready`. */
1371
+ readonly missing: ReadonlyArray<string>;
1372
+ }
1373
+ /**
1374
+ * What a test suite should do about a {@link PackedInstallPreflight}.
1375
+ *
1376
+ * @public
1377
+ */
1378
+ interface PackedInstallGate {
1379
+ /**
1380
+ * `"run"` when the pack source is there; `"skip"` when it is missing and
1381
+ * the run is not under CI; `"fail"` when it is missing under CI.
1382
+ */
1383
+ readonly action: "run" | "skip" | "fail";
1384
+ /** Why, naming the missing paths and the build to run. Empty when `action` is `"run"`. */
1385
+ readonly message: string;
1386
+ }
1326
1387
  /**
1327
1388
  * Prove a carrier's bins reach a consumer that is not part of the workspace,
1328
1389
  * once per available package manager.
@@ -1490,6 +1551,76 @@ export declare class PackedInstall {
1490
1551
  static readonly timeoutBudgetFor: (options: PackedInstallOptions, extra?: {
1491
1552
  readonly perConsumer?: Duration.Input | undefined;
1492
1553
  }) => Effect.Effect<Duration.Duration, PackedInstallError, WorkspaceDiscovery | FileSystem.FileSystem | Path.Path>;
1554
+ /**
1555
+ * Whether the pack source {@link PackedInstall.run} would read is on disk,
1556
+ * before any package manager is spawned.
1557
+ *
1558
+ * @remarks
1559
+ * Plans the closure as the run does (`PackedInstall.closure`'s planner) and
1560
+ * checks the `package.json` of each closure package under `packFrom`
1561
+ * (default `{ directory: "dist/prod/npm/pkg" }`), the same file whose
1562
+ * absence makes the run fail `PackSourceMissing`. Under `packFrom: "source"`
1563
+ * the packer builds nothing of its own, so nothing is checked and the
1564
+ * result is ready. A package a bundler's dev build produced is no
1565
+ * substitute: the prod output exists only after the prod build, which a
1566
+ * test job's usual `build:dev` pre-step does not run. Pair it with
1567
+ * {@link PackedInstall.gate} to skip locally and fail under CI.
1568
+ *
1569
+ * Fails as `closure` does, plus `Io` when a path cannot be inspected.
1570
+ *
1571
+ * @example
1572
+ * ```ts
1573
+ * import { NodeServices } from "@effect/platform-node";
1574
+ * import { Workspaces } from "@effected/workspaces";
1575
+ * import { PackedInstall } from "@effected/workspaces/testing";
1576
+ * import { Effect, Layer } from "effect";
1577
+ *
1578
+ * const Live = Workspaces.layer({ cwd: "/repo" }).pipe(Layer.provideMerge(NodeServices.layer));
1579
+ * const preflight = await Effect.runPromise(
1580
+ * PackedInstall.preflight({ carrier: "my-tool", closure: "auto" }).pipe(Effect.provide(Live)),
1581
+ * );
1582
+ * console.log(preflight.ready, preflight.missing);
1583
+ * ```
1584
+ *
1585
+ * @param options - The run's carrier, closure, overrides and pack source.
1586
+ */
1587
+ static readonly preflight: (options: PackedInstallPreflightOptions) => Effect.Effect<PackedInstallPreflight, PackedInstallError, WorkspaceDiscovery | FileSystem.FileSystem | Path.Path>;
1588
+ /**
1589
+ * Turns a {@link PackedInstallPreflight} into the suite's decision: run,
1590
+ * skip locally, or fail under CI.
1591
+ *
1592
+ * @remarks
1593
+ * `CI` is read through `Config`, so the ambient `ConfigProvider` decides —
1594
+ * the process environment at a test file's top level, and whatever a test
1595
+ * provides in a test; nothing here reads `process`. `CI` counts as set when
1596
+ * it is present and not `""`, `"0"` or `"false"` (case-insensitive), and an
1597
+ * unreadable `CI` counts as unset. Ready is always `"run"`;
1598
+ * missing is `"skip"` off CI and `"fail"` on it, so a CI job that never ran
1599
+ * the prod build cannot pass by silently skipping the proof. The message
1600
+ * names every missing path. The test job must run the prod build
1601
+ * (`build:prod` for a `@savvy-web/bundler` package) before the packed-install
1602
+ * tests: a dev build leaves `dist/prod` absent.
1603
+ *
1604
+ * @example
1605
+ * ```ts
1606
+ * import { assert, describe, it } from "@effect/vitest";
1607
+ * import { PackedInstall } from "@effected/workspaces/testing";
1608
+ * import { Effect } from "effect";
1609
+ *
1610
+ * declare const preflight: { readonly ready: boolean; readonly missing: ReadonlyArray<string> };
1611
+ * const gate = Effect.runSync(PackedInstall.gate(preflight));
1612
+ *
1613
+ * describe.runIf(gate.action === "run")("packed install", () => {
1614
+ * it("installs", () => {});
1615
+ * });
1616
+ * describe.runIf(gate.action === "fail")("packed install prod build", () => {
1617
+ * it("exists", () => assert.fail(gate.message));
1618
+ * });
1619
+ * ```
1620
+ *
1621
+ * @param preflight - What {@link PackedInstall.preflight} answered.
1622
+ */
1623
+ static readonly gate: (preflight: PackedInstallPreflight) => Effect.Effect<PackedInstallGate>;
1493
1624
  /** Pack, then install under every available manager. */
1494
1625
  static readonly run: (options: PackedInstallOptions) => Effect.Effect<PackedInstallResult, PackedInstallError, ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem | Path.Path | import("effect/Scope").Scope | WorkspaceDiscovery>;
1495
1626
  }
@@ -1861,5 +1992,5 @@ export declare class WorkspaceLayering {
1861
1992
  static readonly checkWorkspace: (policy: LayerPolicy) => Effect.Effect<LayeringReport, WorkspaceDiscoveryFailure, WorkspaceDiscovery>;
1862
1993
  }
1863
1994
  //#endregion
1864
- export type { BinCommandOptions, BinProvenance, BoundaryFixture, BoundaryRule, DependencyDiff, FindWorkspaceRootOptions, LayeringGraph, OffenceRule, PackSource, PackedInstallBudget, PackedInstallClosureOptions, PackedInstallOptions, ReferenceOptions, RunBinOptions, ScanOptions, WorkspaceDiscoveryFailure, WorkspaceDiscoveryOptions, WorkspaceDiscoveryShape, WorkspaceLookupFailure, WorkspaceRootShape };
1995
+ export type { BinCommandOptions, BinProvenance, BoundaryFixture, BoundaryRule, DependencyDiff, FindWorkspaceRootOptions, LayeringGraph, OffenceRule, PackSource, PackedInstallBudget, PackedInstallClosureOptions, PackedInstallGate, PackedInstallOptions, PackedInstallPreflight, PackedInstallPreflightOptions, ReferenceOptions, RunBinOptions, ScanOptions, WorkspaceDiscoveryFailure, WorkspaceDiscoveryOptions, WorkspaceDiscoveryShape, WorkspaceLookupFailure, WorkspaceRootShape };
1865
1996
  //# sourceMappingURL=testing.d.ts.map