@systemfsoftware/trace-spec 0.2.1 → 0.3.0
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 +13 -0
- package/dist/index.d.ts +26 -20
- package/dist/index.mjs +52 -37
- package/package.json +7 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @systemfsoftware/trace-spec
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- The observation window is built on the `Blueprint` and `Handle` kinds, with unchanged behaviour. The blueprint's `TypeId` is now exported as a value.
|
|
12
|
+
|
|
13
|
+
- Updated dependencies:
|
|
14
|
+
- @systemfsoftware/effect-spec-runtime@0.2.0
|
|
15
|
+
|
|
3
16
|
## 0.2.1
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Pipeable } from "effect/Pipeable";
|
|
|
3
3
|
import { Span, Taxonomy } from "@systemfsoftware/trace-taxonomy";
|
|
4
4
|
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
5
5
|
import * as OtelTracer from "@effect/opentelemetry/OtelTracer";
|
|
6
|
+
import { Blueprint, Handle } from "@systemfsoftware/effect-cell-types";
|
|
6
7
|
import { BasicTracerProvider, InMemorySpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
7
8
|
import { Suite } from "@systemfsoftware/effect-spec-runtime";
|
|
8
9
|
import * as Scope from "effect/Scope";
|
|
@@ -320,7 +321,7 @@ declare const check: {
|
|
|
320
321
|
<Input, Output, E, R>(self: Contract<Input, Output, E, R>, input: Input, options?: CheckOptions): Effect.Effect<Judgment<Input, Output>, CheckFailure<E>, Services<R>>;
|
|
321
322
|
};
|
|
322
323
|
declare namespace RemoteObservation_d_exports {
|
|
323
|
-
export { Options, TraceSource, layer };
|
|
324
|
+
export { Options, TraceSource, layer$1 as layer };
|
|
324
325
|
}
|
|
325
326
|
/**
|
|
326
327
|
* The cadence of the poll, the quiet window the observed span set must hold
|
|
@@ -348,7 +349,7 @@ type TraceSource<R> = (traceId: string) => Effect.Effect<ReadonlyArray<SpanRecor
|
|
|
348
349
|
* the source requires (`Scope.Scope` is supplied by the layer machinery, as
|
|
349
350
|
* every Effect layer constructor scopes it away).
|
|
350
351
|
*/
|
|
351
|
-
declare const layer: {
|
|
352
|
+
declare const layer$1: {
|
|
352
353
|
(options: Options): <R>(source: TraceSource<R>) => Layer.Layer<Observation, never, Exclude<R, Scope.Scope>>;
|
|
353
354
|
<R>(source: TraceSource<R>, options: Options): Layer.Layer<Observation, never, Exclude<R, Scope.Scope>>;
|
|
354
355
|
};
|
|
@@ -370,15 +371,19 @@ declare namespace observation_window_handle_d_exports {
|
|
|
370
371
|
}
|
|
371
372
|
declare const TypeId$2: unique symbol;
|
|
372
373
|
type TypeId$2 = typeof TypeId$2;
|
|
373
|
-
declare const
|
|
374
|
-
declare const ProviderId: unique symbol;
|
|
375
|
-
interface ObservationWindow extends Pipeable {
|
|
376
|
-
readonly [TypeId$2]: typeof TypeId$2;
|
|
377
|
-
readonly [ExporterId]: InMemorySpanExporter;
|
|
378
|
-
readonly [ProviderId]: BasicTracerProvider;
|
|
374
|
+
declare const ObservationWindow: Handle.Definition<typeof TypeId$2, {
|
|
379
375
|
readonly serviceName: string;
|
|
380
|
-
}
|
|
381
|
-
|
|
376
|
+
}, {
|
|
377
|
+
readonly exporter: InMemorySpanExporter;
|
|
378
|
+
readonly provider: BasicTracerProvider;
|
|
379
|
+
}, never>;
|
|
380
|
+
type ObservationWindow = Handle.Of<typeof ObservationWindow>;
|
|
381
|
+
declare const isObservationWindow: (u: unknown) => u is Handle.Handle<typeof TypeId$2, {
|
|
382
|
+
readonly serviceName: string;
|
|
383
|
+
}, {
|
|
384
|
+
readonly exporter: InMemorySpanExporter;
|
|
385
|
+
readonly provider: BasicTracerProvider;
|
|
386
|
+
}, never>;
|
|
382
387
|
declare const make$2: (spec: ObservationWindowSpec) => ObservationWindow;
|
|
383
388
|
declare const shutdown: (self: ObservationWindow) => Effect.Effect<void>;
|
|
384
389
|
declare const collect: {
|
|
@@ -386,18 +391,19 @@ declare const collect: {
|
|
|
386
391
|
(self: ObservationWindow, traceId: string): Effect.Effect<ReadonlyArray<SpanRecord>, EmptyObservationError>;
|
|
387
392
|
};
|
|
388
393
|
declare const context: (self: ObservationWindow) => Context.Context<Observation | OtelTracer.OtelTracerProvider>;
|
|
389
|
-
declare namespace
|
|
390
|
-
export { ObservationWindow,
|
|
394
|
+
declare namespace observation_window_blueprint_d_exports {
|
|
395
|
+
export { ObservationWindow, ObservationWindowBlueprint, ObservationWindowSpec, TypeId$2 as ObservationWindowTypeId, TypeId$1 as TypeId, collect, isObservationWindow, make$1 as make };
|
|
391
396
|
}
|
|
392
397
|
declare const TypeId$1: unique symbol;
|
|
393
398
|
type TypeId$1 = typeof TypeId$1;
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
readonly scoped:
|
|
398
|
-
readonly layer:
|
|
399
|
-
}
|
|
400
|
-
|
|
399
|
+
declare const scoped: (spec: ObservationWindowSpec) => Effect.Effect<ObservationWindow, never, Scope.Scope>;
|
|
400
|
+
declare const layer: (spec: ObservationWindowSpec) => Layer.Layer<Observation | OtelTracer.OtelTracer>;
|
|
401
|
+
declare const ObservationWindows: Blueprint.Definition<typeof TypeId$1, ObservationWindowSpec, Blueprint.Derived<ObservationWindowSpec, {}, {
|
|
402
|
+
readonly scoped: typeof scoped;
|
|
403
|
+
readonly layer: typeof layer;
|
|
404
|
+
}>, ObservationWindowSpec>;
|
|
405
|
+
type ObservationWindowBlueprint = Blueprint.Of<typeof ObservationWindows>;
|
|
406
|
+
declare const make$1: (serviceName: string) => ObservationWindowBlueprint;
|
|
401
407
|
//#endregion
|
|
402
408
|
//#region src/HarnessFailure.schema.d.ts
|
|
403
409
|
declare const HarnessFailure: Schema.Union<readonly [typeof ContractDecodeError, typeof EmptyObservationError, typeof IncompleteObservationError, typeof TransportObservationError, typeof TraceDisparityError]>;
|
|
@@ -447,4 +453,4 @@ declare const withScenarioLayer: {
|
|
|
447
453
|
};
|
|
448
454
|
declare const make: (bindings: Suite.Bindings) => (name: string) => Declared;
|
|
449
455
|
//#endregion
|
|
450
|
-
export { Contract_d_exports as Contract, Graph_d_exports as Graph, Observation_service_d_exports as Observation,
|
|
456
|
+
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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { recordAssertion } from "@effect/vitest";
|
|
2
3
|
import { Array as Array$1, Cause, Clock, Context, Duration, Effect, Encoding, Equal, FileSystem, Layer, Match, Option, Predicate, Ref, Result, Schema, SchemaGetter, SchemaTransformation } from "effect";
|
|
3
4
|
import { dual } from "effect/Function";
|
|
4
5
|
import { Prototype } from "effect/Pipeable";
|
|
@@ -9,6 +10,7 @@ import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
|
9
10
|
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
10
11
|
import * as OtelTracer from "@effect/opentelemetry/OtelTracer";
|
|
11
12
|
import * as OtelResource from "@effect/opentelemetry/Resource";
|
|
13
|
+
import { Blueprint, Handle } from "@systemfsoftware/effect-cell-types";
|
|
12
14
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
13
15
|
import { AlwaysOnSampler, BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
14
16
|
import { Suite, TaskRef } from "@systemfsoftware/effect-spec-runtime";
|
|
@@ -326,7 +328,7 @@ const refuseBreak = (relationId, judgment) => Match.value(judgment.verdict).pipe
|
|
|
326
328
|
breaks: [breach],
|
|
327
329
|
dumpPath: judgment.dumpPath
|
|
328
330
|
}))), Match.tag("Hold", () => Effect.succeed(judgment)), Match.exhaustive);
|
|
329
|
-
const checkDual = (self, input, options) => Effect.flatMap(judgeDual(self, input, options), (judgment) => refuseBreak(self.relation.id, judgment));
|
|
331
|
+
const checkDual = (self, input, options) => Effect.flatMap(judgeDual(self, input, options), (judgment) => Effect.andThen(Effect.sync(recordAssertion), refuseBreak(self.relation.id, judgment)));
|
|
330
332
|
const check = dual((args) => isContract(args[0]), checkDual);
|
|
331
333
|
//#endregion
|
|
332
334
|
//#region src/drivers/tempo-trace.schema.ts
|
|
@@ -544,24 +546,20 @@ Schema.encodeEffect(HexId);
|
|
|
544
546
|
//#endregion
|
|
545
547
|
//#region src/observation-window.handle.ts
|
|
546
548
|
const TypeId$2 = Symbol.for("~systemfsoftware/trace-spec/ObservationWindow");
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
const isObservationWindow = (u) => Predicate.hasProperty(u, TypeId$2);
|
|
549
|
+
const ObservationWindow = Handle.make()(TypeId$2);
|
|
550
|
+
const isObservationWindow = ObservationWindow.is;
|
|
550
551
|
const make$3 = (spec) => {
|
|
551
552
|
const exporter = new InMemorySpanExporter();
|
|
552
553
|
const provider = new BasicTracerProvider({
|
|
553
554
|
sampler: new AlwaysOnSampler(),
|
|
554
555
|
spanProcessors: [new SimpleSpanProcessor(exporter)]
|
|
555
556
|
});
|
|
556
|
-
return {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
serviceName: spec.serviceName,
|
|
561
|
-
...Prototype
|
|
562
|
-
};
|
|
557
|
+
return ObservationWindow.make({ serviceName: spec.serviceName }, {
|
|
558
|
+
exporter,
|
|
559
|
+
provider
|
|
560
|
+
});
|
|
563
561
|
};
|
|
564
|
-
const shutdown = (self) => Effect.promise(() => self
|
|
562
|
+
const shutdown = (self) => Effect.promise(() => ObservationWindow.slot(self).provider.shutdown());
|
|
565
563
|
const SCALAR_TYPES = {
|
|
566
564
|
string: true,
|
|
567
565
|
number: true,
|
|
@@ -616,39 +614,36 @@ const emptyObservation = (traceId) => new EmptyObservationError({
|
|
|
616
614
|
traceId,
|
|
617
615
|
detail: "the observation window closed with no span carrying the stimulated trace id"
|
|
618
616
|
});
|
|
619
|
-
const recordsOf = (self, traceId) => self
|
|
617
|
+
const recordsOf = (self, traceId) => ObservationWindow.slot(self).exporter.getFinishedSpans().filter((span) => span.spanContext().traceId === traceId).map(spanRecordOf);
|
|
620
618
|
const collect = dual(2, (self, traceId) => Effect.suspend(() => {
|
|
621
619
|
const records = recordsOf(self, traceId);
|
|
622
620
|
return records.length > 0 ? Effect.succeed(records) : Effect.fail(emptyObservation(traceId));
|
|
623
621
|
}));
|
|
624
|
-
const context = (self) => Context.make(Observation, { collect: (traceId) => collect(self, traceId) }).pipe(Context.add(OtelTracer.OtelTracerProvider, self
|
|
622
|
+
const context = (self) => Context.make(Observation, { collect: (traceId) => collect(self, traceId) }).pipe(Context.add(OtelTracer.OtelTracerProvider, ObservationWindow.slot(self).provider));
|
|
625
623
|
//#endregion
|
|
626
624
|
//#region src/ObservationWindowSpec.schema.ts
|
|
627
625
|
var ObservationWindowSpec = class extends Schema.Class("ObservationWindowSpec")({ serviceName: Schema.NonEmptyString }) {};
|
|
628
626
|
//#endregion
|
|
629
|
-
//#region src/observation-window.
|
|
630
|
-
var
|
|
627
|
+
//#region src/observation-window.blueprint.ts
|
|
628
|
+
var observation_window_blueprint_exports = /* @__PURE__ */ __exportAll({
|
|
631
629
|
ObservationWindowSpec: () => ObservationWindowSpec,
|
|
632
630
|
ObservationWindowTypeId: () => TypeId$2,
|
|
631
|
+
TypeId: () => TypeId$1,
|
|
633
632
|
collect: () => collect,
|
|
634
633
|
isObservationWindow: () => isObservationWindow,
|
|
635
634
|
make: () => make$2
|
|
636
635
|
});
|
|
637
|
-
const TypeId$1 = Symbol.for("~systemfsoftware/trace-spec/
|
|
636
|
+
const TypeId$1 = Symbol.for("~systemfsoftware/trace-spec/ObservationWindowBlueprint");
|
|
638
637
|
const scoped = (spec) => Effect.acquireRelease(Effect.sync(() => make$3(spec)), shutdown);
|
|
639
638
|
const layer$1 = (spec) => OtelTracer.layer.pipe(Layer.provideMerge(OtelResource.layer({ serviceName: spec.serviceName })), Layer.provideMerge(Layer.effectContext(Effect.map(scoped(spec), context))));
|
|
640
|
-
const
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
}
|
|
646
|
-
get layer() {
|
|
647
|
-
return layer$1(spec);
|
|
648
|
-
},
|
|
649
|
-
...Prototype
|
|
639
|
+
const ObservationWindows = Blueprint.make()(TypeId$1).steps({
|
|
640
|
+
steps: {},
|
|
641
|
+
targets: {
|
|
642
|
+
scoped,
|
|
643
|
+
layer: layer$1
|
|
644
|
+
}
|
|
650
645
|
});
|
|
651
|
-
const make$2 = (serviceName) =>
|
|
646
|
+
const make$2 = (serviceName) => ObservationWindows.of(new ObservationWindowSpec({ serviceName }));
|
|
652
647
|
//#endregion
|
|
653
648
|
//#region src/Rel.ts
|
|
654
649
|
var Rel_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -1087,18 +1082,35 @@ const HarnessFailure = Schema.Union([
|
|
|
1087
1082
|
//#region src/Prop.ts
|
|
1088
1083
|
const isHold = Schema.is(Hold);
|
|
1089
1084
|
const isBreak = Schema.is(Break);
|
|
1090
|
-
const annotate = (
|
|
1091
|
-
|
|
1092
|
-
|
|
1085
|
+
const annotate = (message) => Effect.gen(function* () {
|
|
1086
|
+
const task = yield* TaskRef.RawVitestTaskRef;
|
|
1087
|
+
return yield* Option.match(Option.fromNullishOr(task?.annotate), {
|
|
1088
|
+
onNone: () => Effect.void,
|
|
1089
|
+
onSome: (record) => Effect.promise(() => Promise.resolve(record(message, "info")))
|
|
1090
|
+
});
|
|
1093
1091
|
});
|
|
1094
1092
|
const dumpMessageOf = (verdict, dumpPath) => Option.map(Option.flatMap(Option.liftPredicate(verdict, isBreak), () => Option.fromNullishOr(dumpPath)), (path) => `trace contract failed; observed graph dumped to ${path}`);
|
|
1095
|
-
const announce = (
|
|
1093
|
+
const announce = (verdict, dumpPath) => Option.match(dumpMessageOf(verdict, dumpPath), {
|
|
1096
1094
|
onNone: () => Effect.void,
|
|
1097
|
-
onSome: (message) => annotate(
|
|
1095
|
+
onSome: (message) => annotate(message)
|
|
1096
|
+
});
|
|
1097
|
+
/**
|
|
1098
|
+
* The contract with its stimulus replaced by the function the property was handed: the impostor gate
|
|
1099
|
+
* swaps in a constant fake, so the judgement must reach the stimulus through the argument, never the
|
|
1100
|
+
* closure.
|
|
1101
|
+
*/
|
|
1102
|
+
const judgedOver = (contract, subject) => ({
|
|
1103
|
+
...contract,
|
|
1104
|
+
stimulus: subject
|
|
1098
1105
|
});
|
|
1106
|
+
/**
|
|
1107
|
+
* The run must be the run of the generated input: a stimulus that ignores its input and answers with one
|
|
1108
|
+
* frozen run satisfies every relation about that run, so the input pin is what refutes it.
|
|
1109
|
+
*/
|
|
1110
|
+
const consumedInput = (judgment, input) => judgment.run.input === input;
|
|
1099
1111
|
const predicateImpl = (title, contract, scenario) => {
|
|
1100
|
-
const checked = (
|
|
1101
|
-
return (
|
|
1112
|
+
const checked = (subject, input) => judge(judgedOver(contract, subject), input, { dumpName: title }).pipe(Effect.tap((judgment) => announce(judgment.verdict, judgment.dumpPath)), Effect.map((judgment) => isHold(judgment.verdict) && consumedInput(judgment, input)), Effect.provide(Layer.fresh(scenario)));
|
|
1113
|
+
return (subject, input) => checked(subject, input);
|
|
1102
1114
|
};
|
|
1103
1115
|
const predicate = dual(3, predicateImpl);
|
|
1104
1116
|
//#endregion
|
|
@@ -1146,7 +1158,10 @@ const caseBody = (contract, input) => check(contract, input).pipe(Effect.catchIf
|
|
|
1146
1158
|
const caseTools = (register, propIt, scenario) => {
|
|
1147
1159
|
const Case = (name, contract, input) => register(name, caseBody(contract, input), "run");
|
|
1148
1160
|
Case.prop = (name, contract, arbitrary) => {
|
|
1149
|
-
propIt.effect.prop(name,
|
|
1161
|
+
propIt.effect.prop(name, {
|
|
1162
|
+
of: [arbitrary],
|
|
1163
|
+
subject: contract.stimulus
|
|
1164
|
+
}, (subject, values) => predicate(name, contract, scenario)(subject, values[0]));
|
|
1150
1165
|
};
|
|
1151
1166
|
return { Case };
|
|
1152
1167
|
};
|
|
@@ -1181,4 +1196,4 @@ const make = (bindings) => (name) => ({
|
|
|
1181
1196
|
...Prototype
|
|
1182
1197
|
});
|
|
1183
1198
|
//#endregion
|
|
1184
|
-
export { Contract_exports as Contract, Graph_exports as Graph, Observation_service_exports as Observation,
|
|
1199
|
+
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": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"author": "Ryan Lee <drdgvhbh@gmail.com>",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -38,15 +38,16 @@
|
|
|
38
38
|
"@opentelemetry/resources": "^2",
|
|
39
39
|
"@opentelemetry/sdk-trace-base": "^2",
|
|
40
40
|
"@opentelemetry/semantic-conventions": "^1.33",
|
|
41
|
-
"@systemfsoftware/effect-
|
|
41
|
+
"@systemfsoftware/effect-cell-types": "^10.2.0",
|
|
42
|
+
"@systemfsoftware/effect-spec-runtime": "^0.2.0",
|
|
42
43
|
"@systemfsoftware/trace-taxonomy": "^0.0.1"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@effect/platform-node": "4.0.0-rc.117",
|
|
46
|
-
"@effect/vitest": "
|
|
47
|
+
"@effect/vitest": "npm:@systemfsoftware/vitest@0.0.0",
|
|
47
48
|
"@systemfsoftware/arethetypeswrong-cli": "^4.2.0",
|
|
48
|
-
"@systemfsoftware/effect-gherkin-spec": "^4.
|
|
49
|
-
"@systemfsoftware/oxlint-config-recommended": "^
|
|
49
|
+
"@systemfsoftware/effect-gherkin-spec": "^4.4.0",
|
|
50
|
+
"@systemfsoftware/oxlint-config-recommended": "^3.0.0",
|
|
50
51
|
"@systemfsoftware/tsconfig": "^2.0.1",
|
|
51
52
|
"@systemfsoftware/tsdown-config": "^0.1.0",
|
|
52
53
|
"@systemfsoftware/vitest-config": "^0.1.0",
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
"vitest": "^5"
|
|
60
61
|
},
|
|
61
62
|
"peerDependencies": {
|
|
62
|
-
"@effect/vitest": "
|
|
63
|
+
"@effect/vitest": "npm:@systemfsoftware/vitest@*",
|
|
63
64
|
"effect": "4.0.0-rc.117",
|
|
64
65
|
"vitest": "^5"
|
|
65
66
|
},
|