@systemfsoftware/trace-spec 1.0.1 → 1.2.4
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/CHANGELOG.md +2 -120
- package/dist/index.d.ts +66 -58
- package/dist/index.mjs +291 -291
- package/package.json +24 -26
package/CHANGELOG.md
CHANGED
|
@@ -1,123 +1,5 @@
|
|
|
1
1
|
# @systemfsoftware/trace-spec
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# @systemfsoftware/trace-spec@1.2.4
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- Updated dependencies:
|
|
8
|
-
- @systemfsoftware/vitest@2.0.0
|
|
9
|
-
|
|
10
|
-
## 1.0.0
|
|
11
|
-
|
|
12
|
-
### Major Changes
|
|
13
|
-
|
|
14
|
-
- Require `effect` `^4` (stable 4.0.0). Release candidates are no longer supported: imports follow 4.0.0's module layout (`effect/http`, `effect/rpc`, `effect/http-api`, `effect/cluster`, `effect/process`, `effect/socket`, `effect/persistence`, `effect/reactivity`, `effect/encoding`, `effect/Arbitrary`), which does not exist in any 4.0.0 RC.
|
|
15
|
-
|
|
16
|
-
### Patch Changes
|
|
17
|
-
|
|
18
|
-
- Updated dependencies:
|
|
19
|
-
- @systemfsoftware/effect-cell-types@12.0.0
|
|
20
|
-
- @systemfsoftware/effect-spec-runtime@1.0.0
|
|
21
|
-
- @systemfsoftware/trace-taxonomy@1.0.0
|
|
22
|
-
- @systemfsoftware/vitest@1.0.0
|
|
23
|
-
|
|
24
|
-
## 0.5.2
|
|
25
|
-
|
|
26
|
-
### Patch Changes
|
|
27
|
-
|
|
28
|
-
- Updated dependencies:
|
|
29
|
-
- @systemfsoftware/vitest@0.3.0
|
|
30
|
-
|
|
31
|
-
## 0.5.1
|
|
32
|
-
|
|
33
|
-
### Patch Changes
|
|
34
|
-
|
|
35
|
-
- Updated dependencies:
|
|
36
|
-
- @systemfsoftware/effect-cell-types@11.0.0
|
|
37
|
-
|
|
38
|
-
## 0.5.0
|
|
39
|
-
|
|
40
|
-
### Minor Changes
|
|
41
|
-
|
|
42
|
-
- `Rel.Hold`, `Rel.Break` and `ObservationWindow.ObservationWindowSpec` are schema structs instead of classes, with unchanged encoded shapes: build them with `.make(...)` instead of `new`. The `VerdictTypeId` type is removed. `IncompleteObservationError.spanCount` must be a non-negative integer, and span `startMillis` and `durationMillis` must be finite and non-negative.
|
|
43
|
-
|
|
44
|
-
- `Contract.check(expect, input, options?)` takes the test's `expect` and ends in one check: a break fails it with the report as the message. `Contract.judge` returns the `{ verdict: 'Hold' | 'Break', report }` value, and `Contract.verdictCheck(contract, expect, judgment)` turns one into a check. `TraceDisparityError` is removed. `Suite` cases register as generator tests and judge each case once.
|
|
45
|
-
|
|
46
|
-
- The lawful test runner is now a peer dependency under its own name, `@systemfsoftware/vitest`, instead of the `@effect/vitest` alias that pointed at it. Install `@systemfsoftware/vitest` and import `it`, `layer`, `expect`, and the rest from `@systemfsoftware/vitest`. `@systemfsoftware/effect-gherkin-spec` re-exports `it` and `layer` from `@systemfsoftware/vitest`.
|
|
47
|
-
|
|
48
|
-
- A broken contract now fails the check with `BreakFailure`, whose message is the break report, pointing at the line that called the check.
|
|
49
|
-
|
|
50
|
-
- Cases now run on the simulation kernel — the zero-preemption schedule, then the profile's seeded schedules — instead of always on the live clock, and each case draws its own crypto-random salt, so two cases never share a trace id.
|
|
51
|
-
|
|
52
|
-
- `Suite.make({ it, layer })` → `Suite.make({ it })`.
|
|
53
|
-
- A case that must stay live declares a reason on any stage: `.live('...')`, or the new `Suite.live` combinator (`self.pipe(Suite.live(reason))`).
|
|
54
|
-
- `withLayer(shared)` rebuilds the shared layer freshly for every case rather than once per suite.
|
|
55
|
-
- `Stimulus.traceContext` draws its ids through Effect's `Random`, so a seeded run replays the same trace ids.
|
|
56
|
-
|
|
57
|
-
### Patch Changes
|
|
58
|
-
|
|
59
|
-
- Update peer and runtime dependency on `effect` and companion packages to `4.0.0-rc.117`.
|
|
60
|
-
|
|
61
|
-
- Updated dependencies:
|
|
62
|
-
- @systemfsoftware/trace-taxonomy@0.0.2
|
|
63
|
-
- @systemfsoftware/vitest@0.2.0
|
|
64
|
-
|
|
65
|
-
## 0.4.0
|
|
66
|
-
|
|
67
|
-
### Minor Changes
|
|
68
|
-
|
|
69
|
-
- `Contract.check(expect, input, options?)` takes the test's `expect` and ends in one check: a break fails it with the report as the message. `Contract.judge` returns the `{ verdict: 'Hold' | 'Break', report }` value, and `Contract.verdictCheck(contract, expect, judgment)` turns one into a check. `TraceDisparityError` is removed. `Suite` cases register as generator tests and judge each case once.
|
|
70
|
-
|
|
71
|
-
### Patch Changes
|
|
72
|
-
|
|
73
|
-
- Updated dependencies:
|
|
74
|
-
- @systemfsoftware/vitest@0.1.0
|
|
75
|
-
|
|
76
|
-
## 0.3.0
|
|
77
|
-
|
|
78
|
-
### Minor Changes
|
|
79
|
-
|
|
80
|
-
- Requires `@systemfsoftware/vitest` installed under the `@effect/vitest` name (`"@effect/vitest": "npm:@systemfsoftware/vitest"`) in place of the upstream package. `Case.prop` checks the stimulus it is handed on every run and pins the run's input, so a stimulus that ignores its input fails; the trace dump path is annotated on the failing test.
|
|
81
|
-
|
|
82
|
-
### Patch Changes
|
|
83
|
-
|
|
84
|
-
- The observation window is built on the `Blueprint` and `Handle` kinds, with unchanged behaviour. The blueprint's `TypeId` is now exported as a value.
|
|
85
|
-
|
|
86
|
-
- Updated dependencies:
|
|
87
|
-
- @systemfsoftware/effect-spec-runtime@0.2.0
|
|
88
|
-
|
|
89
|
-
## 0.2.1
|
|
90
|
-
|
|
91
|
-
### Patch Changes
|
|
92
|
-
|
|
93
|
-
- Update peer and runtime dependency on `effect` and companion packages to `4.0.0-rc.117`.
|
|
94
|
-
|
|
95
|
-
- Updated dependencies:
|
|
96
|
-
- @systemfsoftware/trace-taxonomy@0.0.1
|
|
97
|
-
|
|
98
|
-
## 0.2.0
|
|
99
|
-
|
|
100
|
-
### Minor Changes
|
|
101
|
-
|
|
102
|
-
- Judge contracts against traces produced in another process. `RemoteObservation.layer(source, { interval, settle, timeout })` provides `Observation` by reading a trace store until no read has added a span for `settle`. `TempoTraceStore.source({ baseUrl })` reads Grafana Tempo's `/api/v2/traces/<traceId>` through the `HttpClient` you provide; put auth and tenant headers on that client.
|
|
103
|
-
|
|
104
|
-
New failures `Observation.IncompleteObservationError` (spans never settled, or the store marked the trace partial) and `Observation.TransportObservationError` (the store could not be read) join `Collector.collect`, `Contract.JudgeFailure`, and `Suite.CaseFailure`. This is a breaking type change: code that matches every case of these unions, or restates them by hand, must handle both.
|
|
105
|
-
|
|
106
|
-
## 0.1.0
|
|
107
|
-
|
|
108
|
-
### Minor Changes
|
|
109
|
-
|
|
110
|
-
- `Rel`, `Graph`, `FailureDump` and `Prop` functions that took their subject first can now also be called data-last inside `pipe`. Existing calls keep working.
|
|
111
|
-
|
|
112
|
-
### Patch Changes
|
|
113
|
-
|
|
114
|
-
- Updated dependencies:
|
|
115
|
-
- @systemfsoftware/effect-spec-runtime@0.1.0
|
|
116
|
-
|
|
117
|
-
## 0.0.0
|
|
118
|
-
|
|
119
|
-
### Minor Changes
|
|
120
|
-
|
|
121
|
-
- New package: hold a behaviour to the trace it produced. `Contract.of(taxonomy).stimulate(stimulus).holds(relation)` composes a spec; `Contract.judge(contract, input)` runs the behaviour under a trace id it mints, decodes the finished trace against the taxonomy, and answers the verdict with the dump path of a break; `Contract.check` fails a break with `Contract.TraceDisparityError`. `Graph.decode` is pure, answering `Result<TraceGraph, ContractDecodeError>`.
|
|
122
|
-
|
|
123
|
-
`ObservationWindow.make(serviceName)` is the in-memory observation resource: `.scoped` acquires a window handle read with `ObservationWindow.collect`, and `.layer` provides `Observation | OtelTracer.OtelTracer`, one window per acquisition. `Suite.make({ it, layer })` registers a `Case` per input or a `Case.prop` per Schema, shrinking through `it.effect.prop` to one dump of the minimal failing input.
|
|
5
|
+
Published declaration files no longer carry the rolldown-plugin-dts patch; they come from rolldown-plugin-dts 0.28.8, which includes the same fix upstream.
|
package/dist/index.d.ts
CHANGED
|
@@ -2,18 +2,20 @@ import { Context, Duration, Effect, FileSystem, Layer, Option, Schema } from "ef
|
|
|
2
2
|
import { Pipeable } from "effect/Pipeable";
|
|
3
3
|
import { Span, Taxonomy } from "@systemfsoftware/trace-taxonomy";
|
|
4
4
|
import * as Result$1 from "effect/Result";
|
|
5
|
-
import * as HttpClient from "effect/http/HttpClient";
|
|
6
|
-
import { Blueprint, Handle } from "@systemfsoftware/effect-cell-types";
|
|
7
5
|
import * as OtelTracer from "@effect/opentelemetry/OtelTracer";
|
|
6
|
+
import { Blueprint, Handle } from "@systemfsoftware/effect-cell-types";
|
|
8
7
|
import { BasicTracerProvider, ReadableSpan } from "@opentelemetry/sdk-trace-base";
|
|
9
8
|
import { Suite } from "@systemfsoftware/effect-spec-runtime";
|
|
9
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
10
10
|
import { Asserted, Check, Expect } from "@systemfsoftware/vitest";
|
|
11
11
|
import * as Scope from "effect/Scope";
|
|
12
12
|
import * as Arbitrary from "effect/Arbitrary";
|
|
13
13
|
//#region src/BreakFailure.schema.d.ts
|
|
14
14
|
declare const BreakFailure_base: Schema.Class<BreakFailure, Schema.TaggedStruct<"BreakFailure", {
|
|
15
15
|
readonly report: Schema.String;
|
|
16
|
-
}>, import("effect/Cause").YieldableError
|
|
16
|
+
}>, import("effect/Cause").YieldableError & {
|
|
17
|
+
readonly brand: unique symbol;
|
|
18
|
+
}>;
|
|
17
19
|
declare class BreakFailure extends BreakFailure_base {
|
|
18
20
|
get message(): string;
|
|
19
21
|
}
|
|
@@ -25,7 +27,9 @@ declare const ContractDecodeError_base: Schema.Class<ContractDecodeError, Schema
|
|
|
25
27
|
readonly spanId: Schema.String;
|
|
26
28
|
readonly attribute: Schema.String;
|
|
27
29
|
readonly detail: Schema.String;
|
|
28
|
-
}>, import("effect/Cause").YieldableError
|
|
30
|
+
}>, import("effect/Cause").YieldableError & {
|
|
31
|
+
readonly brand: unique symbol;
|
|
32
|
+
}>;
|
|
29
33
|
declare class ContractDecodeError extends ContractDecodeError_base {
|
|
30
34
|
get message(): string;
|
|
31
35
|
}
|
|
@@ -34,7 +38,9 @@ declare class ContractDecodeError extends ContractDecodeError_base {
|
|
|
34
38
|
declare const EmptyObservationError_base: Schema.Class<EmptyObservationError, Schema.TaggedStruct<"EmptyObservationError", {
|
|
35
39
|
readonly traceId: Schema.String;
|
|
36
40
|
readonly detail: Schema.String;
|
|
37
|
-
}>, import("effect/Cause").YieldableError
|
|
41
|
+
}>, import("effect/Cause").YieldableError & {
|
|
42
|
+
readonly brand: unique symbol;
|
|
43
|
+
}>;
|
|
38
44
|
declare class EmptyObservationError extends EmptyObservationError_base {
|
|
39
45
|
get message(): string;
|
|
40
46
|
}
|
|
@@ -44,7 +50,9 @@ declare const IncompleteObservationError_base: Schema.Class<IncompleteObservatio
|
|
|
44
50
|
readonly traceId: Schema.String;
|
|
45
51
|
readonly spanCount: Schema.Int;
|
|
46
52
|
readonly detail: Schema.String;
|
|
47
|
-
}>, import("effect/Cause").YieldableError
|
|
53
|
+
}>, import("effect/Cause").YieldableError & {
|
|
54
|
+
readonly brand: unique symbol;
|
|
55
|
+
}>;
|
|
48
56
|
declare class IncompleteObservationError extends IncompleteObservationError_base {
|
|
49
57
|
get message(): string;
|
|
50
58
|
}
|
|
@@ -160,7 +168,9 @@ declare const TransportObservationError_base: Schema.Class<TransportObservationE
|
|
|
160
168
|
readonly traceId: Schema.String;
|
|
161
169
|
readonly source: Schema.String;
|
|
162
170
|
readonly detail: Schema.String;
|
|
163
|
-
}>, import("effect/Cause").YieldableError
|
|
171
|
+
}>, import("effect/Cause").YieldableError & {
|
|
172
|
+
readonly brand: unique symbol;
|
|
173
|
+
}>;
|
|
164
174
|
declare class TransportObservationError extends TransportObservationError_base {
|
|
165
175
|
get message(): string;
|
|
166
176
|
}
|
|
@@ -256,7 +266,7 @@ declare const fromTaxonomy: {
|
|
|
256
266
|
}): Relation;
|
|
257
267
|
};
|
|
258
268
|
declare namespace Stimulus_d_exports {
|
|
259
|
-
export { Run, Stimulus, TraceContext, make$
|
|
269
|
+
export { Run, Stimulus, TraceContext, make$2 as make, traceContext };
|
|
260
270
|
}
|
|
261
271
|
interface TraceContext {
|
|
262
272
|
readonly traceId: string;
|
|
@@ -273,7 +283,7 @@ interface Stimulus<Input, Output, E, R> {
|
|
|
273
283
|
readonly name: string;
|
|
274
284
|
(input: Input): Effect.Effect<Run<Input, Output>, E, R>;
|
|
275
285
|
}
|
|
276
|
-
declare const make$
|
|
286
|
+
declare const make$2: <Input, Output, E, R>(options: {
|
|
277
287
|
readonly name: string;
|
|
278
288
|
readonly run: (context: {
|
|
279
289
|
readonly input: Input;
|
|
@@ -354,55 +364,14 @@ declare const check: {
|
|
|
354
364
|
<Input>(expect: Expect, input: Input, options?: CheckOptions): <Output, E, R>(self: Contract<Input, Output, E, R>) => Effect.Effect<void, JudgeFailure<E>, Asserted | Services<R>>;
|
|
355
365
|
<Input, Output, E, R>(self: Contract<Input, Output, E, R>, expect: Expect, input: Input, options?: CheckOptions): Effect.Effect<void, JudgeFailure<E>, Asserted | Services<R>>;
|
|
356
366
|
};
|
|
357
|
-
declare namespace RemoteObservation_d_exports {
|
|
358
|
-
export { Options, TraceSource, layer$1 as layer };
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* The cadence of the poll, the quiet window the observed span set must hold
|
|
362
|
-
* before the read is trusted, and the deadline the whole poll runs under.
|
|
363
|
-
* Durations are refused unless they are positive, at layer construction.
|
|
364
|
-
*/
|
|
365
|
-
interface Options {
|
|
366
|
-
readonly interval: Duration.Input;
|
|
367
|
-
readonly settle: Duration.Input;
|
|
368
|
-
readonly timeout: Duration.Input;
|
|
369
|
-
}
|
|
370
|
-
/**
|
|
371
|
-
* One read against one store: the spans that store currently holds for a trace
|
|
372
|
-
* id, or the store's own transport or unfinished answer. A source never polls,
|
|
373
|
-
* waits, or retries — the cadence belongs to {@link layer}.
|
|
374
|
-
*/
|
|
375
|
-
type TraceSource<R> = (traceId: string) => Effect.Effect<ReadonlyArray<SpanRecord>, IncompleteObservationError | TransportObservationError, R>;
|
|
376
|
-
/**
|
|
377
|
-
* The `Observation` service over one trace source. `collect` reads the source at
|
|
378
|
-
* `options.interval`, keeps every span it has seen keyed by span id, and answers
|
|
379
|
-
* when that union has stayed unchanged for `options.settle`; a trace the store
|
|
380
|
-
* never served is `EmptyObservationError`, one that never settled is
|
|
381
|
-
* `IncompleteObservationError`, and the source's own failure ends the poll
|
|
382
|
-
* immediately, unchanged. Provides `Observation` alone and requires exactly what
|
|
383
|
-
* the source requires (`Scope.Scope` is supplied by the layer machinery, as
|
|
384
|
-
* every Effect layer constructor scopes it away).
|
|
385
|
-
*/
|
|
386
|
-
declare const layer$1: {
|
|
387
|
-
(options: Options): <R>(source: TraceSource<R>) => Layer.Layer<Observation, never, Exclude<R, Scope.Scope>>;
|
|
388
|
-
<R>(source: TraceSource<R>, options: Options): Layer.Layer<Observation, never, Exclude<R, Scope.Scope>>;
|
|
389
|
-
};
|
|
390
|
-
declare namespace tempo_trace_store_d_exports {
|
|
391
|
-
export { source };
|
|
392
|
-
}
|
|
393
|
-
/** One read against one Tempo endpoint. Its only requirement is the caller's HTTP client. */
|
|
394
|
-
declare const source: (options: {
|
|
395
|
-
readonly baseUrl: string;
|
|
396
|
-
}) => TraceSource<HttpClient.HttpClient>;
|
|
397
367
|
//#endregion
|
|
398
368
|
//#region src/ObservationWindowSpec.schema.d.ts
|
|
399
369
|
declare const ObservationWindowSpec: Schema.Struct<{
|
|
400
370
|
readonly serviceName: Schema.NonEmptyString;
|
|
401
371
|
}>;
|
|
402
372
|
type ObservationWindowSpec = typeof ObservationWindowSpec.Type;
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
373
|
+
//#endregion
|
|
374
|
+
//#region src/observation-window.handle.d.ts
|
|
406
375
|
declare const TypeId$2: unique symbol;
|
|
407
376
|
type TypeId$2 = typeof TypeId$2;
|
|
408
377
|
/**
|
|
@@ -424,32 +393,62 @@ declare const isObservationWindow: (u: unknown) => u is Handle.Handle<typeof Typ
|
|
|
424
393
|
readonly store: SpanRecordStore;
|
|
425
394
|
readonly provider: BasicTracerProvider;
|
|
426
395
|
}, never>;
|
|
427
|
-
declare const make$2: (spec: ObservationWindowSpec) => ObservationWindow;
|
|
428
|
-
declare const shutdown: (self: ObservationWindow) => Effect.Effect<void>;
|
|
429
396
|
declare const collect: {
|
|
430
397
|
(traceId: string): (self: ObservationWindow) => Effect.Effect<ReadonlyArray<SpanRecord>, EmptyObservationError>;
|
|
431
398
|
(self: ObservationWindow, traceId: string): Effect.Effect<ReadonlyArray<SpanRecord>, EmptyObservationError>;
|
|
432
399
|
};
|
|
433
|
-
declare const context: (self: ObservationWindow) => Context.Context<Observation | OtelTracer.OtelTracerProvider>;
|
|
434
400
|
declare namespace observation_window_blueprint_d_exports {
|
|
435
401
|
export { ObservationWindow, ObservationWindowBlueprint, ObservationWindowSpec, TypeId$2 as ObservationWindowTypeId, TypeId$1 as TypeId, collect, isObservationWindow, make$1 as make };
|
|
436
402
|
}
|
|
437
403
|
declare const TypeId$1: unique symbol;
|
|
438
404
|
type TypeId$1 = typeof TypeId$1;
|
|
439
405
|
declare const scoped: (spec: ObservationWindowSpec) => Effect.Effect<ObservationWindow, never, Scope.Scope>;
|
|
440
|
-
declare const layer: (spec: ObservationWindowSpec) => Layer.Layer<Observation | OtelTracer.OtelTracer>;
|
|
406
|
+
declare const layer$1: (spec: ObservationWindowSpec) => Layer.Layer<Observation | OtelTracer.OtelTracer>;
|
|
441
407
|
declare const ObservationWindows: Blueprint.Definition<typeof TypeId$1, {
|
|
442
408
|
readonly serviceName: string;
|
|
443
409
|
}, Blueprint.Derived<{
|
|
444
410
|
readonly serviceName: string;
|
|
445
411
|
}, {}, {
|
|
446
412
|
readonly scoped: typeof scoped;
|
|
447
|
-
readonly layer: typeof layer;
|
|
413
|
+
readonly layer: typeof layer$1;
|
|
448
414
|
}>, {
|
|
449
415
|
readonly serviceName: string;
|
|
450
416
|
}>;
|
|
451
417
|
type ObservationWindowBlueprint = Blueprint.Of<typeof ObservationWindows>;
|
|
452
418
|
declare const make$1: (serviceName: string) => ObservationWindowBlueprint;
|
|
419
|
+
declare namespace RemoteObservation_d_exports {
|
|
420
|
+
export { Options, TraceSource, layer };
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* The cadence of the poll, the quiet window the observed span set must hold
|
|
424
|
+
* before the read is trusted, and the deadline the whole poll runs under.
|
|
425
|
+
* Durations are refused unless they are positive, at layer construction.
|
|
426
|
+
*/
|
|
427
|
+
interface Options {
|
|
428
|
+
readonly interval: Duration.Input;
|
|
429
|
+
readonly settle: Duration.Input;
|
|
430
|
+
readonly timeout: Duration.Input;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* One read against one store: the spans that store currently holds for a trace
|
|
434
|
+
* id, or the store's own transport or unfinished answer. A source never polls,
|
|
435
|
+
* waits, or retries — the cadence belongs to {@link layer}.
|
|
436
|
+
*/
|
|
437
|
+
type TraceSource<R> = (traceId: string) => Effect.Effect<ReadonlyArray<SpanRecord>, IncompleteObservationError | TransportObservationError, R>;
|
|
438
|
+
/**
|
|
439
|
+
* The `Observation` service over one trace source. `collect` reads the source at
|
|
440
|
+
* `options.interval`, keeps every span it has seen keyed by span id, and answers
|
|
441
|
+
* when that union has stayed unchanged for `options.settle`; a trace the store
|
|
442
|
+
* never served is `EmptyObservationError`, one that never settled is
|
|
443
|
+
* `IncompleteObservationError`, and the source's own failure ends the poll
|
|
444
|
+
* immediately, unchanged. Provides `Observation` alone and requires exactly what
|
|
445
|
+
* the source requires (`Scope.Scope` is supplied by the layer machinery, as
|
|
446
|
+
* every Effect layer constructor scopes it away).
|
|
447
|
+
*/
|
|
448
|
+
declare const layer: {
|
|
449
|
+
(options: Options): <R>(source: TraceSource<R>) => Layer.Layer<Observation, never, Exclude<R, Scope.Scope>>;
|
|
450
|
+
<R>(source: TraceSource<R>, options: Options): Layer.Layer<Observation, never, Exclude<R, Scope.Scope>>;
|
|
451
|
+
};
|
|
453
452
|
//#endregion
|
|
454
453
|
//#region src/HarnessFailure.schema.d.ts
|
|
455
454
|
declare const HarnessFailure: Schema.Union<readonly [typeof ContractDecodeError, typeof EmptyObservationError, typeof IncompleteObservationError, typeof TransportObservationError]>;
|
|
@@ -459,7 +458,9 @@ type HarnessFailure = typeof HarnessFailure.Type;
|
|
|
459
458
|
declare const StimulusFailure_base: Schema.Class<StimulusFailure, Schema.TaggedStruct<"StimulusFailure", {
|
|
460
459
|
readonly stimulus: Schema.String;
|
|
461
460
|
readonly detail: Schema.String;
|
|
462
|
-
}>, import("effect/Cause").YieldableError
|
|
461
|
+
}>, import("effect/Cause").YieldableError & {
|
|
462
|
+
readonly brand: unique symbol;
|
|
463
|
+
}>;
|
|
463
464
|
declare class StimulusFailure extends StimulusFailure_base {
|
|
464
465
|
get message(): string;
|
|
465
466
|
}
|
|
@@ -518,5 +519,12 @@ declare const live: {
|
|
|
518
519
|
<Self>(self: LiveStage<Self>, reason: string): Self;
|
|
519
520
|
};
|
|
520
521
|
declare const make: (bindings: Suite.Bindings) => (name: string) => Declared;
|
|
522
|
+
declare namespace tempo_trace_store_d_exports {
|
|
523
|
+
export { source };
|
|
524
|
+
}
|
|
525
|
+
/** One read against one Tempo endpoint. Its only requirement is the caller's HTTP client. */
|
|
526
|
+
declare const source: (options: {
|
|
527
|
+
readonly baseUrl: string;
|
|
528
|
+
}) => TraceSource<HttpClient.HttpClient>;
|
|
521
529
|
//#endregion
|
|
522
530
|
export { Contract_d_exports as Contract, Graph_d_exports as Graph, Observation_service_d_exports as Observation, observation_window_blueprint_d_exports as ObservationWindow, Rel_d_exports as Rel, RemoteObservation_d_exports as RemoteObservation, Stimulus_d_exports as Stimulus, Suite_d_exports as Suite, tempo_trace_store_d_exports as TempoTraceStore };
|
package/dist/index.mjs
CHANGED
|
@@ -7,16 +7,16 @@ import "effect/PlatformError";
|
|
|
7
7
|
import "@systemfsoftware/trace-taxonomy";
|
|
8
8
|
import "effect/Array";
|
|
9
9
|
import * as Result$1 from "effect/Result";
|
|
10
|
-
import * as HttpClient from "effect/http/HttpClient";
|
|
11
|
-
import * as HttpClientRequest from "effect/http/HttpClientRequest";
|
|
12
|
-
import * as HttpClientResponse from "effect/http/HttpClientResponse";
|
|
13
|
-
import { Blueprint, Handle, Workflow } from "@systemfsoftware/effect-cell-types";
|
|
14
|
-
import { Hex } from "effect/encoding";
|
|
15
10
|
import * as OtelTracer from "@effect/opentelemetry/OtelTracer";
|
|
16
11
|
import * as OtelResource from "@effect/opentelemetry/Resource";
|
|
12
|
+
import { Blueprint, Handle, Workflow } from "@systemfsoftware/effect-cell-types";
|
|
17
13
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
18
14
|
import { AlwaysOnSampler, BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
|
|
19
15
|
import { Suite, TaskRef } from "@systemfsoftware/effect-spec-runtime";
|
|
16
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
17
|
+
import * as HttpClientRequest from "effect/http/HttpClientRequest";
|
|
18
|
+
import * as HttpClientResponse from "effect/http/HttpClientResponse";
|
|
19
|
+
import { Hex } from "effect/encoding";
|
|
20
20
|
//#region src/BreakFailure.schema.ts
|
|
21
21
|
var BreakFailure = class extends Schema.TaggedError()("BreakFailure", { report: Schema.String }) {
|
|
22
22
|
get message() {
|
|
@@ -367,258 +367,6 @@ const checkDual = (self, expect, input, options) => {
|
|
|
367
367
|
*/
|
|
368
368
|
const check = dual((args) => isContract(args[0]), checkDual);
|
|
369
369
|
//#endregion
|
|
370
|
-
//#region src/drivers/judge-tempo-answer.workflow.ts
|
|
371
|
-
const JudgeTempoAnswerTypeId = Symbol.for("@systemfsoftware/trace-spec/JudgeTempoAnswer");
|
|
372
|
-
var TempoBodyDecoded = class extends Schema.TaggedClass()("TempoBodyDecoded", {
|
|
373
|
-
spans: Schema.Array(SpanRecord),
|
|
374
|
-
status: Schema.optional(Schema.String)
|
|
375
|
-
}) {};
|
|
376
|
-
var TempoBodyUndecodable = class extends Schema.TaggedClass()("TempoBodyUndecodable", { detail: Schema.String }) {};
|
|
377
|
-
const TempoBody = Schema.Union([TempoBodyDecoded, TempoBodyUndecodable]);
|
|
378
|
-
var TempoAnswerComplete = class extends Schema.TaggedClass()("TempoAnswerComplete", { spans: Schema.Array(SpanRecord) }) {
|
|
379
|
-
[JudgeTempoAnswerTypeId] = JudgeTempoAnswerTypeId;
|
|
380
|
-
};
|
|
381
|
-
var TempoAnswerIncomplete = class extends Schema.TaggedClass()("TempoAnswerIncomplete", {
|
|
382
|
-
status: Schema.String,
|
|
383
|
-
spanCount: Schema.Int
|
|
384
|
-
}) {
|
|
385
|
-
[JudgeTempoAnswerTypeId] = JudgeTempoAnswerTypeId;
|
|
386
|
-
};
|
|
387
|
-
var TempoAnswerUndecodable = class extends Schema.TaggedClass()("TempoAnswerUndecodable", { detail: Schema.String }) {
|
|
388
|
-
[JudgeTempoAnswerTypeId] = JudgeTempoAnswerTypeId;
|
|
389
|
-
};
|
|
390
|
-
const TempoAnswerDecision = Schema.Union([
|
|
391
|
-
TempoAnswerComplete,
|
|
392
|
-
TempoAnswerIncomplete,
|
|
393
|
-
TempoAnswerUndecodable
|
|
394
|
-
]);
|
|
395
|
-
var JudgeTempoAnswer = class extends Schema.TaggedClass()("JudgeTempoAnswer", { body: TempoBody }) {
|
|
396
|
-
static [Workflow.InstrumentationBrand] = {};
|
|
397
|
-
};
|
|
398
|
-
const isComplete = (status) => Option.match(status, {
|
|
399
|
-
onNone: () => true,
|
|
400
|
-
onSome: (marked) => marked === "COMPLETE"
|
|
401
|
-
});
|
|
402
|
-
const classify$1 = (spans, status) => Match.value(isComplete(status)).pipe(Match.when(true, () => new TempoAnswerComplete({ spans })), Match.when(false, () => new TempoAnswerIncomplete({
|
|
403
|
-
status: Option.getOrElse(status, () => "INCOMPLETE"),
|
|
404
|
-
spanCount: spans.length
|
|
405
|
-
})), Match.exhaustive);
|
|
406
|
-
const decide$5 = (command) => Result$1.succeed(Match.value(command.body).pipe(Match.tag("TempoBodyDecoded", (decoded) => classify$1(decoded.spans, Option.fromUndefinedOr(decoded.status))), Match.tag("TempoBodyUndecodable", (undecodable) => new TempoAnswerUndecodable({ detail: undecodable.detail })), Match.exhaustive));
|
|
407
|
-
const judgeTempoAnswer = Workflow.make({
|
|
408
|
-
command: JudgeTempoAnswer,
|
|
409
|
-
decision: TempoAnswerDecision,
|
|
410
|
-
error: Schema.Never,
|
|
411
|
-
decide: decide$5
|
|
412
|
-
});
|
|
413
|
-
//#endregion
|
|
414
|
-
//#region src/drivers/tempo-trace.schema.ts
|
|
415
|
-
/** Proto bytes (base64 on the wire) as the lowercase hex ids traces are addressed by. */
|
|
416
|
-
const HexId = Schema.Uint8ArrayFromBase64.pipe(Schema.decodeTo(Schema.String, {
|
|
417
|
-
decode: SchemaGetter.transform(Hex.encode),
|
|
418
|
-
encode: SchemaGetter.decodeHex()
|
|
419
|
-
}));
|
|
420
|
-
const secondsAndNanos = (nanos) => {
|
|
421
|
-
const text = nanos.padStart(10, "0");
|
|
422
|
-
return [Number(text.slice(0, -9)), Number(text.slice(-9))];
|
|
423
|
-
};
|
|
424
|
-
const millisOf$2 = (nanos) => {
|
|
425
|
-
const [seconds, subMillis] = secondsAndNanos(nanos);
|
|
426
|
-
return seconds * 1e3 + subMillis / 1e6;
|
|
427
|
-
};
|
|
428
|
-
const MillisFromNanos = Schema.String.pipe(Schema.decodeTo(Schema.Finite, SchemaTransformation.transform({
|
|
429
|
-
decode: millisOf$2,
|
|
430
|
-
encode: (millis) => String(Math.round(millis * 1e6))
|
|
431
|
-
})));
|
|
432
|
-
const unsetParent = (parent) => parent === "" || /^0+$/.test(parent);
|
|
433
|
-
const heldParent = (parent) => unsetParent(parent) ? null : parent;
|
|
434
|
-
const parentOf$1 = (parent) => heldParent(parent ?? "");
|
|
435
|
-
const EMPTY_STATUS = {};
|
|
436
|
-
const codeOf = (status) => status.code ?? "STATUS_CODE_UNSET";
|
|
437
|
-
const STATUS_BY_CODE$1 = {
|
|
438
|
-
STATUS_CODE_UNSET: "unset",
|
|
439
|
-
STATUS_CODE_OK: "ok",
|
|
440
|
-
STATUS_CODE_ERROR: "error"
|
|
441
|
-
};
|
|
442
|
-
const statusOf$1 = (status) => STATUS_BY_CODE$1[codeOf(status ?? EMPTY_STATUS)];
|
|
443
|
-
const stringValueOf = (value) => value.stringValue ?? null;
|
|
444
|
-
const numberOf = (value) => value.doubleValue ?? intToNumber(value.intValue);
|
|
445
|
-
const intToNumber = (text) => text === void 0 ? null : Number(text);
|
|
446
|
-
const boolOrNumberOf = (value) => value.boolValue ?? numberOf(value);
|
|
447
|
-
const scalarOf = (value) => stringValueOf(value) ?? boolOrNumberOf(value);
|
|
448
|
-
const isScalarMember = (member) => member !== null;
|
|
449
|
-
const arrayValuesOf = (value) => value.arrayValue?.values;
|
|
450
|
-
const scalarMembersOf = (values) => values.map(scalarOf).filter(isScalarMember);
|
|
451
|
-
const arrayValueOf = (values) => values === void 0 ? null : scalarMembersOf(values);
|
|
452
|
-
const presentValueOf = (value) => scalarOf(value) ?? arrayValueOf(arrayValuesOf(value));
|
|
453
|
-
const isPresent$1 = (entry) => entry[1] !== null;
|
|
454
|
-
const entryOf = (attribute) => [attribute.key, attributeValueOf(attribute.value)];
|
|
455
|
-
const attributeValueOf = (value) => value === void 0 ? null : presentValueOf(value);
|
|
456
|
-
const attributesOf = (attributes) => Object.fromEntries((attributes ?? []).map(entryOf).filter(isPresent$1));
|
|
457
|
-
const errorTypeOf$1 = (attributes) => {
|
|
458
|
-
const value = attributes["error.type"];
|
|
459
|
-
return typeof value === "string" ? value : null;
|
|
460
|
-
};
|
|
461
|
-
const eventOf = (event) => ({
|
|
462
|
-
name: event.name,
|
|
463
|
-
attributes: attributesOf(event.attributes)
|
|
464
|
-
});
|
|
465
|
-
const linkIdOf = (id) => id ?? "";
|
|
466
|
-
const linkOf = (link) => ({
|
|
467
|
-
traceId: linkIdOf(link.traceId),
|
|
468
|
-
spanId: linkIdOf(link.spanId)
|
|
469
|
-
});
|
|
470
|
-
const eventsOf$1 = (span) => (span.events ?? []).map(eventOf);
|
|
471
|
-
const linksOf$1 = (span) => (span.links ?? []).map(linkOf);
|
|
472
|
-
const spanRecordOf$1 = (span) => {
|
|
473
|
-
const attributes = attributesOf(span.attributes);
|
|
474
|
-
return {
|
|
475
|
-
traceId: span.traceId,
|
|
476
|
-
spanId: span.spanId,
|
|
477
|
-
parentSpanId: parentOf$1(span.parentSpanId),
|
|
478
|
-
name: span.name,
|
|
479
|
-
status: statusOf$1(span.status),
|
|
480
|
-
errorType: errorTypeOf$1(attributes),
|
|
481
|
-
startMillis: span.startTimeUnixNano,
|
|
482
|
-
durationMillis: span.endTimeUnixNano - span.startTimeUnixNano,
|
|
483
|
-
attributes,
|
|
484
|
-
events: eventsOf$1(span),
|
|
485
|
-
links: linksOf$1(span)
|
|
486
|
-
};
|
|
487
|
-
};
|
|
488
|
-
const wireTextOf = (value) => typeof value === "string" ? { stringValue: value } : void 0;
|
|
489
|
-
const wireBoolOf = (value) => typeof value === "boolean" ? { boolValue: value } : void 0;
|
|
490
|
-
const isValueArray = (value) => Array.isArray(value);
|
|
491
|
-
const wireArrayOf = (value) => ({ arrayValue: { values: isValueArray(value) ? value.map(wireValueOf) : [] } });
|
|
492
|
-
const wireNumberOrArrayOf = (value) => typeof value === "number" ? { doubleValue: value } : wireArrayOf(value);
|
|
493
|
-
const wireBoolOrNumberOf = (value) => wireBoolOf(value) ?? wireNumberOrArrayOf(value);
|
|
494
|
-
const wireValueOf = (value) => wireTextOf(value) ?? wireBoolOrNumberOf(value);
|
|
495
|
-
const wireAttributesOf = (attributes) => Object.entries(attributes).map(([key, value]) => ({
|
|
496
|
-
key,
|
|
497
|
-
value: wireValueOf(value)
|
|
498
|
-
}));
|
|
499
|
-
const STATUS_CODE_BY_STATUS = {
|
|
500
|
-
unset: "STATUS_CODE_UNSET",
|
|
501
|
-
ok: "STATUS_CODE_OK",
|
|
502
|
-
error: "STATUS_CODE_ERROR"
|
|
503
|
-
};
|
|
504
|
-
const wireStatusOf = (status) => ({ code: STATUS_CODE_BY_STATUS[status] });
|
|
505
|
-
const wireSpanOf = (record) => ({
|
|
506
|
-
traceId: record.traceId,
|
|
507
|
-
spanId: record.spanId,
|
|
508
|
-
parentSpanId: record.parentSpanId ?? void 0,
|
|
509
|
-
name: record.name,
|
|
510
|
-
startTimeUnixNano: record.startMillis,
|
|
511
|
-
endTimeUnixNano: record.startMillis + record.durationMillis,
|
|
512
|
-
attributes: wireAttributesOf(record.attributes),
|
|
513
|
-
events: record.events.map((event) => ({
|
|
514
|
-
name: event.name,
|
|
515
|
-
attributes: wireAttributesOf(event.attributes)
|
|
516
|
-
})),
|
|
517
|
-
links: record.links.map((link) => ({
|
|
518
|
-
traceId: link.traceId,
|
|
519
|
-
spanId: link.spanId
|
|
520
|
-
})),
|
|
521
|
-
status: wireStatusOf(record.status)
|
|
522
|
-
});
|
|
523
|
-
const WireAnyValue = Schema.Struct({
|
|
524
|
-
stringValue: Schema.optional(Schema.String),
|
|
525
|
-
boolValue: Schema.optional(Schema.Boolean),
|
|
526
|
-
intValue: Schema.optional(Schema.String),
|
|
527
|
-
doubleValue: Schema.optional(Schema.Finite),
|
|
528
|
-
arrayValue: Schema.optional(Schema.Struct({ values: Schema.optional(Schema.Array(Schema.suspend(() => WireAnyValue))) }))
|
|
529
|
-
});
|
|
530
|
-
const WireAttribute = Schema.Struct({
|
|
531
|
-
key: Schema.String,
|
|
532
|
-
value: Schema.optional(WireAnyValue)
|
|
533
|
-
});
|
|
534
|
-
const WireStatus = Schema.Struct({
|
|
535
|
-
message: Schema.optional(Schema.String),
|
|
536
|
-
code: Schema.optional(Schema.Literals([
|
|
537
|
-
"STATUS_CODE_UNSET",
|
|
538
|
-
"STATUS_CODE_OK",
|
|
539
|
-
"STATUS_CODE_ERROR"
|
|
540
|
-
]))
|
|
541
|
-
});
|
|
542
|
-
const WireEvent = Schema.Struct({
|
|
543
|
-
name: Schema.String,
|
|
544
|
-
timeUnixNano: Schema.optional(MillisFromNanos),
|
|
545
|
-
attributes: Schema.optional(Schema.Array(WireAttribute))
|
|
546
|
-
});
|
|
547
|
-
const WireLink = Schema.Struct({
|
|
548
|
-
traceId: Schema.optional(HexId),
|
|
549
|
-
spanId: Schema.optional(HexId)
|
|
550
|
-
});
|
|
551
|
-
const WireSpan = Schema.Struct({
|
|
552
|
-
traceId: HexId,
|
|
553
|
-
spanId: HexId,
|
|
554
|
-
parentSpanId: Schema.optional(HexId),
|
|
555
|
-
name: Schema.String,
|
|
556
|
-
kind: Schema.optional(Schema.String),
|
|
557
|
-
startTimeUnixNano: MillisFromNanos,
|
|
558
|
-
endTimeUnixNano: MillisFromNanos,
|
|
559
|
-
attributes: Schema.optional(Schema.Array(WireAttribute)),
|
|
560
|
-
events: Schema.optional(Schema.Array(WireEvent)),
|
|
561
|
-
links: Schema.optional(Schema.Array(WireLink)),
|
|
562
|
-
status: Schema.optional(WireStatus),
|
|
563
|
-
traceState: Schema.optional(Schema.String),
|
|
564
|
-
flags: Schema.optional(Schema.Finite)
|
|
565
|
-
}).pipe(Schema.decodeTo(SpanRecord, SchemaTransformation.transform({
|
|
566
|
-
decode: spanRecordOf$1,
|
|
567
|
-
encode: wireSpanOf
|
|
568
|
-
})));
|
|
569
|
-
const WireScopeSpans = Schema.Struct({ spans: Schema.optional(Schema.Array(WireSpan)) });
|
|
570
|
-
const WireResourceSpans = Schema.Struct({ scopeSpans: Schema.optional(Schema.Array(WireScopeSpans)) });
|
|
571
|
-
/** One trace-by-id answer: the spans, plus the store's completeness status when it is not complete. */
|
|
572
|
-
const TempoV2TraceResponse = Schema.Struct({
|
|
573
|
-
trace: Schema.Struct({ resourceSpans: Schema.optional(Schema.Array(WireResourceSpans)) }),
|
|
574
|
-
status: Schema.optional(Schema.String),
|
|
575
|
-
message: Schema.optional(Schema.String)
|
|
576
|
-
});
|
|
577
|
-
//#endregion
|
|
578
|
-
//#region src/drivers/tempo-trace-store.ts
|
|
579
|
-
var tempo_trace_store_exports = /* @__PURE__ */ __exportAll({ source: () => source });
|
|
580
|
-
/**
|
|
581
|
-
* The Grafana Tempo trace source: one read of one trace by id against one
|
|
582
|
-
* store endpoint. It never polls, waits, or retries — completeness belongs
|
|
583
|
-
* to the remote observation layer. Any refusal (unreachable endpoint,
|
|
584
|
-
* non-2xx answer, undecodable body) fails with the transport failure whose
|
|
585
|
-
* `source` is the request URL; an answer the store marked incomplete fails
|
|
586
|
-
* with the incomplete failure, since waiting cannot complete it.
|
|
587
|
-
*/
|
|
588
|
-
const parseBody = Schema.decodeResult(Schema.fromJsonString(TempoV2TraceResponse));
|
|
589
|
-
const orEmpty = (values) => values ?? [];
|
|
590
|
-
const spansOf = (response) => orEmpty(response.trace.resourceSpans).flatMap((resourceSpans) => orEmpty(resourceSpans.scopeSpans).flatMap((scopeSpans) => orEmpty(scopeSpans.spans)));
|
|
591
|
-
const bodyOf = (decoded) => Result.match(decoded, {
|
|
592
|
-
onFailure: (error) => new TempoBodyUndecodable({ detail: error.message }),
|
|
593
|
-
onSuccess: (response) => new TempoBodyDecoded({
|
|
594
|
-
spans: spansOf(response),
|
|
595
|
-
status: response.status
|
|
596
|
-
})
|
|
597
|
-
});
|
|
598
|
-
const transportFailureOf = (traceId, url, cause) => new TransportObservationError({
|
|
599
|
-
traceId,
|
|
600
|
-
source: url,
|
|
601
|
-
detail: cause
|
|
602
|
-
});
|
|
603
|
-
const refusedOf = (traceId, url, error) => transportFailureOf(traceId, url, error.message);
|
|
604
|
-
const undecodableOf = (traceId, url, detail) => transportFailureOf(traceId, url, `undecodable trace body: ${detail}`);
|
|
605
|
-
const incompleteOf = (traceId, status, spanCount) => new IncompleteObservationError({
|
|
606
|
-
traceId,
|
|
607
|
-
spanCount,
|
|
608
|
-
detail: `the store answered ${status}: waiting cannot complete the trace`
|
|
609
|
-
});
|
|
610
|
-
const readTrace = (traceId, url, body) => Effect.flatMap(Effect.fromResult(judgeTempoAnswer(new JudgeTempoAnswer({ body: bodyOf(parseBody(body)) }))), (decision) => Match.value(decision).pipe(Match.tag("TempoAnswerComplete", (complete) => Effect.succeed(complete.spans)), Match.tag("TempoAnswerIncomplete", (incomplete) => Effect.fail(incompleteOf(traceId, incomplete.status, incomplete.spanCount))), Match.tag("TempoAnswerUndecodable", (undecodable) => Effect.fail(undecodableOf(traceId, url, undecodable.detail))), Match.exhaustive));
|
|
611
|
-
/** One read against one Tempo endpoint. Its only requirement is the caller's HTTP client. */
|
|
612
|
-
const source = (options) => {
|
|
613
|
-
const urlOf = (traceId) => `${options.baseUrl}/api/v2/traces/${traceId}`;
|
|
614
|
-
return (traceId) => {
|
|
615
|
-
const url = urlOf(traceId);
|
|
616
|
-
return HttpClient.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.acceptJson)).pipe(Effect.flatMap(HttpClientResponse.filterStatusOk), Effect.flatMap((response) => response.text), Effect.mapError((error) => refusedOf(traceId, url, error)), Effect.flatMap((body) => readTrace(traceId, url, body)));
|
|
617
|
-
};
|
|
618
|
-
};
|
|
619
|
-
Schema.decodeEffect(HexId);
|
|
620
|
-
Schema.encodeEffect(HexId);
|
|
621
|
-
//#endregion
|
|
622
370
|
//#region src/observation-window.handle.ts
|
|
623
371
|
const TypeId$2 = Symbol.for("~systemfsoftware/trace-spec/ObservationWindow");
|
|
624
372
|
const ObservationWindow = Handle.make()(TypeId$2);
|
|
@@ -669,52 +417,52 @@ const isScalar = (value) => SCALAR_TYPES[typeof value] === true;
|
|
|
669
417
|
const scalarArrayOrNull = (value) => Array.isArray(value) ? value.filter(isScalar) : null;
|
|
670
418
|
const definedOrEmpty = (value) => value ?? [];
|
|
671
419
|
const attributeValue = (value) => isScalar(value) ? value : scalarArrayOrNull(definedOrEmpty(value));
|
|
672
|
-
const isPresent = (entry) => entry[1] !== null;
|
|
673
|
-
const attributeRecordOf = (attributes) => Object.fromEntries(Object.entries(attributes).map((entry) => [entry[0], attributeValue(entry[1])]).filter(isPresent));
|
|
674
|
-
const STATUS_BY_CODE = {
|
|
420
|
+
const isPresent$1 = (entry) => entry[1] !== null;
|
|
421
|
+
const attributeRecordOf = (attributes) => Object.fromEntries(Object.entries(attributes).map((entry) => [entry[0], attributeValue(entry[1])]).filter(isPresent$1));
|
|
422
|
+
const STATUS_BY_CODE$1 = {
|
|
675
423
|
[SpanStatusCode.OK]: "ok",
|
|
676
424
|
[SpanStatusCode.ERROR]: "error",
|
|
677
425
|
[SpanStatusCode.UNSET]: "unset"
|
|
678
426
|
};
|
|
679
|
-
const statusOf = (code) => STATUS_BY_CODE[code] ?? "unset";
|
|
680
|
-
const errorTypeOf = (attributes) => {
|
|
427
|
+
const statusOf$1 = (code) => STATUS_BY_CODE$1[code] ?? "unset";
|
|
428
|
+
const errorTypeOf$1 = (attributes) => {
|
|
681
429
|
const value = attributes["error.type"];
|
|
682
430
|
return typeof value === "string" ? value : null;
|
|
683
431
|
};
|
|
684
|
-
const parentOf = (span) => {
|
|
432
|
+
const parentOf$1 = (span) => {
|
|
685
433
|
const parent = span.parentSpanContext;
|
|
686
434
|
return parent === void 0 ? null : parent.spanId;
|
|
687
435
|
};
|
|
688
|
-
const eventsOf = (span) => span.events.map((event) => ({
|
|
436
|
+
const eventsOf$1 = (span) => span.events.map((event) => ({
|
|
689
437
|
name: event.name,
|
|
690
438
|
attributes: attributeRecordOf(event.attributes ?? {})
|
|
691
439
|
}));
|
|
692
|
-
const linksOf = (span) => span.links.map((link) => ({
|
|
440
|
+
const linksOf$1 = (span) => span.links.map((link) => ({
|
|
693
441
|
traceId: link.context.traceId,
|
|
694
442
|
spanId: link.context.spanId
|
|
695
443
|
}));
|
|
696
|
-
const millisOf$
|
|
697
|
-
const spanRecordOf = (span) => {
|
|
444
|
+
const millisOf$2 = (duration) => duration[0] * 1e3 + duration[1] / 1e6;
|
|
445
|
+
const spanRecordOf$1 = (span) => {
|
|
698
446
|
const attributes = attributeRecordOf(span.attributes);
|
|
699
447
|
return {
|
|
700
448
|
traceId: span.spanContext().traceId,
|
|
701
449
|
spanId: span.spanContext().spanId,
|
|
702
|
-
parentSpanId: parentOf(span),
|
|
450
|
+
parentSpanId: parentOf$1(span),
|
|
703
451
|
name: span.name,
|
|
704
|
-
status: statusOf(span.status.code),
|
|
705
|
-
errorType: errorTypeOf(attributes),
|
|
706
|
-
startMillis: millisOf$
|
|
707
|
-
durationMillis: millisOf$
|
|
452
|
+
status: statusOf$1(span.status.code),
|
|
453
|
+
errorType: errorTypeOf$1(attributes),
|
|
454
|
+
startMillis: millisOf$2(span.startTime),
|
|
455
|
+
durationMillis: millisOf$2(span.duration),
|
|
708
456
|
attributes,
|
|
709
|
-
events: eventsOf(span),
|
|
710
|
-
links: linksOf(span)
|
|
457
|
+
events: eventsOf$1(span),
|
|
458
|
+
links: linksOf$1(span)
|
|
711
459
|
};
|
|
712
460
|
};
|
|
713
461
|
const emptyObservation = (traceId) => new EmptyObservationError({
|
|
714
462
|
traceId,
|
|
715
463
|
detail: "the observation window closed with no span carrying the stimulated trace id"
|
|
716
464
|
});
|
|
717
|
-
const recordsOf = (self, traceId) => ObservationWindow.slot(self).store.getFinishedSpans().filter((span) => span.spanContext().traceId === traceId).map(spanRecordOf);
|
|
465
|
+
const recordsOf = (self, traceId) => ObservationWindow.slot(self).store.getFinishedSpans().filter((span) => span.spanContext().traceId === traceId).map(spanRecordOf$1);
|
|
718
466
|
const collect = dual(2, (self, traceId) => Effect.suspend(() => {
|
|
719
467
|
const records = recordsOf(self, traceId);
|
|
720
468
|
return records.length > 0 ? Effect.succeed(records) : Effect.fail(emptyObservation(traceId));
|
|
@@ -772,7 +520,7 @@ var CombineAnyRelations = class extends Schema.TaggedClass()("CombineAnyRelation
|
|
|
772
520
|
const holds = (verdict) => Match.value(verdict).pipe(Match.tag("Hold", () => true), Match.tag("Break", () => false), Match.exhaustive);
|
|
773
521
|
const inspectedOf$1 = (verdicts) => Array$1.flatMap(verdicts, (verdict) => verdict.inspected);
|
|
774
522
|
const namedConjuncts = (verdicts) => Array$1.join(Array$1.map(verdicts, (verdict) => verdict.conjunct), ", ");
|
|
775
|
-
const decide$
|
|
523
|
+
const decide$5 = (command) => Result$1.succeed(Match.value(Array$1.some(command.verdicts, holds)).pipe(Match.when(true, () => new AnyRelationHeld({
|
|
776
524
|
conjunct: command.conjunct,
|
|
777
525
|
inspected: inspectedOf$1(command.verdicts)
|
|
778
526
|
})), Match.when(false, () => new NoRelationHeld({
|
|
@@ -785,7 +533,7 @@ const combineAnyRelations = Workflow.make({
|
|
|
785
533
|
command: CombineAnyRelations,
|
|
786
534
|
decision: CombineAnyRelationsDecision,
|
|
787
535
|
error: Schema.Never,
|
|
788
|
-
decide: decide$
|
|
536
|
+
decide: decide$5
|
|
789
537
|
});
|
|
790
538
|
//#endregion
|
|
791
539
|
//#region src/combine-relation-verdicts.workflow.ts
|
|
@@ -846,7 +594,7 @@ const aggregateSoft = (command) => {
|
|
|
846
594
|
})
|
|
847
595
|
});
|
|
848
596
|
};
|
|
849
|
-
const decide$
|
|
597
|
+
const decide$4 = (command) => Result$1.succeed(Option.match(firstHardBreak(command.verdicts), {
|
|
850
598
|
onNone: () => aggregateSoft(command),
|
|
851
599
|
onSome: (winner) => new FirstHardBreak({
|
|
852
600
|
conjunct: winner.conjunct,
|
|
@@ -858,7 +606,7 @@ const combineRelationVerdicts = Workflow.make({
|
|
|
858
606
|
command: CombineRelationVerdicts,
|
|
859
607
|
decision: CombineRelationVerdictsDecision,
|
|
860
608
|
error: Schema.Never,
|
|
861
|
-
decide: decide$
|
|
609
|
+
decide: decide$4
|
|
862
610
|
});
|
|
863
611
|
//#endregion
|
|
864
612
|
//#region src/judge-edge-placement.workflow.ts
|
|
@@ -886,7 +634,7 @@ var JudgeEdgePlacement = class extends Schema.TaggedClass()("JudgeEdgePlacement"
|
|
|
886
634
|
static [Workflow.InstrumentationBrand] = {};
|
|
887
635
|
};
|
|
888
636
|
const everyPlaced = (placed, reached) => Array$1.every(placed, (spanId) => Array$1.some(reached, (candidate) => candidate === spanId));
|
|
889
|
-
const decide$
|
|
637
|
+
const decide$3 = (command) => Result$1.succeed(Match.value(everyPlaced(command.placed, command.reached)).pipe(Match.when(true, () => new EdgePlacementHeld({
|
|
890
638
|
conjunct: command.conjunct,
|
|
891
639
|
inspected: command.placed
|
|
892
640
|
})), Match.when(false, () => new EdgePlacementBroken({
|
|
@@ -898,7 +646,7 @@ const judgeEdgePlacement = Workflow.make({
|
|
|
898
646
|
command: JudgeEdgePlacement,
|
|
899
647
|
decision: JudgeEdgePlacementDecision,
|
|
900
648
|
error: Schema.Never,
|
|
901
|
-
decide: decide$
|
|
649
|
+
decide: decide$3
|
|
902
650
|
});
|
|
903
651
|
//#endregion
|
|
904
652
|
//#region src/negate-relation-verdict.workflow.ts
|
|
@@ -923,7 +671,7 @@ var NegateRelationVerdict = class extends Schema.TaggedClass()("NegateRelationVe
|
|
|
923
671
|
}) {
|
|
924
672
|
static [Workflow.InstrumentationBrand] = {};
|
|
925
673
|
};
|
|
926
|
-
const decide$
|
|
674
|
+
const decide$2 = (command) => Result$1.succeed(Match.value(command.verdict).pipe(Match.tag("Hold", (held) => new HoldNegated({
|
|
927
675
|
conjunct: command.conjunct,
|
|
928
676
|
inspected: held.inspected,
|
|
929
677
|
detail: `${held.conjunct} held`
|
|
@@ -935,7 +683,7 @@ const negateRelationVerdict = Workflow.make({
|
|
|
935
683
|
command: NegateRelationVerdict,
|
|
936
684
|
decision: NegateRelationVerdictDecision,
|
|
937
685
|
error: Schema.Never,
|
|
938
|
-
decide: decide$
|
|
686
|
+
decide: decide$2
|
|
939
687
|
});
|
|
940
688
|
//#endregion
|
|
941
689
|
//#region src/Rel.ts
|
|
@@ -1276,13 +1024,13 @@ const absorbed = (observed, read, elapsedMillis) => {
|
|
|
1276
1024
|
});
|
|
1277
1025
|
};
|
|
1278
1026
|
const quietForLongEnough = (observed, elapsedMillis, settleMillis) => Array$1.every([observed.spans.length > 0, elapsedMillis - observed.addedAtMillis >= settleMillis], Boolean);
|
|
1279
|
-
const classify = (observation, elapsedMillis, settleMillis, timeoutMillis) => Match.value(quietForLongEnough(observation, elapsedMillis, settleMillis)).pipe(Match.when(true, () => new ReadSettled({ observation })), Match.when(false, () => Match.value(elapsedMillis >= timeoutMillis).pipe(Match.when(true, () => Match.value(observation.spans.length === 0).pipe(Match.when(true, () => new ReadAbsent({ observation })), Match.when(false, () => new ReadUnfinished({ observation })), Match.exhaustive)), Match.when(false, () => new ReadStillGrowing({ observation })), Match.exhaustive)), Match.exhaustive);
|
|
1280
|
-
const decide = (command) => Result$1.succeed(classify(absorbed(command.observed, command.read, command.elapsedMillis), command.elapsedMillis, command.settleMillis, command.timeoutMillis));
|
|
1027
|
+
const classify$1 = (observation, elapsedMillis, settleMillis, timeoutMillis) => Match.value(quietForLongEnough(observation, elapsedMillis, settleMillis)).pipe(Match.when(true, () => new ReadSettled({ observation })), Match.when(false, () => Match.value(elapsedMillis >= timeoutMillis).pipe(Match.when(true, () => Match.value(observation.spans.length === 0).pipe(Match.when(true, () => new ReadAbsent({ observation })), Match.when(false, () => new ReadUnfinished({ observation })), Match.exhaustive)), Match.when(false, () => new ReadStillGrowing({ observation })), Match.exhaustive)), Match.exhaustive);
|
|
1028
|
+
const decide$1 = (command) => Result$1.succeed(classify$1(absorbed(command.observed, command.read, command.elapsedMillis), command.elapsedMillis, command.settleMillis, command.timeoutMillis));
|
|
1281
1029
|
const settleRemoteRead = Workflow.make({
|
|
1282
1030
|
command: SettleRemoteRead,
|
|
1283
1031
|
decision: SettleRemoteReadDecision,
|
|
1284
1032
|
error: Schema.Never,
|
|
1285
|
-
decide
|
|
1033
|
+
decide: decide$1
|
|
1286
1034
|
});
|
|
1287
1035
|
//#endregion
|
|
1288
1036
|
//#region src/RemoteObservation.ts
|
|
@@ -1314,17 +1062,17 @@ const settleStep = (settlement, read, elapsedMillis, windows) => {
|
|
|
1314
1062
|
};
|
|
1315
1063
|
const lateRefusal = (settlement) => settlement.spans.length === 0 ? "absent" : "unfinished";
|
|
1316
1064
|
const deadlineVerdict = (settlement, elapsedMillis, windows) => pastDeadline(elapsedMillis, windows) ? refusedVerdict(lateRefusal(settlement)) : Option.none();
|
|
1317
|
-
const millisOf = (input) => Duration.toMillis(input);
|
|
1065
|
+
const millisOf$1 = (input) => Duration.toMillis(input);
|
|
1318
1066
|
const windowsOf = (options) => ({
|
|
1319
|
-
settleMillis: millisOf(options.settle),
|
|
1320
|
-
timeoutMillis: millisOf(options.timeout)
|
|
1067
|
+
settleMillis: millisOf$1(options.settle),
|
|
1068
|
+
timeoutMillis: millisOf$1(options.timeout)
|
|
1321
1069
|
});
|
|
1322
1070
|
const nonPositive = (options) => [
|
|
1323
1071
|
options.interval,
|
|
1324
1072
|
options.settle,
|
|
1325
1073
|
options.timeout
|
|
1326
|
-
].some((input) => millisOf(input) <= 0);
|
|
1327
|
-
const requirePositive = (options) => nonPositive(options) ? Effect.die(/* @__PURE__ */ new Error(`remote observation durations must be positive milliseconds: interval=${millisOf(options.interval)}, settle=${millisOf(options.settle)}, timeout=${millisOf(options.timeout)}`)) : Effect.void;
|
|
1074
|
+
].some((input) => millisOf$1(input) <= 0);
|
|
1075
|
+
const requirePositive = (options) => nonPositive(options) ? Effect.die(/* @__PURE__ */ new Error(`remote observation durations must be positive milliseconds: interval=${millisOf$1(options.interval)}, settle=${millisOf$1(options.settle)}, timeout=${millisOf$1(options.timeout)}`)) : Effect.void;
|
|
1328
1076
|
const absence = (traceId, windows) => new EmptyObservationError({
|
|
1329
1077
|
traceId,
|
|
1330
1078
|
detail: `the remote trace store served no span for trace ${traceId} before the ${windows.timeoutMillis}ms timeout`
|
|
@@ -1559,4 +1307,256 @@ const liveDual = (self, reason) => self.live(reason);
|
|
|
1559
1307
|
const live = dual(2, liveDual);
|
|
1560
1308
|
const make = (bindings) => (name) => declared(bindings, name, void 0);
|
|
1561
1309
|
//#endregion
|
|
1310
|
+
//#region src/judge-tempo-answer.workflow.ts
|
|
1311
|
+
const JudgeTempoAnswerTypeId = Symbol.for("@systemfsoftware/trace-spec/JudgeTempoAnswer");
|
|
1312
|
+
var TempoBodyDecoded = class extends Schema.TaggedClass()("TempoBodyDecoded", {
|
|
1313
|
+
spans: Schema.Array(SpanRecord),
|
|
1314
|
+
status: Schema.optional(Schema.String)
|
|
1315
|
+
}) {};
|
|
1316
|
+
var TempoBodyUndecodable = class extends Schema.TaggedClass()("TempoBodyUndecodable", { detail: Schema.String }) {};
|
|
1317
|
+
const TempoBody = Schema.Union([TempoBodyDecoded, TempoBodyUndecodable]);
|
|
1318
|
+
var TempoAnswerComplete = class extends Schema.TaggedClass()("TempoAnswerComplete", { spans: Schema.Array(SpanRecord) }) {
|
|
1319
|
+
[JudgeTempoAnswerTypeId] = JudgeTempoAnswerTypeId;
|
|
1320
|
+
};
|
|
1321
|
+
var TempoAnswerIncomplete = class extends Schema.TaggedClass()("TempoAnswerIncomplete", {
|
|
1322
|
+
status: Schema.String,
|
|
1323
|
+
spanCount: Schema.Int
|
|
1324
|
+
}) {
|
|
1325
|
+
[JudgeTempoAnswerTypeId] = JudgeTempoAnswerTypeId;
|
|
1326
|
+
};
|
|
1327
|
+
var TempoAnswerUndecodable = class extends Schema.TaggedClass()("TempoAnswerUndecodable", { detail: Schema.String }) {
|
|
1328
|
+
[JudgeTempoAnswerTypeId] = JudgeTempoAnswerTypeId;
|
|
1329
|
+
};
|
|
1330
|
+
const TempoAnswerDecision = Schema.Union([
|
|
1331
|
+
TempoAnswerComplete,
|
|
1332
|
+
TempoAnswerIncomplete,
|
|
1333
|
+
TempoAnswerUndecodable
|
|
1334
|
+
]);
|
|
1335
|
+
var JudgeTempoAnswer = class extends Schema.TaggedClass()("JudgeTempoAnswer", { body: TempoBody }) {
|
|
1336
|
+
static [Workflow.InstrumentationBrand] = {};
|
|
1337
|
+
};
|
|
1338
|
+
const isComplete = (status) => Option.match(status, {
|
|
1339
|
+
onNone: () => true,
|
|
1340
|
+
onSome: (marked) => marked === "COMPLETE"
|
|
1341
|
+
});
|
|
1342
|
+
const classify = (spans, status) => Match.value(isComplete(status)).pipe(Match.when(true, () => new TempoAnswerComplete({ spans })), Match.when(false, () => new TempoAnswerIncomplete({
|
|
1343
|
+
status: Option.getOrElse(status, () => "INCOMPLETE"),
|
|
1344
|
+
spanCount: spans.length
|
|
1345
|
+
})), Match.exhaustive);
|
|
1346
|
+
const decide = (command) => Result$1.succeed(Match.value(command.body).pipe(Match.tag("TempoBodyDecoded", (decoded) => classify(decoded.spans, Option.fromUndefinedOr(decoded.status))), Match.tag("TempoBodyUndecodable", (undecodable) => new TempoAnswerUndecodable({ detail: undecodable.detail })), Match.exhaustive));
|
|
1347
|
+
const judgeTempoAnswer = Workflow.make({
|
|
1348
|
+
command: JudgeTempoAnswer,
|
|
1349
|
+
decision: TempoAnswerDecision,
|
|
1350
|
+
error: Schema.Never,
|
|
1351
|
+
decide
|
|
1352
|
+
});
|
|
1353
|
+
//#endregion
|
|
1354
|
+
//#region src/tempo-trace.schema.ts
|
|
1355
|
+
/** Proto bytes (base64 on the wire) as the lowercase hex ids traces are addressed by. */
|
|
1356
|
+
const HexId = Schema.Uint8ArrayFromBase64.pipe(Schema.decodeTo(Schema.String, {
|
|
1357
|
+
decode: SchemaGetter.transform(Hex.encode),
|
|
1358
|
+
encode: SchemaGetter.decodeHex()
|
|
1359
|
+
}));
|
|
1360
|
+
const secondsAndNanos = (nanos) => {
|
|
1361
|
+
const text = nanos.padStart(10, "0");
|
|
1362
|
+
return [Number(text.slice(0, -9)), Number(text.slice(-9))];
|
|
1363
|
+
};
|
|
1364
|
+
const millisOf = (nanos) => {
|
|
1365
|
+
const [seconds, subMillis] = secondsAndNanos(nanos);
|
|
1366
|
+
return seconds * 1e3 + subMillis / 1e6;
|
|
1367
|
+
};
|
|
1368
|
+
const MillisFromNanos = Schema.String.pipe(Schema.decodeTo(Schema.Finite, SchemaTransformation.transform({
|
|
1369
|
+
decode: millisOf,
|
|
1370
|
+
encode: (millis) => String(Math.round(millis * 1e6))
|
|
1371
|
+
})));
|
|
1372
|
+
const unsetParent = (parent) => parent === "" || /^0+$/.test(parent);
|
|
1373
|
+
const heldParent = (parent) => unsetParent(parent) ? null : parent;
|
|
1374
|
+
const parentOf = (parent) => heldParent(parent ?? "");
|
|
1375
|
+
const EMPTY_STATUS = {};
|
|
1376
|
+
const codeOf = (status) => status.code ?? "STATUS_CODE_UNSET";
|
|
1377
|
+
const STATUS_BY_CODE = {
|
|
1378
|
+
STATUS_CODE_UNSET: "unset",
|
|
1379
|
+
STATUS_CODE_OK: "ok",
|
|
1380
|
+
STATUS_CODE_ERROR: "error"
|
|
1381
|
+
};
|
|
1382
|
+
const statusOf = (status) => STATUS_BY_CODE[codeOf(status ?? EMPTY_STATUS)];
|
|
1383
|
+
const stringValueOf = (value) => value.stringValue ?? null;
|
|
1384
|
+
const numberOf = (value) => value.doubleValue ?? intToNumber(value.intValue);
|
|
1385
|
+
const intToNumber = (text) => text === void 0 ? null : Number(text);
|
|
1386
|
+
const boolOrNumberOf = (value) => value.boolValue ?? numberOf(value);
|
|
1387
|
+
const scalarOf = (value) => stringValueOf(value) ?? boolOrNumberOf(value);
|
|
1388
|
+
const isScalarMember = (member) => member !== null;
|
|
1389
|
+
const arrayValuesOf = (value) => value.arrayValue?.values;
|
|
1390
|
+
const scalarMembersOf = (values) => values.map(scalarOf).filter(isScalarMember);
|
|
1391
|
+
const arrayValueOf = (values) => values === void 0 ? null : scalarMembersOf(values);
|
|
1392
|
+
const presentValueOf = (value) => scalarOf(value) ?? arrayValueOf(arrayValuesOf(value));
|
|
1393
|
+
const isPresent = (entry) => entry[1] !== null;
|
|
1394
|
+
const entryOf = (attribute) => [attribute.key, attributeValueOf(attribute.value)];
|
|
1395
|
+
const attributeValueOf = (value) => value === void 0 ? null : presentValueOf(value);
|
|
1396
|
+
const attributesOf = (attributes) => Object.fromEntries((attributes ?? []).map(entryOf).filter(isPresent));
|
|
1397
|
+
const errorTypeOf = (attributes) => {
|
|
1398
|
+
const value = attributes["error.type"];
|
|
1399
|
+
return typeof value === "string" ? value : null;
|
|
1400
|
+
};
|
|
1401
|
+
const eventOf = (event) => ({
|
|
1402
|
+
name: event.name,
|
|
1403
|
+
attributes: attributesOf(event.attributes)
|
|
1404
|
+
});
|
|
1405
|
+
const linkIdOf = (id) => id ?? "";
|
|
1406
|
+
const linkOf = (link) => ({
|
|
1407
|
+
traceId: linkIdOf(link.traceId),
|
|
1408
|
+
spanId: linkIdOf(link.spanId)
|
|
1409
|
+
});
|
|
1410
|
+
const eventsOf = (span) => (span.events ?? []).map(eventOf);
|
|
1411
|
+
const linksOf = (span) => (span.links ?? []).map(linkOf);
|
|
1412
|
+
const spanRecordOf = (span) => {
|
|
1413
|
+
const attributes = attributesOf(span.attributes);
|
|
1414
|
+
return {
|
|
1415
|
+
traceId: span.traceId,
|
|
1416
|
+
spanId: span.spanId,
|
|
1417
|
+
parentSpanId: parentOf(span.parentSpanId),
|
|
1418
|
+
name: span.name,
|
|
1419
|
+
status: statusOf(span.status),
|
|
1420
|
+
errorType: errorTypeOf(attributes),
|
|
1421
|
+
startMillis: span.startTimeUnixNano,
|
|
1422
|
+
durationMillis: span.endTimeUnixNano - span.startTimeUnixNano,
|
|
1423
|
+
attributes,
|
|
1424
|
+
events: eventsOf(span),
|
|
1425
|
+
links: linksOf(span)
|
|
1426
|
+
};
|
|
1427
|
+
};
|
|
1428
|
+
const wireTextOf = (value) => typeof value === "string" ? { stringValue: value } : void 0;
|
|
1429
|
+
const wireBoolOf = (value) => typeof value === "boolean" ? { boolValue: value } : void 0;
|
|
1430
|
+
const isValueArray = (value) => Array.isArray(value);
|
|
1431
|
+
const wireArrayOf = (value) => ({ arrayValue: { values: isValueArray(value) ? value.map(wireValueOf) : [] } });
|
|
1432
|
+
const wireNumberOrArrayOf = (value) => typeof value === "number" ? { doubleValue: value } : wireArrayOf(value);
|
|
1433
|
+
const wireBoolOrNumberOf = (value) => wireBoolOf(value) ?? wireNumberOrArrayOf(value);
|
|
1434
|
+
const wireValueOf = (value) => wireTextOf(value) ?? wireBoolOrNumberOf(value);
|
|
1435
|
+
const wireAttributesOf = (attributes) => Object.entries(attributes).map(([key, value]) => ({
|
|
1436
|
+
key,
|
|
1437
|
+
value: wireValueOf(value)
|
|
1438
|
+
}));
|
|
1439
|
+
const STATUS_CODE_BY_STATUS = {
|
|
1440
|
+
unset: "STATUS_CODE_UNSET",
|
|
1441
|
+
ok: "STATUS_CODE_OK",
|
|
1442
|
+
error: "STATUS_CODE_ERROR"
|
|
1443
|
+
};
|
|
1444
|
+
const wireStatusOf = (status) => ({ code: STATUS_CODE_BY_STATUS[status] });
|
|
1445
|
+
const wireSpanOf = (record) => ({
|
|
1446
|
+
traceId: record.traceId,
|
|
1447
|
+
spanId: record.spanId,
|
|
1448
|
+
parentSpanId: record.parentSpanId ?? void 0,
|
|
1449
|
+
name: record.name,
|
|
1450
|
+
startTimeUnixNano: record.startMillis,
|
|
1451
|
+
endTimeUnixNano: record.startMillis + record.durationMillis,
|
|
1452
|
+
attributes: wireAttributesOf(record.attributes),
|
|
1453
|
+
events: record.events.map((event) => ({
|
|
1454
|
+
name: event.name,
|
|
1455
|
+
attributes: wireAttributesOf(event.attributes)
|
|
1456
|
+
})),
|
|
1457
|
+
links: record.links.map((link) => ({
|
|
1458
|
+
traceId: link.traceId,
|
|
1459
|
+
spanId: link.spanId
|
|
1460
|
+
})),
|
|
1461
|
+
status: wireStatusOf(record.status)
|
|
1462
|
+
});
|
|
1463
|
+
const WireAnyValue = Schema.Struct({
|
|
1464
|
+
stringValue: Schema.optional(Schema.String),
|
|
1465
|
+
boolValue: Schema.optional(Schema.Boolean),
|
|
1466
|
+
intValue: Schema.optional(Schema.String),
|
|
1467
|
+
doubleValue: Schema.optional(Schema.Finite),
|
|
1468
|
+
arrayValue: Schema.optional(Schema.Struct({ values: Schema.optional(Schema.Array(Schema.suspend(() => WireAnyValue))) }))
|
|
1469
|
+
});
|
|
1470
|
+
const WireAttribute = Schema.Struct({
|
|
1471
|
+
key: Schema.String,
|
|
1472
|
+
value: Schema.optional(WireAnyValue)
|
|
1473
|
+
});
|
|
1474
|
+
const WireStatus = Schema.Struct({
|
|
1475
|
+
message: Schema.optional(Schema.String),
|
|
1476
|
+
code: Schema.optional(Schema.Literals([
|
|
1477
|
+
"STATUS_CODE_UNSET",
|
|
1478
|
+
"STATUS_CODE_OK",
|
|
1479
|
+
"STATUS_CODE_ERROR"
|
|
1480
|
+
]))
|
|
1481
|
+
});
|
|
1482
|
+
const WireEvent = Schema.Struct({
|
|
1483
|
+
name: Schema.String,
|
|
1484
|
+
timeUnixNano: Schema.optional(MillisFromNanos),
|
|
1485
|
+
attributes: Schema.optional(Schema.Array(WireAttribute))
|
|
1486
|
+
});
|
|
1487
|
+
const WireLink = Schema.Struct({
|
|
1488
|
+
traceId: Schema.optional(HexId),
|
|
1489
|
+
spanId: Schema.optional(HexId)
|
|
1490
|
+
});
|
|
1491
|
+
const WireSpan = Schema.Struct({
|
|
1492
|
+
traceId: HexId,
|
|
1493
|
+
spanId: HexId,
|
|
1494
|
+
parentSpanId: Schema.optional(HexId),
|
|
1495
|
+
name: Schema.String,
|
|
1496
|
+
kind: Schema.optional(Schema.String),
|
|
1497
|
+
startTimeUnixNano: MillisFromNanos,
|
|
1498
|
+
endTimeUnixNano: MillisFromNanos,
|
|
1499
|
+
attributes: Schema.optional(Schema.Array(WireAttribute)),
|
|
1500
|
+
events: Schema.optional(Schema.Array(WireEvent)),
|
|
1501
|
+
links: Schema.optional(Schema.Array(WireLink)),
|
|
1502
|
+
status: Schema.optional(WireStatus),
|
|
1503
|
+
traceState: Schema.optional(Schema.String),
|
|
1504
|
+
flags: Schema.optional(Schema.Finite)
|
|
1505
|
+
}).pipe(Schema.decodeTo(SpanRecord, SchemaTransformation.transform({
|
|
1506
|
+
decode: spanRecordOf,
|
|
1507
|
+
encode: wireSpanOf
|
|
1508
|
+
})));
|
|
1509
|
+
const WireScopeSpans = Schema.Struct({ spans: Schema.optional(Schema.Array(WireSpan)) });
|
|
1510
|
+
const WireResourceSpans = Schema.Struct({ scopeSpans: Schema.optional(Schema.Array(WireScopeSpans)) });
|
|
1511
|
+
/** One trace-by-id answer: the spans, plus the store's completeness status when it is not complete. */
|
|
1512
|
+
const TempoV2TraceResponse = Schema.Struct({
|
|
1513
|
+
trace: Schema.Struct({ resourceSpans: Schema.optional(Schema.Array(WireResourceSpans)) }),
|
|
1514
|
+
status: Schema.optional(Schema.String),
|
|
1515
|
+
message: Schema.optional(Schema.String)
|
|
1516
|
+
});
|
|
1517
|
+
//#endregion
|
|
1518
|
+
//#region src/tempo-trace-store.ts
|
|
1519
|
+
var tempo_trace_store_exports = /* @__PURE__ */ __exportAll({ source: () => source });
|
|
1520
|
+
/**
|
|
1521
|
+
* The Grafana Tempo trace source: one read of one trace by id against one
|
|
1522
|
+
* store endpoint. It never polls, waits, or retries — completeness belongs
|
|
1523
|
+
* to the remote observation layer. Any refusal (unreachable endpoint,
|
|
1524
|
+
* non-2xx answer, undecodable body) fails with the transport failure whose
|
|
1525
|
+
* `source` is the request URL; an answer the store marked incomplete fails
|
|
1526
|
+
* with the incomplete failure, since waiting cannot complete it.
|
|
1527
|
+
*/
|
|
1528
|
+
const parseBody = Schema.decodeResult(Schema.fromJsonString(TempoV2TraceResponse));
|
|
1529
|
+
const orEmpty = (values) => values ?? [];
|
|
1530
|
+
const spansOf = (response) => orEmpty(response.trace.resourceSpans).flatMap((resourceSpans) => orEmpty(resourceSpans.scopeSpans).flatMap((scopeSpans) => orEmpty(scopeSpans.spans)));
|
|
1531
|
+
const bodyOf = (decoded) => Result.match(decoded, {
|
|
1532
|
+
onFailure: (error) => new TempoBodyUndecodable({ detail: error.message }),
|
|
1533
|
+
onSuccess: (response) => new TempoBodyDecoded({
|
|
1534
|
+
spans: spansOf(response),
|
|
1535
|
+
status: response.status
|
|
1536
|
+
})
|
|
1537
|
+
});
|
|
1538
|
+
const transportFailureOf = (traceId, url, cause) => new TransportObservationError({
|
|
1539
|
+
traceId,
|
|
1540
|
+
source: url,
|
|
1541
|
+
detail: cause
|
|
1542
|
+
});
|
|
1543
|
+
const refusedOf = (traceId, url, error) => transportFailureOf(traceId, url, error.message);
|
|
1544
|
+
const undecodableOf = (traceId, url, detail) => transportFailureOf(traceId, url, `undecodable trace body: ${detail}`);
|
|
1545
|
+
const incompleteOf = (traceId, status, spanCount) => new IncompleteObservationError({
|
|
1546
|
+
traceId,
|
|
1547
|
+
spanCount,
|
|
1548
|
+
detail: `the store answered ${status}: waiting cannot complete the trace`
|
|
1549
|
+
});
|
|
1550
|
+
const readTrace = (traceId, url, body) => Effect.flatMap(Effect.fromResult(judgeTempoAnswer(new JudgeTempoAnswer({ body: bodyOf(parseBody(body)) }))), (decision) => Match.value(decision).pipe(Match.tag("TempoAnswerComplete", (complete) => Effect.succeed(complete.spans)), Match.tag("TempoAnswerIncomplete", (incomplete) => Effect.fail(incompleteOf(traceId, incomplete.status, incomplete.spanCount))), Match.tag("TempoAnswerUndecodable", (undecodable) => Effect.fail(undecodableOf(traceId, url, undecodable.detail))), Match.exhaustive));
|
|
1551
|
+
/** One read against one Tempo endpoint. Its only requirement is the caller's HTTP client. */
|
|
1552
|
+
const source = (options) => {
|
|
1553
|
+
const urlOf = (traceId) => `${options.baseUrl}/api/v2/traces/${traceId}`;
|
|
1554
|
+
return (traceId) => {
|
|
1555
|
+
const url = urlOf(traceId);
|
|
1556
|
+
return HttpClient.execute(HttpClientRequest.get(url).pipe(HttpClientRequest.acceptJson)).pipe(Effect.flatMap(HttpClientResponse.filterStatusOk), Effect.flatMap((response) => response.text), Effect.mapError((error) => refusedOf(traceId, url, error)), Effect.flatMap((body) => readTrace(traceId, url, body)));
|
|
1557
|
+
};
|
|
1558
|
+
};
|
|
1559
|
+
Schema.decodeEffect(HexId);
|
|
1560
|
+
Schema.encodeEffect(HexId);
|
|
1561
|
+
//#endregion
|
|
1562
1562
|
export { Contract_exports as Contract, Graph_exports as Graph, Observation_service_exports as Observation, observation_window_blueprint_exports as ObservationWindow, Rel_exports as Rel, RemoteObservation_exports as RemoteObservation, Stimulus_exports as Stimulus, Suite_exports as Suite, tempo_trace_store_exports as TempoTraceStore };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@systemfsoftware/trace-spec",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.4",
|
|
5
5
|
"author": "Ryan Lee <drdgvhbh@gmail.com>",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -33,44 +33,43 @@
|
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@effect/opentelemetry": "^4",
|
|
36
|
+
"@effect/opentelemetry": "^4.1.2",
|
|
37
37
|
"@opentelemetry/api": "^1.9.0",
|
|
38
|
-
"@opentelemetry/resources": "^2",
|
|
39
|
-
"@opentelemetry/sdk-trace-base": "^2",
|
|
40
|
-
"@opentelemetry/semantic-conventions": "^1.
|
|
41
|
-
"@systemfsoftware/effect-cell-types": "^12.
|
|
42
|
-
"@systemfsoftware/effect-spec-runtime": "^1.
|
|
43
|
-
"@systemfsoftware/trace-taxonomy": "^1.0.
|
|
38
|
+
"@opentelemetry/resources": "^2.12.0",
|
|
39
|
+
"@opentelemetry/sdk-trace-base": "^2.12.0",
|
|
40
|
+
"@opentelemetry/semantic-conventions": "^1.43.0",
|
|
41
|
+
"@systemfsoftware/effect-cell-types": "^12.1.4",
|
|
42
|
+
"@systemfsoftware/effect-spec-runtime": "^1.1.5",
|
|
43
|
+
"@systemfsoftware/trace-taxonomy": "^1.0.6"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@effect/platform-node": "^4",
|
|
46
|
+
"@effect/platform-node": "^4.1.2",
|
|
47
47
|
"@systemfsoftware/arethetypeswrong-cli": "^4.2.0",
|
|
48
|
-
"@systemfsoftware/conformance-spec": "^1.
|
|
49
|
-
"@systemfsoftware/effect-gherkin-spec": "^
|
|
50
|
-
"@systemfsoftware/oxlint-config-recommended": "^
|
|
51
|
-
"@systemfsoftware/stryker-config": "^0.1
|
|
48
|
+
"@systemfsoftware/conformance-spec": "^1.1.4",
|
|
49
|
+
"@systemfsoftware/effect-gherkin-spec": "^8.1.4",
|
|
50
|
+
"@systemfsoftware/oxlint-config-recommended": "^6.0.1",
|
|
51
|
+
"@systemfsoftware/stryker-config": "^0.2.1",
|
|
52
52
|
"@systemfsoftware/stryker-ignorer-effect-schema-declarations": "latest",
|
|
53
53
|
"@systemfsoftware/stryker-ignorer-in-source-vitest-block": "latest",
|
|
54
54
|
"@systemfsoftware/stryker-js": "latest",
|
|
55
55
|
"@systemfsoftware/stryker-js-typescript-checker": "latest",
|
|
56
56
|
"@systemfsoftware/stryker-js-vitest-runner": "latest",
|
|
57
|
-
"@systemfsoftware/
|
|
58
|
-
"@systemfsoftware/
|
|
59
|
-
"@systemfsoftware/
|
|
60
|
-
"@systemfsoftware/vitest": "^
|
|
61
|
-
"@
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"oxlint": "~1.82.0",
|
|
57
|
+
"@systemfsoftware/tsconfig": "^2.1.0",
|
|
58
|
+
"@systemfsoftware/tsdown-config": "^0.1.2",
|
|
59
|
+
"@systemfsoftware/vitest": "^4.1.1",
|
|
60
|
+
"@systemfsoftware/vitest-config": "^0.1.3",
|
|
61
|
+
"@types/node": "^26.6.5",
|
|
62
|
+
"effect": "^4.1.2",
|
|
63
|
+
"oxlint": "~1.89.0",
|
|
65
64
|
"rimraf": "^6.1.3",
|
|
66
65
|
"tsdown": "^0.23.0",
|
|
67
66
|
"tstyche": "^7.2.5",
|
|
68
|
-
"vitest": "^5"
|
|
67
|
+
"vitest": "^5.1.0"
|
|
69
68
|
},
|
|
70
69
|
"peerDependencies": {
|
|
71
|
-
"@systemfsoftware/vitest": "^
|
|
72
|
-
"effect": "^4",
|
|
73
|
-
"vitest": "^5"
|
|
70
|
+
"@systemfsoftware/vitest": "^4.1.1",
|
|
71
|
+
"effect": "^4.1.2",
|
|
72
|
+
"vitest": "^5.1.0"
|
|
74
73
|
},
|
|
75
74
|
"publishConfig": {
|
|
76
75
|
"access": "public",
|
|
@@ -82,7 +81,6 @@
|
|
|
82
81
|
"typecheck": "tsc -b",
|
|
83
82
|
"test": "vitest run",
|
|
84
83
|
"mutation": "stryker run",
|
|
85
|
-
"mutation:full": "stryker run --force",
|
|
86
84
|
"test:types": "TSTYCHE_TYPESCRIPT_MODULE=tstyche-typescript tstyche",
|
|
87
85
|
"lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}",
|
|
88
86
|
"attw": "attw --pack ."
|