@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 +135 -15
- package/package.json +3 -3
- package/testing.d.ts +138 -7
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
stdin
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|