@dailephd/my-frontend-observer 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +397 -0
- package/LICENSE +21 -0
- package/README.md +255 -0
- package/dist/application/browserCaptureService.d.ts +11 -0
- package/dist/application/browserCaptureService.js +12 -0
- package/dist/application/browserCaptureService.js.map +1 -0
- package/dist/application/comparisonService.d.ts +56 -0
- package/dist/application/comparisonService.js +77 -0
- package/dist/application/comparisonService.js.map +1 -0
- package/dist/application/externalReferencePersistenceService.d.ts +75 -0
- package/dist/application/externalReferencePersistenceService.js +182 -0
- package/dist/application/externalReferencePersistenceService.js.map +1 -0
- package/dist/application/frontendContractEvaluationService.d.ts +49 -0
- package/dist/application/frontendContractEvaluationService.js +112 -0
- package/dist/application/frontendContractEvaluationService.js.map +1 -0
- package/dist/application/frontendContractPersistenceService.d.ts +56 -0
- package/dist/application/frontendContractPersistenceService.js +91 -0
- package/dist/application/frontendContractPersistenceService.js.map +1 -0
- package/dist/application/observationPersistence.d.ts +59 -0
- package/dist/application/observationPersistence.js +79 -0
- package/dist/application/observationPersistence.js.map +1 -0
- package/dist/application/projectCheckService.d.ts +3 -0
- package/dist/application/projectCheckService.js +169 -0
- package/dist/application/projectCheckService.js.map +1 -0
- package/dist/application/projectWorkflowService.d.ts +46 -0
- package/dist/application/projectWorkflowService.js +92 -0
- package/dist/application/projectWorkflowService.js.map +1 -0
- package/dist/application/referenceFidelityEvaluationService.d.ts +28 -0
- package/dist/application/referenceFidelityEvaluationService.js +45 -0
- package/dist/application/referenceFidelityEvaluationService.js.map +1 -0
- package/dist/artifacts/artifactReader.d.ts +19 -0
- package/dist/artifacts/artifactReader.js +36 -0
- package/dist/artifacts/artifactReader.js.map +1 -0
- package/dist/artifacts/artifactWriter.d.ts +25 -0
- package/dist/artifacts/artifactWriter.js +68 -0
- package/dist/artifacts/artifactWriter.js.map +1 -0
- package/dist/artifacts/comparisonArtifactReader.d.ts +18 -0
- package/dist/artifacts/comparisonArtifactReader.js +35 -0
- package/dist/artifacts/comparisonArtifactReader.js.map +1 -0
- package/dist/artifacts/comparisonArtifactWriter.d.ts +41 -0
- package/dist/artifacts/comparisonArtifactWriter.js +67 -0
- package/dist/artifacts/comparisonArtifactWriter.js.map +1 -0
- package/dist/artifacts/externalReferenceArtifactReader.d.ts +18 -0
- package/dist/artifacts/externalReferenceArtifactReader.js +35 -0
- package/dist/artifacts/externalReferenceArtifactReader.js.map +1 -0
- package/dist/artifacts/externalReferenceArtifactWriter.d.ts +44 -0
- package/dist/artifacts/externalReferenceArtifactWriter.js +77 -0
- package/dist/artifacts/externalReferenceArtifactWriter.js.map +1 -0
- package/dist/artifacts/frontendContractArtifactReader.d.ts +24 -0
- package/dist/artifacts/frontendContractArtifactReader.js +47 -0
- package/dist/artifacts/frontendContractArtifactReader.js.map +1 -0
- package/dist/artifacts/frontendContractArtifactWriter.d.ts +34 -0
- package/dist/artifacts/frontendContractArtifactWriter.js +70 -0
- package/dist/artifacts/frontendContractArtifactWriter.js.map +1 -0
- package/dist/artifacts/frontendContractEvaluationArtifactReader.d.ts +17 -0
- package/dist/artifacts/frontendContractEvaluationArtifactReader.js +34 -0
- package/dist/artifacts/frontendContractEvaluationArtifactReader.js.map +1 -0
- package/dist/artifacts/frontendContractEvaluationArtifactWriter.d.ts +32 -0
- package/dist/artifacts/frontendContractEvaluationArtifactWriter.js +58 -0
- package/dist/artifacts/frontendContractEvaluationArtifactWriter.js.map +1 -0
- package/dist/artifacts/types.d.ts +17 -0
- package/dist/artifacts/types.js +2 -0
- package/dist/artifacts/types.js.map +1 -0
- package/dist/browser/chromiumAdapter.d.ts +21 -0
- package/dist/browser/chromiumAdapter.js +204 -0
- package/dist/browser/chromiumAdapter.js.map +1 -0
- package/dist/browser/evidenceCapture.d.ts +62 -0
- package/dist/browser/evidenceCapture.js +500 -0
- package/dist/browser/evidenceCapture.js.map +1 -0
- package/dist/browser/scrollCapture.d.ts +32 -0
- package/dist/browser/scrollCapture.js +163 -0
- package/dist/browser/scrollCapture.js.map +1 -0
- package/dist/browser/types.d.ts +24 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/types.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +2411 -0
- package/dist/cli.js.map +1 -0
- package/dist/domain/boundedAgentContext.d.ts +226 -0
- package/dist/domain/boundedAgentContext.js +355 -0
- package/dist/domain/boundedAgentContext.js.map +1 -0
- package/dist/domain/boundedAgentContextCorrelation.d.ts +74 -0
- package/dist/domain/boundedAgentContextCorrelation.js +441 -0
- package/dist/domain/boundedAgentContextCorrelation.js.map +1 -0
- package/dist/domain/boundedAgentContextIdentity.d.ts +26 -0
- package/dist/domain/boundedAgentContextIdentity.js +69 -0
- package/dist/domain/boundedAgentContextIdentity.js.map +1 -0
- package/dist/domain/boundedAgentContextProjection.d.ts +71 -0
- package/dist/domain/boundedAgentContextProjection.js +477 -0
- package/dist/domain/boundedAgentContextProjection.js.map +1 -0
- package/dist/domain/comparison.d.ts +220 -0
- package/dist/domain/comparison.js +350 -0
- package/dist/domain/comparison.js.map +1 -0
- package/dist/domain/comparisonEngine.d.ts +76 -0
- package/dist/domain/comparisonEngine.js +734 -0
- package/dist/domain/comparisonEngine.js.map +1 -0
- package/dist/domain/comparisonIdentity.d.ts +13 -0
- package/dist/domain/comparisonIdentity.js +46 -0
- package/dist/domain/comparisonIdentity.js.map +1 -0
- package/dist/domain/completion.d.ts +30 -0
- package/dist/domain/completion.js +22 -0
- package/dist/domain/completion.js.map +1 -0
- package/dist/domain/diagnostics.d.ts +17 -0
- package/dist/domain/diagnostics.js +77 -0
- package/dist/domain/diagnostics.js.map +1 -0
- package/dist/domain/evidence.d.ts +29 -0
- package/dist/domain/evidence.js +59 -0
- package/dist/domain/evidence.js.map +1 -0
- package/dist/domain/explicitState.d.ts +40 -0
- package/dist/domain/explicitState.js +54 -0
- package/dist/domain/explicitState.js.map +1 -0
- package/dist/domain/externalReference.d.ts +158 -0
- package/dist/domain/externalReference.js +167 -0
- package/dist/domain/externalReference.js.map +1 -0
- package/dist/domain/externalReferenceApplicability.d.ts +43 -0
- package/dist/domain/externalReferenceApplicability.js +52 -0
- package/dist/domain/externalReferenceApplicability.js.map +1 -0
- package/dist/domain/externalReferenceCompatibility.d.ts +40 -0
- package/dist/domain/externalReferenceCompatibility.js +48 -0
- package/dist/domain/externalReferenceCompatibility.js.map +1 -0
- package/dist/domain/externalReferenceFidelity.d.ts +171 -0
- package/dist/domain/externalReferenceFidelity.js +419 -0
- package/dist/domain/externalReferenceFidelity.js.map +1 -0
- package/dist/domain/externalReferenceIdentity.d.ts +38 -0
- package/dist/domain/externalReferenceIdentity.js +70 -0
- package/dist/domain/externalReferenceIdentity.js.map +1 -0
- package/dist/domain/externalReferenceImage.d.ts +35 -0
- package/dist/domain/externalReferenceImage.js +160 -0
- package/dist/domain/externalReferenceImage.js.map +1 -0
- package/dist/domain/externalReferenceRegionRelationships.d.ts +63 -0
- package/dist/domain/externalReferenceRegionRelationships.js +98 -0
- package/dist/domain/externalReferenceRegionRelationships.js.map +1 -0
- package/dist/domain/externalReferenceRegions.d.ts +65 -0
- package/dist/domain/externalReferenceRegions.js +105 -0
- package/dist/domain/externalReferenceRegions.js.map +1 -0
- package/dist/domain/externalReferenceRequirementIdentity.d.ts +12 -0
- package/dist/domain/externalReferenceRequirementIdentity.js +35 -0
- package/dist/domain/externalReferenceRequirementIdentity.js.map +1 -0
- package/dist/domain/externalReferenceRequirements.d.ts +215 -0
- package/dist/domain/externalReferenceRequirements.js +401 -0
- package/dist/domain/externalReferenceRequirements.js.map +1 -0
- package/dist/domain/externalReferenceRuntimeBinding.d.ts +146 -0
- package/dist/domain/externalReferenceRuntimeBinding.js +183 -0
- package/dist/domain/externalReferenceRuntimeBinding.js.map +1 -0
- package/dist/domain/frontendContractEvaluation.d.ts +57 -0
- package/dist/domain/frontendContractEvaluation.js +454 -0
- package/dist/domain/frontendContractEvaluation.js.map +1 -0
- package/dist/domain/frontendContractEvaluationArtifact.d.ts +65 -0
- package/dist/domain/frontendContractEvaluationArtifact.js +108 -0
- package/dist/domain/frontendContractEvaluationArtifact.js.map +1 -0
- package/dist/domain/frontendContractIdentity.d.ts +39 -0
- package/dist/domain/frontendContractIdentity.js +70 -0
- package/dist/domain/frontendContractIdentity.js.map +1 -0
- package/dist/domain/frontendContracts.d.ts +188 -0
- package/dist/domain/frontendContracts.js +260 -0
- package/dist/domain/frontendContracts.js.map +1 -0
- package/dist/domain/identity.d.ts +21 -0
- package/dist/domain/identity.js +47 -0
- package/dist/domain/identity.js.map +1 -0
- package/dist/domain/referenceCorrectionIdentity.d.ts +40 -0
- package/dist/domain/referenceCorrectionIdentity.js +77 -0
- package/dist/domain/referenceCorrectionIdentity.js.map +1 -0
- package/dist/domain/referenceCorrectionWorkflow.d.ts +160 -0
- package/dist/domain/referenceCorrectionWorkflow.js +165 -0
- package/dist/domain/referenceCorrectionWorkflow.js.map +1 -0
- package/dist/domain/referenceFidelityProjection.d.ts +65 -0
- package/dist/domain/referenceFidelityProjection.js +135 -0
- package/dist/domain/referenceFidelityProjection.js.map +1 -0
- package/dist/domain/relationships.d.ts +210 -0
- package/dist/domain/relationships.js +352 -0
- package/dist/domain/relationships.js.map +1 -0
- package/dist/domain/schema.d.ts +269 -0
- package/dist/domain/schema.js +442 -0
- package/dist/domain/schema.js.map +1 -0
- package/dist/domain/scrollEvidence.d.ts +51 -0
- package/dist/domain/scrollEvidence.js +134 -0
- package/dist/domain/scrollEvidence.js.map +1 -0
- package/dist/index.d.ts +114 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/projectWorkflow/aliasCatalog.d.ts +21 -0
- package/dist/projectWorkflow/aliasCatalog.js +67 -0
- package/dist/projectWorkflow/aliasCatalog.js.map +1 -0
- package/dist/projectWorkflow/checkAcceptance.d.ts +25 -0
- package/dist/projectWorkflow/checkAcceptance.js +55 -0
- package/dist/projectWorkflow/checkAcceptance.js.map +1 -0
- package/dist/projectWorkflow/checkResult.d.ts +85 -0
- package/dist/projectWorkflow/checkResult.js +101 -0
- package/dist/projectWorkflow/checkResult.js.map +1 -0
- package/dist/projectWorkflow/projectConfig.d.ts +43 -0
- package/dist/projectWorkflow/projectConfig.js +84 -0
- package/dist/projectWorkflow/projectConfig.js.map +1 -0
- package/dist/projectWorkflow/projectDiscovery.d.ts +9 -0
- package/dist/projectWorkflow/projectDiscovery.js +20 -0
- package/dist/projectWorkflow/projectDiscovery.js.map +1 -0
- package/dist/projectWorkflow/projectPaths.d.ts +8 -0
- package/dist/projectWorkflow/projectPaths.js +23 -0
- package/dist/projectWorkflow/projectPaths.js.map +1 -0
- package/dist/request/paths.d.ts +14 -0
- package/dist/request/paths.js +33 -0
- package/dist/request/paths.js.map +1 -0
- package/dist/request/request.d.ts +111 -0
- package/dist/request/request.js +464 -0
- package/dist/request/request.js.map +1 -0
- package/dist/safety/policy.d.ts +14 -0
- package/dist/safety/policy.js +81 -0
- package/dist/safety/policy.js.map +1 -0
- package/dist/viewer/assets/index-CN_yb9Uf.css +1 -0
- package/dist/viewer/assets/index-D98S1_2d.js +9 -0
- package/dist/viewer/icons/icon-192.png +0 -0
- package/dist/viewer/icons/icon-512.png +0 -0
- package/dist/viewer/index.html +15 -0
- package/dist/viewer/manifest.webmanifest +1 -0
- package/dist/viewer/registerSW.js +1 -0
- package/dist/viewer/sw.js +1 -0
- package/dist/viewer/workbox-9c191d2f.js +1 -0
- package/dist/viewerServer/context.d.ts +48 -0
- package/dist/viewerServer/context.js +60 -0
- package/dist/viewerServer/context.js.map +1 -0
- package/dist/viewerServer/evidence/classify.d.ts +59 -0
- package/dist/viewerServer/evidence/classify.js +124 -0
- package/dist/viewerServer/evidence/classify.js.map +1 -0
- package/dist/viewerServer/evidence/comparisonView.d.ts +28 -0
- package/dist/viewerServer/evidence/comparisonView.js +43 -0
- package/dist/viewerServer/evidence/comparisonView.js.map +1 -0
- package/dist/viewerServer/evidence/contextSourceView.d.ts +25 -0
- package/dist/viewerServer/evidence/contextSourceView.js +20 -0
- package/dist/viewerServer/evidence/contextSourceView.js.map +1 -0
- package/dist/viewerServer/evidence/discovery.d.ts +31 -0
- package/dist/viewerServer/evidence/discovery.js +78 -0
- package/dist/viewerServer/evidence/discovery.js.map +1 -0
- package/dist/viewerServer/evidence/evaluationView.d.ts +32 -0
- package/dist/viewerServer/evidence/evaluationView.js +50 -0
- package/dist/viewerServer/evidence/evaluationView.js.map +1 -0
- package/dist/viewerServer/evidence/handles.d.ts +21 -0
- package/dist/viewerServer/evidence/handles.js +43 -0
- package/dist/viewerServer/evidence/handles.js.map +1 -0
- package/dist/viewerServer/evidence/index.d.ts +41 -0
- package/dist/viewerServer/evidence/index.js +82 -0
- package/dist/viewerServer/evidence/index.js.map +1 -0
- package/dist/viewerServer/evidence/limits.d.ts +27 -0
- package/dist/viewerServer/evidence/limits.js +28 -0
- package/dist/viewerServer/evidence/limits.js.map +1 -0
- package/dist/viewerServer/evidence/linkedEvidence.d.ts +43 -0
- package/dist/viewerServer/evidence/linkedEvidence.js +151 -0
- package/dist/viewerServer/evidence/linkedEvidence.js.map +1 -0
- package/dist/viewerServer/evidence/mediaResolver.d.ts +16 -0
- package/dist/viewerServer/evidence/mediaResolver.js +85 -0
- package/dist/viewerServer/evidence/mediaResolver.js.map +1 -0
- package/dist/viewerServer/evidence/observationView.d.ts +29 -0
- package/dist/viewerServer/evidence/observationView.js +46 -0
- package/dist/viewerServer/evidence/observationView.js.map +1 -0
- package/dist/viewerServer/evidence/pathSafety.d.ts +11 -0
- package/dist/viewerServer/evidence/pathSafety.js +31 -0
- package/dist/viewerServer/evidence/pathSafety.js.map +1 -0
- package/dist/viewerServer/evidence/projection.d.ts +55 -0
- package/dist/viewerServer/evidence/projection.js +152 -0
- package/dist/viewerServer/evidence/projection.js.map +1 -0
- package/dist/viewerServer/evidence/referenceView.d.ts +133 -0
- package/dist/viewerServer/evidence/referenceView.js +169 -0
- package/dist/viewerServer/evidence/referenceView.js.map +1 -0
- package/dist/viewerServer/httpServer.d.ts +27 -0
- package/dist/viewerServer/httpServer.js +381 -0
- package/dist/viewerServer/httpServer.js.map +1 -0
- package/dist/viewerServer/openBrowser.d.ts +7 -0
- package/dist/viewerServer/openBrowser.js +32 -0
- package/dist/viewerServer/openBrowser.js.map +1 -0
- package/dist/viewerServer/port.d.ts +16 -0
- package/dist/viewerServer/port.js +19 -0
- package/dist/viewerServer/port.js.map +1 -0
- package/dist/viewerServer/viewerService.d.ts +62 -0
- package/dist/viewerServer/viewerService.js +88 -0
- package/dist/viewerServer/viewerService.js.map +1 -0
- package/docs/ARCHITECTURE.md +1286 -0
- package/docs/CI_CD.md +250 -0
- package/docs/COMMANDS.md +972 -0
- package/docs/CONTRACTS.md +1856 -0
- package/docs/CURRENT_STATE.md +1049 -0
- package/docs/DEVELOPMENT.md +202 -0
- package/docs/DOCUMENTATION_PRESERVATION_POLICY.md +50 -0
- package/docs/PROJECT_DESCRIPTION.md +2221 -0
- package/docs/PROJECT_MILESTONES.md +2526 -0
- package/docs/PROJECT_OVERVIEW.md +150 -0
- package/docs/QUICKSTART.md +83 -0
- package/docs/RELEASE.md +27 -0
- package/docs/ROADMAP.md +641 -0
- package/docs/SECURITY.md +218 -0
- package/docs/WORKFLOWS.md +642 -0
- package/docs/plans/v0.8-implementation-plan.md +655 -0
- package/docs/plans/v0.8.1-cli-usability-patch-plan.md +505 -0
- package/docs/reports/v0.7-bounded-fidelity-context-prompt7.md +243 -0
- package/docs/reports/v0.7-implementation-completeness-documentation-reconciliation.md +497 -0
- package/docs/reports/v0.7-pre-release-readiness.md +337 -0
- package/docs/reports/v0.7-reference-binding-prompt5.md +223 -0
- package/docs/reports/v0.7-reference-compatibility-prompt4.md +234 -0
- package/docs/reports/v0.7-reference-correction-workflow-prompt8.md +222 -0
- package/docs/reports/v0.7-reference-fidelity-prompt6.md +216 -0
- package/docs/reports/v0.7-reference-foundation-prompt1.md +151 -0
- package/docs/reports/v0.7-reference-regions-prompt2.md +195 -0
- package/docs/reports/v0.7-reference-requirements-prompt3.md +217 -0
- package/docs/reports/v0.7-release-prep.md +423 -0
- package/docs/reports/v0.8-binding-fidelity-interaction-batch6.md +279 -0
- package/docs/reports/v0.8-bounded-context-correlation-batch7.md +233 -0
- package/docs/reports/v0.8-comparison-contract-inspection-batch4.md +279 -0
- package/docs/reports/v0.8-evidence-index-readers-batch2.md +247 -0
- package/docs/reports/v0.8-implementation-completeness-documentation-reconciliation.md +741 -0
- package/docs/reports/v0.8-integrated-viewer-acceptance-batch8.md +128 -0
- package/docs/reports/v0.8-observation-svg-inspection-batch3.md +223 -0
- package/docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md +687 -0
- package/docs/reports/v0.8-reference-candidate-inspection-batch5.md +232 -0
- package/docs/reports/v0.8-viewer-runtime-pwa-batch1.md +278 -0
- package/docs/reports/v0.8.1-check-orchestration-prompt2.md +69 -0
- package/docs/reports/v0.8.1-implementation-completeness-documentation-reconciliation.md +114 -0
- package/docs/reports/v0.8.1-prerelease-readiness-cross-platform-security-code-rot.md +170 -0
- package/docs/reports/v0.8.1-project-workflow-foundation-prompt1.md +66 -0
- package/package.json +58 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Diagnostic } from '../domain/diagnostics.js';
|
|
2
|
+
import type { WriteFrontendContractArtifactOptions } from '../artifacts/frontendContractArtifactWriter.js';
|
|
3
|
+
export interface PersistFrontendContractOptions {
|
|
4
|
+
outputLocation: string;
|
|
5
|
+
cwd?: WriteFrontendContractArtifactOptions['cwd'];
|
|
6
|
+
}
|
|
7
|
+
export type ApplicationApproveBaselineResult = {
|
|
8
|
+
ok: true;
|
|
9
|
+
baselineId: string;
|
|
10
|
+
supersedesBaselineId?: string;
|
|
11
|
+
artifactRoot: string;
|
|
12
|
+
manifestPath: string;
|
|
13
|
+
clauseCount: number;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
diagnostics: Diagnostic[];
|
|
17
|
+
};
|
|
18
|
+
export type ApplicationPersistChangeContractResult = {
|
|
19
|
+
ok: true;
|
|
20
|
+
contractId: string;
|
|
21
|
+
artifactRoot: string;
|
|
22
|
+
manifestPath: string;
|
|
23
|
+
clauseCount: number;
|
|
24
|
+
supersedesBaselineClauseCount: number;
|
|
25
|
+
} | {
|
|
26
|
+
ok: false;
|
|
27
|
+
diagnostics: Diagnostic[];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The one canonical application-level baseline-approval use case: `approve-baseline`
|
|
31
|
+
* is the *only* place a baseline is ever approved (see governing scope) - this
|
|
32
|
+
* function is never called from `observe`/`compare`/`evaluate-contract`, and a
|
|
33
|
+
* `PASS` evaluation never triggers it. Validates the raw (still-`unknown`,
|
|
34
|
+
* freshly-parsed-JSON) baseline through the existing frozen
|
|
35
|
+
* `isValidPersistentBaselineContract` structural gate (never a second
|
|
36
|
+
* validator), reads the supplied observation through the existing
|
|
37
|
+
* `readObservationArtifact` reader, verifies the baseline's frozen
|
|
38
|
+
* `sourceObservation` reference actually corresponds to that observation's
|
|
39
|
+
* stable identity, and - only then - persists exactly once through the
|
|
40
|
+
* existing contract writer. Never mutates the observation. Never infers,
|
|
41
|
+
* creates, or removes `supersedesBaselineId` - whatever the caller already
|
|
42
|
+
* authored is preserved exactly.
|
|
43
|
+
*/
|
|
44
|
+
export declare function approveAndPersistBaseline(rawContract: unknown, observationRoot: string, options: PersistFrontendContractOptions): Promise<ApplicationApproveBaselineResult>;
|
|
45
|
+
/**
|
|
46
|
+
* The one canonical application-level per-change-contract persistence use
|
|
47
|
+
* case (`save-change-contract`) - persistence only, never approval
|
|
48
|
+
* terminology or semantics. Validates the raw contract through the existing
|
|
49
|
+
* frozen `isValidPerChangeContract` gate (which already rejects a
|
|
50
|
+
* `PersistentBaselineContract` passed here, an authored `category:
|
|
51
|
+
* "unexpected"`, and every other structural rule - never duplicated here),
|
|
52
|
+
* then persists exactly once. `supersedesBaselineClauseIds` is preserved
|
|
53
|
+
* exactly as authored; this function performs no baseline-reference
|
|
54
|
+
* resolution (that remains the evaluator's responsibility).
|
|
55
|
+
*/
|
|
56
|
+
export declare function persistPerChangeContract(rawContract: unknown, options: PersistFrontendContractOptions): Promise<ApplicationPersistChangeContractResult>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { isValidPersistentBaselineContract, isValidPerChangeContract } from '../domain/frontendContracts.js';
|
|
3
|
+
import { DIAGNOSTIC_SEVERITY } from '../domain/diagnostics.js';
|
|
4
|
+
import { normalizeOutputLocation } from '../request/paths.js';
|
|
5
|
+
import { writePersistentBaselineContract, writePerChangeContract } from '../artifacts/frontendContractArtifactWriter.js';
|
|
6
|
+
import { readObservationArtifact } from '../artifacts/artifactReader.js';
|
|
7
|
+
import { MANIFEST_FILENAME as OBSERVATION_MANIFEST_FILENAME } from '../artifacts/artifactWriter.js';
|
|
8
|
+
function invalidRequest(message) {
|
|
9
|
+
return { ok: false, diagnostics: [{ code: 'invalid-request', severity: DIAGNOSTIC_SEVERITY['invalid-request'], message }] };
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The one canonical application-level baseline-approval use case: `approve-baseline`
|
|
13
|
+
* is the *only* place a baseline is ever approved (see governing scope) - this
|
|
14
|
+
* function is never called from `observe`/`compare`/`evaluate-contract`, and a
|
|
15
|
+
* `PASS` evaluation never triggers it. Validates the raw (still-`unknown`,
|
|
16
|
+
* freshly-parsed-JSON) baseline through the existing frozen
|
|
17
|
+
* `isValidPersistentBaselineContract` structural gate (never a second
|
|
18
|
+
* validator), reads the supplied observation through the existing
|
|
19
|
+
* `readObservationArtifact` reader, verifies the baseline's frozen
|
|
20
|
+
* `sourceObservation` reference actually corresponds to that observation's
|
|
21
|
+
* stable identity, and - only then - persists exactly once through the
|
|
22
|
+
* existing contract writer. Never mutates the observation. Never infers,
|
|
23
|
+
* creates, or removes `supersedesBaselineId` - whatever the caller already
|
|
24
|
+
* authored is preserved exactly.
|
|
25
|
+
*/
|
|
26
|
+
export async function approveAndPersistBaseline(rawContract, observationRoot, options) {
|
|
27
|
+
const validation = isValidPersistentBaselineContract(rawContract);
|
|
28
|
+
if (!validation.valid)
|
|
29
|
+
return invalidRequest(`baseline contract is invalid: ${validation.reason}`);
|
|
30
|
+
const contract = rawContract;
|
|
31
|
+
const observationRead = await readObservationArtifact(path.join(observationRoot, OBSERVATION_MANIFEST_FILENAME));
|
|
32
|
+
if (!observationRead.ok)
|
|
33
|
+
return invalidRequest(`observation artifact: ${observationRead.reason}`);
|
|
34
|
+
const observation = observationRead.artifact;
|
|
35
|
+
const ref = contract.sourceObservation;
|
|
36
|
+
const matchesObservation = ref.observationId === observation.observationId &&
|
|
37
|
+
ref.requestId === observation.requestId &&
|
|
38
|
+
ref.producer.name === observation.producer.name &&
|
|
39
|
+
ref.producer.version === observation.producer.version &&
|
|
40
|
+
ref.observationSchemaVersion === observation.schemaVersion;
|
|
41
|
+
if (!matchesObservation) {
|
|
42
|
+
return invalidRequest('baseline contract sourceObservation does not match the supplied observation artifact; refusing to approve an unrelated observation');
|
|
43
|
+
}
|
|
44
|
+
const normalized = normalizeOutputLocation(options.outputLocation);
|
|
45
|
+
if (!normalized.ok)
|
|
46
|
+
return { ok: false, diagnostics: [normalized.diagnostic] };
|
|
47
|
+
const persisted = await writePersistentBaselineContract(contract, normalized.value, options.cwd === undefined ? {} : { cwd: options.cwd });
|
|
48
|
+
if (!persisted.ok)
|
|
49
|
+
return { ok: false, diagnostics: persisted.diagnostics };
|
|
50
|
+
return {
|
|
51
|
+
ok: true,
|
|
52
|
+
baselineId: contract.baselineId,
|
|
53
|
+
...(contract.supersedesBaselineId === undefined ? {} : { supersedesBaselineId: contract.supersedesBaselineId }),
|
|
54
|
+
artifactRoot: persisted.artifactRoot,
|
|
55
|
+
manifestPath: persisted.manifestPath,
|
|
56
|
+
clauseCount: contract.clauses.length,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The one canonical application-level per-change-contract persistence use
|
|
61
|
+
* case (`save-change-contract`) - persistence only, never approval
|
|
62
|
+
* terminology or semantics. Validates the raw contract through the existing
|
|
63
|
+
* frozen `isValidPerChangeContract` gate (which already rejects a
|
|
64
|
+
* `PersistentBaselineContract` passed here, an authored `category:
|
|
65
|
+
* "unexpected"`, and every other structural rule - never duplicated here),
|
|
66
|
+
* then persists exactly once. `supersedesBaselineClauseIds` is preserved
|
|
67
|
+
* exactly as authored; this function performs no baseline-reference
|
|
68
|
+
* resolution (that remains the evaluator's responsibility).
|
|
69
|
+
*/
|
|
70
|
+
export async function persistPerChangeContract(rawContract, options) {
|
|
71
|
+
const validation = isValidPerChangeContract(rawContract);
|
|
72
|
+
if (!validation.valid)
|
|
73
|
+
return invalidRequest(`per-change contract is invalid: ${validation.reason}`);
|
|
74
|
+
const contract = rawContract;
|
|
75
|
+
const normalized = normalizeOutputLocation(options.outputLocation);
|
|
76
|
+
if (!normalized.ok)
|
|
77
|
+
return { ok: false, diagnostics: [normalized.diagnostic] };
|
|
78
|
+
const persisted = await writePerChangeContract(contract, normalized.value, options.cwd === undefined ? {} : { cwd: options.cwd });
|
|
79
|
+
if (!persisted.ok)
|
|
80
|
+
return { ok: false, diagnostics: persisted.diagnostics };
|
|
81
|
+
const supersedesBaselineClauseCount = contract.clauses.reduce((count, clause) => count + (clause.supersedesBaselineClauseIds?.length ?? 0), 0);
|
|
82
|
+
return {
|
|
83
|
+
ok: true,
|
|
84
|
+
contractId: contract.contractId,
|
|
85
|
+
artifactRoot: persisted.artifactRoot,
|
|
86
|
+
manifestPath: persisted.manifestPath,
|
|
87
|
+
clauseCount: contract.clauses.length,
|
|
88
|
+
supersedesBaselineClauseCount,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=frontendContractPersistenceService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontendContractPersistenceService.js","sourceRoot":"","sources":["../../src/application/frontendContractPersistenceService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { NormalizedObservationRequest } from '../request/request.js';
|
|
2
|
+
import type { BrowserCaptureResult } from '../browser/types.js';
|
|
3
|
+
import type { ObservationArtifact } from '../domain/schema.js';
|
|
4
|
+
import type { CompletionState } from '../domain/completion.js';
|
|
5
|
+
import type { Diagnostic } from '../domain/diagnostics.js';
|
|
6
|
+
import type { PersistedObservationResult } from '../artifacts/types.js';
|
|
7
|
+
import type { WriteObservationArtifactOptions } from '../artifacts/artifactWriter.js';
|
|
8
|
+
export type { PersistedObservationResult } from '../artifacts/types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Assembles the frozen ObservationArtifact shape from one already-successful
|
|
11
|
+
* Batch 2/3 browser capture. Pure - no filesystem access, no Playwright
|
|
12
|
+
* dependency - so it is testable independently of both Chromium and disk
|
|
13
|
+
* I/O. `requestId`/`observationId` reuse the existing Batch 1 identity
|
|
14
|
+
* functions verbatim; `completion` reuses the existing Batch 1
|
|
15
|
+
* post-capture completion rule over the capture's own diagnostics.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildObservationArtifact(capture: Extract<BrowserCaptureResult, {
|
|
18
|
+
ok: true;
|
|
19
|
+
}>, request: NormalizedObservationRequest): ObservationArtifact;
|
|
20
|
+
/**
|
|
21
|
+
* Minimum application-facing persistence seam for Batch 5: combines an
|
|
22
|
+
* existing successful browser capture with the artifact writer. Not wired to
|
|
23
|
+
* the CLI in this batch.
|
|
24
|
+
*/
|
|
25
|
+
export declare function persistBrowserCapture(capture: Extract<BrowserCaptureResult, {
|
|
26
|
+
ok: true;
|
|
27
|
+
}>, request: NormalizedObservationRequest, options?: WriteObservationArtifactOptions): Promise<PersistedObservationResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Observer-owned summary of one full observation attempt, with just enough
|
|
30
|
+
* information for a presentation layer (the Batch 5 CLI) to report a result
|
|
31
|
+
* without ever seeing a Playwright object, a raw Node filesystem exception,
|
|
32
|
+
* or persistence implementation detail.
|
|
33
|
+
*/
|
|
34
|
+
export type ApplicationObservationResult = {
|
|
35
|
+
ok: true;
|
|
36
|
+
observationId: string;
|
|
37
|
+
requestId: string;
|
|
38
|
+
completion: CompletionState;
|
|
39
|
+
artifactRoot: string;
|
|
40
|
+
manifestPath: string;
|
|
41
|
+
screenshotPath: string;
|
|
42
|
+
targetCount: number;
|
|
43
|
+
diagnostics: Diagnostic[];
|
|
44
|
+
} | {
|
|
45
|
+
ok: false;
|
|
46
|
+
diagnostics: Diagnostic[];
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The one canonical application-level observation use case: takes an
|
|
50
|
+
* already-normalized request, runs the existing browser capture exactly
|
|
51
|
+
* once, and - only for a successful capture - persists it exactly once
|
|
52
|
+
* through the existing artifact writer. Reuses `runBrowserCapture`,
|
|
53
|
+
* `buildObservationArtifact`, and `writeObservationArtifact` verbatim; it
|
|
54
|
+
* does not go through `persistBrowserCapture` because that helper re-derives
|
|
55
|
+
* a fresh `ObservationArtifact` (and therefore a fresh, different
|
|
56
|
+
* `observationId`) internally, which would desync the identity a caller
|
|
57
|
+
* inspects here from the identity actually written to disk.
|
|
58
|
+
*/
|
|
59
|
+
export declare function observe(request: NormalizedObservationRequest, options?: WriteObservationArtifactOptions): Promise<ApplicationObservationResult>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { runBrowserCapture } from './browserCaptureService.js';
|
|
2
|
+
import { ARTIFACT_KIND, SCHEMA_VERSION, getProducerInfo } from '../domain/schema.js';
|
|
3
|
+
import { buildRequestIdentity, buildObservationIdentity } from '../domain/identity.js';
|
|
4
|
+
import { deriveCompletion } from '../domain/completion.js';
|
|
5
|
+
import { writeObservationArtifact, SCREENSHOT_FILENAME } from '../artifacts/artifactWriter.js';
|
|
6
|
+
/**
|
|
7
|
+
* Assembles the frozen ObservationArtifact shape from one already-successful
|
|
8
|
+
* Batch 2/3 browser capture. Pure - no filesystem access, no Playwright
|
|
9
|
+
* dependency - so it is testable independently of both Chromium and disk
|
|
10
|
+
* I/O. `requestId`/`observationId` reuse the existing Batch 1 identity
|
|
11
|
+
* functions verbatim; `completion` reuses the existing Batch 1
|
|
12
|
+
* post-capture completion rule over the capture's own diagnostics.
|
|
13
|
+
*/
|
|
14
|
+
export function buildObservationArtifact(capture, request) {
|
|
15
|
+
const requestId = buildRequestIdentity(request);
|
|
16
|
+
const observationId = buildObservationIdentity(requestId);
|
|
17
|
+
const completion = deriveCompletion(capture.diagnostics, 'post-capture');
|
|
18
|
+
return {
|
|
19
|
+
artifactKind: ARTIFACT_KIND,
|
|
20
|
+
schemaVersion: SCHEMA_VERSION,
|
|
21
|
+
observationId,
|
|
22
|
+
requestId,
|
|
23
|
+
producer: getProducerInfo(),
|
|
24
|
+
browser: { state: 'available', source: 'browser', value: capture.provenance },
|
|
25
|
+
requestConfig: request,
|
|
26
|
+
provenance: { capturedAt: new Date().toISOString(), observationMethod: 'chromium-navigate-and-capture' },
|
|
27
|
+
pageEvidence: capture.pageEvidence,
|
|
28
|
+
targetEvidence: capture.targetEvidence,
|
|
29
|
+
...(capture.scrollScenarioEvidence ? { scrollScenarioEvidence: capture.scrollScenarioEvidence } : {}),
|
|
30
|
+
screenshot: { state: 'available', source: 'browser', value: { path: SCREENSHOT_FILENAME } },
|
|
31
|
+
completion,
|
|
32
|
+
diagnostics: capture.diagnostics,
|
|
33
|
+
limits: { truncated: false, omittedFields: [], omittedTargets: [] },
|
|
34
|
+
artifactReferences: [{ path: SCREENSHOT_FILENAME, kind: 'screenshot' }],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Minimum application-facing persistence seam for Batch 5: combines an
|
|
39
|
+
* existing successful browser capture with the artifact writer. Not wired to
|
|
40
|
+
* the CLI in this batch.
|
|
41
|
+
*/
|
|
42
|
+
export async function persistBrowserCapture(capture, request, options = {}) {
|
|
43
|
+
const artifact = buildObservationArtifact(capture, request);
|
|
44
|
+
return writeObservationArtifact(artifact, capture.screenshot, options);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The one canonical application-level observation use case: takes an
|
|
48
|
+
* already-normalized request, runs the existing browser capture exactly
|
|
49
|
+
* once, and - only for a successful capture - persists it exactly once
|
|
50
|
+
* through the existing artifact writer. Reuses `runBrowserCapture`,
|
|
51
|
+
* `buildObservationArtifact`, and `writeObservationArtifact` verbatim; it
|
|
52
|
+
* does not go through `persistBrowserCapture` because that helper re-derives
|
|
53
|
+
* a fresh `ObservationArtifact` (and therefore a fresh, different
|
|
54
|
+
* `observationId`) internally, which would desync the identity a caller
|
|
55
|
+
* inspects here from the identity actually written to disk.
|
|
56
|
+
*/
|
|
57
|
+
export async function observe(request, options = {}) {
|
|
58
|
+
const capture = await runBrowserCapture(request);
|
|
59
|
+
if (!capture.ok) {
|
|
60
|
+
return { ok: false, diagnostics: capture.diagnostics };
|
|
61
|
+
}
|
|
62
|
+
const artifact = buildObservationArtifact(capture, request);
|
|
63
|
+
const persisted = await writeObservationArtifact(artifact, capture.screenshot, options);
|
|
64
|
+
if (!persisted.ok) {
|
|
65
|
+
return { ok: false, diagnostics: persisted.diagnostics };
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
ok: true,
|
|
69
|
+
observationId: artifact.observationId,
|
|
70
|
+
requestId: artifact.requestId,
|
|
71
|
+
completion: artifact.completion,
|
|
72
|
+
artifactRoot: persisted.artifactRoot,
|
|
73
|
+
manifestPath: persisted.manifestPath,
|
|
74
|
+
screenshotPath: persisted.screenshotPath,
|
|
75
|
+
targetCount: Object.keys(artifact.targetEvidence).length,
|
|
76
|
+
diagnostics: artifact.diagnostics,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=observationPersistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observationPersistence.js","sourceRoot":"","sources":["../../src/application/observationPersistence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type CheckWorkflowResult } from '../projectWorkflow/checkResult.js';
|
|
2
|
+
/** Compose existing canonical capture/comparison/contract/reference owners into one project workflow. */
|
|
3
|
+
export declare function checkProject(projectRoot: string, requestedBaseline?: string): Promise<CheckWorkflowResult>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { readProjectConfig } from '../projectWorkflow/projectConfig.js';
|
|
3
|
+
import { aliasCatalogPath, projectConfigPath, projectEvidenceRoot } from '../projectWorkflow/projectPaths.js';
|
|
4
|
+
import { readAliasCatalog } from '../projectWorkflow/aliasCatalog.js';
|
|
5
|
+
import { loadBindingsFile, loadComparisonConfigFile, resolveContainedAcceptancePath } from '../projectWorkflow/checkAcceptance.js';
|
|
6
|
+
import { readObservationArtifact } from '../artifacts/artifactReader.js';
|
|
7
|
+
import { readFrontendContractEvaluationArtifact } from '../artifacts/frontendContractEvaluationArtifactReader.js';
|
|
8
|
+
import { readPersistentBaselineContract, readPerChangeContract } from '../artifacts/frontendContractArtifactReader.js';
|
|
9
|
+
import { readExternalReferenceArtifact } from '../artifacts/externalReferenceArtifactReader.js';
|
|
10
|
+
import { compareAndPersistFromArtifactRoots } from './comparisonService.js';
|
|
11
|
+
import { evaluateAndPersistFromArtifactRoots } from './frontendContractEvaluationService.js';
|
|
12
|
+
import { evaluateReferenceCandidateFidelityFromArtifactRoots } from './referenceFidelityEvaluationService.js';
|
|
13
|
+
import { captureCurrentObservation } from './projectWorkflowService.js';
|
|
14
|
+
import { emptyCheckResult, finalizeCheckStatus, projectContractResult, projectReferenceResult, } from '../projectWorkflow/checkResult.js';
|
|
15
|
+
function projectRelative(projectRoot, absolutePath) {
|
|
16
|
+
return path.relative(projectRoot, absolutePath).split(path.sep).join('/');
|
|
17
|
+
}
|
|
18
|
+
function addBlocker(result, code, message) {
|
|
19
|
+
result.blockers.push({ code, message });
|
|
20
|
+
}
|
|
21
|
+
async function prepareAcceptance(projectRoot, config) {
|
|
22
|
+
const prepared = {};
|
|
23
|
+
if (config.comparisonConfigFile !== undefined) {
|
|
24
|
+
const resolved = resolveContainedAcceptancePath(projectRoot, config.comparisonConfigFile);
|
|
25
|
+
if (!resolved.ok)
|
|
26
|
+
return { ok: false, code: 'acceptance-config-invalid', message: resolved.error };
|
|
27
|
+
const loaded = loadComparisonConfigFile(resolved.path);
|
|
28
|
+
if (!loaded.ok)
|
|
29
|
+
return { ok: false, code: 'acceptance-config-invalid', message: loaded.error };
|
|
30
|
+
prepared.comparisonConfig = loaded.config;
|
|
31
|
+
}
|
|
32
|
+
if (config.contract !== undefined) {
|
|
33
|
+
const baselineRoot = resolveContainedAcceptancePath(projectRoot, config.contract.baselineArtifact);
|
|
34
|
+
const changeRoot = resolveContainedAcceptancePath(projectRoot, config.contract.changeArtifact);
|
|
35
|
+
if (!baselineRoot.ok || !changeRoot.ok)
|
|
36
|
+
return { ok: false, code: 'acceptance-config-invalid', message: 'configured contract artifact path is invalid or outside the project' };
|
|
37
|
+
const baseline = await readPersistentBaselineContract(path.join(baselineRoot.path, 'manifest.json'));
|
|
38
|
+
const change = await readPerChangeContract(path.join(changeRoot.path, 'manifest.json'));
|
|
39
|
+
if (!baseline.ok || !change.ok)
|
|
40
|
+
return { ok: false, code: 'acceptance-config-invalid', message: 'configured contract artifact is invalid' };
|
|
41
|
+
prepared.contract = { baselineRoot: baselineRoot.path, changeRoot: changeRoot.path, baseline: baseline.contract, change: change.contract };
|
|
42
|
+
}
|
|
43
|
+
if (config.reference !== undefined) {
|
|
44
|
+
const referenceRoot = resolveContainedAcceptancePath(projectRoot, config.reference.approvedArtifact);
|
|
45
|
+
if (!referenceRoot.ok)
|
|
46
|
+
return { ok: false, code: 'acceptance-config-invalid', message: referenceRoot.error };
|
|
47
|
+
const reference = await readExternalReferenceArtifact(path.join(referenceRoot.path, 'manifest.json'));
|
|
48
|
+
if (!reference.ok)
|
|
49
|
+
return { ok: false, code: 'acceptance-config-invalid', message: 'configured reference artifact is invalid' };
|
|
50
|
+
if (reference.artifact.lifecycle.state !== 'approved')
|
|
51
|
+
return { ok: false, code: 'reference-not-approved', message: 'configured reference is not approved' };
|
|
52
|
+
let bindings = [];
|
|
53
|
+
if (config.reference.bindingsFile !== undefined) {
|
|
54
|
+
const bindingsPath = resolveContainedAcceptancePath(projectRoot, config.reference.bindingsFile);
|
|
55
|
+
if (!bindingsPath.ok)
|
|
56
|
+
return { ok: false, code: 'acceptance-config-invalid', message: bindingsPath.error };
|
|
57
|
+
const loaded = loadBindingsFile(bindingsPath.path);
|
|
58
|
+
if (!loaded.ok)
|
|
59
|
+
return { ok: false, code: 'acceptance-config-invalid', message: loaded.error };
|
|
60
|
+
bindings = loaded.bindings;
|
|
61
|
+
}
|
|
62
|
+
prepared.reference = { root: referenceRoot.path, bindings: bindings };
|
|
63
|
+
}
|
|
64
|
+
return { ok: true, value: prepared };
|
|
65
|
+
}
|
|
66
|
+
/** Compose existing canonical capture/comparison/contract/reference owners into one project workflow. */
|
|
67
|
+
export async function checkProject(projectRoot, requestedBaseline) {
|
|
68
|
+
const result = emptyCheckResult();
|
|
69
|
+
const configRead = await readProjectConfig(projectConfigPath(projectRoot));
|
|
70
|
+
if (!configRead.ok) {
|
|
71
|
+
addBlocker(result, 'acceptance-config-invalid', 'project configuration is invalid');
|
|
72
|
+
return finalizeCheckStatus(result);
|
|
73
|
+
}
|
|
74
|
+
const config = configRead.config;
|
|
75
|
+
result.contract.configured = config.acceptance?.contract !== undefined;
|
|
76
|
+
result.contract.state = result.contract.configured ? 'NOT_RUN' : 'NOT_CONFIGURED';
|
|
77
|
+
result.reference.configured = config.acceptance?.reference !== undefined;
|
|
78
|
+
result.reference.state = result.reference.configured ? 'NOT_RUN' : 'NOT_CONFIGURED';
|
|
79
|
+
const catalogRead = await readAliasCatalog(aliasCatalogPath(projectRoot));
|
|
80
|
+
if (!catalogRead.ok) {
|
|
81
|
+
addBlocker(result, 'baseline-artifact-invalid', 'project alias catalog is invalid');
|
|
82
|
+
return finalizeCheckStatus(result);
|
|
83
|
+
}
|
|
84
|
+
const baselineAlias = requestedBaseline ?? config.defaultBaseline;
|
|
85
|
+
const baselineRecord = catalogRead.catalog.observations[baselineAlias];
|
|
86
|
+
if (baselineRecord === undefined) {
|
|
87
|
+
addBlocker(result, 'baseline-alias-not-found', `baseline alias "${baselineAlias}" was not found`);
|
|
88
|
+
return finalizeCheckStatus(result);
|
|
89
|
+
}
|
|
90
|
+
const evidenceRoot = projectEvidenceRoot(projectRoot);
|
|
91
|
+
const baselineRoot = path.join(evidenceRoot, ...baselineRecord.relativeArtifactDir.split('/'));
|
|
92
|
+
const baselineRead = await readObservationArtifact(path.join(baselineRoot, 'manifest.json'));
|
|
93
|
+
if (!baselineRead.ok || baselineRead.artifact.observationId !== baselineRecord.observationId || baselineRead.artifact.requestId !== baselineRecord.requestId) {
|
|
94
|
+
addBlocker(result, 'baseline-artifact-invalid', 'baseline alias does not resolve to the declared canonical observation identity');
|
|
95
|
+
return finalizeCheckStatus(result);
|
|
96
|
+
}
|
|
97
|
+
result.baseline = { alias: baselineAlias, observationId: baselineRecord.observationId, requestId: baselineRecord.requestId, artifactPath: projectRelative(projectRoot, baselineRoot) };
|
|
98
|
+
const acceptance = config.acceptance === undefined ? { ok: true, value: {} } : await prepareAcceptance(projectRoot, config.acceptance);
|
|
99
|
+
if (!acceptance.ok) {
|
|
100
|
+
addBlocker(result, acceptance.code, acceptance.message);
|
|
101
|
+
if (acceptance.code === 'reference-not-approved')
|
|
102
|
+
result.reference.state = 'BLOCKED';
|
|
103
|
+
return finalizeCheckStatus(result);
|
|
104
|
+
}
|
|
105
|
+
const captured = await captureCurrentObservation(projectRoot);
|
|
106
|
+
if (!captured.ok) {
|
|
107
|
+
addBlocker(result, 'candidate-capture-failed', 'current candidate capture failed; the previous current alias was preserved');
|
|
108
|
+
return finalizeCheckStatus(result);
|
|
109
|
+
}
|
|
110
|
+
result.candidate = { alias: 'current', observationId: captured.observationId, requestId: captured.requestId, artifactPath: projectRelative(projectRoot, captured.artifactRoot) };
|
|
111
|
+
const compared = await compareAndPersistFromArtifactRoots(baselineRoot, captured.artifactRoot, {
|
|
112
|
+
...(acceptance.value.comparisonConfig === undefined ? {} : { config: acceptance.value.comparisonConfig }),
|
|
113
|
+
outputLocation: `.frontend-observer/evidence/comparisons/${baselineAlias}-vs-current`,
|
|
114
|
+
cwd: projectRoot,
|
|
115
|
+
});
|
|
116
|
+
if (!compared.ok) {
|
|
117
|
+
result.comparison = { state: 'BLOCKED' };
|
|
118
|
+
addBlocker(result, 'comparison-failed', 'canonical comparison could not be constructed');
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
result.comparison = {
|
|
122
|
+
state: compared.comparability,
|
|
123
|
+
comparisonId: compared.comparisonId,
|
|
124
|
+
artifactPath: projectRelative(projectRoot, compared.artifactRoot),
|
|
125
|
+
differenceCount: compared.differenceCount,
|
|
126
|
+
relationshipChangeCount: compared.relationshipChangeCount,
|
|
127
|
+
diagnosticsCount: compared.diagnosticsCount,
|
|
128
|
+
};
|
|
129
|
+
if (compared.comparability === 'incomparable')
|
|
130
|
+
addBlocker(result, 'comparison-incomparable', 'canonical comparison is incomparable');
|
|
131
|
+
if (acceptance.value.contract !== undefined && compared.comparability !== 'incomparable') {
|
|
132
|
+
const contract = acceptance.value.contract;
|
|
133
|
+
const evaluated = await evaluateAndPersistFromArtifactRoots(baselineRoot, captured.artifactRoot, compared.artifactRoot, contract.baselineRoot, contract.changeRoot, { outputLocation: `.frontend-observer/evidence/evaluations/${baselineAlias}-vs-current`, cwd: projectRoot });
|
|
134
|
+
if (!evaluated.ok) {
|
|
135
|
+
result.contract.state = 'BLOCKED';
|
|
136
|
+
addBlocker(result, 'contract-evaluation-failed', 'canonical frontend-contract evaluation could not be constructed');
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const persisted = await readFrontendContractEvaluationArtifact(evaluated.manifestPath);
|
|
140
|
+
if (!persisted.ok) {
|
|
141
|
+
result.contract.state = 'BLOCKED';
|
|
142
|
+
addBlocker(result, 'contract-evaluation-failed', 'persisted frontend-contract evaluation is invalid');
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
result.contract = projectContractResult(persisted.artifact, contract.baseline.clauses, contract.change.clauses, projectRelative(projectRoot, evaluated.artifactRoot));
|
|
146
|
+
if (result.contract.state === 'BLOCKED')
|
|
147
|
+
addBlocker(result, 'contract-evidence-unavailable', 'contract evidence is unavailable or conflicting');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (acceptance.value.reference !== undefined) {
|
|
153
|
+
const reference = acceptance.value.reference;
|
|
154
|
+
const evaluated = await evaluateReferenceCandidateFidelityFromArtifactRoots(reference.root, captured.artifactRoot, reference.bindings);
|
|
155
|
+
if (!evaluated.ok) {
|
|
156
|
+
result.reference.state = 'BLOCKED';
|
|
157
|
+
addBlocker(result, 'reference-evaluation-failed', 'canonical reference-fidelity evaluation could not be constructed');
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
result.reference = projectReferenceResult(evaluated.evaluation);
|
|
161
|
+
if (evaluated.evaluation.state === 'not-evaluated')
|
|
162
|
+
addBlocker(result, 'reference-not-evaluated', 'reference fidelity was not evaluated');
|
|
163
|
+
else if (result.reference.unavailableRequirements.length > 0)
|
|
164
|
+
addBlocker(result, 'reference-evidence-unavailable', 'required reference evidence is unavailable');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return finalizeCheckStatus(result);
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=projectCheckService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projectCheckService.js","sourceRoot":"","sources":["../../src/application/projectCheckService.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAExE,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9G,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,8BAA8B,EAAE,MAAM,uCAAuC,CAAC;AACnI,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,sCAAsC,EAAE,MAAM,0DAA0D,CAAC;AAClH,OAAO,EAAE,8BAA8B,EAAE,qBAAqB,EAAE,MAAM,gDAAgD,CAAC;AACvH,OAAO,EAAE,6BAA6B,EAAE,MAAM,iDAAiD,CAAC;AAChG,OAAO,EAAE,kCAAkC,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,EAAE,mCAAmC,EAAE,MAAM,wCAAwC,CAAC;AAC7F,OAAO,EAAE,mDAAmD,EAAE,MAAM,yCAAyC,CAAC;AAC9G,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,GAEvB,MAAM,mCAAmC,CAAC;AAK3C,SAAS,eAAe,CAAC,WAAmB,EAAE,YAAoB;IAChE,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,UAAU,CAAC,MAA2B,EAAE,IAAY,EAAE,OAAe;IAC5E,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC1C,CAAC;AAQD,KAAK,UAAU,iBAAiB,CAAC,WAAmB,EAAE,MAAwC;IAC5F,MAAM,QAAQ,GAAuB,EAAE,CAAC;IACxC,IAAI,MAAM,CAAC,oBAAoB,KAAK,SAAS,EAAE,CAAC;QAC9C,MAAM,QAAQ,GAAG,8BAA8B,CAAC,WAAW,EAAE,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC1F,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnG,MAAM,MAAM,GAAG,wBAAwB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QAC/F,QAAQ,CAAC,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5C,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,YAAY,GAAG,8BAA8B,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACnG,MAAM,UAAU,GAAG,8BAA8B,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;QAC/F,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,qEAAqE,EAAE,CAAC;QAChL,MAAM,QAAQ,GAAG,MAAM,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;QACrG,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;QACxF,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,yCAAyC,EAAE,CAAC;QAC5I,QAAQ,CAAC,QAAQ,GAAG,EAAE,YAAY,EAAE,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC7I,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACnC,MAAM,aAAa,GAAG,8BAA8B,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACrG,IAAI,CAAC,aAAa,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,aAAa,CAAC,KAAK,EAAE,CAAC;QAC7G,MAAM,SAAS,GAAG,MAAM,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;QACtG,IAAI,CAAC,SAAS,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,0CAA0C,EAAE,CAAC;QAChI,IAAI,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,KAAK,UAAU;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,sCAAsC,EAAE,CAAC;QAC7J,IAAI,QAAQ,GAAY,EAAE,CAAC;QAC3B,IAAI,MAAM,CAAC,SAAS,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YAChD,MAAM,YAAY,GAAG,8BAA8B,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;YAChG,IAAI,CAAC,YAAY,CAAC,EAAE;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC;YAC3G,MAAM,MAAM,GAAG,gBAAgB,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACnD,IAAI,CAAC,MAAM,CAAC,EAAE;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;YAC/F,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC7B,CAAC;QACD,QAAQ,CAAC,SAAS,GAAG,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAgD,EAAE,CAAC;IAChH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AACvC,CAAC;AAED,yGAAyG;AACzG,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAmB,EAAE,iBAA0B;IAChF,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;IAC3E,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;QACnB,UAAU,CAAC,MAAM,EAAE,2BAA2B,EAAE,kCAAkC,CAAC,CAAC;QACpF,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IACjC,MAAM,CAAC,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,EAAE,QAAQ,KAAK,SAAS,CAAC;IACvE,MAAM,CAAC,QAAQ,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAClF,MAAM,CAAC,SAAS,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,EAAE,SAAS,KAAK,SAAS,CAAC;IACzE,MAAM,CAAC,SAAS,CAAC,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAEpF,MAAM,WAAW,GAAG,MAAM,gBAAgB,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1E,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC;QACpB,UAAU,CAAC,MAAM,EAAE,2BAA2B,EAAE,kCAAkC,CAAC,CAAC;QACpF,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,aAAa,GAAG,iBAAiB,IAAI,MAAM,CAAC,eAAe,CAAC;IAClE,MAAM,cAAc,GAAG,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;IACvE,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,UAAU,CAAC,MAAM,EAAE,0BAA0B,EAAE,mBAAmB,aAAa,iBAAiB,CAAC,CAAC;QAClG,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,YAAY,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,cAAc,CAAC,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/F,MAAM,YAAY,GAAG,MAAM,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC;IAC7F,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,YAAY,CAAC,QAAQ,CAAC,aAAa,KAAK,cAAc,CAAC,aAAa,IAAI,YAAY,CAAC,QAAQ,CAAC,SAAS,KAAK,cAAc,CAAC,SAAS,EAAE,CAAC;QAC7J,UAAU,CAAC,MAAM,EAAE,2BAA2B,EAAE,gFAAgF,CAAC,CAAC;QAClI,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,CAAC,QAAQ,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,CAAC,aAAa,EAAE,SAAS,EAAE,cAAc,CAAC,SAAS,EAAE,YAAY,EAAE,eAAe,CAAC,WAAW,EAAE,YAAY,CAAC,EAAE,CAAC;IAEvL,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAa,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAiB,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAChJ,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;QACnB,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;QACxD,IAAI,UAAU,CAAC,IAAI,KAAK,wBAAwB;YAAE,MAAM,CAAC,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC;QACrF,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,yBAAyB,CAAC,WAAW,CAAC,CAAC;IAC9D,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,UAAU,CAAC,MAAM,EAAE,0BAA0B,EAAE,4EAA4E,CAAC,CAAC;QAC7H,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,CAAC,SAAS,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,CAAC,aAAa,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,YAAY,EAAE,eAAe,CAAC,WAAW,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;IAEjL,MAAM,QAAQ,GAAG,MAAM,kCAAkC,CAAC,YAAY,EAAE,QAAQ,CAAC,YAAY,EAAE;QAC7F,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC;QACzG,cAAc,EAAE,2CAA2C,aAAa,aAAa;QACrF,GAAG,EAAE,WAAW;KACjB,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,CAAC,UAAU,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QACzC,UAAU,CAAC,MAAM,EAAE,mBAAmB,EAAE,+CAA+C,CAAC,CAAC;IAC3F,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,UAAU,GAAG;YAClB,KAAK,EAAE,QAAQ,CAAC,aAAa;YAC7B,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,YAAY,EAAE,eAAe,CAAC,WAAW,EAAE,QAAQ,CAAC,YAAY,CAAC;YACjE,eAAe,EAAE,QAAQ,CAAC,eAAe;YACzC,uBAAuB,EAAE,QAAQ,CAAC,uBAAuB;YACzD,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;SAC5C,CAAC;QACF,IAAI,QAAQ,CAAC,aAAa,KAAK,cAAc;YAAE,UAAU,CAAC,MAAM,EAAE,yBAAyB,EAAE,sCAAsC,CAAC,CAAC;QAErI,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,aAAa,KAAK,cAAc,EAAE,CAAC;YACzF,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC3C,MAAM,SAAS,GAAG,MAAM,mCAAmC,CACzD,YAAY,EACZ,QAAQ,CAAC,YAAY,EACrB,QAAQ,CAAC,YAAY,EACrB,QAAQ,CAAC,YAAY,EACrB,QAAQ,CAAC,UAAU,EACnB,EAAE,cAAc,EAAE,2CAA2C,aAAa,aAAa,EAAE,GAAG,EAAE,WAAW,EAAE,CAC5G,CAAC;YACF,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;gBAClB,MAAM,CAAC,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAC;gBAClC,UAAU,CAAC,MAAM,EAAE,4BAA4B,EAAE,iEAAiE,CAAC,CAAC;YACtH,CAAC;iBAAM,CAAC;gBACN,MAAM,SAAS,GAAG,MAAM,sCAAsC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;gBACvF,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;oBAClB,MAAM,CAAC,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAC;oBAClC,UAAU,CAAC,MAAM,EAAE,4BAA4B,EAAE,mDAAmD,CAAC,CAAC;gBACxG,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,QAAQ,GAAG,qBAAqB,CACrC,SAAS,CAAC,QAAQ,EAClB,QAAQ,CAAC,QAAQ,CAAC,OAAO,EACzB,QAAQ,CAAC,MAAM,CAAC,OAAO,EACvB,eAAe,CAAC,WAAW,EAAE,SAAS,CAAC,YAAY,CAAC,CACrD,CAAC;oBACF,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,KAAK,SAAS;wBAAE,UAAU,CAAC,MAAM,EAAE,+BAA+B,EAAE,iDAAiD,CAAC,CAAC;gBAClJ,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,mDAAmD,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;QACvI,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;YAClB,MAAM,CAAC,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC;YACnC,UAAU,CAAC,MAAM,EAAE,6BAA6B,EAAE,kEAAkE,CAAC,CAAC;QACxH,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,SAAS,GAAG,sBAAsB,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YAChE,IAAI,SAAS,CAAC,UAAU,CAAC,KAAK,KAAK,eAAe;gBAAE,UAAU,CAAC,MAAM,EAAE,yBAAyB,EAAE,sCAAsC,CAAC,CAAC;iBACrI,IAAI,MAAM,CAAC,SAAS,CAAC,uBAAuB,CAAC,MAAM,GAAG,CAAC;gBAAE,UAAU,CAAC,MAAM,EAAE,gCAAgC,EAAE,4CAA4C,CAAC,CAAC;QACnK,CAAC;IACH,CAAC;IAED,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Diagnostic } from '../domain/diagnostics.js';
|
|
2
|
+
import type { RawNamedTarget } from '../request/request.js';
|
|
3
|
+
export type ProjectWorkflowErrorCode = 'project-not-initialized' | 'project-config-exists' | 'project-config-invalid' | 'project-catalog-invalid' | 'alias-invalid' | 'alias-reserved' | 'alias-exists' | 'project-state-write-failure' | 'observation-failure';
|
|
4
|
+
type WorkflowFailure = {
|
|
5
|
+
ok: false;
|
|
6
|
+
code: ProjectWorkflowErrorCode;
|
|
7
|
+
message: string;
|
|
8
|
+
diagnostics?: readonly Diagnostic[];
|
|
9
|
+
};
|
|
10
|
+
export interface InitializeProjectInput {
|
|
11
|
+
projectRoot: string;
|
|
12
|
+
url: string;
|
|
13
|
+
viewport: {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
};
|
|
17
|
+
targets: RawNamedTarget[];
|
|
18
|
+
defaultBaseline?: string;
|
|
19
|
+
replace: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function initializeFrontendObserverProject(input: InitializeProjectInput): Promise<{
|
|
22
|
+
ok: true;
|
|
23
|
+
configPath: string;
|
|
24
|
+
} | WorkflowFailure>;
|
|
25
|
+
export interface CaptureNamedObservationInput {
|
|
26
|
+
projectRoot: string;
|
|
27
|
+
alias: string;
|
|
28
|
+
replace: boolean;
|
|
29
|
+
}
|
|
30
|
+
export type CaptureNamedObservationResult = {
|
|
31
|
+
ok: true;
|
|
32
|
+
alias: string;
|
|
33
|
+
observationId: string;
|
|
34
|
+
requestId: string;
|
|
35
|
+
artifactRoot: string;
|
|
36
|
+
completionState: string;
|
|
37
|
+
diagnostics: readonly Diagnostic[];
|
|
38
|
+
} | WorkflowFailure;
|
|
39
|
+
export declare function captureNamedObservation(input: CaptureNamedObservationInput): Promise<CaptureNamedObservationResult>;
|
|
40
|
+
export declare function captureCurrentObservation(projectRoot: string): Promise<CaptureNamedObservationResult>;
|
|
41
|
+
export declare function loadProjectViewerState(projectRoot: string): Promise<{
|
|
42
|
+
ok: true;
|
|
43
|
+
root: string;
|
|
44
|
+
aliases: Readonly<Record<string, string>>;
|
|
45
|
+
} | WorkflowFailure>;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { access, mkdir, readFile, writeFile, rename, rm } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
import { normalizeRequest } from '../request/request.js';
|
|
5
|
+
import { observe } from './observationPersistence.js';
|
|
6
|
+
import { PROJECT_CONFIG_SCHEMA_VERSION, readProjectConfig, validateObservationAlias, validateProjectConfig } from '../projectWorkflow/projectConfig.js';
|
|
7
|
+
import { ALIAS_CATALOG_SCHEMA_VERSION, readAliasCatalog, writeAliasCatalog } from '../projectWorkflow/aliasCatalog.js';
|
|
8
|
+
import { aliasCatalogPath, observationOutputLocation, projectConfigPath, projectEvidenceRoot } from '../projectWorkflow/projectPaths.js';
|
|
9
|
+
async function exists(file) { return access(file).then(() => true, () => false); }
|
|
10
|
+
async function atomicTextWrite(file, text) {
|
|
11
|
+
const temp = `${file}.tmp-${process.pid}-${randomBytes(6).toString('hex')}`;
|
|
12
|
+
try {
|
|
13
|
+
await writeFile(temp, text, 'utf8');
|
|
14
|
+
await rename(temp, file);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
await rm(temp, { force: true }).catch(() => undefined);
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export async function initializeFrontendObserverProject(input) {
|
|
22
|
+
const root = path.resolve(input.projectRoot);
|
|
23
|
+
const configPath = projectConfigPath(root);
|
|
24
|
+
if ((await exists(configPath)) && !input.replace)
|
|
25
|
+
return { ok: false, code: 'project-config-exists', message: 'frontend-observer.json already exists; pass --replace to replace configuration only' };
|
|
26
|
+
const candidate = { schemaVersion: PROJECT_CONFIG_SCHEMA_VERSION, url: input.url, viewport: input.viewport, targets: input.targets, defaultBaseline: input.defaultBaseline ?? 'baseline' };
|
|
27
|
+
const validated = validateProjectConfig(candidate);
|
|
28
|
+
if (!validated.ok)
|
|
29
|
+
return { ok: false, code: 'project-config-invalid', message: validated.reason };
|
|
30
|
+
try {
|
|
31
|
+
await mkdir(projectEvidenceRoot(root), { recursive: true });
|
|
32
|
+
await atomicTextWrite(configPath, `${JSON.stringify(validated.config, null, 2)}\n`);
|
|
33
|
+
const catalogPath = aliasCatalogPath(root);
|
|
34
|
+
if (!(await exists(catalogPath)))
|
|
35
|
+
await writeAliasCatalog(catalogPath, { schemaVersion: ALIAS_CATALOG_SCHEMA_VERSION, observations: {} });
|
|
36
|
+
const ignorePath = path.join(root, '.gitignore');
|
|
37
|
+
const existing = await readFile(ignorePath, 'utf8').catch(() => '');
|
|
38
|
+
if (!existing.split(/\r?\n/).some((line) => line.trim() === '.frontend-observer/')) {
|
|
39
|
+
const prefix = existing.length === 0 || existing.endsWith('\n') ? existing : `${existing}\n`;
|
|
40
|
+
await writeFile(ignorePath, `${prefix}.frontend-observer/\n`, 'utf8');
|
|
41
|
+
}
|
|
42
|
+
return { ok: true, configPath };
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
return { ok: false, code: 'project-state-write-failure', message: error instanceof Error ? error.message : String(error) };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function captureObservation(input, allowReserved) {
|
|
49
|
+
const aliasValidation = validateObservationAlias(input.alias, allowReserved);
|
|
50
|
+
if (!aliasValidation.ok)
|
|
51
|
+
return { ok: false, code: aliasValidation.reason === 'reserved' ? 'alias-reserved' : 'alias-invalid', message: aliasValidation.reason === 'reserved' ? 'alias "current" is reserved for a later workflow command' : 'alias must match ^[a-z0-9][a-z0-9._-]{0,63}$ and must not be a traversal expression' };
|
|
52
|
+
const config = await readProjectConfig(projectConfigPath(input.projectRoot));
|
|
53
|
+
if (!config.ok)
|
|
54
|
+
return { ok: false, code: 'project-config-invalid', message: config.reason };
|
|
55
|
+
const catalogResult = await readAliasCatalog(aliasCatalogPath(input.projectRoot));
|
|
56
|
+
if (!catalogResult.ok)
|
|
57
|
+
return { ok: false, code: 'project-catalog-invalid', message: catalogResult.reason };
|
|
58
|
+
if (catalogResult.catalog.observations[input.alias] !== undefined && !input.replace)
|
|
59
|
+
return { ok: false, code: 'alias-exists', message: `alias "${input.alias}" already exists; pass --replace to point it at a new observation` };
|
|
60
|
+
const normalized = normalizeRequest({ targetUrl: config.config.url, viewport: config.config.viewport, targets: config.config.targets, outputLocation: observationOutputLocation(input.alias) });
|
|
61
|
+
if (!normalized.ok)
|
|
62
|
+
return { ok: false, code: 'project-config-invalid', message: normalized.diagnostics.map((entry) => entry.message).join('; ') };
|
|
63
|
+
const result = await observe(normalized.request, { cwd: input.projectRoot });
|
|
64
|
+
if (!result.ok)
|
|
65
|
+
return { ok: false, code: 'observation-failure', message: 'observation failed', diagnostics: result.diagnostics };
|
|
66
|
+
const relative = path.relative(projectEvidenceRoot(input.projectRoot), result.artifactRoot).split(path.sep).join('/');
|
|
67
|
+
if (relative.startsWith('../') || path.isAbsolute(relative))
|
|
68
|
+
return { ok: false, code: 'project-state-write-failure', message: 'persisted observation path escaped the managed evidence root' };
|
|
69
|
+
const updated = { ...catalogResult.catalog, observations: { ...catalogResult.catalog.observations, [input.alias]: { observationId: result.observationId, requestId: result.requestId, relativeArtifactDir: relative } } };
|
|
70
|
+
try {
|
|
71
|
+
await writeAliasCatalog(aliasCatalogPath(input.projectRoot), updated);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return { ok: false, code: 'project-state-write-failure', message: `observation persisted, but alias catalog update failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
75
|
+
}
|
|
76
|
+
return { ok: true, alias: input.alias, observationId: result.observationId, requestId: result.requestId, artifactRoot: result.artifactRoot, completionState: result.completion.state, diagnostics: result.diagnostics };
|
|
77
|
+
}
|
|
78
|
+
export async function captureNamedObservation(input) { return captureObservation(input, false); }
|
|
79
|
+
export async function captureCurrentObservation(projectRoot) { return captureObservation({ projectRoot, alias: 'current', replace: true }, true); }
|
|
80
|
+
export async function loadProjectViewerState(projectRoot) {
|
|
81
|
+
const config = await readProjectConfig(projectConfigPath(projectRoot));
|
|
82
|
+
if (!config.ok)
|
|
83
|
+
return { ok: false, code: 'project-config-invalid', message: config.reason };
|
|
84
|
+
const catalog = await readAliasCatalog(aliasCatalogPath(projectRoot));
|
|
85
|
+
if (!catalog.ok)
|
|
86
|
+
return { ok: false, code: 'project-catalog-invalid', message: catalog.reason };
|
|
87
|
+
const aliases = {};
|
|
88
|
+
for (const [alias, record] of Object.entries(catalog.catalog.observations))
|
|
89
|
+
aliases[record.relativeArtifactDir] = alias;
|
|
90
|
+
return { ok: true, root: projectEvidenceRoot(projectRoot), aliases };
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=projectWorkflowService.js.map
|