@ia-qa/self-healing 0.1.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/README.md +91 -0
- package/TUTORIAL.md +399 -0
- package/dist/aom.d.ts +22 -0
- package/dist/aom.js +58 -0
- package/dist/aom.js.map +1 -0
- package/dist/browser/contract.d.ts +1 -0
- package/dist/browser/contract.js +107 -0
- package/dist/browser/contract.js.map +1 -0
- package/dist/browser/extract.d.ts +25 -0
- package/dist/browser/extract.js +194 -0
- package/dist/browser/extract.js.map +1 -0
- package/dist/browser/match.d.ts +88 -0
- package/dist/browser/match.js +179 -0
- package/dist/browser/match.js.map +1 -0
- package/dist/cli/diff.d.ts +13 -0
- package/dist/cli/diff.js +143 -0
- package/dist/cli/diff.js.map +1 -0
- package/dist/cli/fix.d.ts +17 -0
- package/dist/cli/fix.js +144 -0
- package/dist/cli/fix.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +50 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +8 -0
- package/dist/cli/init.js +185 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/map.d.ts +9 -0
- package/dist/cli/map.js +118 -0
- package/dist/cli/map.js.map +1 -0
- package/dist/config.d.ts +55 -0
- package/dist/config.js +111 -0
- package/dist/config.js.map +1 -0
- package/dist/fixEngine.d.ts +49 -0
- package/dist/fixEngine.js +154 -0
- package/dist/fixEngine.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/launcher.d.ts +38 -0
- package/dist/launcher.js +38 -0
- package/dist/launcher.js.map +1 -0
- package/dist/mapUrl.d.ts +34 -0
- package/dist/mapUrl.js +90 -0
- package/dist/mapUrl.js.map +1 -0
- package/dist/markdown.d.ts +10 -0
- package/dist/markdown.js +54 -0
- package/dist/markdown.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +251 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/playwright/healer.d.ts +39 -0
- package/dist/playwright/healer.js +84 -0
- package/dist/playwright/healer.js.map +1 -0
- package/package.json +54 -0
package/dist/cli/map.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runMap = runMap;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const launcher_1 = require("../launcher");
|
|
39
|
+
const config_1 = require("../config");
|
|
40
|
+
const aom_1 = require("../aom");
|
|
41
|
+
const markdown_1 = require("../markdown");
|
|
42
|
+
/**
|
|
43
|
+
* `ia-qa-heal map [pageName]` — scraping & DOM compression engine.
|
|
44
|
+
*
|
|
45
|
+
* Loads `.ia-qa/config.json`, resolves credentials at runtime (env or AWS SSM,
|
|
46
|
+
* see `resolveSecret`), launches headless Chromium, logs in if configured,
|
|
47
|
+
* then visits each target page and writes a compressed accessibility mapping
|
|
48
|
+
* to `.ia-qa/mapping/<page-name>.json`.
|
|
49
|
+
*/
|
|
50
|
+
async function runMap(pageNameArg) {
|
|
51
|
+
const config = (0, config_1.loadConfig)();
|
|
52
|
+
const targets = pageNameArg
|
|
53
|
+
? config.pages.filter((p) => p.name === pageNameArg)
|
|
54
|
+
: config.pages;
|
|
55
|
+
if (targets.length === 0) {
|
|
56
|
+
const known = config.pages.map((p) => p.name).join(', ') || '(none configured)';
|
|
57
|
+
throw new Error(`Unknown page "${pageNameArg}". Configured pages: ${known}`);
|
|
58
|
+
}
|
|
59
|
+
const { browser, using } = await (0, launcher_1.launchBrowser)();
|
|
60
|
+
console.log(`🌐 Using ${using}`);
|
|
61
|
+
const context = await browser.newContext();
|
|
62
|
+
const page = await context.newPage();
|
|
63
|
+
try {
|
|
64
|
+
if (config.auth) {
|
|
65
|
+
console.log('🔐 Logging in…');
|
|
66
|
+
await login(page, config);
|
|
67
|
+
console.log(' Logged in.');
|
|
68
|
+
}
|
|
69
|
+
for (const target of targets) {
|
|
70
|
+
const url = new URL(target.url, config.baseUrl).toString();
|
|
71
|
+
console.log(`🗺 Mapping "${target.name}" (${url})…`);
|
|
72
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
|
|
73
|
+
const elements = await (0, aom_1.extractInteractiveElements)(page);
|
|
74
|
+
const mapping = {
|
|
75
|
+
page: target.name,
|
|
76
|
+
url: target.url,
|
|
77
|
+
capturedAt: new Date().toISOString(),
|
|
78
|
+
elements,
|
|
79
|
+
};
|
|
80
|
+
const file = (0, aom_1.saveMapping)(mapping);
|
|
81
|
+
const md = (0, markdown_1.saveMarkdown)(mapping);
|
|
82
|
+
console.log(` ✔ ${elements.length} interactive elements → ` +
|
|
83
|
+
`${path.relative(process.cwd(), file)} + ${path.relative(process.cwd(), md)}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
await browser.close();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function login(page, config) {
|
|
91
|
+
const auth = config.auth;
|
|
92
|
+
const usernameRef = config.secrets[auth.usernameSecret];
|
|
93
|
+
const passwordRef = config.secrets[auth.passwordSecret];
|
|
94
|
+
if (!usernameRef || !passwordRef) {
|
|
95
|
+
throw new Error(`auth.usernameSecret/"${auth.usernameSecret}" or auth.passwordSecret/"${auth.passwordSecret}" not found in config.secrets.`);
|
|
96
|
+
}
|
|
97
|
+
const [username, password] = await Promise.all([
|
|
98
|
+
(0, config_1.resolveSecret)(usernameRef),
|
|
99
|
+
(0, config_1.resolveSecret)(passwordRef),
|
|
100
|
+
]);
|
|
101
|
+
await page.goto(new URL(auth.loginUrl, config.baseUrl).toString(), {
|
|
102
|
+
waitUntil: 'domcontentloaded',
|
|
103
|
+
timeout: 30000,
|
|
104
|
+
});
|
|
105
|
+
await page.fill(auth.usernameSelector, username);
|
|
106
|
+
await page.fill(auth.passwordSelector, password);
|
|
107
|
+
await page.click(auth.submitSelector);
|
|
108
|
+
await page.waitForLoadState('networkidle', { timeout: 30000 });
|
|
109
|
+
if (auth.successSelector) {
|
|
110
|
+
try {
|
|
111
|
+
await page.waitForSelector(auth.successSelector, { timeout: 10000 });
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
throw new Error(`Login likely failed: "${auth.successSelector}" not visible after submit. Check credentials and selectors.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map.js","sourceRoot":"","sources":["../../src/cli/map.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,wBA6CC;AA5DD,2CAA6B;AAE7B,0CAA4C;AAC5C,sCAAkE;AAClE,gCAA8E;AAC9E,0CAA2C;AAE3C;;;;;;;GAOG;AACI,KAAK,UAAU,MAAM,CAAC,WAAoB;IAC/C,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAE5B,MAAM,OAAO,GAAG,WAAW;QACzB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC;QACpD,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;IACjB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,mBAAmB,CAAC;QAChF,MAAM,IAAI,KAAK,CAAC,iBAAiB,WAAW,wBAAwB,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,IAAA,wBAAa,GAAE,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IAErC,IAAI,CAAC;QACH,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;YAC9B,MAAM,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC/B,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC3D,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,IAAI,MAAM,GAAG,IAAI,CAAC,CAAC;YACtD,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAEnE,MAAM,QAAQ,GAAG,MAAM,IAAA,gCAA0B,EAAC,IAAI,CAAC,CAAC;YACxD,MAAM,OAAO,GAAgB;gBAC3B,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACpC,QAAQ;aACT,CAAC;YACF,MAAM,IAAI,GAAG,IAAA,iBAAW,EAAC,OAAO,CAAC,CAAC;YAClC,MAAM,EAAE,GAAG,IAAA,uBAAY,EAAC,OAAO,CAAC,CAAC;YACjC,OAAO,CAAC,GAAG,CACT,QAAQ,QAAQ,CAAC,MAAM,0BAA0B;gBAC/C,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAChF,CAAC;QACJ,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,KAAK,CAAC,IAAU,EAAE,MAAkB;IACjD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAK,CAAC;IAC1B,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxD,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,wBAAwB,IAAI,CAAC,cAAc,6BAA6B,IAAI,CAAC,cAAc,gCAAgC,CAC5H,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC7C,IAAA,sBAAa,EAAC,WAAW,CAAC;QAC1B,IAAA,sBAAa,EAAC,WAAW,CAAC;KAC3B,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QACjE,SAAS,EAAE,kBAAkB;QAC7B,OAAO,EAAE,KAAK;KACf,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACtC,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAE/D,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CACb,yBAAyB,IAAI,CAAC,eAAe,8DAA8D,CAC5G,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration & secret resolution for @ia-qa/self-healing.
|
|
3
|
+
*
|
|
4
|
+
* Security model: `.ia-qa/config.json` only ever contains *references* to
|
|
5
|
+
* secrets (an env var name, or an AWS SSM parameter name). Actual values are
|
|
6
|
+
* resolved at runtime — from `process.env` or from SSM Parameter Store using
|
|
7
|
+
* the developer's local AWS credential chain — and are never written to disk.
|
|
8
|
+
*/
|
|
9
|
+
export type SecretSource = 'env' | 'aws-ssm';
|
|
10
|
+
export interface SecretRef {
|
|
11
|
+
source: SecretSource;
|
|
12
|
+
/** `env`: the environment variable name. `aws-ssm`: the parameter name (e.g. `/staging/app/password`). */
|
|
13
|
+
key: string;
|
|
14
|
+
/** AWS region — required when source is `aws-ssm`. */
|
|
15
|
+
region?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface PageTarget {
|
|
18
|
+
/** Logical page name — becomes the mapping filename (`.ia-qa/mapping/<name>.json`). */
|
|
19
|
+
name: string;
|
|
20
|
+
/** Path or absolute URL, resolved against `baseUrl`. */
|
|
21
|
+
url: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AuthConfig {
|
|
24
|
+
loginUrl: string;
|
|
25
|
+
usernameSelector: string;
|
|
26
|
+
passwordSelector: string;
|
|
27
|
+
submitSelector: string;
|
|
28
|
+
/** Keys into `config.secrets` used to fill the login form. */
|
|
29
|
+
usernameSecret: string;
|
|
30
|
+
passwordSecret: string;
|
|
31
|
+
/** Optional selector that must appear after login (sanity check). */
|
|
32
|
+
successSelector?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface IaQaConfig {
|
|
35
|
+
framework: 'playwright';
|
|
36
|
+
baseUrl: string;
|
|
37
|
+
secrets: Record<string, SecretRef>;
|
|
38
|
+
auth?: AuthConfig;
|
|
39
|
+
pages: PageTarget[];
|
|
40
|
+
}
|
|
41
|
+
export declare const CONFIG_DIR = ".ia-qa";
|
|
42
|
+
export declare const MAPPING_DIRNAME = "mapping";
|
|
43
|
+
export declare function configPath(cwd?: string): string;
|
|
44
|
+
export declare function mappingDir(cwd?: string): string;
|
|
45
|
+
export declare function mappingPath(pageName: string, dir?: string): string;
|
|
46
|
+
export declare function loadConfig(cwd?: string): IaQaConfig;
|
|
47
|
+
export declare function saveConfig(config: IaQaConfig, cwd?: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve a secret reference to its actual value.
|
|
50
|
+
* - `env`: read from `process.env` (load your `.env` beforehand, e.g. via dotenv).
|
|
51
|
+
* - `aws-ssm`: `GetParameter` with decryption, using the local AWS credential
|
|
52
|
+
* chain (env vars, `~/.aws/credentials`, SSO, instance profile…).
|
|
53
|
+
* The AWS SDK is imported lazily so env-only users don't need it at runtime.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveSecret(ref: SecretRef): Promise<string>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.MAPPING_DIRNAME = exports.CONFIG_DIR = void 0;
|
|
37
|
+
exports.configPath = configPath;
|
|
38
|
+
exports.mappingDir = mappingDir;
|
|
39
|
+
exports.mappingPath = mappingPath;
|
|
40
|
+
exports.loadConfig = loadConfig;
|
|
41
|
+
exports.saveConfig = saveConfig;
|
|
42
|
+
exports.resolveSecret = resolveSecret;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
exports.CONFIG_DIR = '.ia-qa';
|
|
46
|
+
exports.MAPPING_DIRNAME = 'mapping';
|
|
47
|
+
function configPath(cwd = process.cwd()) {
|
|
48
|
+
return path.join(cwd, exports.CONFIG_DIR, 'config.json');
|
|
49
|
+
}
|
|
50
|
+
function mappingDir(cwd = process.cwd()) {
|
|
51
|
+
return path.join(cwd, exports.CONFIG_DIR, exports.MAPPING_DIRNAME);
|
|
52
|
+
}
|
|
53
|
+
function mappingPath(pageName, dir = mappingDir()) {
|
|
54
|
+
const safe = pageName.replace(/[^a-zA-Z0-9._-]/g, '-');
|
|
55
|
+
return path.join(dir, `${safe}.json`);
|
|
56
|
+
}
|
|
57
|
+
function loadConfig(cwd = process.cwd()) {
|
|
58
|
+
const file = configPath(cwd);
|
|
59
|
+
if (!fs.existsSync(file)) {
|
|
60
|
+
throw new Error(`No ${exports.CONFIG_DIR}/config.json found in ${cwd}. Run \`ia-qa-heal init\` first.`);
|
|
61
|
+
}
|
|
62
|
+
const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
63
|
+
if (!raw.baseUrl || !Array.isArray(raw.pages)) {
|
|
64
|
+
throw new Error(`${file} is malformed: "baseUrl" and "pages" are required.`);
|
|
65
|
+
}
|
|
66
|
+
return raw;
|
|
67
|
+
}
|
|
68
|
+
function saveConfig(config, cwd = process.cwd()) {
|
|
69
|
+
const file = configPath(cwd);
|
|
70
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
71
|
+
fs.mkdirSync(mappingDir(cwd), { recursive: true });
|
|
72
|
+
fs.writeFileSync(file, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
73
|
+
return file;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Resolve a secret reference to its actual value.
|
|
77
|
+
* - `env`: read from `process.env` (load your `.env` beforehand, e.g. via dotenv).
|
|
78
|
+
* - `aws-ssm`: `GetParameter` with decryption, using the local AWS credential
|
|
79
|
+
* chain (env vars, `~/.aws/credentials`, SSO, instance profile…).
|
|
80
|
+
* The AWS SDK is imported lazily so env-only users don't need it at runtime.
|
|
81
|
+
*/
|
|
82
|
+
async function resolveSecret(ref) {
|
|
83
|
+
if (!ref || !ref.key) {
|
|
84
|
+
throw new Error('Invalid secret reference: missing "key".');
|
|
85
|
+
}
|
|
86
|
+
if (ref.source === 'env') {
|
|
87
|
+
const value = process.env[ref.key];
|
|
88
|
+
if (!value) {
|
|
89
|
+
throw new Error(`Environment variable "${ref.key}" is not set. Add it to your .env or shell environment — never to config.json.`);
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
if (ref.source === 'aws-ssm') {
|
|
94
|
+
const { SSMClient, GetParameterCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-ssm')));
|
|
95
|
+
const client = new SSMClient(ref.region ? { region: ref.region } : {});
|
|
96
|
+
try {
|
|
97
|
+
const out = await client.send(new GetParameterCommand({ Name: ref.key, WithDecryption: true }));
|
|
98
|
+
const value = out.Parameter?.Value;
|
|
99
|
+
if (!value)
|
|
100
|
+
throw new Error('parameter exists but has no value');
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
105
|
+
throw new Error(`Failed to read SSM parameter "${ref.key}"${ref.region ? ` (region ${ref.region})` : ''}: ${reason}. ` +
|
|
106
|
+
'Check your local AWS credentials and IAM permissions (ssm:GetParameter + kms:Decrypt for SecureString).');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
throw new Error(`Unknown secret source "${ref.source}". Expected "env" or "aws-ssm".`);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The deterministic codemod engine, shared by the CLI (`ia-qa-heal fix`) and the
|
|
3
|
+
* MCP server (`fix_tests` tool). Pure of any I/O presentation — it computes the
|
|
4
|
+
* rewrites, applies them to files, and returns a structured result. Callers do
|
|
5
|
+
* the reporting.
|
|
6
|
+
*/
|
|
7
|
+
export interface DiffRowLike {
|
|
8
|
+
status: string;
|
|
9
|
+
selector?: string;
|
|
10
|
+
healedSelector?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface FileEdit {
|
|
13
|
+
file: string;
|
|
14
|
+
replacements: Array<{
|
|
15
|
+
from: string;
|
|
16
|
+
to: string;
|
|
17
|
+
count: number;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
export interface FixResult {
|
|
21
|
+
rewrites: Array<{
|
|
22
|
+
from: string;
|
|
23
|
+
to: string;
|
|
24
|
+
}>;
|
|
25
|
+
filesScanned: number;
|
|
26
|
+
edits: FileEdit[];
|
|
27
|
+
totalReplacements: number;
|
|
28
|
+
dryRun: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Map of old→new selector, taken ONLY from the healable rows. */
|
|
31
|
+
export declare function computeRewrites(rows: DiffRowLike[]): Map<string, string>;
|
|
32
|
+
/**
|
|
33
|
+
* Apply `rewrites` to every file in `files`. Replaces a selector only where it
|
|
34
|
+
* appears as a quoted string literal (`'…'`/`"…"`/`` `…` ``), quote-safe for
|
|
35
|
+
* attribute selectors. When `dryRun` is true, computes counts without writing.
|
|
36
|
+
*/
|
|
37
|
+
export declare function applyRewrites(files: string[], rewrites: Map<string, string>, dryRun: boolean): FixResult;
|
|
38
|
+
/**
|
|
39
|
+
* Replace `oldSel` with `newSel` only inside a matching pair of quotes.
|
|
40
|
+
* Quote-safe: if the source wraps the old selector in the same quote the new
|
|
41
|
+
* selector uses (attribute selectors contain `"`), re-wrap the replacement in a
|
|
42
|
+
* quote char not present in `newSel` so we never emit a broken string literal.
|
|
43
|
+
*/
|
|
44
|
+
export declare function replaceQuoted(text: string, oldSel: string, newSel: string): {
|
|
45
|
+
text: string;
|
|
46
|
+
count: number;
|
|
47
|
+
};
|
|
48
|
+
/** Expand file/dir paths to a de-duped list of code files (skips node_modules/.git). */
|
|
49
|
+
export declare function collectFiles(paths: string[], onSkip?: (p: string) => void): string[];
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.computeRewrites = computeRewrites;
|
|
37
|
+
exports.applyRewrites = applyRewrites;
|
|
38
|
+
exports.replaceQuoted = replaceQuoted;
|
|
39
|
+
exports.collectFiles = collectFiles;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
/** Map of old→new selector, taken ONLY from the healable rows. */
|
|
43
|
+
function computeRewrites(rows) {
|
|
44
|
+
const rewrites = new Map();
|
|
45
|
+
for (const r of rows) {
|
|
46
|
+
if (r.status === 'healable' && r.selector && r.healedSelector) {
|
|
47
|
+
rewrites.set(r.selector, r.healedSelector);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return rewrites;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Apply `rewrites` to every file in `files`. Replaces a selector only where it
|
|
54
|
+
* appears as a quoted string literal (`'…'`/`"…"`/`` `…` ``), quote-safe for
|
|
55
|
+
* attribute selectors. When `dryRun` is true, computes counts without writing.
|
|
56
|
+
*/
|
|
57
|
+
function applyRewrites(files, rewrites, dryRun) {
|
|
58
|
+
const edits = [];
|
|
59
|
+
let totalReplacements = 0;
|
|
60
|
+
if (rewrites.size > 0) {
|
|
61
|
+
for (const file of files) {
|
|
62
|
+
const original = fs.readFileSync(file, 'utf8');
|
|
63
|
+
let updated = original;
|
|
64
|
+
const replacements = [];
|
|
65
|
+
for (const [from, to] of rewrites) {
|
|
66
|
+
const res = replaceQuoted(updated, from, to);
|
|
67
|
+
if (res.count > 0) {
|
|
68
|
+
updated = res.text;
|
|
69
|
+
replacements.push({ from, to, count: res.count });
|
|
70
|
+
totalReplacements += res.count;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (updated !== original) {
|
|
74
|
+
if (!dryRun)
|
|
75
|
+
fs.writeFileSync(file, updated, 'utf8');
|
|
76
|
+
edits.push({ file, replacements });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
rewrites: Array.from(rewrites, ([from, to]) => ({ from, to })),
|
|
82
|
+
filesScanned: files.length,
|
|
83
|
+
edits,
|
|
84
|
+
totalReplacements,
|
|
85
|
+
dryRun,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const QUOTES = ['"', "'", '`'];
|
|
89
|
+
/**
|
|
90
|
+
* Replace `oldSel` with `newSel` only inside a matching pair of quotes.
|
|
91
|
+
* Quote-safe: if the source wraps the old selector in the same quote the new
|
|
92
|
+
* selector uses (attribute selectors contain `"`), re-wrap the replacement in a
|
|
93
|
+
* quote char not present in `newSel` so we never emit a broken string literal.
|
|
94
|
+
*/
|
|
95
|
+
function replaceQuoted(text, oldSel, newSel) {
|
|
96
|
+
let count = 0;
|
|
97
|
+
let out = text;
|
|
98
|
+
for (const q of QUOTES) {
|
|
99
|
+
const needle = q + oldSel + q;
|
|
100
|
+
if (out.indexOf(needle) === -1)
|
|
101
|
+
continue;
|
|
102
|
+
const wrap = newSel.indexOf(q) === -1 ? q : QUOTES.find((c) => newSel.indexOf(c) === -1);
|
|
103
|
+
if (!wrap)
|
|
104
|
+
continue;
|
|
105
|
+
const replacement = wrap + newSel + wrap;
|
|
106
|
+
const parts = out.split(needle);
|
|
107
|
+
count += parts.length - 1;
|
|
108
|
+
out = parts.join(replacement);
|
|
109
|
+
}
|
|
110
|
+
return { text: out, count };
|
|
111
|
+
}
|
|
112
|
+
const CODE_EXT = new Set([
|
|
113
|
+
'.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
|
|
114
|
+
'.py', '.java', '.cs', '.rb', '.php', '.go', '.kt', '.feature',
|
|
115
|
+
]);
|
|
116
|
+
/** Expand file/dir paths to a de-duped list of code files (skips node_modules/.git). */
|
|
117
|
+
function collectFiles(paths, onSkip) {
|
|
118
|
+
const out = [];
|
|
119
|
+
const seen = new Set();
|
|
120
|
+
const add = (f) => {
|
|
121
|
+
const abs = path.resolve(f);
|
|
122
|
+
if (!seen.has(abs)) {
|
|
123
|
+
seen.add(abs);
|
|
124
|
+
out.push(abs);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
for (const p of paths) {
|
|
128
|
+
let stat;
|
|
129
|
+
try {
|
|
130
|
+
stat = fs.statSync(p);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
onSkip?.(p);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (stat.isDirectory())
|
|
137
|
+
walkDir(p, add);
|
|
138
|
+
else
|
|
139
|
+
add(p);
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
function walkDir(dir, add) {
|
|
144
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
145
|
+
if (entry.name === 'node_modules' || entry.name === '.git')
|
|
146
|
+
continue;
|
|
147
|
+
const full = path.join(dir, entry.name);
|
|
148
|
+
if (entry.isDirectory())
|
|
149
|
+
walkDir(full, add);
|
|
150
|
+
else if (CODE_EXT.has(path.extname(entry.name)))
|
|
151
|
+
add(full);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=fixEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixEngine.js","sourceRoot":"","sources":["../src/fixEngine.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,0CAQC;AAOD,sCAmCC;AAUD,sCAkBC;AAQD,oCAsBC;AA1ID,uCAAyB;AACzB,2CAA6B;AA4B7B,kEAAkE;AAClE,SAAgB,eAAe,CAAC,IAAmB;IACjD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;YAC9D,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,SAAgB,aAAa,CAC3B,KAAe,EACf,QAA6B,EAC7B,MAAe;IAEf,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAE1B,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC/C,IAAI,OAAO,GAAG,QAAQ,CAAC;YACvB,MAAM,YAAY,GAA6B,EAAE,CAAC;YAClD,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,QAAQ,EAAE,CAAC;gBAClC,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC7C,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;oBAClB,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC;oBACnB,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;oBAClD,iBAAiB,IAAI,GAAG,CAAC,KAAK,CAAC;gBACjC,CAAC;YACH,CAAC;YACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACzB,IAAI,CAAC,MAAM;oBAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;gBACrD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9D,YAAY,EAAE,KAAK,CAAC,MAAM;QAC1B,KAAK;QACL,iBAAiB;QACjB,MAAM;KACP,CAAC;AACJ,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAE/B;;;;;GAKG;AACH,SAAgB,aAAa,CAC3B,IAAY,EACZ,MAAc,EACd,MAAc;IAEd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;QAC9B,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAAE,SAAS;QACzC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;QACzC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAChC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QAC1B,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAC5C,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU;CAC/D,CAAC,CAAC;AAEH,wFAAwF;AACxF,SAAgB,YAAY,CAAC,KAAe,EAAE,MAA4B;IACxE,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,IAAc,CAAC;QACnB,IAAI,CAAC;YACH,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;YACZ,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,EAAE;YAAE,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;;YACnC,GAAG,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,OAAO,CAAC,GAAW,EAAE,GAAwB;IACpD,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACjE,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,SAAS;QACrE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;aACvC,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { aiClick, aiFill, llmResolverStub } from './playwright/healer';
|
|
2
|
+
export type { HealerOptions } from './playwright/healer';
|
|
3
|
+
export { extractInteractiveElements, loadMapping, saveMapping } from './aom';
|
|
4
|
+
export type { MappedElement, PageMapping } from './aom';
|
|
5
|
+
export { renderPageContract, saveMarkdown, markdownPath } from './markdown';
|
|
6
|
+
export { mapUrl, pageNameFromUrl } from './mapUrl';
|
|
7
|
+
export { launchBrowser } from './launcher';
|
|
8
|
+
export type { BrowserChoice, LaunchedBrowser } from './launcher';
|
|
9
|
+
export type { MapUrlOptions, MapUrlResult } from './mapUrl';
|
|
10
|
+
export { computeRewrites, applyRewrites, collectFiles, replaceQuoted } from './fixEngine';
|
|
11
|
+
export { loadConfig, resolveSecret } from './config';
|
|
12
|
+
export type { IaQaConfig, SecretRef, PageTarget, AuthConfig } from './config';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveSecret = exports.loadConfig = exports.replaceQuoted = exports.collectFiles = exports.applyRewrites = exports.computeRewrites = exports.launchBrowser = exports.pageNameFromUrl = exports.mapUrl = exports.markdownPath = exports.saveMarkdown = exports.renderPageContract = exports.saveMapping = exports.loadMapping = exports.extractInteractiveElements = exports.llmResolverStub = exports.aiFill = exports.aiClick = void 0;
|
|
4
|
+
var healer_1 = require("./playwright/healer");
|
|
5
|
+
Object.defineProperty(exports, "aiClick", { enumerable: true, get: function () { return healer_1.aiClick; } });
|
|
6
|
+
Object.defineProperty(exports, "aiFill", { enumerable: true, get: function () { return healer_1.aiFill; } });
|
|
7
|
+
Object.defineProperty(exports, "llmResolverStub", { enumerable: true, get: function () { return healer_1.llmResolverStub; } });
|
|
8
|
+
var aom_1 = require("./aom");
|
|
9
|
+
Object.defineProperty(exports, "extractInteractiveElements", { enumerable: true, get: function () { return aom_1.extractInteractiveElements; } });
|
|
10
|
+
Object.defineProperty(exports, "loadMapping", { enumerable: true, get: function () { return aom_1.loadMapping; } });
|
|
11
|
+
Object.defineProperty(exports, "saveMapping", { enumerable: true, get: function () { return aom_1.saveMapping; } });
|
|
12
|
+
var markdown_1 = require("./markdown");
|
|
13
|
+
Object.defineProperty(exports, "renderPageContract", { enumerable: true, get: function () { return markdown_1.renderPageContract; } });
|
|
14
|
+
Object.defineProperty(exports, "saveMarkdown", { enumerable: true, get: function () { return markdown_1.saveMarkdown; } });
|
|
15
|
+
Object.defineProperty(exports, "markdownPath", { enumerable: true, get: function () { return markdown_1.markdownPath; } });
|
|
16
|
+
var mapUrl_1 = require("./mapUrl");
|
|
17
|
+
Object.defineProperty(exports, "mapUrl", { enumerable: true, get: function () { return mapUrl_1.mapUrl; } });
|
|
18
|
+
Object.defineProperty(exports, "pageNameFromUrl", { enumerable: true, get: function () { return mapUrl_1.pageNameFromUrl; } });
|
|
19
|
+
var launcher_1 = require("./launcher");
|
|
20
|
+
Object.defineProperty(exports, "launchBrowser", { enumerable: true, get: function () { return launcher_1.launchBrowser; } });
|
|
21
|
+
var fixEngine_1 = require("./fixEngine");
|
|
22
|
+
Object.defineProperty(exports, "computeRewrites", { enumerable: true, get: function () { return fixEngine_1.computeRewrites; } });
|
|
23
|
+
Object.defineProperty(exports, "applyRewrites", { enumerable: true, get: function () { return fixEngine_1.applyRewrites; } });
|
|
24
|
+
Object.defineProperty(exports, "collectFiles", { enumerable: true, get: function () { return fixEngine_1.collectFiles; } });
|
|
25
|
+
Object.defineProperty(exports, "replaceQuoted", { enumerable: true, get: function () { return fixEngine_1.replaceQuoted; } });
|
|
26
|
+
var config_1 = require("./config");
|
|
27
|
+
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_1.loadConfig; } });
|
|
28
|
+
Object.defineProperty(exports, "resolveSecret", { enumerable: true, get: function () { return config_1.resolveSecret; } });
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,8CAAuE;AAA9D,iGAAA,OAAO,OAAA;AAAE,gGAAA,MAAM,OAAA;AAAE,yGAAA,eAAe,OAAA;AAEzC,6BAA6E;AAApE,iHAAA,0BAA0B,OAAA;AAAE,kGAAA,WAAW,OAAA;AAAE,kGAAA,WAAW,OAAA;AAE7D,uCAA4E;AAAnE,8GAAA,kBAAkB,OAAA;AAAE,wGAAA,YAAY,OAAA;AAAE,wGAAA,YAAY,OAAA;AACvD,mCAAmD;AAA1C,gGAAA,MAAM,OAAA;AAAE,yGAAA,eAAe,OAAA;AAChC,uCAA2C;AAAlC,yGAAA,aAAa,OAAA;AAGtB,yCAA0F;AAAjF,4GAAA,eAAe,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,yGAAA,YAAY,OAAA;AAAE,0GAAA,aAAa,OAAA;AACpE,mCAAqD;AAA5C,oGAAA,UAAU,OAAA;AAAE,uGAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Browser } from 'playwright';
|
|
2
|
+
/**
|
|
3
|
+
* Browser resolution — reuse whatever is already on the machine before asking
|
|
4
|
+
* anyone to download 150 MB.
|
|
5
|
+
*
|
|
6
|
+
* Two facts drive this:
|
|
7
|
+
* 1. Playwright's browser cache is **machine-wide and shared**
|
|
8
|
+
* (`%LOCALAPPDATA%\ms-playwright` / `~/.cache/ms-playwright`). If the user
|
|
9
|
+
* already has ANY Playwright install — their own tests, or the Playwright
|
|
10
|
+
* MCP — the bundled Chromium is already there and we reuse it for free.
|
|
11
|
+
* There is no "second Chromium" to avoid.
|
|
12
|
+
* 2. The real cost is **revision skew**: a different Playwright version pins a
|
|
13
|
+
* different Chromium revision, which does trigger a fresh download. For that
|
|
14
|
+
* case (and for locked-down machines that forbid the download), we let the
|
|
15
|
+
* caller point at a browser that already exists.
|
|
16
|
+
*
|
|
17
|
+
* Resolution order:
|
|
18
|
+
* 1. explicit executable path (`browserPath` / `$IAQA_BROWSER_PATH`)
|
|
19
|
+
* 2. explicit channel (`browserChannel` / `$IAQA_BROWSER_CHANNEL`, e.g. `chrome`, `msedge`)
|
|
20
|
+
* 3. Playwright's bundled Chromium (shared cache — usually already present)
|
|
21
|
+
* 4. fallback: the system Google Chrome, then Microsoft Edge
|
|
22
|
+
* Only if all four fail do we error, with an actionable message.
|
|
23
|
+
*
|
|
24
|
+
* For DOM extraction the engine is irrelevant — Chrome, Edge and Chromium all
|
|
25
|
+
* produce the same contract — so reusing the system browser is safe here.
|
|
26
|
+
*/
|
|
27
|
+
export interface BrowserChoice {
|
|
28
|
+
/** Absolute path to a browser executable. Wins over everything. */
|
|
29
|
+
browserPath?: string;
|
|
30
|
+
/** Playwright channel: 'chrome' | 'msedge' | 'chrome-beta'… */
|
|
31
|
+
browserChannel?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface LaunchedBrowser {
|
|
34
|
+
browser: Browser;
|
|
35
|
+
/** Human-readable description of what we actually launched. */
|
|
36
|
+
using: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function launchBrowser(choice?: BrowserChoice): Promise<LaunchedBrowser>;
|
package/dist/launcher.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.launchBrowser = launchBrowser;
|
|
4
|
+
const playwright_1 = require("playwright");
|
|
5
|
+
async function launchBrowser(choice = {}) {
|
|
6
|
+
const execPath = choice.browserPath || process.env.IAQA_BROWSER_PATH;
|
|
7
|
+
const channel = choice.browserChannel || process.env.IAQA_BROWSER_CHANNEL;
|
|
8
|
+
if (execPath) {
|
|
9
|
+
const browser = await playwright_1.chromium.launch({ headless: true, executablePath: execPath });
|
|
10
|
+
return { browser, using: `browser at ${execPath}` };
|
|
11
|
+
}
|
|
12
|
+
if (channel) {
|
|
13
|
+
const browser = await playwright_1.chromium.launch({ headless: true, channel });
|
|
14
|
+
return { browser, using: `system browser (channel "${channel}")` };
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const browser = await playwright_1.chromium.launch({ headless: true });
|
|
18
|
+
return { browser, using: 'bundled Chromium (shared Playwright cache)' };
|
|
19
|
+
}
|
|
20
|
+
catch (bundledError) {
|
|
21
|
+
for (const fallback of ['chrome', 'msedge']) {
|
|
22
|
+
try {
|
|
23
|
+
const browser = await playwright_1.chromium.launch({ headless: true, channel: fallback });
|
|
24
|
+
return { browser, using: `system browser (channel "${fallback}") — bundled Chromium not installed` };
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
/* try the next one */
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const reason = bundledError instanceof Error ? bundledError.message.split('\n')[0] : String(bundledError);
|
|
31
|
+
throw new Error(`No usable browser found. ${reason}\n` +
|
|
32
|
+
'Fix it with any one of these:\n' +
|
|
33
|
+
' • install the bundled browser: npx playwright install chromium\n' +
|
|
34
|
+
' • reuse an installed browser: set IAQA_BROWSER_CHANNEL=chrome (or msedge)\n' +
|
|
35
|
+
' • point at an executable: set IAQA_BROWSER_PATH=/path/to/chrome');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=launcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../src/launcher.ts"],"names":[],"mappings":";;AAyCA,sCAmCC;AA5ED,2CAA+C;AAyCxC,KAAK,UAAU,aAAa,CAAC,SAAwB,EAAE;IAC5D,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACrE,MAAM,OAAO,GAAG,MAAM,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAE1E,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,MAAM,qBAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,CAAC;QACpF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,QAAQ,EAAE,EAAE,CAAC;IACtD,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,OAAO,GAAG,MAAM,qBAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACnE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,4BAA4B,OAAO,IAAI,EAAE,CAAC;IACrE,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,qBAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,4CAA4C,EAAE,CAAC;IAC1E,CAAC;IAAC,OAAO,YAAY,EAAE,CAAC;QACtB,KAAK,MAAM,QAAQ,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC5C,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,qBAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAC7E,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,4BAA4B,QAAQ,qCAAqC,EAAE,CAAC;YACvG,CAAC;YAAC,MAAM,CAAC;gBACP,sBAAsB;YACxB,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,YAAY,YAAY,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC1G,MAAM,IAAI,KAAK,CACb,4BAA4B,MAAM,IAAI;YACpC,iCAAiC;YACjC,qEAAqE;YACrE,iFAAiF;YACjF,yEAAyE,CAC5E,CAAC;IACJ,CAAC;AACH,CAAC"}
|