@systemfsoftware/trace-spec 0.0.0 → 0.1.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 +11 -0
- package/dist/index.d.ts +60 -16
- package/dist/index.mjs +40 -33
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @systemfsoftware/trace-spec
|
|
2
2
|
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- `Rel`, `Graph`, `FailureDump` and `Prop` functions that took their subject first can now also be called data-last inside `pipe`. Existing calls keep working.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies:
|
|
12
|
+
- @systemfsoftware/effect-spec-runtime@0.1.0
|
|
13
|
+
|
|
3
14
|
## 0.0.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -104,9 +104,18 @@ type TraceGraph = typeof TraceGraph.Type;
|
|
|
104
104
|
declare namespace Graph_d_exports {
|
|
105
105
|
export { AttributeValue, Attributes, GraphNode, SpanEvent, SpanLink, SpanRecord, Status, TraceGraph, byId, children, decode, descendants };
|
|
106
106
|
}
|
|
107
|
-
declare const byId:
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
declare const byId: {
|
|
108
|
+
(spec: Span.Span): (graph: TraceGraph) => ReadonlyArray<GraphNode>;
|
|
109
|
+
(graph: TraceGraph, spec: Span.Span): ReadonlyArray<GraphNode>;
|
|
110
|
+
};
|
|
111
|
+
declare const children: {
|
|
112
|
+
(node: GraphNode): (graph: TraceGraph) => ReadonlyArray<GraphNode>;
|
|
113
|
+
(graph: TraceGraph, node: GraphNode): ReadonlyArray<GraphNode>;
|
|
114
|
+
};
|
|
115
|
+
declare const descendants: {
|
|
116
|
+
(node: GraphNode): (graph: TraceGraph) => ReadonlyArray<GraphNode>;
|
|
117
|
+
(graph: TraceGraph, node: GraphNode): ReadonlyArray<GraphNode>;
|
|
118
|
+
};
|
|
110
119
|
/**
|
|
111
120
|
* Decodes the recorded spans of one trace against a taxonomy, purely: a declared span
|
|
112
121
|
* whose required attributes do not decode refuses with {@link ContractDecodeError}
|
|
@@ -114,7 +123,10 @@ declare const descendants: (graph: TraceGraph, node: GraphNode) => ReadonlyArray
|
|
|
114
123
|
* the graph as recorded. Failure is a `Result`, so the phase that reads the trace cannot
|
|
115
124
|
* fail the pipeline by accident.
|
|
116
125
|
*/
|
|
117
|
-
declare const decode:
|
|
126
|
+
declare const decode: {
|
|
127
|
+
(spans: ReadonlyArray<SpanRecord>, taxonomy: Taxonomy.Taxonomy): (traceId: string) => Result.Result<TraceGraph, ContractDecodeError>;
|
|
128
|
+
(traceId: string, spans: ReadonlyArray<SpanRecord>, taxonomy: Taxonomy.Taxonomy): Result.Result<TraceGraph, ContractDecodeError>;
|
|
129
|
+
};
|
|
118
130
|
declare namespace Observation_service_d_exports {
|
|
119
131
|
export { Collector, EmptyObservationError, Observation };
|
|
120
132
|
}
|
|
@@ -156,22 +168,54 @@ interface Relation {
|
|
|
156
168
|
declare const exists: <S extends Span.Span>(spec: S) => Relation;
|
|
157
169
|
declare const absent: <S extends Span.Span>(spec: S) => Relation;
|
|
158
170
|
declare const unique: <S extends Span.Span>(spec: S) => Relation;
|
|
159
|
-
declare const child:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
declare const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
declare const
|
|
171
|
+
declare const child: {
|
|
172
|
+
(childSpan: Span.Span): (parent: Span.Span) => Relation;
|
|
173
|
+
(parent: Span.Span, childSpan: Span.Span): Relation;
|
|
174
|
+
};
|
|
175
|
+
declare const descendant: {
|
|
176
|
+
(childSpan: Span.Span): (parent: Span.Span) => Relation;
|
|
177
|
+
(parent: Span.Span, childSpan: Span.Span): Relation;
|
|
178
|
+
};
|
|
179
|
+
declare const order: {
|
|
180
|
+
(after: Span.Span): (before: Span.Span) => Relation;
|
|
181
|
+
(before: Span.Span, after: Span.Span): Relation;
|
|
182
|
+
};
|
|
183
|
+
declare const status: {
|
|
184
|
+
(expected: Status): (spec: Span.Span) => Relation;
|
|
185
|
+
(spec: Span.Span, expected: Status): Relation;
|
|
186
|
+
};
|
|
187
|
+
declare const errorType: {
|
|
188
|
+
(expected: string): (spec: Span.Span) => Relation;
|
|
189
|
+
(spec: Span.Span, expected: string): Relation;
|
|
190
|
+
};
|
|
191
|
+
declare const attrs: {
|
|
192
|
+
<S extends Span.Span>(partial: Partial<Span.AttrsOf<S>>): (spec: S) => Relation;
|
|
193
|
+
<S extends Span.Span>(spec: S, partial: Partial<Span.AttrsOf<S>>): Relation;
|
|
194
|
+
};
|
|
195
|
+
declare const durationLessThan: {
|
|
196
|
+
(millis: number): (spec: Span.Span) => Relation;
|
|
197
|
+
(spec: Span.Span, millis: number): Relation;
|
|
198
|
+
};
|
|
199
|
+
declare const event: {
|
|
200
|
+
(name: string): (spec: Span.Span) => Relation;
|
|
201
|
+
(spec: Span.Span, name: string): Relation;
|
|
202
|
+
};
|
|
203
|
+
declare const forall: {
|
|
204
|
+
(predicate: (node: GraphNode) => boolean, detail: string): (spec: Span.Span) => Relation;
|
|
205
|
+
(spec: Span.Span, predicate: (node: GraphNode) => boolean, detail: string): Relation;
|
|
206
|
+
};
|
|
168
207
|
declare const soft: (self: Relation) => Relation;
|
|
169
208
|
declare const not: (self: Relation) => Relation;
|
|
170
209
|
declare const all: (...relations: ReadonlyArray<Relation>) => Relation;
|
|
171
210
|
declare const any: (...relations: ReadonlyArray<Relation>) => Relation;
|
|
172
|
-
declare const fromTaxonomy:
|
|
173
|
-
|
|
174
|
-
|
|
211
|
+
declare const fromTaxonomy: {
|
|
212
|
+
(options: {
|
|
213
|
+
readonly path: string;
|
|
214
|
+
}): (taxonomy: Taxonomy.Taxonomy) => Relation;
|
|
215
|
+
(taxonomy: Taxonomy.Taxonomy, options: {
|
|
216
|
+
readonly path: string;
|
|
217
|
+
}): Relation;
|
|
218
|
+
};
|
|
175
219
|
declare namespace Stimulus_d_exports {
|
|
176
220
|
export { Run, Stimulus, TraceContext, make$3 as make, traceContext };
|
|
177
221
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -61,15 +61,16 @@ const renderNode = (spans, node, depth) => {
|
|
|
61
61
|
...childrenOf(spans, node.spanId).map((child) => renderNode(spans, child, depth + 1))
|
|
62
62
|
]].join("\n");
|
|
63
63
|
};
|
|
64
|
-
|
|
65
|
-
* The dump document: the trace it belongs to, the break that refused it, and the recorded
|
|
66
|
-
* spans as the observation window saw them.
|
|
67
|
-
*/
|
|
68
|
-
const document = (observed, report) => [
|
|
64
|
+
const documentImpl = (observed, report) => [
|
|
69
65
|
`trace ${observed.traceId}`,
|
|
70
66
|
report,
|
|
71
67
|
...observed.spans.map((node) => renderNode(observed.spans, node, 0))
|
|
72
68
|
].join("\n");
|
|
69
|
+
/**
|
|
70
|
+
* The dump document: the trace it belongs to, the break that refused it, and the recorded
|
|
71
|
+
* spans as the observation window saw them.
|
|
72
|
+
*/
|
|
73
|
+
const document = dual(2, documentImpl);
|
|
73
74
|
const sanitize = (value) => value.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
74
75
|
const fileNameOf = (request) => `${sanitize(request.name ?? request.traceId)}.${sanitize(request.conjunct)}.txt`;
|
|
75
76
|
/**
|
|
@@ -156,12 +157,15 @@ var Graph_exports = /* @__PURE__ */ __exportAll({
|
|
|
156
157
|
decode: () => decode,
|
|
157
158
|
descendants: () => descendants
|
|
158
159
|
});
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
const
|
|
160
|
+
const byIdImpl = (graph, spec) => graph.nodes.filter((node) => node.name === spec.name);
|
|
161
|
+
const byId = dual(2, byIdImpl);
|
|
162
|
+
const childrenImpl = (graph, node) => graph.nodes.filter((candidate) => candidate.parentSpanId === node.spanId);
|
|
163
|
+
const children = dual(2, childrenImpl);
|
|
164
|
+
const descendantsImpl = (graph, node) => {
|
|
162
165
|
const direct = children(graph, node);
|
|
163
166
|
return [...direct, ...direct.flatMap((child) => descendants(graph, child))];
|
|
164
167
|
};
|
|
168
|
+
const descendants = dual(2, descendantsImpl);
|
|
165
169
|
const indexDeclarations = (taxonomy) => new Map(taxonomy.spans.map((span) => [span.name, span]));
|
|
166
170
|
const keyOfPath = (path) => {
|
|
167
171
|
const last = path.at(-1);
|
|
@@ -204,6 +208,10 @@ const graphOf = (traceId, nodes) => ({
|
|
|
204
208
|
traceId,
|
|
205
209
|
nodes
|
|
206
210
|
});
|
|
211
|
+
const decodeImpl = (traceId, spans, taxonomy) => {
|
|
212
|
+
const declarations = indexDeclarations(taxonomy);
|
|
213
|
+
return Result.map(Result.all(spans.map((record) => declaredNodeOf(record, declarations))), (nodes) => graphOf(traceId, nodes));
|
|
214
|
+
};
|
|
207
215
|
/**
|
|
208
216
|
* Decodes the recorded spans of one trace against a taxonomy, purely: a declared span
|
|
209
217
|
* whose required attributes do not decode refuses with {@link ContractDecodeError}
|
|
@@ -211,10 +219,7 @@ const graphOf = (traceId, nodes) => ({
|
|
|
211
219
|
* the graph as recorded. Failure is a `Result`, so the phase that reads the trace cannot
|
|
212
220
|
* fail the pipeline by accident.
|
|
213
221
|
*/
|
|
214
|
-
const decode = (
|
|
215
|
-
const declarations = indexDeclarations(taxonomy);
|
|
216
|
-
return Result.map(Result.all(spans.map((record) => declaredNodeOf(record, declarations))), (nodes) => graphOf(traceId, nodes));
|
|
217
|
-
};
|
|
222
|
+
const decode = dual(3, decodeImpl);
|
|
218
223
|
//#endregion
|
|
219
224
|
//#region src/Observation.service.ts
|
|
220
225
|
var Observation_service_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -497,65 +502,73 @@ const reachedUnder = (graph, parent, childSpan, reach) => {
|
|
|
497
502
|
const reached = reach(graph, matchedNodes(graph, parent));
|
|
498
503
|
return matchedNodes(graph, childSpan).some((node) => reached.has(node.spanId));
|
|
499
504
|
};
|
|
500
|
-
const
|
|
505
|
+
const childImpl = (parent, childSpan) => declared({
|
|
501
506
|
id: `child(${parent.id},${childSpan.id})`,
|
|
502
507
|
spans: [parent, childSpan],
|
|
503
508
|
softenable: true,
|
|
504
509
|
detail: `no ${childSpan.id} span is a direct child of a ${parent.id} span`,
|
|
505
510
|
holds: (graph) => reachedUnder(graph, parent, childSpan, childIdsOf)
|
|
506
511
|
});
|
|
507
|
-
const
|
|
512
|
+
const child = dual(2, childImpl);
|
|
513
|
+
const descendantImpl = (parent, childSpan) => declared({
|
|
508
514
|
id: `descendant(${parent.id},${childSpan.id})`,
|
|
509
515
|
spans: [parent, childSpan],
|
|
510
516
|
softenable: true,
|
|
511
517
|
detail: `no ${childSpan.id} span descends from a ${parent.id} span`,
|
|
512
518
|
holds: (graph) => reachedUnder(graph, parent, childSpan, descendantIdsOf)
|
|
513
519
|
});
|
|
514
|
-
const
|
|
520
|
+
const descendant = dual(2, descendantImpl);
|
|
521
|
+
const orderImpl = (before, after) => declared({
|
|
515
522
|
id: `order(${before.id},${after.id})`,
|
|
516
523
|
spans: [before, after],
|
|
517
524
|
softenable: true,
|
|
518
525
|
detail: `a ${after.id} span starts before every ${before.id} span`,
|
|
519
526
|
holds: (graph) => matchedAll(matchedNodes(graph, after), (node) => startsAfterSome(matchedNodes(graph, before), node))
|
|
520
527
|
});
|
|
521
|
-
const
|
|
528
|
+
const order = dual(2, orderImpl);
|
|
529
|
+
const statusImpl = (spec, expected) => declared({
|
|
522
530
|
id: `status(${spec.id},${expected})`,
|
|
523
531
|
spans: [spec],
|
|
524
532
|
softenable: true,
|
|
525
533
|
detail: `a ${spec.id} span is not ${expected}`,
|
|
526
534
|
holds: holdsEveryOf(spec, (node) => node.status === expected)
|
|
527
535
|
});
|
|
528
|
-
const
|
|
536
|
+
const status = dual(2, statusImpl);
|
|
537
|
+
const errorTypeImpl = (spec, expected) => declared({
|
|
529
538
|
id: `errorType(${spec.id},${expected})`,
|
|
530
539
|
spans: [spec],
|
|
531
540
|
softenable: true,
|
|
532
541
|
detail: `a ${spec.id} span carries a different error.type`,
|
|
533
542
|
holds: holdsEveryOf(spec, (node) => node.errorType === expected)
|
|
534
543
|
});
|
|
544
|
+
const errorType = dual(2, errorTypeImpl);
|
|
535
545
|
const partialMatches = (attributes, partial) => Object.entries(partial).filter(([, value]) => value !== void 0).every(([key, value]) => Equal.equals(attributes[key], value));
|
|
536
|
-
const
|
|
546
|
+
const attrsImpl = (spec, partial) => declared({
|
|
537
547
|
id: `attrs(${spec.id})`,
|
|
538
548
|
spans: [spec],
|
|
539
549
|
softenable: true,
|
|
540
550
|
detail: `a ${spec.id} span carries a different declared attribute`,
|
|
541
551
|
holds: holdsEveryOf(spec, (node) => partialMatches(node.attrs, partial))
|
|
542
552
|
});
|
|
543
|
-
const
|
|
553
|
+
const attrs = dual(2, attrsImpl);
|
|
554
|
+
const durationLessThanImpl = (spec, millis) => declared({
|
|
544
555
|
id: `durationLessThan(${spec.id},${millis})`,
|
|
545
556
|
spans: [spec],
|
|
546
557
|
softenable: true,
|
|
547
558
|
detail: `a ${spec.id} span exceeded ${millis}ms`,
|
|
548
559
|
holds: holdsEveryOf(spec, (node) => node.durationMillis < millis)
|
|
549
560
|
});
|
|
561
|
+
const durationLessThan = dual(2, durationLessThanImpl);
|
|
550
562
|
const carriesEvent = (name) => (node) => node.events.some((event) => event.name === name);
|
|
551
|
-
const
|
|
563
|
+
const eventImpl = (spec, name) => declared({
|
|
552
564
|
id: `event(${spec.id},${name})`,
|
|
553
565
|
spans: [spec],
|
|
554
566
|
softenable: true,
|
|
555
567
|
detail: `a ${spec.id} span carries no ${name} event`,
|
|
556
568
|
holds: holdsEveryOf(spec, carriesEvent(name))
|
|
557
569
|
});
|
|
558
|
-
const
|
|
570
|
+
const event = dual(2, eventImpl);
|
|
571
|
+
const forallImpl = (spec, predicate, detail) => {
|
|
559
572
|
const id = `forall(${spec.id})`;
|
|
560
573
|
return relation({
|
|
561
574
|
id,
|
|
@@ -572,6 +585,7 @@ const forall = (spec, predicate, detail) => {
|
|
|
572
585
|
}
|
|
573
586
|
});
|
|
574
587
|
};
|
|
588
|
+
const forall = dual(3, forallImpl);
|
|
575
589
|
const soft = (self) => self.softenable ? relation({
|
|
576
590
|
id: self.id,
|
|
577
591
|
soft: true,
|
|
@@ -667,12 +681,13 @@ const forbidsAt = (entry, path) => Option.match(entry.unless, {
|
|
|
667
681
|
onSome: (tag) => tag !== path
|
|
668
682
|
});
|
|
669
683
|
const forbiddenOn = (path) => (entry) => forbidsAt(entry, path) ? [absent(entry.span)] : [];
|
|
670
|
-
const
|
|
684
|
+
const fromTaxonomyImpl = (taxonomy, options) => combined({
|
|
671
685
|
id: `fromTaxonomy(${taxonomy.id},${options.path})`,
|
|
672
686
|
relations: [...taxonomy.edges.map(placementOf), ...taxonomy.forbidden.flatMap(forbiddenOn(options.path))],
|
|
673
687
|
soft: false,
|
|
674
688
|
softenable: false
|
|
675
689
|
});
|
|
690
|
+
const fromTaxonomy = dual(2, fromTaxonomyImpl);
|
|
676
691
|
//#endregion
|
|
677
692
|
//#region src/Stimulus.ts
|
|
678
693
|
var Stimulus_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -724,19 +739,11 @@ const announce = (context, verdict, dumpPath) => Option.match(dumpMessageOf(verd
|
|
|
724
739
|
onNone: () => Effect.void,
|
|
725
740
|
onSome: (message) => annotate(context, message)
|
|
726
741
|
});
|
|
727
|
-
|
|
728
|
-
* The generated-case predicate for one case: a factory that runs the contract's cell with
|
|
729
|
-
* the dump named after the case — so every failing draw overwrites one file and the last
|
|
730
|
-
* failing draw is the shrunk counterexample the runner reports — and answers the verdict as
|
|
731
|
-
* the boolean: `true` while the relation held, `false` on a break, falsifying the property.
|
|
732
|
-
* Only infrastructure refusals — a failing behaviour, an undecodable span, an empty
|
|
733
|
-
* observation — stay on the error channel. The scenario layer is built fresh per draw, so
|
|
734
|
-
* each draw owns its observation window.
|
|
735
|
-
*/
|
|
736
|
-
function predicate(title, contract, scenario) {
|
|
742
|
+
const predicateImpl = (title, contract, scenario) => {
|
|
737
743
|
const checked = (input, context) => judge(contract, input, { dumpName: title }).pipe(Effect.tap((judgment) => announce(context, judgment.verdict, judgment.dumpPath)), Effect.map((judgment) => isHold(judgment.verdict)), Effect.provide(Layer.fresh(scenario)));
|
|
738
744
|
return (input, context) => checked(input, context);
|
|
739
|
-
}
|
|
745
|
+
};
|
|
746
|
+
const predicate = dual(3, predicateImpl);
|
|
740
747
|
//#endregion
|
|
741
748
|
//#region src/StimulusFailure.schema.ts
|
|
742
749
|
var StimulusFailure = class extends Schema.TaggedError()("StimulusFailure", {
|
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.1.0",
|
|
5
5
|
"author": "Ryan Lee <drdgvhbh@gmail.com>",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -38,25 +38,25 @@
|
|
|
38
38
|
"@opentelemetry/resources": "^2",
|
|
39
39
|
"@opentelemetry/sdk-trace-base": "^2",
|
|
40
40
|
"@opentelemetry/semantic-conventions": "^1.33",
|
|
41
|
-
"@systemfsoftware/
|
|
42
|
-
"@systemfsoftware/
|
|
41
|
+
"@systemfsoftware/effect-spec-runtime": "^0.1.0",
|
|
42
|
+
"@systemfsoftware/trace-taxonomy": "^0.0.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@effect/platform-node": "4.0.0-rc.116",
|
|
46
46
|
"@effect/vitest": "4.0.0-rc.116",
|
|
47
47
|
"@systemfsoftware/arethetypeswrong-cli": "^4.2.0",
|
|
48
|
+
"@systemfsoftware/effect-gherkin-spec": "^4.3.0",
|
|
49
|
+
"@systemfsoftware/oxlint-config-recommended": "^2.0.0",
|
|
50
|
+
"@systemfsoftware/tsconfig": "^2.0.0",
|
|
51
|
+
"@systemfsoftware/tsdown-config": "^0.1.0",
|
|
52
|
+
"@systemfsoftware/vitest-config": "^0.1.0",
|
|
48
53
|
"@types/node": "^26",
|
|
49
54
|
"effect": "4.0.0-rc.116",
|
|
50
55
|
"oxlint": "~1.82.0",
|
|
51
56
|
"rimraf": "^6.1.3",
|
|
52
57
|
"tsdown": "^0.23.0",
|
|
53
58
|
"tstyche": "^7.2.5",
|
|
54
|
-
"vitest": "^5"
|
|
55
|
-
"@systemfsoftware/effect-gherkin-spec": "^4.2.3",
|
|
56
|
-
"@systemfsoftware/oxlint-config-recommended": "^1.1.1",
|
|
57
|
-
"@systemfsoftware/tsconfig": "^1.3.6",
|
|
58
|
-
"@systemfsoftware/tsdown-config": "^0.1.0",
|
|
59
|
-
"@systemfsoftware/vitest-config": "^0.1.0"
|
|
59
|
+
"vitest": "^5"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@effect/vitest": "4.0.0-rc.116",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"scripts": {
|
|
70
70
|
"clean": "rimraf dist",
|
|
71
71
|
"build": "tsdown -l warn",
|
|
72
|
-
"typecheck": "tsc
|
|
72
|
+
"typecheck": "tsc -b",
|
|
73
73
|
"test": "vitest run",
|
|
74
74
|
"test:types": "TSTYCHE_TYPESCRIPT_MODULE=tstyche-typescript tstyche",
|
|
75
75
|
"lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}",
|