@fabricorg/databricks-bdd 0.3.1 → 0.4.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.
Files changed (49) hide show
  1. package/README.md +56 -5
  2. package/dist/actions-CuXXKUqe.d.ts +92 -0
  3. package/dist/actions-CxfxhBZh.d.cts +92 -0
  4. package/dist/actions.cjs +14 -0
  5. package/dist/actions.cjs.map +1 -0
  6. package/dist/actions.d.cts +6 -0
  7. package/dist/actions.d.ts +6 -0
  8. package/dist/actions.js +3 -0
  9. package/dist/actions.js.map +1 -0
  10. package/dist/cardinality.cjs +24 -0
  11. package/dist/cardinality.cjs.map +1 -0
  12. package/dist/cardinality.d.cts +19 -0
  13. package/dist/cardinality.d.ts +19 -0
  14. package/dist/cardinality.js +3 -0
  15. package/dist/cardinality.js.map +1 -0
  16. package/dist/{chunk-FNTI2VUP.js → chunk-3XDMBHFP.js} +117 -4
  17. package/dist/chunk-3XDMBHFP.js.map +1 -0
  18. package/dist/chunk-6JC65RH2.js +11 -0
  19. package/dist/chunk-6JC65RH2.js.map +1 -0
  20. package/dist/chunk-HTKRJLVX.js +70 -0
  21. package/dist/chunk-HTKRJLVX.js.map +1 -0
  22. package/dist/chunk-K5LJ7WSW.js +22 -0
  23. package/dist/chunk-K5LJ7WSW.js.map +1 -0
  24. package/dist/chunk-SZL3KWW7.js +62 -0
  25. package/dist/chunk-SZL3KWW7.js.map +1 -0
  26. package/dist/fixtures.cjs +65 -0
  27. package/dist/fixtures.cjs.map +1 -0
  28. package/dist/fixtures.d.cts +34 -0
  29. package/dist/fixtures.d.ts +34 -0
  30. package/dist/fixtures.js +3 -0
  31. package/dist/fixtures.js.map +1 -0
  32. package/dist/index.cjs +277 -0
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +24 -55
  35. package/dist/index.d.ts +24 -55
  36. package/dist/index.js +5 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/scoped-state.cjs +74 -0
  39. package/dist/scoped-state.cjs.map +1 -0
  40. package/dist/scoped-state.d.cts +30 -0
  41. package/dist/scoped-state.d.ts +30 -0
  42. package/dist/scoped-state.js +3 -0
  43. package/dist/scoped-state.js.map +1 -0
  44. package/dist/steps.cjs +254 -2
  45. package/dist/steps.cjs.map +1 -1
  46. package/dist/steps.js +15 -4
  47. package/dist/steps.js.map +1 -1
  48. package/package.json +41 -1
  49. package/dist/chunk-FNTI2VUP.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,60 +1,13 @@
1
- import { World, IWorldOptions } from '@cucumber/cucumber';
2
- import { TestProfile, ExecutionContext, TableFixture, WorkspaceClientLike, PollOptions } from '@fabricorg/databricks-testkit';
1
+ export { D as DatabricksWorld, a as DatabricksWorldParameters, S as ScenarioAction, d as defineAction, r as runAction } from './actions-CxfxhBZh.cjs';
2
+ import { TestProfile, TableFixture, WorkspaceClientLike, PollOptions } from '@fabricorg/databricks-testkit';
3
3
  export { waitForPipelineUpdate } from '@fabricorg/databricks-testkit';
4
- import { StepOutputCapture } from './output-capture.cjs';
5
- export { CapturedStepOutput, StepOutputCaptureOptions, startStepOutputCapture } from './output-capture.cjs';
6
4
  export { RUN_STATES, RunState, parseDuration, parseTableIdentifier } from './parameter-types.cjs';
7
5
  export { EvidenceReport, EvidenceScenario, FAILURE_EVIDENCE_MEDIA_TYPE, StepFailureEvidence, parseFailureEvidenceAttachment, redactSecrets } from './evidence-formatter.cjs';
8
-
9
- interface DatabricksWorldParameters {
10
- /** Default schema for the execution context; overridable per scenario via the catalog/schema step. */
11
- schema?: string;
12
- /** Arbitrary typed runner configuration, comparable to behave `-D` userdata. */
13
- userdata?: Record<string, string | number | boolean>;
14
- }
15
- type Cleanup = () => void | Promise<void>;
16
- /**
17
- * Cucumber World holding one ExecutionContext per scenario. The context is
18
- * created lazily on first use so `Given catalog ... and schema ...` can run
19
- * first; it is closed by the After hook registered in ./steps.ts.
20
- */
21
- declare class DatabricksWorld extends World<DatabricksWorldParameters> {
22
- readonly profile: TestProfile;
23
- /** Rows from the last `When I run the SQL` / `When I query table` step. */
24
- lastRows: Record<string, unknown>[] | null;
25
- /** Error captured from the last statement, for `Then the statement fails ...`. */
26
- lastError: Error | null;
27
- /** Terminal run record from the last `When I run job ...` step. */
28
- lastRun: Record<string, unknown> | null;
29
- /** Pipeline update handle from `When I start a full refresh of pipeline ...`. */
30
- lastPipelineUpdate: {
31
- pipelineId: string;
32
- updateId: string;
33
- } | null;
34
- schemaOverride: string | undefined;
35
- catalogOverride: string | undefined;
36
- lastSql: string | undefined;
37
- lastStatementId: string | undefined;
38
- stepOutputCapture: StepOutputCapture | undefined;
39
- lakebasePool: {
40
- query<T extends Record<string, unknown>>(sql: string): Promise<{
41
- rows: T[];
42
- }>;
43
- } | undefined;
44
- private ctx;
45
- private activeCtx;
46
- private readonly cleanups;
47
- constructor(options: IWorldOptions<DatabricksWorldParameters>);
48
- context(): Promise<ExecutionContext>;
49
- /** Route subsequent steps through a scenario-owned secondary identity. */
50
- useExecutionContext(context: ExecutionContext): void;
51
- /** Register scenario-scoped cleanup. Cleanups run in reverse order. */
52
- addCleanup(cleanup: Cleanup): void;
53
- /** Resolve runner userdata from worldParameters first, then DBX_TEST_USERDATA_* env. */
54
- userdata(name: string): string | number | boolean | undefined;
55
- scopeTo(catalog: string, schema: string): void;
56
- dispose(): Promise<void>;
57
- }
6
+ export { CapturedStepOutput, StepOutputCapture, StepOutputCaptureOptions, startStepOutputCapture } from './output-capture.cjs';
7
+ export { FixtureCleanup, FixtureSetup, SharedFixtureRegistry, sharedFixtures } from './fixtures.cjs';
8
+ export { CardinalityOptions, CardinalityResult, FieldCardinality, parseCardinalityField } from './cardinality.cjs';
9
+ export { ScopedState, SharedStateRegistry, StateScope, sharedState } from './scoped-state.cjs';
10
+ import '@cucumber/cucumber';
58
11
 
59
12
  /**
60
13
  * Tags that require a live workspace (or a long budget). Scenarios carrying
@@ -63,6 +16,22 @@ declare class DatabricksWorld extends World<DatabricksWorldParameters> {
63
16
  declare const LIVE_ONLY_TAGS: readonly ["@live", "@jobs", "@dlt", "@pipeline", "@autoloader", "@lakebase", "@slow"];
64
17
  /** Returns a skip reason when the scenario cannot run under the given profile, else null. */
65
18
  declare function liveOnlyReason(tags: readonly string[], profile: TestProfile): string | null;
19
+ type CapabilityValue = string | number | boolean;
20
+ type CapabilityMap = Readonly<Record<string, CapabilityValue>>;
21
+ /** Build the runtime capability map used by active-tag predicates. */
22
+ declare function capabilitiesFromEnv(profile: TestProfile, env?: NodeJS.ProcessEnv): CapabilityMap;
23
+ /**
24
+ * Evaluate TypeScript-native active tags.
25
+ *
26
+ * Supported forms:
27
+ * - `@requires.cloud=azure` / `@requires.serverless`
28
+ * - `@excludes.compute=classic`
29
+ * - Behave-compatible aliases `@use.with_cloud=azure` and
30
+ * `@not.with_cloud=azure`
31
+ */
32
+ declare function capabilityTagReason(tags: readonly string[], capabilities: CapabilityMap): string | null;
33
+ /** Combined local/live and active-capability skip policy. */
34
+ declare function scenarioSkipReason(tags: readonly string[], profile: TestProfile, env?: NodeJS.ProcessEnv): string | null;
66
35
 
67
36
  /**
68
37
  * Turn a Gherkin data table (header row + string cells) into a TableFixture,
@@ -119,4 +88,4 @@ interface RunFeaturesResult {
119
88
  */
120
89
  declare function runFeatures(options: RunFeaturesOptions): Promise<RunFeaturesResult>;
121
90
 
122
- export { DatabricksWorld, type DatabricksWorldParameters, type JobRunResult, LIVE_ONLY_TAGS, type PipelineWaitOptions, type RunFeaturesOptions, type RunFeaturesResult, type RunJobOptions, StepOutputCapture, coerceMatchRows, inferFixture, liveOnlyReason, resolveJobId, resolvePipelineId, runFeatures, runJobToTerminal, startPipelineUpdate };
91
+ export { type CapabilityMap, type CapabilityValue, type JobRunResult, LIVE_ONLY_TAGS, type PipelineWaitOptions, type RunFeaturesOptions, type RunFeaturesResult, type RunJobOptions, capabilitiesFromEnv, capabilityTagReason, coerceMatchRows, inferFixture, liveOnlyReason, resolveJobId, resolvePipelineId, runFeatures, runJobToTerminal, scenarioSkipReason, startPipelineUpdate };
package/dist/index.d.ts CHANGED
@@ -1,60 +1,13 @@
1
- import { World, IWorldOptions } from '@cucumber/cucumber';
2
- import { TestProfile, ExecutionContext, TableFixture, WorkspaceClientLike, PollOptions } from '@fabricorg/databricks-testkit';
1
+ export { D as DatabricksWorld, a as DatabricksWorldParameters, S as ScenarioAction, d as defineAction, r as runAction } from './actions-CuXXKUqe.js';
2
+ import { TestProfile, TableFixture, WorkspaceClientLike, PollOptions } from '@fabricorg/databricks-testkit';
3
3
  export { waitForPipelineUpdate } from '@fabricorg/databricks-testkit';
4
- import { StepOutputCapture } from './output-capture.js';
5
- export { CapturedStepOutput, StepOutputCaptureOptions, startStepOutputCapture } from './output-capture.js';
6
4
  export { RUN_STATES, RunState, parseDuration, parseTableIdentifier } from './parameter-types.js';
7
5
  export { EvidenceReport, EvidenceScenario, FAILURE_EVIDENCE_MEDIA_TYPE, StepFailureEvidence, parseFailureEvidenceAttachment, redactSecrets } from './evidence-formatter.js';
8
-
9
- interface DatabricksWorldParameters {
10
- /** Default schema for the execution context; overridable per scenario via the catalog/schema step. */
11
- schema?: string;
12
- /** Arbitrary typed runner configuration, comparable to behave `-D` userdata. */
13
- userdata?: Record<string, string | number | boolean>;
14
- }
15
- type Cleanup = () => void | Promise<void>;
16
- /**
17
- * Cucumber World holding one ExecutionContext per scenario. The context is
18
- * created lazily on first use so `Given catalog ... and schema ...` can run
19
- * first; it is closed by the After hook registered in ./steps.ts.
20
- */
21
- declare class DatabricksWorld extends World<DatabricksWorldParameters> {
22
- readonly profile: TestProfile;
23
- /** Rows from the last `When I run the SQL` / `When I query table` step. */
24
- lastRows: Record<string, unknown>[] | null;
25
- /** Error captured from the last statement, for `Then the statement fails ...`. */
26
- lastError: Error | null;
27
- /** Terminal run record from the last `When I run job ...` step. */
28
- lastRun: Record<string, unknown> | null;
29
- /** Pipeline update handle from `When I start a full refresh of pipeline ...`. */
30
- lastPipelineUpdate: {
31
- pipelineId: string;
32
- updateId: string;
33
- } | null;
34
- schemaOverride: string | undefined;
35
- catalogOverride: string | undefined;
36
- lastSql: string | undefined;
37
- lastStatementId: string | undefined;
38
- stepOutputCapture: StepOutputCapture | undefined;
39
- lakebasePool: {
40
- query<T extends Record<string, unknown>>(sql: string): Promise<{
41
- rows: T[];
42
- }>;
43
- } | undefined;
44
- private ctx;
45
- private activeCtx;
46
- private readonly cleanups;
47
- constructor(options: IWorldOptions<DatabricksWorldParameters>);
48
- context(): Promise<ExecutionContext>;
49
- /** Route subsequent steps through a scenario-owned secondary identity. */
50
- useExecutionContext(context: ExecutionContext): void;
51
- /** Register scenario-scoped cleanup. Cleanups run in reverse order. */
52
- addCleanup(cleanup: Cleanup): void;
53
- /** Resolve runner userdata from worldParameters first, then DBX_TEST_USERDATA_* env. */
54
- userdata(name: string): string | number | boolean | undefined;
55
- scopeTo(catalog: string, schema: string): void;
56
- dispose(): Promise<void>;
57
- }
6
+ export { CapturedStepOutput, StepOutputCapture, StepOutputCaptureOptions, startStepOutputCapture } from './output-capture.js';
7
+ export { FixtureCleanup, FixtureSetup, SharedFixtureRegistry, sharedFixtures } from './fixtures.js';
8
+ export { CardinalityOptions, CardinalityResult, FieldCardinality, parseCardinalityField } from './cardinality.js';
9
+ export { ScopedState, SharedStateRegistry, StateScope, sharedState } from './scoped-state.js';
10
+ import '@cucumber/cucumber';
58
11
 
59
12
  /**
60
13
  * Tags that require a live workspace (or a long budget). Scenarios carrying
@@ -63,6 +16,22 @@ declare class DatabricksWorld extends World<DatabricksWorldParameters> {
63
16
  declare const LIVE_ONLY_TAGS: readonly ["@live", "@jobs", "@dlt", "@pipeline", "@autoloader", "@lakebase", "@slow"];
64
17
  /** Returns a skip reason when the scenario cannot run under the given profile, else null. */
65
18
  declare function liveOnlyReason(tags: readonly string[], profile: TestProfile): string | null;
19
+ type CapabilityValue = string | number | boolean;
20
+ type CapabilityMap = Readonly<Record<string, CapabilityValue>>;
21
+ /** Build the runtime capability map used by active-tag predicates. */
22
+ declare function capabilitiesFromEnv(profile: TestProfile, env?: NodeJS.ProcessEnv): CapabilityMap;
23
+ /**
24
+ * Evaluate TypeScript-native active tags.
25
+ *
26
+ * Supported forms:
27
+ * - `@requires.cloud=azure` / `@requires.serverless`
28
+ * - `@excludes.compute=classic`
29
+ * - Behave-compatible aliases `@use.with_cloud=azure` and
30
+ * `@not.with_cloud=azure`
31
+ */
32
+ declare function capabilityTagReason(tags: readonly string[], capabilities: CapabilityMap): string | null;
33
+ /** Combined local/live and active-capability skip policy. */
34
+ declare function scenarioSkipReason(tags: readonly string[], profile: TestProfile, env?: NodeJS.ProcessEnv): string | null;
66
35
 
67
36
  /**
68
37
  * Turn a Gherkin data table (header row + string cells) into a TableFixture,
@@ -119,4 +88,4 @@ interface RunFeaturesResult {
119
88
  */
120
89
  declare function runFeatures(options: RunFeaturesOptions): Promise<RunFeaturesResult>;
121
90
 
122
- export { DatabricksWorld, type DatabricksWorldParameters, type JobRunResult, LIVE_ONLY_TAGS, type PipelineWaitOptions, type RunFeaturesOptions, type RunFeaturesResult, type RunJobOptions, StepOutputCapture, coerceMatchRows, inferFixture, liveOnlyReason, resolveJobId, resolvePipelineId, runFeatures, runJobToTerminal, startPipelineUpdate };
91
+ export { type CapabilityMap, type CapabilityValue, type JobRunResult, LIVE_ONLY_TAGS, type PipelineWaitOptions, type RunFeaturesOptions, type RunFeaturesResult, type RunJobOptions, capabilitiesFromEnv, capabilityTagReason, coerceMatchRows, inferFixture, liveOnlyReason, resolveJobId, resolvePipelineId, runFeatures, runJobToTerminal, scenarioSkipReason, startPipelineUpdate };
package/dist/index.js CHANGED
@@ -1,7 +1,11 @@
1
- export { DatabricksWorld, LIVE_ONLY_TAGS, coerceMatchRows, inferFixture, liveOnlyReason, resolveJobId, resolvePipelineId, runJobToTerminal, startPipelineUpdate, waitForPipelineUpdate } from './chunk-FNTI2VUP.js';
1
+ export { DatabricksWorld, LIVE_ONLY_TAGS, capabilitiesFromEnv, capabilityTagReason, coerceMatchRows, inferFixture, liveOnlyReason, resolveJobId, resolvePipelineId, runJobToTerminal, scenarioSkipReason, startPipelineUpdate, waitForPipelineUpdate } from './chunk-3XDMBHFP.js';
2
+ export { defineAction, runAction } from './chunk-6JC65RH2.js';
3
+ export { parseCardinalityField } from './chunk-K5LJ7WSW.js';
2
4
  export { FAILURE_EVIDENCE_MEDIA_TYPE, parseFailureEvidenceAttachment, redactSecrets } from './chunk-UQAKGVJS.js';
5
+ export { SharedFixtureRegistry, sharedFixtures } from './chunk-SZL3KWW7.js';
3
6
  export { startStepOutputCapture } from './chunk-THB7O2G6.js';
4
7
  export { RUN_STATES, parseDuration, parseTableIdentifier } from './chunk-YNOLRERF.js';
8
+ export { ScopedState, SharedStateRegistry, sharedState } from './chunk-HTKRJLVX.js';
5
9
  import { execFile } from 'child_process';
6
10
  import { createRequire } from 'module';
7
11
  import { join, dirname } from 'path';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/run-features.ts"],"names":["require"],"mappings":";;;;;;;;AA2BA,eAAsB,YAAY,OAAA,EAAyD;AACzF,EAAA,MAAMA,QAAAA,GAAU,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA;AAG7C,EAAA,MAAM,OAAA,GAAUA,QAAAA,CAAQ,OAAA,CAAQ,iCAAiC,CAAA;AACjE,EAAA,MAAM,GAAA,GAAMA,SAAQ,OAAO,CAAA;AAC3B,EAAA,MAAM,GAAA,GAAM,KAAK,OAAA,CAAQ,OAAO,GAAG,GAAA,CAAI,GAAA,CAAI,aAAa,CAAA,IAAK,iBAAiB,CAAA;AAC9E,EAAA,MAAM,MAAyB,EAAE,GAAG,QAAQ,GAAA,EAAK,GAAG,QAAQ,GAAA,EAAI;AAChE,EAAA,IAAI,OAAA,CAAQ,OAAO,IAAA,EAAM;AACvB,IAAA,MAAM,SAAA,GAAY,cAAA;AAClB,IAAA,GAAA,CAAI,YAAA,GAAe,IAAI,YAAA,GAAe,CAAA,EAAG,IAAI,YAAY,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA,GAAK,SAAA;AAAA,EAC7E;AACA,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY;AAC9B,IAAA,QAAA;AAAA,MACE,OAAA,CAAQ,QAAA;AAAA,MACR,CAAC,GAAA,EAAK,GAAI,OAAA,CAAQ,IAAA,IAAQ,EAAG,CAAA;AAAA,MAC7B,EAAE,GAAA,EAAK,OAAA,CAAQ,GAAA,EAAK,GAAA,EAAK,OAAA,EAAS,OAAA,CAAQ,SAAA,IAAa,IAAA,EAAS,SAAA,EAAW,EAAA,GAAK,IAAA,GAAO,IAAA,EAAK;AAAA,MAC5F,CAAC,KAAA,EAAO,MAAA,EAAQ,MAAA,KAAW;AACzB,QAAA,MAAM,QAAA,GACJ,KAAA,IAAS,OAAQ,KAAA,CAA6B,IAAA,KAAS,WACjD,KAAA,CAA4B,IAAA,IAAQ,CAAA,GACtC,KAAA,GACE,CAAA,GACA,CAAA;AACR,QAAA,OAAA,CAAQ,EAAE,OAAA,EAAS,QAAA,KAAa,GAAG,QAAA,EAAU,MAAA,EAAQ,GAAG,MAAM;AAAA,EAAK,MAAM,IAAI,CAAA;AAAA,MAC/E;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH","file":"index.js","sourcesContent":["import { execFile } from 'node:child_process';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\n\nexport interface RunFeaturesOptions {\n /** Directory containing the cucumber config / features. */\n cwd: string;\n /** Extra CLI args, e.g. ['--tags', 'not @slow'] or ['features/foo.feature']. */\n args?: string[];\n /** Extra environment (merged over process.env). */\n env?: Record<string, string>;\n /** Load TypeScript support code via tsx (default true). */\n tsx?: boolean;\n timeoutMs?: number;\n}\n\nexport interface RunFeaturesResult {\n success: boolean;\n exitCode: number;\n output: string;\n}\n\n/**\n * Vitest bridge: run cucumber-js as a subprocess so feature suites can be\n * embedded in an existing vitest run (one CI entry point) without fighting\n * over cucumber's global step registry or vitest's module transforms.\n */\nexport async function runFeatures(options: RunFeaturesOptions): Promise<RunFeaturesResult> {\n const require = createRequire(import.meta.url);\n // The bin path is not in the package's exports map — resolve it relative\n // to package.json (which is exported).\n const pkgPath = require.resolve('@cucumber/cucumber/package.json');\n const pkg = require(pkgPath) as { bin: Record<string, string> };\n const bin = join(dirname(pkgPath), pkg.bin['cucumber-js'] ?? 'bin/cucumber.js');\n const env: NodeJS.ProcessEnv = { ...process.env, ...options.env };\n if (options.tsx ?? true) {\n const tsxImport = '--import tsx';\n env.NODE_OPTIONS = env.NODE_OPTIONS ? `${env.NODE_OPTIONS} ${tsxImport}` : tsxImport;\n }\n return new Promise((resolve) => {\n execFile(\n process.execPath,\n [bin, ...(options.args ?? [])],\n { cwd: options.cwd, env, timeout: options.timeoutMs ?? 120_000, maxBuffer: 16 * 1024 * 1024 },\n (error, stdout, stderr) => {\n const exitCode =\n error && typeof (error as { code?: unknown }).code === 'number'\n ? ((error as { code?: number }).code ?? 1)\n : error\n ? 1\n : 0;\n resolve({ success: exitCode === 0, exitCode, output: `${stdout}\\n${stderr}` });\n },\n );\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/run-features.ts"],"names":["require"],"mappings":";;;;;;;;;;;;AA2BA,eAAsB,YAAY,OAAA,EAAyD;AACzF,EAAA,MAAMA,QAAAA,GAAU,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA;AAG7C,EAAA,MAAM,OAAA,GAAUA,QAAAA,CAAQ,OAAA,CAAQ,iCAAiC,CAAA;AACjE,EAAA,MAAM,GAAA,GAAMA,SAAQ,OAAO,CAAA;AAC3B,EAAA,MAAM,GAAA,GAAM,KAAK,OAAA,CAAQ,OAAO,GAAG,GAAA,CAAI,GAAA,CAAI,aAAa,CAAA,IAAK,iBAAiB,CAAA;AAC9E,EAAA,MAAM,MAAyB,EAAE,GAAG,QAAQ,GAAA,EAAK,GAAG,QAAQ,GAAA,EAAI;AAChE,EAAA,IAAI,OAAA,CAAQ,OAAO,IAAA,EAAM;AACvB,IAAA,MAAM,SAAA,GAAY,cAAA;AAClB,IAAA,GAAA,CAAI,YAAA,GAAe,IAAI,YAAA,GAAe,CAAA,EAAG,IAAI,YAAY,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA,GAAK,SAAA;AAAA,EAC7E;AACA,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY;AAC9B,IAAA,QAAA;AAAA,MACE,OAAA,CAAQ,QAAA;AAAA,MACR,CAAC,GAAA,EAAK,GAAI,OAAA,CAAQ,IAAA,IAAQ,EAAG,CAAA;AAAA,MAC7B,EAAE,GAAA,EAAK,OAAA,CAAQ,GAAA,EAAK,GAAA,EAAK,OAAA,EAAS,OAAA,CAAQ,SAAA,IAAa,IAAA,EAAS,SAAA,EAAW,EAAA,GAAK,IAAA,GAAO,IAAA,EAAK;AAAA,MAC5F,CAAC,KAAA,EAAO,MAAA,EAAQ,MAAA,KAAW;AACzB,QAAA,MAAM,QAAA,GACJ,KAAA,IAAS,OAAQ,KAAA,CAA6B,IAAA,KAAS,WACjD,KAAA,CAA4B,IAAA,IAAQ,CAAA,GACtC,KAAA,GACE,CAAA,GACA,CAAA;AACR,QAAA,OAAA,CAAQ,EAAE,OAAA,EAAS,QAAA,KAAa,GAAG,QAAA,EAAU,MAAA,EAAQ,GAAG,MAAM;AAAA,EAAK,MAAM,IAAI,CAAA;AAAA,MAC/E;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH","file":"index.js","sourcesContent":["import { execFile } from 'node:child_process';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\n\nexport interface RunFeaturesOptions {\n /** Directory containing the cucumber config / features. */\n cwd: string;\n /** Extra CLI args, e.g. ['--tags', 'not @slow'] or ['features/foo.feature']. */\n args?: string[];\n /** Extra environment (merged over process.env). */\n env?: Record<string, string>;\n /** Load TypeScript support code via tsx (default true). */\n tsx?: boolean;\n timeoutMs?: number;\n}\n\nexport interface RunFeaturesResult {\n success: boolean;\n exitCode: number;\n output: string;\n}\n\n/**\n * Vitest bridge: run cucumber-js as a subprocess so feature suites can be\n * embedded in an existing vitest run (one CI entry point) without fighting\n * over cucumber's global step registry or vitest's module transforms.\n */\nexport async function runFeatures(options: RunFeaturesOptions): Promise<RunFeaturesResult> {\n const require = createRequire(import.meta.url);\n // The bin path is not in the package's exports map — resolve it relative\n // to package.json (which is exported).\n const pkgPath = require.resolve('@cucumber/cucumber/package.json');\n const pkg = require(pkgPath) as { bin: Record<string, string> };\n const bin = join(dirname(pkgPath), pkg.bin['cucumber-js'] ?? 'bin/cucumber.js');\n const env: NodeJS.ProcessEnv = { ...process.env, ...options.env };\n if (options.tsx ?? true) {\n const tsxImport = '--import tsx';\n env.NODE_OPTIONS = env.NODE_OPTIONS ? `${env.NODE_OPTIONS} ${tsxImport}` : tsxImport;\n }\n return new Promise((resolve) => {\n execFile(\n process.execPath,\n [bin, ...(options.args ?? [])],\n { cwd: options.cwd, env, timeout: options.timeoutMs ?? 120_000, maxBuffer: 16 * 1024 * 1024 },\n (error, stdout, stderr) => {\n const exitCode =\n error && typeof (error as { code?: unknown }).code === 'number'\n ? ((error as { code?: number }).code ?? 1)\n : error\n ? 1\n : 0;\n resolve({ success: exitCode === 0, exitCode, output: `${stdout}\\n${stderr}` });\n },\n );\n });\n}\n"]}
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ // src/scoped-state.ts
4
+ var SharedStateRegistry = class {
5
+ runValues = /* @__PURE__ */ new Map();
6
+ featureValues = /* @__PURE__ */ new Map();
7
+ run() {
8
+ return this.runValues;
9
+ }
10
+ feature(featureUri) {
11
+ if (!featureUri) throw new Error("Feature state requires a feature URI");
12
+ let values = this.featureValues.get(featureUri);
13
+ if (!values) {
14
+ values = /* @__PURE__ */ new Map();
15
+ this.featureValues.set(featureUri, values);
16
+ }
17
+ return values;
18
+ }
19
+ clear() {
20
+ this.runValues.clear();
21
+ this.featureValues.clear();
22
+ }
23
+ };
24
+ var ScopedState = class {
25
+ constructor(runValues, featureValues) {
26
+ this.runValues = runValues;
27
+ this.featureValues = featureValues;
28
+ }
29
+ runValues;
30
+ featureValues;
31
+ scenarioValues = /* @__PURE__ */ new Map();
32
+ set(scope, key, value) {
33
+ this.values(scope).set(validateKey(key), value);
34
+ return value;
35
+ }
36
+ get(key) {
37
+ const validKey = validateKey(key);
38
+ if (this.scenarioValues.has(validKey)) return this.scenarioValues.get(validKey);
39
+ if (this.featureValues.has(validKey)) return this.featureValues.get(validKey);
40
+ return this.runValues.get(validKey);
41
+ }
42
+ require(key) {
43
+ const value = this.get(key);
44
+ if (value === void 0) throw new Error(`Required BDD state '${key}' is not set`);
45
+ return value;
46
+ }
47
+ has(key) {
48
+ const validKey = validateKey(key);
49
+ return this.scenarioValues.has(validKey) || this.featureValues.has(validKey) || this.runValues.has(validKey);
50
+ }
51
+ delete(scope, key) {
52
+ return this.values(scope).delete(validateKey(key));
53
+ }
54
+ clearScenario() {
55
+ this.scenarioValues.clear();
56
+ }
57
+ values(scope) {
58
+ if (scope === "run") return this.runValues;
59
+ if (scope === "feature") return this.featureValues;
60
+ return this.scenarioValues;
61
+ }
62
+ };
63
+ function validateKey(key) {
64
+ const trimmed = key.trim();
65
+ if (!trimmed) throw new Error("BDD state key must not be empty");
66
+ return trimmed;
67
+ }
68
+ var sharedState = new SharedStateRegistry();
69
+
70
+ exports.ScopedState = ScopedState;
71
+ exports.SharedStateRegistry = SharedStateRegistry;
72
+ exports.sharedState = sharedState;
73
+ //# sourceMappingURL=scoped-state.cjs.map
74
+ //# sourceMappingURL=scoped-state.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scoped-state.ts"],"names":[],"mappings":"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","file":"scoped-state.cjs","sourcesContent":["export type StateScope = 'run' | 'feature' | 'scenario';\n\ntype StateValues = Map<string, unknown>;\n\n/**\n * Process-local state backing Behave-style run and feature context layers.\n * Scenario values stay on the scenario World and are never shared.\n */\nexport class SharedStateRegistry {\n private readonly runValues: StateValues = new Map();\n private readonly featureValues = new Map<string, StateValues>();\n\n run(): StateValues {\n return this.runValues;\n }\n\n feature(featureUri: string): StateValues {\n if (!featureUri) throw new Error('Feature state requires a feature URI');\n let values = this.featureValues.get(featureUri);\n if (!values) {\n values = new Map();\n this.featureValues.set(featureUri, values);\n }\n return values;\n }\n\n clear(): void {\n this.runValues.clear();\n this.featureValues.clear();\n }\n}\n\n/** Layered state lookup: scenario overrides feature, which overrides run. */\nexport class ScopedState {\n private readonly scenarioValues: StateValues = new Map();\n\n constructor(\n private readonly runValues: StateValues,\n private readonly featureValues: StateValues,\n ) {}\n\n set<T>(scope: StateScope, key: string, value: T): T {\n this.values(scope).set(validateKey(key), value);\n return value;\n }\n\n get<T>(key: string): T | undefined {\n const validKey = validateKey(key);\n if (this.scenarioValues.has(validKey)) return this.scenarioValues.get(validKey) as T;\n if (this.featureValues.has(validKey)) return this.featureValues.get(validKey) as T;\n return this.runValues.get(validKey) as T | undefined;\n }\n\n require<T>(key: string): T {\n const value = this.get<T>(key);\n if (value === undefined) throw new Error(`Required BDD state '${key}' is not set`);\n return value;\n }\n\n has(key: string): boolean {\n const validKey = validateKey(key);\n return (\n this.scenarioValues.has(validKey) ||\n this.featureValues.has(validKey) ||\n this.runValues.has(validKey)\n );\n }\n\n delete(scope: StateScope, key: string): boolean {\n return this.values(scope).delete(validateKey(key));\n }\n\n clearScenario(): void {\n this.scenarioValues.clear();\n }\n\n private values(scope: StateScope): StateValues {\n if (scope === 'run') return this.runValues;\n if (scope === 'feature') return this.featureValues;\n return this.scenarioValues;\n }\n}\n\nfunction validateKey(key: string): string {\n const trimmed = key.trim();\n if (!trimmed) throw new Error('BDD state key must not be empty');\n return trimmed;\n}\n\nexport const sharedState = new SharedStateRegistry();\n"]}
@@ -0,0 +1,30 @@
1
+ type StateScope = 'run' | 'feature' | 'scenario';
2
+ type StateValues = Map<string, unknown>;
3
+ /**
4
+ * Process-local state backing Behave-style run and feature context layers.
5
+ * Scenario values stay on the scenario World and are never shared.
6
+ */
7
+ declare class SharedStateRegistry {
8
+ private readonly runValues;
9
+ private readonly featureValues;
10
+ run(): StateValues;
11
+ feature(featureUri: string): StateValues;
12
+ clear(): void;
13
+ }
14
+ /** Layered state lookup: scenario overrides feature, which overrides run. */
15
+ declare class ScopedState {
16
+ private readonly runValues;
17
+ private readonly featureValues;
18
+ private readonly scenarioValues;
19
+ constructor(runValues: StateValues, featureValues: StateValues);
20
+ set<T>(scope: StateScope, key: string, value: T): T;
21
+ get<T>(key: string): T | undefined;
22
+ require<T>(key: string): T;
23
+ has(key: string): boolean;
24
+ delete(scope: StateScope, key: string): boolean;
25
+ clearScenario(): void;
26
+ private values;
27
+ }
28
+ declare const sharedState: SharedStateRegistry;
29
+
30
+ export { ScopedState, SharedStateRegistry, type StateScope, sharedState };
@@ -0,0 +1,30 @@
1
+ type StateScope = 'run' | 'feature' | 'scenario';
2
+ type StateValues = Map<string, unknown>;
3
+ /**
4
+ * Process-local state backing Behave-style run and feature context layers.
5
+ * Scenario values stay on the scenario World and are never shared.
6
+ */
7
+ declare class SharedStateRegistry {
8
+ private readonly runValues;
9
+ private readonly featureValues;
10
+ run(): StateValues;
11
+ feature(featureUri: string): StateValues;
12
+ clear(): void;
13
+ }
14
+ /** Layered state lookup: scenario overrides feature, which overrides run. */
15
+ declare class ScopedState {
16
+ private readonly runValues;
17
+ private readonly featureValues;
18
+ private readonly scenarioValues;
19
+ constructor(runValues: StateValues, featureValues: StateValues);
20
+ set<T>(scope: StateScope, key: string, value: T): T;
21
+ get<T>(key: string): T | undefined;
22
+ require<T>(key: string): T;
23
+ has(key: string): boolean;
24
+ delete(scope: StateScope, key: string): boolean;
25
+ clearScenario(): void;
26
+ private values;
27
+ }
28
+ declare const sharedState: SharedStateRegistry;
29
+
30
+ export { ScopedState, SharedStateRegistry, type StateScope, sharedState };
@@ -0,0 +1,3 @@
1
+ export { ScopedState, SharedStateRegistry, sharedState } from './chunk-HTKRJLVX.js';
2
+ //# sourceMappingURL=scoped-state.js.map
3
+ //# sourceMappingURL=scoped-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"scoped-state.js"}