@ontrails/core 1.0.0-beta.13 → 1.0.0-beta.15
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/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +17 -0
- package/README.md +19 -14
- package/dist/branded.d.ts +1 -1
- package/dist/branded.d.ts.map +1 -1
- package/dist/branded.js +1 -1
- package/dist/branded.js.map +1 -1
- package/dist/context.d.ts +13 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +19 -3
- package/dist/context.js.map +1 -1
- package/dist/contour.d.ts +81 -0
- package/dist/contour.d.ts.map +1 -0
- package/dist/contour.js +176 -0
- package/dist/contour.js.map +1 -0
- package/dist/cross-schema.d.ts +18 -0
- package/dist/cross-schema.d.ts.map +1 -0
- package/dist/cross-schema.js +29 -0
- package/dist/cross-schema.js.map +1 -0
- package/dist/derive.d.ts +6 -0
- package/dist/derive.d.ts.map +1 -1
- package/dist/derive.js +29 -5
- package/dist/derive.js.map +1 -1
- package/dist/draft.d.ts +28 -0
- package/dist/draft.d.ts.map +1 -0
- package/dist/draft.js +157 -0
- package/dist/draft.js.map +1 -0
- package/dist/errors.d.ts +62 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +44 -1
- package/dist/errors.js.map +1 -1
- package/dist/execute.d.ts +26 -9
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +441 -20
- package/dist/execute.js.map +1 -1
- package/dist/fire.d.ts +43 -0
- package/dist/fire.d.ts.map +1 -0
- package/dist/fire.js +185 -0
- package/dist/fire.js.map +1 -0
- package/dist/index.d.ts +33 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -12
- package/dist/index.js.map +1 -1
- package/dist/internal/cross-batch.d.ts +44 -0
- package/dist/internal/cross-batch.d.ts.map +1 -0
- package/dist/internal/cross-batch.js +58 -0
- package/dist/internal/cross-batch.js.map +1 -0
- package/dist/internal/fork-ctx.d.ts +41 -0
- package/dist/internal/fork-ctx.d.ts.map +1 -0
- package/dist/internal/fork-ctx.js +47 -0
- package/dist/internal/fork-ctx.js.map +1 -0
- package/dist/internal/signal-ref.d.ts +15 -0
- package/dist/internal/signal-ref.d.ts.map +1 -0
- package/dist/internal/signal-ref.js +41 -0
- package/dist/internal/signal-ref.js.map +1 -0
- package/dist/internal/topo-saves.d.ts +53 -0
- package/dist/internal/topo-saves.d.ts.map +1 -0
- package/dist/internal/topo-saves.js +449 -0
- package/dist/internal/topo-saves.js.map +1 -0
- package/dist/internal/topo-snapshots.d.ts +57 -0
- package/dist/internal/topo-snapshots.d.ts.map +1 -0
- package/dist/internal/topo-snapshots.js +372 -0
- package/dist/internal/topo-snapshots.js.map +1 -0
- package/dist/internal/topo-store-read.d.ts +70 -0
- package/dist/internal/topo-store-read.d.ts.map +1 -0
- package/dist/internal/topo-store-read.js +221 -0
- package/dist/internal/topo-store-read.js.map +1 -0
- package/dist/internal/topo-store.d.ts +26 -0
- package/dist/internal/topo-store.d.ts.map +1 -0
- package/dist/internal/topo-store.js +621 -0
- package/dist/internal/topo-store.js.map +1 -0
- package/dist/internal/tracing.d.ts +98 -0
- package/dist/internal/tracing.d.ts.map +1 -0
- package/dist/internal/tracing.js +102 -0
- package/dist/internal/tracing.js.map +1 -0
- package/dist/internal/trails-db.d.ts +16 -0
- package/dist/internal/trails-db.d.ts.map +1 -0
- package/dist/internal/trails-db.js +118 -0
- package/dist/internal/trails-db.js.map +1 -0
- package/dist/internal/zod-wrappers.d.ts +40 -0
- package/dist/internal/zod-wrappers.d.ts.map +1 -0
- package/dist/internal/zod-wrappers.js +59 -0
- package/dist/internal/zod-wrappers.js.map +1 -0
- package/dist/layer.d.ts +22 -5
- package/dist/layer.d.ts.map +1 -1
- package/dist/layer.js +20 -3
- package/dist/layer.js.map +1 -1
- package/dist/path-security.d.ts +1 -1
- package/dist/path-security.d.ts.map +1 -1
- package/dist/path-security.js +1 -1
- package/dist/path-security.js.map +1 -1
- package/dist/resilience.d.ts +1 -1
- package/dist/resilience.d.ts.map +1 -1
- package/dist/resilience.js +3 -3
- package/dist/resilience.js.map +1 -1
- package/dist/resource-config.d.ts +22 -0
- package/dist/resource-config.d.ts.map +1 -0
- package/dist/resource-config.js +210 -0
- package/dist/resource-config.js.map +1 -0
- package/dist/resource.d.ts +74 -0
- package/dist/resource.d.ts.map +1 -0
- package/dist/resource.js +61 -0
- package/dist/resource.js.map +1 -0
- package/dist/run.d.ts +4 -3
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +5 -4
- package/dist/run.js.map +1 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +2 -1
- package/dist/serialization.js.map +1 -1
- package/dist/store/accessor-protocol.d.ts +51 -0
- package/dist/store/accessor-protocol.d.ts.map +1 -0
- package/dist/store/accessor-protocol.js +2 -0
- package/dist/store/accessor-protocol.js.map +1 -0
- package/dist/store/index.d.ts +2 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +2 -0
- package/dist/store/index.js.map +1 -0
- package/dist/surface-filter.d.ts +13 -0
- package/dist/surface-filter.d.ts.map +1 -0
- package/dist/surface-filter.js +76 -0
- package/dist/surface-filter.js.map +1 -0
- package/dist/topo-store.d.ts +62 -0
- package/dist/topo-store.d.ts.map +1 -0
- package/dist/topo-store.js +319 -0
- package/dist/topo-store.js.map +1 -0
- package/dist/topo.d.ts +23 -9
- package/dist/topo.d.ts.map +1 -1
- package/dist/topo.js +175 -46
- package/dist/topo.js.map +1 -1
- package/dist/trail.d.ts +86 -19
- package/dist/trail.d.ts.map +1 -1
- package/dist/trail.js +59 -4
- package/dist/trail.js.map +1 -1
- package/dist/trails/derive-trail.d.ts +66 -0
- package/dist/trails/derive-trail.d.ts.map +1 -0
- package/dist/trails/derive-trail.js +378 -0
- package/dist/trails/derive-trail.js.map +1 -0
- package/dist/trails/index.d.ts +5 -0
- package/dist/trails/index.d.ts.map +1 -0
- package/dist/trails/index.js +3 -0
- package/dist/trails/index.js.map +1 -0
- package/dist/trails/ingest.d.ts +24 -0
- package/dist/trails/ingest.d.ts.map +1 -0
- package/dist/trails/ingest.js +63 -0
- package/dist/trails/ingest.js.map +1 -0
- package/dist/transport-error-map.d.ts +106 -0
- package/dist/transport-error-map.d.ts.map +1 -0
- package/dist/transport-error-map.js +24 -0
- package/dist/transport-error-map.js.map +1 -0
- package/dist/type-checks.test-d.d.ts +71 -0
- package/dist/type-checks.test-d.d.ts.map +1 -0
- package/dist/type-checks.test-d.js +12 -0
- package/dist/type-checks.test-d.js.map +1 -0
- package/dist/type-utils.d.ts +11 -2
- package/dist/type-utils.d.ts.map +1 -1
- package/dist/type-utils.js.map +1 -1
- package/dist/types.d.ts +113 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validate-established-topo.d.ts +76 -0
- package/dist/validate-established-topo.d.ts.map +1 -0
- package/dist/validate-established-topo.js +43 -0
- package/dist/validate-established-topo.js.map +1 -0
- package/dist/validate-topo.d.ts.map +1 -1
- package/dist/validate-topo.js +27 -8
- package/dist/validate-topo.js.map +1 -1
- package/dist/workspace.d.ts +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +1 -1
- package/dist/workspace.js.map +1 -1
- package/package.json +10 -1
- package/src/__tests__/branded.test.ts +5 -5
- package/src/__tests__/context.test.ts +3 -3
- package/src/__tests__/contour.test.ts +263 -0
- package/src/__tests__/derive-trail.test.ts +668 -0
- package/src/__tests__/derive.test.ts +58 -1
- package/src/__tests__/errors.test.ts +118 -0
- package/src/__tests__/execute.test.ts +1179 -141
- package/src/__tests__/fire.test.ts +1056 -0
- package/src/__tests__/fork-ctx.test.ts +83 -0
- package/src/__tests__/ingest.test.ts +191 -0
- package/src/__tests__/{gate.test.ts → layer.test.ts} +21 -21
- package/src/__tests__/path-security.test.ts +7 -7
- package/src/__tests__/resilience.test.ts +8 -8
- package/src/__tests__/resource.test.ts +204 -0
- package/src/__tests__/run.test.ts +13 -13
- package/src/__tests__/service-config.test.ts +28 -32
- package/src/__tests__/surface-filter.test.ts +196 -0
- package/src/__tests__/topo-store-read.test.ts +256 -0
- package/src/__tests__/topo-store.test.ts +869 -0
- package/src/__tests__/topo.test.ts +350 -54
- package/src/__tests__/trail.test.ts +189 -24
- package/src/__tests__/trails-db.test.ts +198 -0
- package/src/__tests__/transport-error-map.test.ts +82 -0
- package/src/__tests__/type-utils.test.ts +29 -1
- package/src/__tests__/validate-topo.test.ts +305 -16
- package/src/__tests__/workspace.test.ts +6 -6
- package/src/__tests__/zod-wrappers.test.ts +62 -0
- package/src/branded.ts +1 -1
- package/src/context.ts +24 -4
- package/src/contour.ts +344 -0
- package/src/cross-schema.ts +36 -0
- package/src/derive.ts +39 -8
- package/src/draft.ts +338 -0
- package/src/errors.ts +62 -18
- package/src/execute.ts +840 -29
- package/src/fire.ts +295 -0
- package/src/index.ts +142 -26
- package/src/internal/cross-batch.ts +77 -0
- package/src/internal/fork-ctx.ts +69 -0
- package/src/internal/signal-ref.ts +87 -0
- package/src/internal/topo-snapshots.ts +517 -0
- package/src/internal/topo-store-read.ts +481 -0
- package/src/internal/topo-store.ts +1192 -0
- package/src/internal/tracing.ts +187 -0
- package/src/internal/trails-db.ts +187 -0
- package/src/internal/zod-wrappers.ts +78 -0
- package/src/layer.ts +50 -0
- package/src/path-security.ts +1 -1
- package/src/resilience.ts +3 -3
- package/src/{provision-config.ts → resource-config.ts} +109 -123
- package/src/resource.ts +155 -0
- package/src/run.ts +5 -4
- package/src/serialization.ts +2 -0
- package/src/store/accessor-protocol.ts +56 -0
- package/src/store/index.ts +4 -0
- package/src/surface-filter.ts +182 -0
- package/src/topo-store.ts +510 -0
- package/src/topo.ts +328 -57
- package/src/trail.ts +199 -31
- package/src/trails/derive-trail.ts +842 -0
- package/src/trails/index.ts +9 -0
- package/src/trails/ingest.ts +146 -0
- package/src/transport-error-map.ts +56 -0
- package/src/type-checks.test-d.ts +106 -0
- package/src/type-utils.ts +17 -2
- package/src/types.ts +148 -11
- package/src/validate-established-topo.ts +63 -0
- package/src/validate-topo.ts +37 -9
- package/src/workspace.ts +1 -1
- package/tsconfig.tests.json +10 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/adapters.d.ts +0 -39
- package/dist/adapters.d.ts.map +0 -1
- package/dist/adapters.js +0 -2
- package/dist/adapters.js.map +0 -1
- package/dist/dispatch.d.ts +0 -27
- package/dist/dispatch.d.ts.map +0 -1
- package/dist/dispatch.js +0 -34
- package/dist/dispatch.js.map +0 -1
- package/dist/event.d.ts +0 -8
- package/dist/event.d.ts.map +0 -1
- package/dist/event.js +0 -7
- package/dist/event.js.map +0 -1
- package/dist/gate.d.ts +0 -17
- package/dist/gate.d.ts.map +0 -1
- package/dist/gate.js +0 -21
- package/dist/gate.js.map +0 -1
- package/dist/health.d.ts +0 -18
- package/dist/health.d.ts.map +0 -1
- package/dist/health.js +0 -5
- package/dist/health.js.map +0 -1
- package/dist/job.d.ts +0 -24
- package/dist/job.d.ts.map +0 -1
- package/dist/job.js +0 -17
- package/dist/job.js.map +0 -1
- package/dist/provision-config.d.ts +0 -22
- package/dist/provision-config.d.ts.map +0 -1
- package/dist/provision-config.js +0 -210
- package/dist/provision-config.js.map +0 -1
- package/dist/provision.d.ts +0 -71
- package/dist/provision.d.ts.map +0 -1
- package/dist/provision.js +0 -56
- package/dist/provision.js.map +0 -1
- package/dist/service-config.d.ts +0 -22
- package/dist/service-config.d.ts.map +0 -1
- package/dist/service-config.js +0 -210
- package/dist/service-config.js.map +0 -1
- package/dist/service.d.ts +0 -71
- package/dist/service.d.ts.map +0 -1
- package/dist/service.js +0 -56
- package/dist/service.js.map +0 -1
- package/src/__tests__/service.test.ts +0 -197
- package/src/event.ts +0 -15
- package/src/gate.ts +0 -44
- package/src/provision.ts +0 -148
package/src/topo.ts
CHANGED
|
@@ -2,46 +2,65 @@
|
|
|
2
2
|
* Application entry point — scans module exports to build a topology graph.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { AnyContour } from './contour.js';
|
|
5
6
|
import { ValidationError } from './errors.js';
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
getLateBoundSignalRef,
|
|
9
|
+
parseLateBoundSignalMarker,
|
|
10
|
+
} from './internal/signal-ref.js';
|
|
11
|
+
import type { AnySignal } from './signal.js';
|
|
12
|
+
import type { AnyResource } from './resource.js';
|
|
13
|
+
import { isResource } from './resource.js';
|
|
9
14
|
import type { AnyTrail } from './trail.js';
|
|
10
15
|
|
|
11
16
|
// ---------------------------------------------------------------------------
|
|
12
17
|
// Public types
|
|
13
18
|
// ---------------------------------------------------------------------------
|
|
14
19
|
|
|
20
|
+
export interface TopoIdentity {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly version?: string;
|
|
23
|
+
readonly description?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
export interface Topo {
|
|
16
27
|
readonly name: string;
|
|
28
|
+
readonly version?: string;
|
|
29
|
+
readonly description?: string;
|
|
30
|
+
readonly contours: ReadonlyMap<string, AnyContour>;
|
|
17
31
|
readonly trails: ReadonlyMap<string, AnyTrail>;
|
|
18
32
|
readonly signals: ReadonlyMap<string, AnySignal>;
|
|
19
|
-
readonly
|
|
33
|
+
readonly resources: ReadonlyMap<string, AnyResource>;
|
|
20
34
|
readonly count: number;
|
|
21
|
-
readonly
|
|
35
|
+
readonly contourCount: number;
|
|
36
|
+
readonly resourceCount: number;
|
|
37
|
+
getContour(name: string): AnyContour | undefined;
|
|
22
38
|
get(id: string): AnyTrail | undefined;
|
|
23
|
-
|
|
39
|
+
getResource(id: string): AnyResource | undefined;
|
|
40
|
+
hasContour(name: string): boolean;
|
|
24
41
|
has(id: string): boolean;
|
|
25
|
-
|
|
42
|
+
hasResource(id: string): boolean;
|
|
43
|
+
contourIds(): string[];
|
|
26
44
|
ids(): string[];
|
|
27
|
-
|
|
45
|
+
resourceIds(): string[];
|
|
46
|
+
listContours(): AnyContour[];
|
|
28
47
|
list(): AnyTrail[];
|
|
29
48
|
listSignals(): AnySignal[];
|
|
30
|
-
|
|
49
|
+
listResources(): AnyResource[];
|
|
31
50
|
}
|
|
32
51
|
|
|
33
52
|
// ---------------------------------------------------------------------------
|
|
34
53
|
// Kind discriminant check
|
|
35
54
|
// ---------------------------------------------------------------------------
|
|
36
55
|
|
|
37
|
-
type Registrable = AnyTrail | AnySignal |
|
|
56
|
+
type Registrable = AnyContour | AnyTrail | AnySignal | AnyResource;
|
|
38
57
|
|
|
39
58
|
const isRegistrable = (value: unknown): value is Registrable => {
|
|
40
59
|
if (typeof value !== 'object' || value === null) {
|
|
41
60
|
return false;
|
|
42
61
|
}
|
|
43
62
|
const { kind } = value as Record<string, unknown>;
|
|
44
|
-
return kind === 'trail' || kind === 'signal';
|
|
63
|
+
return kind === 'contour' || kind === 'trail' || kind === 'signal';
|
|
45
64
|
};
|
|
46
65
|
|
|
47
66
|
// ---------------------------------------------------------------------------
|
|
@@ -49,23 +68,35 @@ const isRegistrable = (value: unknown): value is Registrable => {
|
|
|
49
68
|
// ---------------------------------------------------------------------------
|
|
50
69
|
|
|
51
70
|
const createTopo = (
|
|
52
|
-
|
|
71
|
+
identity: TopoIdentity,
|
|
72
|
+
contours: ReadonlyMap<string, AnyContour>,
|
|
53
73
|
trails: ReadonlyMap<string, AnyTrail>,
|
|
54
74
|
signals: ReadonlyMap<string, AnySignal>,
|
|
55
|
-
|
|
75
|
+
resources: ReadonlyMap<string, AnyResource>
|
|
56
76
|
): Topo => ({
|
|
77
|
+
contourCount: contours.size,
|
|
78
|
+
contourIds(): string[] {
|
|
79
|
+
return [...contours.keys()];
|
|
80
|
+
},
|
|
81
|
+
contours,
|
|
57
82
|
count: trails.size,
|
|
58
83
|
get(id: string): AnyTrail | undefined {
|
|
59
84
|
return trails.get(id);
|
|
60
85
|
},
|
|
61
|
-
|
|
62
|
-
return
|
|
86
|
+
getContour(contourName: string): AnyContour | undefined {
|
|
87
|
+
return contours.get(contourName);
|
|
88
|
+
},
|
|
89
|
+
getResource(id: string): AnyResource | undefined {
|
|
90
|
+
return resources.get(id);
|
|
63
91
|
},
|
|
64
92
|
has(id: string): boolean {
|
|
65
93
|
return trails.has(id);
|
|
66
94
|
},
|
|
67
|
-
|
|
68
|
-
return
|
|
95
|
+
hasContour(contourName: string): boolean {
|
|
96
|
+
return contours.has(contourName);
|
|
97
|
+
},
|
|
98
|
+
hasResource(id: string): boolean {
|
|
99
|
+
return resources.has(id);
|
|
69
100
|
},
|
|
70
101
|
ids(): string[] {
|
|
71
102
|
return [...trails.keys()];
|
|
@@ -74,21 +105,27 @@ const createTopo = (
|
|
|
74
105
|
list(): AnyTrail[] {
|
|
75
106
|
return [...trails.values()];
|
|
76
107
|
},
|
|
77
|
-
|
|
78
|
-
return [...
|
|
108
|
+
listContours(): AnyContour[] {
|
|
109
|
+
return [...contours.values()];
|
|
110
|
+
},
|
|
111
|
+
listResources(): AnyResource[] {
|
|
112
|
+
return [...resources.values()];
|
|
79
113
|
},
|
|
80
114
|
|
|
81
115
|
listSignals(): AnySignal[] {
|
|
82
116
|
return [...signals.values()];
|
|
83
117
|
},
|
|
84
118
|
|
|
85
|
-
name,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
119
|
+
name: identity.name,
|
|
120
|
+
...(identity.version !== undefined && { version: identity.version }),
|
|
121
|
+
...(identity.description !== undefined && {
|
|
122
|
+
description: identity.description,
|
|
123
|
+
}),
|
|
124
|
+
resourceCount: resources.size,
|
|
125
|
+
resourceIds(): string[] {
|
|
126
|
+
return [...resources.keys()];
|
|
90
127
|
},
|
|
91
|
-
|
|
128
|
+
resources,
|
|
92
129
|
signals,
|
|
93
130
|
trails,
|
|
94
131
|
});
|
|
@@ -97,35 +134,237 @@ const createTopo = (
|
|
|
97
134
|
// topo()
|
|
98
135
|
// ---------------------------------------------------------------------------
|
|
99
136
|
|
|
137
|
+
const registerUnique = <T>(
|
|
138
|
+
collection: Map<string, T>,
|
|
139
|
+
id: string,
|
|
140
|
+
value: T,
|
|
141
|
+
duplicateMessage: string
|
|
142
|
+
): void => {
|
|
143
|
+
const existing = collection.get(id);
|
|
144
|
+
if (existing === value) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (existing !== undefined) {
|
|
148
|
+
throw new ValidationError(duplicateMessage);
|
|
149
|
+
}
|
|
150
|
+
collection.set(id, value);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const registerContour = (
|
|
154
|
+
contour: AnyContour,
|
|
155
|
+
contours: Map<string, AnyContour>
|
|
156
|
+
): void => {
|
|
157
|
+
registerUnique(
|
|
158
|
+
contours,
|
|
159
|
+
contour.name,
|
|
160
|
+
contour,
|
|
161
|
+
`Duplicate contour name: "${contour.name}"`
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const registerResourceValue = (
|
|
166
|
+
resource: AnyResource,
|
|
167
|
+
resources: Map<string, AnyResource>
|
|
168
|
+
): void => {
|
|
169
|
+
registerUnique(
|
|
170
|
+
resources,
|
|
171
|
+
resource.id,
|
|
172
|
+
resource,
|
|
173
|
+
`Duplicate resource ID: "${resource.id}"`
|
|
174
|
+
);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const registerSignal = (
|
|
178
|
+
signal: AnySignal,
|
|
179
|
+
signals: Map<string, AnySignal>
|
|
180
|
+
): void => {
|
|
181
|
+
registerUnique(
|
|
182
|
+
signals,
|
|
183
|
+
signal.id,
|
|
184
|
+
signal,
|
|
185
|
+
`Duplicate signal ID: "${signal.id}"`
|
|
186
|
+
);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const registerResourceSignals = (
|
|
190
|
+
resource: AnyResource,
|
|
191
|
+
signals: Map<string, AnySignal>
|
|
192
|
+
): void => {
|
|
193
|
+
for (const derived of resource.signals ?? []) {
|
|
194
|
+
registerSignal(derived, signals);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const registerTrail = (
|
|
199
|
+
trail: AnyTrail,
|
|
200
|
+
trails: Map<string, AnyTrail>
|
|
201
|
+
): void => {
|
|
202
|
+
registerUnique(trails, trail.id, trail, `Duplicate trail ID: "${trail.id}"`);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const registerLateBoundSignalId = (
|
|
206
|
+
byToken: Map<string, Set<string>>,
|
|
207
|
+
signal: AnySignal
|
|
208
|
+
): void => {
|
|
209
|
+
const ref = getLateBoundSignalRef(signal);
|
|
210
|
+
if (!ref) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const ids = byToken.get(ref.token) ?? new Set<string>();
|
|
215
|
+
ids.add(signal.id);
|
|
216
|
+
byToken.set(ref.token, ids);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const collectLateBoundSignalIdsByToken = (
|
|
220
|
+
resources: ReadonlyMap<string, AnyResource>
|
|
221
|
+
): ReadonlyMap<string, readonly string[]> => {
|
|
222
|
+
const byToken = new Map<string, Set<string>>();
|
|
223
|
+
|
|
224
|
+
for (const resource of resources.values()) {
|
|
225
|
+
for (const signal of resource.signals ?? []) {
|
|
226
|
+
registerLateBoundSignalId(byToken, signal);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return new Map(
|
|
231
|
+
[...byToken.entries()].map(([token, ids]) => [token, [...ids]])
|
|
232
|
+
);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const resolveLateBoundSignalId = (
|
|
236
|
+
trailId: string,
|
|
237
|
+
signalId: string,
|
|
238
|
+
lateBoundSignalIdsByToken: ReadonlyMap<string, readonly string[]>
|
|
239
|
+
): string => {
|
|
240
|
+
const marker = parseLateBoundSignalMarker(signalId);
|
|
241
|
+
if (!marker) {
|
|
242
|
+
return signalId;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const matches = lateBoundSignalIdsByToken.get(marker.token) ?? [];
|
|
246
|
+
if (matches.length === 1) {
|
|
247
|
+
return matches[0] ?? signalId;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (matches.length === 0) {
|
|
251
|
+
// Intentional throw: split-topo composition (where a trail and the
|
|
252
|
+
// store that backs its signals live in different topos) is not yet
|
|
253
|
+
// supported. Failing loudly here surfaces the case at assembly time
|
|
254
|
+
// instead of silently producing a trail with an unresolved store
|
|
255
|
+
// reference that would misbehave at runtime.
|
|
256
|
+
throw new ValidationError(
|
|
257
|
+
`Trail "${trailId}" references store-derived signal "${marker.displayId}", but no resource bound in this topo exposes it. ` +
|
|
258
|
+
'This usually means the store that backs this signal is not bound in this topo. ' +
|
|
259
|
+
`Bind the store via resource() in the same topo() call as "${trailId}", or compose this topo with the topo that binds the store. ` +
|
|
260
|
+
'Splitting a trail and its backing store across independent topos is not yet supported.'
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
throw new ValidationError(
|
|
265
|
+
`Trail "${trailId}" references late-bound signal "${marker.displayId}" but it resolves to multiple bound resource signals: ${matches.join(', ')}. Use canonical scoped ids when the same store definition is bound more than once.`
|
|
266
|
+
);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const resolveTrailSignalIds = (
|
|
270
|
+
trailId: string,
|
|
271
|
+
signalIds: readonly string[],
|
|
272
|
+
lateBoundSignalIdsByToken: ReadonlyMap<string, readonly string[]>
|
|
273
|
+
): { changed: boolean; ids: readonly string[] } => {
|
|
274
|
+
let changed = false;
|
|
275
|
+
const ids = Object.freeze(
|
|
276
|
+
signalIds.map((signalId) => {
|
|
277
|
+
const resolved = resolveLateBoundSignalId(
|
|
278
|
+
trailId,
|
|
279
|
+
signalId,
|
|
280
|
+
lateBoundSignalIdsByToken
|
|
281
|
+
);
|
|
282
|
+
changed ||= resolved !== signalId;
|
|
283
|
+
return resolved;
|
|
284
|
+
})
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
return { changed, ids };
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const finalizeTrailSignals = (
|
|
291
|
+
trails: ReadonlyMap<string, AnyTrail>,
|
|
292
|
+
resources: ReadonlyMap<string, AnyResource>
|
|
293
|
+
): Map<string, AnyTrail> => {
|
|
294
|
+
const lateBoundSignalIdsByToken = collectLateBoundSignalIdsByToken(resources);
|
|
295
|
+
const finalized = new Map<string, AnyTrail>();
|
|
296
|
+
|
|
297
|
+
for (const trail of trails.values()) {
|
|
298
|
+
const resolvedFires = resolveTrailSignalIds(
|
|
299
|
+
trail.id,
|
|
300
|
+
trail.fires ?? [],
|
|
301
|
+
lateBoundSignalIdsByToken
|
|
302
|
+
);
|
|
303
|
+
const resolvedOn = resolveTrailSignalIds(
|
|
304
|
+
trail.id,
|
|
305
|
+
trail.on ?? [],
|
|
306
|
+
lateBoundSignalIdsByToken
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
if (!resolvedFires.changed && !resolvedOn.changed) {
|
|
310
|
+
finalized.set(trail.id, trail);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
finalized.set(
|
|
315
|
+
trail.id,
|
|
316
|
+
Object.freeze({
|
|
317
|
+
...trail,
|
|
318
|
+
fires: resolvedFires.ids,
|
|
319
|
+
on: resolvedOn.ids,
|
|
320
|
+
})
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return finalized;
|
|
325
|
+
};
|
|
326
|
+
|
|
100
327
|
/** Register a single registrable value into the appropriate map. */
|
|
101
328
|
const register = (
|
|
102
329
|
value: Registrable,
|
|
330
|
+
contours: Map<string, AnyContour>,
|
|
103
331
|
trails: Map<string, AnyTrail>,
|
|
104
332
|
signals: Map<string, AnySignal>,
|
|
105
|
-
|
|
333
|
+
resources: Map<string, AnyResource>
|
|
106
334
|
): void => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
335
|
+
switch (value.kind) {
|
|
336
|
+
case 'contour': {
|
|
337
|
+
registerContour(value as AnyContour, contours);
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
case 'resource': {
|
|
341
|
+
registerResourceValue(value as AnyResource, resources);
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
case 'signal': {
|
|
345
|
+
registerSignal(value as AnySignal, signals);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
case 'trail': {
|
|
349
|
+
registerTrail(value as AnyTrail, trails);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
default: {
|
|
353
|
+
throw new ValidationError('Unsupported registrable value in topo()');
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const registerTrailContours = (
|
|
359
|
+
trail: AnyTrail,
|
|
360
|
+
contours: Map<string, AnyContour>,
|
|
361
|
+
trails: Map<string, AnyTrail>,
|
|
362
|
+
signals: Map<string, AnySignal>,
|
|
363
|
+
resources: Map<string, AnyResource>
|
|
364
|
+
): void => {
|
|
365
|
+
for (const contour of trail.contours ?? []) {
|
|
366
|
+
register(contour, contours, trails, signals, resources);
|
|
367
|
+
}
|
|
129
368
|
};
|
|
130
369
|
|
|
131
370
|
const markUniqueObject = (
|
|
@@ -144,41 +383,73 @@ const markUniqueObject = (
|
|
|
144
383
|
|
|
145
384
|
const registerModuleValue = (
|
|
146
385
|
value: unknown,
|
|
386
|
+
contours: Map<string, AnyContour>,
|
|
147
387
|
trails: Map<string, AnyTrail>,
|
|
148
388
|
signals: Map<string, AnySignal>,
|
|
149
|
-
|
|
389
|
+
resources: Map<string, AnyResource>
|
|
150
390
|
): void => {
|
|
151
|
-
if (
|
|
152
|
-
register(value, trails, signals,
|
|
391
|
+
if (isResource(value) || isRegistrable(value)) {
|
|
392
|
+
register(value, contours, trails, signals, resources);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (isResource(value)) {
|
|
396
|
+
registerResourceSignals(value, signals);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (
|
|
400
|
+
typeof value === 'object' &&
|
|
401
|
+
value !== null &&
|
|
402
|
+
(value as { kind?: unknown }).kind === 'trail'
|
|
403
|
+
) {
|
|
404
|
+
registerTrailContours(
|
|
405
|
+
value as AnyTrail,
|
|
406
|
+
contours,
|
|
407
|
+
trails,
|
|
408
|
+
signals,
|
|
409
|
+
resources
|
|
410
|
+
);
|
|
153
411
|
}
|
|
154
412
|
};
|
|
155
413
|
|
|
156
414
|
const registerModuleValues = (
|
|
157
415
|
mod: Record<string, unknown>,
|
|
416
|
+
contours: Map<string, AnyContour>,
|
|
158
417
|
trails: Map<string, AnyTrail>,
|
|
159
418
|
signals: Map<string, AnySignal>,
|
|
160
|
-
|
|
419
|
+
resources: Map<string, AnyResource>
|
|
161
420
|
): void => {
|
|
162
421
|
const seenValues = new WeakSet<object>();
|
|
163
422
|
for (const value of Object.values(mod)) {
|
|
164
423
|
if (!markUniqueObject(value, seenValues)) {
|
|
165
424
|
continue;
|
|
166
425
|
}
|
|
167
|
-
registerModuleValue(value, trails, signals,
|
|
426
|
+
registerModuleValue(value, contours, trails, signals, resources);
|
|
168
427
|
}
|
|
169
428
|
};
|
|
170
429
|
|
|
171
430
|
export const topo = (
|
|
172
|
-
|
|
431
|
+
nameOrIdentity: string | TopoIdentity,
|
|
173
432
|
...modules: Record<string, unknown>[]
|
|
174
433
|
): Topo => {
|
|
434
|
+
const identity: TopoIdentity =
|
|
435
|
+
typeof nameOrIdentity === 'string'
|
|
436
|
+
? { name: nameOrIdentity }
|
|
437
|
+
: nameOrIdentity;
|
|
438
|
+
|
|
439
|
+
const contours = new Map<string, AnyContour>();
|
|
175
440
|
const trails = new Map<string, AnyTrail>();
|
|
176
441
|
const signals = new Map<string, AnySignal>();
|
|
177
|
-
const
|
|
442
|
+
const resources = new Map<string, AnyResource>();
|
|
178
443
|
|
|
179
444
|
for (const mod of modules) {
|
|
180
|
-
registerModuleValues(mod, trails, signals,
|
|
445
|
+
registerModuleValues(mod, contours, trails, signals, resources);
|
|
181
446
|
}
|
|
182
447
|
|
|
183
|
-
return createTopo(
|
|
448
|
+
return createTopo(
|
|
449
|
+
identity,
|
|
450
|
+
contours,
|
|
451
|
+
finalizeTrailSignals(trails, resources),
|
|
452
|
+
signals,
|
|
453
|
+
resources
|
|
454
|
+
);
|
|
184
455
|
};
|