my-frontend-observer 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +32 -8
  3. package/dist/application/comparisonService.d.ts +56 -0
  4. package/dist/application/comparisonService.js +77 -0
  5. package/dist/application/comparisonService.js.map +1 -0
  6. package/dist/artifacts/artifactReader.d.ts +19 -0
  7. package/dist/artifacts/artifactReader.js +36 -0
  8. package/dist/artifacts/artifactReader.js.map +1 -0
  9. package/dist/artifacts/comparisonArtifactWriter.d.ts +41 -0
  10. package/dist/artifacts/comparisonArtifactWriter.js +67 -0
  11. package/dist/artifacts/comparisonArtifactWriter.js.map +1 -0
  12. package/dist/cli.js +220 -1
  13. package/dist/cli.js.map +1 -1
  14. package/dist/domain/comparison.d.ts +198 -0
  15. package/dist/domain/comparison.js +324 -0
  16. package/dist/domain/comparison.js.map +1 -0
  17. package/dist/domain/comparisonEngine.d.ts +48 -0
  18. package/dist/domain/comparisonEngine.js +694 -0
  19. package/dist/domain/comparisonEngine.js.map +1 -0
  20. package/dist/domain/comparisonIdentity.d.ts +13 -0
  21. package/dist/domain/comparisonIdentity.js +46 -0
  22. package/dist/domain/comparisonIdentity.js.map +1 -0
  23. package/dist/domain/evidence.d.ts +2 -0
  24. package/dist/domain/evidence.js +4 -0
  25. package/dist/domain/evidence.js.map +1 -1
  26. package/dist/domain/relationships.d.ts +168 -0
  27. package/dist/domain/relationships.js +343 -0
  28. package/dist/domain/relationships.js.map +1 -0
  29. package/dist/index.d.ts +14 -1
  30. package/dist/index.js +8 -1
  31. package/dist/index.js.map +1 -1
  32. package/docs/ARCHITECTURE.md +76 -3
  33. package/docs/CI_CD.md +29 -0
  34. package/docs/COMMANDS.md +137 -0
  35. package/docs/CONTRACTS.md +96 -4
  36. package/docs/CURRENT_STATE.md +102 -9
  37. package/docs/DEVELOPMENT.md +33 -10
  38. package/docs/PROJECT_OVERVIEW.md +16 -12
  39. package/docs/QUICKSTART.md +5 -0
  40. package/docs/RELEASE.md +10 -8
  41. package/docs/ROADMAP.md +4 -0
  42. package/docs/SECURITY.md +14 -1
  43. package/docs/WORKFLOWS.md +56 -6
  44. package/package.json +1 -1
@@ -0,0 +1,343 @@
1
+ import { isValidObservationArtifact } from './schema.js';
2
+ function isPlainObject(value) {
3
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
4
+ }
5
+ // --- Geometry tolerance -------------------------------------------------------
6
+ /** Suppresses insignificant geometry noise only - never a design contract, never permission for changes. */
7
+ export const GEOMETRY_TOLERANCE_DEFAULT_PX = 0.5;
8
+ export const GEOMETRY_TOLERANCE_MIN_PX = 0;
9
+ export const GEOMETRY_TOLERANCE_MAX_PX = 10;
10
+ export function isValidGeometryTolerancePx(value) {
11
+ return typeof value === 'number' && Number.isFinite(value) && value >= GEOMETRY_TOLERANCE_MIN_PX && value <= GEOMETRY_TOLERANCE_MAX_PX;
12
+ }
13
+ export const HORIZONTAL_ORDER_RELATIONSHIPS = ['left-of', 'right-of', 'horizontally-overlapping'];
14
+ export const VERTICAL_ORDER_RELATIONSHIPS = ['above', 'below', 'vertically-overlapping'];
15
+ export const AREA_OVERLAP_RELATIONSHIPS = ['overlaps', 'does-not-overlap'];
16
+ export const RELATIVE_WIDTH_RELATIONSHIPS = ['wider-than', 'narrower-than', 'equal-width-within-tolerance'];
17
+ export const GEOMETRIC_FIT_RELATIONSHIPS = ['fits-inside', 'does-not-fit-inside'];
18
+ /** Rendered-geometry sequencing only - never source DOM order (see docs/CONTRACTS.md target containment discussion). */
19
+ export const VERTICAL_SEQUENCE_RELATIONSHIPS = ['follows-vertically'];
20
+ export const PAGE_WIDTH_RELATIONSHIPS = ['document-width-fits-viewport', 'document-width-exceeds-viewport'];
21
+ /** Every pairwise (two-target) relationship kind this batch freezes. Deliberately bounded - not a general-purpose spatial language. */
22
+ export const PAIRWISE_RELATIONSHIP_KINDS = [
23
+ ...HORIZONTAL_ORDER_RELATIONSHIPS,
24
+ ...VERTICAL_ORDER_RELATIONSHIPS,
25
+ ...AREA_OVERLAP_RELATIONSHIPS,
26
+ ...RELATIVE_WIDTH_RELATIONSHIPS,
27
+ ...GEOMETRIC_FIT_RELATIONSHIPS,
28
+ ...VERTICAL_SEQUENCE_RELATIONSHIPS,
29
+ ];
30
+ /** Every page-level (no target pair) relationship kind this batch freezes. */
31
+ export const PAGE_LEVEL_RELATIONSHIP_KINDS = [...PAGE_WIDTH_RELATIONSHIPS];
32
+ export function isValidEvidenceReference(value) {
33
+ return isPlainObject(value) && typeof value.path === 'string' && value.path.length > 0;
34
+ }
35
+ function isEvidenceReferenceArray(value) {
36
+ return Array.isArray(value) && value.every((entry) => isValidEvidenceReference(entry));
37
+ }
38
+ export function isValidPairwiseLayoutRelationship(value) {
39
+ if (!isPlainObject(value))
40
+ return false;
41
+ if (typeof value.kind !== 'string' || !PAIRWISE_RELATIONSHIP_KINDS.includes(value.kind))
42
+ return false;
43
+ if (typeof value.subjectTarget !== 'string' || value.subjectTarget.length === 0)
44
+ return false;
45
+ if (typeof value.relatedTarget !== 'string' || value.relatedTarget.length === 0)
46
+ return false;
47
+ if (value.subjectTarget === value.relatedTarget)
48
+ return false;
49
+ return isEvidenceReferenceArray(value.evidence);
50
+ }
51
+ export function isValidPageLevelLayoutRelationship(value) {
52
+ if (!isPlainObject(value))
53
+ return false;
54
+ if (typeof value.kind !== 'string' || !PAGE_LEVEL_RELATIONSHIP_KINDS.includes(value.kind))
55
+ return false;
56
+ return isEvidenceReferenceArray(value.evidence);
57
+ }
58
+ /** A configured target excluded from relationship derivation because it lacks usable geometry - never a fabricated zero-sized rectangle. */
59
+ export const UNRESOLVED_TARGET_REASONS = ['not-found', 'ambiguous', 'unavailable', 'hidden'];
60
+ export function isValidUnresolvedRelationshipTarget(value) {
61
+ if (!isPlainObject(value))
62
+ return false;
63
+ if (typeof value.target !== 'string' || value.target.length === 0)
64
+ return false;
65
+ return typeof value.reason === 'string' && UNRESOLVED_TARGET_REASONS.includes(value.reason);
66
+ }
67
+ export const MAX_CONFIGURED_TARGETS_FOR_RELATIONSHIPS = 20;
68
+ export const MAX_PAIRWISE_RELATIONSHIP_PAIRS = (MAX_CONFIGURED_TARGETS_FOR_RELATIONSHIPS * (MAX_CONFIGURED_TARGETS_FOR_RELATIONSHIPS - 1)) / 2;
69
+ /**
70
+ * A single unordered pair can carry up to one record per independent
71
+ * pairwise relationship family (horizontal-order, vertical-order,
72
+ * area-overlap, relative-width, geometric-fit, vertical-sequence - see
73
+ * `deriveLayoutRelationships` below), never more than one per family per
74
+ * pair. This is the resulting bound on `pairwiseRelationships.length`, not a
75
+ * second target-count configuration.
76
+ */
77
+ export const PAIRWISE_RELATIONSHIP_FAMILY_COUNT = 6;
78
+ export const MAX_PAIRWISE_RELATIONSHIP_RECORDS = MAX_PAIRWISE_RELATIONSHIP_PAIRS * PAIRWISE_RELATIONSHIP_FAMILY_COUNT;
79
+ export function isValidLayoutRelationshipGraph(value) {
80
+ if (!isPlainObject(value))
81
+ return false;
82
+ if (typeof value.observationId !== 'string' || value.observationId.length === 0)
83
+ return false;
84
+ if (typeof value.requestId !== 'string' || value.requestId.length === 0)
85
+ return false;
86
+ if (typeof value.geometryTolerancePx !== 'number' || !Number.isFinite(value.geometryTolerancePx))
87
+ return false;
88
+ if (!Array.isArray(value.targets) || !value.targets.every((t) => typeof t === 'string' && t.length > 0))
89
+ return false;
90
+ if (new Set(value.targets).size !== value.targets.length)
91
+ return false;
92
+ if (!Array.isArray(value.unresolvedTargets) || !value.unresolvedTargets.every((t) => isValidUnresolvedRelationshipTarget(t)))
93
+ return false;
94
+ const targetSet = new Set(value.targets);
95
+ if (value.unresolvedTargets.some((t) => targetSet.has(t.target)))
96
+ return false;
97
+ if (!Array.isArray(value.pairwiseRelationships) || !value.pairwiseRelationships.every((r) => isValidPairwiseLayoutRelationship(r)))
98
+ return false;
99
+ if (value.pairwiseRelationships.length > MAX_PAIRWISE_RELATIONSHIP_RECORDS)
100
+ return false;
101
+ for (const relationship of value.pairwiseRelationships) {
102
+ if (!targetSet.has(relationship.subjectTarget) || !targetSet.has(relationship.relatedTarget))
103
+ return false;
104
+ }
105
+ if (!Array.isArray(value.pageRelationships) || !value.pageRelationships.every((r) => isValidPageLevelLayoutRelationship(r)))
106
+ return false;
107
+ return true;
108
+ }
109
+ /**
110
+ * Bounded derived clipping concept (docs/PROJECT_MILESTONES.md Milestone 4).
111
+ * `clipped` requires both actual dimensional overflow on that axis AND a
112
+ * computed `overflow` value of `hidden`/`clip` on that axis - `auto`,
113
+ * `scroll`, and `visible` never classify as clipped solely from dimensional
114
+ * overflow. `unavailable` when the underlying overflow/geometry evidence
115
+ * cannot support a conclusion - never guessed.
116
+ */
117
+ export const CLIPPING_STATES = ['clipped', 'not-clipped', 'unavailable'];
118
+ export const CLIPPING_OVERFLOW_STYLES = ['hidden', 'clip'];
119
+ export function isValidTargetClippingEvidence(value) {
120
+ if (!isPlainObject(value))
121
+ return false;
122
+ return (typeof value.horizontal === 'string' &&
123
+ CLIPPING_STATES.includes(value.horizontal) &&
124
+ typeof value.vertical === 'string' &&
125
+ CLIPPING_STATES.includes(value.vertical));
126
+ }
127
+ /**
128
+ * Pure per-target clipping derivation from one target's own existing
129
+ * `layout`/`style` evidence (never a scroll scenario - normal target
130
+ * evidence already carries `scrollWidth`/`scrollHeight`/`clientWidth`/
131
+ * `clientHeight`/`overflowX`/`overflowY`). `TargetClippingEvidence` has no
132
+ * slot in `LayoutRelationshipGraph` (that graph is spatial/page-level only,
133
+ * per the frozen Batch 1 vocabulary) - this is a standalone per-target
134
+ * derivation for callers (e.g. a later comparison engine) that need it
135
+ * directly from an `ObservationArtifact`'s `targetEvidence`, one target at a
136
+ * time. `unavailable` (never guessed) when the underlying layout/style
137
+ * evidence is not itself available - this naturally covers an unresolved
138
+ * target without any special-casing. A resolved-but-`display:none` target
139
+ * naturally computes `not-clipped` too: its `scrollWidth`/`clientWidth` are
140
+ * both `0`, so `scrollWidth > clientWidth` is false - clipping is never
141
+ * inferred from hidden-ness itself.
142
+ */
143
+ export function deriveTargetClipping(record) {
144
+ const layoutField = record.layout;
145
+ const styleField = record.style;
146
+ if (layoutField.state !== 'available' || styleField.state !== 'available') {
147
+ return { horizontal: 'unavailable', vertical: 'unavailable' };
148
+ }
149
+ const layout = layoutField.value;
150
+ const style = styleField.value;
151
+ const isClippingOverflowStyle = (value) => CLIPPING_OVERFLOW_STYLES.includes(value);
152
+ const horizontalOverflow = layout.scrollWidth > layout.clientWidth;
153
+ const verticalOverflow = layout.scrollHeight > layout.clientHeight;
154
+ return {
155
+ horizontal: horizontalOverflow && isClippingOverflowStyle(style.overflowX) ? 'clipped' : 'not-clipped',
156
+ vertical: verticalOverflow && isClippingOverflowStyle(style.overflowY) ? 'clipped' : 'not-clipped',
157
+ };
158
+ }
159
+ // --- deriveLayoutRelationships (v0.4 Batch 2 canonical derivation engine) ----
160
+ /**
161
+ * Pure tolerance-aware geometry predicates (Batch 1's frozen rules,
162
+ * verbatim). Every predicate is a total function over exactly one relation
163
+ * family, so it can never itself produce a contradictory pair of values
164
+ * (e.g. `left-of` and `right-of` for the same call) - the caller decides
165
+ * `subject`/`related` once per pair and each family is evaluated exactly
166
+ * once for that fixed direction.
167
+ */
168
+ function horizontalOrderOf(a, b, tolerance) {
169
+ if (a.right <= b.x + tolerance)
170
+ return 'left-of';
171
+ if (b.right <= a.x + tolerance)
172
+ return 'right-of';
173
+ return 'horizontally-overlapping';
174
+ }
175
+ function verticalOrderOf(a, b, tolerance) {
176
+ if (a.bottom <= b.y + tolerance)
177
+ return 'above';
178
+ if (b.bottom <= a.y + tolerance)
179
+ return 'below';
180
+ return 'vertically-overlapping';
181
+ }
182
+ function areaOverlapOf(a, b, tolerance) {
183
+ const overlapWidth = Math.min(a.right, b.right) - Math.max(a.x, b.x);
184
+ const overlapHeight = Math.min(a.bottom, b.bottom) - Math.max(a.y, b.y);
185
+ return overlapWidth > tolerance && overlapHeight > tolerance ? 'overlaps' : 'does-not-overlap';
186
+ }
187
+ function relativeWidthOf(a, b, tolerance) {
188
+ if (a.width > b.width + tolerance)
189
+ return 'wider-than';
190
+ if (a.width < b.width - tolerance)
191
+ return 'narrower-than';
192
+ return 'equal-width-within-tolerance';
193
+ }
194
+ function geometricFitOf(a, b, tolerance) {
195
+ const fits = a.x >= b.x - tolerance && a.y >= b.y - tolerance && a.right <= b.right + tolerance && a.bottom <= b.bottom + tolerance;
196
+ return fits ? 'fits-inside' : 'does-not-fit-inside';
197
+ }
198
+ /**
199
+ * `follows-vertically` is the one family whose vocabulary has a single
200
+ * value (not a total 3-way partition like the others), so it is emitted
201
+ * only when one target's geometry actually precedes the other's - never for
202
+ * both directions, never when they vertically overlap. The direction is
203
+ * decided by actual rendered geometry, not configured target order:
204
+ * `above(x, y)` and "y follows x" share the same tolerance-relative
205
+ * condition, so whichever of the pair is algebraically "above" the other
206
+ * becomes `relatedTarget` and the one that follows becomes `subjectTarget`.
207
+ */
208
+ function verticalSequenceOf(x, y, tolerance) {
209
+ if (x.geometry.bottom <= y.geometry.y + tolerance)
210
+ return { subjectTarget: y.name, relatedTarget: x.name };
211
+ if (y.geometry.bottom <= x.geometry.y + tolerance)
212
+ return { subjectTarget: x.name, relatedTarget: y.name };
213
+ return undefined;
214
+ }
215
+ function geometryEvidence(a, b) {
216
+ return [{ path: `targetEvidence.${a}.geometry` }, { path: `targetEvidence.${b}.geometry` }];
217
+ }
218
+ /** Maps a configured target's resolution/visibility evidence to the frozen unresolved-target reason vocabulary - never a fabricated relationship for a target that cannot honestly carry one. */
219
+ function classifyUnresolvedTarget(record) {
220
+ if (!record)
221
+ return 'unavailable';
222
+ const resolutionField = record.resolution;
223
+ if (resolutionField.state === 'available' || resolutionField.state === 'partial') {
224
+ const status = resolutionField.value.selectionStatus;
225
+ if (status === 'not-found')
226
+ return 'not-found';
227
+ if (status === 'ambiguous')
228
+ return 'ambiguous';
229
+ if (status === 'unavailable')
230
+ return 'unavailable';
231
+ }
232
+ return 'unavailable';
233
+ }
234
+ /**
235
+ * The one canonical pure derivation from an already-captured
236
+ * `ObservationArtifact` to a `LayoutRelationshipGraph` (real Chromium ->
237
+ * ObservationArtifact -> **relationship derivation** -> comparability ->
238
+ * comparison -> ComparisonArtifact, see docs/ARCHITECTURE.md). Deterministic
239
+ * and synchronous: same `observation`/`geometryTolerancePx` in, same graph
240
+ * out, every time - no Chromium, target re-resolution, DOM access, randomness,
241
+ * clock, filesystem, or network involved, and `observation` is never
242
+ * mutated.
243
+ *
244
+ * Scope, by construction:
245
+ * - only targets present in `observation.requestConfig.targets` are ever
246
+ * considered (bounded at the existing 20-target/190-pair request limit -
247
+ * never arbitrary DOM/accessibility-tree traversal);
248
+ * - a configured target missing usable geometry (never resolved, or
249
+ * resolved but reported not-visible) becomes an `unresolvedTargets` entry
250
+ * with an honest reason, never a fabricated zero-sized relation;
251
+ * - for each unordered pair of geometry-eligible targets, exactly one
252
+ * record per relationship family is derived, with `subjectTarget`/
253
+ * `relatedTarget` fixed to that pair's configured order (earlier
254
+ * configured target is always `subjectTarget`) for every family except
255
+ * `follows-vertically` (see `verticalSequenceOf`) - so
256
+ * `pairwiseRelationships.length` never exceeds
257
+ * `MAX_PAIRWISE_RELATIONSHIP_RECORDS`;
258
+ * - DOM containment is deliberately not re-derived here: it already exists
259
+ * as `targetEvidence.<name>.containment` on the observation itself (see
260
+ * `TargetContainment` in domain/schema.ts, explicitly documented there as
261
+ * "never a layout/relationship graph") and stays separate from
262
+ * `fits-inside`/`does-not-fit-inside`, which is geometry-only;
263
+ * - the page-width relationship is derived only when both
264
+ * `pageEvidence.documentWidth` and `pageEvidence.viewportWidth` are
265
+ * themselves available - never defaulted to "fits" when evidence is
266
+ * missing.
267
+ */
268
+ export function deriveLayoutRelationships(observation, options = {}) {
269
+ const validation = isValidObservationArtifact(observation);
270
+ if (!validation.valid) {
271
+ return { ok: false, reason: `cannot derive relationships from an invalid ObservationArtifact: ${validation.reason}` };
272
+ }
273
+ const geometryTolerancePx = options.geometryTolerancePx ?? GEOMETRY_TOLERANCE_DEFAULT_PX;
274
+ if (!isValidGeometryTolerancePx(geometryTolerancePx)) {
275
+ return {
276
+ ok: false,
277
+ reason: `geometryTolerancePx must be a finite number in [${GEOMETRY_TOLERANCE_MIN_PX}, ${GEOMETRY_TOLERANCE_MAX_PX}]`,
278
+ };
279
+ }
280
+ const configuredNames = observation.requestConfig.targets.map((target) => target.name);
281
+ if (configuredNames.length > MAX_CONFIGURED_TARGETS_FOR_RELATIONSHIPS) {
282
+ return {
283
+ ok: false,
284
+ reason: `observation.requestConfig.targets has ${configuredNames.length} entries, exceeding the ${MAX_CONFIGURED_TARGETS_FOR_RELATIONSHIPS}-target relationship-derivation bound`,
285
+ };
286
+ }
287
+ const eligible = [];
288
+ const unresolvedTargets = [];
289
+ for (const name of configuredNames) {
290
+ const record = observation.targetEvidence[name];
291
+ const geometryField = record?.geometry;
292
+ if (!record || geometryField?.state !== 'available') {
293
+ unresolvedTargets.push({ target: name, reason: classifyUnresolvedTarget(record) });
294
+ continue;
295
+ }
296
+ const visibilityField = record.visibility;
297
+ if ((visibilityField.state === 'available' || visibilityField.state === 'partial') && visibilityField.value.visible === false) {
298
+ unresolvedTargets.push({ target: name, reason: 'hidden' });
299
+ continue;
300
+ }
301
+ eligible.push({ name, geometry: geometryField.value });
302
+ }
303
+ const pairwiseRelationships = [];
304
+ for (let i = 0; i < eligible.length; i += 1) {
305
+ const subject = eligible[i];
306
+ for (let j = i + 1; j < eligible.length; j += 1) {
307
+ const related = eligible[j];
308
+ const evidence = geometryEvidence(subject.name, related.name);
309
+ pairwiseRelationships.push({ kind: horizontalOrderOf(subject.geometry, related.geometry, geometryTolerancePx), subjectTarget: subject.name, relatedTarget: related.name, evidence }, { kind: verticalOrderOf(subject.geometry, related.geometry, geometryTolerancePx), subjectTarget: subject.name, relatedTarget: related.name, evidence }, { kind: areaOverlapOf(subject.geometry, related.geometry, geometryTolerancePx), subjectTarget: subject.name, relatedTarget: related.name, evidence }, { kind: relativeWidthOf(subject.geometry, related.geometry, geometryTolerancePx), subjectTarget: subject.name, relatedTarget: related.name, evidence }, { kind: geometricFitOf(subject.geometry, related.geometry, geometryTolerancePx), subjectTarget: subject.name, relatedTarget: related.name, evidence });
310
+ const sequence = verticalSequenceOf(subject, related, geometryTolerancePx);
311
+ if (sequence) {
312
+ pairwiseRelationships.push({
313
+ kind: 'follows-vertically',
314
+ subjectTarget: sequence.subjectTarget,
315
+ relatedTarget: sequence.relatedTarget,
316
+ evidence: geometryEvidence(sequence.subjectTarget, sequence.relatedTarget),
317
+ });
318
+ }
319
+ }
320
+ }
321
+ const pageRelationships = [];
322
+ const documentWidthField = observation.pageEvidence.documentWidth;
323
+ const viewportWidthField = observation.pageEvidence.viewportWidth;
324
+ const documentWidth = documentWidthField?.state === 'available' && typeof documentWidthField.value === 'number' ? documentWidthField.value : undefined;
325
+ const viewportWidth = viewportWidthField?.state === 'available' && typeof viewportWidthField.value === 'number' ? viewportWidthField.value : undefined;
326
+ if (documentWidth !== undefined && viewportWidth !== undefined) {
327
+ pageRelationships.push({
328
+ kind: documentWidth <= viewportWidth + geometryTolerancePx ? 'document-width-fits-viewport' : 'document-width-exceeds-viewport',
329
+ evidence: [{ path: 'pageEvidence.documentWidth' }, { path: 'pageEvidence.viewportWidth' }],
330
+ });
331
+ }
332
+ const graph = {
333
+ observationId: observation.observationId,
334
+ requestId: observation.requestId,
335
+ geometryTolerancePx,
336
+ targets: eligible.map((entry) => entry.name),
337
+ unresolvedTargets,
338
+ pairwiseRelationships,
339
+ pageRelationships,
340
+ };
341
+ return { ok: true, graph };
342
+ }
343
+ //# sourceMappingURL=relationships.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationships.js","sourceRoot":"","sources":["../../src/domain/relationships.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type { EvidenceState, EvidenceSource, EvidenceField } from './domain/evidence.js';
2
- export { isValidEvidenceField } from './domain/evidence.js';
2
+ export { isValidEvidenceField, evidenceValue } from './domain/evidence.js';
3
3
  export type { DiagnosticCode, DiagnosticSeverity, Diagnostic } from './domain/diagnostics.js';
4
4
  export { DIAGNOSTIC_CODES, DIAGNOSTIC_SEVERITY, orderDiagnostics } from './domain/diagnostics.js';
5
5
  export type { CompletionState, RequestPhase } from './domain/completion.js';
@@ -19,3 +19,16 @@ export type { PersistedObservationResult } from './artifacts/types.js';
19
19
  export { MANIFEST_FILENAME, SCREENSHOT_FILENAME } from './artifacts/artifactWriter.js';
20
20
  export type { ApplicationObservationResult } from './application/observationPersistence.js';
21
21
  export { buildObservationArtifact, persistBrowserCapture, observe } from './application/observationPersistence.js';
22
+ export type { HorizontalOrderRelationship, VerticalOrderRelationship, AreaOverlapRelationship, RelativeWidthRelationship, GeometricFitRelationship, VerticalSequenceRelationship, PageWidthRelationship, PairwiseRelationshipKind, PageLevelRelationshipKind, EvidenceReference, PairwiseLayoutRelationship, PageLevelLayoutRelationship, UnresolvedTargetReason, UnresolvedRelationshipTarget, LayoutRelationshipGraph, ClippingState, ClippingOverflowStyle, TargetClippingEvidence, DeriveLayoutRelationshipsOptions, DeriveLayoutRelationshipsResult, } from './domain/relationships.js';
23
+ export { HORIZONTAL_ORDER_RELATIONSHIPS, VERTICAL_ORDER_RELATIONSHIPS, AREA_OVERLAP_RELATIONSHIPS, RELATIVE_WIDTH_RELATIONSHIPS, GEOMETRIC_FIT_RELATIONSHIPS, VERTICAL_SEQUENCE_RELATIONSHIPS, PAGE_WIDTH_RELATIONSHIPS, PAIRWISE_RELATIONSHIP_KINDS, PAGE_LEVEL_RELATIONSHIP_KINDS, MAX_CONFIGURED_TARGETS_FOR_RELATIONSHIPS, MAX_PAIRWISE_RELATIONSHIP_PAIRS, PAIRWISE_RELATIONSHIP_FAMILY_COUNT, MAX_PAIRWISE_RELATIONSHIP_RECORDS, CLIPPING_STATES, CLIPPING_OVERFLOW_STYLES, isValidEvidenceReference, isValidPairwiseLayoutRelationship, isValidPageLevelLayoutRelationship, isValidUnresolvedRelationshipTarget, isValidLayoutRelationshipGraph, isValidTargetClippingEvidence, deriveTargetClipping, deriveLayoutRelationships, } from './domain/relationships.js';
24
+ export type { DependencyProperty, DependencyDirection, ExpectedDependencySource, DependencyTermDeclaration, ExpectedDependencyDeclaration, DependencyEvidenceOutcome, ExpectedDependencyEvidence, ComparisonConfig, ComparabilityState, ComparabilityReasonCode, ComparabilityReasonSeverity, ComparabilityReason, ComparabilityResult, TargetConfigurationChangeKind, TargetConfigurationChange, DifferenceKind, ComparisonDifferenceSubject, ComparisonDifference, RelationshipChangeRecord, ComparisonSourceObservationReference, ComparisonArtifact, ComparisonSchemaValidationResult, } from './domain/comparison.js';
25
+ export { COMPARISON_ARTIFACT_KIND, COMPARISON_SCHEMA_VERSION, GEOMETRY_TOLERANCE_DEFAULT_PX, GEOMETRY_TOLERANCE_MIN_PX, GEOMETRY_TOLERANCE_MAX_PX, isValidGeometryTolerancePx, DEPENDENCY_PROPERTIES, DEPENDENCY_DIRECTIONS, EXPECTED_DEPENDENCY_SOURCES, isValidExpectedDependencyDeclaration, DEPENDENCY_EVIDENCE_OUTCOMES, isValidExpectedDependencyEvidence, isValidComparisonConfig, COMPARABILITY_STATES, COMPARABILITY_REASON_CODES, COMPARABILITY_REASON_SEVERITY, isValidComparabilityResult, TARGET_CONFIGURATION_CHANGE_KINDS, isValidTargetConfigurationChange, DIFFERENCE_KINDS, isValidComparisonDifference, isValidComparisonSourceObservationReference, isValidComparisonArtifact, } from './domain/comparison.js';
26
+ export { buildComparisonRequestIdentity, buildComparisonIdentity } from './domain/comparisonIdentity.js';
27
+ export type { CompareObservationsResult } from './domain/comparisonEngine.js';
28
+ export { compareObservations, evaluateComparability, evaluateExpectedDependencies, compareTargetConfiguration } from './domain/comparisonEngine.js';
29
+ export type { PersistedComparisonResult, WriteComparisonArtifactOptions } from './artifacts/comparisonArtifactWriter.js';
30
+ export { COMPARISON_MANIFEST_FILENAME, writeComparisonArtifact } from './artifacts/comparisonArtifactWriter.js';
31
+ export type { ReadObservationArtifactResult } from './artifacts/artifactReader.js';
32
+ export { readObservationArtifact } from './artifacts/artifactReader.js';
33
+ export type { CompareAndPersistOptions, ApplicationComparisonResult } from './application/comparisonService.js';
34
+ export { DEFAULT_COMPARISON_OUTPUT_LOCATION, compareAndPersist, compareAndPersistFromArtifactRoots } from './application/comparisonService.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { isValidEvidenceField } from './domain/evidence.js';
1
+ export { isValidEvidenceField, evidenceValue } from './domain/evidence.js';
2
2
  export { DIAGNOSTIC_CODES, DIAGNOSTIC_SEVERITY, orderDiagnostics } from './domain/diagnostics.js';
3
3
  export { deriveCompletion } from './domain/completion.js';
4
4
  export { ARTIFACT_KIND, SCHEMA_VERSION, PRODUCER_NAME, TARGET_LANDMARK_ROLES, getProducerInfo, isValidObservationArtifact, } from './domain/schema.js';
@@ -9,4 +9,11 @@ export { classifyUrl, classifyRedirect, classifySubresource, classifyPopup, clas
9
9
  export { runBrowserCapture } from './application/browserCaptureService.js';
10
10
  export { MANIFEST_FILENAME, SCREENSHOT_FILENAME } from './artifacts/artifactWriter.js';
11
11
  export { buildObservationArtifact, persistBrowserCapture, observe } from './application/observationPersistence.js';
12
+ export { HORIZONTAL_ORDER_RELATIONSHIPS, VERTICAL_ORDER_RELATIONSHIPS, AREA_OVERLAP_RELATIONSHIPS, RELATIVE_WIDTH_RELATIONSHIPS, GEOMETRIC_FIT_RELATIONSHIPS, VERTICAL_SEQUENCE_RELATIONSHIPS, PAGE_WIDTH_RELATIONSHIPS, PAIRWISE_RELATIONSHIP_KINDS, PAGE_LEVEL_RELATIONSHIP_KINDS, MAX_CONFIGURED_TARGETS_FOR_RELATIONSHIPS, MAX_PAIRWISE_RELATIONSHIP_PAIRS, PAIRWISE_RELATIONSHIP_FAMILY_COUNT, MAX_PAIRWISE_RELATIONSHIP_RECORDS, CLIPPING_STATES, CLIPPING_OVERFLOW_STYLES, isValidEvidenceReference, isValidPairwiseLayoutRelationship, isValidPageLevelLayoutRelationship, isValidUnresolvedRelationshipTarget, isValidLayoutRelationshipGraph, isValidTargetClippingEvidence, deriveTargetClipping, deriveLayoutRelationships, } from './domain/relationships.js';
13
+ export { COMPARISON_ARTIFACT_KIND, COMPARISON_SCHEMA_VERSION, GEOMETRY_TOLERANCE_DEFAULT_PX, GEOMETRY_TOLERANCE_MIN_PX, GEOMETRY_TOLERANCE_MAX_PX, isValidGeometryTolerancePx, DEPENDENCY_PROPERTIES, DEPENDENCY_DIRECTIONS, EXPECTED_DEPENDENCY_SOURCES, isValidExpectedDependencyDeclaration, DEPENDENCY_EVIDENCE_OUTCOMES, isValidExpectedDependencyEvidence, isValidComparisonConfig, COMPARABILITY_STATES, COMPARABILITY_REASON_CODES, COMPARABILITY_REASON_SEVERITY, isValidComparabilityResult, TARGET_CONFIGURATION_CHANGE_KINDS, isValidTargetConfigurationChange, DIFFERENCE_KINDS, isValidComparisonDifference, isValidComparisonSourceObservationReference, isValidComparisonArtifact, } from './domain/comparison.js';
14
+ export { buildComparisonRequestIdentity, buildComparisonIdentity } from './domain/comparisonIdentity.js';
15
+ export { compareObservations, evaluateComparability, evaluateExpectedDependencies, compareTargetConfiguration } from './domain/comparisonEngine.js';
16
+ export { COMPARISON_MANIFEST_FILENAME, writeComparisonArtifact } from './artifacts/comparisonArtifactWriter.js';
17
+ export { readObservationArtifact } from './artifacts/artifactReader.js';
18
+ export { DEFAULT_COMPARISON_OUTPUT_LOCATION, compareAndPersist, compareAndPersistFromArtifactRoots } from './application/comparisonService.js';
12
19
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG5D,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGlG,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAmC1D,OAAO,EACL,aAAa,EACb,cAAc,EACd,aAAa,EACb,qBAAqB,EACrB,eAAe,EACf,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAetF,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGzH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAG3E,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEvF,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG3E,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGlG,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAmC1D,OAAO,EACL,aAAa,EACb,cAAc,EACd,aAAa,EACb,qBAAqB,EACrB,eAAe,EACf,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAetF,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGzH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAG3E,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEvF,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAwBnH,OAAO,EACL,8BAA8B,EAC9B,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,EAC5B,2BAA2B,EAC3B,+BAA+B,EAC/B,wBAAwB,EACxB,2BAA2B,EAC3B,6BAA6B,EAC7B,wCAAwC,EACxC,+BAA+B,EAC/B,kCAAkC,EAClC,iCAAiC,EACjC,eAAe,EACf,wBAAwB,EACxB,wBAAwB,EACxB,iCAAiC,EACjC,kCAAkC,EAClC,mCAAmC,EACnC,8BAA8B,EAC9B,6BAA6B,EAC7B,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,2BAA2B,CAAC;AA0BnC,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,6BAA6B,EAC7B,yBAAyB,EACzB,yBAAyB,EACzB,0BAA0B,EAC1B,qBAAqB,EACrB,qBAAqB,EACrB,2BAA2B,EAC3B,oCAAoC,EACpC,4BAA4B,EAC5B,iCAAiC,EACjC,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,6BAA6B,EAC7B,0BAA0B,EAC1B,iCAAiC,EACjC,gCAAgC,EAChC,gBAAgB,EAChB,2BAA2B,EAC3B,2CAA2C,EAC3C,yBAAyB,GAC1B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAGzG,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAGpJ,OAAO,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAGhH,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EAAE,kCAAkC,EAAE,iBAAiB,EAAE,kCAAkC,EAAE,MAAM,oCAAoC,CAAC"}
@@ -3,7 +3,7 @@
3
3
  ## Current scaffold architecture
4
4
 
5
5
  The current repository is one published TypeScript ESM package
6
- (`my-frontend-observer@0.3.0`):
6
+ (`my-frontend-observer@0.4.0`):
7
7
 
8
8
  - `src/cli.ts` is the real, thin `observe` command entry point (argument
9
9
  parsing/output only).
@@ -45,8 +45,8 @@ Playwright or the filesystem-write path directly. Batch 6 closed the
45
45
  remaining real-Chromium coverage gap (a genuine navigation failure, distinct
46
46
  from a readiness timeout or a pre-launch safety rejection) and validated the
47
47
  packed npm tarball end to end in a clean consumer environment, independent
48
- of the source checkout. There is still no controlled-scroll/comparison
49
- behavior.
48
+ of the source checkout. At the end of the v0.1 implementation there was no
49
+ controlled-scroll or comparison behavior.
50
50
 
51
51
  ## Current v0.2 architecture (released/current architecture)
52
52
 
@@ -108,6 +108,79 @@ is still one canonical `observe()` application use case and one artifact
108
108
  writer; `scrollScenarioEvidence` is simply one more optional field on the
109
109
  same `ObservationArtifact`.
110
110
 
111
+ ## Current v0.4 architecture (released/current architecture)
112
+
113
+ v0.4 adds one new downstream pipeline that consumes `ObservationArtifact`
114
+ values rather than producing them - it never adds a second browser lifecycle,
115
+ target resolver, or observation engine:
116
+
117
+ ```text
118
+ ObservationArtifact before ObservationArtifact after
119
+ \ /
120
+ `--------. .-------'
121
+ \ /
122
+ artifact reader (src/artifacts/artifactReader.ts)
123
+ ↓
124
+ comparability evaluation (src/domain/comparisonEngine.ts)
125
+ ↓
126
+ canonical relationship derivation, called for each side independently
127
+ (src/domain/relationships.ts#deriveLayoutRelationships)
128
+ ↓
129
+ canonical comparison derivation
130
+ (src/domain/comparisonEngine.ts#compareObservations)
131
+ ↓
132
+ ComparisonArtifact
133
+ ↓
134
+ atomic comparison writer (src/artifacts/comparisonArtifactWriter.ts)
135
+
136
+ CLI `compare`
137
+ ↓
138
+ application service only (src/application/comparisonService.ts)
139
+ ↓
140
+ [reader → domain comparison → writer, as above]
141
+ ```
142
+
143
+ `src/domain/relationships.ts` froze the layout-relationship contract and
144
+ implements the one canonical pure derivation,
145
+ `deriveLayoutRelationships(observation, options?)`: horizontal/vertical
146
+ order, area overlap, relative width, geometric fit, vertical sequencing,
147
+ page-width fit/exceeds, and a standalone `deriveTargetClipping(record)` -
148
+ all computed only from an already-captured `ObservationArtifact`'s own
149
+ `targetEvidence`/`pageEvidence`, never from a second browser query. DOM
150
+ containment is read directly from the existing v0.2 `TargetContainment`
151
+ evidence rather than re-derived, and stays a distinct concept from
152
+ geometric fit.
153
+
154
+ `src/domain/comparisonEngine.ts` implements the one canonical pure
155
+ before/after engine, `compareObservations(before, after, config?)`:
156
+ validates both source artifacts, evaluates comparability *before* any
157
+ rendered difference is calculated, calls `deriveLayoutRelationships` once
158
+ per side with the same tolerance, and derives target/page differences and
159
+ relationship changes. `src/artifacts/comparisonArtifactWriter.ts` persists
160
+ the result atomically (sibling temp directory, then one rename) as
161
+ `<outputLocation>/<comparisonId>/manifest.json` only - no screenshot is
162
+ copied; the manifest's `before`/`after` references point back to the
163
+ source observations' own `screenshot.path`. `src/application/
164
+ comparisonService.ts` is the one application-layer seam: `compareAndPersist`
165
+ takes two in-memory `ObservationArtifact`s and does exactly one comparison
166
+ plus exactly one persist; `compareAndPersistFromArtifactRoots` is a thin
167
+ wrapper that additionally reads both sides from disk via the existing
168
+ `src/artifacts/artifactReader.ts#readObservationArtifact` reader (itself
169
+ just a `manifest.json` parse plus the same `isValidObservationArtifact`
170
+ structural gate the writer uses).
171
+
172
+ `src/cli.ts` gained one new top-level command, `compare`
173
+ (`--before`/`--after`/`--output`/`--config-file`), implemented with the same
174
+ thin-CLI-boundary discipline as `observe`: `parseCompareArgs` handles only
175
+ argument shape/duplication, an optional `loadComparisonConfigFile` reads and
176
+ validates only file readability/JSON-validity/non-array-object-root (exactly
177
+ like `--targets-file`/`--scroll-scenario-file`), and the command body calls
178
+ `compareAndPersistFromArtifactRoots` exactly once. **The CLI's comparison
179
+ path never launches Chromium** - `src/cli.ts` imports nothing from
180
+ `src/browser/` or `src/artifacts/` (it only reaches persistence and artifact
181
+ reading indirectly, through the application-layer seam above), matching the
182
+ same import-boundary discipline already enforced for `observe`.
183
+
111
184
  ## Planned v0.1 architecture constraints
112
185
 
113
186
  v0.1 planning must preserve these approved boundaries without treating module
package/docs/CI_CD.md CHANGED
@@ -67,3 +67,32 @@ observations. This was proven on `validation/v0.3-pre-release`
67
67
  hash-verified candidate tarball (SHA-256
68
68
  `da6cbc18a98d005b84a2f12c06f5c01d390006b53c02176a6c9cdc6d9b9d24d0`) - the
69
69
  release candidate for `v0.3.0`.
70
+
71
+ ## v0.4 readiness coverage
72
+
73
+ The packed-candidate readiness gap that previously existed for the v0.4
74
+ `compare` public command - identified during the v0.4 implementation/
75
+ documentation audit as `V0_4_READINESS_VALIDATION_GAP_EXISTS` - has been
76
+ closed: `scripts/ci/runPackedObservationSmoke.mjs` now also installs the
77
+ candidate tarball, observes a deterministic before/after fixture through
78
+ the installed `observe`, and runs the installed `compare` against both
79
+ resulting artifacts, asserting (through the *installed package's own*
80
+ exported `isValidComparisonArtifact`/`isValidObservationArtifact`, resolved
81
+ from the consumer's own `node_modules` rather than the source checkout):
82
+ comparison artifact kind/schema `1.0.0`, a `comparable` result with a real
83
+ `moved`+`resized` target difference and a real relationship-family
84
+ transition (`does-not-overlap`→`overlaps` and `left-of`→
85
+ `horizontally-overlapping` simultaneously - the same pair the v0.4
86
+ relationship-family matching regression concerns), an explicit
87
+ `--config-file` non-causal dependency-evidence result, an `incomparable`
88
+ result (differing viewport) with the expected blocking comparability
89
+ reason and no fabricated ordinary differences, comparison-manifest path
90
+ privacy (no absolute operational path leakage), retained (never copied)
91
+ before/after screenshot references, a `manifest.json`-only comparison
92
+ directory, and byte-identical source-observation manifests/screenshots
93
+ before and after comparing - on every platform in the matrix, using the
94
+ same single hash-verified candidate tarball as the legacy/semantic/scroll
95
+ observations. This preserves every pre-existing v0.1-v0.3 packed
96
+ observation proof unchanged and adds the v0.4 proof additively, in the same
97
+ script and the same candidate, per the established same-candidate
98
+ architecture (no second `npm pack`, no per-platform rebuild).