@vijaypjavvadi/bdd2pw 3.12.0 → 4.0.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 @@
1
+ {"version":3,"file":"dataLoader.js","sourceRoot":"","sources":["../../src/data/dataLoader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * v4.0.0 — ExamplesInjector.
3
+ *
4
+ * Mutates a parsed `FeatureIR` in place by replacing the `examples`
5
+ * array on every Scenario Outline with externally-sourced rows
6
+ * (from a CSV/JSON/XLSX file via DataLoader, or from a synthetic
7
+ * generator via SynthGenerator).
8
+ *
9
+ * Design decisions (locked in during v4.0.0 planning):
10
+ *
11
+ * 1. External rows OVERRIDE inline Examples entirely. Inline Examples
12
+ * in the .feature file become the no-data fallback. When --data is
13
+ * passed, every Scenario Outline gets its examples swapped.
14
+ *
15
+ * 2. Scenarios without Outline syntax (no <placeholder>) are skipped.
16
+ * A vanilla Scenario doesn't care about data injection.
17
+ *
18
+ * 3. If a scenario's placeholders reference columns NOT present in
19
+ * the data, that scenario is LEFT ALONE (keeps inline Examples)
20
+ * and the gap is logged as a `ReviewItem` so BDD_REVIEW.md surfaces
21
+ * the mismatch. Partial column overrides are not supported on
22
+ * purpose — they produce confusing test output.
23
+ *
24
+ * 4. On synth-generator failure (LLM down, schema invalid, Faker
25
+ * missing), we leave inline Examples in place and surface a
26
+ * `ReviewItem` warning. Same graceful-degradation philosophy as
27
+ * the LLM-fallback failure mode.
28
+ */
29
+ import type { FeatureIR, ReviewItem, ScenarioIR } from "../types";
30
+ import type { DataRow } from "./dataLoader";
31
+ /**
32
+ * Discover the placeholder names referenced anywhere in a scenario's
33
+ * step text. Gherkin syntax: `<name>` substitutes a value from the
34
+ * row keyed by `name`.
35
+ *
36
+ * Returns a deduped, sorted list. Empty list = vanilla Scenario, not
37
+ * an Outline.
38
+ */
39
+ export declare function extractPlaceholders(scenario: ScenarioIR): string[];
40
+ /**
41
+ * Inject `rows` into every Scenario Outline of `feature`. Returns
42
+ * a report describing what was swapped and what was skipped (so the
43
+ * caller can emit `ReviewItem`s and pino logs).
44
+ *
45
+ * Mutates `feature` in place — the caller passes the already-parsed
46
+ * IR and gets back a mutation summary.
47
+ */
48
+ export interface InjectionReport {
49
+ totalScenarios: number;
50
+ swappedScenarios: number;
51
+ skippedScenarios: Array<{
52
+ name: string;
53
+ reason: string;
54
+ }>;
55
+ rowsPerSwap: number;
56
+ }
57
+ export declare function injectExamples(feature: FeatureIR, rows: DataRow[]): InjectionReport;
58
+ /**
59
+ * Convert an `InjectionReport` into `ReviewItem`s ready to push onto
60
+ * the scaffold's review queue. One info line per swap, one warning
61
+ * per skip whose reason indicates a real misconfiguration (missing
62
+ * columns) — skips for "vanilla Scenario" or "zero rows" are info-only.
63
+ */
64
+ export declare function reportToReviewItems(report: InjectionReport, source: string): ReviewItem[];
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ /**
3
+ * v4.0.0 — ExamplesInjector.
4
+ *
5
+ * Mutates a parsed `FeatureIR` in place by replacing the `examples`
6
+ * array on every Scenario Outline with externally-sourced rows
7
+ * (from a CSV/JSON/XLSX file via DataLoader, or from a synthetic
8
+ * generator via SynthGenerator).
9
+ *
10
+ * Design decisions (locked in during v4.0.0 planning):
11
+ *
12
+ * 1. External rows OVERRIDE inline Examples entirely. Inline Examples
13
+ * in the .feature file become the no-data fallback. When --data is
14
+ * passed, every Scenario Outline gets its examples swapped.
15
+ *
16
+ * 2. Scenarios without Outline syntax (no <placeholder>) are skipped.
17
+ * A vanilla Scenario doesn't care about data injection.
18
+ *
19
+ * 3. If a scenario's placeholders reference columns NOT present in
20
+ * the data, that scenario is LEFT ALONE (keeps inline Examples)
21
+ * and the gap is logged as a `ReviewItem` so BDD_REVIEW.md surfaces
22
+ * the mismatch. Partial column overrides are not supported on
23
+ * purpose — they produce confusing test output.
24
+ *
25
+ * 4. On synth-generator failure (LLM down, schema invalid, Faker
26
+ * missing), we leave inline Examples in place and surface a
27
+ * `ReviewItem` warning. Same graceful-degradation philosophy as
28
+ * the LLM-fallback failure mode.
29
+ */
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.extractPlaceholders = extractPlaceholders;
32
+ exports.injectExamples = injectExamples;
33
+ exports.reportToReviewItems = reportToReviewItems;
34
+ /**
35
+ * Discover the placeholder names referenced anywhere in a scenario's
36
+ * step text. Gherkin syntax: `<name>` substitutes a value from the
37
+ * row keyed by `name`.
38
+ *
39
+ * Returns a deduped, sorted list. Empty list = vanilla Scenario, not
40
+ * an Outline.
41
+ */
42
+ function extractPlaceholders(scenario) {
43
+ const found = new Set();
44
+ const re = /<([A-Za-z_][\w-]*)>/g;
45
+ for (const step of scenario.steps) {
46
+ let m;
47
+ while ((m = re.exec(step.text)) !== null)
48
+ found.add(m[1]);
49
+ // Also scan doc-string and data-table arguments.
50
+ if (typeof step.argument === "string") {
51
+ while ((m = re.exec(step.argument)) !== null)
52
+ found.add(m[1]);
53
+ }
54
+ else if (Array.isArray(step.argument)) {
55
+ for (const row of step.argument) {
56
+ for (const cell of row) {
57
+ while ((m = re.exec(cell)) !== null)
58
+ found.add(m[1]);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ // Also walk the existing examples table headers, since some
64
+ // scenarios reference a placeholder only via the row title and not
65
+ // in step text. (Rare, but legal Gherkin.)
66
+ if (scenario.examples && scenario.examples.length > 0) {
67
+ for (const k of Object.keys(scenario.examples[0])) {
68
+ // Headers don't need a `< >` wrapper in the parsed IR — we
69
+ // include them so column matching is symmetric with whatever
70
+ // the .feature file declared.
71
+ found.add(k);
72
+ }
73
+ }
74
+ return Array.from(found).sort();
75
+ }
76
+ function injectExamples(feature, rows) {
77
+ const report = {
78
+ totalScenarios: feature.scenarios.length,
79
+ swappedScenarios: 0,
80
+ skippedScenarios: [],
81
+ rowsPerSwap: rows.length,
82
+ };
83
+ if (rows.length === 0) {
84
+ // Nothing to inject — leave inline Examples untouched.
85
+ for (const s of feature.scenarios) {
86
+ report.skippedScenarios.push({
87
+ name: s.name,
88
+ reason: "data file had zero rows",
89
+ });
90
+ }
91
+ return report;
92
+ }
93
+ const dataColumns = new Set(Object.keys(rows[0]));
94
+ for (const scenario of feature.scenarios) {
95
+ const placeholders = extractPlaceholders(scenario);
96
+ if (placeholders.length === 0) {
97
+ // Vanilla Scenario — no placeholders, no injection needed.
98
+ report.skippedScenarios.push({
99
+ name: scenario.name,
100
+ reason: "vanilla Scenario (no <placeholders>)",
101
+ });
102
+ continue;
103
+ }
104
+ const missing = placeholders.filter((p) => !dataColumns.has(p));
105
+ if (missing.length > 0) {
106
+ report.skippedScenarios.push({
107
+ name: scenario.name,
108
+ reason: `data file is missing column(s): ${missing.join(", ")}`,
109
+ });
110
+ continue;
111
+ }
112
+ // Swap. Keep only the columns the scenario actually references —
113
+ // extra columns in the data file are silently dropped per
114
+ // scenario, so a single 50-column data file can feed many narrow
115
+ // scenarios without each one pulling in noise.
116
+ scenario.examples = rows.map((row) => {
117
+ const filtered = {};
118
+ for (const p of placeholders)
119
+ filtered[p] = row[p];
120
+ return filtered;
121
+ });
122
+ report.swappedScenarios += 1;
123
+ }
124
+ return report;
125
+ }
126
+ /**
127
+ * Convert an `InjectionReport` into `ReviewItem`s ready to push onto
128
+ * the scaffold's review queue. One info line per swap, one warning
129
+ * per skip whose reason indicates a real misconfiguration (missing
130
+ * columns) — skips for "vanilla Scenario" or "zero rows" are info-only.
131
+ */
132
+ function reportToReviewItems(report, source) {
133
+ const items = [];
134
+ if (report.swappedScenarios > 0) {
135
+ items.push({
136
+ severity: "info",
137
+ message: `Data injection from ${source}: swapped Examples in ${report.swappedScenarios}/${report.totalScenarios} scenario(s), ${report.rowsPerSwap} rows each.`,
138
+ });
139
+ }
140
+ for (const skip of report.skippedScenarios) {
141
+ const isMisconfig = skip.reason.startsWith("data file is missing");
142
+ items.push({
143
+ severity: isMisconfig ? "warn" : "info",
144
+ message: `Data injection skipped scenario "${skip.name}": ${skip.reason}`,
145
+ });
146
+ }
147
+ return items;
148
+ }
149
+ //# sourceMappingURL=examplesInjector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"examplesInjector.js","sourceRoot":"","sources":["../../src/data/examplesInjector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * v4.0.0 — Synthetic data generator.
3
+ *
4
+ * Reads a schema like:
5
+ *
6
+ * {
7
+ * "email": "faker.internet.email",
8
+ * "password": "faker.internet.password",
9
+ * "url_fragment": "inventory.html", // literal
10
+ * "claim_desc": "llm:auto insurance claim, one sentence"
11
+ * }
12
+ *
13
+ * Produces N `DataRow`s. Each row's fields are filled by routing the
14
+ * source string:
15
+ *
16
+ * - "faker.X.Y[.Z]..." → dynamic dotted-path lookup on faker, called
17
+ * with no args. Standard faker fields cover
18
+ * >95% of test data needs.
19
+ * - "llm:<prompt>" → ONE batched LLM call generates an array of
20
+ * N values for the whole column. Cost-optimal:
21
+ * three LLM-driven fields = three batched
22
+ * calls regardless of row count.
23
+ * - anything else → literal string, same value in every row.
24
+ *
25
+ * Determinism: faker is seeded with `opts.seed` (default 42). Same
26
+ * seed + same schema + same row count = byte-identical output across
27
+ * runs. LLM fields are NOT deterministic in the same way (temperature
28
+ * is fixed at 0 but provider sampling has residual nondeterminism),
29
+ * which is intentional — LLM-generated domain data should look
30
+ * varied each time you regenerate.
31
+ *
32
+ * Why batched LLM calls per column instead of one big call for the
33
+ * whole table: the prompt stays simple ("generate N car insurance
34
+ * claim descriptions"), the response stays a flat JSON array (easy
35
+ * to parse), and a column failure doesn't poison the other columns.
36
+ */
37
+ import type { DataRow } from "./dataLoader";
38
+ import type { LLMClient } from "../llm/types";
39
+ export interface SynthSchema {
40
+ /** field name → source spec. See module doc for syntax. */
41
+ [columnName: string]: string;
42
+ }
43
+ export interface SynthOptions {
44
+ /** How many rows to generate. */
45
+ rows: number;
46
+ /** Faker RNG seed for reproducibility. Default 42. */
47
+ seed?: number;
48
+ /**
49
+ * LLM client to use for "llm:..." fields. Optional — if a schema
50
+ * has no LLM fields you can omit this. If a schema HAS LLM fields
51
+ * and this is undefined, those fields will be filled with the
52
+ * source-string literal as a fallback (visible in output so the
53
+ * user notices the gap).
54
+ */
55
+ llm?: LLMClient;
56
+ /** Optional schema-relative model override for LLM calls. */
57
+ llmModel?: string;
58
+ }
59
+ export declare class SynthGeneratorError extends Error {
60
+ constructor(message: string);
61
+ }
62
+ /**
63
+ * Generate N rows. Returns `DataRow[]` with the same shape as
64
+ * `loadDataFile` so downstream Examples injection works identically.
65
+ */
66
+ export declare function generateSyntheticRows(schema: SynthSchema, opts: SynthOptions): Promise<DataRow[]>;
67
+ /**
68
+ * Convenience: load a schema JSON from disk and validate it has at
69
+ * least one column. Throws SynthGeneratorError on any read/parse error.
70
+ */
71
+ export declare function loadSchema(schemaPath: string): SynthSchema;
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ /**
3
+ * v4.0.0 — Synthetic data generator.
4
+ *
5
+ * Reads a schema like:
6
+ *
7
+ * {
8
+ * "email": "faker.internet.email",
9
+ * "password": "faker.internet.password",
10
+ * "url_fragment": "inventory.html", // literal
11
+ * "claim_desc": "llm:auto insurance claim, one sentence"
12
+ * }
13
+ *
14
+ * Produces N `DataRow`s. Each row's fields are filled by routing the
15
+ * source string:
16
+ *
17
+ * - "faker.X.Y[.Z]..." → dynamic dotted-path lookup on faker, called
18
+ * with no args. Standard faker fields cover
19
+ * >95% of test data needs.
20
+ * - "llm:<prompt>" → ONE batched LLM call generates an array of
21
+ * N values for the whole column. Cost-optimal:
22
+ * three LLM-driven fields = three batched
23
+ * calls regardless of row count.
24
+ * - anything else → literal string, same value in every row.
25
+ *
26
+ * Determinism: faker is seeded with `opts.seed` (default 42). Same
27
+ * seed + same schema + same row count = byte-identical output across
28
+ * runs. LLM fields are NOT deterministic in the same way (temperature
29
+ * is fixed at 0 but provider sampling has residual nondeterminism),
30
+ * which is intentional — LLM-generated domain data should look
31
+ * varied each time you regenerate.
32
+ *
33
+ * Why batched LLM calls per column instead of one big call for the
34
+ * whole table: the prompt stays simple ("generate N car insurance
35
+ * claim descriptions"), the response stays a flat JSON array (easy
36
+ * to parse), and a column failure doesn't poison the other columns.
37
+ */
38
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
39
+ if (k2 === undefined) k2 = k;
40
+ var desc = Object.getOwnPropertyDescriptor(m, k);
41
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
42
+ desc = { enumerable: true, get: function() { return m[k]; } };
43
+ }
44
+ Object.defineProperty(o, k2, desc);
45
+ }) : (function(o, m, k, k2) {
46
+ if (k2 === undefined) k2 = k;
47
+ o[k2] = m[k];
48
+ }));
49
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
50
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
51
+ }) : function(o, v) {
52
+ o["default"] = v;
53
+ });
54
+ var __importStar = (this && this.__importStar) || (function () {
55
+ var ownKeys = function(o) {
56
+ ownKeys = Object.getOwnPropertyNames || function (o) {
57
+ var ar = [];
58
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
59
+ return ar;
60
+ };
61
+ return ownKeys(o);
62
+ };
63
+ return function (mod) {
64
+ if (mod && mod.__esModule) return mod;
65
+ var result = {};
66
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
67
+ __setModuleDefault(result, mod);
68
+ return result;
69
+ };
70
+ })();
71
+ Object.defineProperty(exports, "__esModule", { value: true });
72
+ exports.SynthGeneratorError = void 0;
73
+ exports.generateSyntheticRows = generateSyntheticRows;
74
+ exports.loadSchema = loadSchema;
75
+ const fs = __importStar(require("fs"));
76
+ const path = __importStar(require("path"));
77
+ class SynthGeneratorError extends Error {
78
+ constructor(message) {
79
+ super(message);
80
+ this.name = "SynthGeneratorError";
81
+ }
82
+ }
83
+ exports.SynthGeneratorError = SynthGeneratorError;
84
+ /**
85
+ * Generate N rows. Returns `DataRow[]` with the same shape as
86
+ * `loadDataFile` so downstream Examples injection works identically.
87
+ */
88
+ async function generateSyntheticRows(schema, opts) {
89
+ if (opts.rows <= 0) {
90
+ throw new SynthGeneratorError(`rows must be >= 1, got ${opts.rows}`);
91
+ }
92
+ const seed = opts.seed ?? 42;
93
+ // Faker is in optionalDependencies. Lazy-load so users who use only
94
+ // literal + LLM fields don't need the dep installed. If they DO have
95
+ // a faker.* field and the dep is missing, we throw a clean install
96
+ // hint rather than crashing in the resolver.
97
+ let faker = null;
98
+ const hasFakerField = Object.values(schema).some((s) => s.startsWith("faker."));
99
+ if (hasFakerField) {
100
+ try {
101
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
102
+ faker = require("@faker-js/faker").faker;
103
+ faker.seed(seed);
104
+ }
105
+ catch {
106
+ throw new SynthGeneratorError("@faker-js/faker not installed. Run: npm install @faker-js/faker");
107
+ }
108
+ }
109
+ // Pre-compute LLM column values in one batch per column.
110
+ const llmColumns = {};
111
+ for (const [col, source] of Object.entries(schema)) {
112
+ if (source.startsWith("llm:")) {
113
+ const prompt = source.slice(4).trim();
114
+ if (!opts.llm) {
115
+ // Fallback: same literal in every row, visible so the user
116
+ // sees the gap and adds --llm to the next run.
117
+ llmColumns[col] = Array.from({ length: opts.rows }, () => `<llm-skipped: ${prompt.slice(0, 40)}>`);
118
+ continue;
119
+ }
120
+ llmColumns[col] = await generateLlmColumn(opts.llm, prompt, opts.rows, col);
121
+ }
122
+ }
123
+ // Build rows. Faker calls happen here so seeded order is stable.
124
+ const rows = [];
125
+ for (let r = 0; r < opts.rows; r++) {
126
+ const row = {};
127
+ for (const [col, source] of Object.entries(schema)) {
128
+ if (source.startsWith("faker.")) {
129
+ row[col] = String(callFakerPath(faker, source));
130
+ }
131
+ else if (source.startsWith("llm:")) {
132
+ row[col] = llmColumns[col][r] ?? "";
133
+ }
134
+ else {
135
+ // Literal string.
136
+ row[col] = source;
137
+ }
138
+ }
139
+ rows.push(row);
140
+ }
141
+ return rows;
142
+ }
143
+ /**
144
+ * Resolve a "faker.X.Y" path against the faker object and invoke the
145
+ * leaf with no arguments. We don't currently support arg passing —
146
+ * if a user needs `faker.internet.password({length: 32})` they can
147
+ * generate the data offline and use --data instead. Keeping the
148
+ * schema syntax simple is more valuable than feature parity here.
149
+ */
150
+ function callFakerPath(faker, source) {
151
+ const segments = source.split(".").slice(1); // drop the leading "faker"
152
+ let cursor = faker;
153
+ for (let i = 0; i < segments.length; i++) {
154
+ if (cursor == null) {
155
+ throw new SynthGeneratorError(`Faker path resolved to null/undefined at segment "${segments
156
+ .slice(0, i + 1)
157
+ .join(".")}" of "${source}"`);
158
+ }
159
+ cursor = cursor[segments[i]];
160
+ }
161
+ if (typeof cursor !== "function") {
162
+ throw new SynthGeneratorError(`Faker path "${source}" does not resolve to a function (got ${typeof cursor}). ` +
163
+ `Did you mean to add () or use a different method? See https://fakerjs.dev/api/`);
164
+ }
165
+ return cursor();
166
+ }
167
+ /**
168
+ * Generate N values for one column via a single LLM call.
169
+ *
170
+ * Uses `LLMClient.generateText()` (v4.0.0+). The method routes
171
+ * through the same governance sanitisation, budget, timeout, and
172
+ * telemetry pipeline as binding generation — no SDK calls escape
173
+ * the perimeter.
174
+ *
175
+ * Older LLMClient implementations that don't yet ship generateText
176
+ * (only the binding API) cause this to throw a clear hint. All three
177
+ * built-in providers (Anthropic, OpenAI, Gemini) have generateText
178
+ * since v4.0.
179
+ */
180
+ async function generateLlmColumn(llm, prompt, count, columnName) {
181
+ if (typeof llm.generateText !== "function") {
182
+ throw new SynthGeneratorError(`LLM client does not implement generateText(). v4.0+ providers (Anthropic, OpenAI, Gemini) do. Custom LLM clients must add this method to support synthetic-data fields.`);
183
+ }
184
+ const instruction = `You are generating synthetic test data for a column named "${columnName}". ` +
185
+ `Field description: ${prompt}. Generate exactly ${count} plausible distinct values. ` +
186
+ `Respond with ONLY a JSON array of ${count} strings, no commentary. ` +
187
+ `Example: ["value1", "value2", "value3"]`;
188
+ const result = await llm.generateText(instruction);
189
+ if (result.error || !result.text) {
190
+ throw new SynthGeneratorError(`LLM column generation failed for "${columnName}": ${result.error ?? "empty text response"}`);
191
+ }
192
+ const arr = tryExtractJsonArray(result.text);
193
+ if (!arr || arr.length === 0) {
194
+ throw new SynthGeneratorError(`LLM column generation for "${columnName}" returned no parseable JSON array. ` +
195
+ `Schema source: "${prompt}". First 200 chars of response: ${result.text.slice(0, 200)}`);
196
+ }
197
+ return padOrTruncate(arr.map(String), count);
198
+ }
199
+ function tryExtractJsonArray(text) {
200
+ // Strip fenced code blocks first.
201
+ let body = text.trim();
202
+ const fence = body.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
203
+ if (fence)
204
+ body = fence[1].trim();
205
+ // Find first '[' and last ']' for tolerant extraction.
206
+ const start = body.indexOf("[");
207
+ const end = body.lastIndexOf("]");
208
+ if (start < 0 || end <= start)
209
+ return null;
210
+ try {
211
+ const parsed = JSON.parse(body.slice(start, end + 1));
212
+ return Array.isArray(parsed) ? parsed : null;
213
+ }
214
+ catch {
215
+ return null;
216
+ }
217
+ }
218
+ function padOrTruncate(arr, n) {
219
+ if (arr.length === n)
220
+ return arr;
221
+ if (arr.length > n)
222
+ return arr.slice(0, n);
223
+ // Pad by cycling through what we have — better than empty strings.
224
+ const out = [...arr];
225
+ while (out.length < n)
226
+ out.push(arr[out.length % arr.length]);
227
+ return out;
228
+ }
229
+ /**
230
+ * Convenience: load a schema JSON from disk and validate it has at
231
+ * least one column. Throws SynthGeneratorError on any read/parse error.
232
+ */
233
+ function loadSchema(schemaPath) {
234
+ const abs = path.resolve(schemaPath);
235
+ if (!fs.existsSync(abs)) {
236
+ throw new SynthGeneratorError(`Schema file not found: ${schemaPath}`);
237
+ }
238
+ let parsed;
239
+ try {
240
+ let raw = fs.readFileSync(abs, "utf-8");
241
+ // Strip UTF-8 BOM. PowerShell's `Out-File -Encoding utf8` adds one
242
+ // by default; without this strip JSON.parse errors on the first byte.
243
+ if (raw.charCodeAt(0) === 0xfeff)
244
+ raw = raw.slice(1);
245
+ parsed = JSON.parse(raw);
246
+ }
247
+ catch (err) {
248
+ const msg = err instanceof Error ? err.message : String(err);
249
+ throw new SynthGeneratorError(`Schema JSON parse error in ${schemaPath}: ${msg}`);
250
+ }
251
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
252
+ throw new SynthGeneratorError(`Schema must be a JSON object of {column: source}, got: ${typeof parsed}`);
253
+ }
254
+ const obj = parsed;
255
+ if (Object.keys(obj).length === 0) {
256
+ throw new SynthGeneratorError("Schema has zero columns");
257
+ }
258
+ for (const [k, v] of Object.entries(obj)) {
259
+ if (typeof v !== "string") {
260
+ throw new SynthGeneratorError(`Schema field "${k}" must map to a string source, got ${typeof v}`);
261
+ }
262
+ }
263
+ return obj;
264
+ }
265
+ //# sourceMappingURL=synthGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synthGenerator.js","sourceRoot":"","sources":["../../src/data/synthGenerator.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCH,sDAmEC;AA8GD,gCAmCC;AA1PD,uCAAyB;AACzB,2CAA6B;AA0B7B,MAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AALD,kDAKC;AAED;;;GAGG;AACI,KAAK,UAAU,qBAAqB,CACzC,MAAmB,EACnB,IAAkB;IAElB,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,mBAAmB,CAAC,0BAA0B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;IAE7B,oEAAoE;IACpE,qEAAqE;IACrE,mEAAmE;IACnE,6CAA6C;IAC7C,IAAI,KAAK,GAAQ,IAAI,CAAC;IACtB,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IAChF,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,CAAC;YACH,8DAA8D;YAC9D,KAAK,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,mBAAmB,CAC3B,iEAAiE,CAClE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,yDAAyD;IACzD,MAAM,UAAU,GAA6B,EAAE,CAAC;IAChD,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACtC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;gBACd,2DAA2D;gBAC3D,+CAA+C;gBAC/C,UAAU,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAC1B,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,EACrB,GAAG,EAAE,CAAC,iBAAiB,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAC9C,CAAC;gBACF,SAAS;YACX,CAAC;YACD,UAAU,CAAC,GAAG,CAAC,GAAG,MAAM,iBAAiB,CACvC,IAAI,CAAC,GAAG,EACR,MAAM,EACN,IAAI,CAAC,IAAI,EACT,GAAG,CACJ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,iEAAiE;IACjE,MAAM,IAAI,GAAc,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,GAAG,GAAY,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnD,IAAI,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;YAClD,CAAC;iBAAM,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrC,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACtC,CAAC;iBAAM,CAAC;gBACN,kBAAkB;gBAClB,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;YACpB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,KAAU,EAAE,MAAc;IAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,2BAA2B;IACxE,IAAI,MAAM,GAAQ,KAAK,CAAC;IACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,mBAAmB,CAC3B,qDAAqD,QAAQ;iBAC1D,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;iBACf,IAAI,CAAC,GAAG,CAAC,SAAS,MAAM,GAAG,CAC/B,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,mBAAmB,CAC3B,eAAe,MAAM,yCAAyC,OAAO,MAAM,KAAK;YAC9E,gFAAgF,CACnF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,EAAE,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,KAAK,UAAU,iBAAiB,CAC9B,GAAc,EACd,MAAc,EACd,KAAa,EACb,UAAkB;IAElB,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;QAC3C,MAAM,IAAI,mBAAmB,CAC3B,yKAAyK,CAC1K,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GACf,8DAA8D,UAAU,KAAK;QAC7E,sBAAsB,MAAM,sBAAsB,KAAK,8BAA8B;QACrF,qCAAqC,KAAK,2BAA2B;QACrE,yCAAyC,CAAC;IAE5C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;IAEnD,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,IAAI,mBAAmB,CAC3B,qCAAqC,UAAU,MAAM,MAAM,CAAC,KAAK,IAAI,qBAAqB,EAAE,CAC7F,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,mBAAmB,CAC3B,8BAA8B,UAAU,sCAAsC;YAC5E,mBAAmB,MAAM,mCAAmC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC1F,CAAC;IACJ,CAAC;IACD,OAAO,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAY;IACvC,kCAAkC;IAClC,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IAC5D,IAAI,KAAK;QAAE,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,uDAAuD;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACtD,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,GAAa,EAAE,CAAS;IAC7C,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACjC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3C,mEAAmE;IACnE,MAAM,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;IACrB,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9D,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,SAAgB,UAAU,CAAC,UAAkB;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,mBAAmB,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,IAAI,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACxC,mEAAmE;QACnE,sEAAsE;QACtE,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM;YAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,IAAI,mBAAmB,CAC3B,8BAA8B,UAAU,KAAK,GAAG,EAAE,CACnD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,mBAAmB,CAC3B,0DAA0D,OAAO,MAAM,EAAE,CAC1E,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,MAAiC,CAAC;IAC9C,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,mBAAmB,CAAC,yBAAyB,CAAC,CAAC;IAC3D,CAAC;IACD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,mBAAmB,CAC3B,iBAAiB,CAAC,sCAAsC,OAAO,CAAC,EAAE,CACnE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,GAAkB,CAAC;AAC5B,CAAC"}
package/dist/index.js CHANGED
@@ -48,6 +48,10 @@ const path = __importStar(require("path"));
48
48
  const PACKAGE_VERSION = require("../package.json").version;
49
49
  const gherkinParser_1 = require("./parser/gherkinParser");
50
50
  const jsonScenarioParser_1 = require("./parser/jsonScenarioParser");
51
+ // v4.0.0 — data-driven scaffolds.
52
+ const dataLoader_1 = require("./data/dataLoader");
53
+ const synthGenerator_1 = require("./data/synthGenerator");
54
+ const examplesInjector_1 = require("./data/examplesInjector");
51
55
  const repoScanner_1 = require("./repo/repoScanner");
52
56
  const projectScaffolder_1 = require("./repo/projectScaffolder");
53
57
  const mcpClient_1 = require("./discovery/mcpClient");
@@ -272,6 +276,52 @@ async function scaffold(opts) {
272
276
  const candidates = llm
273
277
  ? new llm_1.CandidateRulesWriter(opts.repo)
274
278
  : undefined;
279
+ // v4.0.0 — data-driven Examples injection. If --data or --gen-data was
280
+ // passed, swap every Scenario Outline's examples with externally-sourced
281
+ // rows BEFORE step matching or emit runs. Failures fall back to inline
282
+ // Examples and surface as ReviewItems (graceful degradation, same as
283
+ // the LLM fallback failure mode).
284
+ if (opts.dataSource) {
285
+ const ds = opts.dataSource;
286
+ let rows = [];
287
+ let sourceLabel = "";
288
+ let dataLoadFailed = false;
289
+ try {
290
+ if (ds.type === "file" && ds.path) {
291
+ rows = (0, dataLoader_1.loadDataFile)(ds.path);
292
+ sourceLabel = ds.path;
293
+ }
294
+ else if (ds.type === "synthetic" && ds.schemaPath) {
295
+ const schema = (0, synthGenerator_1.loadSchema)(ds.schemaPath);
296
+ rows = await (0, synthGenerator_1.generateSyntheticRows)(schema, {
297
+ rows: ds.rows ?? 20,
298
+ seed: ds.seed ?? 42,
299
+ llm: llm ?? undefined,
300
+ });
301
+ sourceLabel = `synthetic(${ds.schemaPath}, ${rows.length} rows)`;
302
+ }
303
+ else {
304
+ throw new Error(`Invalid dataSource: type=${ds.type}, path=${ds.path}, schemaPath=${ds.schemaPath}`);
305
+ }
306
+ }
307
+ catch (err) {
308
+ dataLoadFailed = true;
309
+ const msg = err instanceof Error ? err.message : String(err);
310
+ const kind = err instanceof dataLoader_1.DataLoaderError
311
+ ? "Data file load"
312
+ : err instanceof synthGenerator_1.SynthGeneratorError
313
+ ? "Synthetic data generation"
314
+ : "Data source";
315
+ reviewItems.push({
316
+ severity: "warn",
317
+ message: `${kind} failed; falling back to inline Examples. Reason: ${msg}`,
318
+ });
319
+ }
320
+ if (!dataLoadFailed && rows.length > 0) {
321
+ const report = (0, examplesInjector_1.injectExamples)(feature, rows);
322
+ reviewItems.push(...(0, examplesInjector_1.reportToReviewItems)(report, sourceLabel));
323
+ }
324
+ }
275
325
  const llmCtx = {
276
326
  llm,
277
327
  candidates,