@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,54 @@
1
+ import { DateTime, Option, Schema } from "effect";
2
+ export const EventSeverity = Schema.Literals(["debug", "info", "warn", "error", "fatal"]);
3
+ export const EventOutcome = Schema.Literals(["success", "failure", "cancelled"]);
4
+ export const EventKind = Schema.Literals(["request", "operation", "domain", "defect", "audit"]);
5
+ export const HttpContext = Schema.Struct({
6
+ method: Schema.NonEmptyString,
7
+ route: Schema.NonEmptyString,
8
+ statusCode: Schema.Int.check(Schema.makeFilter((statusCode) => statusCode >= 100 && statusCode <= 599, {
9
+ expected: "an HTTP status code from 100 to 599",
10
+ })),
11
+ });
12
+ export const ErrorContext = Schema.Struct({
13
+ type: Schema.NonEmptyString,
14
+ message: Schema.String,
15
+ retryable: Schema.Boolean,
16
+ });
17
+ export const maxOtlpUnixTimestampMillis = 18_446_744_073_709;
18
+ const rfc3339UtcPattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?Z$/;
19
+ const isLeapYear = (year) => year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
20
+ const daysInMonth = (year, month) => {
21
+ if (month === 2) {
22
+ return isLeapYear(year) ? 29 : 28;
23
+ }
24
+ return [4, 6, 9, 11].includes(month) ? 30 : 31;
25
+ };
26
+ const isValidTimestamp = (timestamp) => {
27
+ const parts = rfc3339UtcPattern.exec(timestamp);
28
+ if (parts === null) {
29
+ return false;
30
+ }
31
+ const year = Number(parts[1]);
32
+ const month = Number(parts[2]);
33
+ const day = Number(parts[3]);
34
+ const hour = Number(parts[4]);
35
+ const minute = Number(parts[5]);
36
+ const second = Number(parts[6]);
37
+ const milliseconds = Date.parse(timestamp);
38
+ return (month >= 1 &&
39
+ month <= 12 &&
40
+ day >= 1 &&
41
+ day <= daysInMonth(year, month) &&
42
+ hour <= 23 &&
43
+ minute <= 59 &&
44
+ second <= 59 &&
45
+ Option.isSome(DateTime.make(timestamp)) &&
46
+ milliseconds >= 0 &&
47
+ milliseconds <= maxOtlpUnixTimestampMillis);
48
+ };
49
+ export const EventTimestamp = Schema.String.check(Schema.makeFilter(isValidTimestamp, {
50
+ expected: `an RFC 3339 UTC timestamp from 1970-01-01T00:00:00.000Z through ${new Date(maxOtlpUnixTimestampMillis).toISOString()}`,
51
+ })).pipe(Schema.brand("EventTimestamp"));
52
+ export const EventDuration = Schema.Finite.check(Schema.makeFilter((durationMs) => durationMs >= 0, {
53
+ expected: "a finite non-negative duration",
54
+ })).pipe(Schema.brand("EventDuration"));
@@ -0,0 +1,11 @@
1
+ export * from "./ContractCompatibility.js";
2
+ export * from "./ContractIndex.js";
3
+ export * from "./ContractSurface.js";
4
+ export * from "./EventName.js";
5
+ export * from "./EventProducer.js";
6
+ export * from "./MetricContractError.js";
7
+ export * from "./MetricProducer.js";
8
+ export * from "./OrganizationEvents.js";
9
+ export * from "./TelemetryContract.js";
10
+ export * from "./TelemetryContractError.js";
11
+ export * from "./TelemetryEvent.js";
@@ -0,0 +1,11 @@
1
+ export * from "./ContractCompatibility.js";
2
+ export * from "./ContractIndex.js";
3
+ export * from "./ContractSurface.js";
4
+ export * from "./EventName.js";
5
+ export * from "./EventProducer.js";
6
+ export * from "./MetricContractError.js";
7
+ export * from "./MetricProducer.js";
8
+ export * from "./OrganizationEvents.js";
9
+ export * from "./TelemetryContract.js";
10
+ export * from "./TelemetryContractError.js";
11
+ export * from "./TelemetryEvent.js";
@@ -0,0 +1,3 @@
1
+ import { Effect } from "effect";
2
+ import type { EventAttributes } from "../contract/TelemetryEvent.js";
3
+ export declare const emit: (name: string, fields: EventAttributes) => Effect.Effect<void>;
@@ -0,0 +1,20 @@
1
+ import { Effect } from "effect";
2
+ import { sanitizeEventName } from "../policy/BrowserFieldPolicy.js";
3
+ import { CurrentDataPolicy } from "../policy/DataPolicy.js";
4
+ import { transformSignalFields } from "../policy/PolicyTransform.js";
5
+ import { effectDroppedAttributesKey } from "../policy/PolicyVocabulary.js";
6
+ export const emit = (name, fields) => Effect.flatMap(CurrentDataPolicy, (policy) => {
7
+ const applicationFields = { ...fields };
8
+ delete applicationFields["event.name"];
9
+ delete applicationFields["event.kind"];
10
+ const eventName = sanitizeEventName(name);
11
+ const decision = transformSignalFields(policy, "log", {
12
+ "event.name": eventName,
13
+ "event.kind": "wide",
14
+ ...applicationFields,
15
+ });
16
+ return Effect.logInfo(eventName).pipe(Effect.annotateLogs({
17
+ ...decision.value,
18
+ [effectDroppedAttributesKey]: decision.dropped,
19
+ }));
20
+ });
@@ -0,0 +1,3 @@
1
+ import { Layer } from "effect";
2
+ import { TelemetryEventSink } from "../contract/EventProducer.js";
3
+ export declare const layerWideEvent: Layer.Layer<TelemetryEventSink>;
@@ -0,0 +1,79 @@
1
+ import { Effect, Layer, Option, Tracer } from "effect";
2
+ import { TelemetryEventSink } from "../contract/EventProducer.js";
3
+ import * as WideEvent from "./WideEvent.js";
4
+ const fieldsForEvent = (event) => {
5
+ const fields = {
6
+ "event.type": event.kind,
7
+ "event.severity": event.severity,
8
+ "event.outcome": event.outcome,
9
+ "event.timestamp": event.timestamp,
10
+ };
11
+ for (const [name, value] of Object.entries(event.attributes)) {
12
+ fields[name] = value;
13
+ }
14
+ if (Option.isSome(event.correlation.requestId)) {
15
+ fields["request.id"] = event.correlation.requestId.value;
16
+ }
17
+ if (Option.isSome(event.correlation.runId)) {
18
+ fields["run.id"] = event.correlation.runId.value;
19
+ }
20
+ switch (event.kind) {
21
+ case "request":
22
+ fields["event.duration_ms"] = event.durationMs;
23
+ fields["http.request.method"] = event.http.method;
24
+ fields["http.route"] = event.http.route;
25
+ fields["http.response.status_code"] = event.http.statusCode;
26
+ break;
27
+ case "operation":
28
+ fields["event.duration_ms"] = event.durationMs;
29
+ break;
30
+ case "defect":
31
+ fields["error.type"] = event.error.type;
32
+ fields["error.message"] = event.error.message;
33
+ fields["error.retryable"] = event.error.retryable;
34
+ break;
35
+ case "audit":
36
+ fields["event.outcome"] = event.outcome === "success" ? "success" : "failure";
37
+ fields["audit.outcome"] = event.outcome;
38
+ fields["audit.action"] = event.audit.action;
39
+ fields["audit.actor.kind"] = event.audit.actor.kind;
40
+ fields["audit.actor.id"] =
41
+ event.audit.actor.kind === "system" ? "system" : event.audit.actor.id;
42
+ fields["audit.resource.type"] = event.audit.resourceType;
43
+ fields["audit.resource.id"] = event.audit.resourceId;
44
+ if (event.audit.reasonCode !== undefined)
45
+ fields["audit.reason_code"] = event.audit.reasonCode;
46
+ break;
47
+ }
48
+ return fields;
49
+ };
50
+ export const layerWideEvent = Layer.succeed(TelemetryEventSink, TelemetryEventSink.of({
51
+ record: (event, admission) => WideEvent.emit(event.name, {
52
+ ...fieldsForEvent(event),
53
+ "event.policy_dropped_attributes": admission.policyDroppedAttributes,
54
+ }),
55
+ admitBrowserBatch: (events) => Effect.succeed({
56
+ commit: Effect.forEach(events, (event) => {
57
+ const fields = {
58
+ ...event.attributes,
59
+ "event.source": "browser",
60
+ "event.outcome": event.error === undefined ? "success" : "failure",
61
+ "browser.event.id": event.id,
62
+ "browser.event.occurred_at": event.occurredAt,
63
+ "event.policy_dropped_attributes": event.admission.policyDroppedAttributes,
64
+ };
65
+ if (event.error !== undefined) {
66
+ fields["error.type"] = event.error.type;
67
+ fields["error.message"] = event.error.message;
68
+ fields["error.retryable"] = event.error.retryable;
69
+ }
70
+ const emission = WideEvent.emit(event.name, fields);
71
+ return event.trace === undefined
72
+ ? emission
73
+ : emission.pipe(Effect.withParentSpan(Tracer.externalSpan({
74
+ traceId: event.trace.traceId,
75
+ spanId: event.trace.spanId,
76
+ })));
77
+ }, { discard: true }),
78
+ }),
79
+ }));
@@ -0,0 +1,2 @@
1
+ export * as WideEvent from "./WideEvent.js";
2
+ export { layerWideEvent } from "./WideEventSink.js";
@@ -0,0 +1,2 @@
1
+ export * as WideEvent from "./WideEvent.js";
2
+ export { layerWideEvent } from "./WideEventSink.js";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,19 @@
1
1
  export * as BrowserEvents from "./BrowserEvents.js";
2
+ export * from "./audit/index.js";
3
+ export * as Contract from "./contract/index.js";
4
+ export { CorrelationContext, CorrelationField, CurrentCorrelation, generateRunId, InvalidCorrelationContext, parseRequestId, parseRunId, parseSpanId, parseTraceId, RequestId, RunId, SpanId, TraceId, TraceLinkage, withBackgroundCorrelation, withCorrelation, } from "./Correlation.js";
5
+ export { defineTelemetryContract, validateContractEvent } from "./contract/TelemetryContract.js";
6
+ export type { BrowserTelemetryEvent, EventPayloadOf, EventProducer, EventAdmissionMetadata, EmitReceipt, } from "./contract/EventProducer.js";
7
+ export { makeEventProducer, TelemetryEventSink } from "./contract/EventProducer.js";
8
+ export { makeMetricProducer } from "./contract/MetricProducer.js";
9
+ export type { ContractCounter, ContractGaugeObservation, ContractHistogram, MetricAttributesOf, MetricAttributeValueOf, MetricProducer, } from "./contract/MetricProducer.js";
10
+ export { InvalidMetricMeasurement } from "./contract/MetricContractError.js";
11
+ export type { MetricMeasurementErrorCode } from "./contract/MetricContractError.js";
12
+ export type { AuditActionDefinition, AuditActionDefinitionInput, TelemetryContract, TelemetryContractInput, } from "./contract/TelemetryContract.js";
13
+ export type { EventAttributes, TelemetryEvent } from "./contract/TelemetryEvent.js";
2
14
  export * as Telemetry from "./Telemetry.js";
3
- export { InvalidTelemetryEnvironment, TelemetryConfig, telemetryConfigFromEnv, } from "./TelemetryConfig.js";
4
- export * as WideEvent from "./WideEvent.js";
15
+ export * from "./profile/index.js";
16
+ export * from "./policy/index.js";
17
+ export { EnvironmentAliasPolicy, EnvironmentName, InvalidResourceIdentity, instanceResourceAttributes, parseResourceIdentity, ResourceIdentity, ResourceIdentityField, ServiceInstanceId, ServiceName, serviceNamespace, serviceResourceAttributes, ServiceVersion, } from "./ResourceIdentity.js";
18
+ export type { ResourceAttributes, ResourceIdentityInput } from "./ResourceIdentity.js";
19
+ export { InvalidTelemetryEnvironment, OtlpEndpoint, TelemetryConfig, telemetryConfigFromEnv, } from "./TelemetryConfig.js";
package/dist/index.js CHANGED
@@ -1,4 +1,13 @@
1
1
  export * as BrowserEvents from "./BrowserEvents.js";
2
+ export * from "./audit/index.js";
3
+ export * as Contract from "./contract/index.js";
4
+ export { CorrelationContext, CorrelationField, CurrentCorrelation, generateRunId, InvalidCorrelationContext, parseRequestId, parseRunId, parseSpanId, parseTraceId, RequestId, RunId, SpanId, TraceId, TraceLinkage, withBackgroundCorrelation, withCorrelation, } from "./Correlation.js";
5
+ export { defineTelemetryContract, validateContractEvent } from "./contract/TelemetryContract.js";
6
+ export { makeEventProducer, TelemetryEventSink } from "./contract/EventProducer.js";
7
+ export { makeMetricProducer } from "./contract/MetricProducer.js";
8
+ export { InvalidMetricMeasurement } from "./contract/MetricContractError.js";
2
9
  export * as Telemetry from "./Telemetry.js";
3
- export { InvalidTelemetryEnvironment, TelemetryConfig, telemetryConfigFromEnv, } from "./TelemetryConfig.js";
4
- export * as WideEvent from "./WideEvent.js";
10
+ export * from "./profile/index.js";
11
+ export * from "./policy/index.js";
12
+ export { EnvironmentAliasPolicy, EnvironmentName, InvalidResourceIdentity, instanceResourceAttributes, parseResourceIdentity, ResourceIdentity, ResourceIdentityField, ServiceInstanceId, ServiceName, serviceNamespace, serviceResourceAttributes, ServiceVersion, } from "./ResourceIdentity.js";
13
+ export { InvalidTelemetryEnvironment, OtlpEndpoint, TelemetryConfig, telemetryConfigFromEnv, } from "./TelemetryConfig.js";
@@ -0,0 +1,5 @@
1
+ export declare const instrumentNamePattern: RegExp;
2
+ export declare const unitPattern: RegExp;
3
+ export declare const maximumMetricAttributes = 16;
4
+ export declare const maximumMetricAttributeCardinality = 100;
5
+ export declare const containsControlCharacter: (value: string) => boolean;
@@ -0,0 +1,12 @@
1
+ export const instrumentNamePattern = /^[A-Za-z][A-Za-z0-9_.\-/]{0,254}$/;
2
+ export const unitPattern = /^(?:1|%|[A-Za-z][A-Za-z0-9]*(?:[./*^][A-Za-z0-9]+)*)$/;
3
+ export const maximumMetricAttributes = 16;
4
+ export const maximumMetricAttributeCardinality = 100;
5
+ export const containsControlCharacter = (value) => {
6
+ for (const character of value) {
7
+ const codePoint = character.codePointAt(0);
8
+ if (codePoint !== undefined && (codePoint <= 31 || codePoint === 127))
9
+ return true;
10
+ }
11
+ return false;
12
+ };
@@ -0,0 +1,3 @@
1
+ import type { MetricAttributeValue } from "../Metrics.js";
2
+ export declare const isFiniteMetricScalar: (value: MetricAttributeValue) => boolean;
3
+ export declare const metricScalarIdentity: (value: MetricAttributeValue) => string;
@@ -0,0 +1,5 @@
1
+ import { Predicate, Schema } from "effect";
2
+ const metricScalarSchema = Schema.Union([Schema.String, Schema.Number, Schema.Boolean]);
3
+ const isMetricScalar = Schema.is(metricScalarSchema);
4
+ export const isFiniteMetricScalar = (value) => isMetricScalar(value) && (!Predicate.isNumber(value) || Number.isFinite(value));
5
+ export const metricScalarIdentity = (value) => `${Predicate.isString(value) ? "string" : Predicate.isNumber(value) ? "number" : "boolean"}:${String(value)}`;
@@ -0,0 +1,3 @@
1
+ import { Layer } from "effect";
2
+ import { AuditDigest } from "../audit/AuditDigest.js";
3
+ export declare const layerNodeAuditDigest: Layer.Layer<AuditDigest, never, never>;
@@ -0,0 +1,6 @@
1
+ import { createHash } from "node:crypto";
2
+ import { Effect, Layer, Schema } from "effect";
3
+ import { AuditDigest, AuditHash } from "../audit/AuditDigest.js";
4
+ export const layerNodeAuditDigest = Layer.succeed(AuditDigest, AuditDigest.of({
5
+ hash: (payload) => Effect.sync(() => Schema.decodeUnknownSync(AuditHash)(createHash("sha256").update(payload).digest("hex"))),
6
+ }));
@@ -1,5 +1,6 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import { BrowserEventBatch } from "../BrowserEvents.js";
3
+ import { TelemetryEventSink } from "../contract/EventProducer.js";
3
4
  declare const InvalidBrowserEventBatch_base: Schema.Class<InvalidBrowserEventBatch, Schema.TaggedStruct<"InvalidBrowserEventBatch", {
4
5
  readonly code: Schema.Literal<"OBS_BROWSER_EVENTS_INVALID_BATCH">;
5
6
  readonly message: Schema.String;
@@ -10,7 +11,11 @@ export declare class InvalidBrowserEventBatch extends InvalidBrowserEventBatch_b
10
11
  export declare const parseBrowserEventBatch: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => Effect.Effect<BrowserEventBatch, InvalidBrowserEventBatch, never>;
11
12
  export type BrowserEventIngestReceipt = {
12
13
  readonly accepted: number;
14
+ readonly redacted: number;
15
+ readonly dropped: number;
16
+ readonly spans?: number;
17
+ readonly metrics?: number;
13
18
  };
14
- export declare const ingestBrowserEventBatch: (batch: BrowserEventBatch) => Effect.Effect<BrowserEventIngestReceipt, never, never>;
15
- export declare const ingestBrowserEvents: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => Effect.Effect<BrowserEventIngestReceipt, InvalidBrowserEventBatch, never>;
19
+ export declare const ingestBrowserEventBatch: (batch: BrowserEventBatch) => Effect.Effect<BrowserEventIngestReceipt, InvalidBrowserEventBatch | import("../contract/TelemetryContractError.js").InvalidTelemetryEvent, TelemetryEventSink>;
20
+ export declare const ingestBrowserEvents: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => Effect.Effect<BrowserEventIngestReceipt, InvalidBrowserEventBatch | import("../contract/TelemetryContractError.js").InvalidTelemetryEvent, TelemetryEventSink>;
16
21
  export {};
@@ -1,6 +1,11 @@
1
- import { Effect, flow, Schema } from "effect";
1
+ import { Effect, flow, Option, Schema } from "effect";
2
2
  import { BrowserEventBatch } from "../BrowserEvents.js";
3
- import * as WideEvent from "../WideEvent.js";
3
+ import { parseResourceIdentity } from "../ResourceIdentity.js";
4
+ import { BrowserSignalExporter } from "../trace/HttpServerOtlpTracer.js";
5
+ import { TelemetryEventSink } from "../contract/EventProducer.js";
6
+ import { CurrentDataPolicy } from "../policy/DataPolicy.js";
7
+ import { transformSignalFields } from "../policy/PolicyTransform.js";
8
+ import { BrowserMetricRecorder, unavailableBrowserMetricRecorder, } from "../browser/BrowserMetricRecorder.js";
4
9
  export class InvalidBrowserEventBatch extends Schema.TaggedError()("InvalidBrowserEventBatch", {
5
10
  code: Schema.Literal("OBS_BROWSER_EVENTS_INVALID_BATCH"),
6
11
  message: Schema.String,
@@ -10,29 +15,116 @@ export class InvalidBrowserEventBatch extends Schema.TaggedError()("InvalidBrows
10
15
  const decodeBrowserEventBatch = Schema.decodeUnknownEffect(BrowserEventBatch);
11
16
  export const parseBrowserEventBatch = flow(decodeBrowserEventBatch, Effect.mapError((cause) => new InvalidBrowserEventBatch({
12
17
  code: "OBS_BROWSER_EVENTS_INVALID_BATCH",
13
- message: "The browser event batch is invalid. Send a version 1 batch with bounded events and scalar fields.",
18
+ message: "The browser event batch is invalid. Send a batch with a positive safe integer version (version 1 or newer), bounded events, and scalar fields.",
14
19
  cause,
15
20
  })));
16
- const reservedFieldPrefixes = ["event.", "browser."];
17
- const trustedFields = (fields) => {
18
- const sanitized = {};
19
- for (const [key, value] of Object.entries(fields)) {
20
- if (reservedFieldPrefixes.some((prefix) => key.startsWith(prefix))) {
21
- continue;
21
+ const invalidSignalBatch = (cause) => new InvalidBrowserEventBatch({
22
+ code: "OBS_BROWSER_EVENTS_INVALID_BATCH",
23
+ message: "The browser event batch has invalid signal correlation. Send completed spans with valid parent and event references in the same trace.",
24
+ cause,
25
+ });
26
+ export const ingestBrowserEventBatch = Effect.fn("ingestBrowserEventBatch")(function* (batch) {
27
+ const policy = yield* CurrentDataPolicy;
28
+ const sink = yield* TelemetryEventSink;
29
+ const signalExporter = yield* BrowserSignalExporter;
30
+ const metricRecorder = yield* Effect.serviceOption(BrowserMetricRecorder).pipe(Effect.map(Option.getOrElse(() => unavailableBrowserMetricRecorder)));
31
+ const spans = batch.spans ?? [];
32
+ const metrics = batch.metrics ?? [];
33
+ const resource = batch.resource === undefined
34
+ ? undefined
35
+ : yield* parseResourceIdentity(batch.resource).pipe(Effect.mapError(() => invalidSignalBatch("invalid browser resource identity")));
36
+ const spanById = new Map(spans.map((span) => [span.spanId, span]));
37
+ if (spanById.size !== spans.length)
38
+ return yield* invalidSignalBatch("duplicate span id");
39
+ for (const span of spans) {
40
+ if (span.endedAt < span.startedAt)
41
+ return yield* invalidSignalBatch("span ended before start");
42
+ if (span.parentSpanId !== undefined) {
43
+ const parent = spanById.get(span.parentSpanId);
44
+ if (parent === undefined ||
45
+ parent.traceId !== span.traceId ||
46
+ parent.spanId === span.spanId) {
47
+ return yield* invalidSignalBatch("invalid span parent");
48
+ }
49
+ }
50
+ }
51
+ for (const span of spans) {
52
+ const ancestors = new Set([span.spanId]);
53
+ let parentId = span.parentSpanId;
54
+ while (parentId !== undefined) {
55
+ if (ancestors.has(parentId))
56
+ return yield* invalidSignalBatch("cyclic span parent");
57
+ ancestors.add(parentId);
58
+ parentId = spanById.get(parentId)?.parentSpanId;
22
59
  }
23
- sanitized[key] = value;
24
60
  }
25
- return sanitized;
26
- };
27
- export const ingestBrowserEventBatch = Effect.fn("ingestBrowserEventBatch")(function* (batch) {
28
61
  for (const event of batch.events) {
29
- yield* WideEvent.emit(event.name, {
30
- ...trustedFields(event.fields),
31
- "event.source": "browser",
32
- "browser.event.id": event.id,
33
- "browser.event.occurred_at": event.occurredAt,
62
+ if (event.trace !== undefined) {
63
+ const span = spanById.get(event.trace.spanId);
64
+ if (span === undefined || span.traceId !== event.trace.traceId) {
65
+ return yield* invalidSignalBatch("invalid event trace reference");
66
+ }
67
+ }
68
+ }
69
+ let redacted = 0;
70
+ let dropped = 0;
71
+ const events = batch.events.map((event) => {
72
+ const decision = transformSignalFields(policy, "browser-ingest", event.fields);
73
+ const errorDecision = event.error === undefined
74
+ ? undefined
75
+ : transformSignalFields(policy, "browser-ingest", {
76
+ "error.type": event.error.type,
77
+ "error.message": event.error.message,
78
+ });
79
+ const eventDropped = decision.dropped + (errorDecision?.dropped ?? 0);
80
+ dropped += eventDropped;
81
+ redacted += [...decision.redactions, ...(errorDecision?.redactions ?? [])].filter((redaction) => redaction.action !== "dropped").length;
82
+ const ingested = {
83
+ id: event.id,
84
+ name: event.name,
85
+ occurredAt: event.occurredAt,
86
+ attributes: decision.value,
87
+ admission: { policyDroppedAttributes: eventDropped },
88
+ };
89
+ if (event.trace !== undefined)
90
+ Object.assign(ingested, { trace: event.trace });
91
+ if (event.error === undefined || errorDecision === undefined)
92
+ return ingested;
93
+ return {
94
+ ...ingested,
95
+ error: {
96
+ type: String(errorDecision.value["error.type"] ?? "Error"),
97
+ message: String(errorDecision.value["error.message"] ?? ""),
98
+ retryable: event.error.retryable,
99
+ },
100
+ };
101
+ });
102
+ const eventAdmission = yield* sink.admitBrowserBatch(events);
103
+ let commitMetrics = () => undefined;
104
+ if (metrics.length > 0) {
105
+ commitMetrics = yield* Effect.try({
106
+ try: () => metricRecorder.admit(metrics).commit,
107
+ catch: (cause) => invalidSignalBatch(cause),
34
108
  });
35
109
  }
36
- return { accepted: batch.events.length };
110
+ yield* eventAdmission.commit;
111
+ commitMetrics();
112
+ const signals = { spans };
113
+ yield* signalExporter.export(resource === undefined
114
+ ? signals
115
+ : {
116
+ ...signals,
117
+ resource: {
118
+ serviceName: resource.serviceName,
119
+ serviceVersion: resource.serviceVersion,
120
+ environment: resource.environment,
121
+ },
122
+ });
123
+ const receipt = { accepted: events.length, redacted, dropped };
124
+ if (batch.spans !== undefined)
125
+ Object.assign(receipt, { spans: spans.length });
126
+ if (batch.metrics !== undefined)
127
+ Object.assign(receipt, { metrics: metrics.length });
128
+ return receipt;
37
129
  });
38
130
  export const ingestBrowserEvents = flow(parseBrowserEventBatch, Effect.flatMap(ingestBrowserEventBatch));
@@ -0,0 +1,20 @@
1
+ import { Schema } from "effect";
2
+ import type { ConformanceTargetBinding } from "../testing/conformance/ConformanceModel.js";
3
+ import { type TelemetryDestinationReceipt } from "../testing/conformance/TelemetryEvidence.js";
4
+ import { type CapturedTelemetry, type OtlpCaptureServer } from "../testing/index.js";
5
+ declare const LocalCollectorError_base: Schema.Class<LocalCollectorError, Schema.TaggedStruct<"LocalCollectorError", {
6
+ readonly code: Schema.Literal<"OBS_CONFORMANCE_LOCAL_COLLECTOR_FAILED">;
7
+ readonly message: Schema.String;
8
+ readonly cause: Schema.Defect;
9
+ }>, import("effect/Cause").YieldableError>;
10
+ export declare class LocalCollectorError extends LocalCollectorError_base {
11
+ }
12
+ export type LocalCollectorDestination = OtlpCaptureServer & {
13
+ readonly destinationTelemetry: () => CapturedTelemetry;
14
+ readonly destinationEndpoint: URL;
15
+ readonly collectorInstance: string;
16
+ readonly awaitDestination: (runId: string, eventRunIdAttribute?: string) => Promise<void>;
17
+ readonly destinationReceipt: (runId: string, binding: ConformanceTargetBinding) => TelemetryDestinationReceipt;
18
+ };
19
+ export declare const startLocalCollectorDestination: () => Promise<LocalCollectorDestination>;
20
+ export {};