@ontrails/trails 1.0.0-beta.30 → 1.0.0-beta.32
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 +89 -0
- package/README.md +2 -2
- package/package.json +16 -16
- package/src/cli.ts +1 -0
- package/src/mcp-options.ts +1 -0
- package/src/regrade/config.ts +152 -0
- package/src/release/pack-coherence.ts +1 -3
- package/src/trails/create-adapter.ts +19 -79
- package/src/trails/deprecate.ts +1 -1
- package/src/trails/guide.ts +4 -4
- package/src/trails/load-app.ts +21 -72
- package/src/trails/regrade.ts +227 -7
- package/src/trails/revise.ts +1 -1
- package/src/trails/survey.ts +12 -12
- package/src/trails/topo-activation.ts +14 -385
- package/src/trails/topo-read-support.ts +13 -13
- package/src/trails/topo.ts +2 -2
- package/src/trails/version-lifecycle-support.ts +11 -11
- package/src/trails/warden-guide.ts +9 -4
- package/src/trails/warden.ts +15 -0
|
@@ -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';
|
|
@@ -144,7 +144,7 @@ const readCommittedLock = async (
|
|
|
144
144
|
// Public read-only consumers
|
|
145
145
|
// ---------------------------------------------------------------------------
|
|
146
146
|
|
|
147
|
-
export const
|
|
147
|
+
export const deriveTopoSummary = (
|
|
148
148
|
app: Topo,
|
|
149
149
|
options?: { readonly rootDir?: string }
|
|
150
150
|
): TopoSummaryReport => {
|
|
@@ -160,17 +160,17 @@ export const buildTopoSummary = (
|
|
|
160
160
|
};
|
|
161
161
|
};
|
|
162
162
|
|
|
163
|
-
export const
|
|
163
|
+
export const deriveCurrentTopoBrief = (
|
|
164
164
|
app: Topo,
|
|
165
165
|
_options?: { readonly rootDir?: string }
|
|
166
166
|
): BriefReport => deriveBriefReport(app);
|
|
167
167
|
|
|
168
|
-
export const
|
|
168
|
+
export const deriveCurrentTopoList = (
|
|
169
169
|
app: Topo,
|
|
170
170
|
_options?: { readonly rootDir?: string }
|
|
171
171
|
): SurveyListReport => deriveSurveyList(app);
|
|
172
172
|
|
|
173
|
-
export const
|
|
173
|
+
export const deriveCurrentGuideEntries = (
|
|
174
174
|
app: Topo,
|
|
175
175
|
_options?: { readonly rootDir?: string }
|
|
176
176
|
): readonly {
|
|
@@ -189,7 +189,7 @@ export const buildCurrentGuideEntries = (
|
|
|
189
189
|
}))
|
|
190
190
|
.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
191
191
|
|
|
192
|
-
export const
|
|
192
|
+
export const deriveCurrentTrailDetail = (
|
|
193
193
|
app: Topo,
|
|
194
194
|
id: string,
|
|
195
195
|
options?: CurrentTopoReadOptions
|
|
@@ -203,7 +203,7 @@ export const buildCurrentTrailDetail = (
|
|
|
203
203
|
});
|
|
204
204
|
};
|
|
205
205
|
|
|
206
|
-
export const
|
|
206
|
+
export const deriveCurrentResourceDetail = (
|
|
207
207
|
app: Topo,
|
|
208
208
|
id: string,
|
|
209
209
|
_options?: { readonly rootDir?: string }
|
|
@@ -212,22 +212,22 @@ export const buildCurrentResourceDetail = (
|
|
|
212
212
|
? undefined
|
|
213
213
|
: (deriveResourceDetail(app, id) as CurrentResourceDetail);
|
|
214
214
|
|
|
215
|
-
export const
|
|
215
|
+
export const deriveCurrentSignalDetail = (
|
|
216
216
|
app: Topo,
|
|
217
217
|
id: string,
|
|
218
218
|
_options?: { readonly rootDir?: string }
|
|
219
219
|
): SignalDetailReport | undefined => deriveSignalDetail(app, id);
|
|
220
220
|
|
|
221
|
-
export const
|
|
221
|
+
export const deriveCurrentTopoDetail = (
|
|
222
222
|
app: Topo,
|
|
223
223
|
id: string,
|
|
224
224
|
options?: CurrentTopoReadOptions
|
|
225
225
|
): CurrentTopoDetail | undefined =>
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
226
|
+
deriveCurrentTrailDetail(app, id, options) ??
|
|
227
|
+
deriveCurrentResourceDetail(app, id) ??
|
|
228
|
+
deriveCurrentSignalDetail(app, id);
|
|
229
229
|
|
|
230
|
-
export const
|
|
230
|
+
export const deriveCurrentTopoMatches = (
|
|
231
231
|
app: Topo,
|
|
232
232
|
id: string,
|
|
233
233
|
options?: CurrentTopoReadOptions
|
|
@@ -251,7 +251,7 @@ export const buildCurrentTopoMatches = (
|
|
|
251
251
|
});
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
const resource =
|
|
254
|
+
const resource = deriveCurrentResourceDetail(app, id);
|
|
255
255
|
if (resource !== undefined) {
|
|
256
256
|
matches.push({ detail: resource, kind: 'resource' });
|
|
257
257
|
}
|
package/src/trails/topo.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
|
|
4
4
|
import { withFreshOperatorApp } from './operator-context.js';
|
|
5
5
|
import { activationOverviewOutput } from './topo-output-schemas.js';
|
|
6
|
-
import {
|
|
6
|
+
import { deriveTopoSummary } from './topo-read-support.js';
|
|
7
7
|
import { createIsolatedExampleInput } from './topo-support.js';
|
|
8
8
|
|
|
9
9
|
const summaryOutput = z.object({
|
|
@@ -74,7 +74,7 @@ const summaryOutput = z.object({
|
|
|
74
74
|
export const topoTrail = trail('topo', {
|
|
75
75
|
blaze: async (input, ctx) =>
|
|
76
76
|
withFreshOperatorApp(input, ctx, ({ lease, rootDir }) =>
|
|
77
|
-
Result.ok(
|
|
77
|
+
Result.ok(deriveTopoSummary(lease.app, { rootDir }))
|
|
78
78
|
),
|
|
79
79
|
description: 'Show the current topo summary and entry list',
|
|
80
80
|
examples: [
|
|
@@ -23,7 +23,7 @@ export interface LifecycleCommandInput {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export interface LifecycleWriteResult {
|
|
26
|
-
readonly
|
|
26
|
+
readonly filePath: string;
|
|
27
27
|
readonly trailId: string;
|
|
28
28
|
readonly updated: boolean;
|
|
29
29
|
readonly warnings?: readonly string[] | undefined;
|
|
@@ -32,8 +32,8 @@ export interface LifecycleWriteResult {
|
|
|
32
32
|
interface TrailSourceMatch {
|
|
33
33
|
readonly configEnd: number;
|
|
34
34
|
readonly configStart: number;
|
|
35
|
-
readonly filePath: string;
|
|
36
35
|
readonly source: string;
|
|
36
|
+
readonly sourcePath: string;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
interface PropertyMatch {
|
|
@@ -108,8 +108,8 @@ const findTrailSource = (
|
|
|
108
108
|
return Result.ok({
|
|
109
109
|
configEnd: match.config.end,
|
|
110
110
|
configStart: match.config.start,
|
|
111
|
-
filePath,
|
|
112
111
|
source: source.value,
|
|
112
|
+
sourcePath: filePath,
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
}
|
|
@@ -566,13 +566,13 @@ export const reviseTrailSource = (
|
|
|
566
566
|
blaze?.value
|
|
567
567
|
)
|
|
568
568
|
);
|
|
569
|
-
const written = writeLifecycleSourceFile(match.value.
|
|
569
|
+
const written = writeLifecycleSourceFile(match.value.sourcePath, nextSource);
|
|
570
570
|
if (written.isErr()) {
|
|
571
571
|
return written;
|
|
572
572
|
}
|
|
573
573
|
|
|
574
574
|
return Result.ok({
|
|
575
|
-
|
|
575
|
+
filePath: match.value.sourcePath,
|
|
576
576
|
trailId: trail.id,
|
|
577
577
|
updated: true,
|
|
578
578
|
...(usesForkPlaceholder
|
|
@@ -724,18 +724,18 @@ export const setVersionStatusSource = (
|
|
|
724
724
|
);
|
|
725
725
|
if (nextSource === match.value.source) {
|
|
726
726
|
return Result.ok({
|
|
727
|
-
|
|
727
|
+
filePath: match.value.sourcePath,
|
|
728
728
|
trailId: target.trailId,
|
|
729
729
|
updated: false,
|
|
730
730
|
});
|
|
731
731
|
}
|
|
732
|
-
const written = writeLifecycleSourceFile(match.value.
|
|
732
|
+
const written = writeLifecycleSourceFile(match.value.sourcePath, nextSource);
|
|
733
733
|
if (written.isErr()) {
|
|
734
734
|
return written;
|
|
735
735
|
}
|
|
736
736
|
|
|
737
737
|
return Result.ok({
|
|
738
|
-
|
|
738
|
+
filePath: match.value.sourcePath,
|
|
739
739
|
trailId: target.trailId,
|
|
740
740
|
updated: true,
|
|
741
741
|
});
|
|
@@ -768,18 +768,18 @@ export const forkVersionEntrySource = (
|
|
|
768
768
|
const nextSource = rewrite.source;
|
|
769
769
|
if (nextSource === match.value.source) {
|
|
770
770
|
return Result.ok({
|
|
771
|
-
|
|
771
|
+
filePath: match.value.sourcePath,
|
|
772
772
|
trailId: target.trailId,
|
|
773
773
|
updated: false,
|
|
774
774
|
});
|
|
775
775
|
}
|
|
776
|
-
const written = writeLifecycleSourceFile(match.value.
|
|
776
|
+
const written = writeLifecycleSourceFile(match.value.sourcePath, nextSource);
|
|
777
777
|
if (written.isErr()) {
|
|
778
778
|
return written;
|
|
779
779
|
}
|
|
780
780
|
|
|
781
781
|
return Result.ok({
|
|
782
|
-
|
|
782
|
+
filePath: match.value.sourcePath,
|
|
783
783
|
trailId: target.trailId,
|
|
784
784
|
updated: true,
|
|
785
785
|
...(rewrite.usedPlaceholder
|
|
@@ -37,6 +37,12 @@ const wardenRuleGuideEntrySchema = z.object({
|
|
|
37
37
|
.object({
|
|
38
38
|
class: z.enum(wardenFixClasses),
|
|
39
39
|
safety: z.enum(wardenFixSafeties),
|
|
40
|
+
scanTargets: z
|
|
41
|
+
.object({
|
|
42
|
+
exclude: z.array(z.string()).readonly().optional(),
|
|
43
|
+
extensions: z.array(z.string()).readonly().optional(),
|
|
44
|
+
})
|
|
45
|
+
.optional(),
|
|
40
46
|
})
|
|
41
47
|
.optional(),
|
|
42
48
|
guidance: wardenGuidanceSchema.optional(),
|
|
@@ -54,10 +60,9 @@ const wardenRuleGuideEntrySchema = z.object({
|
|
|
54
60
|
const wardenGuideManifestSchema = z.object({
|
|
55
61
|
generatedFrom: z.object({
|
|
56
62
|
package: z.literal('@ontrails/warden'),
|
|
57
|
-
registries: z
|
|
58
|
-
z.literal('wardenRules'),
|
|
59
|
-
|
|
60
|
-
]),
|
|
63
|
+
registries: z
|
|
64
|
+
.tuple([z.literal('wardenRules'), z.literal('wardenTopoRules')])
|
|
65
|
+
.readonly(),
|
|
61
66
|
source: z.literal('builtin-rule-metadata'),
|
|
62
67
|
}),
|
|
63
68
|
kind: z.literal('trails-warden-guide-manifest'),
|
package/src/trails/warden.ts
CHANGED
|
@@ -66,6 +66,10 @@ const wardenInputSchema = z.object({
|
|
|
66
66
|
prePush: z.boolean().default(false).describe('Use the pre-push preset'),
|
|
67
67
|
refresh: z.boolean().default(false).describe('Alias for --lock refresh'),
|
|
68
68
|
rootDir: z.string().optional().describe('Root directory to scan'),
|
|
69
|
+
scopeExclude: z
|
|
70
|
+
.array(z.string())
|
|
71
|
+
.optional()
|
|
72
|
+
.describe('Root-relative path globs that Warden should not govern'),
|
|
69
73
|
skipLock: z.boolean().default(false).describe('Alias for --lock skip'),
|
|
70
74
|
strict: z.boolean().default(false).describe('Alias for --fail-on warning'),
|
|
71
75
|
summary: z.boolean().default(false).describe('Alias for --format summary'),
|
|
@@ -106,6 +110,16 @@ const pushApps = (
|
|
|
106
110
|
}
|
|
107
111
|
};
|
|
108
112
|
|
|
113
|
+
const pushRepeatedValues = (
|
|
114
|
+
args: string[],
|
|
115
|
+
flag: string,
|
|
116
|
+
values: readonly string[] | undefined
|
|
117
|
+
): void => {
|
|
118
|
+
for (const value of values ?? []) {
|
|
119
|
+
args.push(flag, value);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
109
123
|
export const buildWardenCommandArgs = (
|
|
110
124
|
input: WardenTrailInput
|
|
111
125
|
): readonly string[] => {
|
|
@@ -150,6 +164,7 @@ export const buildWardenCommandArgs = (
|
|
|
150
164
|
pushFlag(args, input.fix, '--fix');
|
|
151
165
|
pushFlag(args, input.adapterCheck, '--adapter-check');
|
|
152
166
|
pushValue(args, '--config-path', input.configPath);
|
|
167
|
+
pushRepeatedValues(args, '--scope-exclude', input.scopeExclude);
|
|
153
168
|
pushApps(args, input.apps);
|
|
154
169
|
|
|
155
170
|
return args;
|