@equipe-tech/observability 0.2.0 → 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.
- package/README.md +11 -9
- package/dist/BrowserEvents.d.ts +89 -12
- package/dist/BrowserEvents.js +67 -13
- package/dist/Correlation.d.ts +51 -0
- package/dist/Correlation.js +95 -0
- package/dist/Metrics.d.ts +19 -3
- package/dist/Metrics.js +16 -0
- package/dist/MetricsRuntime.d.ts +45 -3
- package/dist/MetricsRuntime.js +478 -120
- package/dist/PolicyOtlpLogger.d.ts +18 -0
- package/dist/PolicyOtlpLogger.js +142 -0
- package/dist/RedactionPolicy.d.ts +2 -9
- package/dist/RedactionPolicy.js +2 -246
- package/dist/ResourceIdentity.d.ts +47 -0
- package/dist/ResourceIdentity.js +82 -0
- package/dist/Telemetry.d.ts +18 -3
- package/dist/Telemetry.js +43 -25
- package/dist/TelemetryConfig.d.ts +11 -4
- package/dist/TelemetryConfig.js +38 -10
- package/dist/audit/AuditCommitDocument.d.ts +41 -0
- package/dist/audit/AuditCommitDocument.js +58 -0
- package/dist/audit/AuditDigest.d.ts +11 -0
- package/dist/audit/AuditDigest.js +25 -0
- package/dist/audit/AuditOutbox.d.ts +75 -0
- package/dist/audit/AuditOutbox.js +104 -0
- package/dist/audit/AuditPublisher.d.ts +49 -0
- package/dist/audit/AuditPublisher.js +60 -0
- package/dist/audit/AuditRecord.d.ts +128 -0
- package/dist/audit/AuditRecord.js +172 -0
- package/dist/audit/CommittedAuditRecordInternal.d.ts +11 -0
- package/dist/audit/CommittedAuditRecordInternal.js +13 -0
- package/dist/audit/InvalidAuditRecord.d.ts +12 -0
- package/dist/audit/InvalidAuditRecord.js +16 -0
- package/dist/audit/index.d.ts +7 -0
- package/dist/audit/index.js +7 -0
- package/dist/browser/BrowserClient.d.ts +82 -3
- package/dist/browser/BrowserClient.js +322 -28
- package/dist/browser/BrowserEventLimits.d.ts +8 -0
- package/dist/browser/BrowserEventLimits.js +8 -0
- package/dist/browser/BrowserMetricRecorder.d.ts +17 -0
- package/dist/browser/BrowserMetricRecorder.js +35 -0
- package/dist/browser/BrowserTelemetry.d.ts +40 -0
- package/dist/browser/BrowserTelemetry.js +105 -0
- package/dist/browser/ClientPolicy.d.ts +3 -0
- package/dist/browser/ClientPolicy.js +52 -0
- package/dist/browser/client.d.ts +3 -2
- package/dist/browser/client.js +2 -1
- package/dist/browser/index.d.ts +4 -37
- package/dist/browser/index.js +3 -95
- package/dist/contract/ContractCompatibility.d.ts +31 -0
- package/dist/contract/ContractCompatibility.js +274 -0
- package/dist/contract/ContractIndex.d.ts +54 -0
- package/dist/contract/ContractIndex.js +178 -0
- package/dist/contract/ContractSurface.d.ts +111 -0
- package/dist/contract/ContractSurface.js +116 -0
- package/dist/contract/EventName.d.ts +6 -0
- package/dist/contract/EventName.js +55 -0
- package/dist/contract/EventProducer.d.ts +123 -0
- package/dist/contract/EventProducer.js +209 -0
- package/dist/contract/MetricContractError.d.ts +19 -0
- package/dist/contract/MetricContractError.js +19 -0
- package/dist/contract/MetricDefinition.d.ts +61 -0
- package/dist/contract/MetricDefinition.js +91 -0
- package/dist/contract/MetricProducer.d.ts +42 -0
- package/dist/contract/MetricProducer.js +123 -0
- package/dist/contract/OrganizationEvents.d.ts +165 -0
- package/dist/contract/OrganizationEvents.js +97 -0
- package/dist/contract/TelemetryContract.d.ts +95 -0
- package/dist/contract/TelemetryContract.js +451 -0
- package/dist/contract/TelemetryContractError.d.ts +44 -0
- package/dist/contract/TelemetryContractError.js +64 -0
- package/dist/contract/TelemetryEvent.d.ts +59 -0
- package/dist/contract/TelemetryEvent.js +54 -0
- package/dist/contract/index.d.ts +11 -0
- package/dist/contract/index.js +11 -0
- package/dist/effect/WideEvent.d.ts +3 -0
- package/dist/effect/WideEvent.js +20 -0
- package/dist/effect/WideEventSink.d.ts +3 -0
- package/dist/effect/WideEventSink.js +79 -0
- package/dist/effect/index.d.ts +2 -0
- package/dist/effect/index.js +2 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.js +11 -2
- package/dist/metrics/InstrumentGrammar.d.ts +5 -0
- package/dist/metrics/InstrumentGrammar.js +12 -0
- package/dist/metrics/MetricScalar.d.ts +3 -0
- package/dist/metrics/MetricScalar.js +5 -0
- package/dist/node/AuditDigest.d.ts +3 -0
- package/dist/node/AuditDigest.js +6 -0
- package/dist/node/BrowserEventIngest.d.ts +7 -2
- package/dist/node/BrowserEventIngest.js +111 -19
- package/dist/node/LocalCollector.d.ts +20 -0
- package/dist/node/LocalCollector.js +188 -0
- package/dist/node/Observability.d.ts +49 -0
- package/dist/node/Observability.js +190 -0
- package/dist/node/Runtime.d.ts +4 -1
- package/dist/node/index.d.ts +4 -0
- package/dist/node/index.js +3 -0
- package/dist/policy/BrowserFieldPolicy.d.ts +7 -0
- package/dist/policy/BrowserFieldPolicy.js +298 -0
- package/dist/policy/DataPolicy.d.ts +19 -0
- package/dist/policy/DataPolicy.js +254 -0
- package/dist/policy/DataPolicyError.d.ts +22 -0
- package/dist/policy/DataPolicyError.js +25 -0
- package/dist/policy/Deduplication.d.ts +13 -0
- package/dist/policy/Deduplication.js +44 -0
- package/dist/policy/DefectEnvelope.d.ts +25 -0
- package/dist/policy/DefectEnvelope.js +52 -0
- package/dist/policy/MetricLabelPolicy.d.ts +5 -0
- package/dist/policy/MetricLabelPolicy.js +65 -0
- package/dist/policy/PolicyTransform.d.ts +16 -0
- package/dist/policy/PolicyTransform.js +136 -0
- package/dist/policy/PolicyVocabulary.d.ts +10 -0
- package/dist/policy/PolicyVocabulary.js +84 -0
- package/dist/policy/ResourceAttributePolicy.d.ts +9 -0
- package/dist/policy/ResourceAttributePolicy.js +33 -0
- package/dist/policy/entrypoint.d.ts +2 -0
- package/dist/policy/entrypoint.js +2 -0
- package/dist/policy/index.d.ts +17 -0
- package/dist/policy/index.js +9 -0
- package/dist/profile/DataPolicy.d.ts +2 -0
- package/dist/profile/DataPolicy.js +1 -0
- package/dist/profile/EnvironmentPolicy.d.ts +22 -0
- package/dist/profile/EnvironmentPolicy.js +41 -0
- package/dist/profile/LifecycleRegistry.d.ts +24 -0
- package/dist/profile/LifecycleRegistry.js +240 -0
- package/dist/profile/ObservabilityAdapter.d.ts +115 -0
- package/dist/profile/ObservabilityAdapter.js +50 -0
- package/dist/profile/ObservabilityConfig.d.ts +68 -0
- package/dist/profile/ObservabilityConfig.js +125 -0
- package/dist/profile/ObservabilityConfigError.d.ts +23 -0
- package/dist/profile/ObservabilityConfigError.js +39 -0
- package/dist/profile/ObservabilityProfile.d.ts +49 -0
- package/dist/profile/ObservabilityProfile.js +121 -0
- package/dist/profile/ReactWebProfile.d.ts +8 -0
- package/dist/profile/ReactWebProfile.js +7 -0
- package/dist/profile/index.d.ts +5 -0
- package/dist/profile/index.js +5 -0
- package/dist/testing/conformance/ConformanceAssertions.d.ts +5 -0
- package/dist/testing/conformance/ConformanceAssertions.js +31 -0
- package/dist/testing/conformance/ConformanceFailure.d.ts +35 -0
- package/dist/testing/conformance/ConformanceFailure.js +56 -0
- package/dist/testing/conformance/ConformanceModel.d.ts +97 -0
- package/dist/testing/conformance/ConformanceModel.js +8 -0
- package/dist/testing/conformance/ConformanceRegistry.d.ts +13 -0
- package/dist/testing/conformance/ConformanceRegistry.js +190 -0
- package/dist/testing/conformance/ConformanceRunner.d.ts +5 -0
- package/dist/testing/conformance/ConformanceRunner.js +198 -0
- package/dist/testing/conformance/TelemetryEvidence.d.ts +91 -0
- package/dist/testing/conformance/TelemetryEvidence.js +370 -0
- package/dist/testing/conformance/index.d.ts +10 -0
- package/dist/testing/conformance/index.js +6 -0
- package/dist/testing/contract.d.ts +81 -0
- package/dist/testing/contract.js +93 -0
- package/dist/testing/deployedCanary.d.ts +1 -0
- package/dist/testing/deployedCanary.js +1 -0
- package/dist/testing/index.d.ts +40 -3
- package/dist/testing/index.js +122 -5
- package/dist/trace/HttpServerOtlpTracer.d.ts +42 -0
- package/dist/trace/HttpServerOtlpTracer.js +301 -0
- package/package.json +16 -29
- package/dist/WideEvent.d.ts +0 -5
- package/dist/WideEvent.js +0 -6
- package/dist/nestjs/BrowserEventsController.d.ts +0 -21
- package/dist/nestjs/BrowserEventsController.js +0 -52
- package/dist/nestjs/HttpRoutePolicy.d.ts +0 -23
- package/dist/nestjs/HttpRoutePolicy.js +0 -179
- package/dist/nestjs/HttpServerOtlpTracer.d.ts +0 -15
- package/dist/nestjs/HttpServerOtlpTracer.js +0 -154
- package/dist/nestjs/RequestWideEventTraceCorrelation.d.ts +0 -15
- package/dist/nestjs/RequestWideEventTraceCorrelation.js +0 -13
- package/dist/nestjs/TelemetryInterceptor.d.ts +0 -31
- package/dist/nestjs/TelemetryInterceptor.js +0 -268
- package/dist/nestjs/TelemetryModule.d.ts +0 -53
- package/dist/nestjs/TelemetryModule.js +0 -428
- package/dist/nestjs/index.d.ts +0 -5
- package/dist/nestjs/index.js +0 -4
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { Effect, Option, Schema } from "effect";
|
|
2
|
+
import { AuditAction, AuditOutcome, AuditReasonCode, AuditResourceType, } from "../audit/AuditRecord.js";
|
|
3
|
+
import { EventName, isValidAttributeName, isValidEventName } from "./EventName.js";
|
|
4
|
+
import { EventKind, EventSeverity, } from "./TelemetryEvent.js";
|
|
5
|
+
import { InvalidTelemetryContract, InvalidTelemetryEvent, } from "./TelemetryContractError.js";
|
|
6
|
+
import { isValidHistogramBoundaries, isValidMetricAttributeCount, isValidMetricAttributeName, isValidMetricCardinality, isValidMetricDescription, isValidMetricName, isValidMetricUnit, validAllowedValues, } from "./MetricDefinition.js";
|
|
7
|
+
export { isValidMetricName } from "./MetricDefinition.js";
|
|
8
|
+
export const AttributeClassification = Schema.Literals([
|
|
9
|
+
"public",
|
|
10
|
+
"internal",
|
|
11
|
+
"sensitive",
|
|
12
|
+
"forbidden",
|
|
13
|
+
]);
|
|
14
|
+
export const defineEventDefinitions = (events) => events;
|
|
15
|
+
export const telemetryContractDefinition = (definition) => definition;
|
|
16
|
+
export const validateContractEvent = (contract, eventName, attributes) => {
|
|
17
|
+
const parsedEventName = EventName.makeOption(eventName);
|
|
18
|
+
const definition = Option.isSome(parsedEventName)
|
|
19
|
+
? contract.eventByName.get(parsedEventName.value)
|
|
20
|
+
: undefined;
|
|
21
|
+
if (definition === undefined) {
|
|
22
|
+
return new InvalidTelemetryEvent({
|
|
23
|
+
code: "OBS_EVENT_UNKNOWN_NAME",
|
|
24
|
+
message: `Event "${eventName}" is not declared by the telemetry contract. Use a valid declared canonical event name.`,
|
|
25
|
+
eventName,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
for (const required of definition.requiredAttributes) {
|
|
29
|
+
if (!Object.hasOwn(attributes, required)) {
|
|
30
|
+
return new InvalidTelemetryEvent({
|
|
31
|
+
code: "OBS_EVENT_MISSING_ATTRIBUTE",
|
|
32
|
+
message: `Event "${eventName}" is missing required attribute "${required}". Add the declared scalar attribute before emitting.`,
|
|
33
|
+
eventName,
|
|
34
|
+
attributeName: required,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
for (const attributeName of Object.keys(attributes)) {
|
|
39
|
+
if (!definition.attributes.has(attributeName)) {
|
|
40
|
+
return new InvalidTelemetryEvent({
|
|
41
|
+
code: "OBS_EVENT_UNDECLARED_ATTRIBUTE",
|
|
42
|
+
message: `Event "${eventName}" does not declare attribute "${attributeName}". Add it to the contract or remove it from the event.`,
|
|
43
|
+
eventName,
|
|
44
|
+
attributeName,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return definition;
|
|
49
|
+
};
|
|
50
|
+
export const telemetryContractProvenance = (contract) => contract.provenance;
|
|
51
|
+
const compareContractKeys = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
52
|
+
const canonicalTelemetryContractProvenance = (definition) => JSON.stringify({
|
|
53
|
+
auditActions: Object.fromEntries(Object.entries(definition.auditActions)
|
|
54
|
+
.sort(([left], [right]) => compareContractKeys(left, right))
|
|
55
|
+
.map(([alias, action]) => [
|
|
56
|
+
alias,
|
|
57
|
+
{
|
|
58
|
+
action: action.action,
|
|
59
|
+
allowedOutcomes: action.allowedOutcomes,
|
|
60
|
+
reasonCodes: action.reasonCodes,
|
|
61
|
+
resourceType: action.resourceType,
|
|
62
|
+
},
|
|
63
|
+
])),
|
|
64
|
+
events: Object.fromEntries(Object.entries(definition.events)
|
|
65
|
+
.sort(([left], [right]) => compareContractKeys(left, right))
|
|
66
|
+
.map(([alias, event]) => [
|
|
67
|
+
alias,
|
|
68
|
+
{
|
|
69
|
+
attributes: Object.fromEntries(Object.entries(event.attributes)
|
|
70
|
+
.sort(([left], [right]) => compareContractKeys(left, right))
|
|
71
|
+
.map(([name, attribute]) => [
|
|
72
|
+
name,
|
|
73
|
+
{
|
|
74
|
+
classification: attribute.classification,
|
|
75
|
+
metricLabel: attribute.metricLabel,
|
|
76
|
+
required: attribute.required,
|
|
77
|
+
},
|
|
78
|
+
])),
|
|
79
|
+
defaultSeverity: event.defaultSeverity,
|
|
80
|
+
kind: event.kind,
|
|
81
|
+
mandatory: event.mandatory,
|
|
82
|
+
name: event.name,
|
|
83
|
+
sampling: event.sampling.kind === "rate"
|
|
84
|
+
? { kind: event.sampling.kind, rate: event.sampling.rate }
|
|
85
|
+
: { kind: event.sampling.kind },
|
|
86
|
+
},
|
|
87
|
+
])),
|
|
88
|
+
metrics: Object.fromEntries(Object.entries(definition.metrics)
|
|
89
|
+
.sort(([left], [right]) => compareContractKeys(left, right))
|
|
90
|
+
.map(([alias, metric]) => [
|
|
91
|
+
alias,
|
|
92
|
+
{
|
|
93
|
+
attributes: Object.fromEntries(Object.entries(metric.attributes)
|
|
94
|
+
.sort(([left], [right]) => compareContractKeys(left, right))
|
|
95
|
+
.map(([name, attribute]) => [
|
|
96
|
+
name,
|
|
97
|
+
{
|
|
98
|
+
allowedValues: attribute.allowedValues,
|
|
99
|
+
classification: attribute.classification,
|
|
100
|
+
maximumCardinality: attribute.maximumCardinality,
|
|
101
|
+
},
|
|
102
|
+
])),
|
|
103
|
+
boundaries: metric.boundaries,
|
|
104
|
+
description: metric.description,
|
|
105
|
+
kind: metric.kind,
|
|
106
|
+
name: metric.name,
|
|
107
|
+
unit: metric.unit,
|
|
108
|
+
},
|
|
109
|
+
])),
|
|
110
|
+
version: definition.version,
|
|
111
|
+
});
|
|
112
|
+
const immutableMap = (entries) => {
|
|
113
|
+
const values = new Map(entries);
|
|
114
|
+
let readonlyValues;
|
|
115
|
+
readonlyValues = Object.freeze({
|
|
116
|
+
get size() {
|
|
117
|
+
return values.size;
|
|
118
|
+
},
|
|
119
|
+
get: (key) => values.get(key),
|
|
120
|
+
has: (key) => values.has(key),
|
|
121
|
+
entries: () => values.entries(),
|
|
122
|
+
keys: () => values.keys(),
|
|
123
|
+
values: () => values.values(),
|
|
124
|
+
forEach(callback, thisArg) {
|
|
125
|
+
values.forEach((value, key) => callback.call(thisArg, value, key, readonlyValues));
|
|
126
|
+
},
|
|
127
|
+
[Symbol.iterator]: () => values[Symbol.iterator](),
|
|
128
|
+
});
|
|
129
|
+
return readonlyValues;
|
|
130
|
+
};
|
|
131
|
+
const freezeTelemetryContractDefinition = (definition) => {
|
|
132
|
+
for (const event of Object.values(definition.events)) {
|
|
133
|
+
Object.freeze(event.sampling);
|
|
134
|
+
Object.values(event.attributes).forEach(Object.freeze);
|
|
135
|
+
Object.freeze(event.attributes);
|
|
136
|
+
Object.freeze(event);
|
|
137
|
+
}
|
|
138
|
+
for (const metric of Object.values(definition.metrics)) {
|
|
139
|
+
if (metric.boundaries !== undefined)
|
|
140
|
+
Object.freeze(metric.boundaries);
|
|
141
|
+
for (const attribute of Object.values(metric.attributes)) {
|
|
142
|
+
if (attribute.allowedValues !== undefined)
|
|
143
|
+
Object.freeze(attribute.allowedValues);
|
|
144
|
+
Object.freeze(attribute);
|
|
145
|
+
}
|
|
146
|
+
Object.freeze(metric.attributes);
|
|
147
|
+
Object.freeze(metric);
|
|
148
|
+
}
|
|
149
|
+
for (const action of Object.values(definition.auditActions)) {
|
|
150
|
+
Object.freeze(action.allowedOutcomes);
|
|
151
|
+
if (action.reasonCodes !== undefined)
|
|
152
|
+
Object.freeze(action.reasonCodes);
|
|
153
|
+
Object.freeze(action);
|
|
154
|
+
}
|
|
155
|
+
Object.freeze(definition.events);
|
|
156
|
+
Object.freeze(definition.metrics);
|
|
157
|
+
Object.freeze(definition.auditActions);
|
|
158
|
+
return Object.freeze(definition);
|
|
159
|
+
};
|
|
160
|
+
const isAttributeClassification = Schema.is(AttributeClassification);
|
|
161
|
+
const isEventKind = Schema.is(EventKind);
|
|
162
|
+
const isEventSeverity = Schema.is(EventSeverity);
|
|
163
|
+
const isAuditAction = Schema.is(AuditAction);
|
|
164
|
+
const isAuditOutcome = Schema.is(AuditOutcome);
|
|
165
|
+
const isAuditReasonCode = Schema.is(AuditReasonCode);
|
|
166
|
+
const isAuditResourceType = Schema.is(AuditResourceType);
|
|
167
|
+
const canonicalSinkFields = new Set([
|
|
168
|
+
"event.name",
|
|
169
|
+
"event.kind",
|
|
170
|
+
"event.type",
|
|
171
|
+
"event.severity",
|
|
172
|
+
"event.outcome",
|
|
173
|
+
"event.timestamp",
|
|
174
|
+
"event.duration_ms",
|
|
175
|
+
"event.source",
|
|
176
|
+
"event.policy_dropped_attributes",
|
|
177
|
+
"browser.event.id",
|
|
178
|
+
"browser.event.occurred_at",
|
|
179
|
+
"http.request.method",
|
|
180
|
+
"http.route",
|
|
181
|
+
"http.response.status_code",
|
|
182
|
+
"error.type",
|
|
183
|
+
"error.name",
|
|
184
|
+
"error.message",
|
|
185
|
+
"error.status",
|
|
186
|
+
"error.retryable",
|
|
187
|
+
"audit.action",
|
|
188
|
+
"audit.actor.kind",
|
|
189
|
+
"audit.actor.id",
|
|
190
|
+
"audit.resource.type",
|
|
191
|
+
"audit.resource.id",
|
|
192
|
+
"audit.outcome",
|
|
193
|
+
"audit.reason_code",
|
|
194
|
+
"audit.tenant.id",
|
|
195
|
+
"audit.record.id",
|
|
196
|
+
"audit.record.hash",
|
|
197
|
+
"audit.occurred_at",
|
|
198
|
+
"audit.schema_version",
|
|
199
|
+
"request.id",
|
|
200
|
+
"run.id",
|
|
201
|
+
]);
|
|
202
|
+
const AttributeDefinitionDocument = Schema.Struct({
|
|
203
|
+
classification: Schema.String,
|
|
204
|
+
required: Schema.Boolean,
|
|
205
|
+
metricLabel: Schema.Boolean,
|
|
206
|
+
});
|
|
207
|
+
const EventDefinitionDocument = Schema.Struct({
|
|
208
|
+
name: Schema.String,
|
|
209
|
+
kind: Schema.String,
|
|
210
|
+
defaultSeverity: Schema.String,
|
|
211
|
+
mandatory: Schema.Boolean,
|
|
212
|
+
sampling: Schema.Struct({
|
|
213
|
+
kind: Schema.String,
|
|
214
|
+
rate: Schema.Number.pipe(Schema.optionalKey),
|
|
215
|
+
}),
|
|
216
|
+
attributes: Schema.Record(Schema.String, AttributeDefinitionDocument),
|
|
217
|
+
});
|
|
218
|
+
const MetricAttributeDefinitionDocument = Schema.Struct({
|
|
219
|
+
classification: Schema.String,
|
|
220
|
+
maximumCardinality: Schema.Number,
|
|
221
|
+
allowedValues: Schema.Array(Schema.Union([Schema.String, Schema.Number, Schema.Boolean])).pipe(Schema.optionalKey),
|
|
222
|
+
});
|
|
223
|
+
const MetricDefinitionDocument = Schema.Struct({
|
|
224
|
+
name: Schema.String,
|
|
225
|
+
description: Schema.String,
|
|
226
|
+
unit: Schema.String,
|
|
227
|
+
kind: Schema.String,
|
|
228
|
+
boundaries: Schema.Array(Schema.Number).pipe(Schema.optionalKey),
|
|
229
|
+
attributes: Schema.Record(Schema.String, MetricAttributeDefinitionDocument),
|
|
230
|
+
});
|
|
231
|
+
const AuditActionDefinitionDocument = Schema.Struct({
|
|
232
|
+
action: Schema.String,
|
|
233
|
+
resourceType: Schema.String,
|
|
234
|
+
allowedOutcomes: Schema.Array(Schema.String),
|
|
235
|
+
reasonCodes: Schema.Array(Schema.String).pipe(Schema.optionalKey),
|
|
236
|
+
});
|
|
237
|
+
const TelemetryContractDocument = Schema.Struct({
|
|
238
|
+
version: Schema.Number,
|
|
239
|
+
events: Schema.Record(Schema.String, EventDefinitionDocument),
|
|
240
|
+
metrics: Schema.Record(Schema.String, MetricDefinitionDocument),
|
|
241
|
+
auditActions: Schema.Record(Schema.String, AuditActionDefinitionDocument),
|
|
242
|
+
});
|
|
243
|
+
const decodeTelemetryContractDocument = Schema.decodeUnknownEffect(TelemetryContractDocument);
|
|
244
|
+
const issue = (code, message, context = {}) => ({ code, message, ...context });
|
|
245
|
+
const collectIssues = (definition) => {
|
|
246
|
+
const issues = [];
|
|
247
|
+
const eventAliasesByName = new Map();
|
|
248
|
+
const auditActionAliasesByName = new Map();
|
|
249
|
+
const metricAliasesByName = new Map();
|
|
250
|
+
if (!Number.isSafeInteger(definition.version) || definition.version <= 0) {
|
|
251
|
+
issues.push(issue("OBS_CONTRACT_INVALID_VERSION", "Telemetry contract version is invalid. Use a positive safe integer."));
|
|
252
|
+
}
|
|
253
|
+
for (const [alias, event] of Object.entries(definition.events)) {
|
|
254
|
+
if (!isValidEventName(event.name)) {
|
|
255
|
+
issues.push(issue("OBS_CONTRACT_INVALID_EVENT_NAME", `Event "${event.name}" is invalid. Use two to four lowercase dot-separated parts without environment, severity, outcome, or identifier segments.`, { eventAlias: alias, eventName: event.name }));
|
|
256
|
+
}
|
|
257
|
+
const existingAlias = eventAliasesByName.get(event.name);
|
|
258
|
+
if (existingAlias !== undefined) {
|
|
259
|
+
issues.push(issue("OBS_CONTRACT_DUPLICATE_EVENT_NAME", `Event "${event.name}" is declared by aliases "${existingAlias}" and "${alias}". Give each event one canonical name.`, { eventAlias: alias, eventName: event.name }));
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
eventAliasesByName.set(event.name, alias);
|
|
263
|
+
}
|
|
264
|
+
if (!isEventKind(event.kind)) {
|
|
265
|
+
issues.push(issue("OBS_CONTRACT_INVALID_EVENT_KIND", `Event "${event.name}" has an invalid kind. Use request, operation, domain, defect, or audit.`, { eventAlias: alias, eventName: event.name }));
|
|
266
|
+
}
|
|
267
|
+
if (!isEventSeverity(event.defaultSeverity)) {
|
|
268
|
+
issues.push(issue("OBS_CONTRACT_INVALID_DEFAULT_SEVERITY", `Event "${event.name}" has an invalid default severity. Use debug, info, warn, error, or fatal.`, { eventAlias: alias, eventName: event.name }));
|
|
269
|
+
}
|
|
270
|
+
if (event.sampling.kind === "rate") {
|
|
271
|
+
if (!Number.isFinite(event.sampling.rate) ||
|
|
272
|
+
event.sampling.rate <= 0 ||
|
|
273
|
+
event.sampling.rate > 1) {
|
|
274
|
+
issues.push(issue("OBS_CONTRACT_INVALID_SAMPLING_RATE", `Event "${event.name}" has an invalid sampling rate. Use a finite rate greater than zero and at most one.`, { eventAlias: alias, eventName: event.name }));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else if (event.sampling.kind !== "always") {
|
|
278
|
+
issues.push(issue("OBS_CONTRACT_INVALID_SAMPLING_RATE", `Event "${event.name}" has an invalid sampling policy. Use always or rate.`, { eventAlias: alias, eventName: event.name }));
|
|
279
|
+
}
|
|
280
|
+
for (const [attributeName, attribute] of Object.entries(event.attributes)) {
|
|
281
|
+
if (!isValidAttributeName(attributeName)) {
|
|
282
|
+
issues.push(issue("OBS_CONTRACT_INVALID_ATTRIBUTE_NAME", `Attribute "${attributeName}" is invalid. Use a dotted lowercase name no longer than 128 characters.`, { eventAlias: alias, eventName: event.name, attributeName }));
|
|
283
|
+
}
|
|
284
|
+
if (canonicalSinkFields.has(attributeName)) {
|
|
285
|
+
issues.push(issue("OBS_CONTRACT_RESERVED_ATTRIBUTE_NAME", `Attribute "${attributeName}" is a canonical sink field. Rename the application attribute.`, { eventAlias: alias, eventName: event.name, attributeName }));
|
|
286
|
+
}
|
|
287
|
+
if (!isAttributeClassification(attribute.classification) ||
|
|
288
|
+
((attribute.classification === "sensitive" || attribute.classification === "forbidden") &&
|
|
289
|
+
(attribute.required || attribute.metricLabel))) {
|
|
290
|
+
issues.push(issue("OBS_CONTRACT_INVALID_ATTRIBUTE_DEFINITION", `Attribute "${attributeName}" has an invalid classification or incompatible flags. Use public, internal, sensitive, or forbidden, and set required and metricLabel to false for restricted attributes.`, { eventAlias: alias, eventName: event.name, attributeName }));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
for (const [alias, metric] of Object.entries(definition.metrics)) {
|
|
295
|
+
const metricName = metric.name;
|
|
296
|
+
const context = { metricAlias: alias, metricName };
|
|
297
|
+
if (!isValidMetricName(metric.name)) {
|
|
298
|
+
issues.push(issue("OBS_CONTRACT_INVALID_METRIC_NAME", `Metric "${metric.name}" is invalid. Use exactly two lowercase dot-separated parts, no reserved dimensions, and at most 128 characters.`, context));
|
|
299
|
+
}
|
|
300
|
+
const existingMetricAlias = metricAliasesByName.get(metric.name);
|
|
301
|
+
if (existingMetricAlias !== undefined) {
|
|
302
|
+
issues.push(issue("OBS_CONTRACT_DUPLICATE_METRIC_NAME", `Metric "${metric.name}" is declared by aliases "${existingMetricAlias}" and "${alias}". Give each metric one canonical name.`, context));
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
metricAliasesByName.set(metric.name, alias);
|
|
306
|
+
}
|
|
307
|
+
if (metric.kind !== "counter" &&
|
|
308
|
+
metric.kind !== "histogram" &&
|
|
309
|
+
metric.kind !== "observable_gauge") {
|
|
310
|
+
issues.push(issue("OBS_CONTRACT_INVALID_METRIC_KIND", `Metric "${metricName}" has an invalid kind. Use counter, histogram, or observable_gauge.`, context));
|
|
311
|
+
}
|
|
312
|
+
if (!isValidMetricDescription(metric.description)) {
|
|
313
|
+
issues.push(issue("OBS_CONTRACT_INVALID_METRIC_DESCRIPTION", `Metric "${metric.name}" has an invalid description. Use 1 to 1024 characters without control characters.`, context));
|
|
314
|
+
}
|
|
315
|
+
if (!isValidMetricUnit(metric.unit)) {
|
|
316
|
+
issues.push(issue("OBS_CONTRACT_INVALID_METRIC_UNIT", `Metric "${metric.name}" has an invalid unit. Use the runtime unit grammar and at most 63 characters.`, context));
|
|
317
|
+
}
|
|
318
|
+
if ((metric.kind === "histogram" &&
|
|
319
|
+
(metric.boundaries === undefined || !isValidHistogramBoundaries(metric.boundaries))) ||
|
|
320
|
+
(metric.kind !== "histogram" && metric.boundaries !== undefined)) {
|
|
321
|
+
issues.push(issue("OBS_CONTRACT_INVALID_METRIC_BOUNDARIES", `Metric "${metric.name}" has invalid boundaries. Histograms require 1 to 50 sorted finite boundaries and other kinds reject boundaries.`, context));
|
|
322
|
+
}
|
|
323
|
+
if (!isValidMetricAttributeCount(Object.keys(metric.attributes).length)) {
|
|
324
|
+
issues.push(issue("OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_DEFINITION", `Metric "${metric.name}" exceeds the runtime ceiling of 16 attributes. Remove attributes before compiling.`, context));
|
|
325
|
+
}
|
|
326
|
+
for (const [attributeName, attribute] of Object.entries(metric.attributes)) {
|
|
327
|
+
const attributeContext = { ...context, attributeName };
|
|
328
|
+
if (!isValidMetricAttributeName(attributeName)) {
|
|
329
|
+
issues.push(issue("OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_NAME", `Metric attribute "${attributeName}" is invalid. Use a dotted lowercase name no longer than 128 characters.`, attributeContext));
|
|
330
|
+
}
|
|
331
|
+
if (attribute.classification !== "public" && attribute.classification !== "internal") {
|
|
332
|
+
issues.push(issue("OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_DEFINITION", `Metric attribute "${attributeName}" has an invalid classification. Use public or internal; sensitive and forbidden attributes cannot be metric labels.`, attributeContext));
|
|
333
|
+
}
|
|
334
|
+
if (!isValidMetricCardinality(attribute.maximumCardinality)) {
|
|
335
|
+
issues.push(issue("OBS_CONTRACT_INVALID_METRIC_CARDINALITY", `Metric attribute "${attributeName}" has invalid maximumCardinality. Use an integer from 1 through 100.`, attributeContext));
|
|
336
|
+
}
|
|
337
|
+
if (!validAllowedValues(attribute.allowedValues, attribute.maximumCardinality)) {
|
|
338
|
+
issues.push(issue("OBS_CONTRACT_INVALID_METRIC_ALLOWED_VALUES", `Metric attribute "${attributeName}" has invalid allowedValues. Use a non-empty unique scalar list no larger than maximumCardinality.`, attributeContext));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
for (const [alias, action] of Object.entries(definition.auditActions)) {
|
|
343
|
+
const existingAlias = auditActionAliasesByName.get(action.action);
|
|
344
|
+
if (existingAlias !== undefined) {
|
|
345
|
+
issues.push(issue("OBS_CONTRACT_DUPLICATE_AUDIT_ACTION", `Audit action "${action.action}" is declared by aliases "${existingAlias}" and "${alias}". Give each audit action one canonical name.`, { auditActionAlias: alias, auditActionName: action.action }));
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
auditActionAliasesByName.set(action.action, alias);
|
|
349
|
+
}
|
|
350
|
+
const actionContext = { auditActionAlias: alias, auditActionName: action.action };
|
|
351
|
+
if (!isAuditAction(action.action)) {
|
|
352
|
+
issues.push(issue("OBS_CONTRACT_INVALID_AUDIT_ACTION", `Audit action alias "${alias}" has an invalid canonical action. Use 3 to 128 characters in dotted lowercase form with at least two segments.`, actionContext));
|
|
353
|
+
}
|
|
354
|
+
if (!isAuditResourceType(action.resourceType)) {
|
|
355
|
+
issues.push(issue("OBS_CONTRACT_INVALID_AUDIT_ACTION", `Audit action "${action.action}" has an invalid resourceType. Use 1 to 64 characters without control characters, starting each dot-separated segment with a lowercase letter and continuing with lowercase letters, digits, or underscores.`, actionContext));
|
|
356
|
+
}
|
|
357
|
+
if (action.allowedOutcomes.length === 0 ||
|
|
358
|
+
action.allowedOutcomes.some((outcome) => !isAuditOutcome(outcome))) {
|
|
359
|
+
issues.push(issue("OBS_CONTRACT_INVALID_AUDIT_ACTION", `Audit action "${action.action}" has invalid allowedOutcomes. Use a non-empty list containing only success, failure, cancelled, or denied.`, actionContext));
|
|
360
|
+
}
|
|
361
|
+
const reasonCodes = action.reasonCodes ?? [];
|
|
362
|
+
const seenReasonCodes = new Set();
|
|
363
|
+
for (const reasonCode of reasonCodes) {
|
|
364
|
+
if (!isAuditReasonCode(reasonCode) || seenReasonCodes.has(reasonCode)) {
|
|
365
|
+
issues.push(issue("OBS_CONTRACT_INVALID_AUDIT_REASON_CODE", `Audit action "${action.action}" has malformed or duplicate reason code "${reasonCode}". Use unique dotted lowercase codes no longer than 64 characters.`, { auditActionAlias: alias, auditActionName: action.action }));
|
|
366
|
+
}
|
|
367
|
+
seenReasonCodes.add(reasonCode);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return issues;
|
|
371
|
+
};
|
|
372
|
+
export const defineTelemetryContract = Effect.fn("defineTelemetryContract")(function* (definition) {
|
|
373
|
+
const documentIsInvalid = yield* decodeTelemetryContractDocument(definition).pipe(Effect.match({ onFailure: () => true, onSuccess: () => false }));
|
|
374
|
+
if (documentIsInvalid) {
|
|
375
|
+
return yield* new InvalidTelemetryContract({
|
|
376
|
+
code: "OBS_CONTRACT_INVALID",
|
|
377
|
+
message: "Telemetry contract has an invalid outer document. Provide version, events, metrics, and auditActions records.",
|
|
378
|
+
issues: [
|
|
379
|
+
issue("OBS_CONTRACT_INVALID_DOCUMENT", "Telemetry contract document is malformed. Provide version, events, metrics, and auditActions records."),
|
|
380
|
+
],
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
const issues = collectIssues(definition);
|
|
384
|
+
if (issues.length > 0) {
|
|
385
|
+
return yield* new InvalidTelemetryContract({
|
|
386
|
+
code: "OBS_CONTRACT_INVALID",
|
|
387
|
+
message: `Telemetry contract compilation failed with ${issues.length} issue(s). Fix every reported issue and compile again.`,
|
|
388
|
+
issues,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
const compiledDefinition = freezeTelemetryContractDefinition(structuredClone(definition));
|
|
392
|
+
const provenance = canonicalTelemetryContractProvenance(compiledDefinition);
|
|
393
|
+
const eventNames = [];
|
|
394
|
+
const mutableEventByAlias = new Map();
|
|
395
|
+
const mutableEventByName = new Map();
|
|
396
|
+
for (const [alias, event] of Object.entries(compiledDefinition.events)) {
|
|
397
|
+
const name = EventName.make(event.name);
|
|
398
|
+
const compiled = Object.freeze({
|
|
399
|
+
alias,
|
|
400
|
+
name,
|
|
401
|
+
kind: event.kind,
|
|
402
|
+
defaultSeverity: event.defaultSeverity,
|
|
403
|
+
mandatory: event.mandatory,
|
|
404
|
+
sampling: event.sampling,
|
|
405
|
+
attributes: immutableMap(Object.entries(event.attributes)),
|
|
406
|
+
requiredAttributes: Object.freeze(Object.entries(event.attributes)
|
|
407
|
+
.filter((entry) => entry[1].required)
|
|
408
|
+
.map((entry) => entry[0])),
|
|
409
|
+
});
|
|
410
|
+
eventNames.push(name);
|
|
411
|
+
mutableEventByAlias.set(alias, compiled);
|
|
412
|
+
mutableEventByName.set(name, compiled);
|
|
413
|
+
}
|
|
414
|
+
const mutableAuditActionByAlias = new Map();
|
|
415
|
+
const mutableAuditActionByName = new Map();
|
|
416
|
+
for (const [alias, action] of Object.entries(compiledDefinition.auditActions)) {
|
|
417
|
+
const compiled = Object.freeze({
|
|
418
|
+
alias,
|
|
419
|
+
action: AuditAction.make(action.action),
|
|
420
|
+
resourceType: action.resourceType,
|
|
421
|
+
allowedOutcomes: action.allowedOutcomes,
|
|
422
|
+
reasonCodes: action.reasonCodes ?? Object.freeze([]),
|
|
423
|
+
});
|
|
424
|
+
mutableAuditActionByAlias.set(alias, compiled);
|
|
425
|
+
mutableAuditActionByName.set(action.action, compiled);
|
|
426
|
+
}
|
|
427
|
+
const mutableMetricByAlias = new Map();
|
|
428
|
+
const mutableMetricByName = new Map();
|
|
429
|
+
for (const [alias, metric] of Object.entries(compiledDefinition.metrics)) {
|
|
430
|
+
const compiled = Object.freeze({
|
|
431
|
+
...metric,
|
|
432
|
+
alias,
|
|
433
|
+
attributes: immutableMap(Object.entries(metric.attributes)),
|
|
434
|
+
});
|
|
435
|
+
mutableMetricByAlias.set(alias, compiled);
|
|
436
|
+
mutableMetricByName.set(metric.name, compiled);
|
|
437
|
+
}
|
|
438
|
+
return Object.freeze({
|
|
439
|
+
version: compiledDefinition.version,
|
|
440
|
+
definition: compiledDefinition,
|
|
441
|
+
provenance,
|
|
442
|
+
eventNames: Object.freeze(eventNames),
|
|
443
|
+
eventByAlias: immutableMap(mutableEventByAlias),
|
|
444
|
+
eventByName: immutableMap(mutableEventByName),
|
|
445
|
+
auditActionByAlias: immutableMap(mutableAuditActionByAlias),
|
|
446
|
+
auditActionByName: immutableMap(mutableAuditActionByName),
|
|
447
|
+
metrics: compiledDefinition.metrics,
|
|
448
|
+
metricByAlias: immutableMap(mutableMetricByAlias),
|
|
449
|
+
metricByName: immutableMap(mutableMetricByName),
|
|
450
|
+
});
|
|
451
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export declare const ContractIssueCode: Schema.Literals<readonly ["OBS_CONTRACT_INVALID_DOCUMENT", "OBS_CONTRACT_INVALID_VERSION", "OBS_CONTRACT_INVALID_EVENT_NAME", "OBS_CONTRACT_DUPLICATE_EVENT_NAME", "OBS_CONTRACT_INVALID_EVENT_KIND", "OBS_CONTRACT_INVALID_DEFAULT_SEVERITY", "OBS_CONTRACT_INVALID_ATTRIBUTE_NAME", "OBS_CONTRACT_RESERVED_ATTRIBUTE_NAME", "OBS_CONTRACT_INVALID_ATTRIBUTE_DEFINITION", "OBS_CONTRACT_INVALID_SAMPLING_RATE", "OBS_CONTRACT_INVALID_AUDIT_ACTION", "OBS_CONTRACT_DUPLICATE_AUDIT_ACTION", "OBS_CONTRACT_INVALID_AUDIT_REASON_CODE", "OBS_CONTRACT_INVALID_METRIC_NAME", "OBS_CONTRACT_DUPLICATE_METRIC_NAME", "OBS_CONTRACT_INVALID_METRIC_KIND", "OBS_CONTRACT_INVALID_METRIC_UNIT", "OBS_CONTRACT_INVALID_METRIC_DESCRIPTION", "OBS_CONTRACT_INVALID_METRIC_BOUNDARIES", "OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_NAME", "OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_DEFINITION", "OBS_CONTRACT_INVALID_METRIC_ALLOWED_VALUES", "OBS_CONTRACT_INVALID_METRIC_CARDINALITY"]>;
|
|
3
|
+
export type ContractIssueCode = typeof ContractIssueCode.Type;
|
|
4
|
+
export declare const ContractIssue: Schema.Struct<{
|
|
5
|
+
readonly code: Schema.Literals<readonly ["OBS_CONTRACT_INVALID_DOCUMENT", "OBS_CONTRACT_INVALID_VERSION", "OBS_CONTRACT_INVALID_EVENT_NAME", "OBS_CONTRACT_DUPLICATE_EVENT_NAME", "OBS_CONTRACT_INVALID_EVENT_KIND", "OBS_CONTRACT_INVALID_DEFAULT_SEVERITY", "OBS_CONTRACT_INVALID_ATTRIBUTE_NAME", "OBS_CONTRACT_RESERVED_ATTRIBUTE_NAME", "OBS_CONTRACT_INVALID_ATTRIBUTE_DEFINITION", "OBS_CONTRACT_INVALID_SAMPLING_RATE", "OBS_CONTRACT_INVALID_AUDIT_ACTION", "OBS_CONTRACT_DUPLICATE_AUDIT_ACTION", "OBS_CONTRACT_INVALID_AUDIT_REASON_CODE", "OBS_CONTRACT_INVALID_METRIC_NAME", "OBS_CONTRACT_DUPLICATE_METRIC_NAME", "OBS_CONTRACT_INVALID_METRIC_KIND", "OBS_CONTRACT_INVALID_METRIC_UNIT", "OBS_CONTRACT_INVALID_METRIC_DESCRIPTION", "OBS_CONTRACT_INVALID_METRIC_BOUNDARIES", "OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_NAME", "OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_DEFINITION", "OBS_CONTRACT_INVALID_METRIC_ALLOWED_VALUES", "OBS_CONTRACT_INVALID_METRIC_CARDINALITY"]>;
|
|
6
|
+
readonly message: Schema.String;
|
|
7
|
+
readonly eventAlias: Schema.optionalKey<Schema.String>;
|
|
8
|
+
readonly eventName: Schema.optionalKey<Schema.String>;
|
|
9
|
+
readonly attributeName: Schema.optionalKey<Schema.String>;
|
|
10
|
+
readonly auditActionAlias: Schema.optionalKey<Schema.String>;
|
|
11
|
+
readonly auditActionName: Schema.optionalKey<Schema.String>;
|
|
12
|
+
readonly metricAlias: Schema.optionalKey<Schema.String>;
|
|
13
|
+
readonly metricName: Schema.optionalKey<Schema.String>;
|
|
14
|
+
}>;
|
|
15
|
+
export type ContractIssue = typeof ContractIssue.Type;
|
|
16
|
+
declare const InvalidTelemetryContract_base: Schema.Class<InvalidTelemetryContract, Schema.TaggedStruct<"InvalidTelemetryContract", {
|
|
17
|
+
readonly code: Schema.Literal<"OBS_CONTRACT_INVALID">;
|
|
18
|
+
readonly message: Schema.String;
|
|
19
|
+
readonly issues: Schema.$Array<Schema.Struct<{
|
|
20
|
+
readonly code: Schema.Literals<readonly ["OBS_CONTRACT_INVALID_DOCUMENT", "OBS_CONTRACT_INVALID_VERSION", "OBS_CONTRACT_INVALID_EVENT_NAME", "OBS_CONTRACT_DUPLICATE_EVENT_NAME", "OBS_CONTRACT_INVALID_EVENT_KIND", "OBS_CONTRACT_INVALID_DEFAULT_SEVERITY", "OBS_CONTRACT_INVALID_ATTRIBUTE_NAME", "OBS_CONTRACT_RESERVED_ATTRIBUTE_NAME", "OBS_CONTRACT_INVALID_ATTRIBUTE_DEFINITION", "OBS_CONTRACT_INVALID_SAMPLING_RATE", "OBS_CONTRACT_INVALID_AUDIT_ACTION", "OBS_CONTRACT_DUPLICATE_AUDIT_ACTION", "OBS_CONTRACT_INVALID_AUDIT_REASON_CODE", "OBS_CONTRACT_INVALID_METRIC_NAME", "OBS_CONTRACT_DUPLICATE_METRIC_NAME", "OBS_CONTRACT_INVALID_METRIC_KIND", "OBS_CONTRACT_INVALID_METRIC_UNIT", "OBS_CONTRACT_INVALID_METRIC_DESCRIPTION", "OBS_CONTRACT_INVALID_METRIC_BOUNDARIES", "OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_NAME", "OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_DEFINITION", "OBS_CONTRACT_INVALID_METRIC_ALLOWED_VALUES", "OBS_CONTRACT_INVALID_METRIC_CARDINALITY"]>;
|
|
21
|
+
readonly message: Schema.String;
|
|
22
|
+
readonly eventAlias: Schema.optionalKey<Schema.String>;
|
|
23
|
+
readonly eventName: Schema.optionalKey<Schema.String>;
|
|
24
|
+
readonly attributeName: Schema.optionalKey<Schema.String>;
|
|
25
|
+
readonly auditActionAlias: Schema.optionalKey<Schema.String>;
|
|
26
|
+
readonly auditActionName: Schema.optionalKey<Schema.String>;
|
|
27
|
+
readonly metricAlias: Schema.optionalKey<Schema.String>;
|
|
28
|
+
readonly metricName: Schema.optionalKey<Schema.String>;
|
|
29
|
+
}>>;
|
|
30
|
+
}>, import("effect/Cause").YieldableError>;
|
|
31
|
+
export declare class InvalidTelemetryContract extends InvalidTelemetryContract_base {
|
|
32
|
+
}
|
|
33
|
+
export declare const TelemetryEventErrorCode: Schema.Literals<readonly ["OBS_EVENT_UNKNOWN_NAME", "OBS_EVENT_UNDECLARED_ATTRIBUTE", "OBS_EVENT_MISSING_ATTRIBUTE", "OBS_EVENT_INVALID_FIELD", "OBS_EVENT_INVALID_OUTCOME", "OBS_EVENT_RESTRICTED_ATTRIBUTE", "OBS_EVENT_SENSITIVE_METRIC_LABEL", "OBS_EVENT_UNKNOWN_AUDIT_ACTION", "OBS_EVENT_INVALID_AUDIT_RESOURCE", "OBS_EVENT_INVALID_AUDIT_OUTCOME", "OBS_EVENT_INVALID_AUDIT_REASON"]>;
|
|
34
|
+
export type TelemetryEventErrorCode = typeof TelemetryEventErrorCode.Type;
|
|
35
|
+
declare const InvalidTelemetryEvent_base: Schema.Class<InvalidTelemetryEvent, Schema.TaggedStruct<"InvalidTelemetryEvent", {
|
|
36
|
+
readonly code: Schema.Literals<readonly ["OBS_EVENT_UNKNOWN_NAME", "OBS_EVENT_UNDECLARED_ATTRIBUTE", "OBS_EVENT_MISSING_ATTRIBUTE", "OBS_EVENT_INVALID_FIELD", "OBS_EVENT_INVALID_OUTCOME", "OBS_EVENT_RESTRICTED_ATTRIBUTE", "OBS_EVENT_SENSITIVE_METRIC_LABEL", "OBS_EVENT_UNKNOWN_AUDIT_ACTION", "OBS_EVENT_INVALID_AUDIT_RESOURCE", "OBS_EVENT_INVALID_AUDIT_OUTCOME", "OBS_EVENT_INVALID_AUDIT_REASON"]>;
|
|
37
|
+
readonly message: Schema.String;
|
|
38
|
+
readonly eventName: Schema.optionalKey<Schema.String>;
|
|
39
|
+
readonly eventAlias: Schema.optionalKey<Schema.String>;
|
|
40
|
+
readonly attributeName: Schema.optionalKey<Schema.String>;
|
|
41
|
+
}>, import("effect/Cause").YieldableError>;
|
|
42
|
+
export declare class InvalidTelemetryEvent extends InvalidTelemetryEvent_base {
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export const ContractIssueCode = Schema.Literals([
|
|
3
|
+
"OBS_CONTRACT_INVALID_DOCUMENT",
|
|
4
|
+
"OBS_CONTRACT_INVALID_VERSION",
|
|
5
|
+
"OBS_CONTRACT_INVALID_EVENT_NAME",
|
|
6
|
+
"OBS_CONTRACT_DUPLICATE_EVENT_NAME",
|
|
7
|
+
"OBS_CONTRACT_INVALID_EVENT_KIND",
|
|
8
|
+
"OBS_CONTRACT_INVALID_DEFAULT_SEVERITY",
|
|
9
|
+
"OBS_CONTRACT_INVALID_ATTRIBUTE_NAME",
|
|
10
|
+
"OBS_CONTRACT_RESERVED_ATTRIBUTE_NAME",
|
|
11
|
+
"OBS_CONTRACT_INVALID_ATTRIBUTE_DEFINITION",
|
|
12
|
+
"OBS_CONTRACT_INVALID_SAMPLING_RATE",
|
|
13
|
+
"OBS_CONTRACT_INVALID_AUDIT_ACTION",
|
|
14
|
+
"OBS_CONTRACT_DUPLICATE_AUDIT_ACTION",
|
|
15
|
+
"OBS_CONTRACT_INVALID_AUDIT_REASON_CODE",
|
|
16
|
+
"OBS_CONTRACT_INVALID_METRIC_NAME",
|
|
17
|
+
"OBS_CONTRACT_DUPLICATE_METRIC_NAME",
|
|
18
|
+
"OBS_CONTRACT_INVALID_METRIC_KIND",
|
|
19
|
+
"OBS_CONTRACT_INVALID_METRIC_UNIT",
|
|
20
|
+
"OBS_CONTRACT_INVALID_METRIC_DESCRIPTION",
|
|
21
|
+
"OBS_CONTRACT_INVALID_METRIC_BOUNDARIES",
|
|
22
|
+
"OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_NAME",
|
|
23
|
+
"OBS_CONTRACT_INVALID_METRIC_ATTRIBUTE_DEFINITION",
|
|
24
|
+
"OBS_CONTRACT_INVALID_METRIC_ALLOWED_VALUES",
|
|
25
|
+
"OBS_CONTRACT_INVALID_METRIC_CARDINALITY",
|
|
26
|
+
]);
|
|
27
|
+
export const ContractIssue = Schema.Struct({
|
|
28
|
+
code: ContractIssueCode,
|
|
29
|
+
message: Schema.String,
|
|
30
|
+
eventAlias: Schema.String.pipe(Schema.optionalKey),
|
|
31
|
+
eventName: Schema.String.pipe(Schema.optionalKey),
|
|
32
|
+
attributeName: Schema.String.pipe(Schema.optionalKey),
|
|
33
|
+
auditActionAlias: Schema.String.pipe(Schema.optionalKey),
|
|
34
|
+
auditActionName: Schema.String.pipe(Schema.optionalKey),
|
|
35
|
+
metricAlias: Schema.String.pipe(Schema.optionalKey),
|
|
36
|
+
metricName: Schema.String.pipe(Schema.optionalKey),
|
|
37
|
+
});
|
|
38
|
+
export class InvalidTelemetryContract extends Schema.TaggedError()("InvalidTelemetryContract", {
|
|
39
|
+
code: Schema.Literal("OBS_CONTRACT_INVALID"),
|
|
40
|
+
message: Schema.String,
|
|
41
|
+
issues: Schema.Array(ContractIssue),
|
|
42
|
+
}) {
|
|
43
|
+
}
|
|
44
|
+
export const TelemetryEventErrorCode = Schema.Literals([
|
|
45
|
+
"OBS_EVENT_UNKNOWN_NAME",
|
|
46
|
+
"OBS_EVENT_UNDECLARED_ATTRIBUTE",
|
|
47
|
+
"OBS_EVENT_MISSING_ATTRIBUTE",
|
|
48
|
+
"OBS_EVENT_INVALID_FIELD",
|
|
49
|
+
"OBS_EVENT_INVALID_OUTCOME",
|
|
50
|
+
"OBS_EVENT_RESTRICTED_ATTRIBUTE",
|
|
51
|
+
"OBS_EVENT_SENSITIVE_METRIC_LABEL",
|
|
52
|
+
"OBS_EVENT_UNKNOWN_AUDIT_ACTION",
|
|
53
|
+
"OBS_EVENT_INVALID_AUDIT_RESOURCE",
|
|
54
|
+
"OBS_EVENT_INVALID_AUDIT_OUTCOME",
|
|
55
|
+
"OBS_EVENT_INVALID_AUDIT_REASON",
|
|
56
|
+
]);
|
|
57
|
+
export class InvalidTelemetryEvent extends Schema.TaggedError()("InvalidTelemetryEvent", {
|
|
58
|
+
code: TelemetryEventErrorCode,
|
|
59
|
+
message: Schema.String,
|
|
60
|
+
eventName: Schema.String.pipe(Schema.optionalKey),
|
|
61
|
+
eventAlias: Schema.String.pipe(Schema.optionalKey),
|
|
62
|
+
attributeName: Schema.String.pipe(Schema.optionalKey),
|
|
63
|
+
}) {
|
|
64
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { AuditContext, AuditOutcome } from "../audit/AuditRecord.js";
|
|
3
|
+
import type { CorrelationContext } from "../Correlation.js";
|
|
4
|
+
import type { EventName } from "./EventName.js";
|
|
5
|
+
export declare const EventSeverity: Schema.Literals<readonly ["debug", "info", "warn", "error", "fatal"]>;
|
|
6
|
+
export type EventSeverity = typeof EventSeverity.Type;
|
|
7
|
+
export declare const EventOutcome: Schema.Literals<readonly ["success", "failure", "cancelled"]>;
|
|
8
|
+
export type EventOutcome = typeof EventOutcome.Type;
|
|
9
|
+
export declare const EventKind: Schema.Literals<readonly ["request", "operation", "domain", "defect", "audit"]>;
|
|
10
|
+
export type EventKind = typeof EventKind.Type;
|
|
11
|
+
export type AttributeValue = string | number | boolean;
|
|
12
|
+
export type EventAttributes = {
|
|
13
|
+
readonly [attributeName: string]: AttributeValue;
|
|
14
|
+
};
|
|
15
|
+
export declare const HttpContext: Schema.Struct<{
|
|
16
|
+
readonly method: Schema.NonEmptyString;
|
|
17
|
+
readonly route: Schema.NonEmptyString;
|
|
18
|
+
readonly statusCode: Schema.Int;
|
|
19
|
+
}>;
|
|
20
|
+
export type HttpContext = typeof HttpContext.Type;
|
|
21
|
+
export declare const ErrorContext: Schema.Struct<{
|
|
22
|
+
readonly type: Schema.NonEmptyString;
|
|
23
|
+
readonly message: Schema.String;
|
|
24
|
+
readonly retryable: Schema.Boolean;
|
|
25
|
+
}>;
|
|
26
|
+
export type ErrorContext = typeof ErrorContext.Type;
|
|
27
|
+
export declare const maxOtlpUnixTimestampMillis = 18446744073709;
|
|
28
|
+
export declare const EventTimestamp: Schema.brand<Schema.String, "EventTimestamp">;
|
|
29
|
+
export type EventTimestamp = typeof EventTimestamp.Type;
|
|
30
|
+
export declare const EventDuration: Schema.brand<Schema.Finite, "EventDuration">;
|
|
31
|
+
export type EventDuration = typeof EventDuration.Type;
|
|
32
|
+
export type EventBase = {
|
|
33
|
+
readonly timestamp: EventTimestamp;
|
|
34
|
+
readonly name: EventName;
|
|
35
|
+
readonly severity: EventSeverity;
|
|
36
|
+
readonly correlation: CorrelationContext;
|
|
37
|
+
readonly attributes: EventAttributes;
|
|
38
|
+
};
|
|
39
|
+
export type TelemetryEvent = (EventBase & {
|
|
40
|
+
readonly kind: "request";
|
|
41
|
+
readonly outcome: EventOutcome;
|
|
42
|
+
readonly durationMs: EventDuration;
|
|
43
|
+
readonly http: HttpContext;
|
|
44
|
+
}) | (EventBase & {
|
|
45
|
+
readonly kind: "operation";
|
|
46
|
+
readonly outcome: EventOutcome;
|
|
47
|
+
readonly durationMs: EventDuration;
|
|
48
|
+
}) | (EventBase & {
|
|
49
|
+
readonly kind: "domain";
|
|
50
|
+
readonly outcome: EventOutcome;
|
|
51
|
+
}) | (EventBase & {
|
|
52
|
+
readonly kind: "defect";
|
|
53
|
+
readonly outcome: "failure";
|
|
54
|
+
readonly error: ErrorContext;
|
|
55
|
+
}) | (EventBase & {
|
|
56
|
+
readonly kind: "audit";
|
|
57
|
+
readonly outcome: AuditOutcome;
|
|
58
|
+
readonly audit: AuditContext;
|
|
59
|
+
});
|