@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
@@ -1,224 +0,0 @@
1
- /* oxlint-disable require-await -- trail implementations satisfy async interface without awaiting */
2
- import { describe, expect, test } from 'bun:test';
3
-
4
- import { z } from 'zod';
5
-
6
- import { executeTrail } from '../execute.js';
7
- import { Result } from '../result.js';
8
- import { resource } from '../resource.js';
9
- import { trail } from '../trail.js';
10
- import type { ResourceContext } from '../resource.js';
11
-
12
- // ---------------------------------------------------------------------------
13
- // Helpers
14
- // ---------------------------------------------------------------------------
15
-
16
- const nextId = (name: string): string =>
17
- `test.svc-config.${name}.${Bun.randomUUIDv7()}`;
18
-
19
- const createSingletonConfigTrail = (id: string) => {
20
- const captures = { createCalls: 0 };
21
- const svc = resource(id, {
22
- config: z.object({ key: z.string() }),
23
- create: (ctx: ResourceContext<{ key: string }>) => {
24
- captures.createCalls += 1;
25
- return Result.ok({
26
- createCall: captures.createCalls,
27
- key: ctx.config.key,
28
- });
29
- },
30
- });
31
-
32
- return {
33
- captures,
34
- trail: trail('svc-config.singleton', {
35
- blaze: (_input, ctx) =>
36
- Result.ok({
37
- createCall: svc.from(ctx).createCall,
38
- key: svc.from(ctx).key,
39
- }),
40
- input: z.object({}),
41
- output: z.object({ createCall: z.number(), key: z.string() }),
42
- resources: [svc],
43
- }),
44
- };
45
- };
46
-
47
- // ---------------------------------------------------------------------------
48
- // Tests
49
- // ---------------------------------------------------------------------------
50
-
51
- describe('ResourceContext.config', () => {
52
- test('resource with config schema receives validated config in svc.config', async () => {
53
- const id = nextId('typed-config');
54
- let capturedConfig: unknown;
55
-
56
- const db = resource(id, {
57
- config: z.object({ poolSize: z.number(), url: z.string().url() }),
58
- create: (svc: ResourceContext<{ url: string; poolSize: number }>) => {
59
- capturedConfig = svc.config;
60
- return Result.ok({ connected: true });
61
- },
62
- });
63
-
64
- const dbTrail = trail('svc-config.typed', {
65
- blaze: (_input, ctx) => Result.ok({ connected: db.from(ctx).connected }),
66
- input: z.object({}),
67
- output: z.object({ connected: z.boolean() }),
68
- resources: [db],
69
- });
70
-
71
- const result = await executeTrail(
72
- dbTrail,
73
- {},
74
- {
75
- configValues: {
76
- [id]: { poolSize: 5, url: 'https://example.com' },
77
- },
78
- }
79
- );
80
-
81
- expect(result.isOk()).toBe(true);
82
- expect(capturedConfig).toEqual({ poolSize: 5, url: 'https://example.com' });
83
- });
84
-
85
- test('resource without config still works — svc.config is undefined', async () => {
86
- const id = nextId('no-config');
87
- let capturedConfig: unknown = 'sentinel';
88
-
89
- const counter = resource(id, {
90
- create: (svc) => {
91
- capturedConfig = svc.config;
92
- return Result.ok(42);
93
- },
94
- });
95
-
96
- const counterTrail = trail('svc-config.no-config', {
97
- blaze: (_input, ctx) => Result.ok({ value: counter.from(ctx) }),
98
- input: z.object({}),
99
- resources: [counter],
100
- });
101
-
102
- const result = await executeTrail(counterTrail, {});
103
-
104
- expect(result.isOk()).toBe(true);
105
- expect(capturedConfig).toBeUndefined();
106
- });
107
-
108
- test('config validation failure returns Result.err at resource creation time', async () => {
109
- const id = nextId('invalid-config');
110
-
111
- const db = resource(id, {
112
- config: z.object({ url: z.string().url() }),
113
- create: () => Result.ok({ connected: true }),
114
- });
115
-
116
- const dbTrail = trail('svc-config.invalid', {
117
- blaze: () => Result.ok(null),
118
- input: z.object({}),
119
- resources: [db],
120
- });
121
-
122
- const result = await executeTrail(
123
- dbTrail,
124
- {},
125
- {
126
- configValues: {
127
- [id]: { url: 'not-a-url' },
128
- },
129
- }
130
- );
131
-
132
- expect(result.isErr()).toBe(true);
133
- expect(result.error.message).toContain(id);
134
- });
135
-
136
- test('missing configValues for a resource with config schema returns Result.err', async () => {
137
- const id = nextId('missing-config');
138
-
139
- const db = resource(id, {
140
- config: z.object({ url: z.string().url() }),
141
- create: () => Result.ok({ connected: true }),
142
- });
143
-
144
- const dbTrail = trail('svc-config.missing', {
145
- blaze: () => Result.ok(null),
146
- input: z.object({}),
147
- resources: [db],
148
- });
149
-
150
- const result = await executeTrail(dbTrail, {});
151
-
152
- expect(result.isErr()).toBe(true);
153
- expect(result.error.message).toContain(id);
154
- });
155
-
156
- test('resource override bypasses config validation', async () => {
157
- const id = nextId('override-bypass');
158
-
159
- const db = resource(id, {
160
- config: z.object({ url: z.string().url() }),
161
- create: () => Result.ok({ connected: true }),
162
- });
163
-
164
- const dbTrail = trail('svc-config.override', {
165
- blaze: (_input, ctx) => Result.ok({ value: db.from(ctx) as number }),
166
- input: z.object({}),
167
- output: z.object({ value: z.number() }),
168
- resources: [db],
169
- });
170
-
171
- // Provide the resource via overrides without any configValues — should NOT fail
172
- const result = await executeTrail(dbTrail, {}, { resources: { [id]: 42 } });
173
-
174
- expect(result.isOk()).toBe(true);
175
- expect(result.unwrap()).toEqual({ value: 42 });
176
- });
177
- test('config values passed through ExecuteTrailOptions.configValues', async () => {
178
- const id = nextId('options-config');
179
- const captures: unknown[] = [];
180
-
181
- const svc = resource(id, {
182
- config: z.object({ key: z.string() }),
183
- create: (ctx: ResourceContext<{ key: string }>) => {
184
- captures.push(ctx.config);
185
- return Result.ok({ key: ctx.config.key });
186
- },
187
- });
188
-
189
- const svcTrail = trail('svc-config.options', {
190
- blaze: (_input, ctx) => Result.ok({ key: svc.from(ctx).key }),
191
- input: z.object({}),
192
- output: z.object({ key: z.string() }),
193
- resources: [svc],
194
- });
195
-
196
- const result = await executeTrail(
197
- svcTrail,
198
- {},
199
- {
200
- configValues: { [id]: { key: 'hello' } },
201
- }
202
- );
203
-
204
- expect(result.isOk()).toBe(true);
205
- expect(result.unwrap()).toEqual({ key: 'hello' });
206
- expect(captures).toEqual([{ key: 'hello' }]);
207
- });
208
-
209
- test('config-aware singleton resources reuse the cached instance', async () => {
210
- const id = nextId('singleton-config');
211
- const { captures, trail: singletonTrail } = createSingletonConfigTrail(id);
212
- const options = {
213
- configValues: { [id]: { key: 'hello' } },
214
- };
215
- const first = await executeTrail(singletonTrail, {}, options);
216
- const second = await executeTrail(singletonTrail, {}, options);
217
-
218
- expect(first.isOk()).toBe(true);
219
- expect(second.isOk()).toBe(true);
220
- expect(first.unwrap()).toEqual({ createCall: 1, key: 'hello' });
221
- expect(second.unwrap()).toEqual({ createCall: 1, key: 'hello' });
222
- expect(captures.createCalls).toBe(1);
223
- });
224
- });
@@ -1,82 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
-
3
- import { z } from 'zod';
4
-
5
- import { signal } from '../signal.js';
6
-
7
- const payloadSchema = z.object({
8
- action: z.string(),
9
- userId: z.string(),
10
- });
11
-
12
- const userAction = signal('user.action', {
13
- description: 'A user performed an action',
14
- from: ['auth.login', 'auth.signup'],
15
- meta: { domain: 'auth', priority: 1 },
16
- payload: payloadSchema,
17
- });
18
-
19
- describe('signal() basics', () => {
20
- test("returns kind 'signal'", () => {
21
- expect(userAction.kind).toBe('signal');
22
- });
23
-
24
- test('returns correct id', () => {
25
- expect(userAction.id).toBe('user.action');
26
- });
27
-
28
- test('preserves payload schema', () => {
29
- const parsed = userAction.payload.safeParse({
30
- action: 'click',
31
- userId: 'u-1',
32
- });
33
- expect(parsed.success).toBe(true);
34
-
35
- const bad = userAction.payload.safeParse({ userId: 42 });
36
- expect(bad.success).toBe(false);
37
- });
38
-
39
- test('preserves description', () => {
40
- expect(userAction.description).toBe('A user performed an action');
41
- });
42
-
43
- test('result object is frozen', () => {
44
- expect(Object.isFrozen(userAction)).toBe(true);
45
- });
46
- });
47
-
48
- describe('signal() from and meta', () => {
49
- test('preserves from', () => {
50
- expect(userAction.from).toEqual(['auth.login', 'auth.signup']);
51
- });
52
-
53
- test('from array is frozen', () => {
54
- expect(Object.isFrozen(userAction.from)).toBe(true);
55
- });
56
-
57
- test('preserves meta', () => {
58
- expect(userAction.meta).toEqual({ domain: 'auth', priority: 1 });
59
- });
60
-
61
- test('optional fields default to undefined', () => {
62
- const minimal = signal('minimal', {
63
- payload: z.string(),
64
- });
65
- expect(minimal.description).toBeUndefined();
66
- expect(minimal.from).toBeUndefined();
67
- expect(minimal.meta).toBeUndefined();
68
- });
69
- });
70
-
71
- describe('signal() single-object overload', () => {
72
- test('accepts spec with id property', () => {
73
- const e = signal({
74
- from: ['entity.add', 'entity.delete'],
75
- id: 'entity.updated',
76
- payload: z.object({ entityId: z.string() }),
77
- });
78
- expect(e.id).toBe('entity.updated');
79
- expect(e.kind).toBe('signal');
80
- expect(e.from).toEqual(['entity.add', 'entity.delete']);
81
- });
82
- });
@@ -1,196 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
-
3
- import { z } from 'zod';
4
-
5
- import { Result } from '../result.js';
6
- import { filterSurfaceTrails, matchesTrailPattern } from '../surface-filter.js';
7
- import { signal } from '../signal.js';
8
- import { trail } from '../trail.js';
9
-
10
- const orderPlaced = signal('order.placed', {
11
- payload: z.object({ orderId: z.string() }),
12
- });
13
-
14
- const publicTrail = trail('entity.show', {
15
- blaze: () => Result.ok({ ok: true }),
16
- input: z.object({}),
17
- intent: 'read',
18
- });
19
-
20
- const nestedTrail = trail('entity.admin.audit', {
21
- blaze: () => Result.ok({ ok: true }),
22
- input: z.object({}),
23
- intent: 'destroy',
24
- });
25
-
26
- const internalTrail = trail('entity.secret.rotate', {
27
- blaze: () => Result.ok({ ok: true }),
28
- input: z.object({}),
29
- visibility: 'internal',
30
- });
31
-
32
- const legacyInternalTrail = trail('entity.legacy.rotate', {
33
- blaze: () => Result.ok({ ok: true }),
34
- input: z.object({}),
35
- meta: { internal: true },
36
- });
37
-
38
- const bareEntityTrail = trail('entity', {
39
- blaze: () => Result.ok({ ok: true }),
40
- input: z.object({}),
41
- });
42
-
43
- const otherTrail = trail('other', {
44
- blaze: () => Result.ok({ ok: true }),
45
- input: z.object({}),
46
- });
47
-
48
- const consumerTrail = trail('notify.email', {
49
- blaze: () => Result.ok({ ok: true }),
50
- input: z.object({}),
51
- on: [orderPlaced],
52
- });
53
-
54
- describe('matchesTrailPattern', () => {
55
- test('exact trail IDs match directly', () => {
56
- expect(matchesTrailPattern('entity.show', 'entity.show')).toBe(true);
57
- });
58
-
59
- test('* matches a single dotted segment', () => {
60
- expect(matchesTrailPattern('entity.show', 'entity.*')).toBe(true);
61
- expect(matchesTrailPattern('entity.admin.show', 'entity.*')).toBe(false);
62
- });
63
-
64
- test('** matches any remaining dotted depth', () => {
65
- expect(matchesTrailPattern('entity.admin.show', 'entity.**')).toBe(true);
66
- expect(matchesTrailPattern('entity.show', 'entity.**')).toBe(true);
67
- });
68
-
69
- test('** terminal segment matches entity and all descendants', () => {
70
- // Zero-remaining-segments case: bare parent matches entity.**.
71
- expect(matchesTrailPattern('entity', 'entity.**')).toBe(true);
72
- // One-remaining-segment case for completeness.
73
- expect(matchesTrailPattern('entity.read', 'entity.**')).toBe(true);
74
- // Sibling namespace must not match entity.**.
75
- expect(matchesTrailPattern('other', 'entity.**')).toBe(false);
76
- });
77
- });
78
-
79
- describe('filterSurfaceTrails', () => {
80
- describe('visibility defaults', () => {
81
- test('includes public trails by default', () => {
82
- expect(filterSurfaceTrails([publicTrail])).toEqual([publicTrail]);
83
- });
84
-
85
- test('excludes internal trails by default', () => {
86
- expect(filterSurfaceTrails([internalTrail])).toEqual([]);
87
- });
88
-
89
- test('does not expose internal trails for wildcard includes', () => {
90
- expect(
91
- filterSurfaceTrails([internalTrail], { include: ['entity.**'] })
92
- ).toEqual([]);
93
- });
94
-
95
- test('allows exact include to expose an internal trail', () => {
96
- expect(
97
- filterSurfaceTrails([internalTrail], {
98
- include: ['entity.secret.rotate'],
99
- })
100
- ).toEqual([internalTrail]);
101
- });
102
-
103
- test('legacy meta.internal is treated as internal visibility', () => {
104
- // Default filter: legacy internal trails must not leak out.
105
- expect(filterSurfaceTrails([legacyInternalTrail])).toEqual([]);
106
-
107
- // Wildcard include must also refuse to expose the legacy internal trail.
108
- expect(
109
- filterSurfaceTrails([legacyInternalTrail], { include: ['entity.**'] })
110
- ).toEqual([]);
111
-
112
- // Explicit exact-id include is the documented escape hatch.
113
- expect(
114
- filterSurfaceTrails([legacyInternalTrail], {
115
- include: ['entity.legacy.rotate'],
116
- })
117
- ).toEqual([legacyInternalTrail]);
118
- });
119
- });
120
-
121
- describe('include and exclude patterns', () => {
122
- test('exclude patterns remove matches before include narrowing', () => {
123
- expect(
124
- filterSurfaceTrails([publicTrail], {
125
- exclude: ['entity.*'],
126
- include: ['entity.show'],
127
- })
128
- ).toEqual([]);
129
- });
130
-
131
- test('include patterns narrow the visible trail set', () => {
132
- expect(
133
- filterSurfaceTrails([publicTrail, nestedTrail], {
134
- include: ['entity.**'],
135
- })
136
- ).toEqual([publicTrail, nestedTrail]);
137
- expect(
138
- filterSurfaceTrails([publicTrail, nestedTrail], {
139
- include: ['entity.*'],
140
- })
141
- ).toEqual([publicTrail]);
142
- });
143
-
144
- test('entity.** include matches bare entity and descendants', () => {
145
- // Regression guard for the ** terminal-segment zero-depth case.
146
- expect(
147
- filterSurfaceTrails([bareEntityTrail, publicTrail, otherTrail], {
148
- include: ['entity.**'],
149
- })
150
- ).toEqual([bareEntityTrail, publicTrail]);
151
- });
152
- });
153
-
154
- describe('intent filtering', () => {
155
- test('intent filtering narrows visible trails by behavior class', () => {
156
- expect(
157
- filterSurfaceTrails([publicTrail, nestedTrail], {
158
- intent: ['read'],
159
- })
160
- ).toEqual([publicTrail]);
161
-
162
- expect(
163
- filterSurfaceTrails([publicTrail, nestedTrail], {
164
- intent: ['destroy'],
165
- })
166
- ).toEqual([nestedTrail]);
167
- });
168
-
169
- test('intent filtering composes with glob include patterns using AND logic', () => {
170
- expect(
171
- filterSurfaceTrails([publicTrail, nestedTrail], {
172
- include: ['entity.**'],
173
- intent: ['read'],
174
- })
175
- ).toEqual([publicTrail]);
176
- });
177
-
178
- test('empty intent filters behave like no filter', () => {
179
- expect(
180
- filterSurfaceTrails([publicTrail, nestedTrail], {
181
- intent: [],
182
- })
183
- ).toEqual([publicTrail, nestedTrail]);
184
- });
185
- });
186
-
187
- describe('consumer trails', () => {
188
- test('consumer trails are never exposed on trailheads', () => {
189
- expect(
190
- filterSurfaceTrails([consumerTrail], {
191
- include: ['notify.email'],
192
- })
193
- ).toEqual([]);
194
- });
195
- });
196
- });