@qa-ai-stlc/core 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +13 -0
  2. package/dist/auth-session-store.d.ts +15 -0
  3. package/dist/auth-session-store.d.ts.map +1 -0
  4. package/dist/auth-session-store.js +34 -0
  5. package/dist/auth-session-store.js.map +1 -0
  6. package/dist/auth.d.ts +28 -0
  7. package/dist/auth.d.ts.map +1 -0
  8. package/dist/auth.js +77 -0
  9. package/dist/auth.js.map +1 -0
  10. package/dist/browser-doctor.d.ts +29 -0
  11. package/dist/browser-doctor.d.ts.map +1 -0
  12. package/dist/browser-doctor.js +130 -0
  13. package/dist/browser-doctor.js.map +1 -0
  14. package/dist/config-loader.d.ts +9 -0
  15. package/dist/config-loader.d.ts.map +1 -0
  16. package/dist/config-loader.js +40 -0
  17. package/dist/config-loader.js.map +1 -0
  18. package/dist/errors.d.ts +15 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +18 -0
  21. package/dist/errors.js.map +1 -0
  22. package/dist/evidence-store.d.ts +43 -0
  23. package/dist/evidence-store.d.ts.map +1 -0
  24. package/dist/evidence-store.js +81 -0
  25. package/dist/evidence-store.js.map +1 -0
  26. package/dist/hash.d.ts +6 -0
  27. package/dist/hash.d.ts.map +1 -0
  28. package/dist/hash.js +19 -0
  29. package/dist/hash.js.map +1 -0
  30. package/dist/index.d.ts +20 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +22 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/json-file.d.ts +6 -0
  35. package/dist/json-file.d.ts.map +1 -0
  36. package/dist/json-file.js +29 -0
  37. package/dist/json-file.js.map +1 -0
  38. package/dist/manifest-store.d.ts +27 -0
  39. package/dist/manifest-store.d.ts.map +1 -0
  40. package/dist/manifest-store.js +65 -0
  41. package/dist/manifest-store.js.map +1 -0
  42. package/dist/paths.d.ts +15 -0
  43. package/dist/paths.d.ts.map +1 -0
  44. package/dist/paths.js +33 -0
  45. package/dist/paths.js.map +1 -0
  46. package/dist/ports/browser-launcher.d.ts +31 -0
  47. package/dist/ports/browser-launcher.d.ts.map +1 -0
  48. package/dist/ports/browser-launcher.js +8 -0
  49. package/dist/ports/browser-launcher.js.map +1 -0
  50. package/dist/ports/clock.d.ts +6 -0
  51. package/dist/ports/clock.d.ts.map +1 -0
  52. package/dist/ports/clock.js +6 -0
  53. package/dist/ports/clock.js.map +1 -0
  54. package/dist/ports/file-system.d.ts +13 -0
  55. package/dist/ports/file-system.d.ts.map +1 -0
  56. package/dist/ports/file-system.js +25 -0
  57. package/dist/ports/file-system.js.map +1 -0
  58. package/dist/ports/http-client.d.ts +13 -0
  59. package/dist/ports/http-client.d.ts.map +1 -0
  60. package/dist/ports/http-client.js +12 -0
  61. package/dist/ports/http-client.js.map +1 -0
  62. package/dist/ports/logger.d.ts +9 -0
  63. package/dist/ports/logger.d.ts.map +1 -0
  64. package/dist/ports/logger.js +9 -0
  65. package/dist/ports/logger.js.map +1 -0
  66. package/dist/ports/process-runner.d.ts +17 -0
  67. package/dist/ports/process-runner.d.ts.map +1 -0
  68. package/dist/ports/process-runner.js +24 -0
  69. package/dist/ports/process-runner.js.map +1 -0
  70. package/dist/qa-store.d.ts +33 -0
  71. package/dist/qa-store.d.ts.map +1 -0
  72. package/dist/qa-store.js +65 -0
  73. package/dist/qa-store.js.map +1 -0
  74. package/dist/redaction.d.ts +11 -0
  75. package/dist/redaction.d.ts.map +1 -0
  76. package/dist/redaction.js +53 -0
  77. package/dist/redaction.js.map +1 -0
  78. package/dist/secret-scan.d.ts +7 -0
  79. package/dist/secret-scan.d.ts.map +1 -0
  80. package/dist/secret-scan.js +21 -0
  81. package/dist/secret-scan.js.map +1 -0
  82. package/dist/test-support/fake-browser-launcher.d.ts +16 -0
  83. package/dist/test-support/fake-browser-launcher.d.ts.map +1 -0
  84. package/dist/test-support/fake-browser-launcher.js +64 -0
  85. package/dist/test-support/fake-browser-launcher.js.map +1 -0
  86. package/dist/test-support/fake-file-system.d.ts +12 -0
  87. package/dist/test-support/fake-file-system.d.ts.map +1 -0
  88. package/dist/test-support/fake-file-system.js +33 -0
  89. package/dist/test-support/fake-file-system.js.map +1 -0
  90. package/package.json +39 -0
package/dist/hash.js ADDED
@@ -0,0 +1,19 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { createHash } from 'node:crypto';
4
+ // A checkout on Windows can leave CRLF line endings for a file whose committed, hashed content
5
+ // used LF, which would otherwise make the same artifact hash differently by OS (AGENTS.md 5.6).
6
+ export function hashText(content) {
7
+ const normalized = content.replace(/\r\n/g, '\n');
8
+ return createHash('sha256').update(normalized, 'utf-8').digest('hex');
9
+ }
10
+ // Binary content (screenshots, traces, video) is hashed byte for byte: there is no line-ending
11
+ // convention to normalize.
12
+ export function hashBytes(content) {
13
+ return createHash('sha256').update(content).digest('hex');
14
+ }
15
+ /** Picks the text or binary hasher based on the content's runtime type. */
16
+ export function hashContent(content) {
17
+ return typeof content === 'string' ? hashText(content) : hashBytes(content);
18
+ }
19
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,+FAA+F;AAC/F,gGAAgG;AAChG,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACxE,CAAC;AAED,+FAA+F;AAC/F,2BAA2B;AAC3B,MAAM,UAAU,SAAS,CAAC,OAAmB;IAC3C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,OAA4B;IACtD,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,20 @@
1
+ export * from './errors.js';
2
+ export * from './hash.js';
3
+ export * from './json-file.js';
4
+ export * from './paths.js';
5
+ export * from './ports/clock.js';
6
+ export * from './ports/file-system.js';
7
+ export * from './ports/logger.js';
8
+ export * from './qa-store.js';
9
+ export * from './manifest-store.js';
10
+ export * from './config-loader.js';
11
+ export * from './ports/http-client.js';
12
+ export * from './ports/process-runner.js';
13
+ export * from './browser-doctor.js';
14
+ export * from './secret-scan.js';
15
+ export * from './redaction.js';
16
+ export * from './evidence-store.js';
17
+ export * from './ports/browser-launcher.js';
18
+ export * from './auth.js';
19
+ export * from './auth-session-store.js';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,WAAW,CAAC;AAC1B,cAAc,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export * from './errors.js';
4
+ export * from './hash.js';
5
+ export * from './json-file.js';
6
+ export * from './paths.js';
7
+ export * from './ports/clock.js';
8
+ export * from './ports/file-system.js';
9
+ export * from './ports/logger.js';
10
+ export * from './qa-store.js';
11
+ export * from './manifest-store.js';
12
+ export * from './config-loader.js';
13
+ export * from './ports/http-client.js';
14
+ export * from './ports/process-runner.js';
15
+ export * from './browser-doctor.js';
16
+ export * from './secret-scan.js';
17
+ export * from './redaction.js';
18
+ export * from './evidence-store.js';
19
+ export * from './ports/browser-launcher.js';
20
+ export * from './auth.js';
21
+ export * from './auth-session-store.js';
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,WAAW,CAAC;AAC1B,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { z } from 'zod';
2
+ import type { FileSystem } from './ports/file-system.js';
3
+ export declare function toCanonicalJson(value: unknown): string;
4
+ export declare function readJsonFile<Schema extends z.ZodType>(fs: FileSystem, absolutePath: string, schema: Schema): Promise<z.infer<Schema>>;
5
+ export declare function writeJsonFile(fs: FileSystem, absolutePath: string, value: unknown): Promise<void>;
6
+ //# sourceMappingURL=json-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-file.d.ts","sourceRoot":"","sources":["../src/json-file.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAKzD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD;AAED,wBAAsB,YAAY,CAAC,MAAM,SAAS,CAAC,CAAC,OAAO,EACzD,EAAE,EAAE,UAAU,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAe1B;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvG"}
@@ -0,0 +1,29 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { QaError } from './errors.js';
4
+ // Two spaces, a trailing newline, and no other formatting choice, so the same value serializes
5
+ // to byte-identical content on every OS and every run (AGENTS.md 12.6).
6
+ export function toCanonicalJson(value) {
7
+ return `${JSON.stringify(value, null, 2)}\n`;
8
+ }
9
+ export async function readJsonFile(fs, absolutePath, schema) {
10
+ const raw = await fs.readFile(absolutePath);
11
+ let parsed;
12
+ try {
13
+ parsed = JSON.parse(raw);
14
+ }
15
+ catch (error) {
16
+ throw new QaError('JSON_FILE_MALFORMED', `"${absolutePath}" is not valid JSON`, { cause: error });
17
+ }
18
+ const result = schema.safeParse(parsed);
19
+ if (!result.success) {
20
+ throw new QaError('JSON_FILE_INVALID', `"${absolutePath}" does not match its schema`, {
21
+ cause: result.error,
22
+ });
23
+ }
24
+ return result.data;
25
+ }
26
+ export async function writeJsonFile(fs, absolutePath, value) {
27
+ await fs.writeFile(absolutePath, toCanonicalJson(value));
28
+ }
29
+ //# sourceMappingURL=json-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-file.js","sourceRoot":"","sources":["../src/json-file.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAItC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,+FAA+F;AAC/F,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,EAAc,EACd,YAAoB,EACpB,MAAc;IAEd,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,OAAO,CAAC,qBAAqB,EAAE,IAAI,YAAY,qBAAqB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACpG,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,OAAO,CAAC,mBAAmB,EAAE,IAAI,YAAY,6BAA6B,EAAE;YACpF,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAc,EAAE,YAAoB,EAAE,KAAc;IACtF,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { type Manifest, type RelativePath } from '@qa-ai-stlc/schemas';
2
+ import { type Clock } from './ports/clock.js';
3
+ import type { QaStore } from './qa-store.js';
4
+ export interface ManifestStoreOptions {
5
+ readonly store: QaStore;
6
+ readonly clock?: Clock;
7
+ }
8
+ /**
9
+ * `.qa/manifest.json`: the hash of every artifact the engine has registered. `qa validate` and
10
+ * every MCP mutation reject a file that is unregistered or whose content no longer matches its
11
+ * recorded hash, so a direct filesystem write cannot pass for an engine-produced artifact
12
+ * (AGENTS.md 2.5, ADR-003).
13
+ */
14
+ export declare class ManifestStore {
15
+ private readonly store;
16
+ private readonly clock;
17
+ constructor(options: ManifestStoreOptions);
18
+ load(): Promise<Manifest>;
19
+ save(manifest: Manifest): Promise<void>;
20
+ /** Hashes `content` and records it as the current registered version of `relativePath`. */
21
+ register(relativePath: RelativePath, content: string | Uint8Array): Promise<void>;
22
+ /** Reports whether `content` still matches the hash registered for `relativePath`, if any. */
23
+ verify(relativePath: RelativePath, content: string | Uint8Array): Promise<boolean>;
24
+ /** Throws a coded `QaError` when `relativePath` is unregistered or its content was tampered with. */
25
+ assertRegistered(relativePath: RelativePath, content: string | Uint8Array): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=manifest-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-store.d.ts","sourceRoot":"","sources":["../src/manifest-store.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkC,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGvG,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7C,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;gBAElB,OAAO,EAAE,oBAAoB;IAKnC,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC;IAQzB,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,2FAA2F;IACrF,QAAQ,CAAC,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvF,8FAA8F;IACxF,MAAM,CAAC,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IAMxF,qGAAqG;IAC/F,gBAAgB,CAAC,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAchG"}
@@ -0,0 +1,65 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { ManifestSchema, SCHEMA_VERSION } from '@qa-ai-stlc/schemas';
4
+ import { QaError } from './errors.js';
5
+ import { hashContent } from './hash.js';
6
+ import { systemClock } from './ports/clock.js';
7
+ const MANIFEST_PATH = 'manifest.json';
8
+ /**
9
+ * `.qa/manifest.json`: the hash of every artifact the engine has registered. `qa validate` and
10
+ * every MCP mutation reject a file that is unregistered or whose content no longer matches its
11
+ * recorded hash, so a direct filesystem write cannot pass for an engine-produced artifact
12
+ * (AGENTS.md 2.5, ADR-003).
13
+ */
14
+ export class ManifestStore {
15
+ store;
16
+ clock;
17
+ constructor(options) {
18
+ this.store = options.store;
19
+ this.clock = options.clock ?? systemClock;
20
+ }
21
+ async load() {
22
+ const exists = await this.store.pathExists(MANIFEST_PATH);
23
+ if (!exists) {
24
+ return { schemaVersion: SCHEMA_VERSION, artifacts: {} };
25
+ }
26
+ return this.store.readJson(MANIFEST_PATH, ManifestSchema);
27
+ }
28
+ async save(manifest) {
29
+ await this.store.writeJson(MANIFEST_PATH, manifest);
30
+ }
31
+ /** Hashes `content` and records it as the current registered version of `relativePath`. */
32
+ async register(relativePath, content) {
33
+ const manifest = await this.load();
34
+ const nextManifest = {
35
+ ...manifest,
36
+ artifacts: {
37
+ ...manifest.artifacts,
38
+ [relativePath]: { sha256: hashContent(content), registeredAt: this.clock.now().toISOString() },
39
+ },
40
+ };
41
+ await this.save(nextManifest);
42
+ }
43
+ /** Reports whether `content` still matches the hash registered for `relativePath`, if any. */
44
+ async verify(relativePath, content) {
45
+ const manifest = await this.load();
46
+ const entry = manifest.artifacts[relativePath];
47
+ return entry?.sha256 === hashContent(content);
48
+ }
49
+ /** Throws a coded `QaError` when `relativePath` is unregistered or its content was tampered with. */
50
+ async assertRegistered(relativePath, content) {
51
+ const manifest = await this.load();
52
+ const entry = manifest.artifacts[relativePath];
53
+ if (entry === undefined) {
54
+ throw new QaError('ARTIFACT_UNREGISTERED', `"${relativePath}" is not registered in the manifest`, {
55
+ remediation: 'Register the artifact through the engine before referencing it.',
56
+ });
57
+ }
58
+ if (entry.sha256 !== hashContent(content)) {
59
+ throw new QaError('ARTIFACT_HASH_MISMATCH', `"${relativePath}" does not match its registered hash`, {
60
+ remediation: 'The file changed outside the engine; regenerate or re-register it through the engine.',
61
+ });
62
+ }
63
+ }
64
+ }
65
+ //# sourceMappingURL=manifest-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-store.js","sourceRoot":"","sources":["../src/manifest-store.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAoC,MAAM,qBAAqB,CAAC;AACvG,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAc,MAAM,kBAAkB,CAAC;AAG3D,MAAM,aAAa,GAAiB,eAAe,CAAC;AAOpD;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IACP,KAAK,CAAU;IACf,KAAK,CAAQ;IAE9B,YAAY,OAA6B;QACvC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,WAAW,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;QAC1D,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;QAC1D,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAkB;QAC3B,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IACtD,CAAC;IAED,2FAA2F;IAC3F,KAAK,CAAC,QAAQ,CAAC,YAA0B,EAAE,OAA4B;QACrE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,YAAY,GAAa;YAC7B,GAAG,QAAQ;YACX,SAAS,EAAE;gBACT,GAAG,QAAQ,CAAC,SAAS;gBACrB,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE;aAC/F;SACF,CAAC;QACF,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IAED,8FAA8F;IAC9F,KAAK,CAAC,MAAM,CAAC,YAA0B,EAAE,OAA4B;QACnE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAC/C,OAAO,KAAK,EAAE,MAAM,KAAK,WAAW,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAED,qGAAqG;IACrG,KAAK,CAAC,gBAAgB,CAAC,YAA0B,EAAE,OAA4B;QAC7E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAC/C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,OAAO,CAAC,uBAAuB,EAAE,IAAI,YAAY,qCAAqC,EAAE;gBAChG,WAAW,EAAE,iEAAiE;aAC/E,CAAC,CAAC;QACL,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,OAAO,CAAC,wBAAwB,EAAE,IAAI,YAAY,sCAAsC,EAAE;gBAClG,WAAW,EAAE,uFAAuF;aACrG,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import { type RelativePath } from '@qa-ai-stlc/schemas';
2
+ /**
3
+ * Validates and returns a project-relative path (AGENTS.md 5.6): forward-slash separators, no
4
+ * leading slash, no ".." segment. Throws `QaError` rather than a bare Zod error so callers get a
5
+ * stable `code` regardless of which artifact schema is involved.
6
+ */
7
+ export declare function assertRelativePath(value: string): RelativePath;
8
+ /**
9
+ * Computes the path from `rootDir` to `absolutePath` in the canonical forward-slash form
10
+ * artifacts are hashed and referenced by, independent of the host OS path separator.
11
+ */
12
+ export declare function toRelativePath(rootDir: string, absolutePath: string): RelativePath;
13
+ /** Resolves a validated project-relative path to an absolute, OS-native path under `rootDir`. */
14
+ export declare function resolveRelativePath(rootDir: string, relativePath: RelativePath): string;
15
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAG5E;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAQ9D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,YAAY,CAGlF;AAED,iGAAiG;AACjG,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,MAAM,CAGvF"}
package/dist/paths.js ADDED
@@ -0,0 +1,33 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { join, relative } from 'node:path';
4
+ import { RelativePathSchema } from '@qa-ai-stlc/schemas';
5
+ import { QaError } from './errors.js';
6
+ /**
7
+ * Validates and returns a project-relative path (AGENTS.md 5.6): forward-slash separators, no
8
+ * leading slash, no ".." segment. Throws `QaError` rather than a bare Zod error so callers get a
9
+ * stable `code` regardless of which artifact schema is involved.
10
+ */
11
+ export function assertRelativePath(value) {
12
+ const result = RelativePathSchema.safeParse(value);
13
+ if (!result.success) {
14
+ throw new QaError('INVALID_RELATIVE_PATH', `"${value}" is not a valid project-relative path`, {
15
+ remediation: 'Use a forward-slash path relative to the store root with no ".." segment.',
16
+ });
17
+ }
18
+ return result.data;
19
+ }
20
+ /**
21
+ * Computes the path from `rootDir` to `absolutePath` in the canonical forward-slash form
22
+ * artifacts are hashed and referenced by, independent of the host OS path separator.
23
+ */
24
+ export function toRelativePath(rootDir, absolutePath) {
25
+ const posixPath = relative(rootDir, absolutePath).split('\\').join('/');
26
+ return assertRelativePath(posixPath);
27
+ }
28
+ /** Resolves a validated project-relative path to an absolute, OS-native path under `rootDir`. */
29
+ export function resolveRelativePath(rootDir, relativePath) {
30
+ assertRelativePath(relativePath);
31
+ return join(rootDir, ...relativePath.split('/'));
32
+ }
33
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAqB,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,OAAO,CAAC,uBAAuB,EAAE,IAAI,KAAK,wCAAwC,EAAE;YAC5F,WAAW,EAAE,2EAA2E;SACzF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,YAAoB;IAClE,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxE,OAAO,kBAAkB,CAAC,SAAS,CAAC,CAAC;AACvC,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,mBAAmB,CAAC,OAAe,EAAE,YAA0B;IAC7E,kBAAkB,CAAC,YAAY,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { BrowserContext as PlaywrightBrowserContext } from 'playwright';
2
+ /** Playwright's own `BrowserContext.storageState()` return shape: cookies plus per-origin storage. */
3
+ export type StorageState = Awaited<ReturnType<PlaywrightBrowserContext['storageState']>>;
4
+ /**
5
+ * The narrow slice of Playwright's `Page` API authentication needs. A real Playwright `Page`
6
+ * satisfies this structurally; unit tests supply a small fake instead (AGENTS.md 5.3, 13).
7
+ */
8
+ export interface AuthPage {
9
+ goto(url: string): Promise<unknown>;
10
+ fill(selector: string, value: string): Promise<void>;
11
+ click(selector: string): Promise<void>;
12
+ waitForLoadState(state?: 'load' | 'domcontentloaded' | 'networkidle'): Promise<void>;
13
+ }
14
+ export interface AuthBrowserContext {
15
+ newPage(): Promise<AuthPage>;
16
+ storageState(): Promise<StorageState>;
17
+ close(): Promise<void>;
18
+ }
19
+ export interface AuthBrowser {
20
+ newContext(): Promise<AuthBrowserContext>;
21
+ /** The contexts an attached, already-authenticated browser owns (ADR-004, section 6.2 step 1). */
22
+ contexts(): readonly AuthBrowserContext[];
23
+ close(): Promise<void>;
24
+ }
25
+ /** Injected so authentication is testable without actually launching or attaching to a browser. */
26
+ export interface BrowserLauncher {
27
+ launch(): Promise<AuthBrowser>;
28
+ connectOverCdp(endpointUrl: string): Promise<AuthBrowser>;
29
+ }
30
+ export declare const playwrightBrowserLauncher: BrowserLauncher;
31
+ //# sourceMappingURL=browser-launcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-launcher.d.ts","sourceRoot":"","sources":["../../src/ports/browser-launcher.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,IAAI,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE7E,sGAAsG;AACtG,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,wBAAwB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;AAEzF;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,gBAAgB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,kBAAkB,GAAG,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7B,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;IACtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC1C,kGAAkG;IAClG,QAAQ,IAAI,SAAS,kBAAkB,EAAE,CAAC;IAC1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,mGAAmG;AACnG,MAAM,WAAW,eAAe;IAC9B,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/B,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC3D;AAED,eAAO,MAAM,yBAAyB,EAAE,eAGvC,CAAC"}
@@ -0,0 +1,8 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { chromium } from 'playwright';
4
+ export const playwrightBrowserLauncher = {
5
+ launch: () => chromium.launch(),
6
+ connectOverCdp: (endpointUrl) => chromium.connectOverCDP(endpointUrl),
7
+ };
8
+ //# sourceMappingURL=browser-launcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-launcher.js","sourceRoot":"","sources":["../../src/ports/browser-launcher.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAoCtC,MAAM,CAAC,MAAM,yBAAyB,GAAoB;IACxD,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE;IAC/B,cAAc,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC;CACtE,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** The current time, injected so artifact timestamps are deterministic in tests. */
2
+ export interface Clock {
3
+ now(): Date;
4
+ }
5
+ export declare const systemClock: Clock;
6
+ //# sourceMappingURL=clock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["../../src/ports/clock.ts"],"names":[],"mappings":"AAGA,oFAAoF;AACpF,MAAM,WAAW,KAAK;IACpB,GAAG,IAAI,IAAI,CAAC;CACb;AAED,eAAO,MAAM,WAAW,EAAE,KAEzB,CAAC"}
@@ -0,0 +1,6 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export const systemClock = {
4
+ now: () => new Date(),
5
+ };
6
+ //# sourceMappingURL=clock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.js","sourceRoot":"","sources":["../../src/ports/clock.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAOtC,MAAM,CAAC,MAAM,WAAW,GAAU;IAChC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE;CACtB,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The filesystem operations the engine needs, injected so store and manifest logic can be
3
+ * tested without touching a real disk (AGENTS.md 5.3). Paths are always absolute, OS-native
4
+ * paths; project-relative path handling lives in `paths.ts`.
5
+ */
6
+ export interface FileSystem {
7
+ readFile(absolutePath: string): Promise<string>;
8
+ writeFile(absolutePath: string, content: string | Uint8Array): Promise<void>;
9
+ mkdir(absolutePath: string): Promise<void>;
10
+ pathExists(absolutePath: string): Promise<boolean>;
11
+ }
12
+ export declare const nodeFileSystem: FileSystem;
13
+ //# sourceMappingURL=file-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../../src/ports/file-system.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpD;AAED,eAAO,MAAM,cAAc,EAAE,UAoB5B,CAAC"}
@@ -0,0 +1,25 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
4
+ export const nodeFileSystem = {
5
+ readFile: (absolutePath) => readFile(absolutePath, 'utf-8'),
6
+ writeFile: (absolutePath, content) => typeof content === 'string'
7
+ ? writeFile(absolutePath, content, 'utf-8')
8
+ : writeFile(absolutePath, content),
9
+ mkdir: async (absolutePath) => {
10
+ await mkdir(absolutePath, { recursive: true });
11
+ },
12
+ pathExists: async (absolutePath) => {
13
+ try {
14
+ await stat(absolutePath);
15
+ return true;
16
+ }
17
+ catch (error) {
18
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
19
+ return false;
20
+ }
21
+ throw error;
22
+ }
23
+ },
24
+ };
25
+ //# sourceMappingURL=file-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-system.js","sourceRoot":"","sources":["../../src/ports/file-system.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAcpE,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,QAAQ,EAAE,CAAC,YAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC3D,SAAS,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,EAAE,CACnC,OAAO,OAAO,KAAK,QAAQ;QACzB,CAAC,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC;QAC3C,CAAC,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC;IACtC,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE;QAC5B,MAAM,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE;QACjC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACzE,OAAO,KAAK,CAAC;YACf,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface HttpResponse {
2
+ readonly ok: boolean;
3
+ readonly status: number;
4
+ }
5
+ export interface HttpRequestOptions {
6
+ readonly signal?: AbortSignal;
7
+ }
8
+ /** The one network operation `qa doctor` needs: injected so reachability checks never make a real network call in tests (AGENTS.md 5.3, 13). */
9
+ export interface HttpClient {
10
+ get(url: string, options?: HttpRequestOptions): Promise<HttpResponse>;
11
+ }
12
+ export declare const fetchHttpClient: HttpClient;
13
+ //# sourceMappingURL=http-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../src/ports/http-client.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,gJAAgJ;AAChJ,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACvE;AAED,eAAO,MAAM,eAAe,EAAE,UAQ7B,CAAC"}
@@ -0,0 +1,12 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export const fetchHttpClient = {
4
+ get: async (url, options) => {
5
+ const response = await fetch(url, {
6
+ method: 'GET',
7
+ ...(options?.signal ? { signal: options.signal } : {}),
8
+ });
9
+ return { ok: response.ok, status: response.status };
10
+ },
11
+ };
12
+ //# sourceMappingURL=http-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/ports/http-client.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAgBtC,MAAM,CAAC,MAAM,eAAe,GAAe;IACzC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;QAC1B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,KAAK;YACb,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvD,CAAC,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IACtD,CAAC;CACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ export type LogMeta = Readonly<Record<string, unknown>>;
2
+ export interface Logger {
3
+ debug(message: string, meta?: LogMeta): void;
4
+ info(message: string, meta?: LogMeta): void;
5
+ warn(message: string, meta?: LogMeta): void;
6
+ error(message: string, meta?: LogMeta): void;
7
+ }
8
+ export declare const noopLogger: Logger;
9
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/ports/logger.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAIxD,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9C;AAED,eAAO,MAAM,UAAU,EAAE,MAKxB,CAAC"}
@@ -0,0 +1,9 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export const noopLogger = {
4
+ debug: () => undefined,
5
+ info: () => undefined,
6
+ warn: () => undefined,
7
+ error: () => undefined,
8
+ };
9
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/ports/logger.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAatC,MAAM,CAAC,MAAM,UAAU,GAAW;IAChC,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS;IACtB,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS;IACrB,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS;IACrB,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS;CACvB,CAAC"}
@@ -0,0 +1,17 @@
1
+ export interface ProcessResult {
2
+ readonly exitCode: number;
3
+ readonly stdout: string;
4
+ readonly stderr: string;
5
+ }
6
+ export interface ProcessRunOptions {
7
+ readonly signal?: AbortSignal;
8
+ }
9
+ /**
10
+ * Runs an external command, injected so browser installation is testable without actually
11
+ * spawning one (AGENTS.md 5.3). Always an argument array, never a shell string (AGENTS.md 5.6).
12
+ */
13
+ export interface ProcessRunner {
14
+ run(command: string, args: readonly string[], options?: ProcessRunOptions): Promise<ProcessResult>;
15
+ }
16
+ export declare const nodeProcessRunner: ProcessRunner;
17
+ //# sourceMappingURL=process-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-runner.d.ts","sourceRoot":"","sources":["../../src/ports/process-runner.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACpG;AAMD,eAAO,MAAM,iBAAiB,EAAE,aAiB/B,CAAC"}
@@ -0,0 +1,24 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { execFile } from 'node:child_process';
4
+ function hasNumericCode(error) {
5
+ return typeof error.code === 'number';
6
+ }
7
+ export const nodeProcessRunner = {
8
+ run: (command, args, options) => new Promise((resolve, reject) => {
9
+ execFile(command, args, { signal: options?.signal }, (error, stdout, stderr) => {
10
+ if (error === null) {
11
+ resolve({ exitCode: 0, stdout, stderr });
12
+ return;
13
+ }
14
+ // A numeric `code` is the child process's own exit code; anything else (a string like
15
+ // "ENOENT") means the command itself could not be spawned at all.
16
+ if (hasNumericCode(error)) {
17
+ resolve({ exitCode: error.code, stdout, stderr });
18
+ return;
19
+ }
20
+ reject(new Error(`Failed to spawn "${command}": ${error.message}`, { cause: error }));
21
+ });
22
+ }),
23
+ };
24
+ //# sourceMappingURL=process-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-runner.js","sourceRoot":"","sources":["../../src/ports/process-runner.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,OAAO,EAAE,QAAQ,EAAsB,MAAM,oBAAoB,CAAC;AAoBlE,SAAS,cAAc,CAAC,KAAoB;IAC1C,OAAO,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAkB;IAC9C,GAAG,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAC9B,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC9B,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAC7E,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBACzC,OAAO;YACT,CAAC;YACD,sFAAsF;YACtF,kEAAkE;YAClE,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBAClD,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,KAAK,CAAC,oBAAoB,OAAO,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACxF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;CACL,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { z } from 'zod';
2
+ import type { RelativePath } from '@qa-ai-stlc/schemas';
3
+ import { type FileSystem } from './ports/file-system.js';
4
+ export interface QaStoreOptions {
5
+ readonly projectRoot: string;
6
+ readonly fs?: FileSystem;
7
+ }
8
+ /**
9
+ * The `.qa/` store: resolves an artifact's project-relative path to a real file under `.qa/`,
10
+ * creates the documented directory layout, and reads or writes JSON through the injected
11
+ * `FileSystem` port so callers never touch `node:fs` directly (AGENTS.md 5.3).
12
+ */
13
+ export declare class QaStore {
14
+ readonly projectRoot: string;
15
+ readonly qaDir: string;
16
+ private readonly fs;
17
+ constructor(options: QaStoreOptions);
18
+ /** Resolves a project-relative artifact path (e.g. `manifest.json`) to an absolute path. */
19
+ resolve(relativePath: RelativePath): string;
20
+ /** Converts an absolute path under `.qa/` back to the project-relative form artifacts use. */
21
+ toRelativePath(absolutePath: string): RelativePath;
22
+ exists(): Promise<boolean>;
23
+ /** Creates `.qa/` and every committed subdirectory from the documented layout, if missing. */
24
+ ensureLayout(): Promise<void>;
25
+ readJson<Schema extends z.ZodType>(relativePath: RelativePath, schema: Schema): Promise<z.infer<Schema>>;
26
+ writeJson(relativePath: RelativePath, value: unknown): Promise<void>;
27
+ pathExists(relativePath: RelativePath): Promise<boolean>;
28
+ readText(relativePath: RelativePath): Promise<string>;
29
+ writeText(relativePath: RelativePath, content: string): Promise<void>;
30
+ /** Like `writeText`, but also accepts raw bytes for binary artifacts such as evidence. */
31
+ writeBytes(relativePath: RelativePath, content: string | Uint8Array): Promise<void>;
32
+ }
33
+ //# sourceMappingURL=qa-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa-store.d.ts","sourceRoot":"","sources":["../src/qa-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC;CAC1B;AAOD;;;;GAIG;AACH,qBAAa,OAAO;IAClB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAa;gBAEpB,OAAO,EAAE,cAAc;IAMnC,4FAA4F;IAC5F,OAAO,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM;IAI3C,8FAA8F;IAC9F,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY;IAI5C,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAIhC,8FAA8F;IACxF,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAK7B,QAAQ,CAAC,MAAM,SAAS,CAAC,CAAC,OAAO,EACrC,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAIrB,SAAS,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpE,UAAU,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxD,QAAQ,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAIrD,SAAS,CAAC,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3E,0FAA0F;IACpF,UAAU,CAAC,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAK1F"}