@ontrails/core 1.0.0-beta.14 → 1.0.0-beta.16
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 +74 -0
- package/README.md +73 -24
- package/package.json +11 -4
- package/src/activation-provenance.ts +111 -0
- package/src/activation-source-compatibility.ts +430 -0
- package/src/activation-source-projection.ts +221 -0
- package/src/activation-source.ts +91 -0
- package/src/blob-ref.ts +51 -0
- package/src/branded.ts +1 -1
- package/src/context.ts +28 -4
- package/src/contour.ts +344 -0
- package/src/cross-batch.ts +69 -0
- package/src/cross-schema.ts +36 -0
- package/src/detours.ts +8 -0
- package/src/draft.ts +60 -52
- package/src/error-projection.ts +51 -0
- package/src/errors.ts +245 -48
- package/src/execute.ts +1250 -40
- package/src/fire.ts +1169 -0
- package/src/index.ts +383 -49
- package/src/internal/fork-ctx.ts +69 -0
- package/src/layer-projection.ts +192 -0
- package/src/layer.ts +81 -0
- package/src/observe.ts +361 -0
- package/src/path-security.ts +1 -1
- package/src/permits.ts +12 -0
- package/src/redaction/patterns.ts +6 -3
- package/src/resilience.ts +11 -11
- package/src/resource-config.ts +792 -0
- package/src/resource.ts +175 -0
- package/src/result.ts +18 -0
- package/src/run.ts +7 -6
- package/src/schedule-runtime.ts +689 -0
- package/src/schedule.ts +324 -0
- package/src/serialization.ts +90 -55
- package/src/signal-diagnostics.ts +633 -0
- package/src/signal-ref.ts +87 -0
- package/src/signal.ts +37 -0
- package/src/store/accessor-protocol.ts +56 -0
- package/src/store/index.ts +4 -0
- package/src/structured-examples.ts +245 -0
- package/src/surface-derivation.ts +91 -0
- package/src/surface-filter.ts +176 -0
- package/src/topo.ts +710 -59
- package/src/tracing.ts +345 -0
- package/src/trail.ts +347 -33
- package/src/trails/derive-trail.ts +842 -0
- package/src/trails/index.ts +9 -0
- package/src/trails/ingest.ts +138 -0
- package/src/{internal/trails-db.ts → trails-db.ts} +50 -31
- package/src/transport-error-map.ts +174 -0
- package/src/type-utils.ts +17 -2
- package/src/types.ts +208 -17
- package/src/validate-established-topo.ts +8 -2
- package/src/validate-topo.ts +244 -13
- package/src/validation.ts +58 -1
- package/src/webhook.ts +276 -0
- package/src/workspace.ts +1 -1
- package/src/zod-wrappers.ts +72 -0
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -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/blob-ref.d.ts +0 -20
- package/dist/blob-ref.d.ts.map +0 -1
- package/dist/blob-ref.js +0 -22
- package/dist/blob-ref.js.map +0 -1
- package/dist/branded.d.ts +0 -36
- package/dist/branded.d.ts.map +0 -1
- package/dist/branded.js +0 -89
- package/dist/branded.js.map +0 -1
- package/dist/collections.d.ts +0 -31
- package/dist/collections.d.ts.map +0 -1
- package/dist/collections.js +0 -60
- package/dist/collections.js.map +0 -1
- package/dist/context.d.ts +0 -10
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -21
- package/dist/context.js.map +0 -1
- package/dist/derive.d.ts +0 -44
- package/dist/derive.d.ts.map +0 -1
- package/dist/derive.js +0 -149
- package/dist/derive.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/draft.d.ts +0 -28
- package/dist/draft.d.ts.map +0 -1
- package/dist/draft.js +0 -156
- package/dist/draft.js.map +0 -1
- package/dist/errors.d.ts +0 -83
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -142
- package/dist/errors.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/execute.d.ts +0 -35
- package/dist/execute.d.ts.map +0 -1
- package/dist/execute.js +0 -90
- package/dist/execute.js.map +0 -1
- package/dist/fetch.d.ts +0 -15
- package/dist/fetch.d.ts.map +0 -1
- package/dist/fetch.js +0 -102
- package/dist/fetch.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/guards.d.ts +0 -17
- package/dist/guards.d.ts.map +0 -1
- package/dist/guards.js +0 -25
- package/dist/guards.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/index.d.ts +0 -48
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -51
- package/dist/index.js.map +0 -1
- package/dist/internal/topo-saves.d.ts +0 -47
- package/dist/internal/topo-saves.d.ts.map +0 -1
- package/dist/internal/topo-saves.js +0 -310
- package/dist/internal/topo-saves.js.map +0 -1
- package/dist/internal/topo-store-read.d.ts +0 -67
- package/dist/internal/topo-store-read.d.ts.map +0 -1
- package/dist/internal/topo-store-read.js +0 -222
- package/dist/internal/topo-store-read.js.map +0 -1
- package/dist/internal/topo-store.d.ts +0 -12
- package/dist/internal/topo-store.d.ts.map +0 -1
- package/dist/internal/topo-store.js +0 -571
- package/dist/internal/topo-store.js.map +0 -1
- package/dist/internal/trails-db.d.ts +0 -16
- package/dist/internal/trails-db.d.ts.map +0 -1
- package/dist/internal/trails-db.js +0 -118
- package/dist/internal/trails-db.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/layer.d.ts +0 -6
- package/dist/layer.d.ts.map +0 -1
- package/dist/layer.js +0 -4
- package/dist/layer.js.map +0 -1
- package/dist/path-security.d.ts +0 -28
- package/dist/path-security.d.ts.map +0 -1
- package/dist/path-security.js +0 -63
- package/dist/path-security.js.map +0 -1
- package/dist/patterns/bulk.d.ts +0 -15
- package/dist/patterns/bulk.d.ts.map +0 -1
- package/dist/patterns/bulk.js +0 -14
- package/dist/patterns/bulk.js.map +0 -1
- package/dist/patterns/change.d.ts +0 -10
- package/dist/patterns/change.d.ts.map +0 -1
- package/dist/patterns/change.js +0 -10
- package/dist/patterns/change.js.map +0 -1
- package/dist/patterns/date-range.d.ts +0 -10
- package/dist/patterns/date-range.d.ts.map +0 -1
- package/dist/patterns/date-range.js +0 -10
- package/dist/patterns/date-range.js.map +0 -1
- package/dist/patterns/index.d.ts +0 -9
- package/dist/patterns/index.d.ts.map +0 -1
- package/dist/patterns/index.js +0 -9
- package/dist/patterns/index.js.map +0 -1
- package/dist/patterns/pagination.d.ts +0 -18
- package/dist/patterns/pagination.d.ts.map +0 -1
- package/dist/patterns/pagination.js +0 -18
- package/dist/patterns/pagination.js.map +0 -1
- package/dist/patterns/progress.d.ts +0 -11
- package/dist/patterns/progress.d.ts.map +0 -1
- package/dist/patterns/progress.js +0 -11
- package/dist/patterns/progress.js.map +0 -1
- package/dist/patterns/sorting.d.ts +0 -13
- package/dist/patterns/sorting.d.ts.map +0 -1
- package/dist/patterns/sorting.js +0 -10
- package/dist/patterns/sorting.js.map +0 -1
- package/dist/patterns/status.d.ts +0 -15
- package/dist/patterns/status.d.ts.map +0 -1
- package/dist/patterns/status.js +0 -9
- package/dist/patterns/status.js.map +0 -1
- package/dist/patterns/timestamps.d.ts +0 -10
- package/dist/patterns/timestamps.d.ts.map +0 -1
- package/dist/patterns/timestamps.js +0 -10
- package/dist/patterns/timestamps.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/redaction/index.d.ts +0 -4
- package/dist/redaction/index.d.ts.map +0 -1
- package/dist/redaction/index.js +0 -3
- package/dist/redaction/index.js.map +0 -1
- package/dist/redaction/patterns.d.ts +0 -9
- package/dist/redaction/patterns.d.ts.map +0 -1
- package/dist/redaction/patterns.js +0 -39
- package/dist/redaction/patterns.js.map +0 -1
- package/dist/redaction/redactor.d.ts +0 -27
- package/dist/redaction/redactor.d.ts.map +0 -1
- package/dist/redaction/redactor.js +0 -89
- package/dist/redaction/redactor.js.map +0 -1
- package/dist/resilience.d.ts +0 -34
- package/dist/resilience.d.ts.map +0 -1
- package/dist/resilience.js +0 -164
- package/dist/resilience.js.map +0 -1
- package/dist/result.d.ts +0 -57
- package/dist/result.d.ts.map +0 -1
- package/dist/result.js +0 -156
- package/dist/result.js.map +0 -1
- package/dist/run.d.ts +0 -27
- package/dist/run.d.ts.map +0 -1
- package/dist/run.js +0 -34
- package/dist/run.js.map +0 -1
- package/dist/serialization.d.ts +0 -27
- package/dist/serialization.d.ts.map +0 -1
- package/dist/serialization.js +0 -153
- package/dist/serialization.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/dist/signal.d.ts +0 -33
- package/dist/signal.d.ts.map +0 -1
- package/dist/signal.js +0 -17
- package/dist/signal.js.map +0 -1
- package/dist/topo-store.d.ts +0 -48
- package/dist/topo-store.d.ts.map +0 -1
- package/dist/topo-store.js +0 -175
- package/dist/topo-store.js.map +0 -1
- package/dist/topo.d.ts +0 -25
- package/dist/topo.d.ts.map +0 -1
- package/dist/topo.js +0 -112
- package/dist/topo.js.map +0 -1
- package/dist/trail.d.ts +0 -96
- package/dist/trail.d.ts.map +0 -1
- package/dist/trail.js +0 -20
- package/dist/trail.js.map +0 -1
- package/dist/type-utils.d.ts +0 -24
- package/dist/type-utils.d.ts.map +0 -1
- package/dist/type-utils.js +0 -12
- package/dist/type-utils.js.map +0 -1
- package/dist/types.d.ts +0 -71
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/validate-established-topo.d.ts +0 -76
- package/dist/validate-established-topo.d.ts.map +0 -1
- package/dist/validate-established-topo.js +0 -43
- package/dist/validate-established-topo.js.map +0 -1
- package/dist/validate-topo.d.ts +0 -24
- package/dist/validate-topo.d.ts.map +0 -1
- package/dist/validate-topo.js +0 -176
- package/dist/validate-topo.js.map +0 -1
- package/dist/validation.d.ts +0 -27
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -165
- package/dist/validation.js.map +0 -1
- package/dist/workspace.d.ts +0 -25
- package/dist/workspace.d.ts.map +0 -1
- package/dist/workspace.js +0 -57
- package/dist/workspace.js.map +0 -1
- package/src/__tests__/blob-ref.test.ts +0 -103
- package/src/__tests__/branded.test.ts +0 -148
- package/src/__tests__/collections.test.ts +0 -126
- package/src/__tests__/context.test.ts +0 -77
- package/src/__tests__/derive.test.ts +0 -260
- package/src/__tests__/errors.test.ts +0 -309
- package/src/__tests__/execute.test.ts +0 -525
- package/src/__tests__/fetch.test.ts +0 -217
- package/src/__tests__/gate.test.ts +0 -125
- package/src/__tests__/guards.test.ts +0 -102
- package/src/__tests__/path-security.test.ts +0 -114
- package/src/__tests__/patterns.test.ts +0 -273
- package/src/__tests__/redaction.test.ts +0 -244
- package/src/__tests__/resilience.test.ts +0 -246
- package/src/__tests__/result.test.ts +0 -155
- package/src/__tests__/run.test.ts +0 -181
- package/src/__tests__/serialization.test.ts +0 -401
- package/src/__tests__/service-config.test.ts +0 -228
- package/src/__tests__/service.test.ts +0 -197
- package/src/__tests__/signal.test.ts +0 -82
- package/src/__tests__/topo-store-read.test.ts +0 -251
- package/src/__tests__/topo-store.test.ts +0 -469
- package/src/__tests__/topo.test.ts +0 -277
- package/src/__tests__/trail-permit.test.ts +0 -60
- package/src/__tests__/trail.test.ts +0 -261
- package/src/__tests__/trails-db.test.ts +0 -191
- package/src/__tests__/type-utils.test.ts +0 -90
- package/src/__tests__/validate-topo.test.ts +0 -489
- package/src/__tests__/validation.test.ts +0 -336
- package/src/__tests__/workspace.test.ts +0 -183
- package/src/event.ts +0 -15
- package/src/gate.ts +0 -44
- package/src/internal/topo-saves.ts +0 -429
- package/src/internal/topo-store-read.ts +0 -473
- package/src/internal/topo-store.ts +0 -1087
- package/src/provision-config.ts +0 -373
- package/src/provision.ts +0 -148
- package/src/topo-store.ts +0 -301
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
package/src/draft.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { AnyContour } from './contour.js';
|
|
2
|
+
import { getContourReferences } from './contour.js';
|
|
1
3
|
import { ValidationError } from './errors.js';
|
|
2
|
-
import type { AnySignal } from './
|
|
3
|
-
import type {
|
|
4
|
+
import type { AnySignal } from './signal.js';
|
|
5
|
+
import type { AnyResource } from './resource.js';
|
|
4
6
|
import { Result } from './result.js';
|
|
5
7
|
import type { Topo } from './topo.js';
|
|
6
8
|
import type { AnyTrail } from './trail.js';
|
|
@@ -9,9 +11,12 @@ export const DRAFT_ID_PREFIX = '_draft.';
|
|
|
9
11
|
|
|
10
12
|
export type DraftDependencyKind =
|
|
11
13
|
| 'cross'
|
|
12
|
-
| '
|
|
13
|
-
| '
|
|
14
|
+
| 'contour'
|
|
15
|
+
| 'resource'
|
|
14
16
|
| 'replaced-by'
|
|
17
|
+
| 'schema-reference'
|
|
18
|
+
| 'signal-fire'
|
|
19
|
+
| 'signal-on'
|
|
15
20
|
| 'signal-from';
|
|
16
21
|
|
|
17
22
|
export interface DraftDependency {
|
|
@@ -22,14 +27,14 @@ export interface DraftDependency {
|
|
|
22
27
|
|
|
23
28
|
export interface DraftFinding {
|
|
24
29
|
readonly id: string;
|
|
25
|
-
readonly kind: '
|
|
30
|
+
readonly kind: 'contour' | 'resource' | 'signal' | 'trail' | 'unknown';
|
|
26
31
|
readonly message: string;
|
|
27
32
|
readonly rule: 'draft-contamination' | 'draft-id';
|
|
28
33
|
readonly via?: DraftDependencyKind | undefined;
|
|
29
34
|
readonly dependsOn?: string | undefined;
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
export interface
|
|
37
|
+
export interface DraftReport {
|
|
33
38
|
readonly contaminatedIds: ReadonlySet<string>;
|
|
34
39
|
readonly declaredDraftIds: ReadonlySet<string>;
|
|
35
40
|
readonly dependencies: readonly DraftDependency[];
|
|
@@ -41,7 +46,7 @@ interface DraftReason {
|
|
|
41
46
|
readonly via: DraftDependencyKind;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
type TopoNode =
|
|
49
|
+
type TopoNode = AnyContour | AnyResource | AnySignal | AnyTrail;
|
|
45
50
|
|
|
46
51
|
const replacedByTarget = (value: TopoNode): string | undefined => {
|
|
47
52
|
const raw = value as unknown as { replacedBy?: unknown };
|
|
@@ -77,59 +82,56 @@ const dependencyFromTarget = (
|
|
|
77
82
|
},
|
|
78
83
|
];
|
|
79
84
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
const trailDependencies = (trail: AnyTrail): DraftDependency[] => [
|
|
86
|
+
...dependenciesFromIds(
|
|
87
|
+
trail.id,
|
|
88
|
+
(trail.contours ?? []).map((contour) => contour.name),
|
|
89
|
+
'contour'
|
|
90
|
+
),
|
|
91
|
+
...dependenciesFromIds(trail.id, trail.crosses, 'cross'),
|
|
92
|
+
...dependenciesFromIds(
|
|
93
|
+
trail.id,
|
|
94
|
+
trail.resources.map(({ id }) => id),
|
|
95
|
+
'resource'
|
|
96
|
+
),
|
|
97
|
+
...dependenciesFromIds(trail.id, trail.fires ?? [], 'signal-fire'),
|
|
98
|
+
...dependenciesFromIds(trail.id, trail.on ?? [], 'signal-on'),
|
|
99
|
+
...dependencyFromTarget(trail.id, replacedByTarget(trail), 'replaced-by'),
|
|
100
|
+
];
|
|
92
101
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
),
|
|
100
|
-
...(detours === undefined
|
|
101
|
-
? []
|
|
102
|
-
: dependenciesFromDetours(trail.id, detours)),
|
|
103
|
-
...dependencyFromTarget(trail.id, replacedByTarget(trail), 'replaced-by'),
|
|
104
|
-
];
|
|
105
|
-
};
|
|
102
|
+
const contourDependencies = (contour: AnyContour): DraftDependency[] =>
|
|
103
|
+
dependenciesFromIds(
|
|
104
|
+
contour.name,
|
|
105
|
+
getContourReferences(contour).map((reference) => reference.contour),
|
|
106
|
+
'schema-reference'
|
|
107
|
+
);
|
|
106
108
|
|
|
107
109
|
const signalDependencies = (signal: AnySignal): DraftDependency[] => [
|
|
108
110
|
...dependenciesFromIds(signal.id, signal.from ?? [], 'signal-from'),
|
|
109
111
|
...dependencyFromTarget(signal.id, replacedByTarget(signal), 'replaced-by'),
|
|
110
112
|
];
|
|
111
113
|
|
|
112
|
-
const
|
|
113
|
-
dependencyFromTarget(
|
|
114
|
-
provision.id,
|
|
115
|
-
replacedByTarget(provision),
|
|
116
|
-
'replaced-by'
|
|
117
|
-
);
|
|
114
|
+
const resourceDependencies = (resource: AnyResource): DraftDependency[] =>
|
|
115
|
+
dependencyFromTarget(resource.id, replacedByTarget(resource), 'replaced-by');
|
|
118
116
|
|
|
119
117
|
const nodeKind = (
|
|
120
118
|
id: string,
|
|
119
|
+
contours: ReadonlyMap<string, AnyContour>,
|
|
121
120
|
trails: ReadonlyMap<string, AnyTrail>,
|
|
122
121
|
signals: ReadonlyMap<string, AnySignal>,
|
|
123
|
-
|
|
122
|
+
resources: ReadonlyMap<string, AnyResource>
|
|
124
123
|
): DraftFinding['kind'] => {
|
|
124
|
+
if (contours.has(id)) {
|
|
125
|
+
return 'contour';
|
|
126
|
+
}
|
|
125
127
|
if (trails.has(id)) {
|
|
126
128
|
return 'trail';
|
|
127
129
|
}
|
|
128
130
|
if (signals.has(id)) {
|
|
129
131
|
return 'signal';
|
|
130
132
|
}
|
|
131
|
-
if (
|
|
132
|
-
return '
|
|
133
|
+
if (resources.has(id)) {
|
|
134
|
+
return 'resource';
|
|
133
135
|
}
|
|
134
136
|
return 'unknown';
|
|
135
137
|
};
|
|
@@ -142,9 +144,10 @@ const draftIdsFromKeys = (keys: Iterable<string>): string[] =>
|
|
|
142
144
|
|
|
143
145
|
const collectDeclaredDraftIds = (topo: Topo): ReadonlySet<string> =>
|
|
144
146
|
new Set([
|
|
147
|
+
...draftIdsFromKeys(topo.contours.keys()),
|
|
145
148
|
...draftIdsFromKeys(topo.trails.keys()),
|
|
146
149
|
...draftIdsFromKeys(topo.signals.keys()),
|
|
147
|
-
...draftIdsFromKeys(topo.
|
|
150
|
+
...draftIdsFromKeys(topo.resources.keys()),
|
|
148
151
|
]);
|
|
149
152
|
|
|
150
153
|
const findingForDraftId = (
|
|
@@ -237,9 +240,10 @@ const contaminationFindingForId = (
|
|
|
237
240
|
id: string,
|
|
238
241
|
declaredDraftIds: ReadonlySet<string>,
|
|
239
242
|
reasons: ReadonlyMap<string, DraftReason>,
|
|
243
|
+
contours: ReadonlyMap<string, AnyContour>,
|
|
240
244
|
trails: ReadonlyMap<string, AnyTrail>,
|
|
241
245
|
signals: ReadonlyMap<string, AnySignal>,
|
|
242
|
-
|
|
246
|
+
resources: ReadonlyMap<string, AnyResource>
|
|
243
247
|
): DraftFinding | undefined => {
|
|
244
248
|
if (declaredDraftIds.has(id)) {
|
|
245
249
|
return undefined;
|
|
@@ -252,7 +256,7 @@ const contaminationFindingForId = (
|
|
|
252
256
|
|
|
253
257
|
return findingForContamination(
|
|
254
258
|
id,
|
|
255
|
-
nodeKind(id, trails, signals,
|
|
259
|
+
nodeKind(id, contours, trails, signals, resources),
|
|
256
260
|
reason
|
|
257
261
|
);
|
|
258
262
|
};
|
|
@@ -261,23 +265,25 @@ const collectFindings = (
|
|
|
261
265
|
declaredDraftIds: ReadonlySet<string>,
|
|
262
266
|
contaminatedIds: ReadonlySet<string>,
|
|
263
267
|
reasons: ReadonlyMap<string, DraftReason>,
|
|
268
|
+
contours: ReadonlyMap<string, AnyContour>,
|
|
264
269
|
trails: ReadonlyMap<string, AnyTrail>,
|
|
265
270
|
signals: ReadonlyMap<string, AnySignal>,
|
|
266
|
-
|
|
271
|
+
resources: ReadonlyMap<string, AnyResource>
|
|
267
272
|
): DraftFinding[] => [
|
|
268
273
|
...[...declaredDraftIds]
|
|
269
274
|
.toSorted()
|
|
270
275
|
.map((id) =>
|
|
271
|
-
findingForDraftId(id, nodeKind(id, trails, signals,
|
|
276
|
+
findingForDraftId(id, nodeKind(id, contours, trails, signals, resources))
|
|
272
277
|
),
|
|
273
278
|
...[...contaminatedIds].toSorted().flatMap((id) => {
|
|
274
279
|
const finding = contaminationFindingForId(
|
|
275
280
|
id,
|
|
276
281
|
declaredDraftIds,
|
|
277
282
|
reasons,
|
|
283
|
+
contours,
|
|
278
284
|
trails,
|
|
279
285
|
signals,
|
|
280
|
-
|
|
286
|
+
resources
|
|
281
287
|
);
|
|
282
288
|
|
|
283
289
|
return finding === undefined ? [] : [finding];
|
|
@@ -285,12 +291,13 @@ const collectFindings = (
|
|
|
285
291
|
];
|
|
286
292
|
|
|
287
293
|
const collectDependencies = (topo: Topo): DraftDependency[] => [
|
|
294
|
+
...[...topo.contours.values()].flatMap(contourDependencies),
|
|
288
295
|
...[...topo.trails.values()].flatMap(trailDependencies),
|
|
289
296
|
...[...topo.signals.values()].flatMap(signalDependencies),
|
|
290
|
-
...[...topo.
|
|
297
|
+
...[...topo.resources.values()].flatMap(resourceDependencies),
|
|
291
298
|
];
|
|
292
299
|
|
|
293
|
-
export const
|
|
300
|
+
export const deriveDraftReport = (topo: Topo): DraftReport => {
|
|
294
301
|
const declaredDraftIds = collectDeclaredDraftIds(topo);
|
|
295
302
|
const dependencies = collectDependencies(topo);
|
|
296
303
|
const { contaminatedIds, reasons } = propagateContaminatedIds(
|
|
@@ -301,9 +308,10 @@ export const analyzeDraftState = (topo: Topo): DraftStateAnalysis => {
|
|
|
301
308
|
declaredDraftIds,
|
|
302
309
|
contaminatedIds,
|
|
303
310
|
reasons,
|
|
311
|
+
topo.contours,
|
|
304
312
|
topo.trails,
|
|
305
313
|
topo.signals,
|
|
306
|
-
topo.
|
|
314
|
+
topo.resources
|
|
307
315
|
);
|
|
308
316
|
|
|
309
317
|
return {
|
|
@@ -314,10 +322,10 @@ export const analyzeDraftState = (topo: Topo): DraftStateAnalysis => {
|
|
|
314
322
|
};
|
|
315
323
|
};
|
|
316
324
|
|
|
317
|
-
export const
|
|
325
|
+
export const validateDraftFreeTopo = (
|
|
318
326
|
topo: Topo
|
|
319
327
|
): Result<void, ValidationError> => {
|
|
320
|
-
const analysis =
|
|
328
|
+
const analysis = deriveDraftReport(topo);
|
|
321
329
|
|
|
322
330
|
if (analysis.findings.length === 0) {
|
|
323
331
|
return Result.ok();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ErrorCategory } from './errors.js';
|
|
2
|
+
import { isTrailsError } from './errors.js';
|
|
3
|
+
import { createRedactor } from './redaction/index.js';
|
|
4
|
+
|
|
5
|
+
const errorRedactor = createRedactor();
|
|
6
|
+
|
|
7
|
+
export const INTERNAL_ERROR_PUBLIC_MESSAGE = 'Internal server error';
|
|
8
|
+
|
|
9
|
+
export interface ErrorDiagnosticsProjection {
|
|
10
|
+
readonly category?: ErrorCategory | undefined;
|
|
11
|
+
readonly context?: Record<string, unknown> | undefined;
|
|
12
|
+
readonly message: string;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly retryable?: boolean | undefined;
|
|
15
|
+
readonly stack?: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const redactErrorString = (value: string): string =>
|
|
19
|
+
errorRedactor.redact(value);
|
|
20
|
+
|
|
21
|
+
export const redactErrorContext = (
|
|
22
|
+
context: Record<string, unknown> | undefined
|
|
23
|
+
): Record<string, unknown> | undefined =>
|
|
24
|
+
context === undefined ? undefined : errorRedactor.redactObject(context);
|
|
25
|
+
|
|
26
|
+
export const redactErrorStack = (
|
|
27
|
+
stack: string | undefined
|
|
28
|
+
): string | undefined =>
|
|
29
|
+
stack === undefined ? undefined : redactErrorString(stack);
|
|
30
|
+
|
|
31
|
+
export const projectErrorDiagnostics = (
|
|
32
|
+
error: Error
|
|
33
|
+
): ErrorDiagnosticsProjection => {
|
|
34
|
+
const context = isTrailsError(error)
|
|
35
|
+
? redactErrorContext(error.context)
|
|
36
|
+
: undefined;
|
|
37
|
+
const stack = redactErrorStack(error.stack);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
...(isTrailsError(error)
|
|
41
|
+
? {
|
|
42
|
+
category: error.category,
|
|
43
|
+
retryable: error.retryable,
|
|
44
|
+
}
|
|
45
|
+
: {}),
|
|
46
|
+
...(context === undefined ? {} : { context }),
|
|
47
|
+
message: redactErrorString(error.message),
|
|
48
|
+
name: error.name || error.constructor.name || 'Error',
|
|
49
|
+
...(stack === undefined ? {} : { stack }),
|
|
50
|
+
};
|
|
51
|
+
};
|
package/src/errors.ts
CHANGED
|
@@ -10,17 +10,20 @@
|
|
|
10
10
|
// Category
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
export const errorCategories = [
|
|
14
|
+
'validation',
|
|
15
|
+
'not_found',
|
|
16
|
+
'conflict',
|
|
17
|
+
'permission',
|
|
18
|
+
'timeout',
|
|
19
|
+
'rate_limit',
|
|
20
|
+
'network',
|
|
21
|
+
'internal',
|
|
22
|
+
'auth',
|
|
23
|
+
'cancelled',
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
export type ErrorCategory = (typeof errorCategories)[number];
|
|
24
27
|
|
|
25
28
|
// ---------------------------------------------------------------------------
|
|
26
29
|
// Base class
|
|
@@ -80,6 +83,16 @@ export class PermissionError extends TrailsError {
|
|
|
80
83
|
readonly retryable = false as const;
|
|
81
84
|
}
|
|
82
85
|
|
|
86
|
+
export class PermitError extends PermissionError {
|
|
87
|
+
constructor(
|
|
88
|
+
message: string,
|
|
89
|
+
options?: { cause?: Error; context?: Record<string, unknown> }
|
|
90
|
+
) {
|
|
91
|
+
super(message, options);
|
|
92
|
+
this.name = 'PermitError';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
83
96
|
export class TimeoutError extends TrailsError {
|
|
84
97
|
readonly category = 'timeout' as const;
|
|
85
98
|
readonly retryable = true as const;
|
|
@@ -109,10 +122,17 @@ export class NetworkError extends TrailsError {
|
|
|
109
122
|
}
|
|
110
123
|
|
|
111
124
|
export class InternalError extends TrailsError {
|
|
125
|
+
readonly category: ErrorCategory = 'internal';
|
|
126
|
+
readonly retryable = false as const;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export class DerivationError extends TrailsError {
|
|
112
130
|
readonly category = 'internal' as const;
|
|
113
131
|
readonly retryable = false as const;
|
|
114
132
|
}
|
|
115
133
|
|
|
134
|
+
export class RecoverableCompletionError extends InternalError {}
|
|
135
|
+
|
|
116
136
|
export class AuthError extends TrailsError {
|
|
117
137
|
readonly category = 'auth' as const;
|
|
118
138
|
readonly retryable = false as const;
|
|
@@ -123,48 +143,225 @@ export class CancelledError extends TrailsError {
|
|
|
123
143
|
readonly retryable = false as const;
|
|
124
144
|
}
|
|
125
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Returned when a detour exhausts all recovery attempts.
|
|
148
|
+
*
|
|
149
|
+
* Inherits the wrapped error's category for surface mapping (e.g. a
|
|
150
|
+
* `RetryExhaustedError<ConflictError>` maps to HTTP 409), but always
|
|
151
|
+
* sets `retryable = false` to prevent amplification across `ctx.cross()`
|
|
152
|
+
* boundaries or stacked layers.
|
|
153
|
+
*/
|
|
154
|
+
export class RetryExhaustedError<
|
|
155
|
+
TErr extends TrailsError = TrailsError,
|
|
156
|
+
> extends InternalError {
|
|
157
|
+
readonly category: ErrorCategory;
|
|
158
|
+
readonly cause: TErr;
|
|
159
|
+
|
|
160
|
+
/** Number of recovery attempts made before exhaustion. */
|
|
161
|
+
readonly attempts: number;
|
|
162
|
+
|
|
163
|
+
/** Name of the detour whose recovery was exhausted. */
|
|
164
|
+
readonly detour: string;
|
|
165
|
+
|
|
166
|
+
constructor(
|
|
167
|
+
wrapped: TErr,
|
|
168
|
+
metadata: { readonly attempts: number; readonly detour: string }
|
|
169
|
+
) {
|
|
170
|
+
super(
|
|
171
|
+
`Recovery exhausted after ${metadata.attempts} attempts: ${wrapped.message}`,
|
|
172
|
+
{ cause: wrapped }
|
|
173
|
+
);
|
|
174
|
+
this.cause = wrapped;
|
|
175
|
+
this.attempts = metadata.attempts;
|
|
176
|
+
this.detour = metadata.detour;
|
|
177
|
+
// Dynamic — inherited from wrapped error at construction time.
|
|
178
|
+
this.category = wrapped.category;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Class registry
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
export type ErrorClassConstructor = new (...args: never[]) => TrailsError;
|
|
187
|
+
|
|
188
|
+
export interface FixedErrorClassRegistryEntry {
|
|
189
|
+
readonly category: ErrorCategory;
|
|
190
|
+
readonly ctor: ErrorClassConstructor;
|
|
191
|
+
readonly name: string;
|
|
192
|
+
readonly retryable: boolean;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export interface DynamicErrorClassRegistryEntry {
|
|
196
|
+
readonly category: 'dynamic';
|
|
197
|
+
readonly ctor: ErrorClassConstructor;
|
|
198
|
+
readonly inheritsCategoryFrom: 'wrapped-error';
|
|
199
|
+
readonly name: string;
|
|
200
|
+
readonly retryable: false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export type ErrorClassRegistryEntry =
|
|
204
|
+
| DynamicErrorClassRegistryEntry
|
|
205
|
+
| FixedErrorClassRegistryEntry;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Authored registry of concrete TrailsError classes.
|
|
209
|
+
*
|
|
210
|
+
* JavaScript cannot enumerate subclasses at runtime, so rule and projection
|
|
211
|
+
* tooling should walk this owner-held list instead of hardcoding parallel
|
|
212
|
+
* class-name tables. `RetryExhaustedError` is marked dynamic because it
|
|
213
|
+
* inherits its runtime category from the wrapped error rather than always
|
|
214
|
+
* mapping as `internal`.
|
|
215
|
+
*/
|
|
216
|
+
export const errorClasses = [
|
|
217
|
+
{
|
|
218
|
+
category: 'validation',
|
|
219
|
+
ctor: ValidationError,
|
|
220
|
+
name: 'ValidationError',
|
|
221
|
+
retryable: false,
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
category: 'validation',
|
|
225
|
+
ctor: AmbiguousError,
|
|
226
|
+
name: 'AmbiguousError',
|
|
227
|
+
retryable: false,
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
category: 'internal',
|
|
231
|
+
ctor: AssertionError,
|
|
232
|
+
name: 'AssertionError',
|
|
233
|
+
retryable: false,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
category: 'not_found',
|
|
237
|
+
ctor: NotFoundError,
|
|
238
|
+
name: 'NotFoundError',
|
|
239
|
+
retryable: false,
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
category: 'conflict',
|
|
243
|
+
ctor: AlreadyExistsError,
|
|
244
|
+
name: 'AlreadyExistsError',
|
|
245
|
+
retryable: false,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
category: 'conflict',
|
|
249
|
+
ctor: ConflictError,
|
|
250
|
+
name: 'ConflictError',
|
|
251
|
+
retryable: false,
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
category: 'permission',
|
|
255
|
+
ctor: PermissionError,
|
|
256
|
+
name: 'PermissionError',
|
|
257
|
+
retryable: false,
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
category: 'permission',
|
|
261
|
+
ctor: PermitError,
|
|
262
|
+
name: 'PermitError',
|
|
263
|
+
retryable: false,
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
category: 'timeout',
|
|
267
|
+
ctor: TimeoutError,
|
|
268
|
+
name: 'TimeoutError',
|
|
269
|
+
retryable: true,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
category: 'rate_limit',
|
|
273
|
+
ctor: RateLimitError,
|
|
274
|
+
name: 'RateLimitError',
|
|
275
|
+
retryable: true,
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
category: 'network',
|
|
279
|
+
ctor: NetworkError,
|
|
280
|
+
name: 'NetworkError',
|
|
281
|
+
retryable: true,
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
category: 'internal',
|
|
285
|
+
ctor: InternalError,
|
|
286
|
+
name: 'InternalError',
|
|
287
|
+
retryable: false,
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
category: 'internal',
|
|
291
|
+
ctor: DerivationError,
|
|
292
|
+
name: 'DerivationError',
|
|
293
|
+
retryable: false,
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
category: 'internal',
|
|
297
|
+
ctor: RecoverableCompletionError,
|
|
298
|
+
name: 'RecoverableCompletionError',
|
|
299
|
+
retryable: false,
|
|
300
|
+
},
|
|
301
|
+
{ category: 'auth', ctor: AuthError, name: 'AuthError', retryable: false },
|
|
302
|
+
{
|
|
303
|
+
category: 'cancelled',
|
|
304
|
+
ctor: CancelledError,
|
|
305
|
+
name: 'CancelledError',
|
|
306
|
+
retryable: false,
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
category: 'dynamic',
|
|
310
|
+
ctor: RetryExhaustedError,
|
|
311
|
+
inheritsCategoryFrom: 'wrapped-error',
|
|
312
|
+
name: 'RetryExhaustedError',
|
|
313
|
+
retryable: false,
|
|
314
|
+
},
|
|
315
|
+
] as const satisfies readonly ErrorClassRegistryEntry[];
|
|
316
|
+
|
|
126
317
|
// ---------------------------------------------------------------------------
|
|
127
318
|
// Taxonomy maps
|
|
128
319
|
// ---------------------------------------------------------------------------
|
|
129
320
|
|
|
130
|
-
export
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
network: 7,
|
|
136
|
-
not_found: 2,
|
|
137
|
-
permission: 4,
|
|
138
|
-
rate_limit: 6,
|
|
139
|
-
timeout: 5,
|
|
140
|
-
validation: 1,
|
|
141
|
-
} as const;
|
|
321
|
+
export interface ErrorCategoryCodes {
|
|
322
|
+
readonly exit: number;
|
|
323
|
+
readonly http: number;
|
|
324
|
+
readonly jsonRpc: number;
|
|
325
|
+
}
|
|
142
326
|
|
|
143
|
-
export const
|
|
144
|
-
auth: 401,
|
|
145
|
-
cancelled: 499,
|
|
146
|
-
conflict: 409,
|
|
147
|
-
internal: 500,
|
|
148
|
-
network: 502,
|
|
149
|
-
not_found: 404,
|
|
150
|
-
permission: 403,
|
|
151
|
-
rate_limit: 429,
|
|
152
|
-
timeout: 504,
|
|
153
|
-
validation: 400,
|
|
154
|
-
} as const
|
|
327
|
+
export const codesByCategory = {
|
|
328
|
+
auth: { exit: 9, http: 401, jsonRpc: -32_600 },
|
|
329
|
+
cancelled: { exit: 130, http: 499, jsonRpc: -32_603 },
|
|
330
|
+
conflict: { exit: 3, http: 409, jsonRpc: -32_603 },
|
|
331
|
+
internal: { exit: 8, http: 500, jsonRpc: -32_603 },
|
|
332
|
+
network: { exit: 7, http: 502, jsonRpc: -32_603 },
|
|
333
|
+
not_found: { exit: 2, http: 404, jsonRpc: -32_601 },
|
|
334
|
+
permission: { exit: 4, http: 403, jsonRpc: -32_600 },
|
|
335
|
+
rate_limit: { exit: 6, http: 429, jsonRpc: -32_603 },
|
|
336
|
+
timeout: { exit: 5, http: 504, jsonRpc: -32_603 },
|
|
337
|
+
validation: { exit: 1, http: 400, jsonRpc: -32_602 },
|
|
338
|
+
} as const satisfies Record<ErrorCategory, ErrorCategoryCodes>;
|
|
155
339
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
340
|
+
const deriveCodeMap = <TCode extends keyof ErrorCategoryCodes>(
|
|
341
|
+
code: TCode
|
|
342
|
+
): {
|
|
343
|
+
readonly [TCategory in ErrorCategory]: (typeof codesByCategory)[TCategory][TCode];
|
|
344
|
+
} => ({
|
|
345
|
+
auth: codesByCategory.auth[code],
|
|
346
|
+
cancelled: codesByCategory.cancelled[code],
|
|
347
|
+
conflict: codesByCategory.conflict[code],
|
|
348
|
+
internal: codesByCategory.internal[code],
|
|
349
|
+
network: codesByCategory.network[code],
|
|
350
|
+
not_found: codesByCategory.not_found[code],
|
|
351
|
+
permission: codesByCategory.permission[code],
|
|
352
|
+
rate_limit: codesByCategory.rate_limit[code],
|
|
353
|
+
timeout: codesByCategory.timeout[code],
|
|
354
|
+
validation: codesByCategory.validation[code],
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
/** @deprecated Prefer `codesByCategory[category].exit`. */
|
|
358
|
+
export const exitCodeMap = deriveCodeMap('exit');
|
|
359
|
+
|
|
360
|
+
/** @deprecated Prefer `codesByCategory[category].http`. */
|
|
361
|
+
export const statusCodeMap = deriveCodeMap('http');
|
|
362
|
+
|
|
363
|
+
/** @deprecated Prefer `codesByCategory[category].jsonRpc`. */
|
|
364
|
+
export const jsonRpcCodeMap = deriveCodeMap('jsonRpc');
|
|
168
365
|
|
|
169
366
|
export const retryableMap: Record<ErrorCategory, boolean> = {
|
|
170
367
|
auth: false,
|
|
@@ -190,7 +387,7 @@ export const isTrailsError = (error?: unknown): error is TrailsError =>
|
|
|
190
387
|
/** Returns true if the error is retryable (TrailsError with retryable category). */
|
|
191
388
|
export const isRetryable = (error: Error): boolean => {
|
|
192
389
|
if (isTrailsError(error)) {
|
|
193
|
-
return
|
|
390
|
+
return error.retryable;
|
|
194
391
|
}
|
|
195
392
|
return false;
|
|
196
393
|
};
|