effect-bdd 0.1.2 → 0.1.3

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.
Files changed (91) hide show
  1. package/dist/Bdd.d.ts +0 -1
  2. package/dist/Bdd.js +1 -2
  3. package/dist/Errors.d.ts +0 -1
  4. package/dist/Errors.js +1 -2
  5. package/dist/bin.d.ts +0 -1
  6. package/dist/bin.js +1 -2
  7. package/dist/index.d.ts +0 -1
  8. package/dist/index.js +1 -2
  9. package/dist/internal/cli/errors.d.ts +0 -1
  10. package/dist/internal/cli/errors.js +1 -2
  11. package/dist/internal/cli/glob.d.ts +0 -1
  12. package/dist/internal/cli/glob.js +1 -2
  13. package/dist/internal/cli/loaders.d.ts +0 -1
  14. package/dist/internal/cli/loaders.js +1 -2
  15. package/dist/internal/cli/models.d.ts +0 -1
  16. package/dist/internal/cli/models.js +1 -2
  17. package/dist/internal/cli/moduleLoader.d.ts +0 -1
  18. package/dist/internal/cli/moduleLoader.js +1 -2
  19. package/dist/internal/cli/reporter.d.ts +0 -1
  20. package/dist/internal/cli/reporter.js +1 -2
  21. package/dist/internal/cli/runner.d.ts +0 -1
  22. package/dist/internal/cli/runner.js +1 -2
  23. package/dist/internal/cli/tagExpression.d.ts +0 -1
  24. package/dist/internal/cli/tagExpression.js +1 -2
  25. package/dist/internal/cucumberCompiler.d.ts +0 -1
  26. package/dist/internal/cucumberCompiler.js +1 -2
  27. package/dist/internal/expression.d.ts +0 -1
  28. package/dist/internal/expression.js +1 -2
  29. package/dist/internal/matching.d.ts +0 -1
  30. package/dist/internal/matching.js +1 -2
  31. package/dist/internal/parser.d.ts +0 -1
  32. package/dist/internal/parser.js +1 -2
  33. package/dist/internal/runner.d.ts +0 -1
  34. package/dist/internal/runner.js +1 -2
  35. package/dist/main.d.ts +0 -1
  36. package/dist/main.js +1 -2
  37. package/package.json +3 -7
  38. package/dist/Bdd.d.ts.map +0 -1
  39. package/dist/Bdd.js.map +0 -1
  40. package/dist/Errors.d.ts.map +0 -1
  41. package/dist/Errors.js.map +0 -1
  42. package/dist/bin.d.ts.map +0 -1
  43. package/dist/bin.js.map +0 -1
  44. package/dist/index.d.ts.map +0 -1
  45. package/dist/index.js.map +0 -1
  46. package/dist/internal/cli/errors.d.ts.map +0 -1
  47. package/dist/internal/cli/errors.js.map +0 -1
  48. package/dist/internal/cli/glob.d.ts.map +0 -1
  49. package/dist/internal/cli/glob.js.map +0 -1
  50. package/dist/internal/cli/loaders.d.ts.map +0 -1
  51. package/dist/internal/cli/loaders.js.map +0 -1
  52. package/dist/internal/cli/models.d.ts.map +0 -1
  53. package/dist/internal/cli/models.js.map +0 -1
  54. package/dist/internal/cli/moduleLoader.d.ts.map +0 -1
  55. package/dist/internal/cli/moduleLoader.js.map +0 -1
  56. package/dist/internal/cli/reporter.d.ts.map +0 -1
  57. package/dist/internal/cli/reporter.js.map +0 -1
  58. package/dist/internal/cli/runner.d.ts.map +0 -1
  59. package/dist/internal/cli/runner.js.map +0 -1
  60. package/dist/internal/cli/tagExpression.d.ts.map +0 -1
  61. package/dist/internal/cli/tagExpression.js.map +0 -1
  62. package/dist/internal/cucumberCompiler.d.ts.map +0 -1
  63. package/dist/internal/cucumberCompiler.js.map +0 -1
  64. package/dist/internal/expression.d.ts.map +0 -1
  65. package/dist/internal/expression.js.map +0 -1
  66. package/dist/internal/matching.d.ts.map +0 -1
  67. package/dist/internal/matching.js.map +0 -1
  68. package/dist/internal/parser.d.ts.map +0 -1
  69. package/dist/internal/parser.js.map +0 -1
  70. package/dist/internal/runner.d.ts.map +0 -1
  71. package/dist/internal/runner.js.map +0 -1
  72. package/dist/main.d.ts.map +0 -1
  73. package/dist/main.js.map +0 -1
  74. package/src/Bdd.ts +0 -575
  75. package/src/Errors.ts +0 -60
  76. package/src/bin.ts +0 -10
  77. package/src/index.ts +0 -155
  78. package/src/internal/cli/errors.ts +0 -20
  79. package/src/internal/cli/glob.ts +0 -37
  80. package/src/internal/cli/loaders.ts +0 -100
  81. package/src/internal/cli/models.ts +0 -118
  82. package/src/internal/cli/moduleLoader.ts +0 -41
  83. package/src/internal/cli/reporter.ts +0 -367
  84. package/src/internal/cli/runner.ts +0 -336
  85. package/src/internal/cli/tagExpression.ts +0 -173
  86. package/src/internal/cucumberCompiler.ts +0 -58
  87. package/src/internal/expression.ts +0 -103
  88. package/src/internal/matching.ts +0 -81
  89. package/src/internal/parser.ts +0 -155
  90. package/src/internal/runner.ts +0 -373
  91. package/src/main.ts +0 -169
@@ -1,373 +0,0 @@
1
- import type { Pickle, PickleDocString, PickleStep, PickleTable } from "@cucumber/messages"
2
- import * as Arr from "effect/Array"
3
- import * as Effect from "effect/Effect"
4
- import { pipe } from "effect/Function"
5
- import * as Option from "effect/Option"
6
- import * as Record from "effect/Record"
7
- import * as Schema from "effect/Schema"
8
- import { MatchError, ParseError, StepError } from "../Errors.ts"
9
- import * as Matching from "./matching.ts"
10
- import * as Parser from "./parser.ts"
11
-
12
- /** @internal */
13
- export type DataTableInput = PickleTable
14
-
15
- /** @internal */
16
- export type DocStringInput = PickleDocString
17
-
18
- /** @internal */
19
- export type StepKind = "Step" | "Given" | "When" | "Then"
20
-
21
- /** @internal */
22
- export interface Matcher<A> {
23
- readonly source: string
24
- readonly match: (text: string) => Option.Option<A>
25
- }
26
-
27
- /** @internal */
28
- export type StepArgument =
29
- | {
30
- readonly _tag: "TableArg"
31
- readonly decode: (argument: DataTableInput) => Effect.Effect<unknown, unknown>
32
- }
33
- | {
34
- readonly _tag: "DocStringArg"
35
- readonly decode: (argument: DocStringInput) => Effect.Effect<unknown, unknown>
36
- }
37
-
38
- /** @internal */
39
- export interface Transition<State, E, R> {
40
- readonly kind: StepKind
41
- readonly expression: Matcher<unknown>
42
- readonly argument?: StepArgument
43
- readonly run: (captures: unknown, argument: unknown, state: State) => Effect.Effect<State, E, R>
44
- }
45
-
46
- /** @internal */
47
- export interface Feature<State, E, R> {
48
- readonly name: string
49
- readonly initial: State
50
- readonly transitions: ReadonlyArray<Transition<State, E, R>>
51
- }
52
-
53
- /** @internal */
54
- export interface Report {
55
- readonly feature: string
56
- readonly scenarios: ReadonlyArray<{
57
- readonly name: string
58
- readonly steps: number
59
- readonly tags: ReadonlyArray<string>
60
- }>
61
- }
62
-
63
- /** @internal */
64
- export type RunError = ParseError | MatchError | StepError
65
-
66
- /** @internal */
67
- export type GherkinCompiler = Parser.GherkinCompiler
68
-
69
- /** @internal */
70
- export interface ScenarioTask<State, E, R> {
71
- readonly featureDefinition: Feature<State, E, R>
72
- readonly featureName: string
73
- readonly scenarioName: string
74
- readonly scenarioIndex: number
75
- readonly scenarioLine: number
76
- readonly ruleName?: string
77
- readonly ruleLine?: number
78
- readonly tags: ReadonlyArray<string>
79
- readonly pickle: Pickle
80
- readonly source: Parser.SourceIndex
81
- }
82
-
83
- interface ResolvedTransition<State, E, R> {
84
- readonly transition: Transition<State, E, R>
85
- readonly captures: unknown
86
- }
87
-
88
- /** @internal */
89
- export type ScenarioReport = Report["scenarios"][number]
90
-
91
- /** @internal */
92
- export const decodeTable = <S extends Schema.Decoder<unknown, never>>(row: S) => {
93
- const decode = Schema.decodeUnknownEffect(row)
94
- return (table: PickleTable): Effect.Effect<ReadonlyArray<S["Type"]>, unknown> => {
95
- const [headers, ...rows] = table.rows.map((row) => row.cells.map((cell) => cell.value))
96
- if (headers === undefined) {
97
- return Effect.succeed([])
98
- }
99
- return Effect.forEach(rows, (cells: ReadonlyArray<string>) => decode(rowObject(headers, cells)))
100
- }
101
- }
102
-
103
- /** @internal */
104
- export const decodeDocString = <S extends Schema.Decoder<unknown, never>>(schema: S) => {
105
- const decode = Schema.decodeUnknownEffect(schema)
106
- return (docString: PickleDocString): Effect.Effect<S["Type"], unknown> => decode(docString.content)
107
- }
108
-
109
- /** @internal */
110
- export const run = <State, E, R>(
111
- featureDefinition: Feature<State, E, R>,
112
- source: string
113
- ): Effect.Effect<Report, RunError, R | Parser.GherkinCompiler> =>
114
- pipe(
115
- Parser.parse(source),
116
- Effect.flatMap((feature) =>
117
- pipe(
118
- validateFeatureDefinition(featureDefinition, feature),
119
- Effect.flatMap(() => Effect.forEach(buildScenarioTasks(featureDefinition, feature), runScenarioTask)),
120
- Effect.map((scenarios): Report => ({
121
- feature: feature.name,
122
- scenarios
123
- }))
124
- )
125
- )
126
- )
127
-
128
- const validateFeatureDefinition = <State, E, R>(
129
- featureDefinition: Feature<State, E, R>,
130
- feature: Parser.CompiledFeature
131
- ): Effect.Effect<void, MatchError> =>
132
- featureDefinition.name === feature.name
133
- ? Effect.void
134
- : Effect.fail(
135
- new MatchError({
136
- message: `Feature definition "${featureDefinition.name}" does not match Gherkin feature "${feature.name}"`,
137
- scenario: "",
138
- step: feature.name,
139
- line: feature.line,
140
- candidates: [featureDefinition.name]
141
- })
142
- )
143
-
144
- /** @internal */
145
- export const buildScenarioTasks = <State, E, R>(
146
- featureDefinition: Feature<State, E, R>,
147
- feature: Parser.CompiledFeature
148
- ): ReadonlyArray<ScenarioTask<State, E, R>> =>
149
- Arr.map(feature.pickles, (pickle, scenarioIndex): ScenarioTask<State, E, R> => {
150
- const source = Parser.findScenario(pickle, feature.source)
151
- const rule = pipe(
152
- source,
153
- Option.map(({ rule }) => rule),
154
- Option.getOrUndefined
155
- )
156
- return {
157
- featureDefinition,
158
- featureName: feature.name,
159
- scenarioName: pickle.name,
160
- scenarioIndex,
161
- scenarioLine: pickle.location?.line ?? pipe(
162
- source,
163
- Option.map(({ scenario }) => scenario.location.line),
164
- Option.getOrElse(() => feature.line)
165
- ),
166
- ...(rule === undefined ? {} : {
167
- ruleName: rule.name,
168
- ruleLine: rule.location.line
169
- }),
170
- tags: pickle.tags.map((tag) => tag.name),
171
- pickle,
172
- source: feature.source
173
- }
174
- })
175
-
176
- /** @internal */
177
- export const runScenarioTask = <State, E, R>(
178
- task: ScenarioTask<State, E, R>
179
- ): Effect.Effect<ScenarioReport, RunError, R> =>
180
- pipe(
181
- runSteps(task.featureDefinition, task.scenarioName, task.pickle.steps, task.source, task.featureDefinition.initial),
182
- Effect.as({
183
- name: task.scenarioName,
184
- steps: task.pickle.steps.length,
185
- tags: task.tags
186
- })
187
- )
188
-
189
- const runSteps = <State, E, R>(
190
- featureDefinition: Feature<State, E, R>,
191
- scenario: string,
192
- steps: ReadonlyArray<PickleStep>,
193
- source: Parser.SourceIndex,
194
- state: State,
195
- index = 0
196
- ): Effect.Effect<State, RunError, R> =>
197
- index >= steps.length
198
- ? Effect.succeed(state)
199
- : pipe(
200
- runStep(featureDefinition, scenario, steps[index], source, state),
201
- Effect.flatMap((state) => runSteps(featureDefinition, scenario, steps, source, state, index + 1))
202
- )
203
-
204
- const runStep = <State, E, R>(
205
- featureDefinition: Feature<State, E, R>,
206
- scenario: string,
207
- step: PickleStep,
208
- source: Parser.SourceIndex,
209
- state: State
210
- ): Effect.Effect<State, RunError, R> =>
211
- pipe(
212
- stepKind(step, source),
213
- Effect.flatMap((kind) =>
214
- pipe(
215
- resolve(featureDefinition, scenario, step, kind, source),
216
- Effect.flatMap(({ transition, captures }) =>
217
- pipe(
218
- decodeArgument(transition, scenario, step, source),
219
- Effect.flatMap((argument) =>
220
- pipe(
221
- transition.run(captures, argument, state),
222
- Effect.mapError((cause) =>
223
- new StepError({
224
- message: `Step failed: ${step.text}`,
225
- scenario,
226
- step: step.text,
227
- line: Parser.stepLine(step, source),
228
- cause
229
- })
230
- )
231
- )
232
- )
233
- )
234
- )
235
- )
236
- )
237
- )
238
-
239
- const decodeArgument = <State, E, R>(
240
- transition: Transition<State, E, R>,
241
- scenario: string,
242
- step: PickleStep,
243
- source: Parser.SourceIndex
244
- ): Effect.Effect<unknown, MatchError> => {
245
- const candidates = [transition.expression.source]
246
- if (transition.argument === undefined) {
247
- return hasStepArgument(step)
248
- ? failMatch(`Step "${step.text}" has an unexpected argument`, scenario, step, source, candidates)
249
- : Effect.succeed(undefined)
250
- }
251
-
252
- if (transition.argument._tag === "TableArg") {
253
- return step.argument?.dataTable === undefined
254
- ? failMatch(`Step "${step.text}" requires a DataTable`, scenario, step, source, candidates)
255
- : pipe(
256
- transition.argument.decode(step.argument.dataTable),
257
- Effect.mapError((cause) =>
258
- matchError(`Could not decode DataTable for step "${step.text}"`, scenario, step, source, candidates, cause)
259
- )
260
- )
261
- }
262
-
263
- return step.argument?.docString === undefined
264
- ? failMatch(`Step "${step.text}" requires a DocString`, scenario, step, source, candidates)
265
- : pipe(
266
- transition.argument.decode(step.argument.docString),
267
- Effect.mapError((cause) =>
268
- matchError(`Could not decode DocString for step "${step.text}"`, scenario, step, source, candidates, cause)
269
- )
270
- )
271
- }
272
-
273
- const resolve = <State, E, R>(
274
- featureDefinition: Feature<State, E, R>,
275
- scenario: string,
276
- step: PickleStep,
277
- kind: "Given" | "When" | "Then",
278
- source: Parser.SourceIndex
279
- ): Effect.Effect<ResolvedTransition<State, E, R>, MatchError> => {
280
- const textMatches = Matching.matchingTextTransitions(featureDefinition.transitions, step.text)
281
- const matches = Matching.matchingKeywordTransitions(textMatches, kind)
282
-
283
- return pipe(
284
- matches,
285
- Arr.match({
286
- onEmpty: () =>
287
- textMatches.length === 0
288
- ? failMatch(
289
- `No transition matched step "${step.text}"`,
290
- scenario,
291
- step,
292
- source,
293
- Arr.map(featureDefinition.transitions, (transition) => transition.expression.source)
294
- )
295
- : failMatch(
296
- `No ${kind} transition matched step "${step.text}"; matching text exists for ${
297
- Matching.renderTransitionKinds(Arr.map(textMatches, (match) => match.transition))
298
- }`,
299
- scenario,
300
- step,
301
- source,
302
- Arr.map(textMatches, (match) => match.transition.expression.source)
303
- ),
304
- onNonEmpty: (matches) =>
305
- matches.length === 1
306
- ? Effect.succeed(Arr.headNonEmpty(matches))
307
- : failMatch(
308
- `Multiple transitions matched step "${step.text}"`,
309
- scenario,
310
- step,
311
- source,
312
- Arr.map(matches, (match) => match.transition.expression.source)
313
- )
314
- })
315
- )
316
- }
317
-
318
- const rowObject = (
319
- headers: ReadonlyArray<string>,
320
- cells: ReadonlyArray<string>
321
- ): Record<string, string> =>
322
- pipe(
323
- headers,
324
- Arr.map((header, index) => [header, cells[index] ?? ""] as const),
325
- Record.fromEntries
326
- )
327
-
328
- const hasStepArgument = (step: PickleStep): boolean => step.argument !== undefined
329
-
330
- const stepKind = (
331
- step: PickleStep,
332
- source: Parser.SourceIndex
333
- ): Effect.Effect<"Given" | "When" | "Then", ParseError> =>
334
- pipe(
335
- Matching.concreteStepKind(step),
336
- Option.match({
337
- onNone: () =>
338
- Effect.fail(
339
- new ParseError({
340
- message: `${Parser.stepKeyword(step, source)} found before a Given, When, or Then step`,
341
- line: Parser.stepLine(step, source),
342
- column: 1
343
- })
344
- ),
345
- onSome: Effect.succeed
346
- })
347
- )
348
-
349
- const failMatch = (
350
- message: string,
351
- scenario: string,
352
- step: PickleStep,
353
- source: Parser.SourceIndex,
354
- candidates: ReadonlyArray<string>,
355
- cause?: unknown
356
- ): Effect.Effect<never, MatchError> => Effect.fail(matchError(message, scenario, step, source, candidates, cause))
357
-
358
- const matchError = (
359
- message: string,
360
- scenario: string,
361
- step: PickleStep,
362
- source: Parser.SourceIndex,
363
- candidates: ReadonlyArray<string>,
364
- cause?: unknown
365
- ): MatchError =>
366
- new MatchError({
367
- message,
368
- scenario,
369
- step: step.text,
370
- line: Parser.stepLine(step, source),
371
- candidates,
372
- ...(cause === undefined ? {} : { cause })
373
- })
package/src/main.ts DELETED
@@ -1,169 +0,0 @@
1
- /** @internal */
2
- import * as Effect from "effect/Effect"
3
- import * as Layer from "effect/Layer"
4
- import * as Option from "effect/Option"
5
- import * as CliError from "effect/unstable/cli/CliError"
6
- import * as Command from "effect/unstable/cli/Command"
7
- import * as Flag from "effect/unstable/cli/Flag"
8
- import PackageJson from "../package.json" with { type: "json" }
9
- import { GlobResolver } from "./internal/cli/glob.ts"
10
- import type { CliOptions } from "./internal/cli/models.ts"
11
- import { ModuleLoader } from "./internal/cli/moduleLoader.ts"
12
- import * as Reporter from "./internal/cli/reporter.ts"
13
- import * as Runner from "./internal/cli/runner.ts"
14
- import * as CucumberCompiler from "./internal/cucumberCompiler.ts"
15
-
16
- const features = Flag.string("features").pipe(
17
- Flag.withAlias("f"),
18
- Flag.withDescription("Feature file glob. Can be supplied multiple times."),
19
- Flag.between(1, Infinity)
20
- )
21
-
22
- const steps = Flag.string("steps").pipe(
23
- Flag.withAlias("s"),
24
- Flag.withDescription("Step definition module glob. Can be supplied multiple times."),
25
- Flag.between(1, Infinity)
26
- )
27
-
28
- const reporter = Flag.choice("reporter", ["text", "html", "json", "junit"] as const).pipe(
29
- Flag.withAlias("r"),
30
- Flag.withDescription("Reporter to run. Can be supplied multiple times."),
31
- Flag.between(0, Infinity)
32
- )
33
-
34
- const outputFileText = Flag.file("output-file.text").pipe(
35
- Flag.withDescription("File path for the text reporter. Defaults to stdout."),
36
- Flag.optional
37
- )
38
-
39
- const outputFileHtml = Flag.file("output-file.html").pipe(
40
- Flag.withDescription("File path for the html reporter."),
41
- Flag.optional
42
- )
43
-
44
- const outputFileJson = Flag.file("output-file.json").pipe(
45
- Flag.withDescription("File path for the json reporter. Defaults to stdout."),
46
- Flag.optional
47
- )
48
-
49
- const outputFileJunit = Flag.file("output-file.junit").pipe(
50
- Flag.withDescription("File path for the junit reporter."),
51
- Flag.optional
52
- )
53
-
54
- const parallel = Flag.integer("parallel").pipe(
55
- Flag.withAlias("p"),
56
- Flag.withDescription("Number of scenarios to run concurrently."),
57
- Flag.filter((value) => value > 0, (value) => `Expected --parallel to be greater than 0, got ${value}`),
58
- Flag.withDefault(1)
59
- )
60
-
61
- const verbose = Flag.boolean("verbose").pipe(
62
- Flag.withAlias("v"),
63
- Flag.withDescription("Print every scenario result instead of only failures and diagnostics.")
64
- )
65
-
66
- const tags = Flag.string("tags").pipe(
67
- Flag.withAlias("t"),
68
- Flag.withDescription("Cucumber-style tag expression. Can be supplied multiple times."),
69
- Flag.between(0, Infinity)
70
- )
71
-
72
- const name = Flag.string("name").pipe(
73
- Flag.withAlias("n"),
74
- Flag.withDescription("Run scenarios whose feature/scenario name contains this text. Can be supplied multiple times."),
75
- Flag.between(0, Infinity)
76
- )
77
-
78
- const failFast = Flag.boolean("fail-fast").pipe(
79
- Flag.withDescription("Stop after the first failed scenario. Runs sequentially when enabled.")
80
- )
81
-
82
- /** @internal */
83
- export const cli = Command.make(
84
- "effect-bdd",
85
- {
86
- features,
87
- steps,
88
- reporter,
89
- outputFileText,
90
- outputFileHtml,
91
- outputFileJson,
92
- outputFileJunit,
93
- parallel,
94
- verbose,
95
- tags,
96
- name,
97
- failFast
98
- },
99
- Effect.fnUntraced(function*(
100
- {
101
- features,
102
- steps,
103
- reporter,
104
- outputFileText,
105
- outputFileHtml,
106
- outputFileJson,
107
- outputFileJunit,
108
- parallel,
109
- verbose,
110
- tags,
111
- name,
112
- failFast
113
- }
114
- ) {
115
- const options: CliOptions = {
116
- features,
117
- steps,
118
- reporters: reporter.length === 0 ? ["text"] : reporter,
119
- outputFiles: {
120
- ...(Option.isSome(outputFileText) ? { text: outputFileText.value } : {}),
121
- ...(Option.isSome(outputFileHtml) ? { html: outputFileHtml.value } : {}),
122
- ...(Option.isSome(outputFileJson) ? { json: outputFileJson.value } : {}),
123
- ...(Option.isSome(outputFileJunit) ? { junit: outputFileJunit.value } : {})
124
- },
125
- verbose,
126
- filters: {
127
- tags,
128
- names: name,
129
- failFast
130
- },
131
- parallel
132
- }
133
- const reporters = yield* Reporter.makeReporters(options.reporters, options.outputFiles, { verbose }).pipe(
134
- Effect.mapError(toUserError)
135
- )
136
- const result = yield* Runner.run(options).pipe(
137
- Effect.mapError(toUserError)
138
- )
139
- yield* Reporter.emitAll(reporters, result).pipe(
140
- Effect.mapError(toUserError)
141
- )
142
- if (result.summary.failed > 0 || result.diagnostics.length > 0) {
143
- return yield* Effect.fail(
144
- new CliError.UserError({
145
- cause: result.summary.failed > 0
146
- ? `${result.summary.failed} scenario(s) failed`
147
- : `${result.diagnostics.length} diagnostic(s) reported`
148
- })
149
- )
150
- }
151
- })
152
- ).pipe(
153
- Command.withDescription("Run effect-bdd feature files"),
154
- Command.provide(Layer.mergeAll(GlobResolver.Live, ModuleLoader.Live, CucumberCompiler.Cucumber))
155
- )
156
-
157
- /** @internal */
158
- export const run = Command.run(cli, {
159
- version: PackageJson.version
160
- })
161
-
162
- const toUserError = (error: unknown): CliError.UserError => new CliError.UserError({ cause: renderUserError(error) })
163
-
164
- const renderUserError = (error: unknown): string => {
165
- if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
166
- return error.message
167
- }
168
- return String(error)
169
- }