@ontrails/core 1.0.0-beta.39 → 1.0.0-beta.41
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 +58 -0
- package/README.md +10 -10
- package/package.json +1 -1
- package/src/activation-provenance.ts +5 -0
- package/src/activation-source-projection.ts +6 -0
- package/src/activation-source.ts +2 -0
- package/src/compose-batch.ts +1 -1
- package/src/draft.ts +21 -21
- package/src/{contour.ts → entity.ts} +72 -72
- package/src/execute.ts +15 -15
- package/src/fire.ts +5 -2
- package/src/index.ts +19 -17
- package/src/layer.ts +1 -1
- package/src/observe.ts +1 -1
- package/src/queue.ts +163 -0
- package/src/signal-ref.ts +30 -6
- package/src/store/accessor-protocol.ts +4 -4
- package/src/surface-overlay.ts +1 -1
- package/src/topo.ts +47 -47
- package/src/tracing.ts +1 -0
- package/src/trail.ts +92 -87
- package/src/trails/derive-trail.ts +202 -209
- package/src/trails/ingest.ts +18 -5
- package/src/trails-db.ts +1 -1
- package/src/type-utils.ts +1 -1
- package/src/types.ts +3 -3
- package/src/validate-established-topo.ts +7 -3
- package/src/validate-topo.ts +31 -15
package/src/trails/ingest.ts
CHANGED
|
@@ -17,7 +17,13 @@ type ExampleBearingSchema<TSchema extends z.ZodType> = TSchema & {
|
|
|
17
17
|
|
|
18
18
|
interface IngestBaseOptions<TSchema extends z.ZodType, TSignal> extends Omit<
|
|
19
19
|
TrailSpec<SchemaValue<TSchema>, void>,
|
|
20
|
-
|
|
20
|
+
| 'implementation'
|
|
21
|
+
| 'examples'
|
|
22
|
+
| 'fires'
|
|
23
|
+
| 'input'
|
|
24
|
+
| 'intent'
|
|
25
|
+
| 'output'
|
|
26
|
+
| 'pattern'
|
|
21
27
|
> {
|
|
22
28
|
/** Override the derived trail id. Defaults to `${signal}.ingest`. */
|
|
23
29
|
readonly id?: string | undefined;
|
|
@@ -63,7 +69,7 @@ const deriveExamples = <TSchema extends z.ZodType>(
|
|
|
63
69
|
);
|
|
64
70
|
};
|
|
65
71
|
|
|
66
|
-
const
|
|
72
|
+
const createIngestImplementation =
|
|
67
73
|
<TSchema extends z.ZodType, TSignal>(
|
|
68
74
|
signalRef: Signal<TSignal>,
|
|
69
75
|
signalId: string,
|
|
@@ -108,7 +114,7 @@ export const ingest = <
|
|
|
108
114
|
const signalId = options.signal.id;
|
|
109
115
|
const id = options.id ?? `${signalId}.ingest`;
|
|
110
116
|
const { id: _id, schema, signal, transform, verify, ...trailSpec } = options;
|
|
111
|
-
const
|
|
117
|
+
const baseImplementation = createIngestImplementation<TSchema, TSignal>(
|
|
112
118
|
signal,
|
|
113
119
|
signalId,
|
|
114
120
|
id,
|
|
@@ -116,9 +122,12 @@ export const ingest = <
|
|
|
116
122
|
);
|
|
117
123
|
const baseSpec = {
|
|
118
124
|
...trailSpec,
|
|
119
|
-
blaze: baseBlaze as TrailSpec<unknown, unknown>['blaze'],
|
|
120
125
|
examples: deriveExamples(schema as ExampleBearingSchema<TSchema>, signalId),
|
|
121
126
|
fires: [signal],
|
|
127
|
+
implementation: baseImplementation as TrailSpec<
|
|
128
|
+
unknown,
|
|
129
|
+
unknown
|
|
130
|
+
>['implementation'],
|
|
122
131
|
input: schema as z.ZodType<unknown>,
|
|
123
132
|
intent: 'write',
|
|
124
133
|
output: z.void(),
|
|
@@ -134,6 +143,10 @@ export const ingest = <
|
|
|
134
143
|
// mutating runner-wide layer configuration.
|
|
135
144
|
return Object.freeze({
|
|
136
145
|
...baseTrail,
|
|
137
|
-
|
|
146
|
+
implementation: composeLayers(
|
|
147
|
+
[verify],
|
|
148
|
+
baseTrail,
|
|
149
|
+
baseTrail.implementation
|
|
150
|
+
),
|
|
138
151
|
}) as Trail<SchemaValue<TSchema>, void>;
|
|
139
152
|
};
|
package/src/trails-db.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { sha256Hex } from './sha256.js';
|
|
|
6
6
|
|
|
7
7
|
// Altitude ruling (TRL-1198, ADR-0051 lens): trails-db stays core-owned
|
|
8
8
|
// shared framework infrastructure (ADR-0014) and stays on the barrel —
|
|
9
|
-
//
|
|
9
|
+
// topography, tracing, warden, wayfinder, and the operator app all
|
|
10
10
|
// consume it from `@ontrails/core`. What it may NOT do is assume runtime
|
|
11
11
|
// capabilities eagerly: `bun:sqlite` and the node builtins load lazily at
|
|
12
12
|
// first use so the barrel's module graph stays execution-portable.
|
package/src/type-utils.ts
CHANGED
|
@@ -42,7 +42,7 @@ export type TrailInput<T extends AnyTrail> = T extends {
|
|
|
42
42
|
|
|
43
43
|
/** Extract the output type from a Trail. */
|
|
44
44
|
export type TrailOutput<T extends AnyTrail> = T extends {
|
|
45
|
-
readonly
|
|
45
|
+
readonly implementation: Implementation<any, infer O>;
|
|
46
46
|
}
|
|
47
47
|
? O
|
|
48
48
|
: never;
|
package/src/types.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { TrailVersionReference } from './version-resolution.js';
|
|
|
11
11
|
// Detour
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
|
|
14
|
-
/** A recovery path that activates when a trail's
|
|
14
|
+
/** A recovery path that activates when a trail's implementation fails with a matching error. */
|
|
15
15
|
export interface Detour<Input, Output, TErr extends TrailsError = TrailsError> {
|
|
16
16
|
/* oxlint-disable-next-line no-explicit-any -- standard pattern for matching abstract+concrete class constructors */
|
|
17
17
|
readonly on: abstract new (...args: any[]) => TErr;
|
|
@@ -69,7 +69,7 @@ export interface ComposeOptions {
|
|
|
69
69
|
* Execute a specific live version of the composed trail.
|
|
70
70
|
*
|
|
71
71
|
* Omit to keep composition current by default. Historical revision entries
|
|
72
|
-
* transpose through the current trail; fork entries run their own
|
|
72
|
+
* transpose through the current trail; fork entries run their own implementation.
|
|
73
73
|
*/
|
|
74
74
|
readonly version?: TrailVersionReference | undefined;
|
|
75
75
|
}
|
|
@@ -277,7 +277,7 @@ export interface TrailContext {
|
|
|
277
277
|
readonly trace?: TraceFn | undefined;
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
/** Trail context for
|
|
280
|
+
/** Trail context for implementations that declare trail composition. */
|
|
281
281
|
export interface ComposeTrailContext extends TrailContext {
|
|
282
282
|
readonly compose: ComposeFn;
|
|
283
283
|
}
|
|
@@ -12,6 +12,7 @@ const PROJECTION_BLOCKING_RULES = new Set([
|
|
|
12
12
|
'activation-source-definition-unique',
|
|
13
13
|
'activation-source-edge-unique',
|
|
14
14
|
'activation-source-kind-known',
|
|
15
|
+
'activation-queue-valid',
|
|
15
16
|
'activation-schedule-valid',
|
|
16
17
|
'resource-exists',
|
|
17
18
|
'signal-fire-exists',
|
|
@@ -19,6 +20,11 @@ const PROJECTION_BLOCKING_RULES = new Set([
|
|
|
19
20
|
'signal-origin-exists',
|
|
20
21
|
]);
|
|
21
22
|
|
|
23
|
+
const isProjectionBlockingIssue = (issue: TopoDiagnostic): boolean =>
|
|
24
|
+
PROJECTION_BLOCKING_RULES.has(issue.rule) ||
|
|
25
|
+
(issue.rule === 'activation-source-input-compatible' &&
|
|
26
|
+
issue.sourceKind === 'queue');
|
|
27
|
+
|
|
22
28
|
const keepProjectionBlockingIssues = (
|
|
23
29
|
result: ReturnType<typeof validateTopo>
|
|
24
30
|
) => {
|
|
@@ -29,9 +35,7 @@ const keepProjectionBlockingIssues = (
|
|
|
29
35
|
const issues = (
|
|
30
36
|
result.error.context as { issues?: readonly TopoDiagnostic[] } | undefined
|
|
31
37
|
)?.issues;
|
|
32
|
-
const remainingIssues = issues?.filter(
|
|
33
|
-
PROJECTION_BLOCKING_RULES.has(issue.rule)
|
|
34
|
-
);
|
|
38
|
+
const remainingIssues = issues?.filter(isProjectionBlockingIssue);
|
|
35
39
|
|
|
36
40
|
if (remainingIssues === undefined || remainingIssues.length === 0) {
|
|
37
41
|
return Result.ok();
|
package/src/validate-topo.ts
CHANGED
|
@@ -12,12 +12,13 @@ import {
|
|
|
12
12
|
activationSourceDeclarationSignature,
|
|
13
13
|
activationSourceKey,
|
|
14
14
|
} from './activation-source-projection.js';
|
|
15
|
-
import type {
|
|
16
|
-
import {
|
|
15
|
+
import type { AnyEntity } from './entity.js';
|
|
16
|
+
import { getEntityReferences } from './entity.js';
|
|
17
17
|
import { ValidationError } from './errors.js';
|
|
18
18
|
import type { ActivationEntry } from './activation-source.js';
|
|
19
19
|
import { isKnownActivationSourceKind } from './activation-source.js';
|
|
20
20
|
import { isDraftId } from './draft.js';
|
|
21
|
+
import { validateQueueSource } from './queue.js';
|
|
21
22
|
import type { AnySignal } from './signal.js';
|
|
22
23
|
import { validateScheduleSource } from './schedule.js';
|
|
23
24
|
import { Result } from './result.js';
|
|
@@ -38,14 +39,14 @@ export type TopoDiagnosticCode = 'topo.missing-reference';
|
|
|
38
39
|
|
|
39
40
|
export type TopoReferenceKind =
|
|
40
41
|
| 'compose'
|
|
41
|
-
| '
|
|
42
|
+
| 'entity-reference'
|
|
42
43
|
| 'resource'
|
|
43
44
|
| 'signal-fire'
|
|
44
45
|
| 'signal-on'
|
|
45
46
|
| 'signal-origin';
|
|
46
47
|
|
|
47
48
|
export type TopoReferenceOwnerKind =
|
|
48
|
-
| '
|
|
49
|
+
| 'entity'
|
|
49
50
|
| 'signal'
|
|
50
51
|
| 'trail'
|
|
51
52
|
| 'trail-version';
|
|
@@ -526,6 +527,21 @@ const checkActivationSources = (
|
|
|
526
527
|
}
|
|
527
528
|
}
|
|
528
529
|
|
|
530
|
+
const queueIssues = validateQueueSource(activation.source);
|
|
531
|
+
for (const issue of queueIssues) {
|
|
532
|
+
issues.push({
|
|
533
|
+
inputPath: [issue.field],
|
|
534
|
+
message: `Trail declares queue source "${activation.source.id}" with invalid ${issue.field}: ${issue.message}`,
|
|
535
|
+
rule: 'activation-queue-valid',
|
|
536
|
+
schemaIssues: [
|
|
537
|
+
{ code: issue.field, message: issue.message, path: [issue.field] },
|
|
538
|
+
],
|
|
539
|
+
sourceId: activation.source.id,
|
|
540
|
+
sourceKind: activation.source.kind,
|
|
541
|
+
trailId: id,
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
529
545
|
const scheduleIssues = validateScheduleSource(activation.source);
|
|
530
546
|
for (const issue of scheduleIssues) {
|
|
531
547
|
issues.push({
|
|
@@ -626,25 +642,25 @@ const checkActivationSourceInputCompatibility = (
|
|
|
626
642
|
return issues;
|
|
627
643
|
};
|
|
628
644
|
|
|
629
|
-
const
|
|
630
|
-
|
|
645
|
+
const checkEntityReferences = (
|
|
646
|
+
entities: ReadonlyMap<string, AnyEntity>,
|
|
631
647
|
topo: Topo
|
|
632
648
|
): TopoDiagnostic[] => {
|
|
633
649
|
const issues: TopoDiagnostic[] = [];
|
|
634
650
|
|
|
635
|
-
for (const [name,
|
|
636
|
-
for (const ref of
|
|
637
|
-
if (!topo.
|
|
651
|
+
for (const [name, entityDef] of entities) {
|
|
652
|
+
for (const ref of getEntityReferences(entityDef)) {
|
|
653
|
+
if (!topo.hasEntity(ref.entity) && !isDraftId(ref.entity)) {
|
|
638
654
|
issues.push(
|
|
639
655
|
missingReferenceDiagnostic({
|
|
640
|
-
message: `
|
|
656
|
+
message: `Entity "${name}" references "${ref.entity}" which is not in the topo`,
|
|
641
657
|
reference: {
|
|
642
658
|
fromId: name,
|
|
643
|
-
fromKind: '
|
|
644
|
-
missingId: ref.
|
|
645
|
-
referenceKind: '
|
|
659
|
+
fromKind: 'entity',
|
|
660
|
+
missingId: ref.entity,
|
|
661
|
+
referenceKind: 'entity-reference',
|
|
646
662
|
},
|
|
647
|
-
rule: '
|
|
663
|
+
rule: 'entity-reference-exists',
|
|
648
664
|
trailId: name,
|
|
649
665
|
})
|
|
650
666
|
);
|
|
@@ -686,7 +702,7 @@ export const validateTopo = (topo: Topo): Result<void, ValidationError> => {
|
|
|
686
702
|
const issues = [
|
|
687
703
|
...checkComposes(topo.trails, topo),
|
|
688
704
|
...checkResources(topo.trails, topo),
|
|
689
|
-
...
|
|
705
|
+
...checkEntityReferences(topo.entities, topo),
|
|
690
706
|
...checkExamples(topo.trails),
|
|
691
707
|
...checkSignalOrigins(topo.signals, topo),
|
|
692
708
|
...checkSignalReferences(topo.trails, topo.signals),
|