@ontrails/core 1.0.0-beta.14 → 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 +11 -0
- package/README.md +17 -13
- 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/draft.d.ts +5 -5
- package/dist/draft.d.ts.map +1 -1
- package/dist/draft.js +28 -27
- package/dist/draft.js.map +1 -1
- 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 +31 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -13
- 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 +8 -2
- package/dist/internal/topo-saves.d.ts.map +1 -1
- package/dist/internal/topo-saves.js +162 -23
- package/dist/internal/topo-saves.js.map +1 -1
- 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 +23 -20
- package/dist/internal/topo-store-read.d.ts.map +1 -1
- package/dist/internal/topo-store-read.js +82 -83
- package/dist/internal/topo-store-read.js.map +1 -1
- package/dist/internal/topo-store.d.ts +19 -5
- package/dist/internal/topo-store.d.ts.map +1 -1
- package/dist/internal/topo-store.js +174 -124
- package/dist/internal/topo-store.js.map +1 -1
- 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 +2 -2
- package/dist/internal/trails-db.d.ts.map +1 -1
- package/dist/internal/trails-db.js +8 -8
- package/dist/internal/trails-db.js.map +1 -1
- 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 +36 -22
- package/dist/topo-store.d.ts.map +1 -1
- package/dist/topo-store.js +215 -71
- package/dist/topo-store.js.map +1 -1
- 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.js +2 -2
- package/dist/validate-established-topo.js.map +1 -1
- package/dist/validate-topo.d.ts.map +1 -1
- package/dist/validate-topo.js +24 -7
- 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 +8 -2
- 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__/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 +41 -36
- package/src/__tests__/topo-store.test.ts +493 -93
- package/src/__tests__/topo.test.ts +350 -54
- package/src/__tests__/trail.test.ts +189 -24
- package/src/__tests__/trails-db.test.ts +32 -25
- 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 +147 -25
- 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/draft.ts +56 -52
- package/src/errors.ts +62 -18
- package/src/execute.ts +840 -29
- package/src/fire.ts +295 -0
- package/src/index.ts +116 -29
- 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 +140 -132
- package/src/internal/topo-store.ts +291 -186
- package/src/internal/tracing.ts +187 -0
- package/src/internal/trails-db.ts +9 -11
- 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 +313 -104
- 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 +2 -2
- package/src/validate-topo.ts +32 -8
- 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/internal/topo-saves.ts +0 -429
- package/src/provision.ts +0 -148
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
import type { Database, SQLQueryBindings } from 'bun:sqlite';
|
|
2
2
|
|
|
3
|
+
import { getContourReferences } from '../contour.js';
|
|
3
4
|
import { deriveCliPath } from '../derive.js';
|
|
4
5
|
import { Result } from '../result.js';
|
|
5
|
-
import type {
|
|
6
|
+
import type { AnyContour } from '../contour.js';
|
|
7
|
+
import type { AnyResource } from '../resource.js';
|
|
6
8
|
import type { AnySignal } from '../signal.js';
|
|
7
9
|
import type { Topo } from '../topo.js';
|
|
8
10
|
import type { AnyTrail } from '../trail.js';
|
|
9
11
|
import { validateEstablishedTopo } from '../validate-established-topo.js';
|
|
10
12
|
import { zodToJsonSchema } from '../validation.js';
|
|
11
|
-
import type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
import type {
|
|
14
|
+
CreateTopoSnapshotInput,
|
|
15
|
+
TopoSnapshot,
|
|
16
|
+
} from './topo-snapshots.js';
|
|
17
|
+
import {
|
|
18
|
+
ensureTopoSnapshotSchema,
|
|
19
|
+
insertTopoSnapshotRecord,
|
|
20
|
+
} from './topo-snapshots.js';
|
|
21
|
+
|
|
22
|
+
type SurfaceMapEntryRecord = Readonly<Record<string, unknown>> & {
|
|
15
23
|
readonly id: string;
|
|
16
|
-
readonly kind: '
|
|
24
|
+
readonly kind: 'contour' | 'resource' | 'signal' | 'trail';
|
|
17
25
|
};
|
|
18
26
|
|
|
19
|
-
type
|
|
20
|
-
readonly entries: readonly
|
|
27
|
+
type SurfaceMapRecord = Readonly<{
|
|
28
|
+
readonly entries: readonly SurfaceMapEntryRecord[];
|
|
21
29
|
readonly generatedAt: string;
|
|
22
30
|
readonly version: '1.0';
|
|
23
31
|
}>;
|
|
@@ -34,57 +42,71 @@ interface TopoTrailRow {
|
|
|
34
42
|
readonly idempotent: number;
|
|
35
43
|
readonly intent: string;
|
|
36
44
|
readonly meta: string | null;
|
|
37
|
-
readonly
|
|
45
|
+
readonly pattern: string | null;
|
|
46
|
+
readonly snapshotId: string;
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
interface TopoCrossingRow {
|
|
41
|
-
readonly
|
|
50
|
+
readonly snapshotId: string;
|
|
42
51
|
readonly sourceId: string;
|
|
43
52
|
readonly targetId: string;
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
interface
|
|
47
|
-
readonly
|
|
48
|
-
readonly
|
|
55
|
+
interface TopoFiresRow {
|
|
56
|
+
readonly snapshotId: string;
|
|
57
|
+
readonly signalId: string;
|
|
58
|
+
readonly trailId: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface TopoOnRow {
|
|
62
|
+
readonly snapshotId: string;
|
|
63
|
+
readonly signalId: string;
|
|
49
64
|
readonly trailId: string;
|
|
50
65
|
}
|
|
51
66
|
|
|
52
|
-
interface
|
|
67
|
+
interface TopoTrailResourceRow {
|
|
68
|
+
readonly resourceId: string;
|
|
69
|
+
readonly snapshotId: string;
|
|
70
|
+
readonly trailId: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface TopoResourceRow {
|
|
53
74
|
readonly hasHealth: number;
|
|
54
75
|
readonly hasMock: number;
|
|
55
76
|
readonly id: string;
|
|
56
|
-
readonly
|
|
77
|
+
readonly snapshotId: string;
|
|
57
78
|
}
|
|
58
79
|
|
|
59
80
|
interface TopoSignalRow {
|
|
60
81
|
readonly description: string | null;
|
|
61
82
|
readonly id: string;
|
|
62
|
-
readonly
|
|
83
|
+
readonly snapshotId: string;
|
|
63
84
|
}
|
|
64
85
|
|
|
65
86
|
interface TopoTrailSignalRow {
|
|
66
|
-
readonly
|
|
87
|
+
readonly snapshotId: string;
|
|
67
88
|
readonly signalId: string;
|
|
68
89
|
readonly trailId: string;
|
|
69
90
|
}
|
|
70
91
|
|
|
71
|
-
interface
|
|
92
|
+
interface TopoSurfaceRow {
|
|
72
93
|
readonly derivedName: string;
|
|
73
94
|
readonly method: string | null;
|
|
74
|
-
readonly
|
|
95
|
+
readonly snapshotId: string;
|
|
75
96
|
readonly trailId: string;
|
|
76
|
-
readonly
|
|
97
|
+
readonly surface: string;
|
|
77
98
|
}
|
|
78
99
|
|
|
79
100
|
interface TopoExampleRow {
|
|
80
101
|
readonly description: string | null;
|
|
81
102
|
readonly error: string | null;
|
|
82
103
|
readonly expected: string | null;
|
|
104
|
+
readonly expectedMatch: string | null;
|
|
83
105
|
readonly id: string;
|
|
84
106
|
readonly input: string;
|
|
85
107
|
readonly name: string;
|
|
86
108
|
readonly ordinal: number;
|
|
87
|
-
readonly
|
|
109
|
+
readonly snapshotId: string;
|
|
88
110
|
readonly trailId: string;
|
|
89
111
|
}
|
|
90
112
|
|
|
@@ -92,28 +114,28 @@ interface TopoSchemaRow {
|
|
|
92
114
|
readonly jsonSchema: string;
|
|
93
115
|
readonly ownerId: string;
|
|
94
116
|
readonly ownerKind: 'signal' | 'trail';
|
|
95
|
-
readonly
|
|
117
|
+
readonly snapshotId: string;
|
|
96
118
|
readonly schemaKind: 'input' | 'output' | 'payload';
|
|
97
119
|
readonly zodHash: string;
|
|
98
120
|
}
|
|
99
121
|
|
|
100
122
|
interface StoredTopoExportRow {
|
|
101
|
-
readonly
|
|
123
|
+
readonly snapshotId: string;
|
|
102
124
|
readonly serializedLock: string;
|
|
103
|
-
readonly
|
|
104
|
-
readonly
|
|
125
|
+
readonly surfaceHash: string;
|
|
126
|
+
readonly surfaceMap: string;
|
|
105
127
|
}
|
|
106
128
|
|
|
107
129
|
interface StoredTopoExportDbRow {
|
|
108
130
|
readonly serialized_lock: string;
|
|
109
|
-
readonly
|
|
110
|
-
readonly
|
|
131
|
+
readonly surface_hash: string;
|
|
132
|
+
readonly surface_map: string;
|
|
111
133
|
}
|
|
112
134
|
|
|
113
135
|
export interface StoredTopoExport {
|
|
114
136
|
readonly lockContent: string;
|
|
115
|
-
readonly
|
|
116
|
-
readonly
|
|
137
|
+
readonly surfaceHash: string;
|
|
138
|
+
readonly surfaceMapJson: string;
|
|
117
139
|
}
|
|
118
140
|
|
|
119
141
|
interface MaterializedSchemas {
|
|
@@ -136,10 +158,12 @@ interface MaterializedTopoArtifacts {
|
|
|
136
158
|
interface NormalizedTopoProjection {
|
|
137
159
|
readonly crossings: readonly TopoCrossingRow[];
|
|
138
160
|
readonly examples: readonly TopoExampleRow[];
|
|
139
|
-
readonly
|
|
161
|
+
readonly fires: readonly TopoFiresRow[];
|
|
162
|
+
readonly on: readonly TopoOnRow[];
|
|
163
|
+
readonly resources: readonly TopoResourceRow[];
|
|
140
164
|
readonly signals: readonly TopoSignalRow[];
|
|
141
|
-
readonly
|
|
142
|
-
readonly
|
|
165
|
+
readonly surfaces: readonly TopoSurfaceRow[];
|
|
166
|
+
readonly trailResources: readonly TopoTrailResourceRow[];
|
|
143
167
|
readonly trailSignals: readonly TopoTrailSignalRow[];
|
|
144
168
|
readonly trails: readonly TopoTrailRow[];
|
|
145
169
|
}
|
|
@@ -262,7 +286,7 @@ const sortKeys = <T extends Record<string, unknown>>(value: T): T => {
|
|
|
262
286
|
|
|
263
287
|
const normalizeTrailRows = (
|
|
264
288
|
trails: readonly AnyTrail[],
|
|
265
|
-
|
|
289
|
+
snapshotId: string
|
|
266
290
|
): readonly TopoTrailRow[] =>
|
|
267
291
|
trails.map((trail) => ({
|
|
268
292
|
description: trail.description ?? null,
|
|
@@ -273,97 +297,122 @@ const normalizeTrailRows = (
|
|
|
273
297
|
idempotent: trail.idempotent === true ? 1 : 0,
|
|
274
298
|
intent: trail.intent,
|
|
275
299
|
meta: trail.meta === undefined ? null : stableJson(trail.meta),
|
|
276
|
-
|
|
300
|
+
pattern: trail.pattern ?? null,
|
|
301
|
+
snapshotId,
|
|
277
302
|
}));
|
|
278
303
|
|
|
279
304
|
const normalizeCrossingRows = (
|
|
280
305
|
trails: readonly AnyTrail[],
|
|
281
|
-
|
|
306
|
+
snapshotId: string
|
|
282
307
|
): readonly TopoCrossingRow[] =>
|
|
283
308
|
trails.flatMap((trail) =>
|
|
284
309
|
[...new Set(trail.crosses)].toSorted().map((targetId) => ({
|
|
285
|
-
|
|
310
|
+
snapshotId,
|
|
286
311
|
sourceId: trail.id,
|
|
287
312
|
targetId,
|
|
288
313
|
}))
|
|
289
314
|
);
|
|
290
315
|
|
|
291
|
-
const
|
|
316
|
+
export const normalizeFiresRows = (
|
|
317
|
+
trails: readonly AnyTrail[],
|
|
318
|
+
snapshotId: string
|
|
319
|
+
): readonly TopoFiresRow[] =>
|
|
320
|
+
trails.flatMap((trail) =>
|
|
321
|
+
[...new Set(trail.fires)].toSorted().map((signalId) => ({
|
|
322
|
+
signalId,
|
|
323
|
+
snapshotId,
|
|
324
|
+
trailId: trail.id,
|
|
325
|
+
}))
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
export const normalizeOnRows = (
|
|
292
329
|
trails: readonly AnyTrail[],
|
|
293
|
-
|
|
294
|
-
): readonly
|
|
330
|
+
snapshotId: string
|
|
331
|
+
): readonly TopoOnRow[] =>
|
|
295
332
|
trails.flatMap((trail) =>
|
|
296
|
-
[...new Set(trail.
|
|
333
|
+
[...new Set(trail.on)].toSorted().map((signalId) => ({
|
|
334
|
+
signalId,
|
|
335
|
+
snapshotId,
|
|
336
|
+
trailId: trail.id,
|
|
337
|
+
}))
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
const normalizeTrailResourceRows = (
|
|
341
|
+
trails: readonly AnyTrail[],
|
|
342
|
+
snapshotId: string
|
|
343
|
+
): readonly TopoTrailResourceRow[] =>
|
|
344
|
+
trails.flatMap((trail) =>
|
|
345
|
+
[...new Set(trail.resources.map((resource) => resource.id))]
|
|
297
346
|
.toSorted()
|
|
298
|
-
.map((
|
|
299
|
-
|
|
300
|
-
|
|
347
|
+
.map((resourceId) => ({
|
|
348
|
+
resourceId,
|
|
349
|
+
snapshotId,
|
|
301
350
|
trailId: trail.id,
|
|
302
351
|
}))
|
|
303
352
|
);
|
|
304
353
|
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
): readonly
|
|
309
|
-
|
|
310
|
-
hasHealth:
|
|
311
|
-
hasMock:
|
|
312
|
-
id:
|
|
313
|
-
|
|
354
|
+
const normalizeResourceRows = (
|
|
355
|
+
resources: readonly AnyResource[],
|
|
356
|
+
snapshotId: string
|
|
357
|
+
): readonly TopoResourceRow[] =>
|
|
358
|
+
resources.map((resource) => ({
|
|
359
|
+
hasHealth: resource.health === undefined ? 0 : 1,
|
|
360
|
+
hasMock: resource.mock === undefined ? 0 : 1,
|
|
361
|
+
id: resource.id,
|
|
362
|
+
snapshotId,
|
|
314
363
|
}));
|
|
315
364
|
|
|
316
365
|
const normalizeSignalRows = (
|
|
317
366
|
signals: readonly AnySignal[],
|
|
318
|
-
|
|
367
|
+
snapshotId: string
|
|
319
368
|
): readonly TopoSignalRow[] =>
|
|
320
369
|
signals.map((signal) => ({
|
|
321
370
|
description: signal.description ?? null,
|
|
322
371
|
id: signal.id,
|
|
323
|
-
|
|
372
|
+
snapshotId,
|
|
324
373
|
}));
|
|
325
374
|
|
|
326
375
|
const normalizeTrailSignalRows = (
|
|
327
376
|
signals: readonly AnySignal[],
|
|
328
|
-
|
|
377
|
+
snapshotId: string
|
|
329
378
|
): readonly TopoTrailSignalRow[] =>
|
|
330
379
|
signals.flatMap((signal) =>
|
|
331
380
|
[...new Set(signal.from)].toSorted().map((trailId) => ({
|
|
332
|
-
saveId,
|
|
333
381
|
signalId: signal.id,
|
|
382
|
+
snapshotId,
|
|
334
383
|
trailId,
|
|
335
384
|
}))
|
|
336
385
|
);
|
|
337
386
|
|
|
338
387
|
/**
|
|
339
|
-
* Project
|
|
388
|
+
* Project surface rows for stored topo.
|
|
340
389
|
*
|
|
341
|
-
* Currently records only CLI-derived rows. MCP, HTTP, and other
|
|
390
|
+
* Currently records only CLI-derived rows. MCP, HTTP, and other surface
|
|
342
391
|
* projections are intentionally deferred until the topo-store schema supports
|
|
343
|
-
* multi-
|
|
392
|
+
* multi-surface representation. The JSON export (`surface_map` in
|
|
344
393
|
* `topo_exports`) is more faithful for now. See ADR-0015 for the target shape.
|
|
345
394
|
*/
|
|
346
|
-
const
|
|
395
|
+
const normalizeSurfaceRows = (
|
|
347
396
|
trails: readonly AnyTrail[],
|
|
348
|
-
|
|
349
|
-
): readonly
|
|
397
|
+
snapshotId: string
|
|
398
|
+
): readonly TopoSurfaceRow[] =>
|
|
350
399
|
trails.map((trail) => ({
|
|
351
400
|
derivedName: deriveCliPath(trail.id).join(' '),
|
|
352
401
|
method: null,
|
|
353
|
-
|
|
402
|
+
snapshotId,
|
|
403
|
+
surface: 'cli',
|
|
354
404
|
trailId: trail.id,
|
|
355
|
-
trailhead: 'cli',
|
|
356
405
|
}));
|
|
357
406
|
|
|
358
407
|
const buildExampleId = (
|
|
359
|
-
|
|
408
|
+
snapshotId: string,
|
|
360
409
|
trailId: string,
|
|
361
410
|
ordinal: number
|
|
362
|
-
): string => `${
|
|
411
|
+
): string => `${snapshotId}:${trailId}:${ordinal.toString().padStart(4, '0')}`;
|
|
363
412
|
|
|
364
413
|
const normalizeExampleRows = (
|
|
365
414
|
trails: readonly AnyTrail[],
|
|
366
|
-
|
|
415
|
+
snapshotId: string
|
|
367
416
|
): readonly TopoExampleRow[] =>
|
|
368
417
|
trails.flatMap((trail) =>
|
|
369
418
|
(trail.examples ?? []).map((example, index) => ({
|
|
@@ -371,46 +420,52 @@ const normalizeExampleRows = (
|
|
|
371
420
|
error: example.error ?? null,
|
|
372
421
|
expected:
|
|
373
422
|
example.expected === undefined ? null : stableJson(example.expected),
|
|
374
|
-
|
|
423
|
+
expectedMatch:
|
|
424
|
+
example.expectedMatch === undefined
|
|
425
|
+
? null
|
|
426
|
+
: stableJson(example.expectedMatch),
|
|
427
|
+
id: buildExampleId(snapshotId, trail.id, index),
|
|
375
428
|
input: stableJson(example.input),
|
|
376
429
|
name: example.name,
|
|
377
430
|
ordinal: index,
|
|
378
|
-
|
|
431
|
+
snapshotId,
|
|
379
432
|
trailId: trail.id,
|
|
380
433
|
}))
|
|
381
434
|
);
|
|
382
435
|
|
|
383
436
|
const normalizeTopoProjection = (
|
|
384
437
|
topo: Topo,
|
|
385
|
-
|
|
438
|
+
snapshotId: string
|
|
386
439
|
): NormalizedTopoProjection => {
|
|
387
440
|
const trails = topo.list().toSorted((a, b) => a.id.localeCompare(b.id));
|
|
388
|
-
const
|
|
389
|
-
.
|
|
441
|
+
const resources = topo
|
|
442
|
+
.listResources()
|
|
390
443
|
.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
391
444
|
const signals = topo
|
|
392
445
|
.listSignals()
|
|
393
446
|
.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
394
447
|
|
|
395
448
|
return {
|
|
396
|
-
crossings: normalizeCrossingRows(trails,
|
|
397
|
-
examples: normalizeExampleRows(trails,
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
449
|
+
crossings: normalizeCrossingRows(trails, snapshotId),
|
|
450
|
+
examples: normalizeExampleRows(trails, snapshotId),
|
|
451
|
+
fires: normalizeFiresRows(trails, snapshotId),
|
|
452
|
+
on: normalizeOnRows(trails, snapshotId),
|
|
453
|
+
resources: normalizeResourceRows(resources, snapshotId),
|
|
454
|
+
signals: normalizeSignalRows(signals, snapshotId),
|
|
455
|
+
surfaces: normalizeSurfaceRows(trails, snapshotId),
|
|
456
|
+
trailResources: normalizeTrailResourceRows(trails, snapshotId),
|
|
457
|
+
trailSignals: normalizeTrailSignalRows(signals, snapshotId),
|
|
458
|
+
trails: normalizeTrailRows(trails, snapshotId),
|
|
404
459
|
};
|
|
405
460
|
};
|
|
406
461
|
|
|
407
462
|
/**
|
|
408
463
|
* Look up a cached JSON schema by content hash.
|
|
409
464
|
*
|
|
410
|
-
* The query matches on `zod_hash` without filtering by `
|
|
411
|
-
* cache is intentionally cross-
|
|
465
|
+
* The query matches on `zod_hash` without filtering by `snapshot_id` because the
|
|
466
|
+
* cache is intentionally cross-snapshot: if the Zod schema definition has not
|
|
412
467
|
* changed (same hash), the serialized JSON Schema is reused regardless of
|
|
413
|
-
* which
|
|
468
|
+
* which snapshot produced it. This is safe as long as `zodToJsonSchema` is
|
|
414
469
|
* deterministic for a given `_def` hash — the `schemaDefinitionHash` pipeline
|
|
415
470
|
* guarantees that structurally identical schemas produce the same hash.
|
|
416
471
|
*/
|
|
@@ -445,7 +500,7 @@ const resolveSchemaRow = (
|
|
|
445
500
|
db: Database,
|
|
446
501
|
ownerId: string,
|
|
447
502
|
ownerKind: TopoSchemaRow['ownerKind'],
|
|
448
|
-
|
|
503
|
+
snapshotId: string,
|
|
449
504
|
schemaKind: TopoSchemaRow['schemaKind'],
|
|
450
505
|
schema: ZodSchemaInput
|
|
451
506
|
): {
|
|
@@ -467,8 +522,8 @@ const resolveSchemaRow = (
|
|
|
467
522
|
jsonSchema: cachedJson,
|
|
468
523
|
ownerId,
|
|
469
524
|
ownerKind,
|
|
470
|
-
saveId,
|
|
471
525
|
schemaKind,
|
|
526
|
+
snapshotId,
|
|
472
527
|
zodHash,
|
|
473
528
|
},
|
|
474
529
|
value: parseJsonRecord(cachedJson),
|
|
@@ -481,8 +536,8 @@ const resolveSchemaRow = (
|
|
|
481
536
|
jsonSchema: generated.json,
|
|
482
537
|
ownerId,
|
|
483
538
|
ownerKind,
|
|
484
|
-
saveId,
|
|
485
539
|
schemaKind,
|
|
540
|
+
snapshotId,
|
|
486
541
|
zodHash,
|
|
487
542
|
},
|
|
488
543
|
value: generated.value,
|
|
@@ -491,7 +546,7 @@ const resolveSchemaRow = (
|
|
|
491
546
|
|
|
492
547
|
const materializeTrailSchema = (
|
|
493
548
|
db: Database,
|
|
494
|
-
|
|
549
|
+
snapshotId: string,
|
|
495
550
|
trail: AnyTrail
|
|
496
551
|
): {
|
|
497
552
|
readonly rows: readonly TopoSchemaRow[];
|
|
@@ -504,7 +559,7 @@ const materializeTrailSchema = (
|
|
|
504
559
|
db,
|
|
505
560
|
trail.id,
|
|
506
561
|
'trail',
|
|
507
|
-
|
|
562
|
+
snapshotId,
|
|
508
563
|
'input',
|
|
509
564
|
trail.input as ZodSchemaInput
|
|
510
565
|
);
|
|
@@ -522,7 +577,7 @@ const materializeTrailSchema = (
|
|
|
522
577
|
db,
|
|
523
578
|
trail.id,
|
|
524
579
|
'trail',
|
|
525
|
-
|
|
580
|
+
snapshotId,
|
|
526
581
|
'output',
|
|
527
582
|
trail.output as ZodSchemaInput
|
|
528
583
|
);
|
|
@@ -538,7 +593,7 @@ const materializeTrailSchema = (
|
|
|
538
593
|
|
|
539
594
|
const materializeTrailSchemas = (
|
|
540
595
|
db: Database,
|
|
541
|
-
|
|
596
|
+
snapshotId: string,
|
|
542
597
|
trails: readonly AnyTrail[]
|
|
543
598
|
): Pick<MaterializedSchemas, 'rows' | 'trailSchemas'> => {
|
|
544
599
|
const rows: TopoSchemaRow[] = [];
|
|
@@ -551,7 +606,7 @@ const materializeTrailSchemas = (
|
|
|
551
606
|
>();
|
|
552
607
|
|
|
553
608
|
for (const trail of trails) {
|
|
554
|
-
const materialized = materializeTrailSchema(db,
|
|
609
|
+
const materialized = materializeTrailSchema(db, snapshotId, trail);
|
|
555
610
|
rows.push(...materialized.rows);
|
|
556
611
|
trailSchemas.set(trail.id, materialized.value);
|
|
557
612
|
}
|
|
@@ -564,7 +619,7 @@ const materializeTrailSchemas = (
|
|
|
564
619
|
|
|
565
620
|
const materializeSignalSchemas = (
|
|
566
621
|
db: Database,
|
|
567
|
-
|
|
622
|
+
snapshotId: string,
|
|
568
623
|
signals: readonly AnySignal[]
|
|
569
624
|
): Pick<MaterializedSchemas, 'rows' | 'signalPayloads'> => {
|
|
570
625
|
const rows: TopoSchemaRow[] = [];
|
|
@@ -575,7 +630,7 @@ const materializeSignalSchemas = (
|
|
|
575
630
|
db,
|
|
576
631
|
signal.id,
|
|
577
632
|
'signal',
|
|
578
|
-
|
|
633
|
+
snapshotId,
|
|
579
634
|
'payload',
|
|
580
635
|
signal.payload as ZodSchemaInput
|
|
581
636
|
);
|
|
@@ -591,12 +646,12 @@ const materializeSignalSchemas = (
|
|
|
591
646
|
|
|
592
647
|
const materializeSchemas = (
|
|
593
648
|
db: Database,
|
|
594
|
-
|
|
649
|
+
snapshotId: string,
|
|
595
650
|
signals: readonly AnySignal[],
|
|
596
651
|
trails: readonly AnyTrail[]
|
|
597
652
|
): MaterializedSchemas => {
|
|
598
|
-
const trailMaterial = materializeTrailSchemas(db,
|
|
599
|
-
const signalMaterial = materializeSignalSchemas(db,
|
|
653
|
+
const trailMaterial = materializeTrailSchemas(db, snapshotId, trails);
|
|
654
|
+
const signalMaterial = materializeSignalSchemas(db, snapshotId, signals);
|
|
600
655
|
|
|
601
656
|
return {
|
|
602
657
|
rows: [...trailMaterial.rows, ...signalMaterial.rows],
|
|
@@ -636,12 +691,11 @@ const addExtendedMetadata = (
|
|
|
636
691
|
entry['replacedBy'] = raw['replacedBy'];
|
|
637
692
|
}
|
|
638
693
|
|
|
639
|
-
if (trail.detours
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
644
|
-
entry['detours'] = detours;
|
|
694
|
+
if (trail.detours.length > 0) {
|
|
695
|
+
entry['detours'] = trail.detours.map((d) => ({
|
|
696
|
+
maxAttempts: Math.max(1, Math.min(d.maxAttempts ?? 1, 5)),
|
|
697
|
+
on: d.on.name,
|
|
698
|
+
}));
|
|
645
699
|
}
|
|
646
700
|
};
|
|
647
701
|
|
|
@@ -653,9 +707,15 @@ const addTrailRelations = (
|
|
|
653
707
|
entry['crosses'] = trail.crosses.toSorted();
|
|
654
708
|
}
|
|
655
709
|
|
|
656
|
-
if (trail.
|
|
657
|
-
entry['
|
|
658
|
-
.map((
|
|
710
|
+
if (trail.contours.length > 0) {
|
|
711
|
+
entry['contours'] = trail.contours
|
|
712
|
+
.map((contour) => contour.name)
|
|
713
|
+
.toSorted();
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (trail.resources.length > 0) {
|
|
717
|
+
entry['resources'] = trail.resources
|
|
718
|
+
.map((resource) => resource.id)
|
|
659
719
|
.toSorted();
|
|
660
720
|
}
|
|
661
721
|
};
|
|
@@ -688,6 +748,10 @@ const buildTrailEntryBase = (
|
|
|
688
748
|
entry['description'] = trail.description;
|
|
689
749
|
}
|
|
690
750
|
|
|
751
|
+
if (trail.pattern !== undefined) {
|
|
752
|
+
entry['pattern'] = trail.pattern;
|
|
753
|
+
}
|
|
754
|
+
|
|
691
755
|
return {
|
|
692
756
|
entry,
|
|
693
757
|
raw,
|
|
@@ -700,18 +764,18 @@ const trailToEntryRecord = (
|
|
|
700
764
|
readonly input: JsonRecord;
|
|
701
765
|
readonly output?: JsonRecord;
|
|
702
766
|
}>
|
|
703
|
-
):
|
|
767
|
+
): SurfaceMapEntryRecord => {
|
|
704
768
|
const { entry, raw } = buildTrailEntryBase(trail, trailSchema);
|
|
705
769
|
addSafetyMarkers(entry, trail);
|
|
706
770
|
addExtendedMetadata(entry, raw, trail);
|
|
707
771
|
addTrailRelations(entry, trail);
|
|
708
|
-
return sortKeys(entry) as
|
|
772
|
+
return sortKeys(entry) as SurfaceMapEntryRecord;
|
|
709
773
|
};
|
|
710
774
|
|
|
711
775
|
const signalToEntryRecord = (
|
|
712
776
|
signal: AnySignal,
|
|
713
777
|
payloadSchema: JsonRecord
|
|
714
|
-
):
|
|
778
|
+
): SurfaceMapEntryRecord => {
|
|
715
779
|
const raw = signal as unknown as Record<string, unknown>;
|
|
716
780
|
const entry: Record<string, unknown> = {
|
|
717
781
|
exampleCount: 0,
|
|
@@ -733,28 +797,47 @@ const signalToEntryRecord = (
|
|
|
733
797
|
entry['replacedBy'] = raw['replacedBy'];
|
|
734
798
|
}
|
|
735
799
|
|
|
736
|
-
return sortKeys(entry) as
|
|
800
|
+
return sortKeys(entry) as SurfaceMapEntryRecord;
|
|
737
801
|
};
|
|
738
802
|
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
):
|
|
803
|
+
const resourceToEntryRecord = (
|
|
804
|
+
resource: AnyResource
|
|
805
|
+
): SurfaceMapEntryRecord => {
|
|
742
806
|
const entry: Record<string, unknown> = {
|
|
743
807
|
exampleCount: 0,
|
|
744
|
-
id:
|
|
745
|
-
kind: '
|
|
808
|
+
id: resource.id,
|
|
809
|
+
kind: 'resource',
|
|
746
810
|
trailheads: [],
|
|
747
811
|
};
|
|
748
812
|
|
|
749
|
-
if (
|
|
750
|
-
entry['description'] =
|
|
813
|
+
if (resource.description !== undefined) {
|
|
814
|
+
entry['description'] = resource.description;
|
|
751
815
|
}
|
|
752
816
|
|
|
753
|
-
if (
|
|
817
|
+
if (resource.health !== undefined) {
|
|
754
818
|
entry['healthcheck'] = true;
|
|
755
819
|
}
|
|
756
820
|
|
|
757
|
-
return sortKeys(entry) as
|
|
821
|
+
return sortKeys(entry) as SurfaceMapEntryRecord;
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
const contourToEntryRecord = (contour: AnyContour): SurfaceMapEntryRecord => {
|
|
825
|
+
const schema = sortedJsonSchema(contour);
|
|
826
|
+
const entry: Record<string, unknown> = {
|
|
827
|
+
exampleCount: contour.examples?.length ?? 0,
|
|
828
|
+
id: contour.name,
|
|
829
|
+
identity: contour.identity,
|
|
830
|
+
kind: 'contour',
|
|
831
|
+
schema: schema.value,
|
|
832
|
+
trailheads: [],
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
const references = getContourReferences(contour);
|
|
836
|
+
if (references.length > 0) {
|
|
837
|
+
entry['references'] = references;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
return sortKeys(entry) as SurfaceMapEntryRecord;
|
|
758
841
|
};
|
|
759
842
|
|
|
760
843
|
const requireTrailSchema = (
|
|
@@ -782,9 +865,10 @@ const requireSignalPayload = (
|
|
|
782
865
|
return payload;
|
|
783
866
|
};
|
|
784
867
|
|
|
785
|
-
const
|
|
868
|
+
const buildSurfaceMap = (
|
|
869
|
+
contours: readonly AnyContour[],
|
|
786
870
|
generatedAt: string,
|
|
787
|
-
|
|
871
|
+
resources: readonly AnyResource[],
|
|
788
872
|
signalPayloads: ReadonlyMap<string, JsonRecord>,
|
|
789
873
|
signals: readonly AnySignal[],
|
|
790
874
|
trailSchemas: ReadonlyMap<
|
|
@@ -795,8 +879,9 @@ const buildTrailheadMap = (
|
|
|
795
879
|
}>
|
|
796
880
|
>,
|
|
797
881
|
trails: readonly AnyTrail[]
|
|
798
|
-
):
|
|
882
|
+
): SurfaceMapRecord => {
|
|
799
883
|
const entries = [
|
|
884
|
+
...contours.map((contour) => contourToEntryRecord(contour)),
|
|
800
885
|
...trails.map((trail) =>
|
|
801
886
|
trailToEntryRecord(trail, requireTrailSchema(trailSchemas, trail.id))
|
|
802
887
|
),
|
|
@@ -806,7 +891,7 @@ const buildTrailheadMap = (
|
|
|
806
891
|
requireSignalPayload(signalPayloads, signal.id)
|
|
807
892
|
)
|
|
808
893
|
),
|
|
809
|
-
...
|
|
894
|
+
...resources.map((resource) => resourceToEntryRecord(resource)),
|
|
810
895
|
].toSorted((a, b) => a.id.localeCompare(b.id));
|
|
811
896
|
|
|
812
897
|
return {
|
|
@@ -816,21 +901,21 @@ const buildTrailheadMap = (
|
|
|
816
901
|
};
|
|
817
902
|
};
|
|
818
903
|
|
|
819
|
-
const
|
|
820
|
-
const { generatedAt: _unused, ...rest } =
|
|
904
|
+
const hashSurfaceMapRecord = (surfaceMap: SurfaceMapRecord): string => {
|
|
905
|
+
const { generatedAt: _unused, ...rest } = surfaceMap;
|
|
821
906
|
return hashValue(rest);
|
|
822
907
|
};
|
|
823
908
|
|
|
824
909
|
const entryPayload = (
|
|
825
|
-
entry:
|
|
910
|
+
entry: SurfaceMapEntryRecord
|
|
826
911
|
): Readonly<Record<string, unknown>> => {
|
|
827
912
|
const { id: _unusedId, kind: _unusedKind, ...rest } = entry;
|
|
828
913
|
return sortKeys(rest);
|
|
829
914
|
};
|
|
830
915
|
|
|
831
916
|
const entriesForKind = (
|
|
832
|
-
entries: readonly
|
|
833
|
-
kind:
|
|
917
|
+
entries: readonly SurfaceMapEntryRecord[],
|
|
918
|
+
kind: SurfaceMapEntryRecord['kind']
|
|
834
919
|
): Readonly<Record<string, Readonly<Record<string, unknown>>>> =>
|
|
835
920
|
Object.fromEntries(
|
|
836
921
|
entries
|
|
@@ -841,55 +926,60 @@ const entriesForKind = (
|
|
|
841
926
|
const buildSerializedLock = (
|
|
842
927
|
hash: string,
|
|
843
928
|
topo: Topo,
|
|
844
|
-
|
|
929
|
+
surfaceMap: SurfaceMapRecord
|
|
845
930
|
): Readonly<Record<string, unknown>> =>
|
|
846
931
|
sortKeys({
|
|
847
932
|
apps: sortKeys({
|
|
848
933
|
[topo.name]: sortKeys({
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
934
|
+
contours: entriesForKind(surfaceMap.entries, 'contour'),
|
|
935
|
+
resources: entriesForKind(surfaceMap.entries, 'resource'),
|
|
936
|
+
signals: entriesForKind(surfaceMap.entries, 'signal'),
|
|
937
|
+
trails: entriesForKind(surfaceMap.entries, 'trail'),
|
|
852
938
|
}),
|
|
853
939
|
}),
|
|
854
|
-
generatedAt:
|
|
940
|
+
generatedAt: surfaceMap.generatedAt,
|
|
855
941
|
hash,
|
|
856
942
|
version: 1,
|
|
857
943
|
});
|
|
858
944
|
|
|
859
945
|
const buildStoredTopoExport = (
|
|
860
946
|
db: Database,
|
|
861
|
-
|
|
947
|
+
snapshot: TopoSnapshot,
|
|
862
948
|
topo: Topo
|
|
863
949
|
): MaterializedTopoArtifacts => {
|
|
950
|
+
const contours = topo
|
|
951
|
+
.listContours()
|
|
952
|
+
.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
864
953
|
const trails = topo.list().toSorted((a, b) => a.id.localeCompare(b.id));
|
|
865
|
-
const
|
|
866
|
-
.
|
|
954
|
+
const resources = topo
|
|
955
|
+
.listResources()
|
|
867
956
|
.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
868
957
|
const signals = topo
|
|
869
958
|
.listSignals()
|
|
870
959
|
.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
871
|
-
const schemas = materializeSchemas(db,
|
|
872
|
-
const
|
|
873
|
-
|
|
874
|
-
|
|
960
|
+
const schemas = materializeSchemas(db, snapshot.id, signals, trails);
|
|
961
|
+
const surfaceMap = buildSurfaceMap(
|
|
962
|
+
contours,
|
|
963
|
+
snapshot.createdAt,
|
|
964
|
+
resources,
|
|
875
965
|
schemas.signalPayloads,
|
|
876
966
|
signals,
|
|
877
967
|
schemas.trailSchemas,
|
|
878
968
|
trails
|
|
879
969
|
);
|
|
880
|
-
const
|
|
970
|
+
const surfaceHash = hashSurfaceMapRecord(surfaceMap);
|
|
881
971
|
const serializedLock = `${JSON.stringify(
|
|
882
|
-
buildSerializedLock(
|
|
972
|
+
buildSerializedLock(surfaceHash, topo, surfaceMap),
|
|
883
973
|
null,
|
|
884
974
|
2
|
|
885
975
|
)}\n`;
|
|
886
976
|
|
|
887
977
|
return {
|
|
888
978
|
exportRow: {
|
|
889
|
-
saveId: save.id,
|
|
890
979
|
serializedLock,
|
|
891
|
-
|
|
892
|
-
|
|
980
|
+
snapshotId: snapshot.id,
|
|
981
|
+
surfaceHash,
|
|
982
|
+
surfaceMap: `${JSON.stringify(surfaceMap, null, 2)}\n`,
|
|
893
983
|
},
|
|
894
984
|
schemaRows: schemas.rows,
|
|
895
985
|
};
|
|
@@ -914,8 +1004,8 @@ const insertProjectedRows = (
|
|
|
914
1004
|
db,
|
|
915
1005
|
projection.trails,
|
|
916
1006
|
`INSERT INTO topo_trails (
|
|
917
|
-
id, intent, idempotent, has_output, has_examples, example_count, description, meta,
|
|
918
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1007
|
+
id, intent, idempotent, has_output, has_examples, example_count, description, pattern, meta, snapshot_id
|
|
1008
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
919
1009
|
(row) => [
|
|
920
1010
|
row.id,
|
|
921
1011
|
row.intent,
|
|
@@ -924,61 +1014,76 @@ const insertProjectedRows = (
|
|
|
924
1014
|
row.hasExamples,
|
|
925
1015
|
row.exampleCount,
|
|
926
1016
|
row.description,
|
|
1017
|
+
row.pattern,
|
|
927
1018
|
row.meta,
|
|
928
|
-
row.
|
|
1019
|
+
row.snapshotId,
|
|
929
1020
|
]
|
|
930
1021
|
);
|
|
931
1022
|
insertRows(
|
|
932
1023
|
db,
|
|
933
1024
|
projection.crossings,
|
|
934
|
-
'INSERT INTO topo_crossings (source_id, target_id,
|
|
935
|
-
(row) => [row.sourceId, row.targetId, row.
|
|
1025
|
+
'INSERT INTO topo_crossings (source_id, target_id, snapshot_id) VALUES (?, ?, ?)',
|
|
1026
|
+
(row) => [row.sourceId, row.targetId, row.snapshotId]
|
|
936
1027
|
);
|
|
937
1028
|
insertRows(
|
|
938
1029
|
db,
|
|
939
|
-
projection.
|
|
940
|
-
`INSERT INTO
|
|
1030
|
+
projection.trailResources,
|
|
1031
|
+
`INSERT INTO topo_trail_resources (trail_id, resource_id, snapshot_id)
|
|
941
1032
|
VALUES (?, ?, ?)`,
|
|
942
|
-
(row) => [row.trailId, row.
|
|
1033
|
+
(row) => [row.trailId, row.resourceId, row.snapshotId]
|
|
943
1034
|
);
|
|
944
1035
|
insertRows(
|
|
945
1036
|
db,
|
|
946
|
-
projection.
|
|
947
|
-
`INSERT INTO
|
|
1037
|
+
projection.resources,
|
|
1038
|
+
`INSERT INTO topo_resources (id, has_mock, has_health, snapshot_id)
|
|
948
1039
|
VALUES (?, ?, ?, ?)`,
|
|
949
|
-
(row) => [row.id, row.hasMock, row.hasHealth, row.
|
|
1040
|
+
(row) => [row.id, row.hasMock, row.hasHealth, row.snapshotId]
|
|
950
1041
|
);
|
|
951
1042
|
insertRows(
|
|
952
1043
|
db,
|
|
953
1044
|
projection.signals,
|
|
954
|
-
'INSERT INTO topo_signals (id, description,
|
|
955
|
-
(row) => [row.id, row.description, row.
|
|
1045
|
+
'INSERT INTO topo_signals (id, description, snapshot_id) VALUES (?, ?, ?)',
|
|
1046
|
+
(row) => [row.id, row.description, row.snapshotId]
|
|
956
1047
|
);
|
|
957
1048
|
insertRows(
|
|
958
1049
|
db,
|
|
959
1050
|
projection.trailSignals,
|
|
960
|
-
`INSERT INTO topo_trail_signals (trail_id, signal_id,
|
|
1051
|
+
`INSERT INTO topo_trail_signals (trail_id, signal_id, snapshot_id)
|
|
1052
|
+
VALUES (?, ?, ?)`,
|
|
1053
|
+
(row) => [row.trailId, row.signalId, row.snapshotId]
|
|
1054
|
+
);
|
|
1055
|
+
insertRows(
|
|
1056
|
+
db,
|
|
1057
|
+
projection.fires,
|
|
1058
|
+
`INSERT INTO topo_trail_fires (trail_id, signal_id, snapshot_id)
|
|
1059
|
+
VALUES (?, ?, ?)`,
|
|
1060
|
+
(row) => [row.trailId, row.signalId, row.snapshotId]
|
|
1061
|
+
);
|
|
1062
|
+
insertRows(
|
|
1063
|
+
db,
|
|
1064
|
+
projection.on,
|
|
1065
|
+
`INSERT INTO topo_trail_on (trail_id, signal_id, snapshot_id)
|
|
961
1066
|
VALUES (?, ?, ?)`,
|
|
962
|
-
(row) => [row.trailId, row.signalId, row.
|
|
1067
|
+
(row) => [row.trailId, row.signalId, row.snapshotId]
|
|
963
1068
|
);
|
|
964
1069
|
insertRows(
|
|
965
1070
|
db,
|
|
966
|
-
projection.
|
|
967
|
-
`INSERT INTO
|
|
1071
|
+
projection.surfaces,
|
|
1072
|
+
`INSERT INTO topo_surfaces (trail_id, surface, derived_name, method, snapshot_id)
|
|
968
1073
|
VALUES (?, ?, ?, ?, ?)`,
|
|
969
1074
|
(row) => [
|
|
970
1075
|
row.trailId,
|
|
971
|
-
row.
|
|
1076
|
+
row.surface,
|
|
972
1077
|
row.derivedName,
|
|
973
1078
|
row.method,
|
|
974
|
-
row.
|
|
1079
|
+
row.snapshotId,
|
|
975
1080
|
]
|
|
976
1081
|
);
|
|
977
1082
|
insertRows(
|
|
978
1083
|
db,
|
|
979
1084
|
projection.examples,
|
|
980
1085
|
`INSERT INTO topo_examples (
|
|
981
|
-
id, trail_id, ordinal, name, description, input, expected, error,
|
|
1086
|
+
id, trail_id, ordinal, name, description, input, expected, error, snapshot_id
|
|
982
1087
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
983
1088
|
(row) => [
|
|
984
1089
|
row.id,
|
|
@@ -989,7 +1094,7 @@ const insertProjectedRows = (
|
|
|
989
1094
|
row.input,
|
|
990
1095
|
row.expected,
|
|
991
1096
|
row.error,
|
|
992
|
-
row.
|
|
1097
|
+
row.snapshotId,
|
|
993
1098
|
]
|
|
994
1099
|
);
|
|
995
1100
|
};
|
|
@@ -1002,7 +1107,7 @@ const insertSchemaRows = (
|
|
|
1002
1107
|
db,
|
|
1003
1108
|
rows,
|
|
1004
1109
|
`INSERT INTO topo_schemas (
|
|
1005
|
-
owner_id, owner_kind, schema_kind, zod_hash, json_schema,
|
|
1110
|
+
owner_id, owner_kind, schema_kind, zod_hash, json_schema, snapshot_id
|
|
1006
1111
|
) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
1007
1112
|
(row) => [
|
|
1008
1113
|
row.ownerId,
|
|
@@ -1010,7 +1115,7 @@ const insertSchemaRows = (
|
|
|
1010
1115
|
row.schemaKind,
|
|
1011
1116
|
row.zodHash,
|
|
1012
1117
|
row.jsonSchema,
|
|
1013
|
-
row.
|
|
1118
|
+
row.snapshotId,
|
|
1014
1119
|
]
|
|
1015
1120
|
);
|
|
1016
1121
|
};
|
|
@@ -1021,12 +1126,12 @@ const insertStoredExport = (
|
|
|
1021
1126
|
): void => {
|
|
1022
1127
|
db.run(
|
|
1023
1128
|
`INSERT INTO topo_exports (
|
|
1024
|
-
|
|
1129
|
+
snapshot_id, surface_map, surface_hash, serialized_lock
|
|
1025
1130
|
) VALUES (?, ?, ?, ?)`,
|
|
1026
1131
|
[
|
|
1027
|
-
exportRow.
|
|
1028
|
-
exportRow.
|
|
1029
|
-
exportRow.
|
|
1132
|
+
exportRow.snapshotId,
|
|
1133
|
+
exportRow.surfaceMap,
|
|
1134
|
+
exportRow.surfaceHash,
|
|
1030
1135
|
exportRow.serializedLock,
|
|
1031
1136
|
]
|
|
1032
1137
|
);
|
|
@@ -1034,15 +1139,15 @@ const insertStoredExport = (
|
|
|
1034
1139
|
|
|
1035
1140
|
export const getStoredTopoExport = (
|
|
1036
1141
|
db: Database,
|
|
1037
|
-
|
|
1142
|
+
snapshotId: string
|
|
1038
1143
|
): StoredTopoExport | undefined => {
|
|
1039
1144
|
const row = db
|
|
1040
1145
|
.query<StoredTopoExportDbRow, [string]>(
|
|
1041
|
-
`SELECT
|
|
1146
|
+
`SELECT surface_map, surface_hash, serialized_lock
|
|
1042
1147
|
FROM topo_exports
|
|
1043
|
-
WHERE
|
|
1148
|
+
WHERE snapshot_id = ?`
|
|
1044
1149
|
)
|
|
1045
|
-
.get(
|
|
1150
|
+
.get(snapshotId);
|
|
1046
1151
|
|
|
1047
1152
|
if (row === undefined || row === null) {
|
|
1048
1153
|
return undefined;
|
|
@@ -1050,32 +1155,32 @@ export const getStoredTopoExport = (
|
|
|
1050
1155
|
|
|
1051
1156
|
return {
|
|
1052
1157
|
lockContent: row.serialized_lock,
|
|
1053
|
-
|
|
1054
|
-
|
|
1158
|
+
surfaceHash: row.surface_hash,
|
|
1159
|
+
surfaceMapJson: row.surface_map,
|
|
1055
1160
|
};
|
|
1056
1161
|
};
|
|
1057
1162
|
|
|
1058
|
-
export const
|
|
1163
|
+
export const createTopoSnapshot = (
|
|
1059
1164
|
db: Database,
|
|
1060
1165
|
topo: Topo,
|
|
1061
|
-
input?:
|
|
1062
|
-
): Result<
|
|
1166
|
+
input?: CreateTopoSnapshotInput
|
|
1167
|
+
): Result<TopoSnapshot, Error> => {
|
|
1063
1168
|
const validated = validateEstablishedTopo(topo);
|
|
1064
1169
|
if (validated.isErr()) {
|
|
1065
1170
|
return Result.err(validated.error);
|
|
1066
1171
|
}
|
|
1067
1172
|
|
|
1068
|
-
|
|
1173
|
+
ensureTopoSnapshotSchema(db);
|
|
1069
1174
|
|
|
1070
|
-
const
|
|
1175
|
+
const snapshotInput: CreateTopoSnapshotInput = {
|
|
1071
1176
|
...input,
|
|
1072
|
-
|
|
1177
|
+
resourceCount: input?.resourceCount ?? topo.resources.size,
|
|
1073
1178
|
signalCount: input?.signalCount ?? topo.signals.size,
|
|
1074
1179
|
trailCount: input?.trailCount ?? topo.trails.size,
|
|
1075
1180
|
};
|
|
1076
1181
|
|
|
1077
|
-
const
|
|
1078
|
-
const record =
|
|
1182
|
+
const snapshot = db.transaction(() => {
|
|
1183
|
+
const record = insertTopoSnapshotRecord(db, snapshotInput);
|
|
1079
1184
|
const artifacts = buildStoredTopoExport(db, record, topo);
|
|
1080
1185
|
insertProjectedRows(db, normalizeTopoProjection(topo, record.id));
|
|
1081
1186
|
insertSchemaRows(db, artifacts.schemaRows);
|
|
@@ -1083,5 +1188,5 @@ export const persistEstablishedTopoSave = (
|
|
|
1083
1188
|
return record;
|
|
1084
1189
|
})();
|
|
1085
1190
|
|
|
1086
|
-
return Result.ok(
|
|
1191
|
+
return Result.ok(snapshot);
|
|
1087
1192
|
};
|