@ontrails/core 1.0.0-beta.12 → 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.
Files changed (98) hide show
  1. package/CHANGELOG.md +59 -35
  2. package/README.md +19 -19
  3. package/dist/context.d.ts +1 -1
  4. package/dist/context.d.ts.map +1 -1
  5. package/dist/context.js +5 -4
  6. package/dist/context.js.map +1 -1
  7. package/dist/derive.d.ts +8 -3
  8. package/dist/derive.d.ts.map +1 -1
  9. package/dist/derive.js +7 -7
  10. package/dist/derive.js.map +1 -1
  11. package/dist/event.d.ts +4 -41
  12. package/dist/event.d.ts.map +1 -1
  13. package/dist/event.js +4 -14
  14. package/dist/event.js.map +1 -1
  15. package/dist/execute.d.ts +10 -10
  16. package/dist/execute.d.ts.map +1 -1
  17. package/dist/execute.js +35 -28
  18. package/dist/execute.js.map +1 -1
  19. package/dist/gate.d.ts +17 -0
  20. package/dist/gate.d.ts.map +1 -0
  21. package/dist/gate.js +21 -0
  22. package/dist/gate.js.map +1 -0
  23. package/dist/index.d.ts +10 -8
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +9 -8
  26. package/dist/index.js.map +1 -1
  27. package/dist/layer.d.ts +5 -16
  28. package/dist/layer.d.ts.map +1 -1
  29. package/dist/layer.js +3 -20
  30. package/dist/layer.js.map +1 -1
  31. package/dist/provision-config.d.ts +22 -0
  32. package/dist/provision-config.d.ts.map +1 -0
  33. package/dist/provision-config.js +210 -0
  34. package/dist/provision-config.js.map +1 -0
  35. package/dist/provision.d.ts +71 -0
  36. package/dist/provision.d.ts.map +1 -0
  37. package/dist/provision.js +56 -0
  38. package/dist/provision.js.map +1 -0
  39. package/dist/run.d.ts +27 -0
  40. package/dist/run.d.ts.map +1 -0
  41. package/dist/run.js +34 -0
  42. package/dist/run.js.map +1 -0
  43. package/dist/service-config.d.ts +7 -7
  44. package/dist/service-config.d.ts.map +1 -1
  45. package/dist/service-config.js +75 -73
  46. package/dist/service-config.js.map +1 -1
  47. package/dist/service.d.ts +32 -36
  48. package/dist/service.d.ts.map +1 -1
  49. package/dist/service.js +21 -21
  50. package/dist/service.js.map +1 -1
  51. package/dist/signal.d.ts +33 -0
  52. package/dist/signal.d.ts.map +1 -0
  53. package/dist/signal.js +17 -0
  54. package/dist/signal.js.map +1 -0
  55. package/dist/topo.d.ts +10 -10
  56. package/dist/topo.d.ts.map +1 -1
  57. package/dist/topo.js +48 -35
  58. package/dist/topo.js.map +1 -1
  59. package/dist/trail.d.ts +16 -16
  60. package/dist/trail.d.ts.map +1 -1
  61. package/dist/trail.js +5 -4
  62. package/dist/trail.js.map +1 -1
  63. package/dist/types.d.ts +10 -10
  64. package/dist/types.d.ts.map +1 -1
  65. package/dist/types.js +2 -2
  66. package/dist/types.js.map +1 -1
  67. package/dist/validate-topo.d.ts +2 -2
  68. package/dist/validate-topo.js +31 -31
  69. package/dist/validate-topo.js.map +1 -1
  70. package/package.json +1 -1
  71. package/src/__tests__/context.test.ts +8 -8
  72. package/src/__tests__/execute.test.ts +124 -122
  73. package/src/__tests__/{layer.test.ts → gate.test.ts} +26 -26
  74. package/src/__tests__/{dispatch.test.ts → run.test.ts} +39 -39
  75. package/src/__tests__/service-config.test.ts +41 -37
  76. package/src/__tests__/service.test.ts +71 -71
  77. package/src/__tests__/{event.test.ts → signal.test.ts} +15 -15
  78. package/src/__tests__/topo.test.ts +54 -54
  79. package/src/__tests__/trail-permit.test.ts +4 -4
  80. package/src/__tests__/trail.test.ts +58 -58
  81. package/src/__tests__/type-utils.test.ts +3 -3
  82. package/src/__tests__/validate-topo.test.ts +38 -38
  83. package/src/context.ts +5 -4
  84. package/src/derive.ts +8 -8
  85. package/src/event.ts +11 -73
  86. package/src/execute.ts +56 -40
  87. package/src/{layer.ts → gate.ts} +13 -13
  88. package/src/index.ts +24 -22
  89. package/src/{service-config.ts → provision-config.ts} +124 -105
  90. package/src/provision.ts +148 -0
  91. package/src/{dispatch.ts → run.ts} +8 -8
  92. package/src/signal.ts +65 -0
  93. package/src/topo.ts +74 -52
  94. package/src/trail.ts +23 -22
  95. package/src/types.ts +11 -11
  96. package/src/validate-topo.ts +33 -33
  97. package/tsconfig.tsbuildinfo +1 -1
  98. package/src/service.ts +0 -145
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Structural validation for a Topo graph.
3
3
  *
4
- * Checks trail follow references, example input validity, event origin
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
  */
8
8
 
9
9
  import { ValidationError } from './errors.js';
10
- import type { AnyEvent } from './event.js';
10
+ import type { AnySignal } from './event.js';
11
11
  import { Result } from './result.js';
12
12
  import type { Topo } from './topo.js';
13
13
  import type { AnyTrail } from './trail.js';
@@ -31,8 +31,8 @@ const WHITE = 0;
31
31
  const GRAY = 1;
32
32
  const BLACK = 2;
33
33
 
34
- /** Build an adjacency list and initial color map from trails with follow. */
35
- const buildFollowGraph = (
34
+ /** Build an adjacency list and initial color map from trails with crossings. */
35
+ const buildCrossGraph = (
36
36
  trails: ReadonlyMap<string, AnyTrail>
37
37
  ): {
38
38
  graph: Map<string, readonly string[]>;
@@ -40,8 +40,8 @@ const buildFollowGraph = (
40
40
  } => {
41
41
  const graph = new Map<string, readonly string[]>();
42
42
  for (const [id, t] of trails) {
43
- if (t.follow.length > 0) {
44
- graph.set(id, t.follow);
43
+ if (t.crosses.length > 0) {
44
+ graph.set(id, t.crosses);
45
45
  }
46
46
  }
47
47
  const color = new Map<string, number>();
@@ -51,12 +51,12 @@ const buildFollowGraph = (
51
51
  return { color, graph };
52
52
  };
53
53
 
54
- /** Detect multi-node cycles in the trail follow graph via DFS. */
55
- const detectFollowCycles = (
54
+ /** Detect multi-node cycles in the trail crossing graph via DFS. */
55
+ const detectCrossCycles = (
56
56
  trails: ReadonlyMap<string, AnyTrail>
57
57
  ): TopoIssue[] => {
58
58
  const issues: TopoIssue[] = [];
59
- const { color, graph } = buildFollowGraph(trails);
59
+ const { color, graph } = buildCrossGraph(trails);
60
60
 
61
61
  const dfs = (node: string, path: string[]): void => {
62
62
  color.set(node, GRAY);
@@ -69,7 +69,7 @@ const detectFollowCycles = (
69
69
  const cycle = [...path.slice(path.indexOf(next)), next];
70
70
  issues.push({
71
71
  message: `Cycle detected: ${cycle.join(' → ')}`,
72
- rule: 'follow-cycle',
72
+ rule: 'cross-cycle',
73
73
  trailId: next,
74
74
  });
75
75
  } else if (c === WHITE) {
@@ -87,44 +87,44 @@ const detectFollowCycles = (
87
87
  return issues;
88
88
  };
89
89
 
90
- const checkFollows = (
90
+ const checkCrosses = (
91
91
  trails: ReadonlyMap<string, AnyTrail>,
92
92
  topo: Topo
93
93
  ): TopoIssue[] => {
94
94
  const issues: TopoIssue[] = [];
95
95
  for (const [id, trail] of trails) {
96
- for (const followId of trail.follow) {
97
- if (followId === id) {
96
+ for (const crossedId of trail.crosses) {
97
+ if (crossedId === id) {
98
98
  issues.push({
99
- message: `Trail follows itself`,
100
- rule: 'no-self-follow',
99
+ message: `Trail crosses itself`,
100
+ rule: 'no-self-cross',
101
101
  trailId: id,
102
102
  });
103
- } else if (!topo.has(followId)) {
103
+ } else if (!topo.has(crossedId)) {
104
104
  issues.push({
105
- message: `Follows "${followId}" which is not in the topo`,
106
- rule: 'follow-exists',
105
+ message: `Crosses "${crossedId}" which is not in the topo`,
106
+ rule: 'cross-exists',
107
107
  trailId: id,
108
108
  });
109
109
  }
110
110
  }
111
111
  }
112
- issues.push(...detectFollowCycles(trails));
112
+ issues.push(...detectCrossCycles(trails));
113
113
  return issues;
114
114
  };
115
115
 
116
- const checkServices = (
116
+ const checkProvisions = (
117
117
  trails: ReadonlyMap<string, AnyTrail>,
118
118
  topo: Topo
119
119
  ): TopoIssue[] => {
120
120
  const issues: TopoIssue[] = [];
121
121
 
122
122
  for (const [id, trail] of trails) {
123
- for (const declaredService of trail.services) {
124
- if (!topo.hasService(declaredService.id)) {
123
+ for (const declaredProvision of trail.provisions) {
124
+ if (!topo.hasProvision(declaredProvision.id)) {
125
125
  issues.push({
126
- message: `Service "${declaredService.id}" is not in the topo`,
127
- rule: 'service-exists',
126
+ message: `Provision "${declaredProvision.id}" is not in the topo`,
127
+ rule: 'provision-exists',
128
128
  trailId: id,
129
129
  });
130
130
  }
@@ -177,20 +177,20 @@ const checkExamples = (trails: ReadonlyMap<string, AnyTrail>): TopoIssue[] => {
177
177
  return issues;
178
178
  };
179
179
 
180
- const checkEventOrigins = (
181
- events: ReadonlyMap<string, AnyEvent>,
180
+ const checkSignalOrigins = (
181
+ signals: ReadonlyMap<string, AnySignal>,
182
182
  topo: Topo
183
183
  ): TopoIssue[] => {
184
184
  const issues: TopoIssue[] = [];
185
- for (const [id, evt] of events) {
185
+ for (const [id, evt] of signals) {
186
186
  if (!evt.from) {
187
187
  continue;
188
188
  }
189
189
  for (const originId of evt.from) {
190
190
  if (!topo.has(originId)) {
191
191
  issues.push({
192
- message: `Event origin "${originId}" is not in the topo`,
193
- rule: 'event-origin-exists',
192
+ message: `Signal origin "${originId}" is not in the topo`,
193
+ rule: 'signal-origin-exists',
194
194
  trailId: id,
195
195
  });
196
196
  }
@@ -206,16 +206,16 @@ const checkEventOrigins = (
206
206
  /**
207
207
  * Validate the structural integrity of a Topo graph.
208
208
  *
209
- * Checks follow references, example inputs, event origins, and output
209
+ * Checks crossing references, example inputs, event origins, and output
210
210
  * schema presence. Returns `Result.ok()` when no issues are found, or
211
211
  * `Result.err(ValidationError)` with all issues in the error context.
212
212
  */
213
213
  export const validateTopo = (topo: Topo): Result<void, ValidationError> => {
214
214
  const issues = [
215
- ...checkFollows(topo.trails, topo),
216
- ...checkServices(topo.trails, topo),
215
+ ...checkCrosses(topo.trails, topo),
216
+ ...checkProvisions(topo.trails, topo),
217
217
  ...checkExamples(topo.trails),
218
- ...checkEventOrigins(topo.events, topo),
218
+ ...checkSignalOrigins(topo.signals, topo),
219
219
  ];
220
220
 
221
221
  if (issues.length === 0) {
@@ -1 +1 @@
1
- {"root":["./src/blob-ref.ts","./src/branded.ts","./src/collections.ts","./src/context.ts","./src/derive.ts","./src/dispatch.ts","./src/errors.ts","./src/event.ts","./src/execute.ts","./src/fetch.ts","./src/guards.ts","./src/index.ts","./src/layer.ts","./src/path-security.ts","./src/resilience.ts","./src/result.ts","./src/serialization.ts","./src/service-config.ts","./src/service.ts","./src/topo.ts","./src/trail.ts","./src/type-utils.ts","./src/types.ts","./src/validate-topo.ts","./src/validation.ts","./src/workspace.ts","./src/patterns/bulk.ts","./src/patterns/change.ts","./src/patterns/date-range.ts","./src/patterns/index.ts","./src/patterns/pagination.ts","./src/patterns/progress.ts","./src/patterns/sorting.ts","./src/patterns/status.ts","./src/patterns/timestamps.ts","./src/redaction/index.ts","./src/redaction/patterns.ts","./src/redaction/redactor.ts"],"version":"5.9.3"}
1
+ {"root":["./src/blob-ref.ts","./src/branded.ts","./src/collections.ts","./src/context.ts","./src/derive.ts","./src/errors.ts","./src/event.ts","./src/execute.ts","./src/fetch.ts","./src/gate.ts","./src/guards.ts","./src/index.ts","./src/path-security.ts","./src/provision-config.ts","./src/provision.ts","./src/resilience.ts","./src/result.ts","./src/run.ts","./src/serialization.ts","./src/signal.ts","./src/topo.ts","./src/trail.ts","./src/type-utils.ts","./src/types.ts","./src/validate-topo.ts","./src/validation.ts","./src/workspace.ts","./src/patterns/bulk.ts","./src/patterns/change.ts","./src/patterns/date-range.ts","./src/patterns/index.ts","./src/patterns/pagination.ts","./src/patterns/progress.ts","./src/patterns/sorting.ts","./src/patterns/status.ts","./src/patterns/timestamps.ts","./src/redaction/index.ts","./src/redaction/patterns.ts","./src/redaction/redactor.ts"],"version":"5.9.3"}
package/src/service.ts DELETED
@@ -1,145 +0,0 @@
1
- import { NotFoundError } from './errors.js';
2
- import type { Result } from './result.js';
3
- import type { ServiceLookup, TrailContext } from './types.js';
4
- import type { z } from 'zod';
5
-
6
- /**
7
- * Stable process-scoped fields available when constructing a service.
8
- *
9
- * Services are app-level singletons, so they intentionally do not receive the
10
- * full per-request TrailContext. When a service declares a `config` schema,
11
- * the validated config is passed as `svc.config`.
12
- */
13
- export type ServiceContext<C = unknown> = Pick<
14
- TrailContext,
15
- 'cwd' | 'env' | 'workspaceRoot'
16
- > & {
17
- readonly config: C;
18
- };
19
-
20
- /**
21
- * Everything needed to describe a service before a factory is introduced.
22
- *
23
- * When `config` is a Zod schema, the `create` callback receives
24
- * `ServiceContext<C>` with the validated config value.
25
- */
26
- export interface ServiceSpec<T, C = unknown> {
27
- /** Create the service instance from stable process-scoped context. */
28
- readonly create: (
29
- svc: ServiceContext<C>
30
- ) => Result<T, Error> | Promise<Result<T, Error>>;
31
- /** Config schema — when present, config is validated and passed to `create`. */
32
- readonly config?: z.ZodType<C> | undefined;
33
- /** Optional cleanup performed when the hosting surface shuts down. */
34
- readonly dispose?: ((service: T) => void | Promise<void>) | undefined;
35
- /** Optional operational readiness probe for introspection tooling. */
36
- readonly health?:
37
- | ((service: T) => Result<unknown, Error> | Promise<Result<unknown, Error>>)
38
- | undefined;
39
- /** Optional test factory used by higher-level helpers. */
40
- readonly mock?: (() => T | Promise<T>) | undefined;
41
- /** Human-readable description. */
42
- readonly description?: string | undefined;
43
- /** Arbitrary metadata for tooling and filtering. */
44
- readonly metadata?: Readonly<Record<string, unknown>> | undefined;
45
- }
46
-
47
- /**
48
- * A typed service definition.
49
- *
50
- * TRL-73 introduces the structural contract only. The `service()` factory and
51
- * runtime helpers land in follow-up branches.
52
- */
53
- export interface Service<T> extends ServiceSpec<T> {
54
- readonly kind: 'service';
55
- readonly id: string;
56
- /** Read the resolved service instance from a trail context. */
57
- from(ctx: TrailContext): T;
58
- }
59
-
60
- /**
61
- * Existential type for heterogeneous service collections.
62
- *
63
- * `Service<T>` includes function parameters in `dispose`/`health`, so `unknown`
64
- * is too narrow for mixed service arrays. `any` is the correct existential here.
65
- */
66
- // oxlint-disable-next-line no-explicit-any -- existential type for heterogeneous service collections
67
- export type AnyService = Service<any>;
68
-
69
- /** Explicit runtime overrides keyed by service ID. */
70
- export type ServiceOverrideMap = Readonly<Record<string, unknown>>;
71
-
72
- const getServiceId = <T>(
73
- serviceOrId: string | Pick<Service<T>, 'id'>
74
- ): string => (typeof serviceOrId === 'string' ? serviceOrId : serviceOrId.id);
75
-
76
- const getServiceInstance = <T>(
77
- ctx: Pick<TrailContext, 'extensions'>,
78
- serviceOrId: string | Pick<Service<T>, 'id'>
79
- ): T => {
80
- const id = getServiceId(serviceOrId);
81
- return ctx.extensions?.[id] as T;
82
- };
83
-
84
- const hasServiceInstance = (
85
- ctx: Pick<TrailContext, 'extensions'>,
86
- serviceOrId: string | Pick<AnyService, 'id'>
87
- ): boolean => Object.hasOwn(ctx.extensions ?? {}, getServiceId(serviceOrId));
88
-
89
- /** Create a `ctx.service(...)` accessor bound to a concrete context snapshot. */
90
- export const createServiceLookup = (
91
- getContext: () => Pick<TrailContext, 'extensions'>
92
- ): ServiceLookup =>
93
- ((serviceOrId: string | Pick<AnyService, 'id'>) => {
94
- const id = getServiceId(serviceOrId);
95
- const ctx = getContext();
96
- if (!hasServiceInstance(ctx, id)) {
97
- throw new NotFoundError(`Service "${id}" not found in trail context`);
98
- }
99
- return getServiceInstance(ctx, id);
100
- }) as ServiceLookup;
101
-
102
- /**
103
- * Create a typed service definition.
104
- *
105
- * The service object is inert until a later execution branch resolves concrete
106
- * instances into TrailContext extensions.
107
- */
108
- export const service = <T>(id: string, spec: ServiceSpec<T>): Service<T> =>
109
- Object.freeze({
110
- ...spec,
111
- from(ctx: TrailContext): T {
112
- const lookup = ctx.service ?? createServiceLookup(() => ctx);
113
- return lookup(this);
114
- },
115
- id,
116
- kind: 'service' as const,
117
- });
118
-
119
- /** Narrow unknown values to service definitions during topo discovery. */
120
- export const isService = (value: unknown): value is AnyService => {
121
- if (typeof value !== 'object' || value === null) {
122
- return false;
123
- }
124
- const v = value as { kind?: unknown; id?: unknown };
125
- return v.kind === 'service' && typeof v.id === 'string';
126
- };
127
-
128
- /**
129
- * Return the first duplicate service ID in a collection, if any.
130
- *
131
- * This supports later topo registration without each caller duplicating the
132
- * same scan logic.
133
- */
134
- export const findDuplicateServiceId = (
135
- services: readonly Pick<AnyService, 'id'>[]
136
- ): string | undefined => {
137
- const seen = new Set<string>();
138
- for (const candidate of services) {
139
- if (seen.has(candidate.id)) {
140
- return candidate.id;
141
- }
142
- seen.add(candidate.id);
143
- }
144
- return undefined;
145
- };