@ontrails/trails 1.0.0-beta.30 → 1.0.0-beta.39
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 +153 -0
- package/README.md +3 -3
- package/package.json +17 -16
- package/src/app.ts +27 -6
- package/src/cli.ts +3 -2
- package/src/mcp-app.ts +2 -0
- package/src/mcp-options.ts +20 -5
- package/src/regrade/config.ts +152 -0
- package/src/regrade/history.ts +320 -0
- package/src/regrade/live-api-preserve.ts +8 -0
- package/src/regrade/plan-artifact.ts +265 -0
- package/src/release/index.ts +17 -0
- package/src/release/lock-roundtrip-smoke.ts +230 -0
- package/src/release/notes-cli.ts +171 -0
- package/src/release/notes.ts +390 -0
- package/src/release/pack-coherence.ts +1 -3
- package/src/release/smoke.ts +11 -1
- package/src/release/wayfinder-dogfood-smoke.ts +1 -1
- package/src/run-schema.ts +34 -1
- package/src/trails/compile.ts +4 -5
- package/src/trails/create-adapter.ts +19 -79
- package/src/trails/deprecate.ts +1 -1
- package/src/trails/guide.ts +5 -5
- package/src/trails/load-app.ts +47 -116
- package/src/trails/regrade.ts +3017 -27
- package/src/trails/release-smoke.ts +2 -1
- package/src/trails/revise.ts +1 -1
- package/src/trails/survey.ts +37 -50
- package/src/trails/topo-activation.ts +14 -385
- package/src/trails/topo-read-support.ts +28 -24
- package/src/trails/topo-store-support.ts +21 -12
- package/src/trails/topo-support.ts +1 -0
- package/src/trails/topo.ts +2 -2
- package/src/trails/validate.ts +1 -1
- package/src/trails/version-lifecycle-support.ts +11 -11
- package/src/trails/warden-guide.ts +9 -4
- package/src/trails/warden.ts +15 -0
- package/src/trails/wayfind.ts +64 -12
|
@@ -14,7 +14,8 @@ const releaseSmokeInputSchema = z.object({
|
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
const releaseSmokeCheckResultSchema = z.object({
|
|
17
|
-
check: z.enum(['packed-artifacts', 'wayfinder-dogfood']),
|
|
17
|
+
check: z.enum(['lock-roundtrip', 'packed-artifacts', 'wayfinder-dogfood']),
|
|
18
|
+
lockCount: z.number().optional(),
|
|
18
19
|
message: z.string(),
|
|
19
20
|
packageCount: z.number().optional(),
|
|
20
21
|
passed: z.literal(true),
|
package/src/trails/revise.ts
CHANGED
package/src/trails/survey.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { basename, extname, join } from 'node:path';
|
|
9
9
|
|
|
10
|
-
import type {
|
|
10
|
+
import type { Topo } from '@ontrails/core';
|
|
11
11
|
import {
|
|
12
12
|
deriveSafePath,
|
|
13
13
|
NotFoundError,
|
|
@@ -15,7 +15,12 @@ import {
|
|
|
15
15
|
trail,
|
|
16
16
|
ValidationError,
|
|
17
17
|
} from '@ontrails/core';
|
|
18
|
-
import type {
|
|
18
|
+
import type {
|
|
19
|
+
DiffEntry,
|
|
20
|
+
DiffResult,
|
|
21
|
+
TopoGraph,
|
|
22
|
+
TopoGraphOverlayRegistration,
|
|
23
|
+
} from '@ontrails/topographer';
|
|
19
24
|
import {
|
|
20
25
|
createTopoStore,
|
|
21
26
|
deriveTopoGraphDiff,
|
|
@@ -30,12 +35,12 @@ import { writeIsolatedExampleJsonFile } from '../local-state-io.js';
|
|
|
30
35
|
|
|
31
36
|
import { withFreshAppLease, withOperatorRootDir } from './operator-context.js';
|
|
32
37
|
import {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
deriveCurrentTopoBrief,
|
|
39
|
+
deriveCurrentTopoList,
|
|
40
|
+
deriveCurrentTopoMatches,
|
|
41
|
+
deriveCurrentTrailDetail,
|
|
42
|
+
deriveCurrentResourceDetail,
|
|
43
|
+
deriveCurrentSignalDetail,
|
|
39
44
|
readSurfaceLayerNamesFromContext,
|
|
40
45
|
} from './topo-read-support.js';
|
|
41
46
|
import {
|
|
@@ -506,13 +511,11 @@ const buildSurveyLookup = (
|
|
|
506
511
|
app: Topo,
|
|
507
512
|
entityId: string,
|
|
508
513
|
rootDir: string,
|
|
509
|
-
|
|
510
|
-
| Readonly<Record<string, readonly CliCommandAliasInput[]>>
|
|
511
|
-
| undefined,
|
|
514
|
+
overlays: readonly TopoGraphOverlayRegistration[] | undefined,
|
|
512
515
|
surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
|
|
513
516
|
): Result<object, Error> => {
|
|
514
|
-
const matches =
|
|
515
|
-
|
|
517
|
+
const matches = deriveCurrentTopoMatches(app, entityId, {
|
|
518
|
+
overlays,
|
|
516
519
|
rootDir,
|
|
517
520
|
surfaceLayerNames,
|
|
518
521
|
});
|
|
@@ -523,13 +526,11 @@ const buildSurveyTrailDetail = (
|
|
|
523
526
|
app: Topo,
|
|
524
527
|
id: string,
|
|
525
528
|
rootDir: string,
|
|
526
|
-
|
|
527
|
-
| Readonly<Record<string, readonly CliCommandAliasInput[]>>
|
|
528
|
-
| undefined,
|
|
529
|
+
overlays: readonly TopoGraphOverlayRegistration[] | undefined,
|
|
529
530
|
surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
|
|
530
531
|
): Result<object, Error> => {
|
|
531
|
-
const detail =
|
|
532
|
-
|
|
532
|
+
const detail = deriveCurrentTrailDetail(app, id, {
|
|
533
|
+
overlays,
|
|
533
534
|
rootDir,
|
|
534
535
|
surfaceLayerNames,
|
|
535
536
|
});
|
|
@@ -543,7 +544,7 @@ const buildSurveyResourceDetail = (
|
|
|
543
544
|
id: string,
|
|
544
545
|
rootDir: string
|
|
545
546
|
): Result<object, Error> => {
|
|
546
|
-
const detail =
|
|
547
|
+
const detail = deriveCurrentResourceDetail(app, id, { rootDir });
|
|
547
548
|
return detail === undefined
|
|
548
549
|
? Result.err(new NotFoundError(`Resource not found: ${id}`))
|
|
549
550
|
: Result.ok(detail);
|
|
@@ -554,7 +555,7 @@ const buildSurveySignalDetail = (
|
|
|
554
555
|
id: string,
|
|
555
556
|
rootDir: string
|
|
556
557
|
): Result<object, Error> => {
|
|
557
|
-
const detail =
|
|
558
|
+
const detail = deriveCurrentSignalDetail(app, id, { rootDir });
|
|
558
559
|
return detail === undefined
|
|
559
560
|
? Result.err(new NotFoundError(`Signal not found: ${id}`))
|
|
560
561
|
: Result.ok(detail);
|
|
@@ -581,26 +582,18 @@ type SurveyHandler = (
|
|
|
581
582
|
app: Topo,
|
|
582
583
|
input: SurveyInput,
|
|
583
584
|
rootDir: string,
|
|
584
|
-
|
|
585
|
-
| Readonly<Record<string, readonly CliCommandAliasInput[]>>
|
|
586
|
-
| undefined,
|
|
585
|
+
overlays: readonly TopoGraphOverlayRegistration[] | undefined,
|
|
587
586
|
surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
|
|
588
587
|
) => Result<object, Error> | Promise<Result<object, Error>>;
|
|
589
588
|
|
|
590
589
|
/** Handlers keyed by survey mode. */
|
|
591
590
|
const surveyHandlers: Record<SurveyMode, SurveyHandler> = {
|
|
592
|
-
lookup: (app, input, rootDir,
|
|
591
|
+
lookup: (app, input, rootDir, overlays, surfaceLayerNames) =>
|
|
593
592
|
input.id === undefined || input.id === ''
|
|
594
593
|
? Result.err(new ValidationError('Survey lookup requires an id'))
|
|
595
|
-
: buildSurveyLookup(
|
|
596
|
-
app,
|
|
597
|
-
input.id,
|
|
598
|
-
rootDir,
|
|
599
|
-
cliAliases,
|
|
600
|
-
surfaceLayerNames
|
|
601
|
-
),
|
|
594
|
+
: buildSurveyLookup(app, input.id, rootDir, overlays, surfaceLayerNames),
|
|
602
595
|
overview: (app, _input, rootDir) =>
|
|
603
|
-
Result.ok(
|
|
596
|
+
Result.ok(deriveCurrentTopoList(app, { rootDir })),
|
|
604
597
|
};
|
|
605
598
|
|
|
606
599
|
const envelopeSurveyValue = (
|
|
@@ -613,9 +606,7 @@ const dispatchSurvey = async (
|
|
|
613
606
|
app: Topo,
|
|
614
607
|
input: SurveyInput,
|
|
615
608
|
rootDir: string,
|
|
616
|
-
|
|
617
|
-
| Readonly<Record<string, readonly CliCommandAliasInput[]>>
|
|
618
|
-
| undefined,
|
|
609
|
+
overlays: readonly TopoGraphOverlayRegistration[] | undefined,
|
|
619
610
|
surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
|
|
620
611
|
): Promise<Result<SurveyEnvelope, Error>> => {
|
|
621
612
|
const mode = deriveSurveyMode(input);
|
|
@@ -624,7 +615,7 @@ const dispatchSurvey = async (
|
|
|
624
615
|
app,
|
|
625
616
|
input,
|
|
626
617
|
rootDir,
|
|
627
|
-
|
|
618
|
+
overlays,
|
|
628
619
|
surfaceLayerNames
|
|
629
620
|
);
|
|
630
621
|
if (result.isErr()) {
|
|
@@ -644,13 +635,11 @@ const withFreshSurveyApp = async <T>(
|
|
|
644
635
|
rootDir: string,
|
|
645
636
|
consume: (
|
|
646
637
|
app: Topo,
|
|
647
|
-
|
|
648
|
-
| Readonly<Record<string, readonly CliCommandAliasInput[]>>
|
|
649
|
-
| undefined
|
|
638
|
+
overlays: readonly TopoGraphOverlayRegistration[] | undefined
|
|
650
639
|
) => Promise<Result<T, Error>> | Result<T, Error>
|
|
651
640
|
): Promise<Result<T, Error>> =>
|
|
652
641
|
withFreshAppLease(input.module, rootDir, (lease) =>
|
|
653
|
-
consume(lease.app, lease.
|
|
642
|
+
consume(lease.app, lease.overlays)
|
|
654
643
|
);
|
|
655
644
|
|
|
656
645
|
const withResolvedSurveyApp = async <T>(
|
|
@@ -662,14 +651,12 @@ const withResolvedSurveyApp = async <T>(
|
|
|
662
651
|
consume: (
|
|
663
652
|
app: Topo,
|
|
664
653
|
rootDir: string,
|
|
665
|
-
|
|
666
|
-
| Readonly<Record<string, readonly CliCommandAliasInput[]>>
|
|
667
|
-
| undefined
|
|
654
|
+
overlays: readonly TopoGraphOverlayRegistration[] | undefined
|
|
668
655
|
) => Promise<Result<T, Error>> | Result<T, Error>
|
|
669
656
|
): Promise<Result<T, Error>> =>
|
|
670
657
|
withOperatorRootDir(input, { cwd }, (rootDir) =>
|
|
671
|
-
withFreshSurveyApp(input, rootDir, (app,
|
|
672
|
-
consume(app, rootDir,
|
|
658
|
+
withFreshSurveyApp(input, rootDir, (app, overlays) =>
|
|
659
|
+
consume(app, rootDir, overlays)
|
|
673
660
|
)
|
|
674
661
|
);
|
|
675
662
|
|
|
@@ -743,12 +730,12 @@ const surveyMatchOutput = z.discriminatedUnion('kind', [
|
|
|
743
730
|
export const surveyTrail = trail('survey', {
|
|
744
731
|
args: ['id'],
|
|
745
732
|
blaze: async (input, ctx) =>
|
|
746
|
-
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir,
|
|
733
|
+
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir, overlays) =>
|
|
747
734
|
dispatchSurvey(
|
|
748
735
|
app,
|
|
749
736
|
input,
|
|
750
737
|
rootDir,
|
|
751
|
-
|
|
738
|
+
overlays,
|
|
752
739
|
readSurfaceLayerNamesFromContext(ctx)
|
|
753
740
|
)
|
|
754
741
|
),
|
|
@@ -824,7 +811,7 @@ export const surveyTrail = trail('survey', {
|
|
|
824
811
|
export const surveyBriefTrail = trail('survey.brief', {
|
|
825
812
|
blaze: async (input, ctx) =>
|
|
826
813
|
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir) =>
|
|
827
|
-
Result.ok(
|
|
814
|
+
Result.ok(deriveCurrentTopoBrief(app, { rootDir }))
|
|
828
815
|
),
|
|
829
816
|
description: 'Summarize topo capabilities',
|
|
830
817
|
examples: [
|
|
@@ -904,12 +891,12 @@ export const surveyDiffTrail = trail('survey.diff', {
|
|
|
904
891
|
export const surveyTrailDetailTrail = trail('survey.trail', {
|
|
905
892
|
args: ['id'],
|
|
906
893
|
blaze: async (input, ctx) =>
|
|
907
|
-
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir,
|
|
894
|
+
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir, overlays) =>
|
|
908
895
|
buildSurveyTrailDetail(
|
|
909
896
|
app,
|
|
910
897
|
input.id,
|
|
911
898
|
rootDir,
|
|
912
|
-
|
|
899
|
+
overlays,
|
|
913
900
|
readSurfaceLayerNamesFromContext(ctx)
|
|
914
901
|
)
|
|
915
902
|
),
|
|
@@ -1,385 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
readonly producer: string;
|
|
16
|
-
readonly signal: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
type JsonSchemaReport = Readonly<Record<string, unknown>>;
|
|
20
|
-
|
|
21
|
-
export interface ActivationSourceReport extends ActivationSourceProjection {
|
|
22
|
-
readonly cron?: string | undefined;
|
|
23
|
-
readonly hasParse?: true | undefined;
|
|
24
|
-
readonly hasPayloadSchema?: true | undefined;
|
|
25
|
-
readonly hasVerify?: true | undefined;
|
|
26
|
-
readonly id: string;
|
|
27
|
-
readonly input?: unknown;
|
|
28
|
-
readonly inputSchema?: JsonSchemaReport | undefined;
|
|
29
|
-
readonly kind: string;
|
|
30
|
-
readonly key: string;
|
|
31
|
-
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
32
|
-
readonly method?: string | undefined;
|
|
33
|
-
readonly parseOutputSchema?: JsonSchemaReport | undefined;
|
|
34
|
-
readonly path?: string | undefined;
|
|
35
|
-
readonly payloadSchema?: JsonSchemaReport | undefined;
|
|
36
|
-
readonly timezone?: string | undefined;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface ActivationEdgeReport extends Readonly<
|
|
40
|
-
Record<string, unknown>
|
|
41
|
-
> {
|
|
42
|
-
readonly hasWhere: boolean;
|
|
43
|
-
readonly sourceId: string;
|
|
44
|
-
readonly sourceKey: string;
|
|
45
|
-
readonly sourceKind: string;
|
|
46
|
-
readonly trailId: string;
|
|
47
|
-
readonly where?: { readonly predicate: true } | undefined;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface SignalActivationRelations {
|
|
51
|
-
readonly consumers: readonly string[];
|
|
52
|
-
readonly producers: readonly string[];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface TrailActivationReport {
|
|
56
|
-
readonly activatedBy: readonly string[];
|
|
57
|
-
readonly activates: readonly string[];
|
|
58
|
-
readonly chains: readonly ActivationChainReport[];
|
|
59
|
-
readonly edges: readonly ActivationEdgeReport[];
|
|
60
|
-
readonly fires: readonly string[];
|
|
61
|
-
readonly on: readonly string[];
|
|
62
|
-
readonly sources: readonly ActivationSourceReport[];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface ActivationOverviewReport {
|
|
66
|
-
readonly chainCount: number;
|
|
67
|
-
readonly chains: readonly ActivationChainReport[];
|
|
68
|
-
readonly edgeCount: number;
|
|
69
|
-
readonly edges: readonly ActivationEdgeReport[];
|
|
70
|
-
readonly signalIds: readonly string[];
|
|
71
|
-
readonly sourceCount: number;
|
|
72
|
-
readonly sourceKeys: readonly string[];
|
|
73
|
-
readonly trailIds: readonly string[];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export interface ActivationGraphReport {
|
|
77
|
-
readonly overview: ActivationOverviewReport;
|
|
78
|
-
readonly signals: ReadonlyMap<string, SignalActivationRelations>;
|
|
79
|
-
readonly sources: ReadonlyMap<string, ActivationSourceReport>;
|
|
80
|
-
readonly trails: ReadonlyMap<string, TrailActivationReport>;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
interface MutableSignalRelations {
|
|
84
|
-
readonly consumers: Set<string>;
|
|
85
|
-
readonly producers: Set<string>;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
interface MutableTrailActivation {
|
|
89
|
-
readonly activatedBy: Set<string>;
|
|
90
|
-
readonly activates: Set<string>;
|
|
91
|
-
readonly chains: ActivationChainReport[];
|
|
92
|
-
readonly fires: readonly string[];
|
|
93
|
-
readonly on: readonly string[];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const canonicalLeaf = (value: unknown): unknown => {
|
|
97
|
-
switch (typeof value) {
|
|
98
|
-
case 'bigint': {
|
|
99
|
-
return value.toString();
|
|
100
|
-
}
|
|
101
|
-
case 'function': {
|
|
102
|
-
return `[Function:${value.name || 'anonymous'}]`;
|
|
103
|
-
}
|
|
104
|
-
case 'symbol': {
|
|
105
|
-
return `[Symbol:${value.description ?? ''}]`;
|
|
106
|
-
}
|
|
107
|
-
case 'undefined': {
|
|
108
|
-
return '[Undefined]';
|
|
109
|
-
}
|
|
110
|
-
default: {
|
|
111
|
-
return value;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const canonicalize = (value: unknown): unknown => {
|
|
117
|
-
if (Array.isArray(value)) {
|
|
118
|
-
return value.map(canonicalize);
|
|
119
|
-
}
|
|
120
|
-
if (value instanceof Date) {
|
|
121
|
-
return value.toISOString();
|
|
122
|
-
}
|
|
123
|
-
if (value instanceof RegExp) {
|
|
124
|
-
return value.toString();
|
|
125
|
-
}
|
|
126
|
-
if (value !== null && typeof value === 'object') {
|
|
127
|
-
const sorted: Record<string, unknown> = {};
|
|
128
|
-
for (const key of Object.keys(value).toSorted()) {
|
|
129
|
-
const next = (value as Record<string, unknown>)[key];
|
|
130
|
-
sorted[key] = next === undefined ? '[Undefined]' : canonicalize(next);
|
|
131
|
-
}
|
|
132
|
-
return sorted;
|
|
133
|
-
}
|
|
134
|
-
return canonicalLeaf(value);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const sortKeys = <T extends Record<string, unknown>>(value: T): T => {
|
|
138
|
-
const sorted: Record<string, unknown> = {};
|
|
139
|
-
for (const key of Object.keys(value).toSorted()) {
|
|
140
|
-
sorted[key] = value[key];
|
|
141
|
-
}
|
|
142
|
-
return sorted as T;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const compareChains = (
|
|
146
|
-
a: ActivationChainReport,
|
|
147
|
-
b: ActivationChainReport
|
|
148
|
-
): number =>
|
|
149
|
-
a.producer.localeCompare(b.producer) ||
|
|
150
|
-
a.signal.localeCompare(b.signal) ||
|
|
151
|
-
a.consumer.localeCompare(b.consumer);
|
|
152
|
-
|
|
153
|
-
const sortedUnique = (values: Iterable<string>): readonly string[] =>
|
|
154
|
-
[...new Set(values)].toSorted();
|
|
155
|
-
|
|
156
|
-
const projectActivationSource = (
|
|
157
|
-
source: ActivationSource
|
|
158
|
-
): ActivationSourceReport =>
|
|
159
|
-
projectActivationSourceDeclaration(source) as ActivationSourceReport;
|
|
160
|
-
|
|
161
|
-
const projectActivationEdge = (
|
|
162
|
-
trailId: string,
|
|
163
|
-
activation: ActivationEntry
|
|
164
|
-
): ActivationEdgeReport => {
|
|
165
|
-
const sourceKey = activationSourceKey(activation.source);
|
|
166
|
-
const edge: Record<string, unknown> = {
|
|
167
|
-
hasWhere: activation.where !== undefined,
|
|
168
|
-
sourceId: activation.source.id,
|
|
169
|
-
sourceKey,
|
|
170
|
-
sourceKind: activation.source.kind,
|
|
171
|
-
trailId,
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
if (activation.meta !== undefined) {
|
|
175
|
-
edge['meta'] = canonicalize(activation.meta);
|
|
176
|
-
}
|
|
177
|
-
if (activation.where !== undefined) {
|
|
178
|
-
edge['where'] = { predicate: true };
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return sortKeys(edge) as ActivationEdgeReport;
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
const collectActivationSourceCatalog = (
|
|
185
|
-
trails: readonly AnyTrail[]
|
|
186
|
-
): readonly ActivationSourceReport[] => {
|
|
187
|
-
const sources = new Map<string, ActivationSourceReport>();
|
|
188
|
-
for (const trail of trails) {
|
|
189
|
-
for (const activation of trail.activationSources) {
|
|
190
|
-
const projected = projectActivationSource(activation.source);
|
|
191
|
-
sources.set(projected.key, projected);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return [...sources.values()].toSorted((a, b) => a.key.localeCompare(b.key));
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
const collectActivationEdges = (
|
|
198
|
-
trails: readonly AnyTrail[]
|
|
199
|
-
): readonly ActivationEdgeReport[] => {
|
|
200
|
-
const edges = new Map<string, ActivationEdgeReport>();
|
|
201
|
-
for (const trail of trails) {
|
|
202
|
-
for (const activation of trail.activationSources) {
|
|
203
|
-
const edge = projectActivationEdge(trail.id, activation);
|
|
204
|
-
const key = `${edge.sourceKey}\0${edge.trailId}`;
|
|
205
|
-
const previous = edges.get(key);
|
|
206
|
-
edges.set(
|
|
207
|
-
key,
|
|
208
|
-
previous === undefined || (!previous.hasWhere && edge.hasWhere)
|
|
209
|
-
? edge
|
|
210
|
-
: previous
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return [...edges.values()].toSorted(
|
|
216
|
-
(a, b) =>
|
|
217
|
-
a.sourceKey.localeCompare(b.sourceKey) ||
|
|
218
|
-
a.trailId.localeCompare(b.trailId)
|
|
219
|
-
);
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const getSignalRelations = (
|
|
223
|
-
relations: Map<string, MutableSignalRelations>,
|
|
224
|
-
signalId: string
|
|
225
|
-
): MutableSignalRelations => {
|
|
226
|
-
const existing = relations.get(signalId);
|
|
227
|
-
if (existing !== undefined) {
|
|
228
|
-
return existing;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const created = {
|
|
232
|
-
consumers: new Set<string>(),
|
|
233
|
-
producers: new Set<string>(),
|
|
234
|
-
};
|
|
235
|
-
relations.set(signalId, created);
|
|
236
|
-
return created;
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const getTrailActivation = (
|
|
240
|
-
trails: Map<string, MutableTrailActivation>,
|
|
241
|
-
trail: AnyTrail
|
|
242
|
-
): MutableTrailActivation => {
|
|
243
|
-
const existing = trails.get(trail.id);
|
|
244
|
-
if (existing !== undefined) {
|
|
245
|
-
return existing;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const created = {
|
|
249
|
-
activatedBy: new Set<string>(),
|
|
250
|
-
activates: new Set<string>(),
|
|
251
|
-
chains: [],
|
|
252
|
-
fires: sortedUnique(trail.fires),
|
|
253
|
-
on: sortedUnique(trail.on),
|
|
254
|
-
};
|
|
255
|
-
trails.set(trail.id, created);
|
|
256
|
-
return created;
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
export const deriveDeclaredTrailActivation = (
|
|
260
|
-
trail: AnyTrail
|
|
261
|
-
): TrailActivationReport => {
|
|
262
|
-
const sources = collectActivationSourceCatalog([trail]);
|
|
263
|
-
return {
|
|
264
|
-
activatedBy: [],
|
|
265
|
-
activates: [],
|
|
266
|
-
chains: [],
|
|
267
|
-
edges: collectActivationEdges([trail]),
|
|
268
|
-
fires: sortedUnique(trail.fires),
|
|
269
|
-
on: sortedUnique(trail.on),
|
|
270
|
-
sources,
|
|
271
|
-
};
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
export const deriveSignalActivationRelations = (
|
|
275
|
-
app: Topo,
|
|
276
|
-
signalId: string
|
|
277
|
-
): SignalActivationRelations => {
|
|
278
|
-
const consumers: string[] = [];
|
|
279
|
-
const producers: string[] = [];
|
|
280
|
-
|
|
281
|
-
for (const trail of app.list()) {
|
|
282
|
-
if (trail.fires.includes(signalId)) {
|
|
283
|
-
producers.push(trail.id);
|
|
284
|
-
}
|
|
285
|
-
if (trail.on.includes(signalId)) {
|
|
286
|
-
consumers.push(trail.id);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return {
|
|
291
|
-
consumers: sortedUnique(consumers),
|
|
292
|
-
producers: sortedUnique(producers),
|
|
293
|
-
};
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
export const deriveActivationGraph = (app: Topo): ActivationGraphReport => {
|
|
297
|
-
const signalRelations = new Map<string, MutableSignalRelations>();
|
|
298
|
-
const trailActivations = new Map<string, MutableTrailActivation>();
|
|
299
|
-
const trails = app.list();
|
|
300
|
-
const sources = collectActivationSourceCatalog(trails);
|
|
301
|
-
const edges = collectActivationEdges(trails);
|
|
302
|
-
|
|
303
|
-
for (const signal of app.listSignals()) {
|
|
304
|
-
getSignalRelations(signalRelations, signal.id);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
for (const trail of trails) {
|
|
308
|
-
const trailActivation = getTrailActivation(trailActivations, trail);
|
|
309
|
-
for (const signalId of trailActivation.fires) {
|
|
310
|
-
getSignalRelations(signalRelations, signalId).producers.add(trail.id);
|
|
311
|
-
}
|
|
312
|
-
for (const signalId of trailActivation.on) {
|
|
313
|
-
getSignalRelations(signalRelations, signalId).consumers.add(trail.id);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const chains: ActivationChainReport[] = [];
|
|
318
|
-
for (const [signal, related] of signalRelations) {
|
|
319
|
-
for (const producer of related.producers) {
|
|
320
|
-
const producerActivation = trailActivations.get(producer);
|
|
321
|
-
for (const consumer of related.consumers) {
|
|
322
|
-
const chain = { consumer, producer, signal };
|
|
323
|
-
chains.push(chain);
|
|
324
|
-
producerActivation?.activates.add(consumer);
|
|
325
|
-
producerActivation?.chains.push(chain);
|
|
326
|
-
const consumerActivation = trailActivations.get(consumer);
|
|
327
|
-
consumerActivation?.activatedBy.add(producer);
|
|
328
|
-
if (consumerActivation !== producerActivation) {
|
|
329
|
-
consumerActivation?.chains.push(chain);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
chains.sort(compareChains);
|
|
336
|
-
const activeTrailIds = sortedUnique([
|
|
337
|
-
...[...trailActivations.entries()].flatMap(([trailId, trail]) =>
|
|
338
|
-
trail.fires.length > 0 || trail.on.length > 0
|
|
339
|
-
? [trailId, ...trail.activatedBy, ...trail.activates]
|
|
340
|
-
: []
|
|
341
|
-
),
|
|
342
|
-
...edges.map((edge) => edge.trailId),
|
|
343
|
-
]);
|
|
344
|
-
|
|
345
|
-
return {
|
|
346
|
-
overview: {
|
|
347
|
-
chainCount: chains.length,
|
|
348
|
-
chains,
|
|
349
|
-
edgeCount: edges.length,
|
|
350
|
-
edges,
|
|
351
|
-
signalIds: [...signalRelations.keys()].toSorted(),
|
|
352
|
-
sourceCount: sources.length,
|
|
353
|
-
sourceKeys: sources.map((source) => source.key),
|
|
354
|
-
trailIds: activeTrailIds,
|
|
355
|
-
},
|
|
356
|
-
signals: new Map(
|
|
357
|
-
[...signalRelations.entries()].map(([id, relations]) => [
|
|
358
|
-
id,
|
|
359
|
-
{
|
|
360
|
-
consumers: sortedUnique(relations.consumers),
|
|
361
|
-
producers: sortedUnique(relations.producers),
|
|
362
|
-
},
|
|
363
|
-
])
|
|
364
|
-
),
|
|
365
|
-
sources: new Map(sources.map((source) => [source.key, source])),
|
|
366
|
-
trails: new Map(
|
|
367
|
-
[...trailActivations.entries()].map(([id, activation]) => [
|
|
368
|
-
id,
|
|
369
|
-
{
|
|
370
|
-
activatedBy: sortedUnique(activation.activatedBy),
|
|
371
|
-
activates: sortedUnique(activation.activates),
|
|
372
|
-
chains: activation.chains.toSorted(compareChains),
|
|
373
|
-
edges: edges.filter((edge) => edge.trailId === id),
|
|
374
|
-
fires: activation.fires,
|
|
375
|
-
on: activation.on,
|
|
376
|
-
sources: sources.filter((source) =>
|
|
377
|
-
edges.some(
|
|
378
|
-
(edge) => edge.trailId === id && edge.sourceKey === source.key
|
|
379
|
-
)
|
|
380
|
-
),
|
|
381
|
-
},
|
|
382
|
-
])
|
|
383
|
-
),
|
|
384
|
-
};
|
|
385
|
-
};
|
|
1
|
+
export {
|
|
2
|
+
deriveActivationGraph,
|
|
3
|
+
deriveDeclaredTrailActivation,
|
|
4
|
+
deriveSignalActivationRelations,
|
|
5
|
+
} from '@ontrails/topographer';
|
|
6
|
+
export type {
|
|
7
|
+
ActivationChainReport,
|
|
8
|
+
ActivationEdgeReport,
|
|
9
|
+
ActivationGraphReport,
|
|
10
|
+
ActivationOverviewReport,
|
|
11
|
+
ActivationSourceReport,
|
|
12
|
+
SignalActivationRelations,
|
|
13
|
+
TrailActivationReport,
|
|
14
|
+
} from '@ontrails/topographer';
|