@ontrails/core 1.0.0-beta.15 → 1.0.0-beta.17

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 +69 -0
  2. package/README.md +64 -19
  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
@@ -0,0 +1,91 @@
1
+ import type { z } from 'zod';
2
+
3
+ import type { AnySignal } from './signal.js';
4
+
5
+ export const activationSourceKinds = Object.freeze([
6
+ 'signal',
7
+ 'schedule',
8
+ 'webhook',
9
+ ] as const);
10
+
11
+ export type BuiltinActivationSourceKind =
12
+ (typeof activationSourceKinds)[number];
13
+
14
+ export type ActivationSourceKind = string;
15
+
16
+ export type ActivationSourceMeta = Readonly<Record<string, unknown>>;
17
+
18
+ export type ActivationSourceParse<TPayload = unknown> =
19
+ | z.ZodType<TPayload>
20
+ | {
21
+ readonly output?: z.ZodType<TPayload> | undefined;
22
+ };
23
+
24
+ export interface ActivationSource {
25
+ readonly id: string;
26
+ readonly kind: ActivationSourceKind;
27
+ readonly cron?: string | undefined;
28
+ readonly input?: unknown;
29
+ readonly meta?: ActivationSourceMeta | undefined;
30
+ readonly method?: string | undefined;
31
+ readonly parse?: ActivationSourceParse | undefined;
32
+ readonly path?: string | undefined;
33
+ readonly payload?: z.ZodType<unknown> | undefined;
34
+ readonly timezone?: string | undefined;
35
+ readonly verify?: unknown;
36
+ }
37
+
38
+ export interface ActivationWhereExample {
39
+ readonly input?: unknown;
40
+ readonly on: boolean;
41
+ readonly payload?: unknown;
42
+ }
43
+
44
+ /* oxlint-disable no-explicit-any -- contextual predicate authoring needs source-specific payload inference; unknown would make inline predicates unusable until a helper API exists. */
45
+ export type ActivationWherePredicate<TPayload = any> = (
46
+ payload: TPayload
47
+ ) => boolean | Promise<boolean>;
48
+
49
+ export interface ActivationWhere<TPayload = any> {
50
+ readonly examples?: readonly ActivationWhereExample[] | undefined;
51
+ readonly predicate: ActivationWherePredicate<TPayload>;
52
+ }
53
+
54
+ export type ActivationWhereSpec<TPayload = any> =
55
+ | ActivationWhere<TPayload>
56
+ | ActivationWherePredicate<TPayload>;
57
+
58
+ export type ActivationSourceRef = string | AnySignal | ActivationSource;
59
+
60
+ export interface ActivationEntrySpec {
61
+ readonly source: ActivationSourceRef;
62
+ readonly meta?: ActivationSourceMeta | undefined;
63
+ readonly where?: ActivationWhereSpec | undefined;
64
+ }
65
+
66
+ export interface ActivationEntry {
67
+ readonly source: ActivationSource;
68
+ readonly meta?: ActivationSourceMeta | undefined;
69
+ readonly where?: ActivationWhereSpec | undefined;
70
+ }
71
+
72
+ export const getActivationWherePredicate = (
73
+ where: ActivationWhereSpec | undefined
74
+ ): ActivationWherePredicate | undefined =>
75
+ typeof where === 'function' ? where : where?.predicate;
76
+
77
+ export const isKnownActivationSourceKind = (
78
+ kind: string
79
+ ): kind is BuiltinActivationSourceKind =>
80
+ (activationSourceKinds as readonly string[]).includes(kind);
81
+
82
+ export const isActivationEntrySpec = (
83
+ value: unknown
84
+ ): value is ActivationEntrySpec =>
85
+ typeof value === 'object' && value !== null && 'source' in value;
86
+
87
+ export const isActivationSource = (value: unknown): value is ActivationSource =>
88
+ typeof value === 'object' &&
89
+ value !== null &&
90
+ 'id' in value &&
91
+ 'kind' in value;
package/src/blob-ref.ts CHANGED
@@ -2,6 +2,11 @@
2
2
  * BlobRef — a frozen reference to binary data for @ontrails/core.
3
3
  */
4
4
 
5
+ import { z } from 'zod';
6
+
7
+ /** Metadata key used to recognize BlobRef schemas during JSON Schema projection. */
8
+ export const BLOB_REF_SCHEMA_META_KEY = 'ontrails/blob-ref';
9
+
5
10
  /** Immutable reference to a blob of binary data. */
6
11
  export interface BlobRef {
7
12
  readonly name: string;
@@ -10,6 +15,37 @@ export interface BlobRef {
10
15
  readonly data: Uint8Array | ReadableStream<Uint8Array>;
11
16
  }
12
17
 
18
+ /** Schema-projected metadata for a BlobRef value. */
19
+ export interface BlobRefDescriptor {
20
+ readonly kind: 'blob';
21
+ readonly mimeType: string;
22
+ readonly name: string;
23
+ readonly size: number;
24
+ readonly uri: string;
25
+ }
26
+
27
+ /** Public descriptor schema emitted to transport clients instead of raw bytes. */
28
+ export const blobRefDescriptorSchema = z.object({
29
+ kind: z.literal('blob'),
30
+ mimeType: z.string(),
31
+ name: z.string(),
32
+ size: z.number(),
33
+ uri: z.string(),
34
+ });
35
+
36
+ /** Canonical JSON Schema for BlobRef descriptors across derived surfaces. */
37
+ export const blobRefJsonSchema = Object.freeze({
38
+ properties: Object.freeze({
39
+ kind: Object.freeze({ const: 'blob' }),
40
+ mimeType: Object.freeze({ type: 'string' }),
41
+ name: Object.freeze({ type: 'string' }),
42
+ size: Object.freeze({ type: 'number' }),
43
+ uri: Object.freeze({ type: 'string' }),
44
+ }),
45
+ required: Object.freeze(['kind', 'mimeType', 'name', 'size', 'uri']),
46
+ type: 'object',
47
+ });
48
+
13
49
  /** Creates a frozen BlobRef. */
14
50
  export const createBlobRef = (options: {
15
51
  name: string;
@@ -37,3 +73,18 @@ export const isBlobRef = (value: unknown): value is BlobRef => {
37
73
  (obj['data'] instanceof Uint8Array || obj['data'] instanceof ReadableStream)
38
74
  );
39
75
  };
76
+
77
+ /** Zod schema for runtime BlobRef values with metadata for descriptor projection. */
78
+ export const blobRefSchema = z
79
+ .custom<BlobRef>(isBlobRef, { error: 'Expected BlobRef' })
80
+ .meta({ [BLOB_REF_SCHEMA_META_KEY]: true });
81
+
82
+ /** Convert a runtime BlobRef into its schema-aware transport descriptor. */
83
+ export const toBlobRefDescriptor = (blob: BlobRef): BlobRefDescriptor =>
84
+ Object.freeze({
85
+ kind: 'blob',
86
+ mimeType: blob.mimeType,
87
+ name: blob.name,
88
+ size: blob.size,
89
+ uri: `blob://${blob.name}`,
90
+ });
package/src/context.ts CHANGED
@@ -34,6 +34,7 @@ export const createTrailContext = (
34
34
  const ctx = {
35
35
  abortSignal: new AbortController().signal,
36
36
  cwd: process.cwd(),
37
+ dryRun: false,
37
38
  env: process.env as Record<string, string | undefined>,
38
39
  requestId: Bun.randomUUIDv7(),
39
40
  trace: passthroughTrace,
@@ -44,5 +45,8 @@ export const createTrailContext = (
44
45
  if (ctx.trace === undefined) {
45
46
  ctx.trace = passthroughTrace;
46
47
  }
48
+ if (ctx.dryRun === undefined) {
49
+ ctx.dryRun = false;
50
+ }
47
51
  return ctx;
48
52
  };
@@ -7,13 +7,11 @@
7
7
  * runner in `@ontrails/testing`. Extracting them here keeps the validation
8
8
  * rule, error message, and worker-pool semantics authored in one place so
9
9
  * the two call sites cannot drift.
10
- *
11
- * @internal
12
10
  */
13
11
 
14
- import { ValidationError } from '../errors.js';
15
- import { Result } from '../result.js';
16
- import type { CrossBatchOptions } from '../types.js';
12
+ import { ValidationError } from './errors.js';
13
+ import { Result } from './result.js';
14
+ import type { CrossBatchOptions } from './types.js';
17
15
 
18
16
  /**
19
17
  * Validate the `concurrency` option on a batch `ctx.cross()` call.
@@ -22,8 +20,6 @@ import type { CrossBatchOptions } from '../types.js';
22
20
  * positive integer is supplied, and `Err(ValidationError)` for any other
23
21
  * value. The error message is load-bearing: callers and tests depend on
24
22
  * the exact string.
25
- *
26
- * @internal
27
23
  */
28
24
  export const normalizeCrossBatchConcurrency = (
29
25
  options: CrossBatchOptions | undefined
@@ -48,8 +44,6 @@ export const normalizeCrossBatchConcurrency = (
48
44
  * Produce one validation-error result per call, preserving the original
49
45
  * call order. Used when `normalizeCrossBatchConcurrency` fails so the caller
50
46
  * can surface a uniform batch shape to the trail implementation.
51
- *
52
- * @internal
53
47
  */
54
48
  export const createCrossBatchValidationResults = <TCall>(
55
49
  calls: readonly TCall[],
@@ -60,8 +54,6 @@ export const createCrossBatchValidationResults = <TCall>(
60
54
  * Claim the next branch index from a shared counter. Safe to call from
61
55
  * multiple worker coroutines because JavaScript is single-threaded between
62
56
  * awaits — the read/increment pair runs without interleaving.
63
- *
64
- * @internal
65
57
  */
66
58
  export const claimNextCrossBatchIndex = <TCall>(
67
59
  nextIndex: { value: number },
package/src/detours.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Hard upper bound for detour recovery attempts.
3
+ *
4
+ * Execution and derived surface/topo projections both clamp declared detour
5
+ * attempts to this value so runtime behavior and inspectable contracts stay in
6
+ * lockstep.
7
+ */
8
+ export const DETOUR_MAX_ATTEMPTS_CAP = 5;
package/src/draft.ts CHANGED
@@ -15,6 +15,8 @@ export type DraftDependencyKind =
15
15
  | 'resource'
16
16
  | 'replaced-by'
17
17
  | 'schema-reference'
18
+ | 'signal-fire'
19
+ | 'signal-on'
18
20
  | 'signal-from';
19
21
 
20
22
  export interface DraftDependency {
@@ -92,6 +94,8 @@ const trailDependencies = (trail: AnyTrail): DraftDependency[] => [
92
94
  trail.resources.map(({ id }) => id),
93
95
  'resource'
94
96
  ),
97
+ ...dependenciesFromIds(trail.id, trail.fires ?? [], 'signal-fire'),
98
+ ...dependenciesFromIds(trail.id, trail.on ?? [], 'signal-on'),
95
99
  ...dependencyFromTarget(trail.id, replacedByTarget(trail), 'replaced-by'),
96
100
  ];
97
101
 
@@ -0,0 +1,51 @@
1
+ import type { ErrorCategory } from './errors.js';
2
+ import { isTrailsError } from './errors.js';
3
+ import { createRedactor } from './redaction/index.js';
4
+
5
+ const errorRedactor = createRedactor();
6
+
7
+ export const INTERNAL_ERROR_PUBLIC_MESSAGE = 'Internal server error';
8
+
9
+ export interface ErrorDiagnosticsProjection {
10
+ readonly category?: ErrorCategory | undefined;
11
+ readonly context?: Record<string, unknown> | undefined;
12
+ readonly message: string;
13
+ readonly name: string;
14
+ readonly retryable?: boolean | undefined;
15
+ readonly stack?: string | undefined;
16
+ }
17
+
18
+ export const redactErrorString = (value: string): string =>
19
+ errorRedactor.redact(value);
20
+
21
+ export const redactErrorContext = (
22
+ context: Record<string, unknown> | undefined
23
+ ): Record<string, unknown> | undefined =>
24
+ context === undefined ? undefined : errorRedactor.redactObject(context);
25
+
26
+ export const redactErrorStack = (
27
+ stack: string | undefined
28
+ ): string | undefined =>
29
+ stack === undefined ? undefined : redactErrorString(stack);
30
+
31
+ export const projectErrorDiagnostics = (
32
+ error: Error
33
+ ): ErrorDiagnosticsProjection => {
34
+ const context = isTrailsError(error)
35
+ ? redactErrorContext(error.context)
36
+ : undefined;
37
+ const stack = redactErrorStack(error.stack);
38
+
39
+ return {
40
+ ...(isTrailsError(error)
41
+ ? {
42
+ category: error.category,
43
+ retryable: error.retryable,
44
+ }
45
+ : {}),
46
+ ...(context === undefined ? {} : { context }),
47
+ message: redactErrorString(error.message),
48
+ name: error.name || error.constructor.name || 'Error',
49
+ ...(stack === undefined ? {} : { stack }),
50
+ };
51
+ };
package/src/errors.ts CHANGED
@@ -83,6 +83,16 @@ export class PermissionError extends TrailsError {
83
83
  readonly retryable = false as const;
84
84
  }
85
85
 
86
+ export class PermitError extends PermissionError {
87
+ constructor(
88
+ message: string,
89
+ options?: { cause?: Error; context?: Record<string, unknown> }
90
+ ) {
91
+ super(message, options);
92
+ this.name = 'PermitError';
93
+ }
94
+ }
95
+
86
96
  export class TimeoutError extends TrailsError {
87
97
  readonly category = 'timeout' as const;
88
98
  readonly retryable = true as const;
@@ -121,6 +131,8 @@ export class DerivationError extends TrailsError {
121
131
  readonly retryable = false as const;
122
132
  }
123
133
 
134
+ export class RecoverableCompletionError extends InternalError {}
135
+
124
136
  export class AuthError extends TrailsError {
125
137
  readonly category = 'auth' as const;
126
138
  readonly retryable = false as const;
@@ -134,7 +146,7 @@ export class CancelledError extends TrailsError {
134
146
  /**
135
147
  * Returned when a detour exhausts all recovery attempts.
136
148
  *
137
- * Inherits the wrapped error's category for trailhead mapping (e.g. a
149
+ * Inherits the wrapped error's category for surface mapping (e.g. a
138
150
  * `RetryExhaustedError<ConflictError>` maps to HTTP 409), but always
139
151
  * sets `retryable = false` to prevent amplification across `ctx.cross()`
140
152
  * boundaries or stacked layers.
@@ -167,48 +179,189 @@ export class RetryExhaustedError<
167
179
  }
168
180
  }
169
181
 
182
+ // ---------------------------------------------------------------------------
183
+ // Class registry
184
+ // ---------------------------------------------------------------------------
185
+
186
+ export type ErrorClassConstructor = new (...args: never[]) => TrailsError;
187
+
188
+ export interface FixedErrorClassRegistryEntry {
189
+ readonly category: ErrorCategory;
190
+ readonly ctor: ErrorClassConstructor;
191
+ readonly name: string;
192
+ readonly retryable: boolean;
193
+ }
194
+
195
+ export interface DynamicErrorClassRegistryEntry {
196
+ readonly category: 'dynamic';
197
+ readonly ctor: ErrorClassConstructor;
198
+ readonly inheritsCategoryFrom: 'wrapped-error';
199
+ readonly name: string;
200
+ readonly retryable: false;
201
+ }
202
+
203
+ export type ErrorClassRegistryEntry =
204
+ | DynamicErrorClassRegistryEntry
205
+ | FixedErrorClassRegistryEntry;
206
+
207
+ /**
208
+ * Authored registry of concrete TrailsError classes.
209
+ *
210
+ * JavaScript cannot enumerate subclasses at runtime, so rule and projection
211
+ * tooling should walk this owner-held list instead of hardcoding parallel
212
+ * class-name tables. `RetryExhaustedError` is marked dynamic because it
213
+ * inherits its runtime category from the wrapped error rather than always
214
+ * mapping as `internal`.
215
+ */
216
+ export const errorClasses = [
217
+ {
218
+ category: 'validation',
219
+ ctor: ValidationError,
220
+ name: 'ValidationError',
221
+ retryable: false,
222
+ },
223
+ {
224
+ category: 'validation',
225
+ ctor: AmbiguousError,
226
+ name: 'AmbiguousError',
227
+ retryable: false,
228
+ },
229
+ {
230
+ category: 'internal',
231
+ ctor: AssertionError,
232
+ name: 'AssertionError',
233
+ retryable: false,
234
+ },
235
+ {
236
+ category: 'not_found',
237
+ ctor: NotFoundError,
238
+ name: 'NotFoundError',
239
+ retryable: false,
240
+ },
241
+ {
242
+ category: 'conflict',
243
+ ctor: AlreadyExistsError,
244
+ name: 'AlreadyExistsError',
245
+ retryable: false,
246
+ },
247
+ {
248
+ category: 'conflict',
249
+ ctor: ConflictError,
250
+ name: 'ConflictError',
251
+ retryable: false,
252
+ },
253
+ {
254
+ category: 'permission',
255
+ ctor: PermissionError,
256
+ name: 'PermissionError',
257
+ retryable: false,
258
+ },
259
+ {
260
+ category: 'permission',
261
+ ctor: PermitError,
262
+ name: 'PermitError',
263
+ retryable: false,
264
+ },
265
+ {
266
+ category: 'timeout',
267
+ ctor: TimeoutError,
268
+ name: 'TimeoutError',
269
+ retryable: true,
270
+ },
271
+ {
272
+ category: 'rate_limit',
273
+ ctor: RateLimitError,
274
+ name: 'RateLimitError',
275
+ retryable: true,
276
+ },
277
+ {
278
+ category: 'network',
279
+ ctor: NetworkError,
280
+ name: 'NetworkError',
281
+ retryable: true,
282
+ },
283
+ {
284
+ category: 'internal',
285
+ ctor: InternalError,
286
+ name: 'InternalError',
287
+ retryable: false,
288
+ },
289
+ {
290
+ category: 'internal',
291
+ ctor: DerivationError,
292
+ name: 'DerivationError',
293
+ retryable: false,
294
+ },
295
+ {
296
+ category: 'internal',
297
+ ctor: RecoverableCompletionError,
298
+ name: 'RecoverableCompletionError',
299
+ retryable: false,
300
+ },
301
+ { category: 'auth', ctor: AuthError, name: 'AuthError', retryable: false },
302
+ {
303
+ category: 'cancelled',
304
+ ctor: CancelledError,
305
+ name: 'CancelledError',
306
+ retryable: false,
307
+ },
308
+ {
309
+ category: 'dynamic',
310
+ ctor: RetryExhaustedError,
311
+ inheritsCategoryFrom: 'wrapped-error',
312
+ name: 'RetryExhaustedError',
313
+ retryable: false,
314
+ },
315
+ ] as const satisfies readonly ErrorClassRegistryEntry[];
316
+
170
317
  // ---------------------------------------------------------------------------
171
318
  // Taxonomy maps
172
319
  // ---------------------------------------------------------------------------
173
320
 
174
- export const exitCodeMap = {
175
- auth: 9,
176
- cancelled: 130,
177
- conflict: 3,
178
- internal: 8,
179
- network: 7,
180
- not_found: 2,
181
- permission: 4,
182
- rate_limit: 6,
183
- timeout: 5,
184
- validation: 1,
185
- } as const satisfies Record<ErrorCategory, number>;
186
-
187
- export const statusCodeMap = {
188
- auth: 401,
189
- cancelled: 499,
190
- conflict: 409,
191
- internal: 500,
192
- network: 502,
193
- not_found: 404,
194
- permission: 403,
195
- rate_limit: 429,
196
- timeout: 504,
197
- validation: 400,
198
- } as const satisfies Record<ErrorCategory, number>;
199
-
200
- export const jsonRpcCodeMap = {
201
- auth: -32_600,
202
- cancelled: -32_603,
203
- conflict: -32_603,
204
- internal: -32_603,
205
- network: -32_603,
206
- not_found: -32_601,
207
- permission: -32_600,
208
- rate_limit: -32_603,
209
- timeout: -32_603,
210
- validation: -32_602,
211
- } as const satisfies Record<ErrorCategory, number>;
321
+ export interface ErrorCategoryCodes {
322
+ readonly exit: number;
323
+ readonly http: number;
324
+ readonly jsonRpc: number;
325
+ }
326
+
327
+ export const codesByCategory = {
328
+ auth: { exit: 9, http: 401, jsonRpc: -32_600 },
329
+ cancelled: { exit: 130, http: 499, jsonRpc: -32_603 },
330
+ conflict: { exit: 3, http: 409, jsonRpc: -32_603 },
331
+ internal: { exit: 8, http: 500, jsonRpc: -32_603 },
332
+ network: { exit: 7, http: 502, jsonRpc: -32_603 },
333
+ not_found: { exit: 2, http: 404, jsonRpc: -32_601 },
334
+ permission: { exit: 4, http: 403, jsonRpc: -32_600 },
335
+ rate_limit: { exit: 6, http: 429, jsonRpc: -32_603 },
336
+ timeout: { exit: 5, http: 504, jsonRpc: -32_603 },
337
+ validation: { exit: 1, http: 400, jsonRpc: -32_602 },
338
+ } as const satisfies Record<ErrorCategory, ErrorCategoryCodes>;
339
+
340
+ const deriveCodeMap = <TCode extends keyof ErrorCategoryCodes>(
341
+ code: TCode
342
+ ): {
343
+ readonly [TCategory in ErrorCategory]: (typeof codesByCategory)[TCategory][TCode];
344
+ } => ({
345
+ auth: codesByCategory.auth[code],
346
+ cancelled: codesByCategory.cancelled[code],
347
+ conflict: codesByCategory.conflict[code],
348
+ internal: codesByCategory.internal[code],
349
+ network: codesByCategory.network[code],
350
+ not_found: codesByCategory.not_found[code],
351
+ permission: codesByCategory.permission[code],
352
+ rate_limit: codesByCategory.rate_limit[code],
353
+ timeout: codesByCategory.timeout[code],
354
+ validation: codesByCategory.validation[code],
355
+ });
356
+
357
+ /** @deprecated Prefer `codesByCategory[category].exit`. */
358
+ export const exitCodeMap = deriveCodeMap('exit');
359
+
360
+ /** @deprecated Prefer `codesByCategory[category].http`. */
361
+ export const statusCodeMap = deriveCodeMap('http');
362
+
363
+ /** @deprecated Prefer `codesByCategory[category].jsonRpc`. */
364
+ export const jsonRpcCodeMap = deriveCodeMap('jsonRpc');
212
365
 
213
366
  export const retryableMap: Record<ErrorCategory, boolean> = {
214
367
  auth: false,