my-frontend-observer 0.4.0 → 0.6.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 (68) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +52 -11
  3. package/dist/application/frontendContractEvaluationService.d.ts +49 -0
  4. package/dist/application/frontendContractEvaluationService.js +112 -0
  5. package/dist/application/frontendContractEvaluationService.js.map +1 -0
  6. package/dist/application/frontendContractPersistenceService.d.ts +56 -0
  7. package/dist/application/frontendContractPersistenceService.js +91 -0
  8. package/dist/application/frontendContractPersistenceService.js.map +1 -0
  9. package/dist/artifacts/comparisonArtifactReader.d.ts +18 -0
  10. package/dist/artifacts/comparisonArtifactReader.js +35 -0
  11. package/dist/artifacts/comparisonArtifactReader.js.map +1 -0
  12. package/dist/artifacts/frontendContractArtifactReader.d.ts +24 -0
  13. package/dist/artifacts/frontendContractArtifactReader.js +47 -0
  14. package/dist/artifacts/frontendContractArtifactReader.js.map +1 -0
  15. package/dist/artifacts/frontendContractArtifactWriter.d.ts +34 -0
  16. package/dist/artifacts/frontendContractArtifactWriter.js +70 -0
  17. package/dist/artifacts/frontendContractArtifactWriter.js.map +1 -0
  18. package/dist/artifacts/frontendContractEvaluationArtifactReader.d.ts +17 -0
  19. package/dist/artifacts/frontendContractEvaluationArtifactReader.js +34 -0
  20. package/dist/artifacts/frontendContractEvaluationArtifactReader.js.map +1 -0
  21. package/dist/artifacts/frontendContractEvaluationArtifactWriter.d.ts +32 -0
  22. package/dist/artifacts/frontendContractEvaluationArtifactWriter.js +58 -0
  23. package/dist/artifacts/frontendContractEvaluationArtifactWriter.js.map +1 -0
  24. package/dist/cli.js +496 -6
  25. package/dist/cli.js.map +1 -1
  26. package/dist/domain/boundedAgentContext.d.ts +174 -0
  27. package/dist/domain/boundedAgentContext.js +312 -0
  28. package/dist/domain/boundedAgentContext.js.map +1 -0
  29. package/dist/domain/boundedAgentContextCorrelation.d.ts +74 -0
  30. package/dist/domain/boundedAgentContextCorrelation.js +441 -0
  31. package/dist/domain/boundedAgentContextCorrelation.js.map +1 -0
  32. package/dist/domain/boundedAgentContextIdentity.d.ts +16 -0
  33. package/dist/domain/boundedAgentContextIdentity.js +53 -0
  34. package/dist/domain/boundedAgentContextIdentity.js.map +1 -0
  35. package/dist/domain/boundedAgentContextProjection.d.ts +60 -0
  36. package/dist/domain/boundedAgentContextProjection.js +449 -0
  37. package/dist/domain/boundedAgentContextProjection.js.map +1 -0
  38. package/dist/domain/frontendContractEvaluation.d.ts +57 -0
  39. package/dist/domain/frontendContractEvaluation.js +454 -0
  40. package/dist/domain/frontendContractEvaluation.js.map +1 -0
  41. package/dist/domain/frontendContractEvaluationArtifact.d.ts +65 -0
  42. package/dist/domain/frontendContractEvaluationArtifact.js +108 -0
  43. package/dist/domain/frontendContractEvaluationArtifact.js.map +1 -0
  44. package/dist/domain/frontendContractIdentity.d.ts +39 -0
  45. package/dist/domain/frontendContractIdentity.js +70 -0
  46. package/dist/domain/frontendContractIdentity.js.map +1 -0
  47. package/dist/domain/frontendContracts.d.ts +188 -0
  48. package/dist/domain/frontendContracts.js +260 -0
  49. package/dist/domain/frontendContracts.js.map +1 -0
  50. package/dist/index.d.ts +29 -0
  51. package/dist/index.js +16 -0
  52. package/dist/index.js.map +1 -1
  53. package/docs/ARCHITECTURE.md +169 -10
  54. package/docs/CI_CD.md +68 -0
  55. package/docs/COMMANDS.md +162 -0
  56. package/docs/CONTRACTS.md +245 -5
  57. package/docs/CURRENT_STATE.md +152 -14
  58. package/docs/DEVELOPMENT.md +61 -11
  59. package/docs/DOCUMENTATION_PRESERVATION_POLICY.md +33 -33
  60. package/docs/PROJECT_DESCRIPTION.md +1770 -1770
  61. package/docs/PROJECT_MILESTONES.md +2073 -2073
  62. package/docs/PROJECT_OVERVIEW.md +29 -16
  63. package/docs/QUICKSTART.md +5 -0
  64. package/docs/RELEASE.md +17 -10
  65. package/docs/ROADMAP.md +9 -0
  66. package/docs/SECURITY.md +30 -1
  67. package/docs/WORKFLOWS.md +98 -9
  68. package/package.json +1 -1
@@ -0,0 +1,47 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { isValidPersistentBaselineContract, isValidPerChangeContract } from '../domain/frontendContracts.js';
3
+ async function readJson(manifestPath) {
4
+ let raw;
5
+ try {
6
+ raw = await readFile(manifestPath, 'utf8');
7
+ }
8
+ catch (err) {
9
+ const message = err instanceof Error ? err.message : String(err);
10
+ return { ok: false, reason: `failed to read artifact manifest at "${manifestPath}": ${message}` };
11
+ }
12
+ try {
13
+ return { ok: true, value: JSON.parse(raw) };
14
+ }
15
+ catch (err) {
16
+ const message = err instanceof Error ? err.message : String(err);
17
+ return { ok: false, reason: `artifact manifest at "${manifestPath}" is not valid JSON: ${message}` };
18
+ }
19
+ }
20
+ /**
21
+ * Narrow canonical reader counterpart to `frontendContractArtifactWriter.ts#writePersistentBaselineContract`:
22
+ * reads one `manifest.json`, parses it, and validates it through the same
23
+ * `isValidPersistentBaselineContract` structural gate the writer uses - never
24
+ * a second validator, never a schema upgrade, never a guessed missing field.
25
+ */
26
+ export async function readPersistentBaselineContract(manifestPath) {
27
+ const parsed = await readJson(manifestPath);
28
+ if (!parsed.ok)
29
+ return parsed;
30
+ const validation = isValidPersistentBaselineContract(parsed.value);
31
+ if (!validation.valid) {
32
+ return { ok: false, reason: `baseline contract manifest at "${manifestPath}" failed structural validation: ${validation.reason}` };
33
+ }
34
+ return { ok: true, contract: parsed.value };
35
+ }
36
+ /** Narrow canonical reader counterpart to `frontendContractArtifactWriter.ts#writePerChangeContract`. */
37
+ export async function readPerChangeContract(manifestPath) {
38
+ const parsed = await readJson(manifestPath);
39
+ if (!parsed.ok)
40
+ return parsed;
41
+ const validation = isValidPerChangeContract(parsed.value);
42
+ if (!validation.valid) {
43
+ return { ok: false, reason: `per-change contract manifest at "${manifestPath}" failed structural validation: ${validation.reason}` };
44
+ }
45
+ return { ok: true, contract: parsed.value };
46
+ }
47
+ //# sourceMappingURL=frontendContractArtifactReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontendContractArtifactReader.js","sourceRoot":"","sources":["../../src/artifacts/frontendContractArtifactReader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,iCAAiC,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAK7G,KAAK,UAAU,QAAQ,CAAC,YAAoB;IAC1C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wCAAwC,YAAY,MAAM,OAAO,EAAE,EAAE,CAAC;IACpG,CAAC;IACD,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,yBAAyB,YAAY,wBAAwB,OAAO,EAAE,EAAE,CAAC;IACvG,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,YAAoB;IACvE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,MAAM,CAAC;IAC9B,MAAM,UAAU,GAAG,iCAAiC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kCAAkC,YAAY,mCAAmC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;IACrI,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAmC,EAAE,CAAC;AAC5E,CAAC;AAED,yGAAyG;AACzG,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,YAAoB;IAC9D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,MAAM,CAAC;IAC9B,MAAM,UAAU,GAAG,wBAAwB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oCAAoC,YAAY,mCAAmC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;IACvI,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,KAA0B,EAAE,CAAC;AACnE,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { PersistentBaselineContract, PerChangeContract } from '../domain/frontendContracts.js';
2
+ import type { Diagnostic } from '../domain/diagnostics.js';
3
+ export declare const FRONTEND_CONTRACT_MANIFEST_FILENAME = "manifest.json";
4
+ /**
5
+ * Mirrors `PersistedComparisonResult`'s shape/semantics - a frontend contract
6
+ * directory has no screenshot file either, and this writer never touches
7
+ * `artifacts/artifactWriter.ts` or `artifacts/comparisonArtifactWriter.ts`.
8
+ */
9
+ export type PersistedFrontendContractResult = {
10
+ ok: true;
11
+ artifactRoot: string;
12
+ manifestPath: string;
13
+ } | {
14
+ ok: false;
15
+ diagnostics: Diagnostic[];
16
+ };
17
+ export interface WriteFrontendContractArtifactOptions {
18
+ /** Base directory that `outputLocation` is resolved against. Defaults to `process.cwd()`. */
19
+ cwd?: string;
20
+ }
21
+ /**
22
+ * Persists one already-valid `PersistentBaselineContract` as
23
+ * `<outputLocation>/<baselineId>/manifest.json`. Never mutates or removes any
24
+ * other baseline artifact - `supersedesBaselineId` (if present) is written
25
+ * exactly as supplied; this writer never inspects, rewrites, or deletes the
26
+ * baseline it refers to.
27
+ */
28
+ export declare function writePersistentBaselineContract(contract: PersistentBaselineContract, outputLocation: string, options?: WriteFrontendContractArtifactOptions): Promise<PersistedFrontendContractResult>;
29
+ /**
30
+ * Persists one already-valid `PerChangeContract` as
31
+ * `<outputLocation>/<contractId>/manifest.json`, symmetric with baseline
32
+ * persistence above (both share `CONTRACT_ARTIFACT_KIND`/`CONTRACT_SCHEMA_VERSION`).
33
+ */
34
+ export declare function writePerChangeContract(contract: PerChangeContract, outputLocation: string, options?: WriteFrontendContractArtifactOptions): Promise<PersistedFrontendContractResult>;
@@ -0,0 +1,70 @@
1
+ import { mkdir, writeFile, rename, rm, access } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { isValidPersistentBaselineContract, isValidPerChangeContract } from '../domain/frontendContracts.js';
4
+ import { DIAGNOSTIC_SEVERITY } from '../domain/diagnostics.js';
5
+ export const FRONTEND_CONTRACT_MANIFEST_FILENAME = 'manifest.json';
6
+ function writeFailure(message, details) {
7
+ const diagnostic = {
8
+ code: 'artifact-write-failure',
9
+ severity: DIAGNOSTIC_SEVERITY['artifact-write-failure'],
10
+ message,
11
+ ...(details === undefined ? {} : { details }),
12
+ };
13
+ return { ok: false, diagnostics: [diagnostic] };
14
+ }
15
+ async function pathExists(candidate) {
16
+ return access(candidate).then(() => true, () => false);
17
+ }
18
+ /**
19
+ * Shared atomic-write body for both contract classes, following the same
20
+ * discipline as `writeObservationArtifact`/`writeComparisonArtifact`: sibling
21
+ * temporary directory, single `manifest.json`, then one atomic rename. An
22
+ * existing directory at the final identity is a genuine collision (never a
23
+ * destructive overwrite of prior baseline/contract history) and is rejected.
24
+ */
25
+ async function writeContractArtifact(value, id, kindLabel, outputLocation, options) {
26
+ const cwd = options.cwd ?? process.cwd();
27
+ const outputRoot = path.resolve(cwd, outputLocation);
28
+ const finalRoot = path.join(outputRoot, id);
29
+ const tempRoot = path.join(outputRoot, `.tmp-${id}`);
30
+ if (await pathExists(finalRoot)) {
31
+ return writeFailure(`a ${kindLabel} artifact already exists at "${id}"; refusing to overwrite it`, { artifactRoot: finalRoot });
32
+ }
33
+ try {
34
+ await rm(tempRoot, { recursive: true, force: true });
35
+ await mkdir(tempRoot, { recursive: true });
36
+ await writeFile(path.join(tempRoot, FRONTEND_CONTRACT_MANIFEST_FILENAME), JSON.stringify(value, null, 2), 'utf8');
37
+ await rename(tempRoot, finalRoot);
38
+ return { ok: true, artifactRoot: finalRoot, manifestPath: path.join(finalRoot, FRONTEND_CONTRACT_MANIFEST_FILENAME) };
39
+ }
40
+ catch (err) {
41
+ await rm(tempRoot, { recursive: true, force: true }).catch(() => undefined);
42
+ const message = err instanceof Error ? err.message : String(err);
43
+ return writeFailure(`failed to persist ${kindLabel} artifact: ${message}`);
44
+ }
45
+ }
46
+ /**
47
+ * Persists one already-valid `PersistentBaselineContract` as
48
+ * `<outputLocation>/<baselineId>/manifest.json`. Never mutates or removes any
49
+ * other baseline artifact - `supersedesBaselineId` (if present) is written
50
+ * exactly as supplied; this writer never inspects, rewrites, or deletes the
51
+ * baseline it refers to.
52
+ */
53
+ export async function writePersistentBaselineContract(contract, outputLocation, options = {}) {
54
+ const validation = isValidPersistentBaselineContract(contract);
55
+ if (!validation.valid)
56
+ return writeFailure(`refusing to persist an invalid PersistentBaselineContract: ${validation.reason}`);
57
+ return writeContractArtifact(contract, contract.baselineId, 'baseline contract', outputLocation, options);
58
+ }
59
+ /**
60
+ * Persists one already-valid `PerChangeContract` as
61
+ * `<outputLocation>/<contractId>/manifest.json`, symmetric with baseline
62
+ * persistence above (both share `CONTRACT_ARTIFACT_KIND`/`CONTRACT_SCHEMA_VERSION`).
63
+ */
64
+ export async function writePerChangeContract(contract, outputLocation, options = {}) {
65
+ const validation = isValidPerChangeContract(contract);
66
+ if (!validation.valid)
67
+ return writeFailure(`refusing to persist an invalid PerChangeContract: ${validation.reason}`);
68
+ return writeContractArtifact(contract, contract.contractId, 'per-change contract', outputLocation, options);
69
+ }
70
+ //# sourceMappingURL=frontendContractArtifactWriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontendContractArtifactWriter.js","sourceRoot":"","sources":["../../src/artifacts/frontendContractArtifactWriter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { FrontendContractEvaluationArtifact } from '../domain/frontendContractEvaluationArtifact.js';
2
+ export type ReadFrontendContractEvaluationArtifactResult = {
3
+ ok: true;
4
+ artifact: FrontendContractEvaluationArtifact;
5
+ } | {
6
+ ok: false;
7
+ reason: string;
8
+ };
9
+ /**
10
+ * Narrow canonical reader counterpart to
11
+ * `frontendContractEvaluationArtifactWriter.ts#writeFrontendContractEvaluationArtifact`:
12
+ * reads one `manifest.json`, parses it, and validates it through the same
13
+ * `isValidFrontendContractEvaluationArtifact` structural gate the writer
14
+ * uses. Read-only: never mutates the file, never recomputes or reinterprets
15
+ * the evaluation, never repairs a malformed artifact.
16
+ */
17
+ export declare function readFrontendContractEvaluationArtifact(manifestPath: string): Promise<ReadFrontendContractEvaluationArtifactResult>;
@@ -0,0 +1,34 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { isValidFrontendContractEvaluationArtifact } from '../domain/frontendContractEvaluationArtifact.js';
3
+ /**
4
+ * Narrow canonical reader counterpart to
5
+ * `frontendContractEvaluationArtifactWriter.ts#writeFrontendContractEvaluationArtifact`:
6
+ * reads one `manifest.json`, parses it, and validates it through the same
7
+ * `isValidFrontendContractEvaluationArtifact` structural gate the writer
8
+ * uses. Read-only: never mutates the file, never recomputes or reinterprets
9
+ * the evaluation, never repairs a malformed artifact.
10
+ */
11
+ export async function readFrontendContractEvaluationArtifact(manifestPath) {
12
+ let raw;
13
+ try {
14
+ raw = await readFile(manifestPath, 'utf8');
15
+ }
16
+ catch (err) {
17
+ const message = err instanceof Error ? err.message : String(err);
18
+ return { ok: false, reason: `failed to read evaluation artifact manifest at "${manifestPath}": ${message}` };
19
+ }
20
+ let parsed;
21
+ try {
22
+ parsed = JSON.parse(raw);
23
+ }
24
+ catch (err) {
25
+ const message = err instanceof Error ? err.message : String(err);
26
+ return { ok: false, reason: `evaluation artifact manifest at "${manifestPath}" is not valid JSON: ${message}` };
27
+ }
28
+ const validation = isValidFrontendContractEvaluationArtifact(parsed);
29
+ if (!validation.valid) {
30
+ return { ok: false, reason: `evaluation artifact manifest at "${manifestPath}" failed structural validation: ${validation.reason}` };
31
+ }
32
+ return { ok: true, artifact: parsed };
33
+ }
34
+ //# sourceMappingURL=frontendContractEvaluationArtifactReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontendContractEvaluationArtifactReader.js","sourceRoot":"","sources":["../../src/artifacts/frontendContractEvaluationArtifactReader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,yCAAyC,EAAE,MAAM,iDAAiD,CAAC;AAI5G;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,sCAAsC,CAAC,YAAoB;IAC/E,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,mDAAmD,YAAY,MAAM,OAAO,EAAE,EAAE,CAAC;IAC/G,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oCAAoC,YAAY,wBAAwB,OAAO,EAAE,EAAE,CAAC;IAClH,CAAC;IAED,MAAM,UAAU,GAAG,yCAAyC,CAAC,MAAM,CAAC,CAAC;IACrE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oCAAoC,YAAY,mCAAmC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;IACvI,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAA4C,EAAE,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { FrontendContractEvaluationArtifact } from '../domain/frontendContractEvaluationArtifact.js';
2
+ import type { Diagnostic } from '../domain/diagnostics.js';
3
+ export declare const EVALUATION_MANIFEST_FILENAME = "manifest.json";
4
+ export type PersistedFrontendContractEvaluationResult = {
5
+ ok: true;
6
+ artifactRoot: string;
7
+ manifestPath: string;
8
+ } | {
9
+ ok: false;
10
+ diagnostics: Diagnostic[];
11
+ };
12
+ export interface WriteFrontendContractEvaluationArtifactOptions {
13
+ /** Base directory that `outputLocation` is resolved against. Defaults to `process.cwd()`. */
14
+ cwd?: string;
15
+ }
16
+ /**
17
+ * Persists one already-built, already-valid `FrontendContractEvaluationArtifact`
18
+ * as `<outputLocation>/<evaluationId>/manifest.json` only - no screenshot is
19
+ * copied (the artifact's `before`/`after` fields already point back to the
20
+ * source observations). Same atomic-write discipline as
21
+ * `artifactWriter.ts#writeObservationArtifact`/`comparisonArtifactWriter.ts#writeComparisonArtifact`
22
+ * without sharing their implementation: sibling temporary directory, then one
23
+ * atomic rename. `evaluationId` is fresh per execution (see
24
+ * `frontendContractIdentity.ts#buildFrontendContractInstanceIdentity`), so an
25
+ * existing directory at the final path is a genuine collision and is
26
+ * rejected rather than overwritten - a legitimate PASS or FAIL evaluation
27
+ * result is never silently replaced. A `FAIL` verdict is a legitimate,
28
+ * persistable evaluation result, not a write failure; only an unpersistable
29
+ * artifact (invalid shape, or a real filesystem error) is reported as
30
+ * `ok: false`.
31
+ */
32
+ export declare function writeFrontendContractEvaluationArtifact(artifact: FrontendContractEvaluationArtifact, outputLocation: string, options?: WriteFrontendContractEvaluationArtifactOptions): Promise<PersistedFrontendContractEvaluationResult>;
@@ -0,0 +1,58 @@
1
+ import { mkdir, writeFile, rename, rm, access } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { isValidFrontendContractEvaluationArtifact } from '../domain/frontendContractEvaluationArtifact.js';
4
+ import { DIAGNOSTIC_SEVERITY } from '../domain/diagnostics.js';
5
+ export const EVALUATION_MANIFEST_FILENAME = 'manifest.json';
6
+ function writeFailure(message, details) {
7
+ const diagnostic = {
8
+ code: 'artifact-write-failure',
9
+ severity: DIAGNOSTIC_SEVERITY['artifact-write-failure'],
10
+ message,
11
+ ...(details === undefined ? {} : { details }),
12
+ };
13
+ return { ok: false, diagnostics: [diagnostic] };
14
+ }
15
+ async function pathExists(candidate) {
16
+ return access(candidate).then(() => true, () => false);
17
+ }
18
+ /**
19
+ * Persists one already-built, already-valid `FrontendContractEvaluationArtifact`
20
+ * as `<outputLocation>/<evaluationId>/manifest.json` only - no screenshot is
21
+ * copied (the artifact's `before`/`after` fields already point back to the
22
+ * source observations). Same atomic-write discipline as
23
+ * `artifactWriter.ts#writeObservationArtifact`/`comparisonArtifactWriter.ts#writeComparisonArtifact`
24
+ * without sharing their implementation: sibling temporary directory, then one
25
+ * atomic rename. `evaluationId` is fresh per execution (see
26
+ * `frontendContractIdentity.ts#buildFrontendContractInstanceIdentity`), so an
27
+ * existing directory at the final path is a genuine collision and is
28
+ * rejected rather than overwritten - a legitimate PASS or FAIL evaluation
29
+ * result is never silently replaced. A `FAIL` verdict is a legitimate,
30
+ * persistable evaluation result, not a write failure; only an unpersistable
31
+ * artifact (invalid shape, or a real filesystem error) is reported as
32
+ * `ok: false`.
33
+ */
34
+ export async function writeFrontendContractEvaluationArtifact(artifact, outputLocation, options = {}) {
35
+ const validation = isValidFrontendContractEvaluationArtifact(artifact);
36
+ if (!validation.valid)
37
+ return writeFailure(`refusing to persist an invalid FrontendContractEvaluationArtifact: ${validation.reason}`);
38
+ const cwd = options.cwd ?? process.cwd();
39
+ const outputRoot = path.resolve(cwd, outputLocation);
40
+ const finalRoot = path.join(outputRoot, artifact.evaluationId);
41
+ const tempRoot = path.join(outputRoot, `.tmp-${artifact.evaluationId}`);
42
+ if (await pathExists(finalRoot)) {
43
+ return writeFailure(`an evaluation artifact already exists at "${artifact.evaluationId}"; refusing to overwrite it`, { artifactRoot: finalRoot });
44
+ }
45
+ try {
46
+ await rm(tempRoot, { recursive: true, force: true });
47
+ await mkdir(tempRoot, { recursive: true });
48
+ await writeFile(path.join(tempRoot, EVALUATION_MANIFEST_FILENAME), JSON.stringify(artifact, null, 2), 'utf8');
49
+ await rename(tempRoot, finalRoot);
50
+ return { ok: true, artifactRoot: finalRoot, manifestPath: path.join(finalRoot, EVALUATION_MANIFEST_FILENAME) };
51
+ }
52
+ catch (err) {
53
+ await rm(tempRoot, { recursive: true, force: true }).catch(() => undefined);
54
+ const message = err instanceof Error ? err.message : String(err);
55
+ return writeFailure(`failed to persist evaluation artifact: ${message}`);
56
+ }
57
+ }
58
+ //# sourceMappingURL=frontendContractEvaluationArtifactWriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontendContractEvaluationArtifactWriter.js","sourceRoot":"","sources":["../../src/artifacts/frontendContractEvaluationArtifactWriter.ts"],"names":[],"mappings":"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"}