@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.
- package/CHANGELOG.md +98 -0
- package/README.md +9 -5
- package/dist/cli.js +29 -0
- package/dist/cli.js.map +1 -1
- package/dist/data/dataLoader.d.ts +78 -0
- package/dist/data/dataLoader.js +339 -0
- package/dist/data/dataLoader.js.map +1 -0
- package/dist/data/examplesInjector.d.ts +64 -0
- package/dist/data/examplesInjector.js +149 -0
- package/dist/data/examplesInjector.js.map +1 -0
- package/dist/data/synthGenerator.d.ts +71 -0
- package/dist/data/synthGenerator.js +265 -0
- package/dist/data/synthGenerator.js.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/anthropicClient.d.ts +11 -0
- package/dist/llm/anthropicClient.js +89 -0
- package/dist/llm/anthropicClient.js.map +1 -1
- package/dist/llm/geminiClient.d.ts +6 -0
- package/dist/llm/geminiClient.js +87 -0
- package/dist/llm/geminiClient.js.map +1 -1
- package/dist/llm/openaiClient.d.ts +6 -0
- package/dist/llm/openaiClient.js +82 -0
- package/dist/llm/openaiClient.js.map +1 -1
- package/dist/llm/types.d.ts +36 -0
- package/dist/types.d.ts +39 -0
- package/package.json +3 -1
|
@@ -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":"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"}
|
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,
|