1/**
2 * Defines the experimental wire schema used by the Effect devtools protocol.
3 *
4 * The module contains the serialized message shapes exchanged between devtools
5 * clients and servers: span snapshots, span events, metric snapshots,
6 * heartbeats, and request/response unions. Use these schemas at protocol
7 * boundaries to encode, decode, or validate custom transports that need to
8 * interoperate with the built-in unstable devtools client and server.
9 *
10 * @stability unstable
11 * @since 4.0.0
12 */
13import * as Exit from "../Exit.ts"
14import { identity } from "../Function.ts"
15import type * as Option from "../Option.ts"
16import * as Schema from "../Schema.ts"
17import * as SchemaTransformation from "../SchemaTransformation.ts"
18
19/**
20 * Schema for a span status representing a span that has started but not yet
21 * ended.
22 *
23 * @stability unstable
24 * @category schemas
25 * @since 4.0.0
26 */
27export const SpanStatusStarted = Schema.Struct({
28 _tag: Schema.tag("Started"),
29 startTime: Schema.BigInt
30})
31
32/**
33 * Type of a span status representing a span that has started but not yet ended.
34 *
35 * @stability unstable
36 * @category schemas
37 * @since 4.0.0
38 */
39export type SpanStatusStarted = Schema.Schema.Type<typeof SpanStatusStarted>
40
41/**
42 * Schema for a span status representing an ended span, including start time,
43 * end time, and encoded exit status. Encoding drops success values with
44 * `Exit.asVoid`.
45 *
46 * @stability unstable
47 * @category schemas
48 * @since 4.0.0
49 */
50export const SpanStatusEnded = Schema.Struct({
51 _tag: Schema.tag("Ended"),
52 startTime: Schema.BigInt,
53 endTime: Schema.BigInt,
54 exit: Schema.Exit(Schema.Void, Schema.Defect({ includeStack: true }), Schema.Defect({ includeStack: true })).pipe(
55 Schema.decodeTo(
56 Schema.Exit(Schema.Unknown, Schema.Unknown, Schema.Unknown),
57 SchemaTransformation.transform({
58 decode: identity,
59 encode: Exit.asVoid
60 })
61 )
62 )
63})
64
65/**
66 * Type of a span status representing an ended span with start time, end time,
67 * and exit status.
68 *
69 * @stability unstable
70 * @category schemas
71 * @since 4.0.0
72 */
73export type SpanStatusEnded = Schema.Schema.Type<typeof SpanStatusEnded>
74
75/**
76 * Schema for devtools span status, either started or ended.
77 *
78 * @stability unstable
79 * @category schemas
80 * @since 4.0.0
81 */
82export const SpanStatus = Schema.Union([SpanStatusStarted, SpanStatusEnded])
83
84/**
85 * Type of a devtools span status, either started or ended.
86 *
87 * @stability unstable
88 * @category schemas
89 * @since 4.0.0
90 */
91export type SpanStatus = Schema.Schema.Type<typeof SpanStatus>
92
93/**
94 * Serialized parent span context for a span created outside the current
95 * devtools span tree.
96 *
97 * @stability unstable
98 * @category schemas
99 * @since 4.0.0
100 */
101export interface ExternalSpan {
102 readonly _tag: "ExternalSpan"
103 readonly spanId: string
104 readonly traceId: string
105 readonly sampled: boolean
106}
107
108/**
109 * Schema for an external parent span context containing span id, trace id, and
110 * sampling flag.
111 *
112 * @stability unstable
113 * @category schemas
114 * @since 4.0.0
115 */
116export const ExternalSpan: Schema.Codec<ExternalSpan> = Schema.Struct({
117 _tag: Schema.tag("ExternalSpan"),
118 spanId: Schema.String,
119 traceId: Schema.String,
120 sampled: Schema.Boolean
121})
122
123/**
124 * Telemetry payload for an Effect span sent to devtools, including identity,
125 * attributes, status, sampling flag, and optional parent span.
126 *
127 * @stability unstable
128 * @category schemas
129 * @since 4.0.0
130 */
131export interface Span {
132 readonly _tag: "Span"
133 readonly spanId: string
134 readonly traceId: string
135 readonly name: string
136 readonly sampled: boolean
137 readonly attributes: ReadonlyMap<string, unknown>
138 readonly status: SpanStatus
139 readonly parent: Option.Option<ParentSpan>
140}
141
142/**
143 * Schema for an Effect span telemetry payload sent to devtools.
144 *
145 * @stability unstable
146 * @category schemas
147 * @since 4.0.0
148 */
149export const Span: Schema.Codec<Span> = Schema.Struct({
150 _tag: Schema.tag("Span"),
151 spanId: Schema.String,
152 traceId: Schema.String,
153 name: Schema.String,
154 sampled: Schema.Boolean,
155 attributes: Schema.ReadonlyMap(Schema.String, Schema.Any),
156 status: SpanStatus,
157 parent: Schema.Option(Schema.suspend(() => ParentSpan))
158})
159
160/**
161 * Schema for a named event emitted by a span, including trace id, span id,
162 * start time, and optional attributes.
163 *
164 * @stability unstable
165 * @category schemas
166 * @since 4.0.0
167 */
168export const SpanEvent = Schema.Struct({
169 _tag: Schema.tag("SpanEvent"),
170 traceId: Schema.String,
171 spanId: Schema.String,
172 name: Schema.String,
173 startTime: Schema.BigInt,
174 attributes: Schema.UndefinedOr(Schema.Record(Schema.String, Schema.Any))
175})
176
177/**
178 * Type of a named event emitted by a span and sent to devtools.
179 *
180 * @stability unstable
181 * @category schemas
182 * @since 4.0.0
183 */
184export type SpanEvent = Schema.Schema.Type<typeof SpanEvent>
185
186/**
187 * Type of a span parent, represented either by a devtools `Span` payload or an
188 * `ExternalSpan` context.
189 *
190 * @stability unstable
191 * @category schemas
192 * @since 4.0.0
193 */
194export type ParentSpan = Span | ExternalSpan
195
196/**
197 * Schema for a span parent, either a full devtools `Span` payload or an
198 * `ExternalSpan` context.
199 *
200 * @stability unstable
201 * @category schemas
202 * @since 4.0.0
203 */
204export const ParentSpan = Schema.Union([Span, ExternalSpan])
205
206/**
207 * Schema for the devtools heartbeat request sent by the client.
208 *
209 * @stability unstable
210 * @category schemas
211 * @since 4.0.0
212 */
213export const Ping = Schema.Struct({
214 _tag: Schema.tag("Ping")
215})
216
217/**
218 * Type of the devtools heartbeat request sent by the client.
219 *
220 * @stability unstable
221 * @category schemas
222 * @since 4.0.0
223 */
224export type Ping = Schema.Schema.Type<typeof Ping>
225
226/**
227 * Schema for the devtools heartbeat response.
228 *
229 * @stability unstable
230 * @category schemas
231 * @since 4.0.0
232 */
233export const Pong = Schema.Struct({
234 _tag: Schema.tag("Pong")
235})
236
237/**
238 * Type of the devtools heartbeat response.
239 *
240 * @stability unstable
241 * @category schemas
242 * @since 4.0.0
243 */
244export type Pong = Schema.Schema.Type<typeof Pong>
245
246/**
247 * Schema for a devtools request asking the client to send a metrics snapshot.
248 *
249 * @stability unstable
250 * @category schemas
251 * @since 4.0.0
252 */
253export const MetricsRequest = Schema.Struct({
254 _tag: Schema.tag("MetricsRequest")
255})
256
257/**
258 * Type of a devtools request asking the client to send a metrics snapshot.
259 *
260 * @stability unstable
261 * @category schemas
262 * @since 4.0.0
263 */
264export type MetricsRequest = Schema.Schema.Type<typeof MetricsRequest>
265
266/**
267 * Schema for a metric label key/value pair in a devtools metrics snapshot.
268 *
269 * @stability unstable
270 * @category schemas
271 * @since 4.0.0
272 */
273export const MetricLabel = Schema.Struct({
274 key: Schema.String,
275 value: Schema.String
276})
277
278/**
279 * Type of a metric label key/value pair in a devtools metrics snapshot.
280 *
281 * @stability unstable
282 * @category schemas
283 * @since 4.0.0
284 */
285export type MetricLabel = Schema.Schema.Type<typeof MetricLabel>
286
287const metric = <Type extends string, State extends Schema.Constraint>(type: Type, state: State) =>
288 Schema.Struct({
289 id: Schema.String,
290 type: Schema.tag(type),
291 description: Schema.UndefinedOr(Schema.String),
292 attributes: Schema.UndefinedOr(Schema.Record(Schema.String, Schema.String)),
293 state
294 })
295
296/**
297 * Schema for a counter metric snapshot, including the count and whether updates
298 * are incremental.
299 *
300 * @stability unstable
301 * @category schemas
302 * @since 4.0.0
303 */
304export const Counter = metric(
305 "Counter",
306 Schema.Struct({
307 count: Schema.Union([Schema.Number, Schema.BigInt]),
308 incremental: Schema.Boolean
309 })
310)
311
312/**
313 * Type of a devtools counter metric snapshot.
314 *
315 * **Details**
316 *
317 * The state contains the current count and whether the counter reports
318 * incremental updates.
319 *
320 * @stability unstable
321 * @category schemas
322 * @since 4.0.0
323 */
324export type Counter = Schema.Schema.Type<typeof Counter>
325
326/**
327 * Schema for a devtools frequency metric snapshot.
328 *
329 * **Details**
330 *
331 * The metric state records occurrence counts by string key.
332 *
333 * @stability unstable
334 * @category schemas
335 * @since 4.0.0
336 */
337export const Frequency = metric(
338 "Frequency",
339 Schema.Struct({
340 occurrences: Schema.ReadonlyMap(Schema.String, Schema.Natural)
341 })
342)
343
344/**
345 * Type of a devtools frequency metric snapshot.
346 *
347 * **Details**
348 *
349 * The state maps observed string values to occurrence counts.
350 *
351 * @stability unstable
352 * @category schemas
353 * @since 4.0.0
354 */
355export type Frequency = Schema.Schema.Type<typeof Frequency>
356
357/**
358 * Schema for a devtools gauge metric snapshot.
359 *
360 * **Details**
361 *
362 * The metric state contains the current numeric or bigint value.
363 *
364 * @stability unstable
365 * @category schemas
366 * @since 4.0.0
367 */
368export const Gauge = metric(
369 "Gauge",
370 Schema.Struct({
371 value: Schema.Union([Schema.Number, Schema.BigInt])
372 })
373)
374
375/**
376 * Type of a devtools gauge metric snapshot.
377 *
378 * **Details**
379 *
380 * The state contains the current numeric or bigint value.
381 *
382 * @stability unstable
383 * @category schemas
384 * @since 4.0.0
385 */
386export type Gauge = Schema.Schema.Type<typeof Gauge>
387
388/**
389 * Schema for a devtools histogram metric snapshot.
390 *
391 * **Details**
392 *
393 * The metric state includes bucket counts plus the total count, minimum,
394 * maximum, and sum.
395 *
396 * @stability unstable
397 * @category schemas
398 * @since 4.0.0
399 */
400export const Histogram = metric(
401 "Histogram",
402 Schema.Struct({
403 buckets: Schema.Array(Schema.Tuple([Schema.Number, Schema.Natural])),
404 count: Schema.Natural,
405 min: Schema.Number,
406 max: Schema.Number,
407 sum: Schema.Number
408 })
409)
410
411/**
412 * Type of a devtools histogram metric snapshot.
413 *
414 * **Details**
415 *
416 * The state includes bucket counts plus the total count, minimum, maximum, and
417 * sum.
418 *
419 * @stability unstable
420 * @category schemas
421 * @since 4.0.0
422 */
423export type Histogram = Schema.Schema.Type<typeof Histogram>
424
425/**
426 * Schema for a devtools summary metric snapshot.
427 *
428 * **Details**
429 *
430 * The metric state contains quantile values plus the total count, minimum,
431 * maximum, and sum.
432 *
433 * @stability unstable
434 * @category schemas
435 * @since 4.0.0
436 */
437export const Summary = metric(
438 "Summary",
439 Schema.Struct({
440 quantiles: Schema.Array(
441 Schema.Tuple([
442 Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 1 })),
443 Schema.UndefinedOr(Schema.Number)
444 ])
445 ),
446 count: Schema.Natural,
447 min: Schema.Number,
448 max: Schema.Number,
449 sum: Schema.Number
450 })
451)
452
453/**
454 * Type of a devtools summary metric snapshot.
455 *
456 * **Details**
457 *
458 * The state contains quantile values plus the total count, minimum, maximum,
459 * and sum.
460 *
461 * @stability unstable
462 * @category schemas
463 * @since 4.0.0
464 */
465export type Summary = Schema.Schema.Type<typeof Summary>
466
467/**
468 * Schema for any devtools metric snapshot.
469 *
470 * **Details**
471 *
472 * Accepted metric kinds are counters, frequencies, gauges, histograms, and
473 * summaries.
474 *
475 * @stability unstable
476 * @category schemas
477 * @since 4.0.0
478 */
479export const Metric = Schema.Union([Counter, Frequency, Gauge, Histogram, Summary])
480
481/**
482 * Type of any devtools metric snapshot.
483 *
484 * **Details**
485 *
486 * The union covers counters, frequencies, gauges, histograms, and summaries.
487 *
488 * @stability unstable
489 * @category schemas
490 * @since 4.0.0
491 */
492export type Metric = Schema.Schema.Type<typeof Metric>
493
494/**
495 * Schema for a devtools protocol message containing the current metric
496 * snapshots.
497 *
498 * @stability unstable
499 * @category schemas
500 * @since 4.0.0
501 */
502export const MetricsSnapshot = Schema.Struct({
503 _tag: Schema.tag("MetricsSnapshot"),
504 metrics: Schema.Array(Metric)
505})
506
507/**
508 * Type of a devtools protocol message containing the current metric snapshots.
509 *
510 * @stability unstable
511 * @category schemas
512 * @since 4.0.0
513 */
514export type MetricsSnapshot = Schema.Schema.Type<typeof MetricsSnapshot>
515
516/**
517 * Schema for devtools protocol requests accepted by the server.
518 *
519 * **Details**
520 *
521 * Requests include heartbeat pings, spans, span events, and metric snapshots.
522 *
523 * @stability unstable
524 * @category schemas
525 * @since 4.0.0
526 */
527export const Request = Schema.Union([Ping, Span, SpanEvent, MetricsSnapshot])
528
529/**
530 * Type of devtools protocol requests accepted by the server.
531 *
532 * **Details**
533 *
534 * Requests include heartbeat pings, spans, span events, and metric snapshots.
535 *
536 * @stability unstable
537 * @category schemas
538 * @since 4.0.0
539 */
540export type Request = Schema.Schema.Type<typeof Request>
541
542/**
543 * Namespace containing helper types for devtools protocol requests.
544 *
545 * @stability unstable
546 * @since 4.0.0
547 */
548export declare namespace Request {
549 /**
550 * Devtools request messages excluding heartbeat pings.
551 *
552 * **Details**
553 *
554 * `DevToolsServer` handles `Ping` internally and exposes only these requests
555 * to client handlers.
556 *
557 * @stability unstable
558 * @since 4.0.0
559 */
560 export type WithoutPing = Exclude<Request, { readonly _tag: "Ping" }>
561}
562
563/**
564 * Schema for devtools protocol responses sent by the server.
565 *
566 * **Details**
567 *
568 * Responses include heartbeat pongs and requests for metric snapshots.
569 *
570 * @stability unstable
571 * @category schemas
572 * @since 4.0.0
573 */
574export const Response = Schema.Union([Pong, MetricsRequest])
575
576/**
577 * Type of devtools protocol responses sent by the server.
578 *
579 * **Details**
580 *
581 * Responses include heartbeat pongs and requests for metric snapshots.
582 *
583 * @stability unstable
584 * @category schemas
585 * @since 4.0.0
586 */
587export type Response = Schema.Schema.Type<typeof Response>
588
589/**
590 * Namespace containing helper types for devtools protocol responses.
591 *
592 * @stability unstable
593 * @since 4.0.0
594 */
595export declare namespace Response {
596 /**
597 * Devtools response messages excluding heartbeat pongs.
598 *
599 * **Details**
600 *
601 * `DevToolsServer` sends `Pong` internally and accepts only these responses
602 * from client handlers.
603 *
604 * @stability unstable
605 * @since 4.0.0
606 */
607 export type WithoutPong = Exclude<Response, { readonly _tag: "Pong" }>
608}