@ontrails/core 1.0.0-beta.11 → 1.0.0-beta.13
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 +67 -30
- package/README.md +19 -19
- package/dist/context.d.ts +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +5 -4
- package/dist/context.js.map +1 -1
- package/dist/derive.d.ts +8 -3
- package/dist/derive.d.ts.map +1 -1
- package/dist/derive.js +7 -7
- package/dist/derive.js.map +1 -1
- package/dist/event.d.ts +4 -41
- package/dist/event.d.ts.map +1 -1
- package/dist/event.js +4 -14
- package/dist/event.js.map +1 -1
- package/dist/execute.d.ts +11 -9
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +34 -151
- package/dist/execute.js.map +1 -1
- package/dist/gate.d.ts +17 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +21 -0
- package/dist/gate.js.map +1 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/layer.d.ts +5 -16
- package/dist/layer.d.ts.map +1 -1
- package/dist/layer.js +3 -20
- package/dist/layer.js.map +1 -1
- package/dist/provision-config.d.ts +22 -0
- package/dist/provision-config.d.ts.map +1 -0
- package/dist/provision-config.js +210 -0
- package/dist/provision-config.js.map +1 -0
- package/dist/provision.d.ts +71 -0
- package/dist/provision.d.ts.map +1 -0
- package/dist/provision.js +56 -0
- package/dist/provision.js.map +1 -0
- package/dist/run.d.ts +27 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +34 -0
- package/dist/run.js.map +1 -0
- package/dist/service-config.d.ts +22 -0
- package/dist/service-config.d.ts.map +1 -0
- package/dist/service-config.js +210 -0
- package/dist/service-config.js.map +1 -0
- package/dist/service.d.ts +39 -37
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +21 -21
- package/dist/service.js.map +1 -1
- package/dist/signal.d.ts +33 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/signal.js +17 -0
- package/dist/signal.js.map +1 -0
- package/dist/topo.d.ts +10 -10
- package/dist/topo.d.ts.map +1 -1
- package/dist/topo.js +48 -35
- package/dist/topo.js.map +1 -1
- package/dist/trail.d.ts +19 -17
- package/dist/trail.d.ts.map +1 -1
- package/dist/trail.js +5 -4
- package/dist/trail.js.map +1 -1
- package/dist/types.d.ts +26 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -1
- package/dist/validate-topo.d.ts +2 -2
- package/dist/validate-topo.js +31 -31
- package/dist/validate-topo.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/context.test.ts +8 -8
- package/src/__tests__/execute.test.ts +124 -122
- package/src/__tests__/{layer.test.ts → gate.test.ts} +26 -26
- package/src/__tests__/{dispatch.test.ts → run.test.ts} +39 -39
- package/src/__tests__/service-config.test.ts +228 -0
- package/src/__tests__/service.test.ts +71 -71
- package/src/__tests__/{event.test.ts → signal.test.ts} +15 -15
- package/src/__tests__/topo.test.ts +54 -54
- package/src/__tests__/trail-permit.test.ts +60 -0
- package/src/__tests__/trail.test.ts +58 -58
- package/src/__tests__/type-utils.test.ts +3 -3
- package/src/__tests__/validate-topo.test.ts +38 -38
- package/src/context.ts +5 -4
- package/src/derive.ts +8 -8
- package/src/event.ts +11 -73
- package/src/execute.ts +59 -238
- package/src/{layer.ts → gate.ts} +13 -13
- package/src/index.ts +26 -21
- package/src/provision-config.ts +373 -0
- package/src/provision.ts +148 -0
- package/src/{dispatch.ts → run.ts} +8 -8
- package/src/signal.ts +65 -0
- package/src/topo.ts +74 -52
- package/src/trail.ts +30 -23
- package/src/types.ts +30 -10
- package/src/validate-topo.ts +33 -33
- package/tsconfig.tsbuildinfo +1 -1
- package/src/service.ts +0 -139
package/dist/topo.js
CHANGED
|
@@ -2,31 +2,30 @@
|
|
|
2
2
|
* Application entry point — scans module exports to build a topology graph.
|
|
3
3
|
*/
|
|
4
4
|
import { ValidationError } from './errors.js';
|
|
5
|
-
import {
|
|
5
|
+
import { isProvision } from './provision.js';
|
|
6
6
|
const isRegistrable = (value) => {
|
|
7
7
|
if (typeof value !== 'object' || value === null) {
|
|
8
8
|
return false;
|
|
9
9
|
}
|
|
10
10
|
const { kind } = value;
|
|
11
|
-
return kind === 'trail' || kind === '
|
|
11
|
+
return kind === 'trail' || kind === 'signal';
|
|
12
12
|
};
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
// Topo implementation
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
16
|
-
const createTopo = (name, trails,
|
|
16
|
+
const createTopo = (name, trails, signals, provisions) => ({
|
|
17
17
|
count: trails.size,
|
|
18
|
-
events,
|
|
19
18
|
get(id) {
|
|
20
19
|
return trails.get(id);
|
|
21
20
|
},
|
|
22
|
-
|
|
23
|
-
return
|
|
21
|
+
getProvision(id) {
|
|
22
|
+
return provisions.get(id);
|
|
24
23
|
},
|
|
25
24
|
has(id) {
|
|
26
25
|
return trails.has(id);
|
|
27
26
|
},
|
|
28
|
-
|
|
29
|
-
return
|
|
27
|
+
hasProvision(id) {
|
|
28
|
+
return provisions.has(id);
|
|
30
29
|
},
|
|
31
30
|
ids() {
|
|
32
31
|
return [...trails.keys()];
|
|
@@ -34,38 +33,39 @@ const createTopo = (name, trails, events, services) => ({
|
|
|
34
33
|
list() {
|
|
35
34
|
return [...trails.values()];
|
|
36
35
|
},
|
|
37
|
-
|
|
38
|
-
return [...
|
|
36
|
+
listProvisions() {
|
|
37
|
+
return [...provisions.values()];
|
|
39
38
|
},
|
|
40
|
-
|
|
41
|
-
return [...
|
|
39
|
+
listSignals() {
|
|
40
|
+
return [...signals.values()];
|
|
42
41
|
},
|
|
43
42
|
name,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return [...
|
|
43
|
+
provisionCount: provisions.size,
|
|
44
|
+
provisionIds() {
|
|
45
|
+
return [...provisions.keys()];
|
|
47
46
|
},
|
|
48
|
-
|
|
47
|
+
provisions,
|
|
48
|
+
signals,
|
|
49
49
|
trails,
|
|
50
50
|
});
|
|
51
51
|
// ---------------------------------------------------------------------------
|
|
52
52
|
// topo()
|
|
53
53
|
// ---------------------------------------------------------------------------
|
|
54
54
|
/** Register a single registrable value into the appropriate map. */
|
|
55
|
-
const register = (value, trails,
|
|
55
|
+
const register = (value, trails, signals, provisions) => {
|
|
56
56
|
const { id } = value;
|
|
57
57
|
const registrars = {
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
throw new ValidationError(`Duplicate
|
|
58
|
+
provision: () => {
|
|
59
|
+
if (provisions.has(id)) {
|
|
60
|
+
throw new ValidationError(`Duplicate provision ID: "${id}"`);
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
provisions.set(id, value);
|
|
63
63
|
},
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
throw new ValidationError(`Duplicate
|
|
64
|
+
signal: () => {
|
|
65
|
+
if (signals.has(id)) {
|
|
66
|
+
throw new ValidationError(`Duplicate signal ID: "${id}"`);
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
signals.set(id, value);
|
|
69
69
|
},
|
|
70
70
|
trail: () => {
|
|
71
71
|
if (trails.has(id)) {
|
|
@@ -76,24 +76,37 @@ const register = (value, trails, events, services) => {
|
|
|
76
76
|
};
|
|
77
77
|
registrars[value.kind]?.();
|
|
78
78
|
};
|
|
79
|
-
const
|
|
79
|
+
const markUniqueObject = (value, seenValues) => {
|
|
80
|
+
if (typeof value !== 'object' || value === null) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (seenValues.has(value)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
seenValues.add(value);
|
|
87
|
+
return true;
|
|
88
|
+
};
|
|
89
|
+
const registerModuleValue = (value, trails, signals, provisions) => {
|
|
90
|
+
if (isProvision(value) || isRegistrable(value)) {
|
|
91
|
+
register(value, trails, signals, provisions);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const registerModuleValues = (mod, trails, signals, provisions) => {
|
|
95
|
+
const seenValues = new WeakSet();
|
|
80
96
|
for (const value of Object.values(mod)) {
|
|
81
|
-
if (
|
|
82
|
-
register(value, trails, events, services);
|
|
97
|
+
if (!markUniqueObject(value, seenValues)) {
|
|
83
98
|
continue;
|
|
84
99
|
}
|
|
85
|
-
|
|
86
|
-
register(value, trails, events, services);
|
|
87
|
-
}
|
|
100
|
+
registerModuleValue(value, trails, signals, provisions);
|
|
88
101
|
}
|
|
89
102
|
};
|
|
90
103
|
export const topo = (name, ...modules) => {
|
|
91
104
|
const trails = new Map();
|
|
92
|
-
const
|
|
93
|
-
const
|
|
105
|
+
const signals = new Map();
|
|
106
|
+
const provisions = new Map();
|
|
94
107
|
for (const mod of modules) {
|
|
95
|
-
registerModuleValues(mod, trails,
|
|
108
|
+
registerModuleValues(mod, trails, signals, provisions);
|
|
96
109
|
}
|
|
97
|
-
return createTopo(name, trails,
|
|
110
|
+
return createTopo(name, trails, signals, provisions);
|
|
98
111
|
};
|
|
99
112
|
//# sourceMappingURL=topo.js.map
|
package/dist/topo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"topo.js","sourceRoot":"","sources":["../src/topo.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"topo.js","sourceRoot":"","sources":["../src/topo.ts"],"names":[],"mappings":"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"}
|
package/dist/trail.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { z } from 'zod';
|
|
2
2
|
import type { FieldOverride } from './derive.js';
|
|
3
3
|
import type { Result } from './result.js';
|
|
4
|
-
import type {
|
|
5
|
-
import type { Implementation, TrailContext } from './types.js';
|
|
4
|
+
import type { AnyProvision } from './provision.js';
|
|
5
|
+
import type { Implementation, PermitRequirement, TrailContext } from './types.js';
|
|
6
6
|
/**
|
|
7
7
|
* A named example for documentation and testing.
|
|
8
8
|
*
|
|
@@ -29,7 +29,7 @@ export interface TrailSpec<I, O> {
|
|
|
29
29
|
/** Zod schema for validating output (optional — some trails are fire-and-forget) */
|
|
30
30
|
readonly output?: z.ZodType<O> | undefined;
|
|
31
31
|
/** The pure function that does the work (sync or async authoring) */
|
|
32
|
-
readonly
|
|
32
|
+
readonly blaze: Implementation<I, O>;
|
|
33
33
|
/** Human-readable description */
|
|
34
34
|
readonly description?: string | undefined;
|
|
35
35
|
/** Named examples for docs and testing */
|
|
@@ -38,28 +38,30 @@ export interface TrailSpec<I, O> {
|
|
|
38
38
|
readonly intent?: 'read' | 'write' | 'destroy' | undefined;
|
|
39
39
|
/** Trail is idempotent (safe to retry) */
|
|
40
40
|
readonly idempotent?: boolean | undefined;
|
|
41
|
-
/** Arbitrary
|
|
42
|
-
readonly
|
|
41
|
+
/** Arbitrary meta for tooling and filtering */
|
|
42
|
+
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
43
43
|
/** Named sets of downstream trail IDs that may be invoked */
|
|
44
44
|
readonly detours?: Readonly<Record<string, readonly string[]>> | undefined;
|
|
45
45
|
/** Per-field overrides for deriveFields() (labels, hints, options) */
|
|
46
46
|
readonly fields?: Readonly<Record<string, FieldOverride>> | undefined;
|
|
47
|
-
/** IDs of downstream trails this trail may invoke via ctx.
|
|
48
|
-
readonly
|
|
49
|
-
/**
|
|
50
|
-
readonly
|
|
47
|
+
/** IDs of downstream trails this trail may invoke via ctx.cross() */
|
|
48
|
+
readonly crosses?: readonly string[] | undefined;
|
|
49
|
+
/** Provisions this trail may access via provision.from(ctx) */
|
|
50
|
+
readonly provisions?: readonly AnyProvision[] | undefined;
|
|
51
|
+
/** Auth requirement: scopes object, 'public', or omitted (undeclared) */
|
|
52
|
+
readonly permit?: PermitRequirement | undefined;
|
|
51
53
|
}
|
|
52
54
|
/** Intent describes what a trail does to the world */
|
|
53
55
|
export type Intent = 'read' | 'write' | 'destroy';
|
|
54
56
|
/** A fully-defined trail — the unit of work in the Trails system */
|
|
55
|
-
export interface Trail<I, O> extends Omit<TrailSpec<I, O>, '
|
|
57
|
+
export interface Trail<I, O> extends Omit<TrailSpec<I, O>, 'blaze' | 'crosses' | 'intent' | 'provisions'> {
|
|
56
58
|
readonly kind: 'trail';
|
|
57
59
|
readonly id: string;
|
|
58
|
-
readonly
|
|
59
|
-
/** IDs of downstream trails this trail may invoke via ctx.
|
|
60
|
-
readonly
|
|
61
|
-
/**
|
|
62
|
-
readonly
|
|
60
|
+
readonly blaze: Implementation<I, O>;
|
|
61
|
+
/** IDs of downstream trails this trail may invoke via ctx.cross() (always present, default []) */
|
|
62
|
+
readonly crosses: readonly string[];
|
|
63
|
+
/** Provisions this trail may access via provision.from(ctx) (always present, default []) */
|
|
64
|
+
readonly provisions: readonly AnyProvision[];
|
|
63
65
|
/** What this trail does to the world (always present, default 'write') */
|
|
64
66
|
readonly intent: Intent;
|
|
65
67
|
}
|
|
@@ -74,14 +76,14 @@ export interface Trail<I, O> extends Omit<TrailSpec<I, O>, 'run' | 'follow' | 'i
|
|
|
74
76
|
* // ID as first argument (recommended for human authoring)
|
|
75
77
|
* const show = trail("entity.show", {
|
|
76
78
|
* input: z.object({ name: z.string() }),
|
|
77
|
-
*
|
|
79
|
+
* blaze: (input) => Result.ok(entity),
|
|
78
80
|
* });
|
|
79
81
|
*
|
|
80
82
|
* // Full spec object (for programmatic generation)
|
|
81
83
|
* const show = trail({
|
|
82
84
|
* id: "entity.show",
|
|
83
85
|
* input: z.object({ name: z.string() }),
|
|
84
|
-
*
|
|
86
|
+
* blaze: (input) => Result.ok(entity),
|
|
85
87
|
* });
|
|
86
88
|
* ```
|
|
87
89
|
*/
|
package/dist/trail.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trail.d.ts","sourceRoot":"","sources":["../src/trail.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"trail.d.ts","sourceRoot":"","sources":["../src/trail.ts"],"names":[],"mappings":"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"}
|
package/dist/trail.js
CHANGED
|
@@ -5,15 +5,16 @@ export function trail(idOrSpec, maybeSpec) {
|
|
|
5
5
|
if (!resolved.spec) {
|
|
6
6
|
throw new TypeError('trail() requires a spec when an id is provided');
|
|
7
7
|
}
|
|
8
|
-
const {
|
|
8
|
+
const { blaze, crosses: rawCrosses, intent: rawIntent, provisions: rawProvisions, ...spec } = resolved.spec;
|
|
9
|
+
const provisions = Object.freeze([...(rawProvisions ?? [])]);
|
|
9
10
|
return Object.freeze({
|
|
10
11
|
...spec,
|
|
11
|
-
|
|
12
|
+
blaze: async (input, ctx) => await blaze(input, ctx),
|
|
13
|
+
crosses: Object.freeze([...(rawCrosses ?? [])]),
|
|
12
14
|
id: resolved.id,
|
|
13
15
|
intent: rawIntent ?? 'write',
|
|
14
16
|
kind: 'trail',
|
|
15
|
-
|
|
16
|
-
services: Object.freeze([...(rawServices ?? [])]),
|
|
17
|
+
provisions,
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
20
|
//# sourceMappingURL=trail.js.map
|
package/dist/trail.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trail.js","sourceRoot":"","sources":["../src/trail.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"trail.js","sourceRoot":"","sources":["../src/trail.ts"],"names":[],"mappings":"AA0HA,MAAM,UAAU,KAAK,CACnB,QAA8D,EAC9D,SAA2B;IAE3B,MAAM,QAAQ,GACZ,OAAO,QAAQ,KAAK,QAAQ;QAC1B,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE;QACnC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAE1C,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,EACJ,KAAK,EACL,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,aAAa,EACzB,GAAG,IAAI,EACR,GAAG,QAAQ,CAAC,IAAI,CAAC;IAClB,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAE7D,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,GAAG,IAAI;QACP,KAAK,EAAE,KAAK,EAAE,KAAQ,EAAE,GAAiB,EAAE,EAAE,CAAC,MAAM,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC;QACrE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC;QAC/C,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,MAAM,EAAE,SAAS,IAAI,OAAO;QAC5B,IAAI,EAAE,OAAgB;QACtB,UAAU;KACX,CAAC,CAAC;AACL,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -7,9 +7,9 @@ import type { Result } from './result.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export type Implementation<I, O> = (input: I, ctx: TrailContext) => Result<O, Error> | Promise<Result<O, Error>>;
|
|
9
9
|
/** Invoke another trail by id — used for trail composition */
|
|
10
|
-
export type
|
|
11
|
-
/** Resolve a
|
|
12
|
-
export type
|
|
10
|
+
export type CrossFn = <O>(id: string, input: unknown) => Promise<Result<O, Error>>;
|
|
11
|
+
/** Resolve a provision instance from the current trail context. */
|
|
12
|
+
export type ProvisionLookup = <T = unknown>(provisionOrId: {
|
|
13
13
|
readonly id: string;
|
|
14
14
|
} | string) => T;
|
|
15
15
|
/** Callback for reporting progress from long-running trails */
|
|
@@ -33,22 +33,39 @@ export interface Logger {
|
|
|
33
33
|
fatal(message: string, data?: Record<string, unknown>): void;
|
|
34
34
|
child(context: Record<string, unknown>): Logger;
|
|
35
35
|
}
|
|
36
|
+
/** Context extension key for the invoking trailhead name. */
|
|
37
|
+
export declare const TRAILHEAD_KEY: "__trails_trailhead";
|
|
38
|
+
/** Minimal permit shape available on TrailContext. Permits extends this. */
|
|
39
|
+
export interface BasePermit {
|
|
40
|
+
readonly id: string;
|
|
41
|
+
readonly scopes: readonly string[];
|
|
42
|
+
}
|
|
36
43
|
/** Runtime context threaded through every trail execution */
|
|
37
44
|
export interface TrailContext {
|
|
38
45
|
readonly requestId: string;
|
|
39
|
-
readonly
|
|
40
|
-
readonly
|
|
41
|
-
readonly permit?:
|
|
46
|
+
readonly abortSignal: AbortSignal;
|
|
47
|
+
readonly cross?: CrossFn | undefined;
|
|
48
|
+
readonly permit?: BasePermit;
|
|
42
49
|
readonly workspaceRoot?: string | undefined;
|
|
43
50
|
readonly logger?: Logger | undefined;
|
|
44
51
|
readonly progress?: ProgressCallback | undefined;
|
|
45
52
|
readonly cwd?: string | undefined;
|
|
46
53
|
readonly env?: Record<string, string | undefined> | undefined;
|
|
47
54
|
readonly extensions?: Readonly<Record<string, unknown>> | undefined;
|
|
48
|
-
readonly
|
|
55
|
+
readonly provision?: ProvisionLookup | undefined;
|
|
49
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Permit requirement declared on a trail spec.
|
|
59
|
+
*
|
|
60
|
+
* A scopes object means the trail requires a permit with those scopes.
|
|
61
|
+
* `'public'` means the trail has explicitly opted out of auth.
|
|
62
|
+
* Omitting the field entirely means the trail hasn't declared an auth posture.
|
|
63
|
+
*/
|
|
64
|
+
export type PermitRequirement = {
|
|
65
|
+
readonly scopes: readonly string[];
|
|
66
|
+
} | 'public';
|
|
50
67
|
/** Input shape used to seed a runtime TrailContext before resolution. */
|
|
51
|
-
export type TrailContextInit = Omit<TrailContext, '
|
|
52
|
-
readonly
|
|
68
|
+
export type TrailContextInit = Omit<TrailContext, 'provision'> & {
|
|
69
|
+
readonly provision?: ProvisionLookup | undefined;
|
|
53
70
|
};
|
|
54
71
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,EAAE,CAAC,IAAI,CACjC,KAAK,EAAE,CAAC,EACR,GAAG,EAAE,YAAY,KACd,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AAElD,8DAA8D;AAC9D,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAgDA,6DAA6D;AAC7D,MAAM,CAAC,MAAM,aAAa,GAAG,oBAA6B,CAAC"}
|
package/dist/validate-topo.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Structural validation for a Topo graph.
|
|
3
3
|
*
|
|
4
|
-
* Checks trail
|
|
4
|
+
* Checks trail crossing references, example input validity, event origin
|
|
5
5
|
* references, and output schema completeness. Returns a Result with all
|
|
6
6
|
* issues collected into a single ValidationError.
|
|
7
7
|
*/
|
|
@@ -16,7 +16,7 @@ export interface TopoIssue {
|
|
|
16
16
|
/**
|
|
17
17
|
* Validate the structural integrity of a Topo graph.
|
|
18
18
|
*
|
|
19
|
-
* Checks
|
|
19
|
+
* Checks crossing references, example inputs, event origins, and output
|
|
20
20
|
* schema presence. Returns `Result.ok()` when no issues are found, or
|
|
21
21
|
* `Result.err(ValidationError)` with all issues in the error context.
|
|
22
22
|
*/
|
package/dist/validate-topo.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Structural validation for a Topo graph.
|
|
3
3
|
*
|
|
4
|
-
* Checks trail
|
|
4
|
+
* Checks trail crossing references, example input validity, event origin
|
|
5
5
|
* references, and output schema completeness. Returns a Result with all
|
|
6
6
|
* issues collected into a single ValidationError.
|
|
7
7
|
*/
|
|
@@ -14,12 +14,12 @@ import { validateInput } from './validation.js';
|
|
|
14
14
|
const WHITE = 0;
|
|
15
15
|
const GRAY = 1;
|
|
16
16
|
const BLACK = 2;
|
|
17
|
-
/** Build an adjacency list and initial color map from trails with
|
|
18
|
-
const
|
|
17
|
+
/** Build an adjacency list and initial color map from trails with crossings. */
|
|
18
|
+
const buildCrossGraph = (trails) => {
|
|
19
19
|
const graph = new Map();
|
|
20
20
|
for (const [id, t] of trails) {
|
|
21
|
-
if (t.
|
|
22
|
-
graph.set(id, t.
|
|
21
|
+
if (t.crosses.length > 0) {
|
|
22
|
+
graph.set(id, t.crosses);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
const color = new Map();
|
|
@@ -28,10 +28,10 @@ const buildFollowGraph = (trails) => {
|
|
|
28
28
|
}
|
|
29
29
|
return { color, graph };
|
|
30
30
|
};
|
|
31
|
-
/** Detect multi-node cycles in the trail
|
|
32
|
-
const
|
|
31
|
+
/** Detect multi-node cycles in the trail crossing graph via DFS. */
|
|
32
|
+
const detectCrossCycles = (trails) => {
|
|
33
33
|
const issues = [];
|
|
34
|
-
const { color, graph } =
|
|
34
|
+
const { color, graph } = buildCrossGraph(trails);
|
|
35
35
|
const dfs = (node, path) => {
|
|
36
36
|
color.set(node, GRAY);
|
|
37
37
|
for (const next of graph.get(node) ?? []) {
|
|
@@ -43,7 +43,7 @@ const detectFollowCycles = (trails) => {
|
|
|
43
43
|
const cycle = [...path.slice(path.indexOf(next)), next];
|
|
44
44
|
issues.push({
|
|
45
45
|
message: `Cycle detected: ${cycle.join(' → ')}`,
|
|
46
|
-
rule: '
|
|
46
|
+
rule: 'cross-cycle',
|
|
47
47
|
trailId: next,
|
|
48
48
|
});
|
|
49
49
|
}
|
|
@@ -60,37 +60,37 @@ const detectFollowCycles = (trails) => {
|
|
|
60
60
|
}
|
|
61
61
|
return issues;
|
|
62
62
|
};
|
|
63
|
-
const
|
|
63
|
+
const checkCrosses = (trails, topo) => {
|
|
64
64
|
const issues = [];
|
|
65
65
|
for (const [id, trail] of trails) {
|
|
66
|
-
for (const
|
|
67
|
-
if (
|
|
66
|
+
for (const crossedId of trail.crosses) {
|
|
67
|
+
if (crossedId === id) {
|
|
68
68
|
issues.push({
|
|
69
|
-
message: `Trail
|
|
70
|
-
rule: 'no-self-
|
|
69
|
+
message: `Trail crosses itself`,
|
|
70
|
+
rule: 'no-self-cross',
|
|
71
71
|
trailId: id,
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
else if (!topo.has(
|
|
74
|
+
else if (!topo.has(crossedId)) {
|
|
75
75
|
issues.push({
|
|
76
|
-
message: `
|
|
77
|
-
rule: '
|
|
76
|
+
message: `Crosses "${crossedId}" which is not in the topo`,
|
|
77
|
+
rule: 'cross-exists',
|
|
78
78
|
trailId: id,
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
issues.push(...
|
|
83
|
+
issues.push(...detectCrossCycles(trails));
|
|
84
84
|
return issues;
|
|
85
85
|
};
|
|
86
|
-
const
|
|
86
|
+
const checkProvisions = (trails, topo) => {
|
|
87
87
|
const issues = [];
|
|
88
88
|
for (const [id, trail] of trails) {
|
|
89
|
-
for (const
|
|
90
|
-
if (!topo.
|
|
89
|
+
for (const declaredProvision of trail.provisions) {
|
|
90
|
+
if (!topo.hasProvision(declaredProvision.id)) {
|
|
91
91
|
issues.push({
|
|
92
|
-
message: `
|
|
93
|
-
rule: '
|
|
92
|
+
message: `Provision "${declaredProvision.id}" is not in the topo`,
|
|
93
|
+
rule: 'provision-exists',
|
|
94
94
|
trailId: id,
|
|
95
95
|
});
|
|
96
96
|
}
|
|
@@ -129,17 +129,17 @@ const checkExamples = (trails) => {
|
|
|
129
129
|
}
|
|
130
130
|
return issues;
|
|
131
131
|
};
|
|
132
|
-
const
|
|
132
|
+
const checkSignalOrigins = (signals, topo) => {
|
|
133
133
|
const issues = [];
|
|
134
|
-
for (const [id, evt] of
|
|
134
|
+
for (const [id, evt] of signals) {
|
|
135
135
|
if (!evt.from) {
|
|
136
136
|
continue;
|
|
137
137
|
}
|
|
138
138
|
for (const originId of evt.from) {
|
|
139
139
|
if (!topo.has(originId)) {
|
|
140
140
|
issues.push({
|
|
141
|
-
message: `
|
|
142
|
-
rule: '
|
|
141
|
+
message: `Signal origin "${originId}" is not in the topo`,
|
|
142
|
+
rule: 'signal-origin-exists',
|
|
143
143
|
trailId: id,
|
|
144
144
|
});
|
|
145
145
|
}
|
|
@@ -153,16 +153,16 @@ const checkEventOrigins = (events, topo) => {
|
|
|
153
153
|
/**
|
|
154
154
|
* Validate the structural integrity of a Topo graph.
|
|
155
155
|
*
|
|
156
|
-
* Checks
|
|
156
|
+
* Checks crossing references, example inputs, event origins, and output
|
|
157
157
|
* schema presence. Returns `Result.ok()` when no issues are found, or
|
|
158
158
|
* `Result.err(ValidationError)` with all issues in the error context.
|
|
159
159
|
*/
|
|
160
160
|
export const validateTopo = (topo) => {
|
|
161
161
|
const issues = [
|
|
162
|
-
...
|
|
163
|
-
...
|
|
162
|
+
...checkCrosses(topo.trails, topo),
|
|
163
|
+
...checkProvisions(topo.trails, topo),
|
|
164
164
|
...checkExamples(topo.trails),
|
|
165
|
-
...
|
|
165
|
+
...checkSignalOrigins(topo.signals, topo),
|
|
166
166
|
];
|
|
167
167
|
if (issues.length === 0) {
|
|
168
168
|
return Result.ok();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-topo.js","sourceRoot":"","sources":["../src/validate-topo.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAYhD,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,MAAM,KAAK,GAAG,CAAC,CAAC;AAChB,MAAM,IAAI,GAAG,CAAC,CAAC;AACf,MAAM,KAAK,GAAG,CAAC,CAAC;AAEhB,
|
|
1
|
+
{"version":3,"file":"validate-topo.js","sourceRoot":"","sources":["../src/validate-topo.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -23,10 +23,10 @@ describe('createTrailContext', () => {
|
|
|
23
23
|
expect(ctx.env).toBe(process.env);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
test('provides a non-aborted
|
|
26
|
+
test('provides a non-aborted abortSignal by default', () => {
|
|
27
27
|
const ctx = createTrailContext();
|
|
28
|
-
expect(ctx.
|
|
29
|
-
expect(ctx.
|
|
28
|
+
expect(ctx.abortSignal).toBeInstanceOf(AbortSignal);
|
|
29
|
+
expect(ctx.abortSignal.aborted).toBe(false);
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
test('override values take precedence', () => {
|
|
@@ -34,17 +34,17 @@ describe('createTrailContext', () => {
|
|
|
34
34
|
ac.abort();
|
|
35
35
|
|
|
36
36
|
const ctx = createTrailContext({
|
|
37
|
+
abortSignal: ac.signal,
|
|
37
38
|
cwd: '/custom/dir',
|
|
38
39
|
env: { CUSTOM: 'yes' },
|
|
39
40
|
requestId: 'custom-id',
|
|
40
|
-
signal: ac.signal,
|
|
41
41
|
workspaceRoot: '/tmp',
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
expect(ctx.cwd).toBe('/custom/dir');
|
|
45
45
|
expect(ctx.env).toEqual({ CUSTOM: 'yes' });
|
|
46
46
|
expect(ctx.requestId).toBe('custom-id');
|
|
47
|
-
expect(ctx.
|
|
47
|
+
expect(ctx.abortSignal.aborted).toBe(true);
|
|
48
48
|
expect(ctx.workspaceRoot).toBe('/tmp');
|
|
49
49
|
});
|
|
50
50
|
|
|
@@ -57,14 +57,14 @@ describe('createTrailContext', () => {
|
|
|
57
57
|
expect(ctx.extensions?.['nested']).toEqual({ key: 'value' });
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
test('provides a
|
|
60
|
+
test('provides a provision accessor backed by extensions', () => {
|
|
61
61
|
const widget = { id: 'widget-1' };
|
|
62
62
|
const ctx = createTrailContext({
|
|
63
63
|
extensions: { 'widget.main': widget },
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
expect(ctx.
|
|
67
|
-
expect(ctx.
|
|
66
|
+
expect(ctx.provision('widget.main')).toBe(widget);
|
|
67
|
+
expect(ctx.provision<{ id: string }>({ id: 'widget.main' }).id).toBe(
|
|
68
68
|
'widget-1'
|
|
69
69
|
);
|
|
70
70
|
});
|