@step-forge/step-forge 0.0.19 → 0.0.21

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 (40) hide show
  1. package/README.md +5 -2
  2. package/RUNTIME.md +212 -0
  3. package/dist/{analyzer-QH03uejK.js → analyzer-DYfdoSIS.js} +55 -16
  4. package/dist/analyzer-DYfdoSIS.js.map +1 -0
  5. package/dist/analyzer-cli.js +1 -1
  6. package/dist/analyzer-cli.js.map +1 -1
  7. package/dist/analyzer.d.ts +14 -0
  8. package/dist/analyzer.js +1 -1
  9. package/dist/cli.cjs +470 -0
  10. package/dist/cli.cjs.map +1 -0
  11. package/dist/cli.d.cts +1 -0
  12. package/dist/cli.d.ts +1 -0
  13. package/dist/cli.js +471 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/engine-DPRxs6eC.js +181 -0
  16. package/dist/engine-DPRxs6eC.js.map +1 -0
  17. package/dist/engine-DWAIlwWp.cjs +204 -0
  18. package/dist/engine-DWAIlwWp.cjs.map +1 -0
  19. package/dist/gherkinParser-CHpkEwii.cjs +243 -0
  20. package/dist/gherkinParser-CHpkEwii.cjs.map +1 -0
  21. package/dist/gherkinParser-CKARHgt4.js +188 -0
  22. package/dist/gherkinParser-CKARHgt4.js.map +1 -0
  23. package/dist/hooks-CGYzwDOv.cjs +117 -0
  24. package/dist/hooks-CGYzwDOv.cjs.map +1 -0
  25. package/dist/hooks-CywugMQQ.js +82 -0
  26. package/dist/hooks-CywugMQQ.js.map +1 -0
  27. package/dist/hooks-Dar49TtT.d.cts +189 -0
  28. package/dist/hooks-Dar49TtT.d.ts +189 -0
  29. package/dist/runtime.cjs +13 -0
  30. package/dist/runtime.d.cts +142 -0
  31. package/dist/runtime.d.ts +142 -0
  32. package/dist/runtime.js +3 -0
  33. package/dist/step-forge.cjs +185 -306
  34. package/dist/step-forge.cjs.map +1 -1
  35. package/dist/step-forge.d.cts +193 -733
  36. package/dist/step-forge.d.ts +193 -733
  37. package/dist/step-forge.js +174 -276
  38. package/dist/step-forge.js.map +1 -1
  39. package/package.json +20 -9
  40. package/dist/analyzer-QH03uejK.js.map +0 -1
@@ -1,62 +1,44 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
- //#endregion
24
- let _cucumber_cucumber = require("@cucumber/cucumber");
2
+ const require_gherkinParser = require("./gherkinParser-CHpkEwii.cjs");
3
+ const require_hooks = require("./hooks-CGYzwDOv.cjs");
25
4
  let lodash = require("lodash");
26
- lodash = __toESM(lodash, 1);
27
- let node_fs_promises = require("node:fs/promises");
28
- let node_path = require("node:path");
29
- node_path = __toESM(node_path, 1);
5
+ lodash = require_gherkinParser.__toESM(lodash, 1);
30
6
  let typescript = require("typescript");
31
- typescript = __toESM(typescript, 1);
32
- let node_fs = require("node:fs");
33
- node_fs = __toESM(node_fs, 1);
34
- let _cucumber_gherkin = require("@cucumber/gherkin");
35
- let _cucumber_messages = require("@cucumber/messages");
36
- _cucumber_messages = __toESM(_cucumber_messages, 1);
7
+ typescript = require_gherkinParser.__toESM(typescript, 1);
37
8
  //#region src/builderTypeUtils.ts
38
9
  const isString = (statement) => typeof statement === "string";
39
10
  //#endregion
40
11
  //#region src/parsers.ts
41
- /** Matches a quoted string (`{string}`) and passes the unquoted contents through. */
12
+ /** Matches a quoted string (`{string}`) and strips the surrounding quotes. */
42
13
  const stringParser = {
43
- parse: (value) => String(value),
44
- gherkin: "{string}"
14
+ name: "string",
15
+ regexp: [/"([^"\\]*(\\.[^"\\]*)*)"/, /'([^'\\]*(\\.[^'\\]*)*)'/],
16
+ parse: (value) => {
17
+ const match = /^"([\s\S]*)"$/.exec(value) ?? /^'([\s\S]*)'$/.exec(value);
18
+ return match ? match[1].replace(/\\(["'])/g, "$1") : value;
19
+ }
45
20
  };
46
21
  /** Matches an unquoted integer (`{int}`). */
47
22
  const intParser = {
48
- parse: (value) => typeof value === "number" ? value : parseInt(String(value), 10),
49
- gherkin: "{int}"
23
+ name: "int",
24
+ regexp: /-?\d+/,
25
+ parse: (value) => parseInt(value, 10)
50
26
  };
51
27
  /** Matches an unquoted floating point number (`{float}`). */
52
28
  const numberParser = {
53
- parse: (value) => typeof value === "number" ? value : parseFloat(String(value)),
54
- gherkin: "{float}"
29
+ name: "float",
30
+ regexp: /-?\d*\.?\d+/,
31
+ parse: (value) => parseFloat(value)
55
32
  };
56
- /** Matches an unquoted `true`/`false` word (`{word}`) and parses it to a boolean. */
33
+ /**
34
+ * Matches an unquoted `true`/`false` word (`{boolean}`) and parses it to a
35
+ * boolean. Unlike the others this is a genuine custom parameter type — cucumber
36
+ * has no built-in `boolean` — so its `regexp`/`parse` are what the matcher uses.
37
+ */
57
38
  const booleanParser = {
58
- parse: (value) => value === true || value === "true",
59
- gherkin: "{word}"
39
+ name: "boolean",
40
+ regexp: /true|false/,
41
+ parse: (value) => value === "true"
60
42
  };
61
43
  //#endregion
62
44
  //#region src/utils.ts
@@ -95,187 +77,123 @@ const requireFromThen = (keys, world) => {
95
77
  };
96
78
  //#endregion
97
79
  //#region src/common.ts
98
- const cucFunctionMap = {
99
- given: _cucumber_cucumber.Given,
100
- when: _cucumber_cucumber.When,
101
- then: _cucumber_cucumber.Then
102
- };
80
+ /**
81
+ * The runtime core of the builder chain. `addStep` is deliberately phase-agnostic:
82
+ * the calling builder (given/when/then) has already computed the exact type of the
83
+ * step function via its two type parameters:
84
+ *
85
+ * - `StepFnInput` — the `{ variables, given, when, then }` object the step receives,
86
+ * with each phase already narrowed to its declared dependencies.
87
+ * - `StepFnOutput` — the phase-appropriate return type (`Partial<State>`, or `void`).
88
+ *
89
+ * Everything else is plain runtime data (a statement function, the step type, the
90
+ * dependency map, the parsers), so `addStep` carries no generics for them.
91
+ */
103
92
  const addStep = (statement, stepType, dependencies = {
104
93
  given: {},
105
94
  when: {},
106
95
  then: {}
107
96
  }, declaredParsers) => (stepFunction) => {
108
- const statementFunction = statement;
109
97
  const { given: givenDependencies, when: whenDependencies, then: thenDependencies } = dependencies;
110
- const argCount = statementFunction.length;
98
+ const argCount = statement.length;
111
99
  const parsers = declaredParsers ?? Array.from({ length: argCount }, () => stringParser);
112
- const expression = statementFunction(...parsers.map((parser) => parser.gherkin));
100
+ const expression = statement(...parsers.map((parser) => `{${parser.name}}`));
101
+ const execute = async (world, capturedArgs) => {
102
+ const requiredKeys = (deps) => Object.entries(deps).filter(([, value]) => value === "required").map(([key]) => key);
103
+ const result = await stepFunction({
104
+ variables: capturedArgs,
105
+ given: {
106
+ ...lodash.default.pick(world.given, Object.keys(givenDependencies)),
107
+ ...requireFromGiven(requiredKeys(givenDependencies), world)
108
+ },
109
+ when: {
110
+ ...lodash.default.pick(world.when, Object.keys(whenDependencies)),
111
+ ...requireFromWhen(requiredKeys(whenDependencies), world)
112
+ },
113
+ then: {
114
+ ...lodash.default.pick(world.then, Object.keys(thenDependencies)),
115
+ ...requireFromThen(requiredKeys(thenDependencies), world)
116
+ }
117
+ });
118
+ world[stepType].merge({ ...result });
119
+ };
120
+ require_hooks.globalRegistry.add({
121
+ stepType,
122
+ expression,
123
+ parsers,
124
+ execute
125
+ });
113
126
  return {
114
127
  statement,
115
128
  expression,
116
129
  dependencies,
117
130
  stepType,
118
- stepFunction,
119
- register: () => {
120
- const cucStepFunction = Object.defineProperty(async function(...args) {
121
- const coercedArgs = parsers.map((parser, index) => parser.parse(args[index]));
122
- const ensuredGivenValues = requireFromGiven(Object.entries(givenDependencies ?? {}).filter(([, value]) => value === "required").map(([key]) => key), this);
123
- const narrowedGiven = {
124
- ...lodash.default.pick(this.given, Object.keys(givenDependencies ?? {})),
125
- ...ensuredGivenValues
126
- };
127
- const ensuredWhenValues = requireFromWhen(Object.entries(whenDependencies ?? {}).filter(([, value]) => value === "required").map(([key]) => key), this);
128
- const narrowedWhen = {
129
- ...lodash.default.pick(this.when, Object.keys(whenDependencies ?? {})),
130
- ...ensuredWhenValues
131
- };
132
- const ensuredThenValues = requireFromThen(Object.entries(thenDependencies ?? {}).filter(([, value]) => value === "required").map(([key]) => key), this);
133
- const result = await stepFunction({
134
- variables: coercedArgs,
135
- given: narrowedGiven,
136
- when: narrowedWhen,
137
- then: {
138
- ...lodash.default.pick(this.then, Object.keys(thenDependencies ?? {})),
139
- ...ensuredThenValues
140
- }
141
- });
142
- this[stepType].merge({ ...result });
143
- }, "length", {
144
- value: argCount,
145
- configurable: true
146
- });
147
- const cucStep = cucFunctionMap[stepType];
148
- cucStep(expression, cucStepFunction);
149
- return {
150
- stepType,
151
- expression,
152
- dependencies,
153
- statement: statementFunction,
154
- stepFunction
155
- };
156
- }
131
+ stepFunction
157
132
  };
158
133
  };
159
134
  //#endregion
160
135
  //#region src/given.ts
161
- const givenDependencies = (statement, stepType, parsers) => (dependencies) => {
162
- return { step: addStep(statement, stepType, {
163
- ...dependencies,
164
- when: {},
165
- then: {}
166
- }, parsers) };
167
- };
168
- const givenParsers = (statement, stepType) => (parsers) => {
169
- return {
170
- dependencies: givenDependencies(statement, stepType, parsers),
171
- step: addStep(statement, stepType, void 0, parsers)
172
- };
173
- };
174
- const givenStatement = (stepType) => (statement) => {
175
- let normalizedStatement;
176
- if (isString(statement)) normalizedStatement = (() => statement);
177
- else normalizedStatement = statement;
136
+ const GIVEN = "given";
137
+ const givenDependencies = (statement, parsers) => (dependencies) => ({ step: addStep(statement, GIVEN, {
138
+ ...dependencies,
139
+ when: {},
140
+ then: {}
141
+ }, parsers) });
142
+ const givenParsers = (statement) => (parsers) => ({
143
+ dependencies: givenDependencies(statement, parsers),
144
+ step: addStep(statement, GIVEN, void 0, parsers)
145
+ });
146
+ const givenStatement = () => (statement) => {
147
+ const normalizedStatement = isString(statement) ? () => statement : statement;
178
148
  return {
179
- dependencies: givenDependencies(normalizedStatement, stepType),
180
- parsers: givenParsers(normalizedStatement, stepType),
181
- step: addStep(normalizedStatement, stepType)
149
+ dependencies: givenDependencies(normalizedStatement),
150
+ parsers: givenParsers(normalizedStatement),
151
+ step: addStep(normalizedStatement, GIVEN)
182
152
  };
183
153
  };
184
- const givenBuilder = () => {
185
- return { statement: givenStatement("given") };
186
- };
154
+ const givenBuilder = () => ({ statement: givenStatement() });
187
155
  //#endregion
188
156
  //#region src/when.ts
189
- const whenDependencies = (statement, stepType, parsers) => (dependencies) => {
190
- return { step: addStep(statement, stepType, {
191
- given: dependencies.given ?? {},
192
- when: dependencies.when ?? {},
193
- then: {}
194
- }, parsers) };
195
- };
196
- const whenParsers = (statement, stepType) => (parsers) => {
197
- return {
198
- dependencies: whenDependencies(statement, stepType, parsers),
199
- step: addStep(statement, stepType, void 0, parsers)
200
- };
201
- };
202
- const whenStatement = (stepType) => (statement) => {
203
- let normalizedStatement;
204
- if (isString(statement)) normalizedStatement = (() => statement);
205
- else normalizedStatement = statement;
157
+ const WHEN = "when";
158
+ const whenDependencies = (statement, parsers) => (dependencies) => ({ step: addStep(statement, WHEN, {
159
+ given: dependencies.given ?? {},
160
+ when: dependencies.when ?? {},
161
+ then: {}
162
+ }, parsers) });
163
+ const whenParsers = (statement) => (parsers) => ({
164
+ dependencies: whenDependencies(statement, parsers),
165
+ step: addStep(statement, WHEN, void 0, parsers)
166
+ });
167
+ const whenStatement = () => (statement) => {
168
+ const normalizedStatement = isString(statement) ? () => statement : statement;
206
169
  return {
207
- dependencies: whenDependencies(normalizedStatement, stepType),
208
- parsers: whenParsers(normalizedStatement, stepType),
209
- step: addStep(normalizedStatement, stepType)
170
+ dependencies: whenDependencies(normalizedStatement),
171
+ parsers: whenParsers(normalizedStatement),
172
+ step: addStep(normalizedStatement, WHEN)
210
173
  };
211
174
  };
212
- const whenBuilder = () => {
213
- return { statement: whenStatement("when") };
214
- };
175
+ const whenBuilder = () => ({ statement: whenStatement() });
215
176
  //#endregion
216
177
  //#region src/then.ts
217
- const thenDependencies = (statement, stepType, parsers) => (dependencies) => {
218
- return { step: addStep(statement, stepType, {
219
- given: dependencies.given ?? {},
220
- when: dependencies.when ?? {},
221
- then: dependencies.then ?? {}
222
- }, parsers) };
223
- };
224
- const thenParsers = (statement, stepType) => (parsers) => {
178
+ const THEN = "then";
179
+ const thenDependencies = (statement, parsers) => (dependencies) => ({ step: addStep(statement, THEN, {
180
+ given: dependencies.given ?? {},
181
+ when: dependencies.when ?? {},
182
+ then: dependencies.then ?? {}
183
+ }, parsers) });
184
+ const thenParsers = (statement) => (parsers) => ({
185
+ dependencies: thenDependencies(statement, parsers),
186
+ step: addStep(statement, THEN, void 0, parsers)
187
+ });
188
+ const thenStatement = () => (statement) => {
189
+ const normalizedStatement = isString(statement) ? () => statement : statement;
225
190
  return {
226
- dependencies: thenDependencies(statement, stepType, parsers),
227
- step: addStep(statement, stepType, void 0, parsers)
191
+ dependencies: thenDependencies(normalizedStatement),
192
+ parsers: thenParsers(normalizedStatement),
193
+ step: addStep(normalizedStatement, THEN)
228
194
  };
229
195
  };
230
- const thenStatement = (stepType) => (statement) => {
231
- let normalizedStatement;
232
- if (isString(statement)) normalizedStatement = (() => statement);
233
- else normalizedStatement = statement;
234
- return {
235
- dependencies: thenDependencies(normalizedStatement, stepType),
236
- parsers: thenParsers(normalizedStatement, stepType),
237
- step: addStep(normalizedStatement, stepType)
238
- };
239
- };
240
- const thenBuilder = () => {
241
- return { statement: thenStatement("then") };
242
- };
243
- //#endregion
244
- //#region src/world.ts
245
- function mergeCustomizer(objValue, srcValue) {
246
- if (lodash.default.isArray(objValue)) return objValue.concat(srcValue);
247
- else if (objValue && !lodash.default.isPlainObject(objValue) && objValue !== srcValue) throw new Error(`Merge would have destroyed previous value ${objValue} with ${srcValue}`);
248
- return objValue;
249
- }
250
- var BasicWorld = class {
251
- givenState = {};
252
- whenState = {};
253
- thenState = {};
254
- get given() {
255
- return {
256
- ...this.givenState,
257
- merge: (newState) => {
258
- this.givenState = lodash.default.merge({ ...this.givenState }, newState, mergeCustomizer);
259
- }
260
- };
261
- }
262
- get when() {
263
- return {
264
- ...this.whenState,
265
- merge: (newState) => {
266
- this.whenState = lodash.default.merge({ ...this.whenState }, newState, mergeCustomizer);
267
- }
268
- };
269
- }
270
- get then() {
271
- return {
272
- ...this.thenState,
273
- merge: (newState) => {
274
- this.thenState = lodash.default.merge({ ...this.thenState }, newState, mergeCustomizer);
275
- }
276
- };
277
- }
278
- };
196
+ const thenBuilder = () => ({ statement: thenStatement() });
279
197
  //#endregion
280
198
  //#region src/analyzer/stepExtractor.ts
281
199
  const BUILDER_NAMES = {
@@ -311,7 +229,7 @@ function extractStepDefinitions(filePaths, tsConfigPath) {
311
229
  function extractFromSourceFile(sourceFile, checker) {
312
230
  const results = [];
313
231
  function visit(node) {
314
- if (typescript.default.isCallExpression(node) && typescript.default.isPropertyAccessExpression(node.expression) && node.expression.name.text === "register") {
232
+ if (typescript.default.isCallExpression(node) && typescript.default.isPropertyAccessExpression(node.expression) && node.expression.name.text === "step") {
315
233
  const meta = extractFromRegisterCall(node, sourceFile, checker);
316
234
  if (meta) results.push(meta);
317
235
  }
@@ -494,105 +412,6 @@ function resolveReExportedCall(chain, checker) {
494
412
  return null;
495
413
  }
496
414
  //#endregion
497
- //#region src/analyzer/gherkinParser.ts
498
- function parseFeatureFiles(filePaths) {
499
- const scenarios = [];
500
- for (const filePath of filePaths) {
501
- const parsed = parseFeatureContent(node_fs.readFileSync(filePath, "utf-8"), filePath);
502
- scenarios.push(...parsed);
503
- }
504
- return scenarios;
505
- }
506
- function parseFeatureContent(content, filePath) {
507
- const feature = new _cucumber_gherkin.Parser(new _cucumber_gherkin.AstBuilder(_cucumber_messages.IdGenerator.uuid()), new _cucumber_gherkin.GherkinClassicTokenMatcher()).parse(content).feature;
508
- if (!feature) return [];
509
- const featureBackground = [];
510
- const scenarios = [];
511
- for (const child of feature.children) {
512
- if (child.background) featureBackground.push(...child.background.steps);
513
- if (child.scenario) scenarios.push(...expandScenario(child.scenario, featureBackground, filePath));
514
- if (child.rule) {
515
- const ruleBackground = [...featureBackground];
516
- for (const ruleChild of child.rule.children) {
517
- if (ruleChild.background) ruleBackground.push(...ruleChild.background.steps);
518
- if (ruleChild.scenario) scenarios.push(...expandScenario(ruleChild.scenario, ruleBackground, filePath));
519
- }
520
- }
521
- }
522
- return scenarios;
523
- }
524
- function expandScenario(scenario, backgroundSteps, filePath) {
525
- if (!(scenario.examples.length > 0 && scenario.examples.some((e) => e.tableBody.length > 0))) {
526
- const bgParsed = convertSteps(backgroundSteps);
527
- const scenarioParsed = convertSteps(scenario.steps);
528
- const allSteps = resolveEffectiveKeywords([...bgParsed, ...scenarioParsed]);
529
- return [{
530
- name: scenario.name,
531
- file: filePath,
532
- steps: allSteps
533
- }];
534
- }
535
- const results = [];
536
- for (const example of scenario.examples) {
537
- if (!example.tableHeader || example.tableBody.length === 0) continue;
538
- const headers = example.tableHeader.cells.map((c) => c.value);
539
- for (const row of example.tableBody) {
540
- const values = row.cells.map((c) => c.value);
541
- const substitution = {};
542
- headers.forEach((h, i) => {
543
- substitution[h] = values[i];
544
- });
545
- const bgParsed = convertSteps(backgroundSteps);
546
- const scenarioSteps = convertSteps(scenario.steps).map((step) => ({
547
- ...step,
548
- text: substituteExampleValues(step.text, substitution)
549
- }));
550
- const allSteps = resolveEffectiveKeywords([...bgParsed, ...scenarioSteps]);
551
- results.push({
552
- name: `${scenario.name} (${values.join(", ")})`,
553
- file: filePath,
554
- steps: allSteps
555
- });
556
- }
557
- }
558
- return results;
559
- }
560
- function convertSteps(steps) {
561
- return steps.map((step) => ({
562
- keyword: normalizeKeyword(step.keyword),
563
- text: step.text,
564
- line: step.location.line,
565
- column: (step.location.column ?? 1) + step.keyword.length
566
- }));
567
- }
568
- function normalizeKeyword(keyword) {
569
- const trimmed = keyword.trim();
570
- if (trimmed === "Given") return "Given";
571
- if (trimmed === "When") return "When";
572
- if (trimmed === "Then") return "Then";
573
- if (trimmed === "And") return "And";
574
- if (trimmed === "But") return "But";
575
- return "Given";
576
- }
577
- function resolveEffectiveKeywords(steps) {
578
- let lastEffective = "Given";
579
- return steps.map((step) => {
580
- let effectiveKeyword;
581
- if (step.keyword === "And" || step.keyword === "But") effectiveKeyword = lastEffective;
582
- else effectiveKeyword = step.keyword;
583
- lastEffective = effectiveKeyword;
584
- return {
585
- ...step,
586
- effectiveKeyword
587
- };
588
- });
589
- }
590
- function substituteExampleValues(text, substitution) {
591
- let result = text;
592
- for (const [key, value] of Object.entries(substitution)) result = result.replace(new RegExp(`<${key}>`, "g"), value);
593
- return result;
594
- }
595
- //#endregion
596
415
  //#region src/analyzer/stepMatcher.ts
597
416
  function compileDefinitions(definitions) {
598
417
  const compiled = [];
@@ -727,12 +546,12 @@ function runRules(rules, scenario, matchedSteps) {
727
546
  //#region src/analyzer/index.ts
728
547
  async function analyze(config, options) {
729
548
  const rules = options?.rules ?? defaultRules;
730
- const stepFilePaths = await resolveGlobs(config.stepFiles);
731
- const featureFilePaths = await resolveGlobs(config.featureFiles);
549
+ const stepFilePaths = await require_gherkinParser.globFiles(config.stepFiles);
550
+ const featureFilePaths = await require_gherkinParser.globFiles(config.featureFiles);
732
551
  if (stepFilePaths.length === 0) return [];
733
552
  if (featureFilePaths.length === 0) return [];
734
553
  const stepDefinitions = extractStepDefinitions(stepFilePaths, config.tsConfigPath);
735
- const scenarios = parseFeatureFiles(featureFilePaths);
554
+ const scenarios = require_gherkinParser.parseFeatureFiles(featureFilePaths);
736
555
  const diagnostics = [];
737
556
  for (const scenario of scenarios) {
738
557
  const scenarioDiags = runRules(rules, scenario, matchScenarioSteps(scenario, stepDefinitions));
@@ -740,11 +559,6 @@ async function analyze(config, options) {
740
559
  }
741
560
  return diagnostics;
742
561
  }
743
- async function resolveGlobs(patterns) {
744
- const files = [];
745
- for (const pattern of patterns) for await (const file of (0, node_fs_promises.glob)(pattern)) files.push(node_path.resolve(file));
746
- return [...new Set(files)];
747
- }
748
562
  //#endregion
749
563
  //#region src/init.ts
750
564
  const createBuilders = () => {
@@ -755,19 +569,84 @@ const createBuilders = () => {
755
569
  };
756
570
  };
757
571
  //#endregion
572
+ //#region src/hooks.ts
573
+ /**
574
+ * Run before every scenario, with access to that scenario's fresh world. For
575
+ * side effects only (reset a mock, seed an external system) — return values are
576
+ * ignored; seed test *state* with given steps so the dependency graph stays the
577
+ * single source of truth.
578
+ */
579
+ function beforeScenario(fn) {
580
+ require_hooks.globalHookRegistry.add({
581
+ scope: "scenario",
582
+ timing: "before",
583
+ fn
584
+ });
585
+ }
586
+ /**
587
+ * Run after every scenario (even when a step failed), with access to that
588
+ * scenario's world. Runs in reverse registration order so teardown unwinds
589
+ * setup. For cleanup side effects only.
590
+ */
591
+ function afterScenario(fn) {
592
+ require_hooks.globalHookRegistry.add({
593
+ scope: "scenario",
594
+ timing: "after",
595
+ fn
596
+ });
597
+ }
598
+ /** Run once at the start of each feature file. No world exists at this boundary. */
599
+ function beforeFeature(fn) {
600
+ require_hooks.globalHookRegistry.add({
601
+ scope: "feature",
602
+ timing: "before",
603
+ fn
604
+ });
605
+ }
606
+ /** Run once at the end of each feature file (reverse registration order). */
607
+ function afterFeature(fn) {
608
+ require_hooks.globalHookRegistry.add({
609
+ scope: "feature",
610
+ timing: "after",
611
+ fn
612
+ });
613
+ }
614
+ /** Run once before the entire test run, across all feature files. */
615
+ function beforeAll(fn) {
616
+ require_hooks.globalHookRegistry.add({
617
+ scope: "global",
618
+ timing: "before",
619
+ fn
620
+ });
621
+ }
622
+ /** Run once after the entire test run (reverse registration order). */
623
+ function afterAll(fn) {
624
+ require_hooks.globalHookRegistry.add({
625
+ scope: "global",
626
+ timing: "after",
627
+ fn
628
+ });
629
+ }
630
+ //#endregion
758
631
  //#region src/index.ts
759
632
  const analyzer = {
760
633
  analyze,
761
634
  extractStepDefinitions,
762
- parseFeatureFiles,
763
- parseFeatureContent,
635
+ parseFeatureFiles: require_gherkinParser.parseFeatureFiles,
636
+ parseFeatureContent: require_gherkinParser.parseFeatureContent,
764
637
  matchScenarioSteps,
765
638
  findMatchingDefinitions,
766
639
  defaultRules
767
640
  };
768
641
  //#endregion
769
- exports.BasicWorld = BasicWorld;
642
+ exports.BasicWorld = require_gherkinParser.BasicWorld;
643
+ exports.afterAll = afterAll;
644
+ exports.afterFeature = afterFeature;
645
+ exports.afterScenario = afterScenario;
770
646
  exports.analyzer = analyzer;
647
+ exports.beforeAll = beforeAll;
648
+ exports.beforeFeature = beforeFeature;
649
+ exports.beforeScenario = beforeScenario;
771
650
  exports.booleanParser = booleanParser;
772
651
  exports.createBuilders = createBuilders;
773
652
  exports.givenBuilder = givenBuilder;