@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,24 @@
|
|
|
1
|
+
import type { PersistentBaselineContract, PerChangeContract } from '../domain/frontendContracts.js';
|
|
2
|
+
export type ReadPersistentBaselineContractResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
contract: PersistentBaselineContract;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
reason: string;
|
|
8
|
+
};
|
|
9
|
+
export type ReadPerChangeContractResult = {
|
|
10
|
+
ok: true;
|
|
11
|
+
contract: PerChangeContract;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
reason: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Narrow canonical reader counterpart to `frontendContractArtifactWriter.ts#writePersistentBaselineContract`:
|
|
18
|
+
* reads one `manifest.json`, parses it, and validates it through the same
|
|
19
|
+
* `isValidPersistentBaselineContract` structural gate the writer uses - never
|
|
20
|
+
* a second validator, never a schema upgrade, never a guessed missing field.
|
|
21
|
+
*/
|
|
22
|
+
export declare function readPersistentBaselineContract(manifestPath: string): Promise<ReadPersistentBaselineContractResult>;
|
|
23
|
+
/** Narrow canonical reader counterpart to `frontendContractArtifactWriter.ts#writePerChangeContract`. */
|
|
24
|
+
export declare function readPerChangeContract(manifestPath: string): Promise<ReadPerChangeContractResult>;
|
|
@@ -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":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,iCAAiC,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE7G,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,MAAM,CAAC,MAAM,mCAAmC,GAAG,eAAe,CAAC;AAWnE,SAAS,YAAY,CAAC,OAAe,EAAE,OAAiC;IACtE,MAAM,UAAU,GAAe;QAC7B,IAAI,EAAE,wBAAwB;QAC9B,QAAQ,EAAE,mBAAmB,CAAC,wBAAwB,CAAC;QACvD,OAAO;QACP,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;KAC9C,CAAC;IACF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,SAAiB;IACzC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAC3B,GAAG,EAAE,CAAC,IAAI,EACV,GAAG,EAAE,CAAC,KAAK,CACZ,CAAC;AACJ,CAAC;AAOD;;;;;;GAMG;AACH,KAAK,UAAU,qBAAqB,CAClC,KAAqD,EACrD,EAAU,EACV,SAAiB,EACjB,cAAsB,EACtB,OAA6C;IAE7C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAErD,IAAI,MAAM,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,OAAO,YAAY,CAAC,KAAK,SAAS,gCAAgC,EAAE,6BAA6B,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,CAAC;IAClI,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE3C,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,mCAAmC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAElH,MAAM,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAElC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,mCAAmC,CAAC,EAAE,CAAC;IACxH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC5E,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,YAAY,CAAC,qBAAqB,SAAS,cAAc,OAAO,EAAE,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACnD,QAAoC,EACpC,cAAsB,EACtB,UAAgD,EAAE;IAElD,MAAM,UAAU,GAAG,iCAAiC,CAAC,QAAQ,CAAC,CAAC;IAC/D,IAAI,CAAC,UAAU,CAAC,KAAK;QAAE,OAAO,YAAY,CAAC,8DAA8D,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9H,OAAO,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE,mBAAmB,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;AAC5G,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,QAA2B,EAC3B,cAAsB,EACtB,UAAgD,EAAE;IAElD,MAAM,UAAU,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IACtD,IAAI,CAAC,UAAU,CAAC,KAAK;QAAE,OAAO,YAAY,CAAC,qDAAqD,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IACrH,OAAO,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE,qBAAqB,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;AAC9G,CAAC"}
|
|
@@ -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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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Diagnostic } from '../domain/diagnostics.js';
|
|
2
|
+
/**
|
|
3
|
+
* Observer-owned result of persisting one ObservationArtifact. `artifactRoot`
|
|
4
|
+
* and the file paths are absolute filesystem paths for the *caller's* local
|
|
5
|
+
* use only (e.g. to print or open the result); they are never written back
|
|
6
|
+
* into the persisted manifest itself, which stores only portable relative
|
|
7
|
+
* references.
|
|
8
|
+
*/
|
|
9
|
+
export type PersistedObservationResult = {
|
|
10
|
+
ok: true;
|
|
11
|
+
artifactRoot: string;
|
|
12
|
+
manifestPath: string;
|
|
13
|
+
screenshotPath: string;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
diagnostics: Diagnostic[];
|
|
17
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/artifacts/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { NormalizedObservationRequest } from '../request/request.js';
|
|
2
|
+
import type { BrowserCaptureResult } from './types.js';
|
|
3
|
+
export type { BrowserCaptureResult, BrowserProvenance } from './types.js';
|
|
4
|
+
export interface CaptureViewportInternalResult {
|
|
5
|
+
result: BrowserCaptureResult;
|
|
6
|
+
/** True if the launched browser process is still connected after cleanup ran. Always false unless cleanup failed. */
|
|
7
|
+
browserConnected: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Owns the entire launch -> context -> page -> navigate -> ready -> screenshot
|
|
11
|
+
* -> close lifecycle for one capture. Guarantees `browser.close()` runs on
|
|
12
|
+
* every exit path (success, safety rejection, navigation/readiness failure,
|
|
13
|
+
* or an unexpected internal error), so callers never manage cleanup
|
|
14
|
+
* themselves. Exported (not re-exported from src/index.ts or the application
|
|
15
|
+
* seam) only so integration tests can assert `browserConnected` becomes
|
|
16
|
+
* false after every call - the Browser handle itself never leaves this
|
|
17
|
+
* module.
|
|
18
|
+
*/
|
|
19
|
+
export declare function captureViewportInternal(request: NormalizedObservationRequest): Promise<CaptureViewportInternalResult>;
|
|
20
|
+
/** Observer-facing entrypoint: no Playwright object is reachable from its signature or return value. */
|
|
21
|
+
export declare function captureViewport(request: NormalizedObservationRequest): Promise<BrowserCaptureResult>;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { chromium } from 'playwright';
|
|
2
|
+
import { DIAGNOSTIC_SEVERITY } from '../domain/diagnostics.js';
|
|
3
|
+
import { classifyUrl, classifyRedirect, classifySubresource, classifyPopup, classifyDownload } from '../safety/policy.js';
|
|
4
|
+
import { capturePageEvidence, captureTargetEvidence, resolveConfiguredTargets, findResolvedTarget } from './evidenceCapture.js';
|
|
5
|
+
import { captureScrollRuntimeSnapshot, performWindowScrollBy, performTargetScrollBy, waitTwoAnimationFrames } from './scrollCapture.js';
|
|
6
|
+
import { deriveScrollScenarioTransition, deriveScrollOwner } from '../domain/scrollEvidence.js';
|
|
7
|
+
function diagnostic(code, message) {
|
|
8
|
+
return { code, severity: DIAGNOSTIC_SEVERITY[code], message };
|
|
9
|
+
}
|
|
10
|
+
function classifyNavigationError(error, readinessTimeoutMs) {
|
|
11
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12
|
+
if (/timeout/i.test(message)) {
|
|
13
|
+
return diagnostic('readiness-timeout', `readiness condition not reached within ${readinessTimeoutMs}ms: ${message}`);
|
|
14
|
+
}
|
|
15
|
+
return diagnostic('navigation-failure', `navigation failed: ${message}`);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Owns the entire launch -> context -> page -> navigate -> ready -> screenshot
|
|
19
|
+
* -> close lifecycle for one capture. Guarantees `browser.close()` runs on
|
|
20
|
+
* every exit path (success, safety rejection, navigation/readiness failure,
|
|
21
|
+
* or an unexpected internal error), so callers never manage cleanup
|
|
22
|
+
* themselves. Exported (not re-exported from src/index.ts or the application
|
|
23
|
+
* seam) only so integration tests can assert `browserConnected` becomes
|
|
24
|
+
* false after every call - the Browser handle itself never leaves this
|
|
25
|
+
* module.
|
|
26
|
+
*/
|
|
27
|
+
export async function captureViewportInternal(request) {
|
|
28
|
+
const initialDecision = classifyUrl(request.targetUrl);
|
|
29
|
+
if (!initialDecision.allowed) {
|
|
30
|
+
return { result: { ok: false, diagnostics: [initialDecision.diagnostic] }, browserConnected: false };
|
|
31
|
+
}
|
|
32
|
+
let browser;
|
|
33
|
+
let result;
|
|
34
|
+
try {
|
|
35
|
+
browser = await chromium.launch({ headless: true });
|
|
36
|
+
const context = await browser.newContext({
|
|
37
|
+
viewport: { width: request.viewport.width, height: request.viewport.height },
|
|
38
|
+
});
|
|
39
|
+
const page = await context.newPage();
|
|
40
|
+
const diagnostics = [];
|
|
41
|
+
page.on('popup', () => diagnostics.push(classifyPopup()));
|
|
42
|
+
page.on('download', () => diagnostics.push(classifyDownload()));
|
|
43
|
+
let lastAllowedMainFrameUrl = request.targetUrl;
|
|
44
|
+
let rejection;
|
|
45
|
+
await page.route('**/*', async (route) => {
|
|
46
|
+
const req = route.request();
|
|
47
|
+
const url = req.url();
|
|
48
|
+
const isMainFrameNavigation = req.isNavigationRequest() && req.frame() === page.mainFrame();
|
|
49
|
+
if (!isMainFrameNavigation) {
|
|
50
|
+
const decision = classifySubresource(url);
|
|
51
|
+
if (!decision.allowed) {
|
|
52
|
+
rejection = rejection ?? decision.diagnostic;
|
|
53
|
+
await route.abort('blockedbyclient').catch(() => undefined);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
await route.continue().catch(() => undefined);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// Chromium follows navigation redirects internally without re-emitting a
|
|
60
|
+
// routable request per hop, so each main-frame hop is fetched manually
|
|
61
|
+
// (maxRedirects: 0) and inspected here before any further hop is
|
|
62
|
+
// allowed to proceed - this is what lets a prohibited redirect target
|
|
63
|
+
// be blocked before it is ever contacted.
|
|
64
|
+
const decision = url === lastAllowedMainFrameUrl ? { allowed: true } : classifyRedirect(lastAllowedMainFrameUrl, url);
|
|
65
|
+
if (!decision.allowed) {
|
|
66
|
+
rejection = rejection ?? decision.diagnostic;
|
|
67
|
+
await route.fulfill({ status: 204, body: '' }).catch(() => undefined);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
lastAllowedMainFrameUrl = url;
|
|
71
|
+
let response;
|
|
72
|
+
try {
|
|
73
|
+
response = await route.fetch({ maxRedirects: 0 });
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
await route.abort('failed').catch(() => undefined);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const status = response.status();
|
|
80
|
+
if (status >= 300 && status < 400) {
|
|
81
|
+
const location = response.headers()['location'];
|
|
82
|
+
const resolvedUrl = location ? new URL(location, url).toString() : undefined;
|
|
83
|
+
const redirectDecision = resolvedUrl ? classifyRedirect(lastAllowedMainFrameUrl, resolvedUrl) : { allowed: true };
|
|
84
|
+
if (!redirectDecision.allowed) {
|
|
85
|
+
rejection = rejection ?? redirectDecision.diagnostic;
|
|
86
|
+
await route.fulfill({ status: 204, body: '' }).catch(() => undefined);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
await route.fulfill({ response }).catch(() => undefined);
|
|
91
|
+
});
|
|
92
|
+
let navigationError;
|
|
93
|
+
try {
|
|
94
|
+
await page.goto(request.targetUrl, {
|
|
95
|
+
waitUntil: request.readiness.condition,
|
|
96
|
+
timeout: request.readiness.timeoutMs,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
navigationError = err;
|
|
101
|
+
}
|
|
102
|
+
if (rejection) {
|
|
103
|
+
result = { ok: false, diagnostics: [rejection, ...diagnostics] };
|
|
104
|
+
}
|
|
105
|
+
else if (navigationError) {
|
|
106
|
+
result = { ok: false, diagnostics: [...diagnostics, classifyNavigationError(navigationError, request.readiness.timeoutMs)] };
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// v0.3 Batch 2/3: for a scenario request, resolve configured targets
|
|
110
|
+
// exactly once (reusing the same canonical resolveConfiguredTargets
|
|
111
|
+
// algorithm captureTargetEvidence uses below), capture the initial
|
|
112
|
+
// runtime snapshot, perform the one immediate scroll action (window or
|
|
113
|
+
// the uniquely-resolved action target - same two-animation-frame
|
|
114
|
+
// stabilization either way), and capture the final runtime snapshot -
|
|
115
|
+
// all before the screenshot and ordinary page/target evidence below,
|
|
116
|
+
// so every one of those downstream captures describes only the final
|
|
117
|
+
// post-action state. No second navigation, page, or observation ever
|
|
118
|
+
// happens.
|
|
119
|
+
let scrollScenarioEvidence;
|
|
120
|
+
if (request.scrollScenario) {
|
|
121
|
+
const action = request.scrollScenario.action;
|
|
122
|
+
const resolvedForScenario = await resolveConfiguredTargets(page, request.targets);
|
|
123
|
+
try {
|
|
124
|
+
const initial = await captureScrollRuntimeSnapshot(page, resolvedForScenario, request.viewport);
|
|
125
|
+
if (action.kind === 'window-scroll-by') {
|
|
126
|
+
await performWindowScrollBy(page, action.deltaX, action.deltaY);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const actionTarget = findResolvedTarget(resolvedForScenario, action.target);
|
|
130
|
+
if (actionTarget?.status === 'matched' && actionTarget.handle) {
|
|
131
|
+
await performTargetScrollBy(actionTarget.handle, action.deltaX, action.deltaY);
|
|
132
|
+
}
|
|
133
|
+
// else: the configured action target could not be uniquely
|
|
134
|
+
// resolved at runtime (not-found/ambiguous/unavailable) - no
|
|
135
|
+
// scroll is performed and no movement is fabricated. The
|
|
136
|
+
// existing target-missing/target-ambiguous/target-hidden/
|
|
137
|
+
// browser-evidence-unavailable diagnostic for this same target
|
|
138
|
+
// name still surfaces below from the ordinary
|
|
139
|
+
// captureTargetEvidence pass, so the unresolved action target
|
|
140
|
+
// remains honestly explained rather than silently ignored.
|
|
141
|
+
}
|
|
142
|
+
await waitTwoAnimationFrames(page);
|
|
143
|
+
const final = await captureScrollRuntimeSnapshot(page, resolvedForScenario, request.viewport);
|
|
144
|
+
const transition = deriveScrollScenarioTransition(initial, final);
|
|
145
|
+
const scrollOwner = deriveScrollOwner(transition);
|
|
146
|
+
scrollScenarioEvidence = { initial, final, transition, scrollOwner };
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
await Promise.all(resolvedForScenario.filter((info) => info.handle).map((info) => info.handle.dispose()));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Captured from the same live page, after the same readiness point (and,
|
|
153
|
+
// for a scenario request, after the scroll action above), as required by
|
|
154
|
+
// Batch 3/v0.3 Batch 2: no second browser/page is ever opened.
|
|
155
|
+
const screenshotBuffer = await page.screenshot({ type: 'png' });
|
|
156
|
+
const provenance = { engine: 'chromium', version: browser.version() };
|
|
157
|
+
let pageEvidence;
|
|
158
|
+
try {
|
|
159
|
+
pageEvidence = await capturePageEvidence(page, request.targetUrl, request.viewport);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
163
|
+
diagnostics.push(diagnostic('browser-evidence-unavailable', `page evidence collection failed: ${message}`));
|
|
164
|
+
pageEvidence = {};
|
|
165
|
+
}
|
|
166
|
+
let targetEvidence;
|
|
167
|
+
try {
|
|
168
|
+
const captured = await captureTargetEvidence(page, request.targets);
|
|
169
|
+
targetEvidence = captured.targetEvidence;
|
|
170
|
+
diagnostics.push(...captured.diagnostics);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
174
|
+
diagnostics.push(diagnostic('browser-evidence-unavailable', `target evidence collection failed: ${message}`));
|
|
175
|
+
targetEvidence = {};
|
|
176
|
+
}
|
|
177
|
+
result = {
|
|
178
|
+
ok: true,
|
|
179
|
+
provenance,
|
|
180
|
+
screenshot: new Uint8Array(screenshotBuffer),
|
|
181
|
+
pageEvidence,
|
|
182
|
+
targetEvidence,
|
|
183
|
+
diagnostics,
|
|
184
|
+
...(scrollScenarioEvidence ? { scrollScenarioEvidence } : {}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
190
|
+
result = { ok: false, diagnostics: [diagnostic('browser-runtime-failure', `unexpected browser failure: ${message}`)] };
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
if (browser) {
|
|
194
|
+
await browser.close().catch(() => undefined);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return { result, browserConnected: browser ? browser.isConnected() : false };
|
|
198
|
+
}
|
|
199
|
+
/** Observer-facing entrypoint: no Playwright object is reachable from its signature or return value. */
|
|
200
|
+
export async function captureViewport(request) {
|
|
201
|
+
const { result } = await captureViewportInternal(request);
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=chromiumAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chromiumAdapter.js","sourceRoot":"","sources":["../../src/browser/chromiumAdapter.ts"],"names":[],"mappings":"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"}
|