@democratize-quality/qualitylens-core 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.
Files changed (85) hide show
  1. package/dist/core/config.d.mts +26 -0
  2. package/dist/core/config.d.ts +26 -0
  3. package/dist/core/config.js +139 -0
  4. package/dist/core/config.js.map +1 -0
  5. package/dist/core/config.mjs +104 -0
  6. package/dist/core/config.mjs.map +1 -0
  7. package/dist/core/detector.d.mts +43 -0
  8. package/dist/core/detector.d.ts +43 -0
  9. package/dist/core/detector.js +431 -0
  10. package/dist/core/detector.js.map +1 -0
  11. package/dist/core/detector.mjs +395 -0
  12. package/dist/core/detector.mjs.map +1 -0
  13. package/dist/core/engine.d.mts +29 -0
  14. package/dist/core/engine.d.ts +29 -0
  15. package/dist/core/engine.js +151 -0
  16. package/dist/core/engine.js.map +1 -0
  17. package/dist/core/engine.mjs +126 -0
  18. package/dist/core/engine.mjs.map +1 -0
  19. package/dist/core/types.d.mts +109 -0
  20. package/dist/core/types.d.ts +109 -0
  21. package/dist/core/types.js +19 -0
  22. package/dist/core/types.js.map +1 -0
  23. package/dist/core/types.mjs +1 -0
  24. package/dist/core/types.mjs.map +1 -0
  25. package/dist/matchers/area.matcher.d.mts +38 -0
  26. package/dist/matchers/area.matcher.d.ts +38 -0
  27. package/dist/matchers/area.matcher.js +102 -0
  28. package/dist/matchers/area.matcher.js.map +1 -0
  29. package/dist/matchers/area.matcher.mjs +77 -0
  30. package/dist/matchers/area.matcher.mjs.map +1 -0
  31. package/dist/matchers/fuzzy.matcher.d.mts +83 -0
  32. package/dist/matchers/fuzzy.matcher.d.ts +83 -0
  33. package/dist/matchers/fuzzy.matcher.js +161 -0
  34. package/dist/matchers/fuzzy.matcher.js.map +1 -0
  35. package/dist/matchers/fuzzy.matcher.mjs +136 -0
  36. package/dist/matchers/fuzzy.matcher.mjs.map +1 -0
  37. package/dist/reporters/base.reporter.d.mts +19 -0
  38. package/dist/reporters/base.reporter.d.ts +19 -0
  39. package/dist/reporters/base.reporter.js +32 -0
  40. package/dist/reporters/base.reporter.js.map +1 -0
  41. package/dist/reporters/base.reporter.mjs +7 -0
  42. package/dist/reporters/base.reporter.mjs.map +1 -0
  43. package/dist/reporters/console.reporter.d.mts +16 -0
  44. package/dist/reporters/console.reporter.d.ts +16 -0
  45. package/dist/reporters/console.reporter.js +130 -0
  46. package/dist/reporters/console.reporter.js.map +1 -0
  47. package/dist/reporters/console.reporter.mjs +95 -0
  48. package/dist/reporters/console.reporter.mjs.map +1 -0
  49. package/dist/sources/base.source.d.mts +22 -0
  50. package/dist/sources/base.source.d.ts +22 -0
  51. package/dist/sources/base.source.js +130 -0
  52. package/dist/sources/base.source.js.map +1 -0
  53. package/dist/sources/base.source.mjs +93 -0
  54. package/dist/sources/base.source.mjs.map +1 -0
  55. package/dist/sources/playwright.source.d.mts +34 -0
  56. package/dist/sources/playwright.source.d.ts +34 -0
  57. package/dist/sources/playwright.source.js +209 -0
  58. package/dist/sources/playwright.source.js.map +1 -0
  59. package/dist/sources/playwright.source.mjs +172 -0
  60. package/dist/sources/playwright.source.mjs.map +1 -0
  61. package/dist/sources/routes.source.d.mts +58 -0
  62. package/dist/sources/routes.source.d.ts +58 -0
  63. package/dist/sources/routes.source.js +288 -0
  64. package/dist/sources/routes.source.js.map +1 -0
  65. package/dist/sources/routes.source.mjs +251 -0
  66. package/dist/sources/routes.source.mjs.map +1 -0
  67. package/dist/sources/yaml.source.d.mts +16 -0
  68. package/dist/sources/yaml.source.d.ts +16 -0
  69. package/dist/sources/yaml.source.js +160 -0
  70. package/dist/sources/yaml.source.js.map +1 -0
  71. package/dist/sources/yaml.source.mjs +123 -0
  72. package/dist/sources/yaml.source.mjs.map +1 -0
  73. package/dist/utils/http.d.mts +7 -0
  74. package/dist/utils/http.d.ts +7 -0
  75. package/dist/utils/http.js +37 -0
  76. package/dist/utils/http.js.map +1 -0
  77. package/dist/utils/http.mjs +12 -0
  78. package/dist/utils/http.mjs.map +1 -0
  79. package/dist/utils/logger.d.mts +35 -0
  80. package/dist/utils/logger.d.ts +35 -0
  81. package/dist/utils/logger.js +114 -0
  82. package/dist/utils/logger.js.map +1 -0
  83. package/dist/utils/logger.mjs +79 -0
  84. package/dist/utils/logger.mjs.map +1 -0
  85. package/package.json +115 -0
@@ -0,0 +1,26 @@
1
+ import { TestGapConfig } from './types.mjs';
2
+
3
+ /**
4
+ * src/core/config.ts
5
+ * Loads and validates qualitylens.yaml. Generates starter config for `qualitylens init`.
6
+ */
7
+
8
+ interface StarterOptions {
9
+ projectName: string;
10
+ routeType: 'nextjs' | 'openapi' | 'express' | 'manual';
11
+ routesPath: string;
12
+ generate?: string | null;
13
+ omitGenerate?: boolean;
14
+ basePath?: string | null;
15
+ testsPath?: string | null;
16
+ areas: Array<{
17
+ name: string;
18
+ patterns: string[];
19
+ }>;
20
+ }
21
+ declare class Config {
22
+ static load(filePath: string): TestGapConfig;
23
+ static generateStarter(opts: StarterOptions): string;
24
+ }
25
+
26
+ export { Config, type StarterOptions };
@@ -0,0 +1,26 @@
1
+ import { TestGapConfig } from './types.js';
2
+
3
+ /**
4
+ * src/core/config.ts
5
+ * Loads and validates qualitylens.yaml. Generates starter config for `qualitylens init`.
6
+ */
7
+
8
+ interface StarterOptions {
9
+ projectName: string;
10
+ routeType: 'nextjs' | 'openapi' | 'express' | 'manual';
11
+ routesPath: string;
12
+ generate?: string | null;
13
+ omitGenerate?: boolean;
14
+ basePath?: string | null;
15
+ testsPath?: string | null;
16
+ areas: Array<{
17
+ name: string;
18
+ patterns: string[];
19
+ }>;
20
+ }
21
+ declare class Config {
22
+ static load(filePath: string): TestGapConfig;
23
+ static generateStarter(opts: StarterOptions): string;
24
+ }
25
+
26
+ export { Config, type StarterOptions };
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/core/config.ts
31
+ var config_exports = {};
32
+ __export(config_exports, {
33
+ Config: () => Config
34
+ });
35
+ module.exports = __toCommonJS(config_exports);
36
+ var fs = __toESM(require("fs"));
37
+ var yaml = __toESM(require("js-yaml"));
38
+ var Config = class {
39
+ static load(filePath) {
40
+ if (!fs.existsSync(filePath)) {
41
+ throw new Error(
42
+ `qualitylens.yaml not found at ${filePath}
43
+ Run 'qualitylens init' to create one.`
44
+ );
45
+ }
46
+ const raw = yaml.load(fs.readFileSync(filePath, "utf-8"));
47
+ if (!raw.projectName) throw new Error("qualitylens.yaml: projectName is required");
48
+ if (!raw.routes) throw new Error("qualitylens.yaml: routes is required");
49
+ if (!raw.routes.type) throw new Error("qualitylens.yaml: routes.type is required");
50
+ if (!raw.routes.path) throw new Error("qualitylens.yaml: routes.path is required");
51
+ if (!raw.areas || raw.areas.length === 0) {
52
+ throw new Error("qualitylens.yaml: at least one area is required");
53
+ }
54
+ return {
55
+ ...raw,
56
+ projectName: raw.projectName,
57
+ routes: raw.routes,
58
+ areas: raw.areas,
59
+ staleThresholdDays: raw.staleThresholdDays ?? 30,
60
+ manualCoverage: raw.manualCoverage ?? [],
61
+ thresholds: raw.thresholds ?? [],
62
+ testHints: raw.testHints ?? []
63
+ };
64
+ }
65
+ static generateStarter(opts) {
66
+ const areasYaml = opts.areas.length > 0 ? opts.areas.map((a) => [
67
+ ` - name: ${a.name}`,
68
+ ` patterns:`,
69
+ ...a.patterns.map((p) => ` - ${p}`)
70
+ ].join("\n")).join("\n\n") : [
71
+ ` - name: API`,
72
+ ` patterns:`,
73
+ ` - /api`
74
+ ].join("\n");
75
+ const generateLine = opts.omitGenerate ? null : opts.generate ? ` generate: ${opts.generate} # runs before route discovery to produce openapi.json` : ` # generate: npm run generate:openapi # uncomment if your spec is generated at build time`;
76
+ const basePathLine = opts.basePath ? ` basePath: ${opts.basePath} # prefix stripped when grouping routes into areas` : ` # basePath: /api # uncomment and set if your routes share a common prefix (e.g. /api, /api/v1)`;
77
+ const testsSection = opts.testsPath ? `
78
+ tests:
79
+ path: ${opts.testsPath} # directory scanned for *.spec.ts / *.test.ts
80
+ ` : `
81
+ # tests:
82
+ # path: ./e2e # uncomment to specify a test directory (default: same folder as this file)
83
+ `;
84
+ return `# qualitylens.yaml \u2014 generated by 'qualitylens init'
85
+ # Run 'qualitylens scan' to generate your first coverage report.
86
+
87
+ projectName: ${opts.projectName}
88
+
89
+ # Days before a manual test entry is flagged as stale
90
+ staleThresholdDays: 30
91
+
92
+ routes:
93
+ type: ${opts.routeType}
94
+ path: ${opts.routesPath}
95
+ ${generateLine != null ? generateLine + "\n" : ""}${basePathLine}
96
+ ${testsSection}
97
+ areas:
98
+ ${areasYaml}
99
+
100
+ # Optional: Azure DevOps Test Plans integration
101
+ # ado:
102
+ # orgUrl: https://dev.azure.com/your-org
103
+ # project: YourProject
104
+ # planId: 42
105
+ # patEnvVar: ADO_PAT
106
+
107
+ # Optional: manual coverage entries
108
+ # manualCoverage:
109
+ # - route: /your-route
110
+ # lastTested: 2026-01-01
111
+ # tester: your-name
112
+
113
+ # Optional: CI gates
114
+ # thresholds:
115
+ # - area: "*"
116
+ # minCoverage: 50
117
+
118
+ # Optional: test hints (Pro) \u2014 resolve orphaned tests, flag route gaps, acknowledge stale tests
119
+ # Run 'qualitylens hint add' or 'qualitylens orphans' to manage this section
120
+ # testHints:
121
+ # - test: "Sign out after registration"
122
+ # status: resolved # resolved | gap | acknowledged | ignore
123
+ # route: /login
124
+ # reason: Sign out is part of the authentication flow
125
+ # confidence: high
126
+ # resolvedBy: agent
127
+ # - test: "DELETE /users/:id returns 404"
128
+ # status: gap
129
+ # route: /users/:id
130
+ # method: DELETE
131
+ # reason: Route not in OpenAPI spec \u2014 missing implementation or undocumented
132
+ `;
133
+ }
134
+ };
135
+ // Annotate the CommonJS export names for ESM import in node:
136
+ 0 && (module.exports = {
137
+ Config
138
+ });
139
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/config.ts"],"sourcesContent":["/**\n * src/core/config.ts\n * Loads and validates qualitylens.yaml. Generates starter config for `qualitylens init`.\n */\n\nimport * as fs from 'fs'\nimport * as yaml from 'js-yaml'\nimport { TestGapConfig } from './types'\n\nexport interface StarterOptions {\n projectName: string\n routeType: 'nextjs' | 'openapi' | 'express' | 'manual'\n routesPath: string\n generate?: string | null\n omitGenerate?: boolean // true = spec file already exists, skip generate line entirely\n basePath?: string | null\n testsPath?: string | null\n areas: Array<{ name: string; patterns: string[] }>\n}\n\nexport class Config {\n static load(filePath: string): TestGapConfig {\n if (!fs.existsSync(filePath)) {\n throw new Error(\n `qualitylens.yaml not found at ${filePath}\\n` +\n `Run 'qualitylens init' to create one.`\n )\n }\n\n const raw = yaml.load(fs.readFileSync(filePath, 'utf-8')) as Partial<TestGapConfig>\n\n // Validate required fields\n if (!raw.projectName) throw new Error('qualitylens.yaml: projectName is required')\n if (!raw.routes) throw new Error('qualitylens.yaml: routes is required')\n if (!raw.routes.type) throw new Error('qualitylens.yaml: routes.type is required')\n if (!raw.routes.path) throw new Error('qualitylens.yaml: routes.path is required')\n if (!raw.areas || raw.areas.length === 0) {\n throw new Error('qualitylens.yaml: at least one area is required')\n }\n\n // Apply defaults\n return {\n ...raw,\n projectName: raw.projectName,\n routes: raw.routes,\n areas: raw.areas,\n staleThresholdDays: raw.staleThresholdDays ?? 30,\n manualCoverage: raw.manualCoverage ?? [],\n thresholds: raw.thresholds ?? [],\n testHints: raw.testHints ?? [],\n }\n }\n\n static generateStarter(opts: StarterOptions): string {\n const areasYaml = opts.areas.length > 0\n ? opts.areas.map(a => [\n ` - name: ${a.name}`,\n ` patterns:`,\n ...a.patterns.map(p => ` - ${p}`),\n ].join('\\n')).join('\\n\\n')\n : [\n ` - name: API`,\n ` patterns:`,\n ` - /api`,\n ].join('\\n')\n\n const generateLine = opts.omitGenerate\n ? null\n : opts.generate\n ? ` generate: ${opts.generate} # runs before route discovery to produce openapi.json`\n : ` # generate: npm run generate:openapi # uncomment if your spec is generated at build time`\n\n const basePathLine = opts.basePath\n ? ` basePath: ${opts.basePath} # prefix stripped when grouping routes into areas`\n : ` # basePath: /api # uncomment and set if your routes share a common prefix (e.g. /api, /api/v1)`\n\n const testsSection = opts.testsPath\n ? `\\ntests:\\n path: ${opts.testsPath} # directory scanned for *.spec.ts / *.test.ts\\n`\n : `\\n# tests:\\n# path: ./e2e # uncomment to specify a test directory (default: same folder as this file)\\n`\n\n return `# qualitylens.yaml — generated by 'qualitylens init'\n# Run 'qualitylens scan' to generate your first coverage report.\n\nprojectName: ${opts.projectName}\n\n# Days before a manual test entry is flagged as stale\nstaleThresholdDays: 30\n\nroutes:\n type: ${opts.routeType}\n path: ${opts.routesPath}\n${generateLine != null ? generateLine + '\\n' : ''}${basePathLine}\n${testsSection}\nareas:\n${areasYaml}\n\n# Optional: Azure DevOps Test Plans integration\n# ado:\n# orgUrl: https://dev.azure.com/your-org\n# project: YourProject\n# planId: 42\n# patEnvVar: ADO_PAT\n\n# Optional: manual coverage entries\n# manualCoverage:\n# - route: /your-route\n# lastTested: 2026-01-01\n# tester: your-name\n\n# Optional: CI gates\n# thresholds:\n# - area: \"*\"\n# minCoverage: 50\n\n# Optional: test hints (Pro) — resolve orphaned tests, flag route gaps, acknowledge stale tests\n# Run 'qualitylens hint add' or 'qualitylens orphans' to manage this section\n# testHints:\n# - test: \"Sign out after registration\"\n# status: resolved # resolved | gap | acknowledged | ignore\n# route: /login\n# reason: Sign out is part of the authentication flow\n# confidence: high\n# resolvedBy: agent\n# - test: \"DELETE /users/:id returns 404\"\n# status: gap\n# route: /users/:id\n# method: DELETE\n# reason: Route not in OpenAPI spec — missing implementation or undocumented\n`\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,SAAoB;AACpB,WAAsB;AAcf,IAAM,SAAN,MAAa;AAAA,EAClB,OAAO,KAAK,UAAiC;AAC3C,QAAI,CAAI,cAAW,QAAQ,GAAG;AAC5B,YAAM,IAAI;AAAA,QACR,iCAAiC,QAAQ;AAAA;AAAA,MAE3C;AAAA,IACF;AAEA,UAAM,MAAW,UAAQ,gBAAa,UAAU,OAAO,CAAC;AAGxD,QAAI,CAAC,IAAI,YAAa,OAAM,IAAI,MAAM,2CAA2C;AACjF,QAAI,CAAC,IAAI,OAAQ,OAAM,IAAI,MAAM,sCAAsC;AACvE,QAAI,CAAC,IAAI,OAAO,KAAM,OAAM,IAAI,MAAM,2CAA2C;AACjF,QAAI,CAAC,IAAI,OAAO,KAAM,OAAM,IAAI,MAAM,2CAA2C;AACjF,QAAI,CAAC,IAAI,SAAS,IAAI,MAAM,WAAW,GAAG;AACxC,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AAGA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,aAAa,IAAI;AAAA,MACjB,QAAQ,IAAI;AAAA,MACZ,OAAO,IAAI;AAAA,MACX,oBAAoB,IAAI,sBAAsB;AAAA,MAC9C,gBAAgB,IAAI,kBAAkB,CAAC;AAAA,MACvC,YAAY,IAAI,cAAc,CAAC;AAAA,MAC/B,WAAW,IAAI,aAAa,CAAC;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,OAAO,gBAAgB,MAA8B;AACnD,UAAM,YAAY,KAAK,MAAM,SAAS,IAClC,KAAK,MAAM,IAAI,OAAK;AAAA,MAClB,aAAa,EAAE,IAAI;AAAA,MACnB;AAAA,MACA,GAAG,EAAE,SAAS,IAAI,OAAK,WAAW,CAAC,EAAE;AAAA,IACvC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,MAAM,IACzB;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAEf,UAAM,eAAe,KAAK,eACtB,OACA,KAAK,WACH,eAAe,KAAK,QAAQ,6DAC5B;AAEN,UAAM,eAAe,KAAK,WACtB,eAAe,KAAK,QAAQ,yDAC5B;AAEJ,UAAM,eAAe,KAAK,YACtB;AAAA;AAAA,UAAqB,KAAK,SAAS;AAAA,IACnC;AAAA;AAAA;AAAA;AAEJ,WAAO;AAAA;AAAA;AAAA,eAGI,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMrB,KAAK,SAAS;AAAA,UACd,KAAK,UAAU;AAAA,EACvB,gBAAgB,OAAO,eAAe,OAAO,EAAE,GAAG,YAAY;AAAA,EAC9D,YAAY;AAAA;AAAA,EAEZ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCT;AACF;","names":[]}
@@ -0,0 +1,104 @@
1
+ // src/core/config.ts
2
+ import * as fs from "fs";
3
+ import * as yaml from "js-yaml";
4
+ var Config = class {
5
+ static load(filePath) {
6
+ if (!fs.existsSync(filePath)) {
7
+ throw new Error(
8
+ `qualitylens.yaml not found at ${filePath}
9
+ Run 'qualitylens init' to create one.`
10
+ );
11
+ }
12
+ const raw = yaml.load(fs.readFileSync(filePath, "utf-8"));
13
+ if (!raw.projectName) throw new Error("qualitylens.yaml: projectName is required");
14
+ if (!raw.routes) throw new Error("qualitylens.yaml: routes is required");
15
+ if (!raw.routes.type) throw new Error("qualitylens.yaml: routes.type is required");
16
+ if (!raw.routes.path) throw new Error("qualitylens.yaml: routes.path is required");
17
+ if (!raw.areas || raw.areas.length === 0) {
18
+ throw new Error("qualitylens.yaml: at least one area is required");
19
+ }
20
+ return {
21
+ ...raw,
22
+ projectName: raw.projectName,
23
+ routes: raw.routes,
24
+ areas: raw.areas,
25
+ staleThresholdDays: raw.staleThresholdDays ?? 30,
26
+ manualCoverage: raw.manualCoverage ?? [],
27
+ thresholds: raw.thresholds ?? [],
28
+ testHints: raw.testHints ?? []
29
+ };
30
+ }
31
+ static generateStarter(opts) {
32
+ const areasYaml = opts.areas.length > 0 ? opts.areas.map((a) => [
33
+ ` - name: ${a.name}`,
34
+ ` patterns:`,
35
+ ...a.patterns.map((p) => ` - ${p}`)
36
+ ].join("\n")).join("\n\n") : [
37
+ ` - name: API`,
38
+ ` patterns:`,
39
+ ` - /api`
40
+ ].join("\n");
41
+ const generateLine = opts.omitGenerate ? null : opts.generate ? ` generate: ${opts.generate} # runs before route discovery to produce openapi.json` : ` # generate: npm run generate:openapi # uncomment if your spec is generated at build time`;
42
+ const basePathLine = opts.basePath ? ` basePath: ${opts.basePath} # prefix stripped when grouping routes into areas` : ` # basePath: /api # uncomment and set if your routes share a common prefix (e.g. /api, /api/v1)`;
43
+ const testsSection = opts.testsPath ? `
44
+ tests:
45
+ path: ${opts.testsPath} # directory scanned for *.spec.ts / *.test.ts
46
+ ` : `
47
+ # tests:
48
+ # path: ./e2e # uncomment to specify a test directory (default: same folder as this file)
49
+ `;
50
+ return `# qualitylens.yaml \u2014 generated by 'qualitylens init'
51
+ # Run 'qualitylens scan' to generate your first coverage report.
52
+
53
+ projectName: ${opts.projectName}
54
+
55
+ # Days before a manual test entry is flagged as stale
56
+ staleThresholdDays: 30
57
+
58
+ routes:
59
+ type: ${opts.routeType}
60
+ path: ${opts.routesPath}
61
+ ${generateLine != null ? generateLine + "\n" : ""}${basePathLine}
62
+ ${testsSection}
63
+ areas:
64
+ ${areasYaml}
65
+
66
+ # Optional: Azure DevOps Test Plans integration
67
+ # ado:
68
+ # orgUrl: https://dev.azure.com/your-org
69
+ # project: YourProject
70
+ # planId: 42
71
+ # patEnvVar: ADO_PAT
72
+
73
+ # Optional: manual coverage entries
74
+ # manualCoverage:
75
+ # - route: /your-route
76
+ # lastTested: 2026-01-01
77
+ # tester: your-name
78
+
79
+ # Optional: CI gates
80
+ # thresholds:
81
+ # - area: "*"
82
+ # minCoverage: 50
83
+
84
+ # Optional: test hints (Pro) \u2014 resolve orphaned tests, flag route gaps, acknowledge stale tests
85
+ # Run 'qualitylens hint add' or 'qualitylens orphans' to manage this section
86
+ # testHints:
87
+ # - test: "Sign out after registration"
88
+ # status: resolved # resolved | gap | acknowledged | ignore
89
+ # route: /login
90
+ # reason: Sign out is part of the authentication flow
91
+ # confidence: high
92
+ # resolvedBy: agent
93
+ # - test: "DELETE /users/:id returns 404"
94
+ # status: gap
95
+ # route: /users/:id
96
+ # method: DELETE
97
+ # reason: Route not in OpenAPI spec \u2014 missing implementation or undocumented
98
+ `;
99
+ }
100
+ };
101
+ export {
102
+ Config
103
+ };
104
+ //# sourceMappingURL=config.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/config.ts"],"sourcesContent":["/**\n * src/core/config.ts\n * Loads and validates qualitylens.yaml. Generates starter config for `qualitylens init`.\n */\n\nimport * as fs from 'fs'\nimport * as yaml from 'js-yaml'\nimport { TestGapConfig } from './types'\n\nexport interface StarterOptions {\n projectName: string\n routeType: 'nextjs' | 'openapi' | 'express' | 'manual'\n routesPath: string\n generate?: string | null\n omitGenerate?: boolean // true = spec file already exists, skip generate line entirely\n basePath?: string | null\n testsPath?: string | null\n areas: Array<{ name: string; patterns: string[] }>\n}\n\nexport class Config {\n static load(filePath: string): TestGapConfig {\n if (!fs.existsSync(filePath)) {\n throw new Error(\n `qualitylens.yaml not found at ${filePath}\\n` +\n `Run 'qualitylens init' to create one.`\n )\n }\n\n const raw = yaml.load(fs.readFileSync(filePath, 'utf-8')) as Partial<TestGapConfig>\n\n // Validate required fields\n if (!raw.projectName) throw new Error('qualitylens.yaml: projectName is required')\n if (!raw.routes) throw new Error('qualitylens.yaml: routes is required')\n if (!raw.routes.type) throw new Error('qualitylens.yaml: routes.type is required')\n if (!raw.routes.path) throw new Error('qualitylens.yaml: routes.path is required')\n if (!raw.areas || raw.areas.length === 0) {\n throw new Error('qualitylens.yaml: at least one area is required')\n }\n\n // Apply defaults\n return {\n ...raw,\n projectName: raw.projectName,\n routes: raw.routes,\n areas: raw.areas,\n staleThresholdDays: raw.staleThresholdDays ?? 30,\n manualCoverage: raw.manualCoverage ?? [],\n thresholds: raw.thresholds ?? [],\n testHints: raw.testHints ?? [],\n }\n }\n\n static generateStarter(opts: StarterOptions): string {\n const areasYaml = opts.areas.length > 0\n ? opts.areas.map(a => [\n ` - name: ${a.name}`,\n ` patterns:`,\n ...a.patterns.map(p => ` - ${p}`),\n ].join('\\n')).join('\\n\\n')\n : [\n ` - name: API`,\n ` patterns:`,\n ` - /api`,\n ].join('\\n')\n\n const generateLine = opts.omitGenerate\n ? null\n : opts.generate\n ? ` generate: ${opts.generate} # runs before route discovery to produce openapi.json`\n : ` # generate: npm run generate:openapi # uncomment if your spec is generated at build time`\n\n const basePathLine = opts.basePath\n ? ` basePath: ${opts.basePath} # prefix stripped when grouping routes into areas`\n : ` # basePath: /api # uncomment and set if your routes share a common prefix (e.g. /api, /api/v1)`\n\n const testsSection = opts.testsPath\n ? `\\ntests:\\n path: ${opts.testsPath} # directory scanned for *.spec.ts / *.test.ts\\n`\n : `\\n# tests:\\n# path: ./e2e # uncomment to specify a test directory (default: same folder as this file)\\n`\n\n return `# qualitylens.yaml — generated by 'qualitylens init'\n# Run 'qualitylens scan' to generate your first coverage report.\n\nprojectName: ${opts.projectName}\n\n# Days before a manual test entry is flagged as stale\nstaleThresholdDays: 30\n\nroutes:\n type: ${opts.routeType}\n path: ${opts.routesPath}\n${generateLine != null ? generateLine + '\\n' : ''}${basePathLine}\n${testsSection}\nareas:\n${areasYaml}\n\n# Optional: Azure DevOps Test Plans integration\n# ado:\n# orgUrl: https://dev.azure.com/your-org\n# project: YourProject\n# planId: 42\n# patEnvVar: ADO_PAT\n\n# Optional: manual coverage entries\n# manualCoverage:\n# - route: /your-route\n# lastTested: 2026-01-01\n# tester: your-name\n\n# Optional: CI gates\n# thresholds:\n# - area: \"*\"\n# minCoverage: 50\n\n# Optional: test hints (Pro) — resolve orphaned tests, flag route gaps, acknowledge stale tests\n# Run 'qualitylens hint add' or 'qualitylens orphans' to manage this section\n# testHints:\n# - test: \"Sign out after registration\"\n# status: resolved # resolved | gap | acknowledged | ignore\n# route: /login\n# reason: Sign out is part of the authentication flow\n# confidence: high\n# resolvedBy: agent\n# - test: \"DELETE /users/:id returns 404\"\n# status: gap\n# route: /users/:id\n# method: DELETE\n# reason: Route not in OpenAPI spec — missing implementation or undocumented\n`\n }\n}\n"],"mappings":";AAKA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAcf,IAAM,SAAN,MAAa;AAAA,EAClB,OAAO,KAAK,UAAiC;AAC3C,QAAI,CAAI,cAAW,QAAQ,GAAG;AAC5B,YAAM,IAAI;AAAA,QACR,iCAAiC,QAAQ;AAAA;AAAA,MAE3C;AAAA,IACF;AAEA,UAAM,MAAW,UAAQ,gBAAa,UAAU,OAAO,CAAC;AAGxD,QAAI,CAAC,IAAI,YAAa,OAAM,IAAI,MAAM,2CAA2C;AACjF,QAAI,CAAC,IAAI,OAAQ,OAAM,IAAI,MAAM,sCAAsC;AACvE,QAAI,CAAC,IAAI,OAAO,KAAM,OAAM,IAAI,MAAM,2CAA2C;AACjF,QAAI,CAAC,IAAI,OAAO,KAAM,OAAM,IAAI,MAAM,2CAA2C;AACjF,QAAI,CAAC,IAAI,SAAS,IAAI,MAAM,WAAW,GAAG;AACxC,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AAGA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,aAAa,IAAI;AAAA,MACjB,QAAQ,IAAI;AAAA,MACZ,OAAO,IAAI;AAAA,MACX,oBAAoB,IAAI,sBAAsB;AAAA,MAC9C,gBAAgB,IAAI,kBAAkB,CAAC;AAAA,MACvC,YAAY,IAAI,cAAc,CAAC;AAAA,MAC/B,WAAW,IAAI,aAAa,CAAC;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,OAAO,gBAAgB,MAA8B;AACnD,UAAM,YAAY,KAAK,MAAM,SAAS,IAClC,KAAK,MAAM,IAAI,OAAK;AAAA,MAClB,aAAa,EAAE,IAAI;AAAA,MACnB;AAAA,MACA,GAAG,EAAE,SAAS,IAAI,OAAK,WAAW,CAAC,EAAE;AAAA,IACvC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,MAAM,IACzB;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAEf,UAAM,eAAe,KAAK,eACtB,OACA,KAAK,WACH,eAAe,KAAK,QAAQ,6DAC5B;AAEN,UAAM,eAAe,KAAK,WACtB,eAAe,KAAK,QAAQ,yDAC5B;AAEJ,UAAM,eAAe,KAAK,YACtB;AAAA;AAAA,UAAqB,KAAK,SAAS;AAAA,IACnC;AAAA;AAAA;AAAA;AAEJ,WAAO;AAAA;AAAA;AAAA,eAGI,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMrB,KAAK,SAAS;AAAA,UACd,KAAK,UAAU;AAAA,EACvB,gBAAgB,OAAO,eAAe,OAAO,EAAE,GAAG,YAAY;AAAA,EAC9D,YAAY;AAAA;AAAA,EAEZ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCT;AACF;","names":[]}
@@ -0,0 +1,43 @@
1
+ import { AppRoute } from './types.mjs';
2
+
3
+ /**
4
+ * src/core/detector.ts
5
+ *
6
+ * Auto-detection logic for `qualitylens init`.
7
+ * Inspects a target directory and returns everything it can infer:
8
+ * framework, route type, routes path, discovered routes, and suggested areas.
9
+ *
10
+ * Nothing here writes files or prompts the user — it only observes.
11
+ */
12
+
13
+ type Framework = 'nextjs' | 'express-openapi' | 'express' | 'unknown';
14
+ type RouteType = 'nextjs' | 'openapi' | 'express' | 'manual';
15
+ interface SuggestedArea {
16
+ suggestedName: string;
17
+ routes: AppRoute[];
18
+ patterns: string[];
19
+ }
20
+ interface DetectionResult {
21
+ projectName: string;
22
+ framework: Framework;
23
+ routeType: RouteType;
24
+ routesPath: string | null;
25
+ testsPath: string | null;
26
+ generate: string | null;
27
+ basePath: string | null;
28
+ hasSwaggerJsdoc: boolean;
29
+ hasPlaywright: boolean;
30
+ discoveredRoutes: AppRoute[];
31
+ suggestedAreas: SuggestedArea[];
32
+ }
33
+ declare function detect(targetDir: string): Promise<DetectionResult>;
34
+ /**
35
+ * Groups routes by their first meaningful path segment and maps each group
36
+ * to a human-readable area name.
37
+ * basePath (e.g. /api, /api/v1) is stripped per-route before grouping so routes
38
+ * are organised by service name rather than the shared prefix.
39
+ * Exported so the init command can re-run it after the user confirms/changes basePath.
40
+ */
41
+ declare function suggestAreas(routes: AppRoute[], basePath: string | null): SuggestedArea[];
42
+
43
+ export { type DetectionResult, type Framework, type RouteType, type SuggestedArea, detect, suggestAreas };
@@ -0,0 +1,43 @@
1
+ import { AppRoute } from './types.js';
2
+
3
+ /**
4
+ * src/core/detector.ts
5
+ *
6
+ * Auto-detection logic for `qualitylens init`.
7
+ * Inspects a target directory and returns everything it can infer:
8
+ * framework, route type, routes path, discovered routes, and suggested areas.
9
+ *
10
+ * Nothing here writes files or prompts the user — it only observes.
11
+ */
12
+
13
+ type Framework = 'nextjs' | 'express-openapi' | 'express' | 'unknown';
14
+ type RouteType = 'nextjs' | 'openapi' | 'express' | 'manual';
15
+ interface SuggestedArea {
16
+ suggestedName: string;
17
+ routes: AppRoute[];
18
+ patterns: string[];
19
+ }
20
+ interface DetectionResult {
21
+ projectName: string;
22
+ framework: Framework;
23
+ routeType: RouteType;
24
+ routesPath: string | null;
25
+ testsPath: string | null;
26
+ generate: string | null;
27
+ basePath: string | null;
28
+ hasSwaggerJsdoc: boolean;
29
+ hasPlaywright: boolean;
30
+ discoveredRoutes: AppRoute[];
31
+ suggestedAreas: SuggestedArea[];
32
+ }
33
+ declare function detect(targetDir: string): Promise<DetectionResult>;
34
+ /**
35
+ * Groups routes by their first meaningful path segment and maps each group
36
+ * to a human-readable area name.
37
+ * basePath (e.g. /api, /api/v1) is stripped per-route before grouping so routes
38
+ * are organised by service name rather than the shared prefix.
39
+ * Exported so the init command can re-run it after the user confirms/changes basePath.
40
+ */
41
+ declare function suggestAreas(routes: AppRoute[], basePath: string | null): SuggestedArea[];
42
+
43
+ export { type DetectionResult, type Framework, type RouteType, type SuggestedArea, detect, suggestAreas };