@vijaypjavvadi/bdd2pw 3.9.0 → 3.12.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 +245 -0
- package/README.md +13 -10
- package/dist/cli.js +40 -7
- package/dist/cli.js.map +1 -1
- package/dist/llm/geminiClient.d.ts +86 -0
- package/dist/llm/geminiClient.js +472 -0
- package/dist/llm/geminiClient.js.map +1 -0
- package/dist/llm/index.d.ts +2 -0
- package/dist/llm/index.js +13 -2
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/openaiClient.d.ts +77 -0
- package/dist/llm/openaiClient.js +447 -0
- package/dist/llm/openaiClient.js.map +1 -0
- package/dist/llm/telemetry.js +14 -0
- package/dist/llm/telemetry.js.map +1 -1
- package/dist/llm/types.d.ts +9 -2
- package/dist/reports/healStats.d.ts +132 -0
- package/dist/reports/healStats.js +303 -0
- package/dist/reports/healStats.js.map +1 -0
- package/dist/types.d.ts +2 -1
- package/package.json +3 -1
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* v3.10.0 — heal-events.jsonl → heal-stats.json analyzer.
|
|
4
|
+
*
|
|
5
|
+
* Same flavor as v3.9.0's `LLMTelemetry` sidecar but for the
|
|
6
|
+
* self-healing pipeline. Where v3.9.0 measures the LLM batching ROI
|
|
7
|
+
* per scaffold, v3.10.0 measures the self-healing ROI per TEST RUN.
|
|
8
|
+
*
|
|
9
|
+
* Input: `<repo>/artefacts/heal-events.jsonl` — one JSON event per
|
|
10
|
+
* line, written by the runtime `healOrThrow` helper from
|
|
11
|
+
* `templates/heal.ts.tmpl`. Event kinds:
|
|
12
|
+
* - `register` — locator constructed
|
|
13
|
+
* - `heal_attempt` — action failed; asking the heal
|
|
14
|
+
* service for a candidate
|
|
15
|
+
* - `healed` — candidate worked; subsequent
|
|
16
|
+
* calls use it transparently
|
|
17
|
+
* - `heal_unavailable` — no candidate / candidate failed
|
|
18
|
+
*
|
|
19
|
+
* Output: `<repo>/artefacts/heal-stats.json` — stable shape:
|
|
20
|
+
*
|
|
21
|
+
* {
|
|
22
|
+
* "version": "3.10.0",
|
|
23
|
+
* "generatedAt": "2026-...",
|
|
24
|
+
* "source": "<events file>",
|
|
25
|
+
* "totals": {
|
|
26
|
+
* "registrations": 47,
|
|
27
|
+
* "healAttempts": 6,
|
|
28
|
+
* "healed": 4,
|
|
29
|
+
* "healUnavailable": 2,
|
|
30
|
+
* "healRate": 0.6667, // healed / healAttempts
|
|
31
|
+
* "uniqueFields": 23,
|
|
32
|
+
* "uniquePages": 3
|
|
33
|
+
* },
|
|
34
|
+
* "topFailingFields": [
|
|
35
|
+
* { "page": "LoginPage", "name": "submitButton", "attempts": 3, "healed": 2 },
|
|
36
|
+
* ...
|
|
37
|
+
* ],
|
|
38
|
+
* "topErrors": [
|
|
39
|
+
* { "error": "Timeout 30000ms exceeded", "count": 4 },
|
|
40
|
+
* ...
|
|
41
|
+
* ],
|
|
42
|
+
* "topCandidates": [
|
|
43
|
+
* { "selector": "[data-testid='submit']", "promotions": 2,
|
|
44
|
+
* "averageConfidence": 0.87 },
|
|
45
|
+
* ...
|
|
46
|
+
* ],
|
|
47
|
+
* "retryLatencyMs": { "p50": 1240, "p95": 3010, "min": 880, "max": 3010 },
|
|
48
|
+
* "perScenario": [
|
|
49
|
+
* { "scenario": "User can log in", "attempts": 1, "healed": 1 },
|
|
50
|
+
* ...
|
|
51
|
+
* ]
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* Why a separate command (not part of scaffold): scaffold GENERATES the
|
|
55
|
+
* test repo; the heal events are produced when the user RUNS the
|
|
56
|
+
* generated tests. So this analyzer is a post-test command, run from
|
|
57
|
+
* CI after `npx playwright test`. Same pattern as `bdd2pw propose-rules`
|
|
58
|
+
* which runs after the LLM has populated `candidate-rules.jsonl`.
|
|
59
|
+
*/
|
|
60
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
61
|
+
if (k2 === undefined) k2 = k;
|
|
62
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
63
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
64
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
65
|
+
}
|
|
66
|
+
Object.defineProperty(o, k2, desc);
|
|
67
|
+
}) : (function(o, m, k, k2) {
|
|
68
|
+
if (k2 === undefined) k2 = k;
|
|
69
|
+
o[k2] = m[k];
|
|
70
|
+
}));
|
|
71
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
72
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
73
|
+
}) : function(o, v) {
|
|
74
|
+
o["default"] = v;
|
|
75
|
+
});
|
|
76
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
77
|
+
var ownKeys = function(o) {
|
|
78
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
79
|
+
var ar = [];
|
|
80
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
81
|
+
return ar;
|
|
82
|
+
};
|
|
83
|
+
return ownKeys(o);
|
|
84
|
+
};
|
|
85
|
+
return function (mod) {
|
|
86
|
+
if (mod && mod.__esModule) return mod;
|
|
87
|
+
var result = {};
|
|
88
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
89
|
+
__setModuleDefault(result, mod);
|
|
90
|
+
return result;
|
|
91
|
+
};
|
|
92
|
+
})();
|
|
93
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
94
|
+
exports.analyseHealStats = analyseHealStats;
|
|
95
|
+
exports.aggregate = aggregate;
|
|
96
|
+
const fs = __importStar(require("fs-extra"));
|
|
97
|
+
const path = __importStar(require("path"));
|
|
98
|
+
const PACKAGE_VERSION = "3.10.0";
|
|
99
|
+
/** Public entry point. */
|
|
100
|
+
async function analyseHealStats(opts) {
|
|
101
|
+
const jsonlPath = await resolveJsonlPath(opts.inputPath);
|
|
102
|
+
const events = await readEvents(jsonlPath);
|
|
103
|
+
const summary = aggregate(events, jsonlPath, opts.topN ?? 10);
|
|
104
|
+
const outputPath = opts.outputPath ?? path.join(path.dirname(jsonlPath), "heal-stats.json");
|
|
105
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
106
|
+
await fs.writeFile(outputPath, JSON.stringify(summary, null, 2), "utf8");
|
|
107
|
+
return { outputPath, totalEvents: events.length };
|
|
108
|
+
}
|
|
109
|
+
/* ─── pure aggregation (testable without fs) ──────────────────────── */
|
|
110
|
+
/**
|
|
111
|
+
* Compute a HealStats summary from an in-memory event list. Pure
|
|
112
|
+
* function — no I/O. Used both by the public CLI entry and the unit
|
|
113
|
+
* tests.
|
|
114
|
+
*/
|
|
115
|
+
function aggregate(events, source, topN) {
|
|
116
|
+
const fieldKey = (e) => `${e.page}::${e.name}`;
|
|
117
|
+
const fieldAttempts = new Map();
|
|
118
|
+
const errorCounts = new Map();
|
|
119
|
+
const candidateStats = new Map();
|
|
120
|
+
const scenarioStats = new Map();
|
|
121
|
+
const uniquePages = new Set();
|
|
122
|
+
const uniqueFields = new Set();
|
|
123
|
+
const retryLatencies = [];
|
|
124
|
+
/**
|
|
125
|
+
* Match heal_attempt events with their matching `healed` /
|
|
126
|
+
* `heal_unavailable` outcome by (page, name, method). The keying
|
|
127
|
+
* assumes a heal attempt resolves before another one fires for the
|
|
128
|
+
* same field+method combo, which holds in practice — actions run
|
|
129
|
+
* serially per test.
|
|
130
|
+
*/
|
|
131
|
+
const openAttempts = new Map();
|
|
132
|
+
const attemptKey = (e) => `${e.page}::${e.name}::${e.method ?? ""}`;
|
|
133
|
+
let registrations = 0;
|
|
134
|
+
let healAttempts = 0;
|
|
135
|
+
let healed = 0;
|
|
136
|
+
let healUnavailable = 0;
|
|
137
|
+
for (const e of events) {
|
|
138
|
+
uniquePages.add(e.page);
|
|
139
|
+
uniqueFields.add(fieldKey(e));
|
|
140
|
+
if (e.event === "register") {
|
|
141
|
+
registrations += 1;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (e.event === "heal_attempt") {
|
|
145
|
+
healAttempts += 1;
|
|
146
|
+
const field = fieldAttempts.get(fieldKey(e)) ?? {
|
|
147
|
+
page: e.page,
|
|
148
|
+
name: e.name,
|
|
149
|
+
attempts: 0,
|
|
150
|
+
healed: 0,
|
|
151
|
+
};
|
|
152
|
+
field.attempts += 1;
|
|
153
|
+
fieldAttempts.set(fieldKey(e), field);
|
|
154
|
+
if (e.error) {
|
|
155
|
+
const msg = truncateError(e.error);
|
|
156
|
+
errorCounts.set(msg, (errorCounts.get(msg) ?? 0) + 1);
|
|
157
|
+
}
|
|
158
|
+
const scenario = e.scenario_name ?? "(no scenario context)";
|
|
159
|
+
const ss = scenarioStats.get(scenario) ?? { attempts: 0, healed: 0 };
|
|
160
|
+
ss.attempts += 1;
|
|
161
|
+
scenarioStats.set(scenario, ss);
|
|
162
|
+
openAttempts.set(attemptKey(e), Date.parse(e.ts));
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (e.event === "healed") {
|
|
166
|
+
healed += 1;
|
|
167
|
+
const field = fieldAttempts.get(fieldKey(e)) ?? {
|
|
168
|
+
page: e.page,
|
|
169
|
+
name: e.name,
|
|
170
|
+
attempts: 0,
|
|
171
|
+
healed: 0,
|
|
172
|
+
};
|
|
173
|
+
field.healed += 1;
|
|
174
|
+
fieldAttempts.set(fieldKey(e), field);
|
|
175
|
+
if (e.healed) {
|
|
176
|
+
const cs = candidateStats.get(e.healed) ?? {
|
|
177
|
+
promotions: 0,
|
|
178
|
+
confidenceSum: 0,
|
|
179
|
+
confidenceCount: 0,
|
|
180
|
+
};
|
|
181
|
+
cs.promotions += 1;
|
|
182
|
+
if (typeof e.confidence === "number") {
|
|
183
|
+
cs.confidenceSum += e.confidence;
|
|
184
|
+
cs.confidenceCount += 1;
|
|
185
|
+
}
|
|
186
|
+
candidateStats.set(e.healed, cs);
|
|
187
|
+
}
|
|
188
|
+
const scenario = e.scenario_name ?? "(no scenario context)";
|
|
189
|
+
const ss = scenarioStats.get(scenario) ?? { attempts: 0, healed: 0 };
|
|
190
|
+
ss.healed += 1;
|
|
191
|
+
scenarioStats.set(scenario, ss);
|
|
192
|
+
const startTs = openAttempts.get(attemptKey(e));
|
|
193
|
+
if (startTs !== undefined) {
|
|
194
|
+
retryLatencies.push(Date.parse(e.ts) - startTs);
|
|
195
|
+
openAttempts.delete(attemptKey(e));
|
|
196
|
+
}
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (e.event === "heal_unavailable") {
|
|
200
|
+
healUnavailable += 1;
|
|
201
|
+
openAttempts.delete(attemptKey(e));
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const sortedFailingFields = Array.from(fieldAttempts.values())
|
|
206
|
+
.sort((a, b) => b.attempts - a.attempts)
|
|
207
|
+
.slice(0, topN);
|
|
208
|
+
const sortedErrors = Array.from(errorCounts.entries())
|
|
209
|
+
.map(([error, count]) => ({ error, count }))
|
|
210
|
+
.sort((a, b) => b.count - a.count)
|
|
211
|
+
.slice(0, topN);
|
|
212
|
+
const sortedCandidates = Array.from(candidateStats.entries())
|
|
213
|
+
.map(([selector, s]) => ({
|
|
214
|
+
selector,
|
|
215
|
+
promotions: s.promotions,
|
|
216
|
+
averageConfidence: s.confidenceCount > 0 ? s.confidenceSum / s.confidenceCount : null,
|
|
217
|
+
}))
|
|
218
|
+
.sort((a, b) => b.promotions - a.promotions)
|
|
219
|
+
.slice(0, topN);
|
|
220
|
+
const sortedScenarios = Array.from(scenarioStats.entries())
|
|
221
|
+
.map(([scenario, s]) => ({ scenario, ...s }))
|
|
222
|
+
.sort((a, b) => b.attempts - a.attempts)
|
|
223
|
+
.slice(0, topN);
|
|
224
|
+
const latenciesSorted = [...retryLatencies].sort((a, b) => a - b);
|
|
225
|
+
return {
|
|
226
|
+
version: PACKAGE_VERSION,
|
|
227
|
+
generatedAt: new Date().toISOString(),
|
|
228
|
+
source,
|
|
229
|
+
totals: {
|
|
230
|
+
registrations,
|
|
231
|
+
healAttempts,
|
|
232
|
+
healed,
|
|
233
|
+
healUnavailable,
|
|
234
|
+
healRate: healAttempts === 0 ? 0 : healed / healAttempts,
|
|
235
|
+
uniqueFields: uniqueFields.size,
|
|
236
|
+
uniquePages: uniquePages.size,
|
|
237
|
+
},
|
|
238
|
+
topFailingFields: sortedFailingFields,
|
|
239
|
+
topErrors: sortedErrors,
|
|
240
|
+
topCandidates: sortedCandidates,
|
|
241
|
+
retryLatencyMs: {
|
|
242
|
+
p50: percentile(latenciesSorted, 0.5),
|
|
243
|
+
p95: percentile(latenciesSorted, 0.95),
|
|
244
|
+
min: latenciesSorted.length ? latenciesSorted[0] : 0,
|
|
245
|
+
max: latenciesSorted.length
|
|
246
|
+
? latenciesSorted[latenciesSorted.length - 1]
|
|
247
|
+
: 0,
|
|
248
|
+
},
|
|
249
|
+
perScenario: sortedScenarios,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function percentile(sortedAsc, p) {
|
|
253
|
+
if (sortedAsc.length === 0)
|
|
254
|
+
return 0;
|
|
255
|
+
const idx = Math.ceil(sortedAsc.length * p) - 1;
|
|
256
|
+
return sortedAsc[Math.max(0, Math.min(idx, sortedAsc.length - 1))];
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Normalise an error message so different timestamps / IDs collapse to
|
|
260
|
+
* the same bucket: lowercase, strip leading "Error: " / "TimeoutError",
|
|
261
|
+
* collapse runs of digits.
|
|
262
|
+
*/
|
|
263
|
+
function truncateError(s) {
|
|
264
|
+
return s
|
|
265
|
+
.toLowerCase()
|
|
266
|
+
.replace(/^(?:error|timeouterror|protocolerror):\s*/i, "")
|
|
267
|
+
.replace(/\b\d{3,}\b/g, "<n>")
|
|
268
|
+
.slice(0, 120);
|
|
269
|
+
}
|
|
270
|
+
/* ─── filesystem glue ─────────────────────────────────────────────── */
|
|
271
|
+
async function resolveJsonlPath(inputPath) {
|
|
272
|
+
const stat = await fs.stat(inputPath).catch(() => undefined);
|
|
273
|
+
if (stat?.isFile())
|
|
274
|
+
return inputPath;
|
|
275
|
+
if (stat?.isDirectory()) {
|
|
276
|
+
const direct = path.join(inputPath, "heal-events.jsonl");
|
|
277
|
+
if (await fs.pathExists(direct))
|
|
278
|
+
return direct;
|
|
279
|
+
const nested = path.join(inputPath, "artefacts", "heal-events.jsonl");
|
|
280
|
+
if (await fs.pathExists(nested))
|
|
281
|
+
return nested;
|
|
282
|
+
return nested; // analyser will handle 'no events' gracefully
|
|
283
|
+
}
|
|
284
|
+
return path.join(inputPath, "artefacts", "heal-events.jsonl");
|
|
285
|
+
}
|
|
286
|
+
async function readEvents(jsonlPath) {
|
|
287
|
+
if (!(await fs.pathExists(jsonlPath)))
|
|
288
|
+
return [];
|
|
289
|
+
const raw = await fs.readFile(jsonlPath, "utf8");
|
|
290
|
+
const out = [];
|
|
291
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
292
|
+
if (!line.trim())
|
|
293
|
+
continue;
|
|
294
|
+
try {
|
|
295
|
+
out.push(JSON.parse(line));
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
/* skip malformed line */
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return out;
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=healStats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"healStats.js","sourceRoot":"","sources":["../../src/reports/healStats.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEH,4CAaC;AASD,8BAgKC;AAzPD,6CAA+B;AAC/B,2CAA6B;AA+D7B,MAAM,eAAe,GAAG,QAAQ,CAAC;AAEjC,0BAA0B;AACnB,KAAK,UAAU,gBAAgB,CACpC,IAAsB;IAEtB,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAE9D,MAAM,UAAU,GACd,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAC3E,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAEzE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;AACpD,CAAC;AAED,yEAAyE;AAEzE;;;;GAIG;AACH,SAAgB,SAAS,CACvB,MAAmB,EACnB,MAAc,EACd,IAAY;IAEZ,MAAM,QAAQ,GAAG,CAAC,CAAY,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,MAAM,aAAa,GAAG,IAAI,GAAG,EAG1B,CAAC;IACJ,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C,MAAM,cAAc,GAAG,IAAI,GAAG,EAG3B,CAAC;IACJ,MAAM,aAAa,GAAG,IAAI,GAAG,EAG1B,CAAC;IACJ,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC;;;;;;OAMG;IACH,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC/C,MAAM,UAAU,GAAG,CAAC,CAAY,EAAE,EAAE,CAClC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;IAE5C,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,eAAe,GAAG,CAAC,CAAC;IAExB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACxB,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAE9B,IAAI,CAAC,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAC3B,aAAa,IAAI,CAAC,CAAC;YACnB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,CAAC,KAAK,KAAK,cAAc,EAAE,CAAC;YAC/B,YAAY,IAAI,CAAC,CAAC;YAClB,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI;gBAC9C,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,MAAM,EAAE,CAAC;aACV,CAAC;YACF,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;YACpB,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YACtC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;gBACZ,MAAM,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;gBACnC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACxD,CAAC;YACD,MAAM,QAAQ,GAAG,CAAC,CAAC,aAAa,IAAI,uBAAuB,CAAC;YAC5D,MAAM,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YACrE,EAAE,CAAC,QAAQ,IAAI,CAAC,CAAC;YACjB,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAChC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAClD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACzB,MAAM,IAAI,CAAC,CAAC;YACZ,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI;gBAC9C,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,MAAM,EAAE,CAAC;aACV,CAAC;YACF,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;YAClB,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YACtC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;gBACb,MAAM,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI;oBACzC,UAAU,EAAE,CAAC;oBACb,aAAa,EAAE,CAAC;oBAChB,eAAe,EAAE,CAAC;iBACnB,CAAC;gBACF,EAAE,CAAC,UAAU,IAAI,CAAC,CAAC;gBACnB,IAAI,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;oBACrC,EAAE,CAAC,aAAa,IAAI,CAAC,CAAC,UAAU,CAAC;oBACjC,EAAE,CAAC,eAAe,IAAI,CAAC,CAAC;gBAC1B,CAAC;gBACD,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACnC,CAAC;YACD,MAAM,QAAQ,GAAG,CAAC,CAAC,aAAa,IAAI,uBAAuB,CAAC;YAC5D,MAAM,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YACrE,EAAE,CAAC,MAAM,IAAI,CAAC,CAAC;YACf,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAChC,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YAChD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;gBAChD,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YACrC,CAAC;YACD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,CAAC,KAAK,KAAK,kBAAkB,EAAE,CAAC;YACnC,eAAe,IAAI,CAAC,CAAC;YACrB,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,SAAS;QACX,CAAC;IACH,CAAC;IAED,MAAM,mBAAmB,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;SAC3D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;SACvC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAElB,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;SACnD,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;SAC3C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;SACjC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAElB,MAAM,gBAAgB,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;SAC1D,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACvB,QAAQ;QACR,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,iBAAiB,EACf,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI;KACrE,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;SAC3C,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAElB,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;SACxD,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;SAC5C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;SACvC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAElB,MAAM,eAAe,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAElE,OAAO;QACL,OAAO,EAAE,eAAe;QACxB,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,MAAM;QACN,MAAM,EAAE;YACN,aAAa;YACb,YAAY;YACZ,MAAM;YACN,eAAe;YACf,QAAQ,EAAE,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,YAAY;YACxD,YAAY,EAAE,YAAY,CAAC,IAAI;YAC/B,WAAW,EAAE,WAAW,CAAC,IAAI;SAC9B;QACD,gBAAgB,EAAE,mBAAmB;QACrC,SAAS,EAAE,YAAY;QACvB,aAAa,EAAE,gBAAgB;QAC/B,cAAc,EAAE;YACd,GAAG,EAAE,UAAU,CAAC,eAAe,EAAE,GAAG,CAAC;YACrC,GAAG,EAAE,UAAU,CAAC,eAAe,EAAE,IAAI,CAAC;YACtC,GAAG,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpD,GAAG,EAAE,eAAe,CAAC,MAAM;gBACzB,CAAC,CAAC,eAAe,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;gBAC7C,CAAC,CAAC,CAAC;SACN;QACD,WAAW,EAAE,eAAe;KAC7B,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,SAAmB,EAAE,CAAS;IAChD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAChD,OAAO,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,CAAS;IAC9B,OAAO,CAAC;SACL,WAAW,EAAE;SACb,OAAO,CAAC,4CAA4C,EAAE,EAAE,CAAC;SACzD,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC;SAC7B,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACnB,CAAC;AAED,yEAAyE;AAEzE,KAAK,UAAU,gBAAgB,CAAC,SAAiB;IAC/C,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC7D,IAAI,IAAI,EAAE,MAAM,EAAE;QAAE,OAAO,SAAS,CAAC;IACrC,IAAI,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;QACzD,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;QACtE,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QAC/C,OAAO,MAAM,CAAC,CAAC,8CAA8C;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;AAChE,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,SAAiB;IACzC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,GAAG,GAAgB,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAC3B,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAc,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -170,7 +170,8 @@ export interface ScaffoldOptions {
|
|
|
170
170
|
* sidecar. Production runs MUST keep this off.
|
|
171
171
|
*/
|
|
172
172
|
llmConfig?: {
|
|
173
|
-
provider:
|
|
173
|
+
/** v3.12.0 — three-provider parity: anthropic, openai, gemini. */
|
|
174
|
+
provider: "anthropic" | "openai" | "gemini";
|
|
174
175
|
model?: string;
|
|
175
176
|
apiKey?: string;
|
|
176
177
|
governanceUrl?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vijaypjavvadi/bdd2pw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.0",
|
|
4
4
|
"description": "Scaffold runnable Playwright TypeScript tests from Gherkin .feature files. Detects existing Page Objects, scans live pages via Microsoft Playwright MCP, emits POMs + specs, ready for execution. CLI + HTTP service. Optional governed LLM fallback (Anthropic / OpenAI / Gemini).",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bdd2pw": "dist/cli.js"
|
|
@@ -81,6 +81,8 @@
|
|
|
81
81
|
},
|
|
82
82
|
"optionalDependencies": {
|
|
83
83
|
"@anthropic-ai/sdk": "^0.91.1",
|
|
84
|
+
"openai": "^4.77.0",
|
|
85
|
+
"@google/generative-ai": "^0.21.0",
|
|
84
86
|
"better-sqlite3": "^11.0.0",
|
|
85
87
|
"pino-pretty": "^11.0.0",
|
|
86
88
|
"playwright": "^1.45.0"
|