my-frontend-observer 0.1.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 (69) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +69 -0
  3. package/dist/application/browserCaptureService.d.ts +11 -0
  4. package/dist/application/browserCaptureService.js +12 -0
  5. package/dist/application/browserCaptureService.js.map +1 -0
  6. package/dist/application/observationPersistence.d.ts +59 -0
  7. package/dist/application/observationPersistence.js +78 -0
  8. package/dist/application/observationPersistence.js.map +1 -0
  9. package/dist/artifacts/artifactWriter.d.ts +25 -0
  10. package/dist/artifacts/artifactWriter.js +68 -0
  11. package/dist/artifacts/artifactWriter.js.map +1 -0
  12. package/dist/artifacts/types.d.ts +17 -0
  13. package/dist/artifacts/types.js +2 -0
  14. package/dist/artifacts/types.js.map +1 -0
  15. package/dist/browser/chromiumAdapter.d.ts +21 -0
  16. package/dist/browser/chromiumAdapter.js +150 -0
  17. package/dist/browser/chromiumAdapter.js.map +1 -0
  18. package/dist/browser/evidenceCapture.d.ts +19 -0
  19. package/dist/browser/evidenceCapture.js +201 -0
  20. package/dist/browser/evidenceCapture.js.map +1 -0
  21. package/dist/browser/types.d.ts +22 -0
  22. package/dist/browser/types.js +2 -0
  23. package/dist/browser/types.js.map +1 -0
  24. package/dist/cli.d.ts +7 -0
  25. package/dist/cli.js +216 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/domain/completion.d.ts +30 -0
  28. package/dist/domain/completion.js +22 -0
  29. package/dist/domain/completion.js.map +1 -0
  30. package/dist/domain/diagnostics.d.ts +17 -0
  31. package/dist/domain/diagnostics.js +55 -0
  32. package/dist/domain/diagnostics.js.map +1 -0
  33. package/dist/domain/evidence.d.ts +27 -0
  34. package/dist/domain/evidence.js +55 -0
  35. package/dist/domain/evidence.js.map +1 -0
  36. package/dist/domain/identity.d.ts +13 -0
  37. package/dist/domain/identity.js +37 -0
  38. package/dist/domain/identity.js.map +1 -0
  39. package/dist/domain/schema.d.ts +111 -0
  40. package/dist/domain/schema.js +126 -0
  41. package/dist/domain/schema.js.map +1 -0
  42. package/dist/index.d.ts +21 -0
  43. package/dist/index.js +12 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/request/paths.d.ts +14 -0
  46. package/dist/request/paths.js +33 -0
  47. package/dist/request/paths.js.map +1 -0
  48. package/dist/request/request.d.ts +43 -0
  49. package/dist/request/request.js +174 -0
  50. package/dist/request/request.js.map +1 -0
  51. package/dist/safety/policy.d.ts +14 -0
  52. package/dist/safety/policy.js +81 -0
  53. package/dist/safety/policy.js.map +1 -0
  54. package/docs/ARCHITECTURE.md +85 -0
  55. package/docs/CI_CD.md +28 -0
  56. package/docs/COMMANDS.md +82 -0
  57. package/docs/CONTRACTS.md +54 -0
  58. package/docs/CURRENT_STATE.md +113 -0
  59. package/docs/DEVELOPMENT.md +65 -0
  60. package/docs/DOCUMENTATION_PRESERVATION_POLICY.md +33 -0
  61. package/docs/PROJECT_DESCRIPTION.md +1770 -0
  62. package/docs/PROJECT_MILESTONES.md +2073 -0
  63. package/docs/PROJECT_OVERVIEW.md +53 -0
  64. package/docs/QUICKSTART.md +35 -0
  65. package/docs/RELEASE.md +9 -0
  66. package/docs/ROADMAP.md +352 -0
  67. package/docs/SECURITY.md +33 -0
  68. package/docs/WORKFLOWS.md +51 -0
  69. package/package.json +46 -0
@@ -0,0 +1,55 @@
1
+ function isPlainObject(value) {
2
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3
+ }
4
+ const EVIDENCE_SOURCES = ['browser', 'computed-browser', 'derived'];
5
+ function isEvidenceSource(value) {
6
+ return typeof value === 'string' && EVIDENCE_SOURCES.includes(value);
7
+ }
8
+ function isDerivedFrom(value) {
9
+ return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === 'string');
10
+ }
11
+ /**
12
+ * Structural validator for the frozen EvidenceField<T> contract. Enforces the
13
+ * per-state field-combination invariants (available/partial require
14
+ * source+value; unavailable/not-applicable forbid both) and the
15
+ * derivedFrom-iff-derived-source invariant.
16
+ */
17
+ export function isValidEvidenceField(field) {
18
+ if (!isPlainObject(field))
19
+ return false;
20
+ const state = field.state;
21
+ if (state === 'available' || state === 'partial') {
22
+ if (!isEvidenceSource(field.source))
23
+ return false;
24
+ if (!('value' in field) || field.value === undefined)
25
+ return false;
26
+ if (state === 'partial' && (typeof field.reason !== 'string' || field.reason.length === 0))
27
+ return false;
28
+ if (field.source === 'derived') {
29
+ if (!isDerivedFrom(field.derivedFrom))
30
+ return false;
31
+ }
32
+ else if ('derivedFrom' in field && field.derivedFrom !== undefined) {
33
+ return false;
34
+ }
35
+ return true;
36
+ }
37
+ if (state === 'unavailable') {
38
+ if ('value' in field && field.value !== undefined)
39
+ return false;
40
+ if ('source' in field && field.source !== undefined)
41
+ return false;
42
+ return typeof field.reason === 'string' && field.reason.length > 0;
43
+ }
44
+ if (state === 'not-applicable') {
45
+ if ('value' in field && field.value !== undefined)
46
+ return false;
47
+ if ('source' in field && field.source !== undefined)
48
+ return false;
49
+ if ('reason' in field && field.reason !== undefined && typeof field.reason !== 'string')
50
+ return false;
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ //# sourceMappingURL=evidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/domain/evidence.ts"],"names":[],"mappings":"AAUA,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,kBAAkB,EAAE,SAAS,CAAU,CAAC;AAE7E,SAAS,gBAAgB,CAAC,KAAc;IACtC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,gBAAsC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC9F,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC;AACvG,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAc,KAAc;IAC9D,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAE1B,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACjD,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QAClD,IAAI,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACnE,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QACzG,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC;gBAAE,OAAO,KAAK,CAAC;QACtD,CAAC;aAAM,IAAI,aAAa,IAAI,KAAK,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACrE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;QAC5B,IAAI,OAAO,IAAI,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAChE,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAClE,OAAO,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,KAAK,KAAK,gBAAgB,EAAE,CAAC;QAC/B,IAAI,OAAO,IAAI,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAChE,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAClE,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QACtG,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { NormalizedObservationRequest } from '../request/request.js';
2
+ /**
3
+ * Pure function of {targetUrl, viewport, targets (order preserved -
4
+ * semantic), readiness.condition} only. timeoutMs, readiness.timeoutMs, and
5
+ * outputLocation never affect the result - this is the frozen definition of
6
+ * "same configuration" for this batch.
7
+ */
8
+ export declare function buildRequestIdentity(request: NormalizedObservationRequest): string;
9
+ /**
10
+ * Fresh, opaque per-call identity - never derived from capturedAt/Date.now(),
11
+ * never collides even when called twice within the same millisecond.
12
+ */
13
+ export declare function buildObservationIdentity(requestIdentity: string): string;
@@ -0,0 +1,37 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ function canonicalize(value) {
3
+ if (Array.isArray(value))
4
+ return value.map(canonicalize);
5
+ if (value !== null && typeof value === 'object') {
6
+ const entries = Object.entries(value)
7
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
8
+ .map(([key, entryValue]) => [key, canonicalize(entryValue)]);
9
+ return Object.fromEntries(entries);
10
+ }
11
+ return value;
12
+ }
13
+ /**
14
+ * Pure function of {targetUrl, viewport, targets (order preserved -
15
+ * semantic), readiness.condition} only. timeoutMs, readiness.timeoutMs, and
16
+ * outputLocation never affect the result - this is the frozen definition of
17
+ * "same configuration" for this batch.
18
+ */
19
+ export function buildRequestIdentity(request) {
20
+ const semanticView = {
21
+ targetUrl: request.targetUrl,
22
+ viewport: { width: request.viewport.width, height: request.viewport.height },
23
+ targets: request.targets.map((target) => ({ name: target.name.toLowerCase(), selector: target.selector })),
24
+ readinessCondition: request.readiness.condition,
25
+ };
26
+ const serialized = JSON.stringify(canonicalize(semanticView));
27
+ return createHash('sha256').update(serialized).digest('hex');
28
+ }
29
+ /**
30
+ * Fresh, opaque per-call identity - never derived from capturedAt/Date.now(),
31
+ * never collides even when called twice within the same millisecond.
32
+ */
33
+ export function buildObservationIdentity(requestIdentity) {
34
+ const nonce = randomBytes(16).toString('hex');
35
+ return `${requestIdentity}-${nonce}`;
36
+ }
37
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/domain/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGtD,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;aAC7D,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aAChD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,YAAY,CAAC,UAAU,CAAC,CAAU,CAAC,CAAC;QACxE,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAqC;IACxE,MAAM,YAAY,GAAG;QACnB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,QAAQ,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE;QAC5E,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC1G,kBAAkB,EAAE,OAAO,CAAC,SAAS,CAAC,SAAS;KAChD,CAAC;IACF,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;IAC9D,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,eAAuB;IAC9D,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,GAAG,eAAe,IAAI,KAAK,EAAE,CAAC;AACvC,CAAC"}
@@ -0,0 +1,111 @@
1
+ import type { EvidenceField } from './evidence.js';
2
+ import type { Diagnostic } from './diagnostics.js';
3
+ import type { CompletionState } from './completion.js';
4
+ import type { NormalizedObservationRequest } from '../request/request.js';
5
+ export declare const ARTIFACT_KIND: "my-frontend-observer/observation";
6
+ export declare const SCHEMA_VERSION: "1.0.0";
7
+ export declare const PRODUCER_NAME: "my-frontend-observer";
8
+ export interface ArtifactReference {
9
+ path: string;
10
+ kind: string;
11
+ }
12
+ export interface TargetGeometry {
13
+ x: number;
14
+ y: number;
15
+ width: number;
16
+ height: number;
17
+ right: number;
18
+ bottom: number;
19
+ }
20
+ export interface TargetComputedStyle {
21
+ display: string;
22
+ position: string;
23
+ overflowX: string;
24
+ overflowY: string;
25
+ }
26
+ export interface TargetLayoutMetrics {
27
+ scrollWidth: number;
28
+ scrollHeight: number;
29
+ clientWidth: number;
30
+ clientHeight: number;
31
+ scrollTop: number;
32
+ scrollLeft: number;
33
+ }
34
+ export interface TargetVisibility {
35
+ visible: boolean;
36
+ }
37
+ export interface TargetSemantics {
38
+ role?: string;
39
+ name?: string;
40
+ }
41
+ /**
42
+ * Batch 1 froze the wrapper (EvidenceField<T>) and the category names
43
+ * (resolution/geometry/style) but deliberately left geometry/style typed
44
+ * `unknown`, deferring the concrete rendered-evidence shapes to whichever
45
+ * batch actually captures them. Batch 3 fills that in and adds the
46
+ * remaining v0.1 target-evidence categories (tag/layout/visibility/
47
+ * semantics) additively: nothing in the repository constructs a
48
+ * TargetEvidenceRecord yet (only `{}` empty records exist in tests), so no
49
+ * existing caller loses a field.
50
+ */
51
+ export interface TargetEvidenceRecord {
52
+ resolution: EvidenceField<{
53
+ selectionMethod: string;
54
+ selectionStatus: string;
55
+ }>;
56
+ tag: EvidenceField<string>;
57
+ geometry: EvidenceField<TargetGeometry>;
58
+ style: EvidenceField<TargetComputedStyle>;
59
+ layout: EvidenceField<TargetLayoutMetrics>;
60
+ visibility: EvidenceField<TargetVisibility>;
61
+ semantics: EvidenceField<TargetSemantics>;
62
+ }
63
+ export interface ObservationArtifact {
64
+ artifactKind: typeof ARTIFACT_KIND;
65
+ schemaVersion: typeof SCHEMA_VERSION;
66
+ observationId: string;
67
+ requestId: string;
68
+ producer: {
69
+ name: typeof PRODUCER_NAME;
70
+ version: string;
71
+ };
72
+ browser: EvidenceField<{
73
+ engine: string;
74
+ version: string;
75
+ }>;
76
+ requestConfig: NormalizedObservationRequest;
77
+ provenance: {
78
+ capturedAt: string;
79
+ observationMethod: string;
80
+ };
81
+ pageEvidence: Record<string, EvidenceField<unknown>>;
82
+ targetEvidence: Record<string, TargetEvidenceRecord>;
83
+ screenshot: EvidenceField<{
84
+ path: string;
85
+ }>;
86
+ completion: CompletionState;
87
+ diagnostics: Diagnostic[];
88
+ limits: {
89
+ truncated: boolean;
90
+ omittedFields: string[];
91
+ omittedTargets: string[];
92
+ };
93
+ artifactReferences: ArtifactReference[];
94
+ }
95
+ export type SchemaValidationResult = {
96
+ valid: true;
97
+ } | {
98
+ valid: false;
99
+ reason: string;
100
+ };
101
+ /**
102
+ * producer.version is read independently from package.json; it is never
103
+ * equal to schemaVersion by construction - the two are different fields with
104
+ * unrelated value spaces.
105
+ */
106
+ export declare function getProducerInfo(): {
107
+ name: typeof PRODUCER_NAME;
108
+ version: string;
109
+ };
110
+ /** Structural validator for the frozen schema-1.0.0 ObservationArtifact contract. Treats `value` as arbitrary/unknown input. */
111
+ export declare function isValidObservationArtifact(value: unknown): SchemaValidationResult;
@@ -0,0 +1,126 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import path from 'node:path';
4
+ import { isValidEvidenceField } from './evidence.js';
5
+ import { DIAGNOSTIC_CODES, DIAGNOSTIC_SEVERITY } from './diagnostics.js';
6
+ export const ARTIFACT_KIND = 'my-frontend-observer/observation';
7
+ export const SCHEMA_VERSION = '1.0.0';
8
+ export const PRODUCER_NAME = 'my-frontend-observer';
9
+ function readPackageVersion() {
10
+ const here = path.dirname(fileURLToPath(import.meta.url));
11
+ const packageJsonPath = path.join(here, '..', '..', 'package.json');
12
+ const raw = readFileSync(packageJsonPath, 'utf8');
13
+ const parsed = JSON.parse(raw);
14
+ if (typeof parsed.version !== 'string' || parsed.version.length === 0) {
15
+ throw new Error('package.json is missing a string "version" field');
16
+ }
17
+ return parsed.version;
18
+ }
19
+ /**
20
+ * producer.version is read independently from package.json; it is never
21
+ * equal to schemaVersion by construction - the two are different fields with
22
+ * unrelated value spaces.
23
+ */
24
+ export function getProducerInfo() {
25
+ return { name: PRODUCER_NAME, version: readPackageVersion() };
26
+ }
27
+ function isPlainObject(value) {
28
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
29
+ }
30
+ function isValidDiagnostic(value) {
31
+ if (!isPlainObject(value))
32
+ return false;
33
+ const code = value.code;
34
+ if (typeof code !== 'string' || !DIAGNOSTIC_CODES.includes(code))
35
+ return false;
36
+ const expectedSeverity = DIAGNOSTIC_SEVERITY[code];
37
+ if (value.severity !== expectedSeverity)
38
+ return false;
39
+ if (typeof value.message !== 'string')
40
+ return false;
41
+ if ('targetName' in value && value.targetName !== undefined && typeof value.targetName !== 'string')
42
+ return false;
43
+ return true;
44
+ }
45
+ function isValidCompletionState(value) {
46
+ if (!isPlainObject(value))
47
+ return false;
48
+ const state = value.state;
49
+ if (state === 'complete') {
50
+ if (!('diagnostics' in value) || value.diagnostics === undefined)
51
+ return true;
52
+ return Array.isArray(value.diagnostics) && value.diagnostics.length === 0;
53
+ }
54
+ if (state === 'partial' || state === 'warning' || state === 'invalid-request' || state === 'fatal') {
55
+ return Array.isArray(value.diagnostics) && value.diagnostics.every((d) => isValidDiagnostic(d));
56
+ }
57
+ return false;
58
+ }
59
+ /** Structural validator for the frozen schema-1.0.0 ObservationArtifact contract. Treats `value` as arbitrary/unknown input. */
60
+ export function isValidObservationArtifact(value) {
61
+ if (!isPlainObject(value))
62
+ return { valid: false, reason: 'artifact must be an object' };
63
+ if (value.artifactKind !== ARTIFACT_KIND)
64
+ return { valid: false, reason: 'artifactKind mismatch' };
65
+ if (value.schemaVersion !== SCHEMA_VERSION)
66
+ return { valid: false, reason: 'schemaVersion mismatch' };
67
+ if (typeof value.observationId !== 'string' || value.observationId.length === 0) {
68
+ return { valid: false, reason: 'observationId must be a non-empty string' };
69
+ }
70
+ if (typeof value.requestId !== 'string' || value.requestId.length === 0) {
71
+ return { valid: false, reason: 'requestId must be a non-empty string' };
72
+ }
73
+ if (!isPlainObject(value.producer) ||
74
+ value.producer.name !== PRODUCER_NAME ||
75
+ typeof value.producer.version !== 'string' ||
76
+ value.producer.version.length === 0) {
77
+ return { valid: false, reason: 'producer must be { name: "my-frontend-observer", version: string }' };
78
+ }
79
+ if (!isValidEvidenceField(value.browser))
80
+ return { valid: false, reason: 'browser evidence field is invalid' };
81
+ if (!isPlainObject(value.pageEvidence) || !Object.values(value.pageEvidence).every((v) => isValidEvidenceField(v))) {
82
+ return { valid: false, reason: 'pageEvidence entries must be valid evidence fields' };
83
+ }
84
+ if (!isPlainObject(value.targetEvidence))
85
+ return { valid: false, reason: 'targetEvidence must be an object' };
86
+ for (const target of Object.values(value.targetEvidence)) {
87
+ if (!isPlainObject(target) ||
88
+ !isValidEvidenceField(target.resolution) ||
89
+ !isValidEvidenceField(target.tag) ||
90
+ !isValidEvidenceField(target.geometry) ||
91
+ !isValidEvidenceField(target.style) ||
92
+ !isValidEvidenceField(target.layout) ||
93
+ !isValidEvidenceField(target.visibility) ||
94
+ !isValidEvidenceField(target.semantics)) {
95
+ return {
96
+ valid: false,
97
+ reason: 'targetEvidence entries must have valid resolution/tag/geometry/style/layout/visibility/semantics evidence fields',
98
+ };
99
+ }
100
+ }
101
+ if (!isValidEvidenceField(value.screenshot))
102
+ return { valid: false, reason: 'screenshot evidence field is invalid' };
103
+ if (!isValidCompletionState(value.completion))
104
+ return { valid: false, reason: 'completion state is invalid' };
105
+ if (!Array.isArray(value.diagnostics) || !value.diagnostics.every((d) => isValidDiagnostic(d))) {
106
+ return { valid: false, reason: 'diagnostics must be an array of valid Diagnostic values' };
107
+ }
108
+ if (isPlainObject(value.completion) && value.completion.state === 'complete' && value.diagnostics.length > 0) {
109
+ return { valid: false, reason: 'completion "complete" requires an empty diagnostics array' };
110
+ }
111
+ const limits = value.limits;
112
+ if (!isPlainObject(limits) ||
113
+ typeof limits.truncated !== 'boolean' ||
114
+ !Array.isArray(limits.omittedFields) ||
115
+ !limits.omittedFields.every((f) => typeof f === 'string') ||
116
+ !Array.isArray(limits.omittedTargets) ||
117
+ !limits.omittedTargets.every((f) => typeof f === 'string')) {
118
+ return { valid: false, reason: 'limits must be { truncated: boolean; omittedFields: string[]; omittedTargets: string[] }' };
119
+ }
120
+ if (!Array.isArray(value.artifactReferences) ||
121
+ !value.artifactReferences.every((r) => isPlainObject(r) && typeof r.path === 'string' && typeof r.kind === 'string')) {
122
+ return { valid: false, reason: 'artifactReferences must be an array of { path: string; kind: string }' };
123
+ }
124
+ return { valid: true };
125
+ }
126
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/domain/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ export type { EvidenceState, EvidenceSource, EvidenceField } from './domain/evidence.js';
2
+ export { isValidEvidenceField } from './domain/evidence.js';
3
+ export type { DiagnosticCode, DiagnosticSeverity, Diagnostic } from './domain/diagnostics.js';
4
+ export { DIAGNOSTIC_CODES, DIAGNOSTIC_SEVERITY, orderDiagnostics } from './domain/diagnostics.js';
5
+ export type { CompletionState, RequestPhase } from './domain/completion.js';
6
+ export { deriveCompletion } from './domain/completion.js';
7
+ export type { ObservationArtifact, ArtifactReference, TargetEvidenceRecord, TargetGeometry, TargetComputedStyle, TargetLayoutMetrics, TargetVisibility, TargetSemantics, SchemaValidationResult, } from './domain/schema.js';
8
+ export { ARTIFACT_KIND, SCHEMA_VERSION, PRODUCER_NAME, getProducerInfo, isValidObservationArtifact } from './domain/schema.js';
9
+ export { buildRequestIdentity, buildObservationIdentity } from './domain/identity.js';
10
+ export type { NamedTarget, Viewport, ReadinessCondition, ReadinessConfig, NormalizedObservationRequest, RawObservationRequest, NormalizeRequestResult, } from './request/request.js';
11
+ export { normalizeRequest } from './request/request.js';
12
+ export type { NormalizeOutputLocationResult } from './request/paths.js';
13
+ export { normalizeOutputLocation } from './request/paths.js';
14
+ export type { SafetyDecision } from './safety/policy.js';
15
+ export { classifyUrl, classifyRedirect, classifySubresource, classifyPopup, classifyDownload } from './safety/policy.js';
16
+ export type { BrowserCaptureResult, BrowserProvenance } from './browser/types.js';
17
+ export { runBrowserCapture } from './application/browserCaptureService.js';
18
+ export type { PersistedObservationResult } from './artifacts/types.js';
19
+ export { MANIFEST_FILENAME, SCREENSHOT_FILENAME } from './artifacts/artifactWriter.js';
20
+ export type { ApplicationObservationResult } from './application/observationPersistence.js';
21
+ export { buildObservationArtifact, persistBrowserCapture, observe } from './application/observationPersistence.js';
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ export { isValidEvidenceField } from './domain/evidence.js';
2
+ export { DIAGNOSTIC_CODES, DIAGNOSTIC_SEVERITY, orderDiagnostics } from './domain/diagnostics.js';
3
+ export { deriveCompletion } from './domain/completion.js';
4
+ export { ARTIFACT_KIND, SCHEMA_VERSION, PRODUCER_NAME, getProducerInfo, isValidObservationArtifact } from './domain/schema.js';
5
+ export { buildRequestIdentity, buildObservationIdentity } from './domain/identity.js';
6
+ export { normalizeRequest } from './request/request.js';
7
+ export { normalizeOutputLocation } from './request/paths.js';
8
+ export { classifyUrl, classifyRedirect, classifySubresource, classifyPopup, classifyDownload } from './safety/policy.js';
9
+ export { runBrowserCapture } from './application/browserCaptureService.js';
10
+ export { MANIFEST_FILENAME, SCREENSHOT_FILENAME } from './artifacts/artifactWriter.js';
11
+ export { buildObservationArtifact, persistBrowserCapture, observe } from './application/observationPersistence.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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;AAa1D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAE/H,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAWtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxD,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"}
@@ -0,0 +1,14 @@
1
+ import type { Diagnostic } from '../domain/diagnostics.js';
2
+ export type NormalizeOutputLocationResult = {
3
+ ok: true;
4
+ value: string;
5
+ } | {
6
+ ok: false;
7
+ diagnostic: Diagnostic;
8
+ };
9
+ /**
10
+ * Portable, relative, forward-slash logical path only: no drive letter, no
11
+ * leading "/", no ".." segment. Backslashes are converted to forward slashes
12
+ * before validation. Never touches the real filesystem.
13
+ */
14
+ export declare function normalizeOutputLocation(raw: string): NormalizeOutputLocationResult;
@@ -0,0 +1,33 @@
1
+ const DRIVE_LETTER_RE = /^[A-Za-z]:\//;
2
+ /**
3
+ * Portable, relative, forward-slash logical path only: no drive letter, no
4
+ * leading "/", no ".." segment. Backslashes are converted to forward slashes
5
+ * before validation. Never touches the real filesystem.
6
+ */
7
+ export function normalizeOutputLocation(raw) {
8
+ const withForwardSlashes = raw.replace(/\\/g, '/');
9
+ if (withForwardSlashes.startsWith('/') || DRIVE_LETTER_RE.test(withForwardSlashes)) {
10
+ return {
11
+ ok: false,
12
+ diagnostic: {
13
+ code: 'invalid-request',
14
+ severity: 'error',
15
+ message: 'outputLocation must be a relative, portable path (no drive letter or leading "/")',
16
+ },
17
+ };
18
+ }
19
+ const segments = withForwardSlashes.split('/');
20
+ if (segments.some((segment) => segment === '..')) {
21
+ return {
22
+ ok: false,
23
+ diagnostic: {
24
+ code: 'invalid-request',
25
+ severity: 'error',
26
+ message: 'outputLocation must not contain ".." segments',
27
+ },
28
+ };
29
+ }
30
+ const cleaned = segments.filter((segment) => segment.length > 0 && segment !== '.');
31
+ return { ok: true, value: cleaned.length > 0 ? cleaned.join('/') : '.' };
32
+ }
33
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/request/paths.ts"],"names":[],"mappings":"AAIA,MAAM,eAAe,GAAG,cAAc,CAAC;AAEvC;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,GAAW;IACjD,MAAM,kBAAkB,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAEnD,IAAI,kBAAkB,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACnF,OAAO;YACL,EAAE,EAAE,KAAK;YACT,UAAU,EAAE;gBACV,IAAI,EAAE,iBAAiB;gBACvB,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,mFAAmF;aAC7F;SACF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC;QACjD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,UAAU,EAAE;gBACV,IAAI,EAAE,iBAAiB;gBACvB,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,+CAA+C;aACzD;SACF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,KAAK,GAAG,CAAC,CAAC;IACpF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { Diagnostic } from '../domain/diagnostics.js';
2
+ export interface NamedTarget {
3
+ name: string;
4
+ selector: string;
5
+ }
6
+ export interface Viewport {
7
+ width: number;
8
+ height: number;
9
+ }
10
+ export type ReadinessCondition = 'load' | 'domcontentloaded';
11
+ export interface ReadinessConfig {
12
+ condition: ReadinessCondition;
13
+ timeoutMs: number;
14
+ }
15
+ export interface NormalizedObservationRequest {
16
+ targetUrl: string;
17
+ viewport: Viewport;
18
+ targets: NamedTarget[];
19
+ outputLocation: string;
20
+ timeoutMs: number;
21
+ readiness: ReadinessConfig;
22
+ }
23
+ export interface RawObservationRequest {
24
+ targetUrl?: unknown;
25
+ viewport?: unknown;
26
+ targets?: unknown;
27
+ outputLocation?: unknown;
28
+ timeoutMs?: unknown;
29
+ readiness?: unknown;
30
+ }
31
+ export type NormalizeRequestResult = {
32
+ ok: true;
33
+ request: NormalizedObservationRequest;
34
+ } | {
35
+ ok: false;
36
+ diagnostics: Diagnostic[];
37
+ };
38
+ /**
39
+ * Total, never throws. Collects every applicable diagnostic before returning
40
+ * (does not short-circuit on the first violation) so callers see every
41
+ * problem at once, in deterministic order.
42
+ */
43
+ export declare function normalizeRequest(raw: RawObservationRequest): NormalizeRequestResult;