@equipe-tech/observability 0.2.1 → 0.3.5

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 (177) hide show
  1. package/README.md +11 -9
  2. package/dist/BrowserEvents.d.ts +89 -12
  3. package/dist/BrowserEvents.js +67 -13
  4. package/dist/Correlation.d.ts +51 -0
  5. package/dist/Correlation.js +95 -0
  6. package/dist/Metrics.d.ts +19 -3
  7. package/dist/Metrics.js +16 -0
  8. package/dist/MetricsRuntime.d.ts +45 -3
  9. package/dist/MetricsRuntime.js +478 -120
  10. package/dist/PolicyOtlpLogger.d.ts +18 -0
  11. package/dist/PolicyOtlpLogger.js +142 -0
  12. package/dist/RedactionPolicy.d.ts +2 -9
  13. package/dist/RedactionPolicy.js +2 -246
  14. package/dist/ResourceIdentity.d.ts +47 -0
  15. package/dist/ResourceIdentity.js +82 -0
  16. package/dist/Telemetry.d.ts +18 -3
  17. package/dist/Telemetry.js +43 -25
  18. package/dist/TelemetryConfig.d.ts +11 -4
  19. package/dist/TelemetryConfig.js +38 -10
  20. package/dist/audit/AuditCommitDocument.d.ts +41 -0
  21. package/dist/audit/AuditCommitDocument.js +58 -0
  22. package/dist/audit/AuditDigest.d.ts +11 -0
  23. package/dist/audit/AuditDigest.js +25 -0
  24. package/dist/audit/AuditOutbox.d.ts +75 -0
  25. package/dist/audit/AuditOutbox.js +104 -0
  26. package/dist/audit/AuditPublisher.d.ts +49 -0
  27. package/dist/audit/AuditPublisher.js +60 -0
  28. package/dist/audit/AuditRecord.d.ts +128 -0
  29. package/dist/audit/AuditRecord.js +172 -0
  30. package/dist/audit/CommittedAuditRecordInternal.d.ts +11 -0
  31. package/dist/audit/CommittedAuditRecordInternal.js +13 -0
  32. package/dist/audit/InvalidAuditRecord.d.ts +12 -0
  33. package/dist/audit/InvalidAuditRecord.js +16 -0
  34. package/dist/audit/index.d.ts +7 -0
  35. package/dist/audit/index.js +7 -0
  36. package/dist/browser/BrowserClient.d.ts +82 -3
  37. package/dist/browser/BrowserClient.js +322 -28
  38. package/dist/browser/BrowserEventLimits.d.ts +8 -0
  39. package/dist/browser/BrowserEventLimits.js +8 -0
  40. package/dist/browser/BrowserMetricRecorder.d.ts +17 -0
  41. package/dist/browser/BrowserMetricRecorder.js +35 -0
  42. package/dist/browser/BrowserTelemetry.d.ts +40 -0
  43. package/dist/browser/BrowserTelemetry.js +105 -0
  44. package/dist/browser/ClientPolicy.d.ts +3 -0
  45. package/dist/browser/ClientPolicy.js +52 -0
  46. package/dist/browser/client.d.ts +3 -2
  47. package/dist/browser/client.js +2 -1
  48. package/dist/browser/index.d.ts +4 -37
  49. package/dist/browser/index.js +3 -95
  50. package/dist/contract/ContractCompatibility.d.ts +31 -0
  51. package/dist/contract/ContractCompatibility.js +274 -0
  52. package/dist/contract/ContractIndex.d.ts +54 -0
  53. package/dist/contract/ContractIndex.js +178 -0
  54. package/dist/contract/ContractSurface.d.ts +111 -0
  55. package/dist/contract/ContractSurface.js +116 -0
  56. package/dist/contract/EventName.d.ts +6 -0
  57. package/dist/contract/EventName.js +55 -0
  58. package/dist/contract/EventProducer.d.ts +123 -0
  59. package/dist/contract/EventProducer.js +209 -0
  60. package/dist/contract/MetricContractError.d.ts +19 -0
  61. package/dist/contract/MetricContractError.js +19 -0
  62. package/dist/contract/MetricDefinition.d.ts +61 -0
  63. package/dist/contract/MetricDefinition.js +91 -0
  64. package/dist/contract/MetricProducer.d.ts +42 -0
  65. package/dist/contract/MetricProducer.js +123 -0
  66. package/dist/contract/OrganizationEvents.d.ts +165 -0
  67. package/dist/contract/OrganizationEvents.js +97 -0
  68. package/dist/contract/TelemetryContract.d.ts +95 -0
  69. package/dist/contract/TelemetryContract.js +451 -0
  70. package/dist/contract/TelemetryContractError.d.ts +44 -0
  71. package/dist/contract/TelemetryContractError.js +64 -0
  72. package/dist/contract/TelemetryEvent.d.ts +59 -0
  73. package/dist/contract/TelemetryEvent.js +54 -0
  74. package/dist/contract/index.d.ts +11 -0
  75. package/dist/contract/index.js +11 -0
  76. package/dist/effect/WideEvent.d.ts +3 -0
  77. package/dist/effect/WideEvent.js +20 -0
  78. package/dist/effect/WideEventSink.d.ts +3 -0
  79. package/dist/effect/WideEventSink.js +79 -0
  80. package/dist/effect/index.d.ts +2 -0
  81. package/dist/effect/index.js +2 -0
  82. package/dist/index.d.ts +17 -2
  83. package/dist/index.js +11 -2
  84. package/dist/metrics/InstrumentGrammar.d.ts +5 -0
  85. package/dist/metrics/InstrumentGrammar.js +12 -0
  86. package/dist/metrics/MetricScalar.d.ts +3 -0
  87. package/dist/metrics/MetricScalar.js +5 -0
  88. package/dist/node/AuditDigest.d.ts +3 -0
  89. package/dist/node/AuditDigest.js +6 -0
  90. package/dist/node/BrowserEventIngest.d.ts +7 -2
  91. package/dist/node/BrowserEventIngest.js +111 -19
  92. package/dist/node/LocalCollector.d.ts +20 -0
  93. package/dist/node/LocalCollector.js +188 -0
  94. package/dist/node/Observability.d.ts +49 -0
  95. package/dist/node/Observability.js +190 -0
  96. package/dist/node/Runtime.d.ts +4 -1
  97. package/dist/node/index.d.ts +4 -0
  98. package/dist/node/index.js +3 -0
  99. package/dist/policy/BrowserFieldPolicy.d.ts +7 -0
  100. package/dist/policy/BrowserFieldPolicy.js +298 -0
  101. package/dist/policy/DataPolicy.d.ts +19 -0
  102. package/dist/policy/DataPolicy.js +254 -0
  103. package/dist/policy/DataPolicyError.d.ts +22 -0
  104. package/dist/policy/DataPolicyError.js +25 -0
  105. package/dist/policy/Deduplication.d.ts +13 -0
  106. package/dist/policy/Deduplication.js +44 -0
  107. package/dist/policy/DefectEnvelope.d.ts +25 -0
  108. package/dist/policy/DefectEnvelope.js +52 -0
  109. package/dist/policy/MetricLabelPolicy.d.ts +5 -0
  110. package/dist/policy/MetricLabelPolicy.js +65 -0
  111. package/dist/policy/PolicyTransform.d.ts +16 -0
  112. package/dist/policy/PolicyTransform.js +136 -0
  113. package/dist/policy/PolicyVocabulary.d.ts +10 -0
  114. package/dist/policy/PolicyVocabulary.js +84 -0
  115. package/dist/policy/ResourceAttributePolicy.d.ts +9 -0
  116. package/dist/policy/ResourceAttributePolicy.js +33 -0
  117. package/dist/policy/entrypoint.d.ts +2 -0
  118. package/dist/policy/entrypoint.js +2 -0
  119. package/dist/policy/index.d.ts +17 -0
  120. package/dist/policy/index.js +9 -0
  121. package/dist/profile/DataPolicy.d.ts +2 -0
  122. package/dist/profile/DataPolicy.js +1 -0
  123. package/dist/profile/EnvironmentPolicy.d.ts +22 -0
  124. package/dist/profile/EnvironmentPolicy.js +41 -0
  125. package/dist/profile/LifecycleRegistry.d.ts +24 -0
  126. package/dist/profile/LifecycleRegistry.js +240 -0
  127. package/dist/profile/ObservabilityAdapter.d.ts +115 -0
  128. package/dist/profile/ObservabilityAdapter.js +50 -0
  129. package/dist/profile/ObservabilityConfig.d.ts +68 -0
  130. package/dist/profile/ObservabilityConfig.js +125 -0
  131. package/dist/profile/ObservabilityConfigError.d.ts +23 -0
  132. package/dist/profile/ObservabilityConfigError.js +39 -0
  133. package/dist/profile/ObservabilityProfile.d.ts +49 -0
  134. package/dist/profile/ObservabilityProfile.js +121 -0
  135. package/dist/profile/ReactWebProfile.d.ts +8 -0
  136. package/dist/profile/ReactWebProfile.js +7 -0
  137. package/dist/profile/index.d.ts +5 -0
  138. package/dist/profile/index.js +5 -0
  139. package/dist/testing/conformance/ConformanceAssertions.d.ts +5 -0
  140. package/dist/testing/conformance/ConformanceAssertions.js +31 -0
  141. package/dist/testing/conformance/ConformanceFailure.d.ts +35 -0
  142. package/dist/testing/conformance/ConformanceFailure.js +56 -0
  143. package/dist/testing/conformance/ConformanceModel.d.ts +97 -0
  144. package/dist/testing/conformance/ConformanceModel.js +8 -0
  145. package/dist/testing/conformance/ConformanceRegistry.d.ts +13 -0
  146. package/dist/testing/conformance/ConformanceRegistry.js +190 -0
  147. package/dist/testing/conformance/ConformanceRunner.d.ts +5 -0
  148. package/dist/testing/conformance/ConformanceRunner.js +198 -0
  149. package/dist/testing/conformance/TelemetryEvidence.d.ts +91 -0
  150. package/dist/testing/conformance/TelemetryEvidence.js +370 -0
  151. package/dist/testing/conformance/index.d.ts +10 -0
  152. package/dist/testing/conformance/index.js +6 -0
  153. package/dist/testing/contract.d.ts +81 -0
  154. package/dist/testing/contract.js +93 -0
  155. package/dist/testing/deployedCanary.d.ts +1 -0
  156. package/dist/testing/deployedCanary.js +1 -0
  157. package/dist/testing/index.d.ts +40 -3
  158. package/dist/testing/index.js +122 -5
  159. package/dist/trace/HttpServerOtlpTracer.d.ts +42 -0
  160. package/dist/trace/HttpServerOtlpTracer.js +301 -0
  161. package/package.json +16 -29
  162. package/dist/WideEvent.d.ts +0 -5
  163. package/dist/WideEvent.js +0 -6
  164. package/dist/nestjs/BrowserEventsController.d.ts +0 -21
  165. package/dist/nestjs/BrowserEventsController.js +0 -52
  166. package/dist/nestjs/HttpRoutePolicy.d.ts +0 -23
  167. package/dist/nestjs/HttpRoutePolicy.js +0 -179
  168. package/dist/nestjs/HttpServerOtlpTracer.d.ts +0 -15
  169. package/dist/nestjs/HttpServerOtlpTracer.js +0 -154
  170. package/dist/nestjs/RequestWideEventTraceCorrelation.d.ts +0 -15
  171. package/dist/nestjs/RequestWideEventTraceCorrelation.js +0 -13
  172. package/dist/nestjs/TelemetryInterceptor.d.ts +0 -31
  173. package/dist/nestjs/TelemetryInterceptor.js +0 -268
  174. package/dist/nestjs/TelemetryModule.d.ts +0 -53
  175. package/dist/nestjs/TelemetryModule.js +0 -428
  176. package/dist/nestjs/index.d.ts +0 -5
  177. package/dist/nestjs/index.js +0 -4
@@ -0,0 +1,198 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { observabilityProfiles, } from "../../profile/ObservabilityProfile.js";
3
+ import { conformanceChecks } from "./ConformanceRegistry.js";
4
+ import { ConformanceFailure, InvalidConformanceSuite } from "./ConformanceFailure.js";
5
+ const ProfileNameSchema = Schema.Literals([
6
+ "nestjs-api",
7
+ "worker",
8
+ "react-web",
9
+ "cli",
10
+ "library",
11
+ ]);
12
+ const TargetText = Schema.String.check(Schema.isPattern(/\S/));
13
+ const ContractIndexDocument = Schema.Struct({
14
+ index: Schema.Literal(1),
15
+ contractVersion: Schema.Int.check(Schema.isGreaterThan(0)),
16
+ service: TargetText,
17
+ events: Schema.Array(Schema.Struct({
18
+ name: TargetText,
19
+ kind: Schema.Literals(["request", "operation", "domain", "defect", "audit"]),
20
+ attributes: Schema.Array(TargetText),
21
+ attributeClassifications: Schema.Array(Schema.Struct({
22
+ name: TargetText,
23
+ classification: Schema.Literals(["public", "internal", "sensitive", "forbidden"]),
24
+ })),
25
+ })),
26
+ metrics: Schema.Array(Schema.Struct({
27
+ name: TargetText,
28
+ kind: Schema.Literals(["counter", "histogram", "observable_gauge"]),
29
+ unit: Schema.String,
30
+ attributes: Schema.Array(TargetText),
31
+ })),
32
+ aliases: Schema.Array(Schema.Struct({
33
+ kind: Schema.Literals(["event", "metric"]),
34
+ from: TargetText,
35
+ to: TargetText,
36
+ })),
37
+ });
38
+ const TargetDocument = Schema.Struct({
39
+ name: TargetText,
40
+ profile: ProfileNameSchema,
41
+ environment: TargetText,
42
+ topology: Schema.Literals(["local", "deployed"]),
43
+ capabilities: Schema.Struct({
44
+ traces: Schema.Boolean,
45
+ metrics: Schema.Boolean,
46
+ defects: Schema.Boolean,
47
+ browserIngest: Schema.Boolean,
48
+ audit: Schema.Boolean,
49
+ }),
50
+ binding: Schema.Struct({
51
+ identity: Schema.Struct({
52
+ serviceName: TargetText,
53
+ serviceVersion: TargetText,
54
+ environment: TargetText,
55
+ }),
56
+ contract: ContractIndexDocument,
57
+ producerContractProvenance: TargetText,
58
+ }),
59
+ providers: Schema.Array(Schema.Struct({
60
+ id: Schema.Literals(conformanceChecks.map((check) => check.id)),
61
+ owner: Schema.Literals([
62
+ "telemetry",
63
+ "cli",
64
+ "evlog",
65
+ "sentry",
66
+ "nestjs",
67
+ "react",
68
+ "application",
69
+ ]),
70
+ verify: Schema.instanceOf(Function),
71
+ })),
72
+ });
73
+ const decodeTarget = Schema.decodeUnknownEffect(TargetDocument);
74
+ const invalidSuite = (code, message, offendingValue, cause) => new InvalidConformanceSuite({ code, message, offendingValue, cause: cause ?? offendingValue });
75
+ const capabilitySelectionIsValid = (requirement, selected, environment) => {
76
+ if (requirement === "forbidden")
77
+ return selected === false;
78
+ if (requirement === "required")
79
+ return selected;
80
+ if (requirement === "required-in-production" && environment === "production")
81
+ return selected;
82
+ return true;
83
+ };
84
+ const validateTarget = (target) => Effect.gen(function* () {
85
+ const document = yield* decodeTarget(target).pipe(Effect.mapError((cause) => invalidSuite("OBS_CONFORMANCE_TARGET_INVALID", "The conformance target document is invalid. Supply valid identity, profile, environment, topology, boolean capabilities, and callable evidence providers.", "conformance target", cause)));
86
+ const profile = observabilityProfiles[document.profile];
87
+ if (document.environment !== document.binding.identity.environment ||
88
+ document.binding.contract.service !== document.binding.identity.serviceName) {
89
+ return yield* invalidSuite("OBS_CONFORMANCE_TARGET_INVALID", "The target environment, resource identity, and contract service must describe one application binding.", `${document.name}:${document.environment}`);
90
+ }
91
+ const capabilityRequirements = [
92
+ ["traces", profile.traces, target.capabilities.traces],
93
+ ["metrics", profile.metrics, target.capabilities.metrics],
94
+ ["defects", profile.defects, target.capabilities.defects],
95
+ ["browserIngest", profile.browserIngest, target.capabilities.browserIngest],
96
+ ];
97
+ for (const [capability, requirement, selected] of capabilityRequirements) {
98
+ if (!capabilitySelectionIsValid(requirement, selected, target.environment)) {
99
+ return yield* invalidSuite("OBS_CONFORMANCE_TARGET_INVALID", `The ${target.profile} profile declares ${capability} as ${requirement}, but the target selected ${selected}.`, `${capability}=${selected}`);
100
+ }
101
+ }
102
+ const providerIds = new Set();
103
+ for (const provider of target.providers) {
104
+ if (providerIds.has(provider.id)) {
105
+ return yield* invalidSuite("OBS_CONFORMANCE_PROVIDER_DUPLICATE", `The conformance target declares duplicate evidence providers for ${provider.id}.`, provider.id);
106
+ }
107
+ providerIds.add(provider.id);
108
+ }
109
+ return {
110
+ name: document.name,
111
+ profile,
112
+ environment: document.environment,
113
+ topology: document.topology,
114
+ capabilities: document.capabilities,
115
+ binding: document.binding,
116
+ };
117
+ });
118
+ const toFailure = (target, check, violation) => new ConformanceFailure({
119
+ code: check.code,
120
+ checkId: check.id,
121
+ profile: target.profile.name,
122
+ rule: check.rule,
123
+ message: violation.message,
124
+ offendingValue: violation.offendingValue,
125
+ cause: violation.cause ?? violation.offendingValue,
126
+ });
127
+ const runCheck = (target, check, providers) => Effect.gen(function* () {
128
+ if (!check.applies(target)) {
129
+ return {
130
+ status: "not-applicable",
131
+ id: check.id,
132
+ profile: target.profile.name,
133
+ rule: check.rule,
134
+ reason: check.notApplicableReason(target),
135
+ };
136
+ }
137
+ const provider = providers.find((candidate) => candidate.id === check.id);
138
+ if (provider === undefined) {
139
+ return yield* invalidSuite("OBS_CONFORMANCE_PROVIDER_MISSING", `The applicable conformance check ${check.id} has no evidence provider. Supply the owner provider for this target.`, check.id);
140
+ }
141
+ return yield* check.run(target, provider).pipe(Effect.match({
142
+ onFailure: (violation) => ({
143
+ status: "fail",
144
+ id: check.id,
145
+ profile: target.profile.name,
146
+ rule: check.rule,
147
+ failure: toFailure(target, check, violation),
148
+ }),
149
+ onSuccess: (evidence) => ({
150
+ status: "pass",
151
+ id: check.id,
152
+ profile: target.profile.name,
153
+ rule: check.rule,
154
+ evidence,
155
+ }),
156
+ }));
157
+ });
158
+ export const runConformance = (target) => Effect.gen(function* () {
159
+ const context = yield* validateTarget(target);
160
+ const checks = [];
161
+ let conforms = true;
162
+ for (const check of conformanceChecks) {
163
+ const result = yield* runCheck(context, check, target.providers);
164
+ if (result.status === "fail")
165
+ conforms = false;
166
+ checks.push(result);
167
+ }
168
+ return {
169
+ target: target.name,
170
+ profile: target.profile,
171
+ conforms,
172
+ checks,
173
+ };
174
+ });
175
+ export const runConformanceSuite = (targets) => Effect.gen(function* () {
176
+ const documents = yield* Schema.decodeUnknownEffect(Schema.Array(TargetDocument))(targets).pipe(Effect.mapError((cause) => invalidSuite("OBS_CONFORMANCE_TARGET_INVALID", "The conformance suite input is invalid. Supply an array of complete target documents.", "conformance suite", cause)));
177
+ const seenProfiles = new Map();
178
+ for (const target of documents) {
179
+ const previous = seenProfiles.get(target.profile);
180
+ if (previous !== undefined) {
181
+ return yield* invalidSuite("OBS_CONFORMANCE_TARGET_INVALID", `The conformance suite declares duplicate targets for profile ${target.profile}: "${previous}" and "${target.name}".`, target.profile);
182
+ }
183
+ seenProfiles.set(target.profile, target.name);
184
+ }
185
+ const profiles = [];
186
+ let conforms = true;
187
+ for (const target of targets) {
188
+ const report = yield* runConformance(target);
189
+ if (!report.conforms)
190
+ conforms = false;
191
+ profiles.push(report);
192
+ }
193
+ return {
194
+ version: 1,
195
+ conforms,
196
+ profiles,
197
+ };
198
+ });
@@ -0,0 +1,91 @@
1
+ import { Option } from "effect";
2
+ import { type EmitReceipt, type TelemetryContract } from "../../contract/index.js";
3
+ import type { TelemetryContractInput } from "../../contract/TelemetryContract.js";
4
+ type StaticEventNames<Definition extends TelemetryContractInput> = {
5
+ readonly events: {
6
+ readonly [Alias in keyof Definition["events"]]: string extends Definition["events"][Alias]["name"] ? never : Definition["events"][Alias];
7
+ };
8
+ };
9
+ import { CorrelationContext } from "../../Correlation.js";
10
+ import type { DataPolicyInput } from "../../policy/DataPolicy.js";
11
+ import { type ProfileName } from "../../profile/ObservabilityProfile.js";
12
+ import type { LifecycleReport } from "../../profile/ObservabilityAdapter.js";
13
+ import type { AuditPublishReceipt, CommitAuditResult, CommittedAuditRecord } from "../../audit/AuditPublisher.js";
14
+ import type { CapturedTelemetry } from "../index.js";
15
+ import { type ConformanceEvidenceProvider, type ConformanceCheckId, type ConformanceTargetBinding } from "./ConformanceModel.js";
16
+ export declare const conformanceTargetBinding: <Definition extends TelemetryContractInput>(contract: TelemetryContract<Definition>, identity: ConformanceTargetBinding["identity"]) => ConformanceTargetBinding;
17
+ export type ConformanceProvider<Id extends ConformanceCheckId> = ConformanceEvidenceProvider<Id>;
18
+ export declare const profileConformance: (input: {
19
+ readonly profile: ProfileName;
20
+ readonly service: {
21
+ readonly name: string;
22
+ readonly version: string;
23
+ readonly environment: string;
24
+ };
25
+ }) => ConformanceProvider<"profile.official">;
26
+ export declare const identityConformance: (input: {
27
+ readonly identity: {
28
+ readonly serviceName: string;
29
+ readonly serviceVersion: string;
30
+ readonly environment: string;
31
+ readonly instance?: Option.Option<string> | undefined;
32
+ };
33
+ }) => ConformanceProvider<"identity.canonical">;
34
+ export declare const contractConformance: <Definition extends TelemetryContractInput & StaticEventNames<Definition>>(input: {
35
+ readonly contract: Definition;
36
+ }) => ConformanceProvider<"contract.compiles">;
37
+ export declare const producersConformance: (input: {
38
+ readonly receipt: EmitReceipt;
39
+ }) => ConformanceProvider<"producers.contract-derived">;
40
+ export declare const correlationConformance: (input: {
41
+ readonly correlation: CorrelationContext;
42
+ }) => ConformanceProvider<"correlation.canonical">;
43
+ export declare const policyConformance: (input: {
44
+ readonly policy: DataPolicyInput;
45
+ }) => ConformanceProvider<"policy.compiles">;
46
+ export declare const lifecycleConformance: (input: {
47
+ readonly report: LifecycleReport;
48
+ }) => ConformanceProvider<"lifecycle.profile-compliant">;
49
+ export declare const libraryLifecycleConformance: (input: {
50
+ readonly runtimeMarkers: ReadonlyArray<string>;
51
+ }) => ConformanceProvider<"lifecycle.profile-compliant">;
52
+ export declare const auditConformance: (input: {
53
+ readonly commit?: CommitAuditResult<unknown> | undefined;
54
+ readonly operationalAction?: string | undefined;
55
+ readonly operationalRecordId?: string | undefined;
56
+ readonly operationalRecord?: CommittedAuditRecord | undefined;
57
+ }) => ConformanceProvider<"audit.durable-before-operational">;
58
+ export type TelemetryDestinationReceipt = {
59
+ readonly topology: "local" | "deployed";
60
+ readonly assessment: "owner-readback" | "application-supplied-readback";
61
+ readonly runId: string;
62
+ readonly identity: ConformanceTargetBinding["identity"];
63
+ readonly observationId: string;
64
+ };
65
+ type TelemetryDestinationAssessment = {
66
+ readonly topology: "local" | "deployed";
67
+ readonly runId: string;
68
+ readonly identity: ConformanceTargetBinding["identity"];
69
+ readonly observationId: string;
70
+ readonly readback: () => CapturedTelemetry;
71
+ };
72
+ export declare const assessTelemetryDestination: (assessment: TelemetryDestinationAssessment) => TelemetryDestinationReceipt;
73
+ export declare const applicationDeployedTelemetryDestinationReceipt: (assessment: {
74
+ readonly runId: string;
75
+ readonly identity: ConformanceTargetBinding["identity"];
76
+ readonly observationId: string;
77
+ readonly readback: () => CapturedTelemetry;
78
+ }) => TelemetryDestinationReceipt;
79
+ export declare const telemetryDestinationTelemetry: (receipt: TelemetryDestinationReceipt) => CapturedTelemetry | undefined;
80
+ export declare const telemetryDestinationMatches: (receipt: TelemetryDestinationReceipt, topology: "local" | "deployed", runId: string, binding: ConformanceTargetBinding) => boolean;
81
+ export declare const telemetryCanaryConformance: (input: {
82
+ readonly runId: string;
83
+ readonly receipt: TelemetryDestinationReceipt;
84
+ readonly eventRunIdAttribute?: string | undefined;
85
+ readonly metricRunIdAttribute?: string | undefined;
86
+ }) => ConformanceProvider<"canary.telemetry-destination">;
87
+ export declare const auditCanaryConformance: (input: {
88
+ readonly ledgerReceiptId: string;
89
+ readonly publish: AuditPublishReceipt;
90
+ }) => ConformanceProvider<"canary.audit">;
91
+ export {};
@@ -0,0 +1,370 @@
1
+ import { Effect, Exit, Option, Schema } from "effect";
2
+ import { parseResourceIdentity } from "../../ResourceIdentity.js";
3
+ import { contractIndex, defineTelemetryContract, telemetryContractProvenance, } from "../../contract/index.js";
4
+ import { CorrelationContext } from "../../Correlation.js";
5
+ import { parseDataPolicy } from "../../policy/DataPolicy.js";
6
+ import { observabilityProfiles } from "../../profile/ObservabilityProfile.js";
7
+ import { isCommittedAuditRecord } from "../../audit/CommittedAuditRecordInternal.js";
8
+ import { ConformanceViolation, defineConformanceEvidenceProvider, } from "./ConformanceModel.js";
9
+ export const conformanceTargetBinding = (contract, identity) => ({
10
+ identity,
11
+ contract: contractIndex(contract, identity.serviceName),
12
+ producerContractProvenance: telemetryContractProvenance(contract),
13
+ });
14
+ const violation = (message, offendingValue, cause) => new ConformanceViolation({ message, offendingValue, cause: cause ?? offendingValue });
15
+ const officialProfile = (profile) => observabilityProfiles[profile] !== undefined;
16
+ export const profileConformance = (input) => defineConformanceEvidenceProvider({
17
+ id: "profile.official",
18
+ owner: "telemetry",
19
+ verify: (target) => Effect.gen(function* () {
20
+ if (!officialProfile(input.profile)) {
21
+ return yield* Effect.fail(violation(`Profile "${input.profile}" is not one of the five official profiles. Select nestjs-api, worker, react-web, cli, or library.`, input.profile));
22
+ }
23
+ if (target.profile.name !== input.profile) {
24
+ return yield* Effect.fail(violation(`The evidence profile ${input.profile} differs from target profile ${target.profile.name}. Use one profile for the complete conformance target.`, `${input.profile} != ${target.profile.name}`));
25
+ }
26
+ if (input.service.name !== target.binding.identity.serviceName ||
27
+ input.service.version !== target.binding.identity.serviceVersion ||
28
+ input.service.environment !== target.binding.identity.environment) {
29
+ return yield* Effect.fail(violation("The profile service identity differs from the target binding.", `${input.service.name}@${input.service.version}:${input.service.environment}`));
30
+ }
31
+ return {
32
+ owner: "telemetry",
33
+ receiptType: "profile-descriptor",
34
+ receiptId: input.profile,
35
+ summary: `official profile ${input.profile} for ${input.service.name}`,
36
+ };
37
+ }),
38
+ });
39
+ export const identityConformance = (input) => defineConformanceEvidenceProvider({
40
+ id: "identity.canonical",
41
+ owner: "telemetry",
42
+ verify: (target) => Effect.gen(function* () {
43
+ const identity = yield* parseResourceIdentity(input.identity).pipe(Effect.mapError((cause) => violation(`The canonical resource identity is invalid: ${cause.message}`, `${cause.field}=${cause.value}`, cause)));
44
+ if (identity.serviceName !== target.binding.identity.serviceName ||
45
+ identity.serviceVersion !== target.binding.identity.serviceVersion ||
46
+ identity.environment !== target.binding.identity.environment) {
47
+ return yield* Effect.fail(violation("The resource identity receipt differs from the target binding. Use the same service name, version, and environment for every provider.", `${identity.serviceName}@${identity.serviceVersion}:${identity.environment}`));
48
+ }
49
+ return {
50
+ owner: "telemetry",
51
+ receiptType: "resource-identity",
52
+ receiptId: identity.serviceName,
53
+ summary: `canonical identity ${identity.serviceName}@${identity.serviceVersion} in ${identity.environment}`,
54
+ };
55
+ }),
56
+ });
57
+ export const contractConformance = (input) => defineConformanceEvidenceProvider({
58
+ id: "contract.compiles",
59
+ owner: "telemetry",
60
+ verify: (target) => Effect.gen(function* () {
61
+ const compiled = yield* Effect.exit(defineTelemetryContract(input.contract));
62
+ if (!Exit.isSuccess(compiled)) {
63
+ return yield* Effect.fail(violation("The telemetry contract does not compile. Fix every reported contract issue.", "telemetry contract input", compiled.cause));
64
+ }
65
+ const index = contractIndex(compiled.value, target.binding.identity.serviceName);
66
+ if (JSON.stringify(index) !== JSON.stringify(target.binding.contract) ||
67
+ telemetryContractProvenance(compiled.value) !== target.binding.producerContractProvenance) {
68
+ return yield* Effect.fail(violation("The compiled telemetry contract differs from the target contract binding. Derive every provider from the same compiled contract.", `contract v${input.contract.version}`));
69
+ }
70
+ const eventCount = Object.keys(input.contract.events).length;
71
+ const metricCount = Object.keys(input.contract.metrics).length;
72
+ return {
73
+ owner: "telemetry",
74
+ receiptType: "telemetry-contract",
75
+ receiptId: `v${input.contract.version}`,
76
+ summary: `contract compiled with ${eventCount} events and ${metricCount} metrics`,
77
+ };
78
+ }),
79
+ });
80
+ export const producersConformance = (input) => defineConformanceEvidenceProvider({
81
+ id: "producers.contract-derived",
82
+ owner: "telemetry",
83
+ verify: (target) => Effect.gen(function* () {
84
+ const eventName = input.receipt.decision === "sampled_out" ? input.receipt.name : input.receipt.event.name;
85
+ if (input.receipt.contractProvenance !== target.binding.producerContractProvenance ||
86
+ !target.binding.contract.events.some((event) => event.name === eventName)) {
87
+ return yield* Effect.fail(violation("The producer receipt does not originate from the complete target contract binding or name one of its events.", eventName));
88
+ }
89
+ if (input.receipt.decision === "sampled_out") {
90
+ return {
91
+ owner: "telemetry",
92
+ receiptType: "emit-receipt",
93
+ receiptId: input.receipt.name,
94
+ summary: "producer derived from the contract returned a sampled_out receipt",
95
+ };
96
+ }
97
+ return {
98
+ owner: "telemetry",
99
+ receiptType: "emit-receipt",
100
+ receiptId: input.receipt.event.name,
101
+ summary: `recorded emit with ${input.receipt.redactions.length} policy redactions`,
102
+ };
103
+ }),
104
+ });
105
+ export const correlationConformance = (input) => defineConformanceEvidenceProvider({
106
+ id: "correlation.canonical",
107
+ owner: "telemetry",
108
+ verify: () => Effect.gen(function* () {
109
+ const valid = Schema.is(CorrelationContext)(input.correlation);
110
+ if (!valid) {
111
+ return yield* Effect.fail(violation("The correlation context is not a canonical CorrelationContext. Use the typed correlation parsers.", "correlation context"));
112
+ }
113
+ const linkage = input.correlation.trace;
114
+ const correlationId = linkage._tag === "Traced"
115
+ ? linkage.traceId
116
+ : (Option.getOrUndefined(input.correlation.runId) ??
117
+ Option.getOrUndefined(input.correlation.requestId) ??
118
+ "untraced");
119
+ return {
120
+ owner: "telemetry",
121
+ receiptType: "correlation-context",
122
+ receiptId: correlationId,
123
+ summary: `canonical correlation with ${linkage._tag} trace linkage`,
124
+ };
125
+ }),
126
+ });
127
+ export const policyConformance = (input) => defineConformanceEvidenceProvider({
128
+ id: "policy.compiles",
129
+ owner: "telemetry",
130
+ verify: () => Effect.gen(function* () {
131
+ const policy = yield* parseDataPolicy(input.policy).pipe(Effect.mapError((cause) => violation("The data policy does not compile. Fix every reported policy issue.", "data policy input", cause)));
132
+ return {
133
+ owner: "telemetry",
134
+ receiptType: "data-policy",
135
+ receiptId: `${policy.attributes.size}`,
136
+ summary: `compiled data policy with ${policy.attributes.size} attribute rules`,
137
+ };
138
+ }),
139
+ });
140
+ const failedOutcomes = (report) => report.outcomes
141
+ .filter((outcome) => outcome.result.kind === "failed" || outcome.result.kind === "deadline-exceeded")
142
+ .map((outcome) => outcome.participant === "adapter"
143
+ ? `${outcome.adapter}:${outcome.result.kind}`
144
+ : `runtime-disposal:${outcome.result.kind}`);
145
+ export const lifecycleConformance = (input) => defineConformanceEvidenceProvider({
146
+ id: "lifecycle.profile-compliant",
147
+ owner: "telemetry",
148
+ verify: () => Effect.gen(function* () {
149
+ const failed = failedOutcomes(input.report);
150
+ const disposed = input.report.outcomes.some((outcome) => outcome.participant === "runtime-disposal" && outcome.result.kind === "completed");
151
+ if (input.report.operation !== "close" || !disposed) {
152
+ return yield* Effect.fail(violation("Shutdown compliance requires a close receipt with completed runtime disposal. Close the runtime before submitting lifecycle evidence.", input.report.operation));
153
+ }
154
+ if (input.report.degraded || failed.length > 0) {
155
+ return yield* Effect.fail(violation(`The lifecycle report is ${input.report.degraded ? "degraded" : "failed"} for outcomes ${failed.join(", ")}. Close the runtime within the profile deadline.`, failed.join(",") || "degraded lifecycle report"));
156
+ }
157
+ return {
158
+ owner: "telemetry",
159
+ receiptType: "lifecycle-report",
160
+ receiptId: input.report.operation,
161
+ summary: `lifecycle ${input.report.operation} completed in ${input.report.durationMillis}ms`,
162
+ };
163
+ }),
164
+ });
165
+ export const libraryLifecycleConformance = (input) => defineConformanceEvidenceProvider({
166
+ id: "lifecycle.profile-compliant",
167
+ owner: "telemetry",
168
+ verify: () => Effect.gen(function* () {
169
+ if (input.runtimeMarkers.length > 0) {
170
+ return yield* Effect.fail(violation(`The library profile forbids a global runtime, adapters, exporters, Sentry clients, and browser ingest. Found ${input.runtimeMarkers.join(", ")}.`, input.runtimeMarkers.join(",")));
171
+ }
172
+ return {
173
+ owner: "telemetry",
174
+ receiptType: "runtime-absence",
175
+ receiptId: "library",
176
+ summary: "no global runtime, adapter, exporter, Sentry client, or browser ingest installed",
177
+ };
178
+ }),
179
+ });
180
+ export const auditConformance = (input) => defineConformanceEvidenceProvider({
181
+ id: "audit.durable-before-operational",
182
+ owner: "telemetry",
183
+ verify: () => Effect.gen(function* () {
184
+ if (input.commit === undefined) {
185
+ return yield* Effect.fail(violation(`The audit action ${input.operationalAction ?? "unknown"} has no durable commit receipt. Call recordAudit with a durable ledger write before the operational copy.`, `audit action ${input.operationalAction ?? "unknown"} has no durable commit receipt`));
186
+ }
187
+ if (!isCommittedAuditRecord(input.commit.record) ||
188
+ input.commit.record !== input.operationalRecord ||
189
+ input.commit.record.action !== input.operationalAction ||
190
+ input.commit.record.recordId !== input.operationalRecordId) {
191
+ return yield* Effect.fail(violation("The durable receipt does not identify the complete requested operational audit record and action. Supply the same owner-produced committed record to both paths.", `${input.operationalRecordId ?? "missing record"}:${input.operationalAction ?? "missing action"}`));
192
+ }
193
+ return {
194
+ owner: "telemetry",
195
+ receiptType: "commit-audit-result",
196
+ receiptId: input.commit.record.recordId,
197
+ summary: "durable ledger receipt precedes the operational audit copy",
198
+ };
199
+ }),
200
+ });
201
+ const cloneOption = (value) => Option.isSome(value) ? Option.some(value.value) : Option.none();
202
+ const cloneCapturedTelemetry = (telemetry) => ({
203
+ spans: telemetry.spans.map((span) => ({
204
+ ...span,
205
+ parentSpanId: cloneOption(span.parentSpanId),
206
+ statusMessage: cloneOption(span.statusMessage),
207
+ attributes: new Map(span.attributes),
208
+ events: span.events.map((event) => ({ ...event, attributes: new Map(event.attributes) })),
209
+ links: span.links.map((link) => ({ ...link, attributes: new Map(link.attributes) })),
210
+ eventNames: [...span.eventNames],
211
+ linkedSpanIds: [...span.linkedSpanIds],
212
+ resourceAttributes: new Map(span.resourceAttributes),
213
+ })),
214
+ logs: telemetry.logs.map((log) => ({
215
+ ...log,
216
+ traceId: cloneOption(log.traceId),
217
+ spanId: cloneOption(log.spanId),
218
+ severityText: cloneOption(log.severityText),
219
+ body: cloneOption(log.body),
220
+ attributes: new Map(log.attributes),
221
+ resourceAttributes: new Map(log.resourceAttributes),
222
+ })),
223
+ metrics: telemetry.metrics.map((metric) => {
224
+ const common = {
225
+ ...metric,
226
+ points: metric.points.map((point) => ({
227
+ ...point,
228
+ value: cloneOption(point.value),
229
+ attributes: new Map(point.attributes),
230
+ })),
231
+ resourceAttributes: new Map(metric.resourceAttributes),
232
+ };
233
+ if (common.kind !== "histogram")
234
+ return common;
235
+ return {
236
+ ...common,
237
+ histogramPoints: common.histogramPoints.map((point) => ({
238
+ ...point,
239
+ attributes: new Map(point.attributes),
240
+ explicitBounds: [...point.explicitBounds],
241
+ bucketCounts: [...point.bucketCounts],
242
+ })),
243
+ };
244
+ }),
245
+ });
246
+ const destinationReceipts = new WeakMap();
247
+ export const assessTelemetryDestination = (assessment) => {
248
+ const receipt = Object.freeze({
249
+ topology: assessment.topology,
250
+ assessment: assessment.topology === "local" ? "owner-readback" : "application-supplied-readback",
251
+ runId: assessment.runId,
252
+ identity: Object.freeze({
253
+ serviceName: assessment.identity.serviceName,
254
+ serviceVersion: assessment.identity.serviceVersion,
255
+ environment: assessment.identity.environment,
256
+ }),
257
+ observationId: assessment.observationId,
258
+ });
259
+ destinationReceipts.set(receipt, cloneCapturedTelemetry(assessment.readback()));
260
+ return receipt;
261
+ };
262
+ export const applicationDeployedTelemetryDestinationReceipt = (assessment) => assessTelemetryDestination({ ...assessment, topology: "deployed" });
263
+ export const telemetryDestinationTelemetry = (receipt) => {
264
+ const telemetry = destinationReceipts.get(receipt);
265
+ return telemetry === undefined ? undefined : cloneCapturedTelemetry(telemetry);
266
+ };
267
+ export const telemetryDestinationMatches = (receipt, topology, runId, binding) => destinationReceipts.has(receipt) &&
268
+ receipt.topology === topology &&
269
+ receipt.runId === runId &&
270
+ receipt.identity.serviceName === binding.identity.serviceName &&
271
+ receipt.identity.serviceVersion === binding.identity.serviceVersion &&
272
+ receipt.identity.environment === binding.identity.environment;
273
+ const traceGraphIsAcyclic = (spans) => {
274
+ const spanById = new Map();
275
+ for (const span of spans) {
276
+ if (spanById.has(span.spanId))
277
+ return false;
278
+ spanById.set(span.spanId, span);
279
+ }
280
+ for (const span of spans) {
281
+ const visited = new Set();
282
+ let current = span;
283
+ while (current !== undefined) {
284
+ if (visited.has(current.spanId))
285
+ return false;
286
+ visited.add(current.spanId);
287
+ current = Option.isSome(current.parentSpanId)
288
+ ? spanById.get(current.parentSpanId.value)
289
+ : undefined;
290
+ }
291
+ }
292
+ return true;
293
+ };
294
+ const matchesIdentity = (attributes, binding) => attributes.get("service.name") === binding.identity.serviceName &&
295
+ attributes.get("service.version") === binding.identity.serviceVersion &&
296
+ attributes.get("deployment.environment.name") === binding.identity.environment;
297
+ export const telemetryCanaryConformance = (input) => defineConformanceEvidenceProvider({
298
+ id: "canary.telemetry-destination",
299
+ owner: "telemetry",
300
+ verify: (target) => Effect.gen(function* () {
301
+ const receipt = input.receipt;
302
+ const telemetry = telemetryDestinationTelemetry(receipt);
303
+ if (telemetry === undefined ||
304
+ !telemetryDestinationMatches(receipt, target.topology, input.runId, target.binding)) {
305
+ return yield* Effect.fail(violation("The telemetry canary lacks a Collector or destination read-back receipt bound to the current topology, run, and resource identity.", receipt?.observationId ?? "exporter capture"));
306
+ }
307
+ const eventRunIdAttribute = input.eventRunIdAttribute ?? "run.id";
308
+ const logs = telemetry.logs.filter((log) => {
309
+ const event = target.binding.contract.events.find((candidate) => candidate.name === log.attributes.get("event.name"));
310
+ return (log.attributes.get(eventRunIdAttribute) === receipt.runId &&
311
+ matchesIdentity(log.resourceAttributes, target.binding) &&
312
+ event !== undefined &&
313
+ (eventRunIdAttribute === "run.id" || event.attributes.includes(eventRunIdAttribute)));
314
+ });
315
+ const currentTraceIds = new Set(logs.flatMap((log) => (Option.isSome(log.traceId) ? [log.traceId.value] : [])));
316
+ const spans = telemetry.spans.filter((span) => currentTraceIds.has(span.traceId) &&
317
+ matchesIdentity(span.resourceAttributes, target.binding));
318
+ const metricRunIdAttribute = input.metricRunIdAttribute;
319
+ const metrics = telemetry.metrics.filter((metric) => matchesIdentity(metric.resourceAttributes, target.binding) &&
320
+ target.binding.contract.metrics.some((entry) => entry.name === metric.name &&
321
+ metricRunIdAttribute !== undefined &&
322
+ entry.attributes.includes(metricRunIdAttribute)) &&
323
+ metricRunIdAttribute !== undefined &&
324
+ metric.points.some((point) => point.attributes.get(metricRunIdAttribute) === receipt.runId));
325
+ const linked = logs.some((log) => {
326
+ if (Option.isNone(log.traceId) || Option.isNone(log.spanId))
327
+ return false;
328
+ const traceId = log.traceId.value;
329
+ const spanId = log.spanId.value;
330
+ const linkedSpan = spans.find((span) => span.traceId === traceId && span.spanId === spanId);
331
+ if (linkedSpan === undefined)
332
+ return false;
333
+ return spans.some((child) => {
334
+ if (child.traceId !== traceId || Option.isNone(child.parentSpanId))
335
+ return false;
336
+ const parentSpanId = child.parentSpanId.value;
337
+ return spans.some((parent) => parent.traceId === traceId &&
338
+ parent.spanId === parentSpanId &&
339
+ parent.spanId !== child.spanId);
340
+ });
341
+ });
342
+ if (logs.length === 0 ||
343
+ (target.capabilities.traces &&
344
+ (spans.length === 0 || !linked || !traceGraphIsAcyclic(spans))) ||
345
+ (target.capabilities.metrics && metrics.length === 0)) {
346
+ return yield* Effect.fail(violation("The telemetry canary lacks current-run contract events, selected signals, canonical resources, or trace parentage.", receipt.runId));
347
+ }
348
+ return {
349
+ owner: "telemetry",
350
+ receiptType: "telemetry-canary",
351
+ receiptId: receipt.observationId,
352
+ summary: `captured current-run telemetry with ${logs.length} events, ${spans.length} spans, and ${metrics.length} metrics`,
353
+ };
354
+ }),
355
+ });
356
+ export const auditCanaryConformance = (input) => defineConformanceEvidenceProvider({
357
+ id: "canary.audit",
358
+ owner: "telemetry",
359
+ verify: () => Effect.gen(function* () {
360
+ if (input.publish.kind === "dropped") {
361
+ return yield* Effect.fail(violation("The audit canary dropped the operational copy. Keep the durable ledger write before publication and retry the canary.", `audit publish dropped: ${input.publish.reason}`));
362
+ }
363
+ return {
364
+ owner: "telemetry",
365
+ receiptType: "audit-canary",
366
+ receiptId: input.ledgerReceiptId,
367
+ summary: `audit canary ${input.publish.kind} with durable ledger receipt`,
368
+ };
369
+ }),
370
+ });
@@ -0,0 +1,10 @@
1
+ export { conformanceChecks } from "./ConformanceRegistry.js";
2
+ export type { ConformanceCheck } from "./ConformanceRegistry.js";
3
+ export { ConformanceAssertionError, ConformanceFailure, InvalidConformanceSuite, conformanceFailureCodes, } from "./ConformanceFailure.js";
4
+ export type { ConformanceFailureCode } from "./ConformanceFailure.js";
5
+ export { assertConformanceFailure, assertConforms } from "./ConformanceAssertions.js";
6
+ export { runConformance, runConformanceSuite } from "./ConformanceRunner.js";
7
+ export { ConformanceViolation, defineConformanceEvidenceProvider } from "./ConformanceModel.js";
8
+ export type { ConformanceCapabilitySelection, ConformanceCheckId, ConformanceEvidence, ConformanceEvidenceProvider, ConformanceFail, ConformanceNotApplicable, ConformanceOwner, ConformancePass, ConformanceProfileReport, ConformanceReport, ConformanceResult, ConformanceTarget, ConformanceTargetBinding, ConformanceTargetContext, ConformanceTopology, SourceRuleReference, } from "./ConformanceModel.js";
9
+ export { applicationDeployedTelemetryDestinationReceipt, auditCanaryConformance, auditConformance, contractConformance, conformanceTargetBinding, correlationConformance, identityConformance, libraryLifecycleConformance, lifecycleConformance, policyConformance, producersConformance, profileConformance, telemetryCanaryConformance, telemetryDestinationMatches, telemetryDestinationTelemetry, } from "./TelemetryEvidence.js";
10
+ export type { TelemetryDestinationReceipt } from "./TelemetryEvidence.js";