@ontrails/core 1.0.0-beta.15 → 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.
Files changed (348) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +61 -16
  3. package/package.json +9 -8
  4. package/src/activation-provenance.ts +111 -0
  5. package/src/activation-source-compatibility.ts +430 -0
  6. package/src/activation-source-projection.ts +221 -0
  7. package/src/activation-source.ts +91 -0
  8. package/src/blob-ref.ts +51 -0
  9. package/src/context.ts +4 -0
  10. package/src/{internal/cross-batch.ts → cross-batch.ts} +3 -11
  11. package/src/detours.ts +8 -0
  12. package/src/draft.ts +4 -0
  13. package/src/error-projection.ts +51 -0
  14. package/src/errors.ts +192 -39
  15. package/src/execute.ts +443 -44
  16. package/src/fire.ts +965 -91
  17. package/src/index.ts +285 -38
  18. package/src/layer-projection.ts +192 -0
  19. package/src/layer.ts +32 -1
  20. package/src/observe.ts +361 -0
  21. package/src/permits.ts +12 -0
  22. package/src/redaction/patterns.ts +6 -3
  23. package/src/resilience.ts +8 -8
  24. package/src/resource-config.ts +477 -44
  25. package/src/resource.ts +21 -1
  26. package/src/result.ts +18 -0
  27. package/src/run.ts +2 -2
  28. package/src/schedule-runtime.ts +689 -0
  29. package/src/schedule.ts +324 -0
  30. package/src/serialization.ts +90 -57
  31. package/src/signal-diagnostics.ts +633 -0
  32. package/src/{internal/signal-ref.ts → signal-ref.ts} +1 -1
  33. package/src/signal.ts +37 -0
  34. package/src/store/accessor-protocol.ts +5 -5
  35. package/src/structured-examples.ts +245 -0
  36. package/src/surface-derivation.ts +91 -0
  37. package/src/surface-filter.ts +7 -13
  38. package/src/topo.ts +386 -6
  39. package/src/tracing.ts +345 -0
  40. package/src/trail.ts +168 -22
  41. package/src/trails/derive-trail.ts +1 -1
  42. package/src/trails/ingest.ts +7 -15
  43. package/src/{internal/trails-db.ts → trails-db.ts} +42 -21
  44. package/src/transport-error-map.ts +146 -28
  45. package/src/types.ts +82 -28
  46. package/src/validate-established-topo.ts +6 -0
  47. package/src/validate-topo.ts +212 -5
  48. package/src/validation.ts +58 -1
  49. package/src/webhook.ts +276 -0
  50. package/src/{internal/zod-wrappers.ts → zod-wrappers.ts} +0 -6
  51. package/.turbo/turbo-build.log +0 -1
  52. package/.turbo/turbo-lint.log +0 -3
  53. package/.turbo/turbo-typecheck.log +0 -1
  54. package/dist/blob-ref.d.ts +0 -20
  55. package/dist/blob-ref.d.ts.map +0 -1
  56. package/dist/blob-ref.js +0 -22
  57. package/dist/blob-ref.js.map +0 -1
  58. package/dist/branded.d.ts +0 -36
  59. package/dist/branded.d.ts.map +0 -1
  60. package/dist/branded.js +0 -89
  61. package/dist/branded.js.map +0 -1
  62. package/dist/collections.d.ts +0 -31
  63. package/dist/collections.d.ts.map +0 -1
  64. package/dist/collections.js +0 -60
  65. package/dist/collections.js.map +0 -1
  66. package/dist/context.d.ts +0 -22
  67. package/dist/context.d.ts.map +0 -1
  68. package/dist/context.js +0 -37
  69. package/dist/context.js.map +0 -1
  70. package/dist/contour.d.ts +0 -81
  71. package/dist/contour.d.ts.map +0 -1
  72. package/dist/contour.js +0 -176
  73. package/dist/contour.js.map +0 -1
  74. package/dist/cross-schema.d.ts +0 -18
  75. package/dist/cross-schema.d.ts.map +0 -1
  76. package/dist/cross-schema.js +0 -29
  77. package/dist/cross-schema.js.map +0 -1
  78. package/dist/derive.d.ts +0 -44
  79. package/dist/derive.d.ts.map +0 -1
  80. package/dist/derive.js +0 -149
  81. package/dist/derive.js.map +0 -1
  82. package/dist/draft.d.ts +0 -28
  83. package/dist/draft.d.ts.map +0 -1
  84. package/dist/draft.js +0 -157
  85. package/dist/draft.js.map +0 -1
  86. package/dist/errors.d.ts +0 -141
  87. package/dist/errors.d.ts.map +0 -1
  88. package/dist/errors.js +0 -185
  89. package/dist/errors.js.map +0 -1
  90. package/dist/execute.d.ts +0 -52
  91. package/dist/execute.d.ts.map +0 -1
  92. package/dist/execute.js +0 -511
  93. package/dist/execute.js.map +0 -1
  94. package/dist/fetch.d.ts +0 -15
  95. package/dist/fetch.d.ts.map +0 -1
  96. package/dist/fetch.js +0 -102
  97. package/dist/fetch.js.map +0 -1
  98. package/dist/fire.d.ts +0 -43
  99. package/dist/fire.d.ts.map +0 -1
  100. package/dist/fire.js +0 -185
  101. package/dist/fire.js.map +0 -1
  102. package/dist/guards.d.ts +0 -17
  103. package/dist/guards.d.ts.map +0 -1
  104. package/dist/guards.js +0 -25
  105. package/dist/guards.js.map +0 -1
  106. package/dist/index.d.ts +0 -59
  107. package/dist/index.d.ts.map +0 -1
  108. package/dist/index.js +0 -61
  109. package/dist/index.js.map +0 -1
  110. package/dist/internal/cross-batch.d.ts +0 -44
  111. package/dist/internal/cross-batch.d.ts.map +0 -1
  112. package/dist/internal/cross-batch.js +0 -58
  113. package/dist/internal/cross-batch.js.map +0 -1
  114. package/dist/internal/fork-ctx.d.ts +0 -41
  115. package/dist/internal/fork-ctx.d.ts.map +0 -1
  116. package/dist/internal/fork-ctx.js +0 -47
  117. package/dist/internal/fork-ctx.js.map +0 -1
  118. package/dist/internal/signal-ref.d.ts +0 -15
  119. package/dist/internal/signal-ref.d.ts.map +0 -1
  120. package/dist/internal/signal-ref.js +0 -41
  121. package/dist/internal/signal-ref.js.map +0 -1
  122. package/dist/internal/topo-saves.d.ts +0 -53
  123. package/dist/internal/topo-saves.d.ts.map +0 -1
  124. package/dist/internal/topo-saves.js +0 -449
  125. package/dist/internal/topo-saves.js.map +0 -1
  126. package/dist/internal/topo-snapshots.d.ts +0 -57
  127. package/dist/internal/topo-snapshots.d.ts.map +0 -1
  128. package/dist/internal/topo-snapshots.js +0 -372
  129. package/dist/internal/topo-snapshots.js.map +0 -1
  130. package/dist/internal/topo-store-read.d.ts +0 -70
  131. package/dist/internal/topo-store-read.d.ts.map +0 -1
  132. package/dist/internal/topo-store-read.js +0 -221
  133. package/dist/internal/topo-store-read.js.map +0 -1
  134. package/dist/internal/topo-store.d.ts +0 -26
  135. package/dist/internal/topo-store.d.ts.map +0 -1
  136. package/dist/internal/topo-store.js +0 -621
  137. package/dist/internal/topo-store.js.map +0 -1
  138. package/dist/internal/tracing.d.ts +0 -98
  139. package/dist/internal/tracing.d.ts.map +0 -1
  140. package/dist/internal/tracing.js +0 -102
  141. package/dist/internal/tracing.js.map +0 -1
  142. package/dist/internal/trails-db.d.ts +0 -16
  143. package/dist/internal/trails-db.d.ts.map +0 -1
  144. package/dist/internal/trails-db.js +0 -118
  145. package/dist/internal/trails-db.js.map +0 -1
  146. package/dist/internal/zod-wrappers.d.ts +0 -40
  147. package/dist/internal/zod-wrappers.d.ts.map +0 -1
  148. package/dist/internal/zod-wrappers.js +0 -59
  149. package/dist/internal/zod-wrappers.js.map +0 -1
  150. package/dist/layer.d.ts +0 -23
  151. package/dist/layer.d.ts.map +0 -1
  152. package/dist/layer.js +0 -21
  153. package/dist/layer.js.map +0 -1
  154. package/dist/path-security.d.ts +0 -28
  155. package/dist/path-security.d.ts.map +0 -1
  156. package/dist/path-security.js +0 -63
  157. package/dist/path-security.js.map +0 -1
  158. package/dist/patterns/bulk.d.ts +0 -15
  159. package/dist/patterns/bulk.d.ts.map +0 -1
  160. package/dist/patterns/bulk.js +0 -14
  161. package/dist/patterns/bulk.js.map +0 -1
  162. package/dist/patterns/change.d.ts +0 -10
  163. package/dist/patterns/change.d.ts.map +0 -1
  164. package/dist/patterns/change.js +0 -10
  165. package/dist/patterns/change.js.map +0 -1
  166. package/dist/patterns/date-range.d.ts +0 -10
  167. package/dist/patterns/date-range.d.ts.map +0 -1
  168. package/dist/patterns/date-range.js +0 -10
  169. package/dist/patterns/date-range.js.map +0 -1
  170. package/dist/patterns/index.d.ts +0 -9
  171. package/dist/patterns/index.d.ts.map +0 -1
  172. package/dist/patterns/index.js +0 -9
  173. package/dist/patterns/index.js.map +0 -1
  174. package/dist/patterns/pagination.d.ts +0 -18
  175. package/dist/patterns/pagination.d.ts.map +0 -1
  176. package/dist/patterns/pagination.js +0 -18
  177. package/dist/patterns/pagination.js.map +0 -1
  178. package/dist/patterns/progress.d.ts +0 -11
  179. package/dist/patterns/progress.d.ts.map +0 -1
  180. package/dist/patterns/progress.js +0 -11
  181. package/dist/patterns/progress.js.map +0 -1
  182. package/dist/patterns/sorting.d.ts +0 -13
  183. package/dist/patterns/sorting.d.ts.map +0 -1
  184. package/dist/patterns/sorting.js +0 -10
  185. package/dist/patterns/sorting.js.map +0 -1
  186. package/dist/patterns/status.d.ts +0 -15
  187. package/dist/patterns/status.d.ts.map +0 -1
  188. package/dist/patterns/status.js +0 -9
  189. package/dist/patterns/status.js.map +0 -1
  190. package/dist/patterns/timestamps.d.ts +0 -10
  191. package/dist/patterns/timestamps.d.ts.map +0 -1
  192. package/dist/patterns/timestamps.js +0 -10
  193. package/dist/patterns/timestamps.js.map +0 -1
  194. package/dist/redaction/index.d.ts +0 -4
  195. package/dist/redaction/index.d.ts.map +0 -1
  196. package/dist/redaction/index.js +0 -3
  197. package/dist/redaction/index.js.map +0 -1
  198. package/dist/redaction/patterns.d.ts +0 -9
  199. package/dist/redaction/patterns.d.ts.map +0 -1
  200. package/dist/redaction/patterns.js +0 -39
  201. package/dist/redaction/patterns.js.map +0 -1
  202. package/dist/redaction/redactor.d.ts +0 -27
  203. package/dist/redaction/redactor.d.ts.map +0 -1
  204. package/dist/redaction/redactor.js +0 -89
  205. package/dist/redaction/redactor.js.map +0 -1
  206. package/dist/resilience.d.ts +0 -34
  207. package/dist/resilience.d.ts.map +0 -1
  208. package/dist/resilience.js +0 -164
  209. package/dist/resilience.js.map +0 -1
  210. package/dist/resource-config.d.ts +0 -22
  211. package/dist/resource-config.d.ts.map +0 -1
  212. package/dist/resource-config.js +0 -210
  213. package/dist/resource-config.js.map +0 -1
  214. package/dist/resource.d.ts +0 -74
  215. package/dist/resource.d.ts.map +0 -1
  216. package/dist/resource.js +0 -61
  217. package/dist/resource.js.map +0 -1
  218. package/dist/result.d.ts +0 -57
  219. package/dist/result.d.ts.map +0 -1
  220. package/dist/result.js +0 -156
  221. package/dist/result.js.map +0 -1
  222. package/dist/run.d.ts +0 -28
  223. package/dist/run.d.ts.map +0 -1
  224. package/dist/run.js +0 -35
  225. package/dist/run.js.map +0 -1
  226. package/dist/serialization.d.ts +0 -27
  227. package/dist/serialization.d.ts.map +0 -1
  228. package/dist/serialization.js +0 -154
  229. package/dist/serialization.js.map +0 -1
  230. package/dist/signal.d.ts +0 -33
  231. package/dist/signal.d.ts.map +0 -1
  232. package/dist/signal.js +0 -17
  233. package/dist/signal.js.map +0 -1
  234. package/dist/store/accessor-protocol.d.ts +0 -51
  235. package/dist/store/accessor-protocol.d.ts.map +0 -1
  236. package/dist/store/accessor-protocol.js +0 -2
  237. package/dist/store/accessor-protocol.js.map +0 -1
  238. package/dist/store/index.d.ts +0 -2
  239. package/dist/store/index.d.ts.map +0 -1
  240. package/dist/store/index.js +0 -2
  241. package/dist/store/index.js.map +0 -1
  242. package/dist/surface-filter.d.ts +0 -13
  243. package/dist/surface-filter.d.ts.map +0 -1
  244. package/dist/surface-filter.js +0 -76
  245. package/dist/surface-filter.js.map +0 -1
  246. package/dist/topo-store.d.ts +0 -62
  247. package/dist/topo-store.d.ts.map +0 -1
  248. package/dist/topo-store.js +0 -319
  249. package/dist/topo-store.js.map +0 -1
  250. package/dist/topo.d.ts +0 -39
  251. package/dist/topo.d.ts.map +0 -1
  252. package/dist/topo.js +0 -241
  253. package/dist/topo.js.map +0 -1
  254. package/dist/trail.d.ts +0 -163
  255. package/dist/trail.d.ts.map +0 -1
  256. package/dist/trail.js +0 -75
  257. package/dist/trail.js.map +0 -1
  258. package/dist/trails/derive-trail.d.ts +0 -66
  259. package/dist/trails/derive-trail.d.ts.map +0 -1
  260. package/dist/trails/derive-trail.js +0 -378
  261. package/dist/trails/derive-trail.js.map +0 -1
  262. package/dist/trails/index.d.ts +0 -5
  263. package/dist/trails/index.d.ts.map +0 -1
  264. package/dist/trails/index.js +0 -3
  265. package/dist/trails/index.js.map +0 -1
  266. package/dist/trails/ingest.d.ts +0 -24
  267. package/dist/trails/ingest.d.ts.map +0 -1
  268. package/dist/trails/ingest.js +0 -63
  269. package/dist/trails/ingest.js.map +0 -1
  270. package/dist/transport-error-map.d.ts +0 -106
  271. package/dist/transport-error-map.d.ts.map +0 -1
  272. package/dist/transport-error-map.js +0 -24
  273. package/dist/transport-error-map.js.map +0 -1
  274. package/dist/type-checks.test-d.d.ts +0 -71
  275. package/dist/type-checks.test-d.d.ts.map +0 -1
  276. package/dist/type-checks.test-d.js +0 -12
  277. package/dist/type-checks.test-d.js.map +0 -1
  278. package/dist/type-utils.d.ts +0 -33
  279. package/dist/type-utils.d.ts.map +0 -1
  280. package/dist/type-utils.js +0 -12
  281. package/dist/type-utils.js.map +0 -1
  282. package/dist/types.d.ts +0 -177
  283. package/dist/types.d.ts.map +0 -1
  284. package/dist/types.js +0 -3
  285. package/dist/types.js.map +0 -1
  286. package/dist/validate-established-topo.d.ts +0 -76
  287. package/dist/validate-established-topo.d.ts.map +0 -1
  288. package/dist/validate-established-topo.js +0 -43
  289. package/dist/validate-established-topo.js.map +0 -1
  290. package/dist/validate-topo.d.ts +0 -24
  291. package/dist/validate-topo.d.ts.map +0 -1
  292. package/dist/validate-topo.js +0 -193
  293. package/dist/validate-topo.js.map +0 -1
  294. package/dist/validation.d.ts +0 -27
  295. package/dist/validation.d.ts.map +0 -1
  296. package/dist/validation.js +0 -165
  297. package/dist/validation.js.map +0 -1
  298. package/dist/workspace.d.ts +0 -25
  299. package/dist/workspace.d.ts.map +0 -1
  300. package/dist/workspace.js +0 -57
  301. package/dist/workspace.js.map +0 -1
  302. package/src/__tests__/blob-ref.test.ts +0 -103
  303. package/src/__tests__/branded.test.ts +0 -148
  304. package/src/__tests__/collections.test.ts +0 -126
  305. package/src/__tests__/context.test.ts +0 -77
  306. package/src/__tests__/contour.test.ts +0 -263
  307. package/src/__tests__/derive-trail.test.ts +0 -668
  308. package/src/__tests__/derive.test.ts +0 -260
  309. package/src/__tests__/errors.test.ts +0 -427
  310. package/src/__tests__/execute.test.ts +0 -1563
  311. package/src/__tests__/fetch.test.ts +0 -217
  312. package/src/__tests__/fire.test.ts +0 -1056
  313. package/src/__tests__/fork-ctx.test.ts +0 -83
  314. package/src/__tests__/guards.test.ts +0 -102
  315. package/src/__tests__/ingest.test.ts +0 -191
  316. package/src/__tests__/layer.test.ts +0 -125
  317. package/src/__tests__/path-security.test.ts +0 -114
  318. package/src/__tests__/patterns.test.ts +0 -273
  319. package/src/__tests__/redaction.test.ts +0 -244
  320. package/src/__tests__/resilience.test.ts +0 -246
  321. package/src/__tests__/resource.test.ts +0 -204
  322. package/src/__tests__/result.test.ts +0 -155
  323. package/src/__tests__/run.test.ts +0 -181
  324. package/src/__tests__/serialization.test.ts +0 -401
  325. package/src/__tests__/service-config.test.ts +0 -224
  326. package/src/__tests__/signal.test.ts +0 -82
  327. package/src/__tests__/surface-filter.test.ts +0 -196
  328. package/src/__tests__/topo-store-read.test.ts +0 -256
  329. package/src/__tests__/topo-store.test.ts +0 -869
  330. package/src/__tests__/topo.test.ts +0 -573
  331. package/src/__tests__/trail-permit.test.ts +0 -60
  332. package/src/__tests__/trail.test.ts +0 -426
  333. package/src/__tests__/trails-db.test.ts +0 -198
  334. package/src/__tests__/transport-error-map.test.ts +0 -82
  335. package/src/__tests__/type-utils.test.ts +0 -118
  336. package/src/__tests__/validate-topo.test.ts +0 -611
  337. package/src/__tests__/validation.test.ts +0 -336
  338. package/src/__tests__/workspace.test.ts +0 -183
  339. package/src/__tests__/zod-wrappers.test.ts +0 -62
  340. package/src/internal/topo-snapshots.ts +0 -517
  341. package/src/internal/topo-store-read.ts +0 -481
  342. package/src/internal/topo-store.ts +0 -1192
  343. package/src/internal/tracing.ts +0 -187
  344. package/src/topo-store.ts +0 -510
  345. package/src/type-checks.test-d.ts +0 -106
  346. package/tsconfig.json +0 -9
  347. package/tsconfig.tests.json +0 -10
  348. package/tsconfig.tsbuildinfo +0 -1
@@ -10,7 +10,6 @@ import type { Trail, TrailExample, TrailSpec } from '../trail.js';
10
10
  import type { TrailContext } from '../types.js';
11
11
 
12
12
  type SchemaValue<TSchema extends z.ZodType> = z.output<TSchema>;
13
- type SignalRef<TSignal> = string | Signal<TSignal>;
14
13
 
15
14
  type ExampleBearingSchema<TSchema extends z.ZodType> = TSchema & {
16
15
  readonly examples?: readonly Partial<SchemaValue<TSchema>>[] | undefined;
@@ -24,8 +23,8 @@ interface IngestBaseOptions<TSchema extends z.ZodType, TSignal> extends Omit<
24
23
  readonly id?: string | undefined;
25
24
  /** Validated external payload shape. */
26
25
  readonly schema: TSchema;
27
- /** Signal to emit after verification and optional transformation. */
28
- readonly signal: SignalRef<TSignal>;
26
+ /** Signal to fire after verification and optional transformation. */
27
+ readonly signal: Signal<TSignal>;
29
28
  /** Optional per-trail verification layer, e.g. HMAC signature checks. */
30
29
  readonly verify?: Layer | undefined;
31
30
  }
@@ -44,9 +43,6 @@ export interface IngestOptions<
44
43
  | undefined;
45
44
  }
46
45
 
47
- const resolveSignalId = <TSignal>(signalRef: SignalRef<TSignal>): string =>
48
- typeof signalRef === 'string' ? signalRef : signalRef.id;
49
-
50
46
  const deriveExampleName = (signalId: string, index: number): string =>
51
47
  `Ingest ${signalId} ${index + 1}`;
52
48
 
@@ -69,7 +65,7 @@ const deriveExamples = <TSchema extends z.ZodType>(
69
65
 
70
66
  const createIngestBlaze =
71
67
  <TSchema extends z.ZodType, TSignal>(
72
- signalRef: SignalRef<TSignal>,
68
+ signalRef: Signal<TSignal>,
73
69
  signalId: string,
74
70
  trailId: string,
75
71
  transform: IngestTransform<SchemaValue<TSchema>, TSignal> | undefined
@@ -81,7 +77,7 @@ const createIngestBlaze =
81
77
  if (ctx.fire === undefined) {
82
78
  return Result.err(
83
79
  new InternalError(
84
- `ingest("${trailId}") requires topo-backed execution to emit "${signalId}"`
80
+ `ingest("${trailId}") requires topo-backed execution to fire "${signalId}"`
85
81
  )
86
82
  );
87
83
  }
@@ -91,12 +87,8 @@ const createIngestBlaze =
91
87
  transform === undefined
92
88
  ? (input as TSignal)
93
89
  : await transform(input, ctx);
94
- const fired =
95
- typeof signalRef === 'string'
96
- ? await ctx.fire(signalRef, payload as unknown)
97
- : await ctx.fire(signalRef as Signal<unknown>, payload as unknown);
98
-
99
- return fired.isErr() ? Result.err(fired.error) : Result.ok();
90
+ await ctx.fire(signalRef, payload);
91
+ return Result.ok();
100
92
  } catch (error) {
101
93
  const message = `ingest("${trailId}"): ${error instanceof Error ? error.message : String(error)}`;
102
94
  return Result.err(
@@ -113,7 +105,7 @@ export const ingest = <
113
105
  >(
114
106
  options: IngestOptions<TSchema, TSignal>
115
107
  ): Trail<SchemaValue<TSchema>, void> => {
116
- const signalId = resolveSignalId(options.signal);
108
+ const signalId = options.signal.id;
117
109
  const id = options.id ?? `${signalId}.ingest`;
118
110
  const { id: _id, schema, signal, transform, verify, ...trailSpec } = options;
119
111
  const baseBlaze = createIngestBlaze<TSchema, TSignal>(
@@ -2,28 +2,41 @@ import { Database } from 'bun:sqlite';
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
 
5
- import { NotFoundError } from '../errors.js';
5
+ import { NotFoundError } from './errors.js';
6
6
 
7
7
  const TRAILS_DIR = '.trails';
8
8
  const TRAILS_DB_FILE = 'trails.db';
9
+ const TRAILS_CACHE_DIR = 'cache';
10
+ const TRAILS_STATE_DIR = 'state';
9
11
  const SCHEMA_VERSION_TABLE = 'meta_schema_versions';
10
- const WORKSPACE_SUBDIRS = ['config', 'dev', 'generated'] as const;
11
- const REQUIRED_GITIGNORE_LINES = [
12
+ const WORKSPACE_SUBDIRS = [TRAILS_CACHE_DIR, TRAILS_STATE_DIR] as const;
13
+
14
+ /**
15
+ * The canonical lines written to a freshly-bootstrapped
16
+ * `.trails/.gitignore`. Kept as the source of truth for every consumer that
17
+ * needs to either write the file (scaffold) or audit its content (tests).
18
+ *
19
+ * @see {@link WORKSPACE_GITIGNORE_CONTENT} for the rendered string form.
20
+ */
21
+ export const WORKSPACE_GITIGNORE_LINES = [
12
22
  '# Local config overrides',
13
- 'config/',
23
+ 'config.local.js',
24
+ 'config.local.ts',
14
25
  '',
15
- '# Development state',
16
- 'dev/',
26
+ '# Rebuildable cache',
27
+ 'cache/',
17
28
  '',
18
- '# Generated artifacts',
19
- 'generated/',
29
+ '# Mutable runtime state',
30
+ 'state/',
20
31
  '',
21
- '# Shared Trails database',
22
- 'trails.db',
23
- 'trails.db-shm',
24
- 'trails.db-wal',
25
- '',
26
- ];
32
+ ] as const;
33
+
34
+ /**
35
+ * The canonical rendered `.trails/.gitignore` content. Use this when writing
36
+ * the file eagerly (e.g. during `trails create` scaffolding) or when asserting
37
+ * on the workspace bootstrap output.
38
+ */
39
+ export const WORKSPACE_GITIGNORE_CONTENT = `${WORKSPACE_GITIGNORE_LINES.join('\n').trimEnd()}\n`;
27
40
 
28
41
  export interface TrailsDbLocationOptions {
29
42
  readonly path?: string;
@@ -49,20 +62,18 @@ export const deriveTrailsDir = (options?: TrailsDbLocationOptions): string =>
49
62
  export const deriveTrailsDbPath = (options?: TrailsDbLocationOptions): string =>
50
63
  options?.path
51
64
  ? resolve(options.path)
52
- : join(deriveTrailsDir(options), TRAILS_DB_FILE);
65
+ : join(deriveTrailsDir(options), TRAILS_STATE_DIR, TRAILS_DB_FILE);
53
66
 
54
67
  const ensureDbParentDir = (dbPath: string): void => {
55
68
  mkdirSync(dirname(dbPath), { recursive: true });
56
69
  };
57
70
 
58
- const GITIGNORE_TEMPLATE = `${REQUIRED_GITIGNORE_LINES.join('\n').trimEnd()}\n`;
59
-
60
71
  const appendMissingGitignoreLines = (
61
72
  gitignorePath: string,
62
73
  content: string
63
74
  ): void => {
64
75
  const existingLines = new Set(content.split('\n').map((l) => l.trim()));
65
- const missing = REQUIRED_GITIGNORE_LINES.filter(
76
+ const missing = WORKSPACE_GITIGNORE_LINES.filter(
66
77
  (line) => line !== '' && !existingLines.has(line)
67
78
  );
68
79
 
@@ -78,7 +89,7 @@ const ensureWorkspaceGitignore = (trailsDir: string): void => {
78
89
  const gitignorePath = join(trailsDir, '.gitignore');
79
90
 
80
91
  if (!existsSync(gitignorePath)) {
81
- writeFileSync(gitignorePath, GITIGNORE_TEMPLATE);
92
+ writeFileSync(gitignorePath, WORKSPACE_GITIGNORE_CONTENT);
82
93
  return;
83
94
  }
84
95
 
@@ -88,7 +99,17 @@ const ensureWorkspaceGitignore = (trailsDir: string): void => {
88
99
  );
89
100
  };
90
101
 
91
- const ensureWorkspaceDir = (rootDir: string): void => {
102
+ /**
103
+ * Bootstrap the `.trails/` workspace at `rootDir`.
104
+ *
105
+ * Creates the workspace directory plus the canonical `cache/` and `state/`
106
+ * subdirectories, then either writes a fresh `.gitignore` matching
107
+ * {@link WORKSPACE_GITIGNORE_CONTENT} or appends any missing canonical lines
108
+ * to an existing one. Safe to call repeatedly. This is the single canonical
109
+ * source of truth for workspace layout — scaffolding, configuration loading,
110
+ * and runtime DB initialization all flow through here.
111
+ */
112
+ export const ensureTrailsWorkspace = (rootDir: string): void => {
92
113
  const trailsDir = deriveTrailsDir({ rootDir });
93
114
  mkdirSync(trailsDir, { recursive: true });
94
115
  for (const subdir of WORKSPACE_SUBDIRS) {
@@ -144,7 +165,7 @@ export const openWriteTrailsDb = (
144
165
  );
145
166
 
146
167
  if (options?.path === undefined) {
147
- ensureWorkspaceDir(rootDir);
168
+ ensureTrailsWorkspace(rootDir);
148
169
  } else {
149
170
  ensureDbParentDir(dbPath);
150
171
  }
@@ -1,56 +1,174 @@
1
- import type { ErrorCategory, TrailsError } from './errors.js';
2
- import { exitCodeMap, jsonRpcCodeMap, statusCodeMap } from './errors.js';
1
+ import type {
2
+ ErrorCategory,
3
+ ErrorCategoryCodes,
4
+ ErrorClassRegistryEntry,
5
+ FixedErrorClassRegistryEntry,
6
+ TrailsError,
7
+ } from './errors.js';
8
+ import {
9
+ codesByCategory,
10
+ errorClasses,
11
+ exitCodeMap,
12
+ isTrailsError,
13
+ jsonRpcCodeMap,
14
+ statusCodeMap,
15
+ } from './errors.js';
16
+ import {
17
+ INTERNAL_ERROR_PUBLIC_MESSAGE,
18
+ redactErrorString,
19
+ } from './error-projection.js';
3
20
 
4
- export const transportNames = ['cli', 'http', 'mcp'] as const;
21
+ export const surfaceNames = ['cli', 'http', 'jsonRpc', 'mcp'] as const;
5
22
 
6
- export type TransportName = (typeof transportNames)[number];
23
+ export type SurfaceName = (typeof surfaceNames)[number];
7
24
 
8
- export type TransportErrorMapper<T> = (error: TrailsError) => T;
25
+ const surfaceCodeKeys = {
26
+ cli: 'exit',
27
+ http: 'http',
28
+ jsonRpc: 'jsonRpc',
29
+ mcp: 'jsonRpc',
30
+ } as const satisfies Record<SurfaceName, keyof ErrorCategoryCodes>;
9
31
 
10
- export type TransportErrorMappings<T> = Record<ErrorCategory, T>;
32
+ export type SurfaceErrorMapper<T> = (error: TrailsError) => T;
33
+
34
+ export type SurfaceErrorMappings<T> = Record<ErrorCategory, T>;
11
35
 
12
36
  /**
13
- * Union of every transport-specific error code emitted by {@link transportErrorMap}.
37
+ * Union of every surface-specific error code emitted by {@link surfaceErrorMap}.
14
38
  *
15
39
  * @remarks
16
- * Previously parameterised by transport (`TransportErrorCode<'cli'>` etc.), but
40
+ * Previously parameterized by surface (`SurfaceErrorCode<'cli'>` etc.), but
17
41
  * the generic collapsed to `number` because the underlying maps were typed as
18
42
  * `Record<ErrorCategory, number>`. With `as const satisfies` on the maps the
19
- * per-transport literals are now observable, but TypeScript cannot narrow
20
- * `transportErrorMap[transport][error.category]` through a generic
21
- * `TTransport` without an unsound cast. The non-generic union honestly reflects
22
- * what `mapTransportError` returns at the call site.
43
+ * per-surface literals are now observable, but TypeScript cannot narrow
44
+ * `surfaceErrorMap[surface][error.category]` through a generic `TSurface`
45
+ * without an unsound cast. The non-generic union honestly reflects what
46
+ * `mapSurfaceError` returns at the call site.
23
47
  */
24
- export type TransportErrorCode =
25
- (typeof transportErrorMap)[TransportName][ErrorCategory];
48
+ export type SurfaceErrorCode =
49
+ (typeof codesByCategory)[ErrorCategory][(typeof surfaceCodeKeys)[SurfaceName]];
50
+
51
+ export interface SurfaceErrorProjection {
52
+ readonly category: ErrorCategory;
53
+ readonly code: SurfaceErrorCode;
54
+ readonly message: string;
55
+ readonly name: string;
56
+ readonly retryable: boolean;
57
+ readonly surface: SurfaceName;
58
+ }
26
59
 
27
- export const createTransportErrorMapper =
28
- <T>(mappings: TransportErrorMappings<T>): TransportErrorMapper<T> =>
60
+ export interface ErrorClassSurfaceProjection {
61
+ readonly category: ErrorCategory;
62
+ readonly code: SurfaceErrorCode;
63
+ readonly name: string;
64
+ readonly retryable: boolean;
65
+ readonly surface: SurfaceName;
66
+ }
67
+
68
+ export const createSurfaceErrorMapper =
69
+ <T>(mappings: SurfaceErrorMappings<T>): SurfaceErrorMapper<T> =>
29
70
  (error) =>
30
71
  mappings[error.category];
31
72
 
32
- export const transportErrorMap = {
73
+ export const surfaceErrorMap = {
33
74
  cli: exitCodeMap,
34
75
  http: statusCodeMap,
76
+ jsonRpc: jsonRpcCodeMap,
35
77
  mcp: jsonRpcCodeMap,
36
- } as const satisfies Record<TransportName, TransportErrorMappings<number>>;
78
+ } as const satisfies Record<SurfaceName, SurfaceErrorMappings<number>>;
37
79
 
38
- export const transportErrorRegistry = {
80
+ export const surfaceErrorRegistry = {
39
81
  cli: {
40
- map: createTransportErrorMapper(transportErrorMap.cli),
41
- values: transportErrorMap.cli,
82
+ map: createSurfaceErrorMapper(surfaceErrorMap.cli),
83
+ values: surfaceErrorMap.cli,
42
84
  },
43
85
  http: {
44
- map: createTransportErrorMapper(transportErrorMap.http),
45
- values: transportErrorMap.http,
86
+ map: createSurfaceErrorMapper(surfaceErrorMap.http),
87
+ values: surfaceErrorMap.http,
88
+ },
89
+ jsonRpc: {
90
+ map: createSurfaceErrorMapper(surfaceErrorMap.jsonRpc),
91
+ values: surfaceErrorMap.jsonRpc,
46
92
  },
47
93
  mcp: {
48
- map: createTransportErrorMapper(transportErrorMap.mcp),
49
- values: transportErrorMap.mcp,
94
+ map: createSurfaceErrorMapper(surfaceErrorMap.mcp),
95
+ values: surfaceErrorMap.mcp,
50
96
  },
51
97
  } as const;
52
98
 
53
- export const mapTransportError = (
54
- transport: TransportName,
99
+ export const mapSurfaceError = (
100
+ surface: SurfaceName,
55
101
  error: TrailsError
56
- ): TransportErrorCode => transportErrorMap[transport][error.category];
102
+ ): SurfaceErrorCode =>
103
+ codesByCategory[error.category][surfaceCodeKeys[surface]];
104
+
105
+ export const projectSurfaceError = (
106
+ surface: SurfaceName,
107
+ error: TrailsError
108
+ ): SurfaceErrorProjection => ({
109
+ category: error.category,
110
+ code: mapSurfaceError(surface, error),
111
+ message: error.message,
112
+ name: error.name,
113
+ retryable: error.retryable,
114
+ surface,
115
+ });
116
+
117
+ export const projectPublicSurfaceError = (
118
+ surface: SurfaceName,
119
+ error: Error
120
+ ): SurfaceErrorProjection => {
121
+ if (isTrailsError(error)) {
122
+ const projection = projectSurfaceError(surface, error);
123
+ return {
124
+ ...projection,
125
+ message:
126
+ projection.category === 'internal'
127
+ ? INTERNAL_ERROR_PUBLIC_MESSAGE
128
+ : redactErrorString(projection.message),
129
+ };
130
+ }
131
+
132
+ return {
133
+ category: 'internal',
134
+ code: codesByCategory.internal[surfaceCodeKeys[surface]],
135
+ message: INTERNAL_ERROR_PUBLIC_MESSAGE,
136
+ name: 'InternalError',
137
+ retryable: false,
138
+ surface,
139
+ };
140
+ };
141
+
142
+ const isFixedErrorClassEntry = (
143
+ entry: ErrorClassRegistryEntry
144
+ ): entry is FixedErrorClassRegistryEntry => entry.category !== 'dynamic';
145
+
146
+ const fixedErrorClassByName: ReadonlyMap<string, FixedErrorClassRegistryEntry> =
147
+ new Map(
148
+ errorClasses.flatMap((entry): [string, FixedErrorClassRegistryEntry][] =>
149
+ isFixedErrorClassEntry(entry) ? [[entry.name, entry]] : []
150
+ )
151
+ );
152
+
153
+ /**
154
+ * Project a known error class name onto a surface without constructing it.
155
+ *
156
+ * Dynamic-category errors such as `RetryExhaustedError` return `undefined`
157
+ * because their surface code depends on the wrapped runtime error.
158
+ */
159
+ export const projectErrorClassSurface = (
160
+ surface: SurfaceName,
161
+ errorName: string
162
+ ): ErrorClassSurfaceProjection | undefined => {
163
+ const entry = fixedErrorClassByName.get(errorName);
164
+ if (entry === undefined) {
165
+ return undefined;
166
+ }
167
+ return {
168
+ category: entry.category,
169
+ code: codesByCategory[entry.category][surfaceCodeKeys[surface]],
170
+ name: entry.name,
171
+ retryable: entry.retryable,
172
+ surface,
173
+ };
174
+ };
package/src/types.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import type { TrailsError } from './errors.js';
2
+ import type { BasePermit } from './permits.js';
2
3
  import type { Result } from './result.js';
3
4
  import type { Signal } from './signal.js';
4
5
  import type { AnyTrail } from './trail.js';
5
6
  import type { CrossInput, TrailOutput } from './type-utils.js';
7
+ import type { ActivationProvenance } from './activation-provenance.js';
6
8
 
7
9
  // ---------------------------------------------------------------------------
8
10
  // Detour
@@ -102,28 +104,17 @@ export interface CrossFn {
102
104
  * Emit a signal — used for signal-driven activation.
103
105
  *
104
106
  * Fan-out to consumer trails (those with the signal in their `on:` array) is
105
- * the framework's responsibility. Producers get `Result.ok(undefined)` unless
106
- * the signal id is unknown or the payload fails schema validation. Consumer
107
- * errors are logged but do not propagate back to the producer. Consumers fan
108
- * out in parallel, each with its own derived context. Runtime cycle
109
- * suppression is still signal-id-based against the current fire stack: it
110
- * prevents re-entrant loops but can over-suppress legitimate diamond
111
- * re-fires, with a debug breadcrumb and a warn emitted when suppression happens.
112
- *
113
- * Two call shapes are supported:
114
- *
115
- * - **By id** (base shape): `ctx.fire('order.placed', { ... })`. Matches
116
- * the shape of `ctx.cross`; payload is typed as `unknown` and validated
117
- * against the signal's schema at the fire boundary.
118
- * - **By signal value** (progressive disclosure): `ctx.fire(orderPlaced, payload)`
119
- * where `orderPlaced` is a `Signal<T>`. The compiler enforces that
120
- * `payload` matches the signal's declared schema type at the call site,
121
- * on top of the runtime validation.
107
+ * the framework's responsibility. Producers call with a `Signal<T>` value and
108
+ * get best-effort `Promise<void>` semantics: payload validation, missing topo
109
+ * entries, guard suppression, and consumer failures are observable through
110
+ * diagnostics/logging but do not become producer-facing `Result` plumbing.
111
+ * Consumers fan out in parallel, each with its own derived context. Runtime
112
+ * cycle suppression is still signal-id-based against the current fire stack:
113
+ * it prevents re-entrant loops but can over-suppress legitimate diamond
114
+ * re-fires, with a debug breadcrumb and a warn emitted when suppression
115
+ * happens.
122
116
  */
123
- export interface FireFn {
124
- <T>(signal: Signal<T>, payload: T): Promise<Result<void, Error>>;
125
- (signalId: string, payload: unknown): Promise<Result<void, Error>>;
126
- }
117
+ export type FireFn = <T>(signal: Signal<T>, payload: T) => Promise<void>;
127
118
 
128
119
  /** Resolve a resource instance from the current trail context. */
129
120
  export type ResourceLookup = <T = unknown>(
@@ -167,22 +158,64 @@ export interface Logger {
167
158
  child(context: Record<string, unknown>): Logger;
168
159
  }
169
160
 
170
- /** Context extension key for the invoking trailhead name. */
171
- export const TRAILHEAD_KEY = '__trails_trailhead' as const;
161
+ export type LogLevel =
162
+ | 'debug'
163
+ | 'error'
164
+ | 'fatal'
165
+ | 'info'
166
+ | 'silent'
167
+ | 'trace'
168
+ | 'warn';
169
+
170
+ export interface LogRecord {
171
+ readonly category: string;
172
+ readonly level: LogLevel;
173
+ readonly message: string;
174
+ readonly metadata: Record<string, unknown>;
175
+ readonly timestamp: Date;
176
+ }
177
+
178
+ export interface LogSink {
179
+ readonly name: string;
180
+ readonly write: (record: LogRecord) => void;
181
+ readonly flush?: (() => Promise<void>) | undefined;
182
+ }
172
183
 
173
- /** Minimal permit shape available on TrailContext. Permits extends this. */
174
- export interface BasePermit {
175
- readonly id: string;
176
- readonly scopes: readonly string[];
184
+ export interface LogFormatter {
185
+ format(record: LogRecord): string;
177
186
  }
178
187
 
188
+ /**
189
+ * Context extension key for the invoking surface name.
190
+ */
191
+ export const SURFACE_KEY = '__trails_surface' as const;
192
+
193
+ /**
194
+ * Context extension key for the layer names attached by the invoking surface.
195
+ */
196
+ export const SURFACE_LAYER_NAMES_KEY = '__trails_surface_layer_names' as const;
197
+
198
+ /**
199
+ * Context extension key carrying per-layer runtime input.
200
+ *
201
+ * Surfaces (CLI, MCP, HTTP) project each typed layer's `input` schema onto
202
+ * their native idioms (flags, tool params, query strings). At execute time
203
+ * the parsed values are partitioned per layer and stored under this key as
204
+ * `Record<layerName, unknown>`. Layers that need runtime input read their
205
+ * own slot via `ctx.extensions?.[LAYER_INPUTS_KEY]?.[layer.name]`.
206
+ *
207
+ * @see TRL-473 for the CLI projection contract.
208
+ */
209
+ export const LAYER_INPUTS_KEY = '__trails_layer_inputs' as const;
210
+
179
211
  /** Runtime context threaded through every trail execution */
180
212
  export interface TrailContext {
213
+ readonly activation?: ActivationProvenance | undefined;
181
214
  readonly requestId: string;
182
215
  readonly abortSignal: AbortSignal;
183
216
  readonly cross?: CrossFn | undefined;
184
217
  /**
185
- * Emit a signal by id. Fans out to every trail with the signal in its
218
+ * Emit a typed signal. Fans out to every trail with the signal in its
186
219
  * `on:` declaration. Bound by the runner that holds the topo (typically
187
220
  * `run()`); undefined when a context is constructed without topo access.
188
221
  */
@@ -195,6 +228,27 @@ export interface TrailContext {
195
228
  readonly env?: Record<string, string | undefined> | undefined;
196
229
  readonly extensions?: Readonly<Record<string, unknown>> | undefined;
197
230
  readonly resource?: ResourceLookup | undefined;
231
+ /**
232
+ * Whether the current invocation is a dry run.
233
+ *
234
+ * Defaults to `false`. Trails that don't read this field are unaffected.
235
+ * Trails that do read it decide what dry-run means for their domain — for
236
+ * example: preview the change without committing, validate inputs without
237
+ * performing side effects, or return what would happen without actually
238
+ * doing it.
239
+ *
240
+ * The framework only carries the flag from the surface (e.g. CLI
241
+ * `--dry-run`) into the context. It never short-circuits trail execution
242
+ * on its own based on this field.
243
+ *
244
+ * Pair this runtime signal with `TrailSpec.dryRun`, which declares whether a
245
+ * trail supports dry-run semantics for governance, derivation, and surface
246
+ * tooling.
247
+ *
248
+ * @remarks Always defined on contexts produced by `executeTrail` or
249
+ * `createTrailContext` (normalized to `false` when not provided).
250
+ */
251
+ readonly dryRun?: boolean | undefined;
198
252
  /**
199
253
  * Wrap a callback in a child trace span.
200
254
  *
@@ -9,7 +9,13 @@ const PROJECTION_BLOCKING_RULES = new Set([
9
9
  'cross-cycle',
10
10
  'cross-exists',
11
11
  'no-self-cross',
12
+ 'activation-source-definition-unique',
13
+ 'activation-source-edge-unique',
14
+ 'activation-source-kind-known',
15
+ 'activation-schedule-valid',
12
16
  'resource-exists',
17
+ 'signal-fire-exists',
18
+ 'signal-on-exists',
13
19
  'signal-origin-exists',
14
20
  ]);
15
21