@effected/cli 0.26.0 → 0.28.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.
@@ -0,0 +1,141 @@
1
+ import { DiffDoc } from "../../render/DiffDoc.js";
2
+ import { CatalogMergeError, DriftError, GateError, NoConfigError, StaleError } from "./errors.js";
3
+ import { Console, Effect, Option } from "effect";
4
+ import { CliInteractive, CliRuntime, Doc } from "@effected/cli-kit";
5
+ import { ConfigLoadError, ConfigLoader, Report, SchemaRun, StepSummary } from "@effected/engine/schema";
6
+ import { Audience } from "@effected/env";
7
+
8
+ // src/commands/schema/execute.ts
9
+ /** `--format` as given, else what the audience reads: JSON for an agent, human text for a person and for CI. */
10
+ const resolveFormat = (format) => Effect.map(Audience, (audience) => Option.getOrElse(format, () => audience.kind === "agent" ? "json" : "human"));
11
+ /** Warned before the walk, because an error from the walk does not carry the mismatch. */
12
+ const warnVersionMismatch = (mismatch) => Effect.logWarning(`VERSION MISMATCH IGNORED (--ignore-version-mismatch): ${mismatch.message}`);
13
+ /** A missing config, with the way out; a load failure's first line, with the rest (a stack) kept at debug. */
14
+ const configFailures = (usage, config) => ({
15
+ ConfigNotFoundError: (error) => Effect.fail(CliRuntime.reported(new NoConfigError({
16
+ names: ConfigLoader.CONFIG_NAMES,
17
+ searched: error.searched,
18
+ explicit: Option.isSome(config),
19
+ usage
20
+ }), 2)),
21
+ ConfigLoadError: (error) => Effect.gen(function* () {
22
+ const [first = "", ...rest] = error.reason.split("\n");
23
+ if (rest.length > 0) yield* Effect.logDebug(rest.join("\n"));
24
+ return yield* Effect.fail(CliRuntime.reported(new ConfigLoadError({
25
+ path: error.path,
26
+ reason: first
27
+ }), 2));
28
+ })
29
+ });
30
+ const LINE_TOKENS = [
31
+ ["DRIFT", "error"],
32
+ ["GATE FAILED", "error"],
33
+ ["CATALOG BLOCKED", "error"],
34
+ ["would write", "warning"],
35
+ ["orphaned", "warning"],
36
+ ["written", "success"],
37
+ ["unchanged", "muted"],
38
+ ["held", "muted"]
39
+ ];
40
+ const paintedLine = (line) => {
41
+ const token = LINE_TOKENS.find(([prefix]) => line.startsWith(prefix))?.[1];
42
+ return token === void 0 ? Doc.text(line) : Doc.text(line, token);
43
+ };
44
+ /**
45
+ * The human report: the outcome lines, each changed document's diff, then the summary, which `Report.human` writes
46
+ * last and which stays at the bottom where a person's eye lands. `Doc.text` is data to every renderer, so a path or a
47
+ * diff line cannot repaint the terminal.
48
+ */
49
+ const humanDocument = (report) => {
50
+ const lines = Report.human(report);
51
+ const outcomes = lines.slice(0, -1);
52
+ const summary = lines.slice(-1);
53
+ return Doc.section(void 0, [
54
+ ...outcomes.length > 0 ? [Doc.lines(outcomes.map(paintedLine), { wrap: false })] : [],
55
+ ...report.schemas.flatMap((schema) => schema.diff !== void 0 ? [DiffDoc.render(schema.path, schema.diff)] : []),
56
+ Doc.lines(summary.map(paintedLine), { wrap: false })
57
+ ]);
58
+ };
59
+ const emit = (report, format, versionMismatch) => Effect.gen(function* () {
60
+ if (format === "json") {
61
+ yield* Console.log(Report.json(report, versionMismatch));
62
+ for (const line of Report.human(report)) yield* Effect.logInfo(line);
63
+ } else yield* Doc.print([humanDocument(report)]);
64
+ for (const line of Report.warnings(report)) yield* Effect.logWarning(line);
65
+ });
66
+ const failOnVerdict = (result) => {
67
+ const { report, verdict } = result;
68
+ const merged = report.catalog?.merged;
69
+ switch (verdict) {
70
+ case "ok": return Effect.void;
71
+ case "gate-failed": {
72
+ const count = report.schemas.filter((schema) => schema.outcome === "gate-failed").length;
73
+ return Effect.fail(CliRuntime.reported(new GateError({ count }), 1));
74
+ }
75
+ case "drift": {
76
+ const drifted = report.schemas.filter((schema) => schema.verdict === "drift").map((schema) => ({
77
+ $id: schema.$id,
78
+ change: schema.change,
79
+ ...schema.version !== void 0 ? { version: schema.version } : {},
80
+ ...schema.nextVersion !== void 0 ? { nextVersion: schema.nextVersion } : {}
81
+ }));
82
+ return Effect.fail(CliRuntime.reported(new DriftError({ drifted }), 1));
83
+ }
84
+ case "catalog-blocked": return Effect.fail(CliRuntime.reported(new CatalogMergeError({
85
+ path: merged?.path ?? "",
86
+ conflicts: merged?.conflicts ?? [],
87
+ invalid: merged?.invalid ?? []
88
+ }), 1));
89
+ case "stale": {
90
+ const catalogFiles = [report.catalog?.slice?.outcome, merged?.outcome];
91
+ const orphaned = catalogFiles.filter((outcome) => outcome === "orphaned").length + (report.orphaned?.length ?? 0);
92
+ const count = report.schemas.filter((schema) => schema.outcome === "would-write").length + catalogFiles.filter((outcome) => outcome === "would-write").length + orphaned;
93
+ return Effect.fail(CliRuntime.reported(new StaleError({
94
+ count,
95
+ ...orphaned > 0 ? { orphaned } : {}
96
+ }), 1));
97
+ }
98
+ }
99
+ };
100
+ /**
101
+ * Run one `build` or `check`.
102
+ *
103
+ * @remarks
104
+ * The live view is for a person watching, so it needs human output and an interactive run, which `CliInteractive` only
105
+ * ever answers `true` for a human audience with terminals. That module, and Ink and React with it, is imported only
106
+ * then: an agent, CI or piped run never loads them. The report is emitted first and the failure follows, so a run that
107
+ * fails has still said why.
108
+ */
109
+ const execute = Effect.fn("effected.schema.execute")(function* (mode, input, deps) {
110
+ const format = yield* resolveFormat(input.format);
111
+ const interactive = yield* CliInteractive;
112
+ const announce = (event) => Effect.gen(function* () {
113
+ if (event._tag !== "Started") return;
114
+ if (input.force) yield* Effect.logWarning(`--force: drift policy is allow for this run; a published document ${mode === "check" ? "would be" : "may be"} rewritten in place, which breaks every consumer pinned to its URL.`);
115
+ if (event.versionMismatch !== void 0) yield* warnVersionMismatch(event.versionMismatch);
116
+ });
117
+ const walk = (onProgress) => SchemaRun.run({
118
+ mode,
119
+ force: input.force,
120
+ versionMismatch: input.ignoreVersionMismatch ? "warn" : "fail",
121
+ ...Option.isSome(input.config) ? { config: input.config.value } : {},
122
+ ...Option.isSome(input.drift) ? { drift: input.drift.value } : {},
123
+ ...Option.isSome(input.onDrift) ? { onDrift: input.onDrift.value } : {}
124
+ }, {
125
+ ...deps,
126
+ onProgress: (event) => Effect.andThen(announce(event), onProgress(event))
127
+ });
128
+ const result = yield* (format === "human" && interactive ? Effect.flatMap(Effect.promise(() => import("./LiveProgress.js")), ({ withLiveProgress }) => withLiveProgress(walk)) : walk(() => Effect.void)).pipe(Effect.catchTags({
129
+ ...configFailures(`${mode} lib/schemastore.config.ts`, input.config),
130
+ ConflictingFlagsError: (error) => Effect.fail(CliRuntime.reported(error, 64)),
131
+ VersionMismatchError: (error) => Effect.fail(CliRuntime.reported(error, 2)),
132
+ FrozenVersionMissingError: (error) => Effect.fail(CliRuntime.reported(error, 1)),
133
+ FrozenVersionIdMismatchError: (error) => Effect.fail(CliRuntime.reported(error, 1)),
134
+ CatalogDirError: (error) => Effect.fail(CliRuntime.reported(error, 2))
135
+ }));
136
+ yield* emit(result.report, format, result.versionMismatch);
137
+ yield* StepSummary.append(Report.markdown(result.report));
138
+ yield* failOnVerdict(result);
139
+ });
140
+
141
+ export { configFailures, execute, resolveFormat, warnVersionMismatch };
@@ -0,0 +1,24 @@
1
+ import { Argument, Flag } from "effect/cli";
2
+
3
+ // src/commands/schema/flags.ts
4
+ /** The optional positional config path; omitted, the config is discovered upward from the working directory. */
5
+ const configArgument = Argument.String("config").pipe(Argument.withDescription("Path to a schemastore config module; omitted, schemastore.config.{ts,mts,js,mjs} is searched upward from the working directory"), Argument.optional);
6
+ /** `--format`: absent, JSON for an agent and human text for everyone else. */
7
+ const formatFlag = Flag.Literals("format", ["human", "json"]).pipe(Flag.withDescription("Output format, by default json for an agent and human otherwise; json writes one document to stdout and moves human text to stderr"), Flag.optional);
8
+ /** `--ignore-version-mismatch`: run on past a config from another `@effected/schemastore`. */
9
+ const ignoreVersionMismatchFlag = Flag.Boolean("ignore-version-mismatch").pipe(Flag.withDefault(false), Flag.withDescription("Run even when the config's values come from a different @effected/schemastore than this tool's; warns loudly"));
10
+ /** What `build` and `check` take. */
11
+ const commandFlags = {
12
+ config: configArgument,
13
+ drift: Flag.Literals("drift", [
14
+ "strict",
15
+ "semantic",
16
+ "allow"
17
+ ]).pipe(Flag.withDescription("Drift tolerance for published schemas; overrides every schema's drift"), Flag.optional),
18
+ onDrift: Flag.Literals("on-drift", ["error", "warn"]).pipe(Flag.withDescription("What drift does: refuse every write (error) or write and warn; overrides the config's onDrift"), Flag.optional),
19
+ force: Flag.Boolean("force").pipe(Flag.withDefault(false), Flag.withDescription("Shorthand for --drift=allow")),
20
+ format: formatFlag,
21
+ ignoreVersionMismatch: ignoreVersionMismatchFlag
22
+ };
23
+
24
+ export { commandFlags, configArgument, formatFlag, ignoreVersionMismatchFlag };
@@ -0,0 +1,14 @@
1
+ import { makeBuildCommand } from "./build.js";
2
+ import { makeCheckCommand } from "./check.js";
3
+ import { makeValidateCommand } from "./validate.js";
4
+ import { Command } from "effect/cli";
5
+
6
+ // src/commands/schema/index.ts
7
+ /** `effected schema`: the group, closed over the process boundary it runs against. */
8
+ const makeSchemaCommand = (deps) => Command.make("schema", {}).pipe(Command.withDescription("Build and check SchemaStore-shaped JSON Schema documents from a schemastore.config.ts, and validate payloads against them"), Command.withSubcommands([
9
+ makeBuildCommand(deps),
10
+ makeCheckCommand(deps),
11
+ makeValidateCommand(deps)
12
+ ]));
13
+
14
+ export { makeSchemaCommand };
@@ -0,0 +1,58 @@
1
+ import { ValidationFailedError } from "./errors.js";
2
+ import { configFailures, resolveFormat, warnVersionMismatch } from "./execute.js";
3
+ import { configArgument, formatFlag, ignoreVersionMismatchFlag } from "./flags.js";
4
+ import { Console, Effect, Option, Schema } from "effect";
5
+ import { CliRuntime, Doc } from "@effected/cli-kit";
6
+ import { Argument, Command, Flag } from "effect/cli";
7
+ import { SchemaValidate, ValidateReport } from "@effected/engine/schema";
8
+
9
+ // src/commands/schema/validate.ts
10
+ const findingLine = (finding) => {
11
+ const at = finding.location;
12
+ const where = at !== void 0 ? `${at.file}:${at.line}:${at.column}: ` : " ";
13
+ const keyword = finding.keyword !== void 0 ? ` [${finding.keyword}]` : "";
14
+ return `${where}${finding.path === "" ? "(root)" : finding.path}: ${finding.message}${keyword}`;
15
+ };
16
+ const humanLines = (report) => report.valid ? [`valid ${report.payload} against ${report.source}`] : [...report.findings.map(findingLine), `${report.findings.length} finding(s): ${report.payload} does not conform to ${report.$ref}`];
17
+ /** Run one `validate`. */
18
+ const runValidate = Effect.fn("effected.schema.validate")(function* (input, deps) {
19
+ const format = yield* resolveFormat(input.format);
20
+ const report = yield* SchemaValidate.run({
21
+ payload: input.payload,
22
+ versionMismatch: input.ignoreVersionMismatch ? "warn" : "fail",
23
+ ...Option.isSome(input.config) ? { config: input.config.value } : {},
24
+ ...Option.isSome(input.schema) ? { schema: input.schema.value } : {}
25
+ }, {
26
+ ...deps,
27
+ onProgress: (event) => event._tag === "Started" && event.versionMismatch !== void 0 ? warnVersionMismatch(event.versionMismatch) : Effect.void
28
+ }).pipe(Effect.catchTags({
29
+ ...configFailures("validate payload.json lib/schemastore.config.ts", input.config),
30
+ VersionMismatchError: (error) => Effect.fail(CliRuntime.reported(error, 2)),
31
+ PayloadError: (error) => Effect.fail(CliRuntime.reported(error, 2)),
32
+ SchemaResolutionError: (error) => Effect.fail(CliRuntime.reported(error, 2)),
33
+ MissingSchemaRefError: (error) => Effect.fail(CliRuntime.reported(error, 64))
34
+ }));
35
+ const lines = humanLines(report);
36
+ if (format === "json") {
37
+ yield* Console.log(JSON.stringify(Schema.encodeSync(ValidateReport.Json)(report)));
38
+ for (const line of lines) yield* Effect.logInfo(line);
39
+ } else yield* Doc.print([Doc.lines(lines.map((line, index) => Doc.text(line, report.valid ? "success" : index === lines.length - 1 ? "emphasis" : "error")), { wrap: false })]);
40
+ if (!report.valid) return yield* Effect.fail(CliRuntime.reported(new ValidationFailedError({
41
+ count: report.findings.length,
42
+ payload: report.payload,
43
+ schema: report.$ref
44
+ }), 1));
45
+ });
46
+ /**
47
+ * `effected schema validate`: check a payload against a published schema document, named by `--schema` or the payload's
48
+ * `$schema`: a file path, or an identity the config derives. No network is involved.
49
+ */
50
+ const makeValidateCommand = (deps) => Command.make("validate", {
51
+ payload: Argument.String("payload").pipe(Argument.withDescription("Path to the JSON payload to validate")),
52
+ schema: Flag.String("schema").pipe(Flag.optional, Flag.withDescription("The document to validate against: a file path, or a $id/url a config schema derives; omitted, the payload's own $schema property")),
53
+ config: configArgument,
54
+ format: formatFlag,
55
+ ignoreVersionMismatch: ignoreVersionMismatchFlag
56
+ }, (input) => runValidate(input, deps)).pipe(Command.withDescription("Validate a JSON payload against a published schema document, resolved by path or by the config's derived $id/url identities"));
57
+
58
+ export { makeValidateCommand, runValidate };
package/index.d.ts CHANGED
@@ -1,22 +1,237 @@
1
- import { Command } from "effect/cli";
1
+ import { Schema } from "effect";
2
+ import { ActionRunDeps, BuildRunDeps } from "@effected/engine/build";
3
+ import "@effected/env";
4
+ import "effect/cli";
5
+ import { SchemaRunDeps } from "@effected/engine/schema";
6
+ declare const ActionBuildFailedError_base: Schema.Class<ActionBuildFailedError, Schema.TaggedStruct<"ActionBuildFailedError", {
7
+ /** How many validation errors there were. */
8
+ readonly errors: Schema.Number;
9
+ /** How many entries failed to bundle or load. */
10
+ readonly failedEntries: Schema.Number;
11
+ }>, import("effect/Cause").YieldableError>;
2
12
  /**
3
- * The `effected` command tree. This release carries no subcommand: `--version`
4
- * and `--help` come from the framework, and the audience flags are shared on
5
- * the root so every future subcommand accepts them.
13
+ * Validation stopped the build or an entry failed. Exit `1`.
6
14
  *
7
15
  * @public
8
16
  */
9
- export declare const rootCommand: Command.Command<"effected", {
10
- readonly audience: readonly import("@effected/env").AudienceKind[];
11
- readonly human: readonly boolean[];
12
- readonly agent: readonly boolean[];
13
- readonly ci: readonly boolean[];
14
- }, {
15
- readonly audience: readonly import("@effected/env").AudienceKind[];
16
- readonly human: readonly boolean[];
17
- readonly agent: readonly boolean[];
18
- readonly ci: readonly boolean[];
19
- }, never, never>;
17
+ export declare class ActionBuildFailedError extends ActionBuildFailedError_base {
18
+ get message(): string;
19
+ }
20
+ declare const ActionConfigError_base: Schema.Class<ActionConfigError, Schema.TaggedStruct<"ActionConfigError", {
21
+ readonly message: Schema.String;
22
+ }>, import("effect/Cause").YieldableError>;
23
+ /**
24
+ * The build could not start: its `effected.toml` would not load, it has no `[action]` table, or the directory is not a
25
+ * package. Exit `2`: exit `1` means it ran and found a problem.
26
+ *
27
+ * @remarks
28
+ * Carries the engine's message. This command's own type rather than the engine's, because the engine's build entry is
29
+ * loaded only when the command runs.
30
+ *
31
+ * @public
32
+ */
33
+ export declare class ActionConfigError extends ActionConfigError_base {}
34
+ declare const ActionRunError_base: Schema.Class<ActionRunError, Schema.TaggedStruct<"ActionRunError", {
35
+ readonly message: Schema.String;
36
+ }>, import("effect/Cause").YieldableError>;
37
+ /**
38
+ * The build could not finish: an entry is missing with validation off, or a file could not be read, cleaned, written or
39
+ * copied. `action init` fails with it too when a write fails after its checks, naming the files already written. Exit
40
+ * `3`, the infrastructure tier.
41
+ *
42
+ * @remarks
43
+ * Carries the engine's message, as {@link ActionConfigError} does.
44
+ *
45
+ * @public
46
+ */
47
+ export declare class ActionRunError extends ActionRunError_base {}
48
+ declare const ActionInitError_base: Schema.Class<ActionInitError, Schema.TaggedStruct<"ActionInitError", {
49
+ readonly message: Schema.String;
50
+ }>, import("effect/Cause").YieldableError>;
51
+ /**
52
+ * `action init` could not start: the directory is not a package, the action's name or description was found nowhere
53
+ * or holds a control character, files it would write exist and `--force` was not given, or a path cannot be written
54
+ * over (a symbolic link, the wrong kind of entry), which `--force` never overrides. Exit `2`, and nothing was written.
55
+ *
56
+ * @remarks
57
+ * Carries the engine's message, as {@link ActionConfigError} does.
58
+ *
59
+ * @public
60
+ */
61
+ export declare class ActionInitError extends ActionInitError_base {}
62
+ /**
63
+ * What `effected action` is given by the process boundary. `main` reads `cwd` and `ci`, and nothing else reads the
64
+ * process.
65
+ *
66
+ * @public
67
+ */
68
+ type ActionCommandDeps = ActionRunDeps;
69
+ declare const BuildFailedError_base: Schema.Class<BuildFailedError, Schema.TaggedStruct<"BuildFailedError", {
70
+ /** How many errors the build recorded. */
71
+ readonly errors: Schema.Number;
72
+ /** What ended the build, when it did not reach its end. */
73
+ readonly failure: Schema.optionalKey<Schema.String>;
74
+ }>, import("effect/Cause").YieldableError>;
75
+ /**
76
+ * The build crashed or recorded an error. Exit `1`.
77
+ *
78
+ * @public
79
+ */
80
+ export declare class BuildFailedError extends BuildFailedError_base {
81
+ get message(): string;
82
+ }
83
+ declare const PackageConfigError_base: Schema.Class<PackageConfigError, Schema.TaggedStruct<"PackageConfigError", {
84
+ readonly message: Schema.String;
85
+ }>, import("effect/Cause").YieldableError>;
86
+ /**
87
+ * The build could not run: its `effected.toml` or a plugin would not load, the directory is not a package, or the
88
+ * bundler refused the config before any work. Exit `2`: exit `1` means it ran and found a problem.
89
+ *
90
+ * @remarks
91
+ * Carries the engine's message, which names the file at fault and the key within it. This command's own type rather
92
+ * than the bundler's, because the CLI reaches the bundler only through the engine.
93
+ *
94
+ * @public
95
+ */
96
+ export declare class PackageConfigError extends PackageConfigError_base {}
97
+ declare const PackageRunError_base: Schema.Class<PackageRunError, Schema.TaggedStruct<"PackageRunError", {
98
+ readonly message: Schema.String;
99
+ }>, import("effect/Cause").YieldableError>;
100
+ /**
101
+ * A file the build needs could not be read or written. Exit `3`, the infrastructure tier.
102
+ *
103
+ * @public
104
+ */
105
+ export declare class PackageRunError extends PackageRunError_base {}
106
+ /**
107
+ * What `effected package` is given by the process boundary, and the test seams: the engine's dependencies, less the
108
+ * progress callback. `main` reads `cwd` and `ci`, and nothing else reads the process.
109
+ *
110
+ * @public
111
+ */
112
+ type PackageCommandDeps = Omit<BuildRunDeps, "onProgress">;
113
+ declare const DriftError_base: Schema.Class<DriftError, Schema.TaggedStruct<"DriftError", {
114
+ readonly drifted: Schema.$Array<Schema.Struct<{
115
+ readonly $id: Schema.String;
116
+ readonly change: Schema.Literals<readonly ["none", "created", "annotations", "contract"]>;
117
+ readonly version: Schema.optionalKey<Schema.String>;
118
+ readonly nextVersion: Schema.optionalKey<Schema.String>;
119
+ }>>;
120
+ }>, import("effect/Cause").YieldableError>;
121
+ /**
122
+ * A published schema drifted under `onDrift: "error"`, so nothing was written. Exit `1`.
123
+ *
124
+ * @public
125
+ */
126
+ export declare class DriftError extends DriftError_base {
127
+ get count(): number;
128
+ get message(): string;
129
+ }
130
+ declare const GateError_base: Schema.Class<GateError, Schema.TaggedStruct<"GateError", {
131
+ readonly count: Schema.Number;
132
+ }>, import("effect/Cause").YieldableError>;
133
+ /**
134
+ * A schema failed the lint/validation gate, so nothing was written. Exit `1`.
135
+ *
136
+ * @public
137
+ */
138
+ export declare class GateError extends GateError_base {
139
+ get message(): string;
140
+ }
141
+ declare const CatalogMergeError_base: Schema.Class<CatalogMergeError, Schema.TaggedStruct<"CatalogMergeError", {
142
+ readonly path: Schema.String;
143
+ readonly conflicts: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
144
+ readonly kind: Schema.Literal<"url">;
145
+ readonly url: Schema.String;
146
+ readonly slices: Schema.$Array<Schema.String>;
147
+ }>, Schema.Struct<{
148
+ readonly kind: Schema.Literal<"name">;
149
+ readonly name: Schema.String;
150
+ readonly slices: Schema.$Array<Schema.String>;
151
+ }>]>>;
152
+ readonly invalid: Schema.$Array<Schema.Struct<{
153
+ readonly path: Schema.String;
154
+ readonly reason: Schema.String;
155
+ }>>;
156
+ }>, import("effect/Cause").YieldableError>;
157
+ /**
158
+ * The merged catalog could not be assembled: a catalog URL or entry name is advertised more than once across the
159
+ * slices, or a slice is not a catalog entry array. Nothing is merged silently, so the merged catalog was left as it
160
+ * is. Exit `1` under both `build` and `check`: only an edit to the slices or the configs clears it.
161
+ *
162
+ * @public
163
+ */
164
+ export declare class CatalogMergeError extends CatalogMergeError_base {
165
+ get message(): string;
166
+ }
167
+ declare const StaleError_base: Schema.Class<StaleError, Schema.TaggedStruct<"StaleError", {
168
+ readonly count: Schema.Number;
169
+ readonly orphaned: Schema.optionalKey<Schema.Number>;
170
+ }>, import("effect/Cause").YieldableError>;
171
+ /**
172
+ * `check` found committed documents that differ from what the config generates, or outputs nothing claims, which
173
+ * `build` never deletes. `check` is the CI drift gate, so a stale tree fails it. Exit `1`. `count` is every finding;
174
+ * `orphaned` the part of it a build cannot clear, so the message names both remedies.
175
+ *
176
+ * @public
177
+ */
178
+ export declare class StaleError extends StaleError_base {
179
+ get message(): string;
180
+ }
181
+ declare const ValidationFailedError_base: Schema.Class<ValidationFailedError, Schema.TaggedStruct<"ValidationFailedError", {
182
+ readonly count: Schema.Number;
183
+ readonly payload: Schema.String;
184
+ readonly schema: Schema.String;
185
+ }>, import("effect/Cause").YieldableError>;
186
+ /**
187
+ * The payload does not conform to the resolved document: the engine answered findings, each already reported. Exit
188
+ * `1`: a validation verdict, the reason CI runs the command.
189
+ *
190
+ * @public
191
+ */
192
+ export declare class ValidationFailedError extends ValidationFailedError_base {
193
+ get message(): string;
194
+ }
195
+ declare const NoConfigError_base: Schema.Class<NoConfigError, Schema.TaggedStruct<"NoConfigError", {
196
+ /** The file names looked for. */
197
+ readonly names: Schema.$Array<Schema.String>;
198
+ /** The directories walked, or the one explicit path checked. */
199
+ readonly searched: Schema.$Array<Schema.String>;
200
+ /** Whether `searched` is the one path the user named. */
201
+ readonly explicit: Schema.Boolean;
202
+ /** What follows `effected schema` in the example: the subcommand and its positionals, with a config path. */
203
+ readonly usage: Schema.String;
204
+ }>, import("effect/Cause").YieldableError>;
205
+ /**
206
+ * No schemastore config was found. Exit `2`: the command could not run, where exit `1` means it ran and found a
207
+ * problem. The engine's error says what was searched; this adds the way out, which is the front end's to state because
208
+ * it shows the command that was run.
209
+ *
210
+ * @public
211
+ */
212
+ export declare class NoConfigError extends NoConfigError_base {
213
+ get message(): string;
214
+ }
215
+ /**
216
+ * What `effected schema` is given by the process boundary, and the test seams: the engine's dependencies, less the
217
+ * progress callback the command supplies itself. `main` reads `cwd`, and nothing else reads the process.
218
+ *
219
+ * @public
220
+ */
221
+ type SchemaCommandDeps = Omit<SchemaRunDeps, "onProgress">;
222
+ /**
223
+ * What every command is given by the process boundary, and the test seams.
224
+ *
225
+ * @public
226
+ */
227
+ type CommandDeps = SchemaCommandDeps & PackageCommandDeps & ActionCommandDeps;
228
+ /**
229
+ * The `effected` command tree, closed over the process boundary it runs against: `main` hands in the process values, a
230
+ * test its own. The audience flags are shared on the root so every subcommand accepts them.
231
+ *
232
+ * @public
233
+ */
234
+ export declare const makeRootCommand: any;
20
235
  /**
21
236
  * The `effected` command's version. The bundler replaces the exact expression
22
237
  * `process.env.__PACKAGE_VERSION__` with the package version at build time, so
@@ -25,4 +240,5 @@ export declare const rootCommand: Command.Command<"effected", {
25
240
  * @public
26
241
  */
27
242
  export declare const CLI_VERSION: string;
243
+ export type { ActionCommandDeps, CommandDeps, PackageCommandDeps, SchemaCommandDeps };
28
244
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,4 +1,7 @@
1
- import { rootCommand } from "./commands/root.js";
1
+ import { ActionBuildFailedError, ActionConfigError, ActionInitError, ActionRunError } from "./commands/action/errors.js";
2
+ import { BuildFailedError, PackageConfigError, PackageRunError } from "./commands/package/errors.js";
3
+ import { makeRootCommand } from "./commands/root.js";
4
+ import { CatalogMergeError, DriftError, GateError, NoConfigError, StaleError, ValidationFailedError } from "./commands/schema/errors.js";
2
5
  import { CLI_VERSION } from "./version.js";
3
6
 
4
- export { CLI_VERSION, rootCommand };
7
+ export { ActionBuildFailedError, ActionConfigError, ActionInitError, ActionRunError, BuildFailedError, CLI_VERSION, CatalogMergeError, DriftError, GateError, NoConfigError, PackageConfigError, PackageRunError, StaleError, ValidationFailedError, makeRootCommand };
package/main.d.ts CHANGED
@@ -1,5 +1,36 @@
1
1
  import { Distribution } from "@effected/kit";
2
2
  import { Layer } from "effect";
3
+ import "effect/cli";
4
+ import { ActionRunDeps, BuildRunDeps } from "@effected/engine/build";
5
+ import "@effected/env";
6
+ import { SchemaRunDeps } from "@effected/engine/schema";
7
+ /**
8
+ * What `effected action` is given by the process boundary. `main` reads `cwd` and `ci`, and nothing else reads the
9
+ * process.
10
+ *
11
+ * @public
12
+ */
13
+ type ActionCommandDeps = ActionRunDeps;
14
+ /**
15
+ * What `effected package` is given by the process boundary, and the test seams: the engine's dependencies, less the
16
+ * progress callback. `main` reads `cwd` and `ci`, and nothing else reads the process.
17
+ *
18
+ * @public
19
+ */
20
+ type PackageCommandDeps = Omit<BuildRunDeps, "onProgress">;
21
+ /**
22
+ * What `effected schema` is given by the process boundary, and the test seams: the engine's dependencies, less the
23
+ * progress callback the command supplies itself. `main` reads `cwd`, and nothing else reads the process.
24
+ *
25
+ * @public
26
+ */
27
+ type SchemaCommandDeps = Omit<SchemaRunDeps, "onProgress">;
28
+ /**
29
+ * What every command is given by the process boundary, and the test seams.
30
+ *
31
+ * @public
32
+ */
33
+ type CommandDeps = SchemaCommandDeps & PackageCommandDeps & ActionCommandDeps;
3
34
  /**
4
35
  * Options a carrier's bin shim passes to {@link main}. `distribution` names
5
36
  * the package the `effected` bin was installed through; omitted for a direct
@@ -10,6 +41,19 @@ import { Layer } from "effect";
10
41
  export interface MainOptions {
11
42
  readonly distribution?: Distribution;
12
43
  }
44
+ /**
45
+ * How `CliRuntime.main` runs the command: exit `3` for a typed error that carries no code of its own, the infrastructure
46
+ * tier, and this module as the program's own, so the failure report's span trail keeps the command's spans when it is
47
+ * installed under `node_modules/@effected/` and leaves out the kit's.
48
+ *
49
+ * @public
50
+ */
51
+ export declare const mainOptions: {
52
+ readonly exitCode: 3;
53
+ readonly env: {
54
+ readonly appModule: string;
55
+ };
56
+ };
13
57
  /**
14
58
  * The `effected` program under `CliRuntime.main`, over `platform`: what
15
59
  * {@link main} runs with the Node services, and what a test runs over a test
@@ -17,7 +61,7 @@ export interface MainOptions {
17
61
  *
18
62
  * @public
19
63
  */
20
- export declare const run: <RP, EP>(options: MainOptions, platform: Layer.Layer<RP, EP>) => import("effect/Effect").Effect<void, Error, Exclude<import("effect/process/ChildProcessSpawner").ChildProcessSpawner, RP> | Exclude<import("effect/FileSystem").FileSystem, RP> | Exclude<import("effect/Path").Path, RP> | Exclude<import("effect/Stdio").Stdio, RP> | Exclude<import("effect/Stdio").Stdio, RP> | Exclude<import("effect/Terminal").Terminal, RP>>;
64
+ export declare const run: <RP, EP>(options: MainOptions, deps: CommandDeps, platform: Layer.Layer<RP, EP>) => import("effect/Effect").Effect<void, Error, Exclude<import("effect/process/ChildProcessSpawner").ChildProcessSpawner, RP> | Exclude<import("effect/FileSystem").FileSystem, RP> | Exclude<import("effect/Path").Path, RP> | Exclude<import("effect/Stdio").Stdio, RP> | Exclude<import("effect/Stdio").Stdio, RP> | Exclude<import("effect/Terminal").Terminal, RP>>;
21
65
  /**
22
66
  * Run the `effected` command. Owns the process: installs the runtime teardown
23
67
  * and sets the exit code.
@@ -25,4 +69,5 @@ export declare const run: <RP, EP>(options: MainOptions, platform: Layer.Layer<R
25
69
  * @public
26
70
  */
27
71
  export declare const main: (options?: MainOptions) => void;
72
+ export type { ActionCommandDeps, CommandDeps, PackageCommandDeps, SchemaCommandDeps };
28
73
  //# sourceMappingURL=main.d.ts.map
package/main.js CHANGED
@@ -1,6 +1,7 @@
1
- import { rootCommand } from "./commands/root.js";
1
+ import { isCI } from "./ci.js";
2
+ import { makeRootCommand } from "./commands/root.js";
2
3
  import { CLI_VERSION } from "./version.js";
3
- import { CliAudience, CliRuntime } from "@effected/cli-kit";
4
+ import { CliAudience, CliLogger, CliRuntime } from "@effected/cli-kit";
4
5
  import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
5
6
  import * as NodeServices from "@effect/platform-node/NodeServices";
6
7
 
@@ -17,20 +18,35 @@ import * as NodeServices from "@effect/platform-node/NodeServices";
17
18
  */
18
19
  const versionFormatter = { formatVersion: (name, version) => `${name} ${version}` };
19
20
  /**
21
+ * How `CliRuntime.main` runs the command: exit `3` for a typed error that carries no code of its own, the infrastructure
22
+ * tier, and this module as the program's own, so the failure report's span trail keeps the command's spans when it is
23
+ * installed under `node_modules/@effected/` and leaves out the kit's.
24
+ *
25
+ * @public
26
+ */
27
+ const mainOptions = {
28
+ exitCode: 3,
29
+ env: { appModule: import.meta.url }
30
+ };
31
+ /** stdout is the report and nothing else, so every log level goes to stderr. */
32
+ const logger = CliLogger.layer({ stderrFrom: "All" });
33
+ /**
20
34
  * The `effected` program under `CliRuntime.main`, over `platform`: what
21
35
  * {@link main} runs with the Node services, and what a test runs over a test
22
36
  * environment.
23
37
  *
24
38
  * @public
25
39
  */
26
- const run = (options, platform) => CliRuntime.main(CliAudience.run(rootCommand, { version: CLI_VERSION }), {
40
+ const run = (options, deps, platform) => CliRuntime.main(CliAudience.run(makeRootCommand(deps), { version: CLI_VERSION }), {
27
41
  platform,
42
+ logger,
28
43
  distribution: options.distribution ?? null,
29
- exitCode: 3,
44
+ exitCode: mainOptions.exitCode,
30
45
  helpOnUsageError: "stderr",
31
46
  env: {
32
- appModule: import.meta.url,
33
- formatter: versionFormatter
47
+ ...mainOptions.env,
48
+ formatter: versionFormatter,
49
+ helpFlags: { inheritedFlags: "summary" }
34
50
  }
35
51
  });
36
52
  /**
@@ -40,7 +56,10 @@ const run = (options, platform) => CliRuntime.main(CliAudience.run(rootCommand,
40
56
  * @public
41
57
  */
42
58
  const main = (options = {}) => {
43
- NodeRuntime.runMain(run(options, NodeServices.layer));
59
+ NodeRuntime.runMain(run(options, {
60
+ cwd: process.cwd(),
61
+ ci: isCI(process.env)
62
+ }, NodeServices.layer));
44
63
  };
45
64
 
46
- export { main, run };
65
+ export { main, mainOptions, run };