@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
|
@@ -3,22 +3,22 @@ import { z } from 'zod';
|
|
|
3
3
|
import type { Branded } from './branded.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Runtime options for
|
|
6
|
+
* Runtime options for an entity declaration.
|
|
7
7
|
*/
|
|
8
|
-
export interface
|
|
8
|
+
export interface EntityOptions<
|
|
9
9
|
TShape extends z.ZodRawShape,
|
|
10
10
|
TIdentity extends keyof TShape & string,
|
|
11
11
|
> {
|
|
12
|
-
/** Field name that acts as the
|
|
12
|
+
/** Field name that acts as the entity's primary identity. */
|
|
13
13
|
readonly identity: TIdentity;
|
|
14
|
-
/** Example instances validated against the
|
|
14
|
+
/** Example instances validated against the entity schema at declaration time. */
|
|
15
15
|
readonly examples?: readonly z.output<z.ZodObject<TShape>>[] | undefined;
|
|
16
|
-
/** Reserved for future
|
|
16
|
+
/** Reserved for future entity-specific design; trail versioning is trail-only. */
|
|
17
17
|
readonly version?: never;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/** Type-level brand name applied to
|
|
21
|
-
export type
|
|
20
|
+
/** Type-level brand name applied to an entity's identity schema. */
|
|
21
|
+
export type EntityIdBrand<TName extends string> = `${Capitalize<TName>}Id`;
|
|
22
22
|
|
|
23
23
|
type BrandedSchema<
|
|
24
24
|
TSchema extends z.core.$ZodType,
|
|
@@ -29,71 +29,71 @@ type BrandableSchema<TSchema extends z.core.$ZodType> = TSchema & {
|
|
|
29
29
|
brand<TBrand extends string>(): BrandedSchema<TSchema, TBrand>;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
/** Output value of a branded
|
|
33
|
-
export type
|
|
32
|
+
/** Output value of a branded entity identity schema. */
|
|
33
|
+
export type EntityIdValue<
|
|
34
34
|
TSchema extends z.core.$ZodType,
|
|
35
35
|
TName extends string,
|
|
36
|
-
> = Branded<z.output<TSchema>,
|
|
36
|
+
> = Branded<z.output<TSchema>, EntityIdBrand<TName>>;
|
|
37
37
|
|
|
38
|
-
/** Runtime metadata attached to schemas returned from `
|
|
39
|
-
export interface
|
|
38
|
+
/** Runtime metadata attached to schemas returned from `entity.id()`. */
|
|
39
|
+
export interface EntityIdMetadata<
|
|
40
40
|
TName extends string = string,
|
|
41
41
|
TIdentity extends string = string,
|
|
42
42
|
> {
|
|
43
|
-
readonly
|
|
43
|
+
readonly entity: TName;
|
|
44
44
|
readonly identity: TIdentity;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/** A structural
|
|
48
|
-
export interface
|
|
47
|
+
/** A structural entity reference declared by another entity field schema. */
|
|
48
|
+
export interface EntityReference<
|
|
49
49
|
TName extends string = string,
|
|
50
50
|
TIdentity extends string = string,
|
|
51
|
-
> extends
|
|
51
|
+
> extends EntityIdMetadata<TName, TIdentity> {
|
|
52
52
|
readonly field: string;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/** Symbol used to tag branded
|
|
56
|
-
export const
|
|
55
|
+
/** Symbol used to tag branded entity reference schemas at runtime. */
|
|
56
|
+
export const ENTITY_ID_METADATA = Symbol.for('@ontrails/core/entity-id');
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* Module-level WeakMap storing
|
|
59
|
+
* Module-level WeakMap storing entity identity metadata keyed by schema object.
|
|
60
60
|
*
|
|
61
|
-
* First-write-wins: when multiple
|
|
62
|
-
* (e.g. `
|
|
61
|
+
* First-write-wins: when multiple entities share the same underlying schema
|
|
62
|
+
* (e.g. `entity('admin', { id: user.shape.id }, ...)`), the first entity to
|
|
63
63
|
* brand the schema claims it. Subsequent calls skip the write to prevent
|
|
64
64
|
* silent metadata corruption.
|
|
65
65
|
*/
|
|
66
|
-
const
|
|
66
|
+
const entityIdMetadata = new WeakMap<object, EntityIdMetadata>();
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
69
|
+
* An entity identity schema branded for one entity and tagged with runtime
|
|
70
70
|
* metadata so the topo layer can recognize declared references later on.
|
|
71
71
|
*/
|
|
72
|
-
export type
|
|
72
|
+
export type EntityIdSchema<
|
|
73
73
|
TSchema extends z.core.$ZodType = z.core.$ZodType,
|
|
74
74
|
TName extends string = string,
|
|
75
75
|
TIdentity extends string = string,
|
|
76
|
-
> = BrandedSchema<TSchema,
|
|
77
|
-
/** @deprecated Use `
|
|
78
|
-
readonly [
|
|
76
|
+
> = BrandedSchema<TSchema, EntityIdBrand<TName>> & {
|
|
77
|
+
/** @deprecated Use `getEntityIdMetadata()` — metadata lives in a WeakMap, not on the schema. */
|
|
78
|
+
readonly [ENTITY_ID_METADATA]?: EntityIdMetadata<TName, TIdentity>;
|
|
79
79
|
};
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* A first-class domain object with schema, identity metadata, and examples.
|
|
83
83
|
*
|
|
84
|
-
*
|
|
84
|
+
* An entity behaves like the `ZodObject` it wraps, so standard Zod composition
|
|
85
85
|
* helpers such as `.pick()`, `.extend()`, and `.array()` continue to work.
|
|
86
86
|
*/
|
|
87
|
-
export type
|
|
87
|
+
export type Entity<
|
|
88
88
|
TName extends string = string,
|
|
89
89
|
TShape extends z.ZodRawShape = z.ZodRawShape,
|
|
90
90
|
TIdentity extends keyof TShape & string = keyof TShape & string,
|
|
91
91
|
> = z.ZodObject<TShape> & {
|
|
92
|
-
readonly kind: '
|
|
92
|
+
readonly kind: 'entity';
|
|
93
93
|
readonly name: TName;
|
|
94
94
|
readonly identity: TIdentity;
|
|
95
95
|
readonly identitySchema: TShape[TIdentity];
|
|
96
|
-
readonly id: () =>
|
|
96
|
+
readonly id: () => EntityIdSchema<TShape[TIdentity], TName, TIdentity>;
|
|
97
97
|
readonly examples?: readonly z.output<z.ZodObject<TShape>>[] | undefined;
|
|
98
98
|
};
|
|
99
99
|
|
|
@@ -115,7 +115,7 @@ const assertIdentityField = <
|
|
|
115
115
|
): void => {
|
|
116
116
|
if (!Object.hasOwn(shape, identity)) {
|
|
117
117
|
throw new TypeError(
|
|
118
|
-
`
|
|
118
|
+
`entity("${name}") identity "${identity}" must match a declared field`
|
|
119
119
|
);
|
|
120
120
|
}
|
|
121
121
|
};
|
|
@@ -129,7 +129,7 @@ const assertExamples = <TShape extends z.ZodRawShape>(
|
|
|
129
129
|
const parsed = schema.safeParse(example);
|
|
130
130
|
if (!parsed.success) {
|
|
131
131
|
throw new TypeError(
|
|
132
|
-
`
|
|
132
|
+
`entity("${name}") example ${index} is invalid: ${formatExampleIssues(parsed.error.issues)}`
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
@@ -150,28 +150,28 @@ const brandIdentitySchema = <
|
|
|
150
150
|
TName extends string,
|
|
151
151
|
TIdentity extends string,
|
|
152
152
|
>(
|
|
153
|
-
|
|
153
|
+
entity: TName,
|
|
154
154
|
identity: TIdentity,
|
|
155
155
|
schema: TSchema
|
|
156
|
-
):
|
|
156
|
+
): EntityIdSchema<TSchema, TName, TIdentity> => {
|
|
157
157
|
const branded = (schema as BrandableSchema<TSchema>).brand<
|
|
158
|
-
|
|
158
|
+
EntityIdBrand<TName>
|
|
159
159
|
>();
|
|
160
160
|
|
|
161
|
-
// First-write-wins: if another
|
|
161
|
+
// First-write-wins: if another entity already claimed this schema object
|
|
162
162
|
// (possible when Zod v4 brand() returns `this`), preserve the original
|
|
163
163
|
// metadata rather than silently overwriting it.
|
|
164
|
-
if (!
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
if (!entityIdMetadata.has(branded)) {
|
|
165
|
+
entityIdMetadata.set(branded, {
|
|
166
|
+
entity,
|
|
167
167
|
identity,
|
|
168
|
-
} satisfies
|
|
168
|
+
} satisfies EntityIdMetadata<TName, TIdentity>);
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
return branded as
|
|
171
|
+
return branded as EntityIdSchema<TSchema, TName, TIdentity>;
|
|
172
172
|
};
|
|
173
173
|
|
|
174
|
-
const
|
|
174
|
+
const attachEntityMetadata = <
|
|
175
175
|
TName extends string,
|
|
176
176
|
TShape extends z.ZodRawShape,
|
|
177
177
|
TIdentity extends keyof TShape & string,
|
|
@@ -179,7 +179,7 @@ const attachContourMetadata = <
|
|
|
179
179
|
schema: z.ZodObject<TShape>,
|
|
180
180
|
metadata: {
|
|
181
181
|
readonly examples?: readonly z.output<z.ZodObject<TShape>>[] | undefined;
|
|
182
|
-
readonly idSchema:
|
|
182
|
+
readonly idSchema: EntityIdSchema<TShape[TIdentity], TName, TIdentity>;
|
|
183
183
|
readonly identity: TIdentity;
|
|
184
184
|
readonly identitySchema: TShape[TIdentity];
|
|
185
185
|
readonly name: TName;
|
|
@@ -208,7 +208,7 @@ const attachContourMetadata = <
|
|
|
208
208
|
},
|
|
209
209
|
kind: {
|
|
210
210
|
enumerable: true,
|
|
211
|
-
value: '
|
|
211
|
+
value: 'entity',
|
|
212
212
|
writable: false,
|
|
213
213
|
},
|
|
214
214
|
name: {
|
|
@@ -219,10 +219,10 @@ const attachContourMetadata = <
|
|
|
219
219
|
});
|
|
220
220
|
};
|
|
221
221
|
|
|
222
|
-
/** Read
|
|
223
|
-
const readMetadata = (schema: unknown):
|
|
222
|
+
/** Read entity identity metadata from the module-level WeakMap, if present. */
|
|
223
|
+
const readMetadata = (schema: unknown): EntityIdMetadata | undefined =>
|
|
224
224
|
typeof schema === 'object' && schema !== null
|
|
225
|
-
?
|
|
225
|
+
? entityIdMetadata.get(schema)
|
|
226
226
|
: undefined;
|
|
227
227
|
|
|
228
228
|
/** Resolve the inner schema from a Zod wrapper (ZodOptional, ZodNullable, etc.). */
|
|
@@ -232,13 +232,13 @@ const unwrapInner = (schema: unknown): unknown => {
|
|
|
232
232
|
};
|
|
233
233
|
|
|
234
234
|
/**
|
|
235
|
-
* Walk through Zod wrapper layers searching for `
|
|
235
|
+
* Walk through Zod wrapper layers searching for `ENTITY_ID_METADATA`.
|
|
236
236
|
*
|
|
237
237
|
* `.nullish()` produces `ZodOptional<ZodNullable<T>>` — two wrapper levels —
|
|
238
238
|
* so a single-step unwrap is insufficient. This iterates until it finds the
|
|
239
239
|
* metadata or exhausts all wrapper layers.
|
|
240
240
|
*/
|
|
241
|
-
const unwrapToMetadata = (schema: unknown):
|
|
241
|
+
const unwrapToMetadata = (schema: unknown): EntityIdMetadata | undefined => {
|
|
242
242
|
let current: unknown = schema;
|
|
243
243
|
while (typeof current === 'object' && current !== null) {
|
|
244
244
|
const inner = unwrapInner(current);
|
|
@@ -255,28 +255,28 @@ const unwrapToMetadata = (schema: unknown): ContourIdMetadata | undefined => {
|
|
|
255
255
|
};
|
|
256
256
|
|
|
257
257
|
/**
|
|
258
|
-
* Read
|
|
258
|
+
* Read entity-reference metadata from a schema returned by `entity.id()`.
|
|
259
259
|
*
|
|
260
260
|
* When the schema is wrapped by Zod combinators (`.optional()`, `.nullable()`,
|
|
261
|
-
* `.default()`, `.nullish()`, etc.) the `
|
|
261
|
+
* `.default()`, `.nullish()`, etc.) the `ENTITY_ID_METADATA` symbol lives on
|
|
262
262
|
* the inner schema, not on the wrapper. The unwrap handles arbitrarily nested
|
|
263
263
|
* wrapper levels.
|
|
264
264
|
*/
|
|
265
|
-
export const
|
|
265
|
+
export const getEntityIdMetadata = (
|
|
266
266
|
schema: unknown
|
|
267
|
-
):
|
|
267
|
+
): EntityIdMetadata | undefined =>
|
|
268
268
|
readMetadata(schema) ?? unwrapToMetadata(schema);
|
|
269
269
|
|
|
270
|
-
/** Inspect
|
|
271
|
-
export const
|
|
272
|
-
|
|
273
|
-
): readonly
|
|
274
|
-
Object.entries(
|
|
270
|
+
/** Inspect an entity schema for fields that reference other entities via `.id()`. */
|
|
271
|
+
export const getEntityReferences = (
|
|
272
|
+
entity: AnyEntity
|
|
273
|
+
): readonly EntityReference[] =>
|
|
274
|
+
Object.entries(entity.shape)
|
|
275
275
|
.flatMap(([field, schema]) => {
|
|
276
|
-
if (field ===
|
|
276
|
+
if (field === entity.identity) {
|
|
277
277
|
return [];
|
|
278
278
|
}
|
|
279
|
-
const metadata =
|
|
279
|
+
const metadata = getEntityIdMetadata(schema);
|
|
280
280
|
if (metadata === undefined) {
|
|
281
281
|
return [];
|
|
282
282
|
}
|
|
@@ -285,16 +285,16 @@ export const getContourReferences = (
|
|
|
285
285
|
})
|
|
286
286
|
.toSorted((left, right) =>
|
|
287
287
|
left.field === right.field
|
|
288
|
-
? left.
|
|
288
|
+
? left.entity.localeCompare(right.entity)
|
|
289
289
|
: left.field.localeCompare(right.field)
|
|
290
290
|
);
|
|
291
291
|
|
|
292
292
|
/**
|
|
293
|
-
* Create
|
|
293
|
+
* Create an entity definition from a raw Zod object shape.
|
|
294
294
|
*
|
|
295
295
|
* @example
|
|
296
296
|
* ```typescript
|
|
297
|
-
* const user =
|
|
297
|
+
* const user = entity(
|
|
298
298
|
* 'user',
|
|
299
299
|
* {
|
|
300
300
|
* id: z.string().uuid(),
|
|
@@ -305,15 +305,15 @@ export const getContourReferences = (
|
|
|
305
305
|
* );
|
|
306
306
|
* ```
|
|
307
307
|
*/
|
|
308
|
-
export const
|
|
308
|
+
export const entity = <
|
|
309
309
|
TName extends string,
|
|
310
310
|
TShape extends z.ZodRawShape,
|
|
311
311
|
TIdentity extends keyof TShape & string,
|
|
312
312
|
>(
|
|
313
313
|
name: TName,
|
|
314
314
|
shape: TShape,
|
|
315
|
-
options:
|
|
316
|
-
):
|
|
315
|
+
options: EntityOptions<TShape, TIdentity>
|
|
316
|
+
): Entity<TName, TShape, TIdentity> => {
|
|
317
317
|
assertIdentityField(name, shape, options.identity);
|
|
318
318
|
|
|
319
319
|
const schema = z.object(shape);
|
|
@@ -322,7 +322,7 @@ export const contour = <
|
|
|
322
322
|
const identitySchema = shape[options.identity];
|
|
323
323
|
if (!identitySchema) {
|
|
324
324
|
throw new TypeError(
|
|
325
|
-
`
|
|
325
|
+
`entity("${name}") identity "${options.identity}" must resolve to a schema`
|
|
326
326
|
);
|
|
327
327
|
}
|
|
328
328
|
|
|
@@ -331,7 +331,7 @@ export const contour = <
|
|
|
331
331
|
? Object.freeze([...options.examples])
|
|
332
332
|
: undefined;
|
|
333
333
|
|
|
334
|
-
|
|
334
|
+
attachEntityMetadata(schema, {
|
|
335
335
|
examples,
|
|
336
336
|
idSchema,
|
|
337
337
|
identity: options.identity,
|
|
@@ -339,8 +339,8 @@ export const contour = <
|
|
|
339
339
|
name,
|
|
340
340
|
});
|
|
341
341
|
|
|
342
|
-
return schema as
|
|
342
|
+
return schema as Entity<TName, TShape, TIdentity>;
|
|
343
343
|
};
|
|
344
344
|
|
|
345
|
-
/** Existential type for heterogeneous
|
|
346
|
-
export type
|
|
345
|
+
/** Existential type for heterogeneous entity collections. */
|
|
346
|
+
export type AnyEntity = Entity<string, z.ZodRawShape, string>;
|
package/src/execute.ts
CHANGED
|
@@ -103,7 +103,7 @@ export interface ExecuteTrailOptions {
|
|
|
103
103
|
/**
|
|
104
104
|
* Typed layers supplied for this execution.
|
|
105
105
|
*
|
|
106
|
-
* Layers compose around the
|
|
106
|
+
* Layers compose around the implementation. Layers without `input`
|
|
107
107
|
* schemas are surface-invisible wrappers for concerns such as tenant guards,
|
|
108
108
|
* rate limiting, circuit breaking, or custom audit logging.
|
|
109
109
|
*/
|
|
@@ -113,7 +113,7 @@ export interface ExecuteTrailOptions {
|
|
|
113
113
|
*
|
|
114
114
|
* Surfaces (CLI, MCP, HTTP) forward their `layers` option here so they
|
|
115
115
|
* compose around every trail dispatched through that surface. The final
|
|
116
|
-
* composition order is `topo → surface → trail →
|
|
116
|
+
* composition order is `topo → surface → trail → implementation` (outermost-first).
|
|
117
117
|
*/
|
|
118
118
|
readonly surfaceLayers?: readonly Layer[] | undefined;
|
|
119
119
|
/**
|
|
@@ -121,7 +121,7 @@ export interface ExecuteTrailOptions {
|
|
|
121
121
|
*
|
|
122
122
|
* The CLI/MCP/HTTP surfaces typically forward `topo.layers` here so the
|
|
123
123
|
* topo's declared layers wrap every trail invocation. The final
|
|
124
|
-
* composition order is `topo → surface → trail →
|
|
124
|
+
* composition order is `topo → surface → trail → implementation` (outermost-first).
|
|
125
125
|
*/
|
|
126
126
|
readonly topoLayers?: readonly Layer[] | undefined;
|
|
127
127
|
/** Factory that produces a base TrailContext (takes precedence over defaults). */
|
|
@@ -1117,22 +1117,22 @@ const runDetourRecovery = async (
|
|
|
1117
1117
|
};
|
|
1118
1118
|
|
|
1119
1119
|
/**
|
|
1120
|
-
* Wrap
|
|
1120
|
+
* Wrap an implementation with the detour recovery loop.
|
|
1121
1121
|
*
|
|
1122
|
-
* If the trail has no detours, returns the
|
|
1123
|
-
* The detour loop runs inside the layer stack, closest to the
|
|
1122
|
+
* If the trail has no detours, returns the implementation unchanged (no wrapper overhead).
|
|
1123
|
+
* The detour loop runs inside the layer stack, closest to the implementation.
|
|
1124
1124
|
*/
|
|
1125
1125
|
const wrapWithDetours = (
|
|
1126
|
-
|
|
1126
|
+
implementation: Implementation<unknown, unknown>,
|
|
1127
1127
|
/* oxlint-disable-next-line no-explicit-any -- existential detour array from AnyTrail */
|
|
1128
1128
|
detours: readonly Detour<any, any, TrailsError>[]
|
|
1129
1129
|
): Implementation<unknown, unknown> => {
|
|
1130
1130
|
if (detours.length === 0) {
|
|
1131
|
-
return
|
|
1131
|
+
return implementation;
|
|
1132
1132
|
}
|
|
1133
1133
|
|
|
1134
1134
|
return async (input, ctx) => {
|
|
1135
|
-
const result = await
|
|
1135
|
+
const result = await implementation(input, ctx);
|
|
1136
1136
|
if (result.isOk()) {
|
|
1137
1137
|
return result;
|
|
1138
1138
|
}
|
|
@@ -1189,11 +1189,11 @@ const prepareRunImpl = (
|
|
|
1189
1189
|
options,
|
|
1190
1190
|
trail.id
|
|
1191
1191
|
);
|
|
1192
|
-
// Detour loop wraps the
|
|
1192
|
+
// Detour loop wraps the implementation (inside layer stack, closest to implementation)
|
|
1193
1193
|
let impl = wrapWithDetours(
|
|
1194
1194
|
bindFireAtLayerBoundary(
|
|
1195
1195
|
bindComposeAtLayerBoundary(
|
|
1196
|
-
trail.
|
|
1196
|
+
trail.implementation as Implementation<unknown, unknown>,
|
|
1197
1197
|
topo,
|
|
1198
1198
|
options
|
|
1199
1199
|
),
|
|
@@ -1302,9 +1302,9 @@ const runTrail = async (
|
|
|
1302
1302
|
/**
|
|
1303
1303
|
* Compose the typed layers attached at topo, surface, and trail scope.
|
|
1304
1304
|
*
|
|
1305
|
-
* Composition order is topo → surface → trail → execution-supplied →
|
|
1305
|
+
* Composition order is topo → surface → trail → execution-supplied → implementation
|
|
1306
1306
|
* (outermost-first): trail-scope layers run inside surface/topo layers, and
|
|
1307
|
-
* `executeTrail({ layers })` layers wrap closest to the
|
|
1307
|
+
* `executeTrail({ layers })` layers wrap closest to the implementation for per-call
|
|
1308
1308
|
* behavior.
|
|
1309
1309
|
*/
|
|
1310
1310
|
const composeAttachedLayers = (
|
|
@@ -1352,7 +1352,7 @@ const createForkTrailVersion = (
|
|
|
1352
1352
|
entry: TrailVersionForkEntry
|
|
1353
1353
|
): AnyTrail => {
|
|
1354
1354
|
const {
|
|
1355
|
-
|
|
1355
|
+
implementation: _implementation,
|
|
1356
1356
|
composeInput: _composeInput,
|
|
1357
1357
|
composes: _composes,
|
|
1358
1358
|
detours: _detours,
|
|
@@ -1366,12 +1366,12 @@ const createForkTrailVersion = (
|
|
|
1366
1366
|
|
|
1367
1367
|
return Object.freeze({
|
|
1368
1368
|
...base,
|
|
1369
|
-
blaze: entry.blaze,
|
|
1370
1369
|
composes: Object.freeze([...(entry.composes ?? [])]),
|
|
1371
1370
|
detours: Object.freeze([...(entry.detours ?? [])]),
|
|
1372
1371
|
...(entry.composeInput === undefined
|
|
1373
1372
|
? {}
|
|
1374
1373
|
: { composeInput: entry.composeInput }),
|
|
1374
|
+
implementation: entry.implementation,
|
|
1375
1375
|
input: entry.input,
|
|
1376
1376
|
output: entry.output,
|
|
1377
1377
|
resources: Object.freeze([...(entry.resources ?? [])]),
|
package/src/fire.ts
CHANGED
|
@@ -98,6 +98,9 @@ const frameworkFireFns = new WeakSet<FireFn>();
|
|
|
98
98
|
export const isFrameworkFireFn = (fire: FireFn | undefined): boolean =>
|
|
99
99
|
fire !== undefined && frameworkFireFns.has(fire);
|
|
100
100
|
|
|
101
|
+
const createFireId = (): string =>
|
|
102
|
+
typeof Bun === 'undefined' ? crypto.randomUUID() : Bun.randomUUIDv7();
|
|
103
|
+
|
|
101
104
|
type FireDispatchTracker = Set<Promise<void>>;
|
|
102
105
|
|
|
103
106
|
const getFireDispatchTracker = (
|
|
@@ -284,7 +287,7 @@ const deriveFireDiagnosticMetadata = (
|
|
|
284
287
|
): FireDiagnosticMetadata => {
|
|
285
288
|
const trace = producerCtx ? getTraceContext(producerCtx) : undefined;
|
|
286
289
|
const parent = getActivationProvenance(producerCtx);
|
|
287
|
-
const fireId =
|
|
290
|
+
const fireId = createFireId();
|
|
288
291
|
return {
|
|
289
292
|
activation: {
|
|
290
293
|
fireId,
|
|
@@ -978,7 +981,7 @@ export const createFireFn = (
|
|
|
978
981
|
// Pre-bind fire on the consumer ctx as a safety net for direct
|
|
979
982
|
// executeTrail calls that skip the topo-aware path. In the normal
|
|
980
983
|
// fan-out flow below, bindFireToCtx in execute.ts rebinds fire to
|
|
981
|
-
// the fully-traced ctx before the
|
|
984
|
+
// the fully-traced ctx before the implementation runs, so this assignment
|
|
982
985
|
// is superseded — but keeping it makes consumerCtx self-sufficient
|
|
983
986
|
// for any caller that inspects it pre-execution.
|
|
984
987
|
fire: createFireFn(
|
package/src/index.ts
CHANGED
|
@@ -222,7 +222,7 @@ export {
|
|
|
222
222
|
} from './version-resolution.js';
|
|
223
223
|
export type {
|
|
224
224
|
AnyTrail,
|
|
225
|
-
|
|
225
|
+
ImplementationInput,
|
|
226
226
|
Intent,
|
|
227
227
|
Trail,
|
|
228
228
|
TrailVersionEntry,
|
|
@@ -300,6 +300,8 @@ export { inputOf, outputOf } from './type-utils.js';
|
|
|
300
300
|
// Signal
|
|
301
301
|
export { signal } from './signal.js';
|
|
302
302
|
export type { AnySignal, Signal, SignalSpec } from './signal.js';
|
|
303
|
+
export { queue, validateQueueSource } from './queue.js';
|
|
304
|
+
export type { QueueSource, QueueSpec, QueueValidationIssue } from './queue.js';
|
|
303
305
|
export { schedule, validateScheduleSource } from './schedule.js';
|
|
304
306
|
export type {
|
|
305
307
|
ScheduleSource,
|
|
@@ -398,23 +400,23 @@ export type {
|
|
|
398
400
|
LateBoundSignalRef,
|
|
399
401
|
} from './signal-ref.js';
|
|
400
402
|
|
|
401
|
-
//
|
|
402
|
-
export {
|
|
403
|
+
// Entity
|
|
404
|
+
export { entity } from './entity.js';
|
|
403
405
|
export {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
} from './
|
|
406
|
+
ENTITY_ID_METADATA,
|
|
407
|
+
getEntityIdMetadata,
|
|
408
|
+
getEntityReferences,
|
|
409
|
+
} from './entity.js';
|
|
408
410
|
export type {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
} from './
|
|
411
|
+
AnyEntity,
|
|
412
|
+
Entity,
|
|
413
|
+
EntityIdBrand,
|
|
414
|
+
EntityIdMetadata,
|
|
415
|
+
EntityIdSchema,
|
|
416
|
+
EntityIdValue,
|
|
417
|
+
EntityOptions,
|
|
418
|
+
EntityReference,
|
|
419
|
+
} from './entity.js';
|
|
418
420
|
|
|
419
421
|
// Topo
|
|
420
422
|
export { topo } from './topo.js';
|
|
@@ -447,7 +449,7 @@ export type {
|
|
|
447
449
|
|
|
448
450
|
// Generic trails-db helpers (shared framework infrastructure per ADR-0014).
|
|
449
451
|
// The topo-store public API that previously lived here moved to
|
|
450
|
-
// `@ontrails/
|
|
452
|
+
// `@ontrails/topography` per ADR-0042.
|
|
451
453
|
export {
|
|
452
454
|
deriveTrailsDbPath,
|
|
453
455
|
deriveTrailsDir,
|
package/src/layer.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { Implementation } from './types.js';
|
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* A composable, named layer that wraps
|
|
11
|
+
* A composable, named layer that wraps implementations.
|
|
12
12
|
*
|
|
13
13
|
* Layers attach at trail, surface, or topo scope and may declare an object
|
|
14
14
|
* `input` schema describing the configuration they need from the surrounding
|
package/src/observe.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface TopoOptions {
|
|
|
28
28
|
* Layers declared here wrap every trail invoked through this topo, on every
|
|
29
29
|
* surface. The execution pipeline composes topo-scope layers outermost —
|
|
30
30
|
* around surface-scope and trail-scope layers — so the final order is
|
|
31
|
-
* `topo → surface → trail →
|
|
31
|
+
* `topo → surface → trail → implementation` (outermost-first).
|
|
32
32
|
*/
|
|
33
33
|
readonly layers?: readonly Layer[] | undefined;
|
|
34
34
|
}
|