@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,260 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
-
3
- import { z } from 'zod';
4
-
5
- import { deriveCliPath, deriveFields } from '../derive.js';
6
-
7
- // ---------------------------------------------------------------------------
8
- // Tests
9
- // ---------------------------------------------------------------------------
10
-
11
- describe('derive', () => {
12
- describe('cli paths', () => {
13
- test('top-level trail IDs derive to a one-segment CLI path', () => {
14
- expect(deriveCliPath('search')).toEqual(['search']);
15
- });
16
-
17
- test('multi-dot trail IDs derive to the full ordered CLI path', () => {
18
- expect(deriveCliPath('topo.pin.remove')).toEqual([
19
- 'topo',
20
- 'pin',
21
- 'remove',
22
- ]);
23
- });
24
-
25
- test('trail IDs with consecutive dots throw a ValidationError', () => {
26
- expect(() => deriveCliPath('topo..pin')).toThrow(
27
- 'Trail ID "topo..pin" contains an empty segment'
28
- );
29
- });
30
-
31
- test('trail IDs with a leading dot throw a ValidationError', () => {
32
- expect(() => deriveCliPath('.pin')).toThrow(
33
- 'Trail ID ".pin" contains an empty segment'
34
- );
35
- });
36
-
37
- test('trail IDs with a trailing dot throw a ValidationError', () => {
38
- expect(() => deriveCliPath('pin.')).toThrow(
39
- 'Trail ID "pin." contains an empty segment'
40
- );
41
- });
42
- });
43
-
44
- describe('primitive types', () => {
45
- test('z.string() derives string field', () => {
46
- const schema = z.object({ name: z.string() });
47
- const fields = deriveFields(schema);
48
- expect(fields).toHaveLength(1);
49
- expect(fields[0]).toMatchObject({
50
- label: 'Name',
51
- name: 'name',
52
- required: true,
53
- type: 'string',
54
- });
55
- });
56
-
57
- test('z.number() derives number field', () => {
58
- const schema = z.object({ count: z.number() });
59
- const fields = deriveFields(schema);
60
- expect(fields[0]).toMatchObject({
61
- name: 'count',
62
- required: true,
63
- type: 'number',
64
- });
65
- });
66
-
67
- test('z.boolean() derives boolean field', () => {
68
- const schema = z.object({ verbose: z.boolean() });
69
- const fields = deriveFields(schema);
70
- expect(fields[0]).toMatchObject({
71
- name: 'verbose',
72
- required: true,
73
- type: 'boolean',
74
- });
75
- });
76
- });
77
-
78
- describe('enum and multiselect', () => {
79
- test('z.enum() derives enum field with options', () => {
80
- const schema = z.object({ color: z.enum(['red', 'green', 'blue']) });
81
- const fields = deriveFields(schema);
82
- expect(fields[0]).toMatchObject({
83
- name: 'color',
84
- required: true,
85
- type: 'enum',
86
- });
87
- expect(fields[0]?.options).toEqual([
88
- { value: 'red' },
89
- { value: 'green' },
90
- { value: 'blue' },
91
- ]);
92
- });
93
-
94
- test('z.array(z.enum()) derives multiselect field', () => {
95
- const schema = z.object({
96
- tags: z.array(z.enum(['a', 'b', 'c'])),
97
- });
98
- const fields = deriveFields(schema);
99
- expect(fields[0]).toMatchObject({
100
- name: 'tags',
101
- required: true,
102
- type: 'multiselect',
103
- });
104
- expect(fields[0]?.options).toEqual([
105
- { value: 'a' },
106
- { value: 'b' },
107
- { value: 'c' },
108
- ]);
109
- });
110
- });
111
-
112
- describe('array types', () => {
113
- test('z.array(z.string()) derives string[] field', () => {
114
- const schema = z.object({ names: z.array(z.string()) });
115
- const fields = deriveFields(schema);
116
- expect(fields[0]).toMatchObject({
117
- name: 'names',
118
- options: undefined,
119
- required: true,
120
- type: 'string[]',
121
- });
122
- });
123
-
124
- test('z.array(z.number()) derives number[] field', () => {
125
- const schema = z.object({ scores: z.array(z.number()) });
126
- const fields = deriveFields(schema);
127
- expect(fields[0]).toMatchObject({
128
- name: 'scores',
129
- options: undefined,
130
- required: true,
131
- type: 'number[]',
132
- });
133
- });
134
-
135
- test('optional z.array(z.string()) marks not required', () => {
136
- const schema = z.object({ tags: z.array(z.string()).optional() });
137
- const fields = deriveFields(schema);
138
- expect(fields[0]).toMatchObject({
139
- name: 'tags',
140
- required: false,
141
- type: 'string[]',
142
- });
143
- });
144
-
145
- test('z.array(z.string()) with default sets default value', () => {
146
- const schema = z.object({
147
- items: z.array(z.string()).default(['a', 'b']),
148
- });
149
- const fields = deriveFields(schema);
150
- expect(fields[0]?.required).toBe(false);
151
- expect(fields[0]?.type).toBe('string[]');
152
- expect(fields[0]?.default).toEqual(['a', 'b']);
153
- });
154
-
155
- test('z.array(z.object(...)) is omitted when it cannot be represented faithfully', () => {
156
- const schema = z.object({
157
- files: z.array(
158
- z.object({
159
- content: z.string(),
160
- filename: z.string(),
161
- })
162
- ),
163
- });
164
-
165
- expect(deriveFields(schema)).toEqual([]);
166
- });
167
- });
168
-
169
- describe('unsupported shapes', () => {
170
- test('nested objects are omitted when they cannot be represented faithfully', () => {
171
- const schema = z.object({
172
- filter: z.object({
173
- query: z.string(),
174
- }),
175
- });
176
-
177
- expect(deriveFields(schema)).toEqual([]);
178
- });
179
- });
180
-
181
- describe('modifiers', () => {
182
- test('.describe() sets label', () => {
183
- const schema = z.object({
184
- name: z.string().describe('Your full name'),
185
- });
186
- const fields = deriveFields(schema);
187
- expect(fields[0]?.label).toBe('Your full name');
188
- });
189
-
190
- test('.default() sets default and marks not required', () => {
191
- const schema = z.object({
192
- port: z.number().default(3000),
193
- });
194
- const fields = deriveFields(schema);
195
- expect(fields[0]?.required).toBe(false);
196
- expect(fields[0]?.default).toBe(3000);
197
- });
198
-
199
- test('.optional() marks not required', () => {
200
- const schema = z.object({
201
- nickname: z.string().optional(),
202
- });
203
- const fields = deriveFields(schema);
204
- expect(fields[0]?.required).toBe(false);
205
- });
206
- });
207
-
208
- describe('overrides', () => {
209
- test('override label replaces derived label', () => {
210
- const schema = z.object({
211
- name: z.string().describe('Derived label'),
212
- });
213
- const fields = deriveFields(schema, {
214
- name: { label: 'Override label' },
215
- });
216
- expect(fields[0]?.label).toBe('Override label');
217
- });
218
-
219
- test('override options enrich enum values with labels and hints', () => {
220
- const schema = z.object({
221
- color: z.enum(['red', 'green', 'blue']),
222
- });
223
- const fields = deriveFields(schema, {
224
- color: {
225
- options: [
226
- { hint: 'Hot color', label: 'Red', value: 'red' },
227
- { label: 'Green', value: 'green' },
228
- ],
229
- },
230
- });
231
- expect(fields[0]?.options).toEqual([
232
- { hint: 'Hot color', label: 'Red', value: 'red' },
233
- { label: 'Green', value: 'green' },
234
- { value: 'blue' },
235
- ]);
236
- });
237
- });
238
-
239
- describe('sorting and edge cases', () => {
240
- test('returns fields sorted by name', () => {
241
- const schema = z.object({
242
- alpha: z.string(),
243
- middle: z.string(),
244
- zebra: z.string(),
245
- });
246
- const fields = deriveFields(schema);
247
- expect(fields.map((f) => f.name)).toEqual(['alpha', 'middle', 'zebra']);
248
- });
249
-
250
- test('returns empty array for non-object schema', () => {
251
- expect(deriveFields(z.string())).toEqual([]);
252
- });
253
-
254
- test('humanizes camelCase field names', () => {
255
- const schema = z.object({ firstName: z.string() });
256
- const fields = deriveFields(schema);
257
- expect(fields[0]?.label).toBe('First Name');
258
- });
259
- });
260
- });
@@ -1,427 +0,0 @@
1
- import { describe, test, expect } from 'bun:test';
2
-
3
- import {
4
- TrailsError,
5
- ValidationError,
6
- AmbiguousError,
7
- AssertionError,
8
- NotFoundError,
9
- AlreadyExistsError,
10
- ConflictError,
11
- PermissionError,
12
- TimeoutError,
13
- RateLimitError,
14
- NetworkError,
15
- InternalError,
16
- AuthError,
17
- CancelledError,
18
- RetryExhaustedError,
19
- exitCodeMap,
20
- statusCodeMap,
21
- jsonRpcCodeMap,
22
- retryableMap,
23
- isRetryable,
24
- isTrailsError,
25
- } from '../errors.js';
26
- import type { ErrorCategory } from '../errors.js';
27
-
28
- // ---------------------------------------------------------------------------
29
- // Error class matrix
30
- // ---------------------------------------------------------------------------
31
-
32
- const errorMatrix: readonly {
33
- Class: new (
34
- message: string,
35
- options?: { cause?: Error; context?: Record<string, unknown> }
36
- ) => TrailsError;
37
- category: ErrorCategory;
38
- retryable: boolean;
39
- name: string;
40
- }[] = [
41
- {
42
- Class: ValidationError,
43
- category: 'validation',
44
- name: 'ValidationError',
45
- retryable: false,
46
- },
47
- {
48
- Class: AmbiguousError,
49
- category: 'validation',
50
- name: 'AmbiguousError',
51
- retryable: false,
52
- },
53
- {
54
- Class: AssertionError,
55
- category: 'internal',
56
- name: 'AssertionError',
57
- retryable: false,
58
- },
59
- {
60
- Class: NotFoundError,
61
- category: 'not_found',
62
- name: 'NotFoundError',
63
- retryable: false,
64
- },
65
- {
66
- Class: AlreadyExistsError,
67
- category: 'conflict',
68
- name: 'AlreadyExistsError',
69
- retryable: false,
70
- },
71
- {
72
- Class: ConflictError,
73
- category: 'conflict',
74
- name: 'ConflictError',
75
- retryable: false,
76
- },
77
- {
78
- Class: PermissionError,
79
- category: 'permission',
80
- name: 'PermissionError',
81
- retryable: false,
82
- },
83
- {
84
- Class: TimeoutError,
85
- category: 'timeout',
86
- name: 'TimeoutError',
87
- retryable: true,
88
- },
89
- {
90
- Class: RateLimitError,
91
- category: 'rate_limit',
92
- name: 'RateLimitError',
93
- retryable: true,
94
- },
95
- {
96
- Class: NetworkError,
97
- category: 'network',
98
- name: 'NetworkError',
99
- retryable: true,
100
- },
101
- {
102
- Class: InternalError,
103
- category: 'internal',
104
- name: 'InternalError',
105
- retryable: false,
106
- },
107
- { Class: AuthError, category: 'auth', name: 'AuthError', retryable: false },
108
- {
109
- Class: CancelledError,
110
- category: 'cancelled',
111
- name: 'CancelledError',
112
- retryable: false,
113
- },
114
- ];
115
-
116
- describe('error classes', () => {
117
- // oxlint-disable-next-line prefer-each -- describe.each loses type safety on heterogeneous class matrix
118
- for (const { Class, category, retryable, name } of errorMatrix) {
119
- describe(name, () => {
120
- test('sets correct category', () => {
121
- const err = new Class('test');
122
- expect(err.category).toBe(category);
123
- });
124
-
125
- test('sets correct retryable', () => {
126
- const err = new Class('test');
127
- expect(err.retryable).toBe(retryable);
128
- });
129
-
130
- test('sets message', () => {
131
- const err = new Class('something went wrong');
132
- expect(err.message).toBe('something went wrong');
133
- });
134
-
135
- test('sets name to constructor name', () => {
136
- const err = new Class('test');
137
- expect(err.name).toBe(name);
138
- });
139
-
140
- test('sets cause when provided', () => {
141
- const cause = new Error('root cause');
142
- const err = new Class('test', { cause });
143
- expect(err.cause).toBe(cause);
144
- });
145
-
146
- test('sets context when provided', () => {
147
- const ctx = { action: 'delete', userId: 'u_123' };
148
- const err = new Class('test', { context: ctx });
149
- expect(err.context).toEqual(ctx);
150
- });
151
-
152
- test('context is undefined when not provided', () => {
153
- const err = new Class('test');
154
- expect(err.context).toBeUndefined();
155
- });
156
-
157
- test('is instanceof TrailsError', () => {
158
- const err = new Class('test');
159
- expect(err).toBeInstanceOf(TrailsError);
160
- });
161
-
162
- test('is instanceof Error', () => {
163
- const err = new Class('test');
164
- expect(err).toBeInstanceOf(Error);
165
- });
166
-
167
- test('is instanceof its own class', () => {
168
- const err = new Class('test');
169
- expect(err).toBeInstanceOf(Class);
170
- });
171
- });
172
- }
173
- });
174
-
175
- // ---------------------------------------------------------------------------
176
- // RateLimitError.retryAfter
177
- // ---------------------------------------------------------------------------
178
-
179
- describe('RateLimitError.retryAfter', () => {
180
- test('stores retryAfter when provided', () => {
181
- const err = new RateLimitError('slow down', { retryAfter: 30 });
182
- expect(err.retryAfter).toBe(30);
183
- });
184
-
185
- test('retryAfter is undefined when not provided', () => {
186
- const err = new RateLimitError('slow down');
187
- expect(err.retryAfter).toBeUndefined();
188
- });
189
-
190
- test('retryAfter coexists with cause and context', () => {
191
- const cause = new Error('upstream');
192
- const err = new RateLimitError('slow down', {
193
- cause,
194
- context: { endpoint: '/api' },
195
- retryAfter: 60,
196
- });
197
- expect(err.retryAfter).toBe(60);
198
- expect(err.cause).toBe(cause);
199
- expect(err.context).toEqual({ endpoint: '/api' });
200
- });
201
- });
202
-
203
- // ---------------------------------------------------------------------------
204
- // Taxonomy maps
205
- // ---------------------------------------------------------------------------
206
-
207
- describe('exitCodeMap', () => {
208
- test('maps all categories to expected exit codes', () => {
209
- expect(exitCodeMap.validation).toBe(1);
210
- expect(exitCodeMap.not_found).toBe(2);
211
- expect(exitCodeMap.conflict).toBe(3);
212
- expect(exitCodeMap.permission).toBe(4);
213
- expect(exitCodeMap.timeout).toBe(5);
214
- expect(exitCodeMap.rate_limit).toBe(6);
215
- expect(exitCodeMap.network).toBe(7);
216
- expect(exitCodeMap.internal).toBe(8);
217
- expect(exitCodeMap.auth).toBe(9);
218
- expect(exitCodeMap.cancelled).toBe(130);
219
- });
220
- });
221
-
222
- describe('statusCodeMap', () => {
223
- test('maps all categories to expected HTTP status codes', () => {
224
- expect(statusCodeMap.validation).toBe(400);
225
- expect(statusCodeMap.not_found).toBe(404);
226
- expect(statusCodeMap.conflict).toBe(409);
227
- expect(statusCodeMap.permission).toBe(403);
228
- expect(statusCodeMap.timeout).toBe(504);
229
- expect(statusCodeMap.rate_limit).toBe(429);
230
- expect(statusCodeMap.network).toBe(502);
231
- expect(statusCodeMap.internal).toBe(500);
232
- expect(statusCodeMap.auth).toBe(401);
233
- expect(statusCodeMap.cancelled).toBe(499);
234
- });
235
- });
236
-
237
- describe('jsonRpcCodeMap', () => {
238
- test('maps all categories to expected JSON-RPC codes', () => {
239
- expect(jsonRpcCodeMap.validation).toBe(-32_602);
240
- expect(jsonRpcCodeMap.not_found).toBe(-32_601);
241
- expect(jsonRpcCodeMap.conflict).toBe(-32_603);
242
- expect(jsonRpcCodeMap.permission).toBe(-32_600);
243
- expect(jsonRpcCodeMap.timeout).toBe(-32_603);
244
- expect(jsonRpcCodeMap.rate_limit).toBe(-32_603);
245
- expect(jsonRpcCodeMap.network).toBe(-32_603);
246
- expect(jsonRpcCodeMap.internal).toBe(-32_603);
247
- expect(jsonRpcCodeMap.auth).toBe(-32_600);
248
- expect(jsonRpcCodeMap.cancelled).toBe(-32_603);
249
- });
250
- });
251
-
252
- describe('retryableMap', () => {
253
- test('only timeout, rate_limit, and network are retryable', () => {
254
- expect(retryableMap.timeout).toBe(true);
255
- expect(retryableMap.rate_limit).toBe(true);
256
- expect(retryableMap.network).toBe(true);
257
-
258
- expect(retryableMap.validation).toBe(false);
259
- expect(retryableMap.not_found).toBe(false);
260
- expect(retryableMap.conflict).toBe(false);
261
- expect(retryableMap.permission).toBe(false);
262
- expect(retryableMap.internal).toBe(false);
263
- expect(retryableMap.auth).toBe(false);
264
- expect(retryableMap.cancelled).toBe(false);
265
- });
266
- });
267
-
268
- // ---------------------------------------------------------------------------
269
- // Helper functions
270
- // ---------------------------------------------------------------------------
271
-
272
- describe('isRetryable', () => {
273
- test('returns true for retryable errors', () => {
274
- expect(isRetryable(new TimeoutError('timed out'))).toBe(true);
275
- expect(isRetryable(new RateLimitError('too fast'))).toBe(true);
276
- expect(isRetryable(new NetworkError('disconnected'))).toBe(true);
277
- });
278
-
279
- test('returns false for non-retryable errors', () => {
280
- expect(isRetryable(new ValidationError('bad input'))).toBe(false);
281
- expect(isRetryable(new NotFoundError('missing'))).toBe(false);
282
- expect(isRetryable(new AuthError('unauthorized'))).toBe(false);
283
- expect(isRetryable(new InternalError('oops'))).toBe(false);
284
- expect(isRetryable(new CancelledError('aborted'))).toBe(false);
285
- });
286
-
287
- test('returns false for plain Error', () => {
288
- expect(isRetryable(new Error('generic'))).toBe(false);
289
- });
290
- });
291
-
292
- describe('isTrailsError', () => {
293
- test('returns true for TrailsError subclasses', () => {
294
- expect(isTrailsError(new ValidationError('bad'))).toBe(true);
295
- expect(isTrailsError(new NetworkError('down'))).toBe(true);
296
- expect(isTrailsError(new CancelledError('stop'))).toBe(true);
297
- });
298
-
299
- test('returns false for plain Error', () => {
300
- expect(isTrailsError(new Error('nope'))).toBe(false);
301
- });
302
-
303
- test('returns false for non-error values', () => {
304
- expect(isTrailsError(null)).toBe(false);
305
- expect(isTrailsError()).toBe(false);
306
- expect(isTrailsError('string')).toBe(false);
307
- expect(isTrailsError(42)).toBe(false);
308
- expect(isTrailsError({})).toBe(false);
309
- });
310
- });
311
-
312
- // ---------------------------------------------------------------------------
313
- // RetryExhaustedError
314
- // ---------------------------------------------------------------------------
315
-
316
- describe('RetryExhaustedError', () => {
317
- describe('category and retryability', () => {
318
- test('inherits category from wrapped error', () => {
319
- const err = new RetryExhaustedError(new ConflictError('conflict'), {
320
- attempts: 1,
321
- detour: 'ConflictError',
322
- });
323
- expect(err.category).toBe('conflict');
324
- });
325
-
326
- test('inherits category from retryable error class', () => {
327
- const err = new RetryExhaustedError(new TimeoutError('timed out'), {
328
- attempts: 3,
329
- detour: 'TimeoutError',
330
- });
331
- expect(err.category).toBe('timeout');
332
- });
333
-
334
- test('retryable is always false regardless of wrapped category', () => {
335
- const fromTimeout = new RetryExhaustedError(
336
- new TimeoutError('timed out'),
337
- { attempts: 3, detour: 'TimeoutError' }
338
- );
339
- expect(fromTimeout.retryable).toBe(false);
340
-
341
- const fromNetwork = new RetryExhaustedError(
342
- new NetworkError('disconnected'),
343
- { attempts: 2, detour: 'NetworkError' }
344
- );
345
- expect(fromNetwork.retryable).toBe(false);
346
- });
347
-
348
- test('isRetryable returns false even when wrapping a retryable error', () => {
349
- const err = new RetryExhaustedError(new TimeoutError('timed out'), {
350
- attempts: 3,
351
- detour: 'TimeoutError',
352
- });
353
- expect(isRetryable(err)).toBe(false);
354
- });
355
- });
356
-
357
- describe('metadata properties', () => {
358
- test('cause preserves the original error', () => {
359
- const original = new ConflictError('version mismatch');
360
- const err = new RetryExhaustedError(original, {
361
- attempts: 1,
362
- detour: 'ConflictError',
363
- });
364
- expect(err.cause).toBe(original);
365
- });
366
-
367
- test('exposes attempts as a typed readonly property', () => {
368
- const err = new RetryExhaustedError(
369
- new ConflictError('version mismatch'),
370
- { attempts: 3, detour: 'ConflictError' }
371
- );
372
- expect(err.attempts).toBe(3);
373
- });
374
-
375
- test('exposes detour as a typed readonly property', () => {
376
- const err = new RetryExhaustedError(new TimeoutError('timed out'), {
377
- attempts: 2,
378
- detour: 'TimeoutRetry',
379
- });
380
- expect(err.detour).toBe('TimeoutRetry');
381
- });
382
-
383
- test('message includes attempt count and wrapped message', () => {
384
- const err = new RetryExhaustedError(
385
- new ConflictError('version mismatch'),
386
- { attempts: 3, detour: 'ConflictError' }
387
- );
388
- expect(err.message).toBe(
389
- 'Recovery exhausted after 3 attempts: version mismatch'
390
- );
391
- });
392
- });
393
-
394
- describe('identity', () => {
395
- test('is instanceof InternalError', () => {
396
- const err = new RetryExhaustedError(new ConflictError('x'), {
397
- attempts: 1,
398
- detour: 'ConflictError',
399
- });
400
- expect(err).toBeInstanceOf(InternalError);
401
- });
402
-
403
- test('name is RetryExhaustedError', () => {
404
- const err = new RetryExhaustedError(new ConflictError('x'), {
405
- attempts: 1,
406
- detour: 'ConflictError',
407
- });
408
- expect(err.name).toBe('RetryExhaustedError');
409
- });
410
-
411
- test('is instanceof TrailsError', () => {
412
- const err = new RetryExhaustedError(new ConflictError('x'), {
413
- attempts: 1,
414
- detour: 'ConflictError',
415
- });
416
- expect(err).toBeInstanceOf(TrailsError);
417
- });
418
-
419
- test('is instanceof Error', () => {
420
- const err = new RetryExhaustedError(new ConflictError('x'), {
421
- attempts: 1,
422
- detour: 'ConflictError',
423
- });
424
- expect(err).toBeInstanceOf(Error);
425
- });
426
- });
427
- });