@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,25 @@
1
+ import { Option, Schema } from "effect";
2
+ import { CorrelationContext } from "../Correlation.js";
3
+ import type { AttributeValue } from "../contract/TelemetryEvent.js";
4
+ import type { DataPolicy } from "./DataPolicy.js";
5
+ import { type PolicyDecision } from "./PolicyTransform.js";
6
+ export declare const DefectEnvelope: Schema.Struct<{
7
+ readonly errorType: Schema.Literal<"UnexpectedDefect">;
8
+ readonly errorMessage: Schema.String;
9
+ readonly stack: Schema.Option<Schema.String>;
10
+ readonly fingerprint: Schema.$Array<Schema.String>;
11
+ readonly tags: Schema.$ReadonlyMap<Schema.String, Schema.String>;
12
+ readonly context: Schema.$ReadonlyMap<Schema.String, Schema.Union<readonly [Schema.String, Schema.Number, Schema.Boolean]>>;
13
+ readonly correlation: typeof CorrelationContext;
14
+ }>;
15
+ export type DefectEnvelope = typeof DefectEnvelope.Type;
16
+ export type UnexpectedDefectInput = {
17
+ readonly error: Error;
18
+ readonly code: string;
19
+ readonly fingerprint?: ReadonlyArray<string>;
20
+ readonly tags?: ReadonlyMap<string, string>;
21
+ readonly context?: ReadonlyMap<string, AttributeValue>;
22
+ readonly correlation?: CorrelationContext;
23
+ };
24
+ export declare const unexpectedDefect: (input: UnexpectedDefectInput) => DefectEnvelope;
25
+ export declare const sanitizeDefectEnvelope: (policy: DataPolicy, envelope: DefectEnvelope) => PolicyDecision<Option.Option<DefectEnvelope>>;
@@ -0,0 +1,52 @@
1
+ import { Option, Schema } from "effect";
2
+ import { CorrelationContext } from "../Correlation.js";
3
+ import { sanitizeText, transformSignalFields } from "./PolicyTransform.js";
4
+ export const DefectEnvelope = Schema.Struct({
5
+ errorType: Schema.Literal("UnexpectedDefect"),
6
+ errorMessage: Schema.String,
7
+ stack: Schema.Option(Schema.String),
8
+ fingerprint: Schema.Array(Schema.String),
9
+ tags: Schema.ReadonlyMap(Schema.String, Schema.String),
10
+ context: Schema.ReadonlyMap(Schema.String, Schema.Union([Schema.String, Schema.Number, Schema.Boolean])),
11
+ correlation: CorrelationContext,
12
+ });
13
+ export const unexpectedDefect = (input) => ({
14
+ errorType: "UnexpectedDefect",
15
+ errorMessage: input.error.message,
16
+ stack: Option.fromNullishOr(input.error.stack),
17
+ fingerprint: input.fingerprint ?? [input.code, input.error.name],
18
+ tags: input.tags ?? new Map(),
19
+ context: input.context ?? new Map(),
20
+ correlation: input.correlation ?? new CorrelationContext({}),
21
+ });
22
+ export const sanitizeDefectEnvelope = (policy, envelope) => {
23
+ const contextInput = {};
24
+ for (const [key, value] of envelope.context)
25
+ contextInput[key] = value;
26
+ const contextDecision = transformSignalFields(policy, "defect", contextInput);
27
+ const context = new Map();
28
+ for (const [key, value] of Object.entries(contextDecision.value))
29
+ context.set(key, value);
30
+ const tagsInput = {};
31
+ for (const [key, value] of envelope.tags)
32
+ tagsInput[key] = value;
33
+ const tagsDecision = transformSignalFields(policy, "defect", tagsInput);
34
+ const tags = new Map();
35
+ for (const [key, value] of Object.entries(tagsDecision.value))
36
+ tags.set(key, String(value));
37
+ const redactions = [...contextDecision.redactions, ...tagsDecision.redactions];
38
+ const dropped = contextDecision.dropped + tagsDecision.dropped;
39
+ if (redactions.some((redaction) => redaction.rule === "classification" && redaction.action === "dropped")) {
40
+ return { value: Option.none(), redactions, dropped };
41
+ }
42
+ const value = {
43
+ ...envelope,
44
+ errorType: envelope.errorType,
45
+ errorMessage: sanitizeText(policy, envelope.errorMessage, "defect"),
46
+ stack: Option.map(envelope.stack, (stack) => sanitizeText(policy, stack, "defect")),
47
+ fingerprint: envelope.fingerprint.map((part) => sanitizeText(policy, part, "defect")),
48
+ tags,
49
+ context,
50
+ };
51
+ return { value: Option.some(value), redactions, dropped };
52
+ };
@@ -0,0 +1,5 @@
1
+ import type { MetricAttributeValue } from "../Metrics.js";
2
+ import type { DataPolicy } from "./DataPolicy.js";
3
+ export declare const isForbiddenMetricAttributeName: (key: string) => boolean;
4
+ export type MetricLabelRejection = "attribute-name" | "classification" | "identifier-shape" | "string-bound" | "blocked-value";
5
+ export declare const metricLabelRejection: (policy: DataPolicy, key: string, value: MetricAttributeValue) => MetricLabelRejection | undefined;
@@ -0,0 +1,65 @@
1
+ import { Predicate } from "effect";
2
+ import { isValidAttributeName } from "../contract/EventName.js";
3
+ import { replaceStructuredAssignments } from "./BrowserFieldPolicy.js";
4
+ import { isSensitiveFieldKey, replaceEmailCandidates } from "./PolicyVocabulary.js";
5
+ const maximumMetricLabelTextLength = 64;
6
+ const labelValuePattern = /^[A-Za-z0-9/][A-Za-z0-9._:/-]{0,63}$/;
7
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
8
+ const tracePattern = /^[0-9a-f]{32}$/;
9
+ const spanPattern = /^[0-9a-f]{16}$/;
10
+ const ulidPattern = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
11
+ const numericIdentifierPattern = /^\d{7,}$/;
12
+ const reserved = new Set([
13
+ "service.instance.id",
14
+ "trace.id",
15
+ "span.id",
16
+ "user.id",
17
+ "session.id",
18
+ "unit",
19
+ "time_unit",
20
+ ]);
21
+ export const isForbiddenMetricAttributeName = (key) => !isValidAttributeName(key) || reserved.has(key) || isSensitiveFieldKey(key);
22
+ export const metricLabelRejection = (policy, key, value) => {
23
+ if (isForbiddenMetricAttributeName(key)) {
24
+ return "attribute-name";
25
+ }
26
+ const definition = policy.attributes.get(key);
27
+ const classification = policy.classify(key);
28
+ if (definition !== undefined &&
29
+ (!definition.metricLabel ||
30
+ definition.classification === "sensitive" ||
31
+ definition.classification === "forbidden")) {
32
+ return "classification";
33
+ }
34
+ if (classification === "sensitive" || classification === "forbidden") {
35
+ return "classification";
36
+ }
37
+ if (Predicate.isNumber(value)) {
38
+ return Number.isInteger(value) && numericIdentifierPattern.test(String(Math.abs(value)))
39
+ ? "identifier-shape"
40
+ : undefined;
41
+ }
42
+ if (Predicate.isBoolean(value))
43
+ return undefined;
44
+ if (value.length > maximumMetricLabelTextLength)
45
+ return "string-bound";
46
+ if (replaceEmailCandidates(value) !== value)
47
+ return "blocked-value";
48
+ for (const pattern of policy.blockedValuePatterns) {
49
+ pattern.lastIndex = 0;
50
+ if (pattern.test(value))
51
+ return "blocked-value";
52
+ }
53
+ if (replaceStructuredAssignments(value) !== value)
54
+ return "blocked-value";
55
+ if (!labelValuePattern.test(value))
56
+ return "string-bound";
57
+ if (uuidPattern.test(value) ||
58
+ tracePattern.test(value) ||
59
+ spanPattern.test(value) ||
60
+ ulidPattern.test(value) ||
61
+ numericIdentifierPattern.test(value)) {
62
+ return "identifier-shape";
63
+ }
64
+ return undefined;
65
+ };
@@ -0,0 +1,16 @@
1
+ import type { EventAttributes } from "../contract/TelemetryEvent.js";
2
+ import type { DataPolicy, PolicySurface } from "./DataPolicy.js";
3
+ export type PolicyAction = "masked" | "truncated" | "dropped";
4
+ export type PolicyRule = "attribute-name" | "blocked-key" | "blocked-value" | "classification" | "bounds";
5
+ export type PolicyRedaction = {
6
+ readonly rule: PolicyRule;
7
+ readonly action: PolicyAction;
8
+ readonly surface: PolicySurface;
9
+ };
10
+ export type PolicyDecision<A> = {
11
+ readonly value: A;
12
+ readonly redactions: ReadonlyArray<PolicyRedaction>;
13
+ readonly dropped: number;
14
+ };
15
+ export declare const transformSignalFields: (policy: DataPolicy, surface: Exclude<PolicySurface, "metric">, fields: EventAttributes) => PolicyDecision<EventAttributes>;
16
+ export declare const sanitizeText: (policy: DataPolicy, value: string, surface?: Exclude<PolicySurface, "browser-ingest" | "metric" | "resource">) => string;
@@ -0,0 +1,136 @@
1
+ import { Predicate } from "effect";
2
+ import { maxFieldKeyLength } from "../BrowserEvents.js";
3
+ import { isValidAttributeName } from "../contract/EventName.js";
4
+ import { maxOriginalStringLength, replaceStructuredText, sanitizeBrowserFields, } from "./BrowserFieldPolicy.js";
5
+ import { replaceEmailCandidates, sensitiveFieldReplacement, sensitiveTextReplacement, } from "./PolicyVocabulary.js";
6
+ const serverBounds = {
7
+ audit: { maximumFields: 64, maximumTextLength: 4_096 },
8
+ event: { maximumFields: 128, maximumTextLength: 16_384 },
9
+ log: { maximumFields: 128, maximumTextLength: 32_768 },
10
+ span: { maximumFields: 128, maximumTextLength: 32_768 },
11
+ defect: { maximumFields: 128, maximumTextLength: 65_536 },
12
+ resource: { maximumFields: 128, maximumTextLength: 8_192 },
13
+ };
14
+ const reservedPrefixes = ["event.", "browser."];
15
+ const replaceBlockedValues = (policy, value) => {
16
+ let output = value;
17
+ for (const pattern of policy.blockedValuePatterns) {
18
+ pattern.lastIndex = 0;
19
+ output = output.replace(pattern, sensitiveTextReplacement);
20
+ }
21
+ return replaceStructuredText(replaceEmailCandidates(output));
22
+ };
23
+ const sanitizeBoundedText = (policy, surface, value) => {
24
+ const maximum = serverBounds[surface].maximumTextLength;
25
+ const bounded = value.slice(0, maximum);
26
+ const sanitized = replaceBlockedValues(policy, bounded);
27
+ return {
28
+ value: sanitized.slice(0, maximum),
29
+ blocked: sanitized !== bounded,
30
+ truncated: value.length > maximum || sanitized.length > maximum,
31
+ };
32
+ };
33
+ const transformBrowserFields = (policy, fields) => {
34
+ const admitted = {};
35
+ const redactions = [];
36
+ for (const [key, value] of Object.entries(fields)) {
37
+ if (!isValidAttributeName(key.slice(0, maxFieldKeyLength)) ||
38
+ reservedPrefixes.some((prefix) => key.startsWith(prefix))) {
39
+ redactions.push({ rule: "attribute-name", action: "dropped", surface: "browser-ingest" });
40
+ continue;
41
+ }
42
+ const classification = policy.classify(key);
43
+ if (classification === "forbidden") {
44
+ redactions.push({ rule: "classification", action: "dropped", surface: "browser-ingest" });
45
+ continue;
46
+ }
47
+ if (classification === "sensitive") {
48
+ admitted[key] = sensitiveFieldReplacement;
49
+ redactions.push({
50
+ rule: policy.attributes.has(key) ? "classification" : "blocked-key",
51
+ action: "masked",
52
+ surface: "browser-ingest",
53
+ });
54
+ continue;
55
+ }
56
+ if (Predicate.isString(value)) {
57
+ const bounded = value.slice(0, maxOriginalStringLength);
58
+ const sanitized = replaceBlockedValues(policy, bounded);
59
+ admitted[key] = value.length > maxOriginalStringLength ? sensitiveTextReplacement : sanitized;
60
+ if (sanitized !== bounded) {
61
+ redactions.push({ rule: "blocked-value", action: "masked", surface: "browser-ingest" });
62
+ }
63
+ }
64
+ else {
65
+ admitted[key] = value;
66
+ }
67
+ }
68
+ const value = sanitizeBrowserFields(admitted);
69
+ const dropped = Object.keys(fields).length - Object.keys(value).length;
70
+ const boundedDrops = dropped - redactions.filter((entry) => entry.action === "dropped").length;
71
+ for (let index = 0; index < boundedDrops; index += 1) {
72
+ redactions.push({ rule: "bounds", action: "dropped", surface: "browser-ingest" });
73
+ }
74
+ for (const [key, original] of Object.entries(admitted)) {
75
+ const boundedKey = key.slice(0, maxFieldKeyLength);
76
+ const sanitized = value[boundedKey];
77
+ if (boundedKey !== key && sanitized !== undefined) {
78
+ redactions.push({ rule: "bounds", action: "truncated", surface: "browser-ingest" });
79
+ }
80
+ if (Predicate.isString(original) &&
81
+ Predicate.isString(sanitized) &&
82
+ sanitized.length < original.length) {
83
+ redactions.push({ rule: "bounds", action: "truncated", surface: "browser-ingest" });
84
+ }
85
+ }
86
+ return { value, redactions, dropped };
87
+ };
88
+ export const transformSignalFields = (policy, surface, fields) => {
89
+ if (surface === "browser-ingest")
90
+ return transformBrowserFields(policy, fields);
91
+ const admitted = {};
92
+ const redactions = [];
93
+ const maximumFields = serverBounds[surface].maximumFields;
94
+ let dropped = 0;
95
+ for (const [key, value] of Object.entries(fields)) {
96
+ if (!isValidAttributeName(key)) {
97
+ dropped += 1;
98
+ redactions.push({ rule: "attribute-name", action: "dropped", surface });
99
+ continue;
100
+ }
101
+ if (Object.keys(admitted).length >= maximumFields) {
102
+ dropped += 1;
103
+ redactions.push({ rule: "bounds", action: "dropped", surface });
104
+ continue;
105
+ }
106
+ const classification = policy.classify(key);
107
+ if (classification === "forbidden") {
108
+ dropped += 1;
109
+ redactions.push({ rule: "classification", action: "dropped", surface });
110
+ continue;
111
+ }
112
+ if (classification === "sensitive") {
113
+ admitted[key] = sensitiveFieldReplacement;
114
+ redactions.push({
115
+ rule: policy.attributes.has(key) || value === sensitiveFieldReplacement
116
+ ? "classification"
117
+ : "blocked-key",
118
+ action: "masked",
119
+ surface,
120
+ });
121
+ continue;
122
+ }
123
+ if (!Predicate.isString(value)) {
124
+ admitted[key] = value;
125
+ continue;
126
+ }
127
+ const sanitized = sanitizeBoundedText(policy, surface, value);
128
+ admitted[key] = sanitized.value;
129
+ if (sanitized.blocked)
130
+ redactions.push({ rule: "blocked-value", action: "masked", surface });
131
+ if (sanitized.truncated)
132
+ redactions.push({ rule: "bounds", action: "truncated", surface });
133
+ }
134
+ return { value: admitted, redactions, dropped };
135
+ };
136
+ export const sanitizeText = (policy, value, surface = "log") => sanitizeBoundedText(policy, surface, value).value;
@@ -0,0 +1,10 @@
1
+ export declare const sensitiveFieldReplacement = "****";
2
+ export declare const sensitiveTextReplacement = "[REDACTED]";
3
+ export declare const effectDroppedAttributesKey = "effect.dropped_attributes_count";
4
+ export declare const baseBlockedKeys: string[];
5
+ export declare const collectorBlockedKeyPattern: string;
6
+ export declare const collectorBlockedValuePatterns: string[];
7
+ export declare const baseBlockedValuePatterns: RegExp[];
8
+ export declare const baseBlockedKeyPatternSource: string;
9
+ export declare const isSensitiveFieldKey: (key: string) => boolean;
10
+ export declare const replaceEmailCandidates: (value: string) => string;
@@ -0,0 +1,84 @@
1
+ export const sensitiveFieldReplacement = "****";
2
+ export const sensitiveTextReplacement = "[REDACTED]";
3
+ export const effectDroppedAttributesKey = "effect.dropped_attributes_count";
4
+ export const baseBlockedKeys = [
5
+ "authorization",
6
+ "proxy[._-]?authorization",
7
+ "cookie",
8
+ "set[._-]?cookie",
9
+ "password",
10
+ "passwd",
11
+ "secret",
12
+ "token",
13
+ "api[._-]?key",
14
+ "apikey",
15
+ "access[._-]?key",
16
+ "client[._-]?secret",
17
+ "private[._-]?key",
18
+ "email",
19
+ "phone",
20
+ "cpf",
21
+ "cnpj",
22
+ "document",
23
+ ];
24
+ export const collectorBlockedKeyPattern = `(?i:${baseBlockedKeys.join("|")})(?:\\]|\\[|\\x22|\\x27|\\x60|[._-]|[A-Z0-9]|$)`;
25
+ const collectorWhitespace = "[[:space:]   -    \\\\x{2028}\\\\x{2029}\\\\x{feff}]";
26
+ export const collectorBlockedValuePatterns = [
27
+ `(?i)https?://[^/@${collectorWhitespace.slice(1, -1)}]+@`,
28
+ `(?i)Bearer${collectorWhitespace}+[A-Za-z0-9._~+/=-]+`,
29
+ "(?:sk|rk)[_-][A-Za-z0-9_*.-]{3,}",
30
+ "eyJ[A-Za-z0-9_-]+[.]eyJ[A-Za-z0-9_-]+[.][A-Za-z0-9_-]+",
31
+ "(?i)[A-Z0-9._%+-]+@[A-Z0-9.-]+[.][A-Z]{2,}",
32
+ "(?s)-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----.*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----",
33
+ ];
34
+ export const baseBlockedValuePatterns = [
35
+ /https?:\/\/[^/@\s]+@/gi,
36
+ /Bearer\s+[A-Za-z0-9._~+/=-]+/gi,
37
+ /(?:sk|rk)[_-][A-Za-z0-9_*.-]{3,}/g,
38
+ /eyJ[A-Za-z0-9_-]+[.]eyJ[A-Za-z0-9_-]+[.][A-Za-z0-9_-]+/g,
39
+ /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g,
40
+ ];
41
+ const asciiCaseInsensitive = (source) => source.replace(/[A-Za-z]/g, (letter) => `[${letter.toLowerCase()}${letter.toUpperCase()}]`);
42
+ const sensitiveTerms = baseBlockedKeys.map(asciiCaseInsensitive).join("|");
43
+ export const baseBlockedKeyPatternSource = `(?:${sensitiveTerms})(?=[\\]\\["'\x60]|[._-]|[A-Z0-9]|$)`;
44
+ const sensitiveKeyPattern = new RegExp(baseBlockedKeyPatternSource);
45
+ export const isSensitiveFieldKey = (key) => sensitiveKeyPattern.test(key);
46
+ const isEmailLocalCharacter = (character) => /[A-Za-z0-9._%+-]/.test(character);
47
+ const isEmailDomainCharacter = (character) => /[A-Za-z0-9.-]/.test(character);
48
+ const isAsciiLetter = (character) => /[A-Za-z]/.test(character);
49
+ export const replaceEmailCandidates = (value) => {
50
+ let output = "";
51
+ let offset = 0;
52
+ let index = 0;
53
+ while (index < value.length) {
54
+ if (!isEmailLocalCharacter(value[index] ?? "")) {
55
+ index += 1;
56
+ continue;
57
+ }
58
+ const start = index;
59
+ while (index < value.length && isEmailLocalCharacter(value[index] ?? ""))
60
+ index += 1;
61
+ if (value[index] !== "@")
62
+ continue;
63
+ index += 1;
64
+ const domainStart = index;
65
+ let lastDot = -1;
66
+ while (index < value.length && isEmailDomainCharacter(value[index] ?? "")) {
67
+ if (value[index] === ".")
68
+ lastDot = index;
69
+ index += 1;
70
+ }
71
+ let suffixIsLetters = lastDot >= domainStart && index - lastDot >= 3;
72
+ if (suffixIsLetters) {
73
+ for (let suffixIndex = lastDot + 1; suffixIndex < index; suffixIndex += 1) {
74
+ if (!isAsciiLetter(value[suffixIndex] ?? ""))
75
+ suffixIsLetters = false;
76
+ }
77
+ }
78
+ if (domainStart === index || !suffixIsLetters)
79
+ continue;
80
+ output += value.slice(offset, start) + sensitiveTextReplacement;
81
+ offset = index;
82
+ }
83
+ return output + value.slice(offset);
84
+ };
@@ -0,0 +1,9 @@
1
+ import { Effect } from "effect";
2
+ import type { ResourceAttributes } from "../ResourceIdentity.js";
3
+ import type { DataPolicy } from "./DataPolicy.js";
4
+ import { InvalidDataPolicy } from "./DataPolicyError.js";
5
+ export type ResourceAttribute = {
6
+ readonly key: string;
7
+ readonly value: string;
8
+ };
9
+ export declare const parseResourceAttributes: (policy: DataPolicy, canonical: ResourceAttributes, additions: readonly ResourceAttribute[]) => Effect.Effect<ReadonlyMap<string, string>, InvalidDataPolicy, never>;
@@ -0,0 +1,33 @@
1
+ import { Effect, Predicate } from "effect";
2
+ import { isValidAttributeName } from "../contract/EventName.js";
3
+ import { InvalidDataPolicy } from "./DataPolicyError.js";
4
+ import { transformSignalFields } from "./PolicyTransform.js";
5
+ export const parseResourceAttributes = Effect.fn("parseResourceAttributes")(function* (policy, canonical, additions) {
6
+ const input = { ...canonical };
7
+ for (const attribute of additions) {
8
+ if (!isValidAttributeName(attribute.key) ||
9
+ !Predicate.isString(attribute.value) ||
10
+ Object.hasOwn(input, attribute.key) ||
11
+ policy.classify(attribute.key) === "forbidden") {
12
+ return yield* new InvalidDataPolicy({
13
+ code: "OBS_POLICY_INVALID",
14
+ message: "Data policy compilation failed with 1 issue(s). Fix every reported rule and compile again.",
15
+ issues: [
16
+ {
17
+ code: "OBS_POLICY_DUPLICATE_RESOURCE_ATTRIBUTE",
18
+ message: "A resource attribute is invalid, duplicated, reserved, forbidden, or non-scalar.",
19
+ rule: "unique-resource-attribute",
20
+ },
21
+ ],
22
+ });
23
+ }
24
+ input[attribute.key] = attribute.value;
25
+ }
26
+ const decision = transformSignalFields(policy, "resource", input);
27
+ const parsed = new Map();
28
+ for (const [key, value] of Object.entries(decision.value)) {
29
+ if (Predicate.isString(value))
30
+ parsed.set(key, value);
31
+ }
32
+ return parsed;
33
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./index.js";
2
+ export { CorrelationContext, RequestId, RunId, SpanId, TraceId, TraceLinkage, parseRequestId, parseRunId, parseSpanId, parseTraceId, } from "../Correlation.js";
@@ -0,0 +1,2 @@
1
+ export * from "./index.js";
2
+ export { CorrelationContext, RequestId, RunId, SpanId, TraceId, TraceLinkage, parseRequestId, parseRunId, parseSpanId, parseTraceId, } from "../Correlation.js";
@@ -0,0 +1,17 @@
1
+ export { baseDataPolicy, CurrentDataPolicy, definePolicy, parseDataPolicy } from "./DataPolicy.js";
2
+ export type { DataPolicy, DataPolicyInput, PolicySurface } from "./DataPolicy.js";
3
+ export { InvalidDataPolicy, PolicyIssueCode } from "./DataPolicyError.js";
4
+ export type { PolicyIssue } from "./DataPolicyError.js";
5
+ export { DefectEnvelope, sanitizeDefectEnvelope, unexpectedDefect } from "./DefectEnvelope.js";
6
+ export { defectDeduplicator } from "./Deduplication.js";
7
+ export type { DedupeDecision, DefectDeduplicator } from "./Deduplication.js";
8
+ export type { UnexpectedDefectInput } from "./DefectEnvelope.js";
9
+ export { metricLabelRejection } from "./MetricLabelPolicy.js";
10
+ export type { MetricLabelRejection } from "./MetricLabelPolicy.js";
11
+ export { sanitizeText, transformSignalFields } from "./PolicyTransform.js";
12
+ export type { PolicyAction, PolicyDecision, PolicyRedaction, PolicyRule, } from "./PolicyTransform.js";
13
+ export { parseResourceAttributes } from "./ResourceAttributePolicy.js";
14
+ export type { ResourceAttribute } from "./ResourceAttributePolicy.js";
15
+ export { ResourceIdentity, parseResourceIdentity } from "../ResourceIdentity.js";
16
+ export type { ResourceIdentityInput } from "../ResourceIdentity.js";
17
+ export { baseBlockedKeys, collectorBlockedKeyPattern, collectorBlockedValuePatterns, isSensitiveFieldKey, sensitiveFieldReplacement, sensitiveTextReplacement, } from "./PolicyVocabulary.js";
@@ -0,0 +1,9 @@
1
+ export { baseDataPolicy, CurrentDataPolicy, definePolicy, parseDataPolicy } from "./DataPolicy.js";
2
+ export { InvalidDataPolicy, PolicyIssueCode } from "./DataPolicyError.js";
3
+ export { DefectEnvelope, sanitizeDefectEnvelope, unexpectedDefect } from "./DefectEnvelope.js";
4
+ export { defectDeduplicator } from "./Deduplication.js";
5
+ export { metricLabelRejection } from "./MetricLabelPolicy.js";
6
+ export { sanitizeText, transformSignalFields } from "./PolicyTransform.js";
7
+ export { parseResourceAttributes } from "./ResourceAttributePolicy.js";
8
+ export { ResourceIdentity, parseResourceIdentity } from "../ResourceIdentity.js";
9
+ export { baseBlockedKeys, collectorBlockedKeyPattern, collectorBlockedValuePatterns, isSensitiveFieldKey, sensitiveFieldReplacement, sensitiveTextReplacement, } from "./PolicyVocabulary.js";
@@ -0,0 +1,2 @@
1
+ export { parseDataPolicy } from "../policy/DataPolicy.js";
2
+ export type { DataPolicy, DataPolicyInput } from "../policy/DataPolicy.js";
@@ -0,0 +1 @@
1
+ export { parseDataPolicy } from "../policy/DataPolicy.js";
@@ -0,0 +1,22 @@
1
+ import { Effect } from "effect";
2
+ import type { EnvironmentVariables } from "../TelemetryConfig.js";
3
+ import { DuplicateReleaseVariable } from "./ObservabilityConfigError.js";
4
+ export type DeploymentScope = "local" | "remote";
5
+ type EnvironmentPolicyResolution = {
6
+ readonly kind: "resolved";
7
+ readonly endpoint: URL;
8
+ readonly deployment: DeploymentScope;
9
+ readonly serviceVersion: string;
10
+ readonly environment: string;
11
+ } | {
12
+ readonly kind: "missing-remote-identity";
13
+ readonly missing: "service-version" | "environment" | "service-version-and-environment";
14
+ };
15
+ export declare const deploymentScopeFromEndpoint: (endpoint: URL) => DeploymentScope;
16
+ export declare const resolveEnvironmentPolicy: (input: {
17
+ readonly endpoint?: URL | undefined;
18
+ readonly serviceVersion?: string | undefined;
19
+ readonly environment?: string | undefined;
20
+ }) => EnvironmentPolicyResolution;
21
+ export declare const rejectSecondReleaseVariables: (env: EnvironmentVariables) => Effect.Effect<void, DuplicateReleaseVariable>;
22
+ export {};
@@ -0,0 +1,41 @@
1
+ import { Effect } from "effect";
2
+ import { DuplicateReleaseVariable, secondReleaseVariables } from "./ObservabilityConfigError.js";
3
+ const defaultOtlpEndpoint = "http://localhost:4318";
4
+ export const deploymentScopeFromEndpoint = (endpoint) => {
5
+ const hostname = endpoint.hostname;
6
+ return hostname === "localhost" ||
7
+ hostname === "localhost." ||
8
+ hostname === "[::1]" ||
9
+ /^127(?:\.\d{1,3}){3}$/.test(hostname) ||
10
+ /^\[::ffff:7f[0-9a-f]{2}:[0-9a-f]{1,4}\]$/.test(hostname)
11
+ ? "local"
12
+ : "remote";
13
+ };
14
+ export const resolveEnvironmentPolicy = (input) => {
15
+ const endpoint = input.endpoint ?? new URL(defaultOtlpEndpoint);
16
+ const deployment = deploymentScopeFromEndpoint(endpoint);
17
+ const serviceVersion = input.serviceVersion ?? (deployment === "local" ? "0.0.0" : undefined);
18
+ const environment = input.environment ?? (deployment === "local" ? "development" : undefined);
19
+ if (serviceVersion !== undefined && environment !== undefined) {
20
+ return { kind: "resolved", endpoint, deployment, serviceVersion, environment };
21
+ }
22
+ const missing = serviceVersion === undefined && environment === undefined
23
+ ? "service-version-and-environment"
24
+ : serviceVersion === undefined
25
+ ? "service-version"
26
+ : "environment";
27
+ return { kind: "missing-remote-identity", missing };
28
+ };
29
+ export const rejectSecondReleaseVariables = (env) => {
30
+ for (const variable of secondReleaseVariables) {
31
+ const value = env[variable];
32
+ if (value !== undefined && value !== "") {
33
+ return Effect.fail(new DuplicateReleaseVariable({
34
+ code: "OBS_TELEMETRY_DUPLICATE_RELEASE_VARIABLE",
35
+ variable,
36
+ message: `${variable} defines a second release identity. Remove it and set OTEL_SERVICE_VERSION.`,
37
+ }));
38
+ }
39
+ }
40
+ return Effect.void;
41
+ };
@@ -0,0 +1,24 @@
1
+ import { Cause, Effect, Schema } from "effect";
2
+ import type { OtlpExporter } from "effect/unstable/observability";
3
+ import { type AdapterRegistration, type LifecycleReport, type StartedAdapter } from "./ObservabilityAdapter.js";
4
+ import { InvalidObservabilityConfig } from "./ObservabilityConfigError.js";
5
+ import { type NodeObservabilityProfile, type ObservabilityProfile } from "./ObservabilityProfile.js";
6
+ declare const ObservabilityLifecycleError_base: Schema.Class<ObservabilityLifecycleError, Schema.TaggedStruct<"ObservabilityLifecycleError", {
7
+ readonly code: Schema.Literals<readonly ["OBS_OBSERVABILITY_STARTUP_FAILED", "OBS_OBSERVABILITY_CLOSED"]>;
8
+ readonly message: Schema.String;
9
+ readonly adapter: Schema.Option<Schema.brand<Schema.String, "AdapterName">>;
10
+ readonly cause: Schema.Defect;
11
+ }>, Cause.YieldableError>;
12
+ export declare class ObservabilityLifecycleError extends ObservabilityLifecycleError_base {
13
+ }
14
+ type AdapterValidationOptions = {
15
+ readonly allowTesting: boolean;
16
+ };
17
+ export declare const validateAdapterRegistrationKinds: (registrations: ReadonlyArray<AdapterRegistration>, options: AdapterValidationOptions) => Effect.Effect<void, InvalidObservabilityConfig>;
18
+ export declare const validateAdapterRegistrations: (profile: ObservabilityProfile, environment: string, registrations: readonly AdapterRegistration[], options: AdapterValidationOptions) => Effect.Effect<void, InvalidObservabilityConfig, never>;
19
+ export type LifecycleRegistry = {
20
+ readonly run: (operation: "flush" | "close") => Effect.Effect<LifecycleReport>;
21
+ };
22
+ export declare const createLifecycleRegistry: (profile: NodeObservabilityProfile, started: ReadonlyArray<StartedAdapter>, flusher: OtlpExporter.Flusher["Service"], disposeRuntime: Effect.Effect<void>) => LifecycleRegistry;
23
+ export declare const rollbackStartedAdapters: (started: readonly StartedAdapter[]) => Effect.Effect<void, never, never>;
24
+ export {};