@ontrails/core 1.0.0-beta.0

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 (216) hide show
  1. package/.turbo/turbo-build.log +1 -0
  2. package/.turbo/turbo-lint.log +3 -0
  3. package/.turbo/turbo-typecheck.log +1 -0
  4. package/CHANGELOG.md +15 -0
  5. package/README.md +179 -0
  6. package/dist/adapters.d.ts +39 -0
  7. package/dist/adapters.d.ts.map +1 -0
  8. package/dist/adapters.js +2 -0
  9. package/dist/adapters.js.map +1 -0
  10. package/dist/blob-ref.d.ts +20 -0
  11. package/dist/blob-ref.d.ts.map +1 -0
  12. package/dist/blob-ref.js +22 -0
  13. package/dist/blob-ref.js.map +1 -0
  14. package/dist/branded.d.ts +36 -0
  15. package/dist/branded.d.ts.map +1 -0
  16. package/dist/branded.js +89 -0
  17. package/dist/branded.js.map +1 -0
  18. package/dist/collections.d.ts +31 -0
  19. package/dist/collections.d.ts.map +1 -0
  20. package/dist/collections.js +60 -0
  21. package/dist/collections.js.map +1 -0
  22. package/dist/context.d.ts +10 -0
  23. package/dist/context.d.ts.map +1 -0
  24. package/dist/context.js +15 -0
  25. package/dist/context.js.map +1 -0
  26. package/dist/derive.d.ts +33 -0
  27. package/dist/derive.d.ts.map +1 -0
  28. package/dist/derive.js +122 -0
  29. package/dist/derive.js.map +1 -0
  30. package/dist/errors.d.ts +83 -0
  31. package/dist/errors.d.ts.map +1 -0
  32. package/dist/errors.js +142 -0
  33. package/dist/errors.js.map +1 -0
  34. package/dist/event.d.ts +45 -0
  35. package/dist/event.d.ts.map +1 -0
  36. package/dist/event.js +17 -0
  37. package/dist/event.js.map +1 -0
  38. package/dist/fetch.d.ts +15 -0
  39. package/dist/fetch.d.ts.map +1 -0
  40. package/dist/fetch.js +102 -0
  41. package/dist/fetch.js.map +1 -0
  42. package/dist/guards.d.ts +17 -0
  43. package/dist/guards.d.ts.map +1 -0
  44. package/dist/guards.js +25 -0
  45. package/dist/guards.js.map +1 -0
  46. package/dist/health.d.ts +18 -0
  47. package/dist/health.d.ts.map +1 -0
  48. package/dist/health.js +5 -0
  49. package/dist/health.js.map +1 -0
  50. package/dist/hike.d.ts +36 -0
  51. package/dist/hike.d.ts.map +1 -0
  52. package/dist/hike.js +20 -0
  53. package/dist/hike.js.map +1 -0
  54. package/dist/index.d.ts +34 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +38 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/job.d.ts +24 -0
  59. package/dist/job.d.ts.map +1 -0
  60. package/dist/job.js +17 -0
  61. package/dist/job.js.map +1 -0
  62. package/dist/layer.d.ts +17 -0
  63. package/dist/layer.d.ts.map +1 -0
  64. package/dist/layer.js +21 -0
  65. package/dist/layer.js.map +1 -0
  66. package/dist/path-security.d.ts +28 -0
  67. package/dist/path-security.d.ts.map +1 -0
  68. package/dist/path-security.js +63 -0
  69. package/dist/path-security.js.map +1 -0
  70. package/dist/patterns/bulk.d.ts +15 -0
  71. package/dist/patterns/bulk.d.ts.map +1 -0
  72. package/dist/patterns/bulk.js +14 -0
  73. package/dist/patterns/bulk.js.map +1 -0
  74. package/dist/patterns/change.d.ts +10 -0
  75. package/dist/patterns/change.d.ts.map +1 -0
  76. package/dist/patterns/change.js +10 -0
  77. package/dist/patterns/change.js.map +1 -0
  78. package/dist/patterns/date-range.d.ts +10 -0
  79. package/dist/patterns/date-range.d.ts.map +1 -0
  80. package/dist/patterns/date-range.js +10 -0
  81. package/dist/patterns/date-range.js.map +1 -0
  82. package/dist/patterns/index.d.ts +9 -0
  83. package/dist/patterns/index.d.ts.map +1 -0
  84. package/dist/patterns/index.js +9 -0
  85. package/dist/patterns/index.js.map +1 -0
  86. package/dist/patterns/pagination.d.ts +18 -0
  87. package/dist/patterns/pagination.d.ts.map +1 -0
  88. package/dist/patterns/pagination.js +18 -0
  89. package/dist/patterns/pagination.js.map +1 -0
  90. package/dist/patterns/progress.d.ts +11 -0
  91. package/dist/patterns/progress.d.ts.map +1 -0
  92. package/dist/patterns/progress.js +11 -0
  93. package/dist/patterns/progress.js.map +1 -0
  94. package/dist/patterns/sorting.d.ts +13 -0
  95. package/dist/patterns/sorting.d.ts.map +1 -0
  96. package/dist/patterns/sorting.js +10 -0
  97. package/dist/patterns/sorting.js.map +1 -0
  98. package/dist/patterns/status.d.ts +15 -0
  99. package/dist/patterns/status.d.ts.map +1 -0
  100. package/dist/patterns/status.js +9 -0
  101. package/dist/patterns/status.js.map +1 -0
  102. package/dist/patterns/timestamps.d.ts +10 -0
  103. package/dist/patterns/timestamps.d.ts.map +1 -0
  104. package/dist/patterns/timestamps.js +10 -0
  105. package/dist/patterns/timestamps.js.map +1 -0
  106. package/dist/redaction/index.d.ts +4 -0
  107. package/dist/redaction/index.d.ts.map +1 -0
  108. package/dist/redaction/index.js +3 -0
  109. package/dist/redaction/index.js.map +1 -0
  110. package/dist/redaction/patterns.d.ts +9 -0
  111. package/dist/redaction/patterns.d.ts.map +1 -0
  112. package/dist/redaction/patterns.js +39 -0
  113. package/dist/redaction/patterns.js.map +1 -0
  114. package/dist/redaction/redactor.d.ts +27 -0
  115. package/dist/redaction/redactor.d.ts.map +1 -0
  116. package/dist/redaction/redactor.js +89 -0
  117. package/dist/redaction/redactor.js.map +1 -0
  118. package/dist/resilience.d.ts +34 -0
  119. package/dist/resilience.d.ts.map +1 -0
  120. package/dist/resilience.js +164 -0
  121. package/dist/resilience.js.map +1 -0
  122. package/dist/result.d.ts +57 -0
  123. package/dist/result.d.ts.map +1 -0
  124. package/dist/result.js +145 -0
  125. package/dist/result.js.map +1 -0
  126. package/dist/serialization.d.ts +27 -0
  127. package/dist/serialization.d.ts.map +1 -0
  128. package/dist/serialization.js +115 -0
  129. package/dist/serialization.js.map +1 -0
  130. package/dist/topo.d.ts +18 -0
  131. package/dist/topo.d.ts.map +1 -0
  132. package/dist/topo.js +74 -0
  133. package/dist/topo.js.map +1 -0
  134. package/dist/trail.d.ts +83 -0
  135. package/dist/trail.d.ts.map +1 -0
  136. package/dist/trail.js +16 -0
  137. package/dist/trail.js.map +1 -0
  138. package/dist/types.d.ts +46 -0
  139. package/dist/types.d.ts.map +1 -0
  140. package/dist/types.js +2 -0
  141. package/dist/types.js.map +1 -0
  142. package/dist/validate-topo.d.ts +24 -0
  143. package/dist/validate-topo.d.ts.map +1 -0
  144. package/dist/validate-topo.js +108 -0
  145. package/dist/validate-topo.js.map +1 -0
  146. package/dist/validation.d.ts +27 -0
  147. package/dist/validation.d.ts.map +1 -0
  148. package/dist/validation.js +134 -0
  149. package/dist/validation.js.map +1 -0
  150. package/dist/workspace.d.ts +25 -0
  151. package/dist/workspace.d.ts.map +1 -0
  152. package/dist/workspace.js +57 -0
  153. package/dist/workspace.js.map +1 -0
  154. package/package.json +21 -0
  155. package/src/__tests__/blob-ref.test.ts +103 -0
  156. package/src/__tests__/branded.test.ts +148 -0
  157. package/src/__tests__/collections.test.ts +126 -0
  158. package/src/__tests__/context.test.ts +66 -0
  159. package/src/__tests__/derive.test.ts +159 -0
  160. package/src/__tests__/errors.test.ts +309 -0
  161. package/src/__tests__/event.test.ts +82 -0
  162. package/src/__tests__/fetch.test.ts +217 -0
  163. package/src/__tests__/guards.test.ts +102 -0
  164. package/src/__tests__/hike.test.ts +117 -0
  165. package/src/__tests__/job.test.ts +98 -0
  166. package/src/__tests__/layer.test.ts +224 -0
  167. package/src/__tests__/path-security.test.ts +114 -0
  168. package/src/__tests__/patterns.test.ts +273 -0
  169. package/src/__tests__/redaction.test.ts +244 -0
  170. package/src/__tests__/resilience.test.ts +246 -0
  171. package/src/__tests__/result.test.ts +155 -0
  172. package/src/__tests__/serialization.test.ts +236 -0
  173. package/src/__tests__/topo.test.ts +184 -0
  174. package/src/__tests__/trail.test.ts +179 -0
  175. package/src/__tests__/validate-topo.test.ts +201 -0
  176. package/src/__tests__/validation.test.ts +283 -0
  177. package/src/__tests__/workspace.test.ts +183 -0
  178. package/src/adapters.ts +68 -0
  179. package/src/blob-ref.ts +39 -0
  180. package/src/branded.ts +135 -0
  181. package/src/collections.ts +99 -0
  182. package/src/context.ts +18 -0
  183. package/src/derive.ts +223 -0
  184. package/src/errors.ts +196 -0
  185. package/src/event.ts +77 -0
  186. package/src/fetch.ts +138 -0
  187. package/src/guards.ts +37 -0
  188. package/src/health.ts +23 -0
  189. package/src/hike.ts +77 -0
  190. package/src/index.ts +158 -0
  191. package/src/job.ts +20 -0
  192. package/src/layer.ts +44 -0
  193. package/src/path-security.ts +90 -0
  194. package/src/patterns/bulk.ts +16 -0
  195. package/src/patterns/change.ts +12 -0
  196. package/src/patterns/date-range.ts +12 -0
  197. package/src/patterns/index.ts +8 -0
  198. package/src/patterns/pagination.ts +22 -0
  199. package/src/patterns/progress.ts +13 -0
  200. package/src/patterns/sorting.ts +14 -0
  201. package/src/patterns/status.ts +11 -0
  202. package/src/patterns/timestamps.ts +12 -0
  203. package/src/redaction/index.ts +3 -0
  204. package/src/redaction/patterns.ts +47 -0
  205. package/src/redaction/redactor.ts +178 -0
  206. package/src/resilience.ts +234 -0
  207. package/src/result.ts +180 -0
  208. package/src/serialization.ts +183 -0
  209. package/src/topo.ts +123 -0
  210. package/src/trail.ts +130 -0
  211. package/src/types.ts +58 -0
  212. package/src/validate-topo.ts +151 -0
  213. package/src/validation.ts +182 -0
  214. package/src/workspace.ts +77 -0
  215. package/tsconfig.json +9 -0
  216. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1 @@
1
+ $ tsc -b
@@ -0,0 +1,3 @@
1
+ $ oxlint ./src
2
+ Found 0 warnings and 0 errors.
3
+ Finished in 101ms on 60 files with 93 rules using 24 threads.
@@ -0,0 +1 @@
1
+ $ tsc --noEmit
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # @ontrails/core
2
+
3
+ ## 1.0.0-beta.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Initial v1 beta release of the Trails framework.
8
+
9
+ - **@ontrails/core** — Result type, error taxonomy, trail/hike/event/topo, validateTopo, validateInput/Output, deriveFields, patterns, redaction, branded types, resilience
10
+ - **@ontrails/cli** — CLI surface adapter, Commander integration, flag derivation, layers
11
+ - **@ontrails/mcp** — MCP surface adapter, tool generation, annotations, progress bridge
12
+ - **@ontrails/logging** — Structured logging, sinks, formatters, LogTape adapter
13
+ - **@ontrails/testing** — testAll, testExamples, testTrail, testHike, testContracts, testDetours, surface harnesses
14
+ - **@ontrails/warden** — AST-based code convention rules via oxc-parser, drift detection, CI formatters
15
+ - **@ontrails/schema** — Surface map generation, hashing, semantic diffing
package/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # @ontrails/core
2
+
3
+ The foundation of Trails. Result type, error taxonomy, trail/hike/event definitions, topo, validation, patterns, redaction, branded types, and collection utilities. One external dependency: `zod`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @ontrails/core
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { trail, hike, topo, Result } from '@ontrails/core';
15
+ import { z } from 'zod';
16
+
17
+ // Define a trail -- the atomic unit of work
18
+ const show = trail('entity.show', {
19
+ input: z.object({ name: z.string().describe('Entity name') }),
20
+ output: z.object({ name: z.string(), type: z.string() }),
21
+ readOnly: true,
22
+ examples: [
23
+ {
24
+ name: 'Show an entity',
25
+ input: { name: 'Alpha' },
26
+ expected: { name: 'Alpha', type: 'concept' },
27
+ },
28
+ ],
29
+ implementation: (input) => Result.ok({ name: input.name, type: 'concept' }),
30
+ });
31
+
32
+ // Define a hike -- a composite that follows other trails
33
+ const onboard = hike('entity.onboard', {
34
+ follows: ['entity.add', 'entity.relate'],
35
+ input: z.object({ name: z.string(), type: z.string() }),
36
+ implementation: async (input, ctx) => {
37
+ const added = await ctx.follow('entity.add', input);
38
+ if (added.isErr()) return added;
39
+ return Result.ok({ entity: added.value });
40
+ },
41
+ });
42
+
43
+ // Collect into an app
44
+ import * as entity from './trails/entity';
45
+ const app = topo('myapp', entity);
46
+ ```
47
+
48
+ Pure trails can return `Result` directly. Hikes and other I/O-bound trails can stay `async`; core normalizes both forms to one awaitable runtime shape before layers and surfaces execute them.
49
+
50
+ ## API Overview
51
+
52
+ ### Trail Primitives
53
+
54
+ - **`trail(id, spec)`** -- Define an atomic unit of work. Typed input via Zod, returns `Result`. Authoring may be sync or async.
55
+ - **`hike(id, spec)`** -- Define a composite that follows multiple trails via `ctx.follow()`. Declares dependencies with `follows: string[]`.
56
+ - **`event(id, spec)`** -- Define a server-originated push with a typed data schema.
57
+ - **`topo(name, ...modules)`** -- Collect trail modules into an app. Scans exports for `Trail` shapes and builds the topo.
58
+
59
+ ### Result Type
60
+
61
+ Built-in `Result<T, E>` with no external dependency.
62
+
63
+ ```typescript
64
+ Result.ok(value); // Create a success
65
+ Result.err(error); // Create a failure
66
+ Result.combine(results); // Collect Result<T>[] into Result<T[]>
67
+
68
+ result.isOk(); // Type guard for Ok
69
+ result.isErr(); // Type guard for Err
70
+ result.map(fn); // Transform success value
71
+ result.flatMap(fn); // Chain Result-returning functions
72
+ result.match({ ok, err }); // Pattern match
73
+ result.unwrapOr(fallback); // Value or fallback
74
+ ```
75
+
76
+ Implementations return `Result`, never `throw`.
77
+
78
+ ### Error Taxonomy
79
+
80
+ 13 error classes across 10 categories, all extending `TrailsError`. Each maps to CLI exit codes, HTTP status codes, JSON-RPC codes, and retryability.
81
+
82
+ | Category | Classes | Exit | HTTP | Retryable |
83
+ | --- | --- | --- | --- | --- |
84
+ | `validation` | `ValidationError`, `AmbiguousError`, `AssertionError` | 1 | 400 | No |
85
+ | `not_found` | `NotFoundError` | 2 | 404 | No |
86
+ | `conflict` | `AlreadyExistsError`, `ConflictError` | 3 | 409 | No |
87
+ | `permission` | `PermissionError` | 4 | 403 | No |
88
+ | `timeout` | `TimeoutError` | 5 | 504 | Yes |
89
+ | `rate_limit` | `RateLimitError` | 6 | 429 | Yes |
90
+ | `network` | `NetworkError` | 7 | 502 | Yes |
91
+ | `internal` | `InternalError` | 8 | 500 | No |
92
+ | `auth` | `AuthError` | 9 | 401 | No |
93
+ | `cancelled` | `CancelledError` | 130 | 499 | No |
94
+
95
+ All extend `TrailsError` directly (class inheritance, no factory pattern). Pattern matching via `instanceof` or `error.category`.
96
+
97
+ ### Patterns (`@ontrails/core/patterns`)
98
+
99
+ Reusable Zod schemas for common input/output shapes:
100
+
101
+ - **Pagination** -- `paginationInput`, `paginationOutput` (cursor-based)
102
+ - **Bulk operations** -- `bulkInput`, `bulkOutput` (batch with per-item results)
103
+ - **Timestamps** -- `timestamps` (`createdAt`/`updatedAt`)
104
+ - **Date ranges** -- `dateRangeInput` (`since`/`until`)
105
+ - **Sorting** -- `sortInput` (`sortBy`/`sortOrder`)
106
+ - **Status** -- `statusField` (lifecycle state)
107
+ - **Change tracking** -- `changeOutput` (before/after snapshots)
108
+ - **Progress** -- `progressOutput` (completion reporting)
109
+
110
+ ### Redaction (`@ontrails/core/redaction`)
111
+
112
+ Strip sensitive data from logs and outputs.
113
+
114
+ ```typescript
115
+ import {
116
+ createRedactor,
117
+ DEFAULT_PATTERNS,
118
+ DEFAULT_SENSITIVE_KEYS,
119
+ } from '@ontrails/core/redaction';
120
+
121
+ const redactor = createRedactor({
122
+ patterns: DEFAULT_PATTERNS,
123
+ sensitiveKeys: DEFAULT_SENSITIVE_KEYS,
124
+ });
125
+ ```
126
+
127
+ ### Validation
128
+
129
+ - **`validateInput(schema, data)`** -- Validate data against a Zod schema, returning `Result`.
130
+ - **`formatZodIssues(issues)`** -- Format Zod issues into human-readable strings.
131
+ - **`zodToJsonSchema(schema)`** -- Convert a Zod schema to JSON Schema for MCP/HTTP surfaces.
132
+
133
+ ### Branded Types
134
+
135
+ Nominal typing for IDs and domain-specific strings that should not be interchangeable.
136
+
137
+ ```typescript
138
+ import { uuid, email, nonEmptyString, positiveInt } from '@ontrails/core';
139
+
140
+ const id = uuid('550e8400-e29b-41d4-a716-446655440000');
141
+ const addr = email('user@example.com');
142
+ ```
143
+
144
+ ### Collections and Guards
145
+
146
+ - **Collections** -- `chunk`, `dedupe`, `groupBy`, `sortBy`, `isNonEmptyArray`
147
+ - **Guards** -- `isDefined`, `isNonEmptyString`, `isPlainObject`, `hasProperty`, `assertNever`
148
+ - **Resilience** -- `retry`, `withTimeout`, `shouldRetry`, `getBackoffDelay`
149
+ - **Serialization** -- `serializeError`, `deserializeError`, `Result.fromJson`, `Result.toJson`
150
+ - **Path Security** -- `securePath`, `isPathSafe`, `resolveSafePath`
151
+ - **Workspace** -- `findWorkspaceRoot`, `isInsideWorkspace`, `getRelativePath`
152
+
153
+ ### Layers
154
+
155
+ Cross-cutting concerns that wrap trail execution:
156
+
157
+ ```typescript
158
+ const loggingLayer: Layer = {
159
+ name: 'logging',
160
+ wrap: (next, trail) => async (input, ctx) => {
161
+ ctx.logger.info(`Executing ${trail.id}`);
162
+ return next(input, ctx);
163
+ },
164
+ };
165
+ ```
166
+
167
+ ## Subpath Exports
168
+
169
+ | Export | Contents |
170
+ | --- | --- |
171
+ | `@ontrails/core` | trail, hike, event, topo, Result, errors, types, validation, guards, collections, layers |
172
+ | `@ontrails/core/patterns` | Reusable Zod schema patterns |
173
+ | `@ontrails/core/redaction` | Redactor, default patterns and keys |
174
+
175
+ ## Further Reading
176
+
177
+ - [Getting Started](../../docs/getting-started.md)
178
+ - [Architecture](../../docs/architecture.md)
179
+ - [Vocabulary](../../docs/vocabulary.md)
@@ -0,0 +1,39 @@
1
+ import type { Result } from './result.js';
2
+ /** Options for search queries */
3
+ export interface SearchOptions {
4
+ readonly limit?: number | undefined;
5
+ readonly offset?: number | undefined;
6
+ readonly filters?: Readonly<Record<string, unknown>> | undefined;
7
+ }
8
+ /** A single search hit */
9
+ export interface SearchResult {
10
+ readonly id: string;
11
+ readonly score: number;
12
+ readonly document: Readonly<Record<string, unknown>>;
13
+ }
14
+ /** Options for storage writes */
15
+ export interface StorageOptions {
16
+ /** Time-to-live in milliseconds */
17
+ readonly ttl?: number | undefined;
18
+ }
19
+ /** Full-text / vector index adapter */
20
+ export interface IndexAdapter {
21
+ index(id: string, document: Readonly<Record<string, unknown>>): Promise<Result<void, Error>>;
22
+ search(query: string, options?: SearchOptions): Promise<Result<readonly SearchResult[], Error>>;
23
+ remove(id: string): Promise<Result<void, Error>>;
24
+ }
25
+ /** Key-value storage adapter */
26
+ export interface StorageAdapter {
27
+ get(key: string): Promise<Result<unknown, Error>>;
28
+ set(key: string, value: unknown, options?: StorageOptions): Promise<Result<void, Error>>;
29
+ delete(key: string): Promise<Result<void, Error>>;
30
+ has(key: string): Promise<Result<boolean, Error>>;
31
+ }
32
+ /** Cache adapter with typed get/set and bulk clear */
33
+ export interface CacheAdapter {
34
+ get<T>(key: string): Promise<Result<T | undefined, Error>>;
35
+ set<T>(key: string, value: T, options?: StorageOptions): Promise<Result<void, Error>>;
36
+ delete(key: string): Promise<Result<void, Error>>;
37
+ clear(): Promise<Result<void, Error>>;
38
+ }
39
+ //# sourceMappingURL=adapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAM1C,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CAClE;AAED,0BAA0B;AAC1B,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACtD;AAED,iCAAiC;AACjC,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAMD,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,KAAK,CACH,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC1C,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAEhC,MAAM,CACJ,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,MAAM,CAAC,SAAS,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;IAEnD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;CAClD;AAED,gCAAgC;AAChC,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAClD,GAAG,CACD,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAChC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAClD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;CACnD;AAED,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IAC3D,GAAG,CAAC,CAAC,EACH,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAChC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAClD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;CACvC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.js","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * BlobRef — a frozen reference to binary data for @ontrails/core.
3
+ */
4
+ /** Immutable reference to a blob of binary data. */
5
+ export interface BlobRef {
6
+ readonly name: string;
7
+ readonly mimeType: string;
8
+ readonly size: number;
9
+ readonly data: Uint8Array | ReadableStream<Uint8Array>;
10
+ }
11
+ /** Creates a frozen BlobRef. */
12
+ export declare const createBlobRef: (options: {
13
+ name: string;
14
+ mimeType: string;
15
+ size: number;
16
+ data: Uint8Array | ReadableStream<Uint8Array>;
17
+ }) => BlobRef;
18
+ /** Type guard for BlobRef-shaped values. */
19
+ export declare const isBlobRef: (value: unknown) => value is BlobRef;
20
+ //# sourceMappingURL=blob-ref.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob-ref.d.ts","sourceRoot":"","sources":["../src/blob-ref.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oDAAoD;AACpD,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;CACxD;AAED,gCAAgC;AAChC,eAAO,MAAM,aAAa,GAAI,SAAS;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;CAC/C,KAAG,OAMA,CAAC;AAEL,4CAA4C;AAC5C,eAAO,MAAM,SAAS,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,OAWnD,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * BlobRef — a frozen reference to binary data for @ontrails/core.
3
+ */
4
+ /** Creates a frozen BlobRef. */
5
+ export const createBlobRef = (options) => Object.freeze({
6
+ data: options.data,
7
+ mimeType: options.mimeType,
8
+ name: options.name,
9
+ size: options.size,
10
+ });
11
+ /** Type guard for BlobRef-shaped values. */
12
+ export const isBlobRef = (value) => {
13
+ if (typeof value !== 'object' || value === null) {
14
+ return false;
15
+ }
16
+ const obj = value;
17
+ return (typeof obj['name'] === 'string' &&
18
+ typeof obj['mimeType'] === 'string' &&
19
+ typeof obj['size'] === 'number' &&
20
+ (obj['data'] instanceof Uint8Array || obj['data'] instanceof ReadableStream));
21
+ };
22
+ //# sourceMappingURL=blob-ref.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob-ref.js","sourceRoot":"","sources":["../src/blob-ref.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,gCAAgC;AAChC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAK7B,EAAW,EAAE,CACZ,MAAM,CAAC,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC,IAAI;IAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;IAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;IAClB,IAAI,EAAE,OAAO,CAAC,IAAI;CACnB,CAAC,CAAC;AAEL,4CAA4C;AAC5C,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAc,EAAoB,EAAE;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,OAAO,CACL,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,QAAQ;QAC/B,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,QAAQ;QACnC,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,QAAQ;QAC/B,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,UAAU,IAAI,GAAG,CAAC,MAAM,CAAC,YAAY,cAAc,CAAC,CAC7E,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Branded types and validated constructors for @ontrails/core.
3
+ *
4
+ * Branded types enforce domain constraints at the type level while remaining
5
+ * plain primitives at runtime. Factory functions return Result so callers
6
+ * handle validation failures explicitly.
7
+ */
8
+ import { ValidationError } from './errors.js';
9
+ import type { Result } from './result.js';
10
+ /** Attach a phantom tag to a base type. */
11
+ export type Branded<T, Tag extends string> = T & {
12
+ readonly __brand: Tag;
13
+ };
14
+ /** Brand a value. No validation — use factory functions for safe construction. */
15
+ export declare const brand: <T, Tag extends string>(_tag: Tag, value: T) => Branded<T, Tag>;
16
+ /** Strip the brand and recover the underlying value. */
17
+ export declare const unbrand: <T>(value: Branded<T, string>) => T;
18
+ export type UUID = Branded<string, 'UUID'>;
19
+ export type Email = Branded<string, 'Email'>;
20
+ export type NonEmptyString = Branded<string, 'NonEmptyString'>;
21
+ export type PositiveInt = Branded<number, 'PositiveInt'>;
22
+ export declare const uuid: (value: string) => Result<UUID, ValidationError>;
23
+ export declare const email: (value: string) => Result<Email, ValidationError>;
24
+ export declare const nonEmptyString: (value: string) => Result<NonEmptyString, ValidationError>;
25
+ export declare const positiveInt: (value: number) => Result<PositiveInt, ValidationError>;
26
+ /**
27
+ * Generate a random alphanumeric ID.
28
+ * Runtime-agnostic: uses `crypto.getRandomValues`.
29
+ */
30
+ export declare const shortId: (length?: number) => string;
31
+ /**
32
+ * Produce a deterministic hex hash from an input string.
33
+ * Uses a simple FNV-1a 32-bit hash — good enough for non-cryptographic IDs.
34
+ */
35
+ export declare const hashId: (input: string) => string;
36
+ //# sourceMappingURL=branded.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branded.d.ts","sourceRoot":"","sources":["../src/branded.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAO1C,2CAA2C;AAC3C,MAAM,MAAM,OAAO,CAAC,CAAC,EAAE,GAAG,SAAS,MAAM,IAAI,CAAC,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC;AAE3E,kFAAkF;AAClF,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,GAAG,SAAS,MAAM,EACzC,MAAM,GAAG,EACT,OAAO,CAAC,KACP,OAAO,CAAC,CAAC,EAAE,GAAG,CAA6B,CAAC;AAE/C,wDAAwD;AACxD,eAAO,MAAM,OAAO,GAAI,CAAC,EAAE,OAAO,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,KAAG,CAAe,CAAC;AAMvE,MAAM,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC3C,MAAM,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC7C,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAC/D,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAezD,eAAO,MAAM,IAAI,GAAI,OAAO,MAAM,KAAG,MAAM,CAAC,IAAI,EAAE,eAAe,CAShE,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,KAAG,MAAM,CAAC,KAAK,EAAE,eAAe,CASlE,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,OAAO,MAAM,KACZ,MAAM,CAAC,cAAc,EAAE,eAAe,CAKxC,CAAC;AAEF,eAAO,MAAM,WAAW,GACtB,OAAO,MAAM,KACZ,MAAM,CAAC,WAAW,EAAE,eAAe,CASrC,CAAC;AASF;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,eAAU,KAAG,MAWpC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,GAAI,OAAO,MAAM,KAAG,MAYtC,CAAC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Branded types and validated constructors for @ontrails/core.
3
+ *
4
+ * Branded types enforce domain constraints at the type level while remaining
5
+ * plain primitives at runtime. Factory functions return Result so callers
6
+ * handle validation failures explicitly.
7
+ */
8
+ import { ValidationError } from './errors.js';
9
+ import { Result as R } from './result.js';
10
+ /** Brand a value. No validation — use factory functions for safe construction. */
11
+ export const brand = (_tag, value) => value;
12
+ /** Strip the brand and recover the underlying value. */
13
+ export const unbrand = (value) => value;
14
+ // ---------------------------------------------------------------------------
15
+ // Validation patterns
16
+ // ---------------------------------------------------------------------------
17
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
18
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
19
+ // ---------------------------------------------------------------------------
20
+ // Factory functions — each returns Result<BrandedType, ValidationError>
21
+ // ---------------------------------------------------------------------------
22
+ export const uuid = (value) => {
23
+ if (!UUID_RE.test(value)) {
24
+ return R.err(new ValidationError(`Invalid UUID: "${value}"`, {
25
+ context: { value },
26
+ }));
27
+ }
28
+ return R.ok(value);
29
+ };
30
+ export const email = (value) => {
31
+ if (!EMAIL_RE.test(value)) {
32
+ return R.err(new ValidationError(`Invalid email: "${value}"`, {
33
+ context: { value },
34
+ }));
35
+ }
36
+ return R.ok(value);
37
+ };
38
+ export const nonEmptyString = (value) => {
39
+ if (value.length === 0) {
40
+ return R.err(new ValidationError('String must not be empty'));
41
+ }
42
+ return R.ok(value);
43
+ };
44
+ export const positiveInt = (value) => {
45
+ if (!Number.isInteger(value) || value <= 0) {
46
+ return R.err(new ValidationError(`Expected positive integer, got ${value}`, {
47
+ context: { value },
48
+ }));
49
+ }
50
+ return R.ok(value);
51
+ };
52
+ // ---------------------------------------------------------------------------
53
+ // ID utilities
54
+ // ---------------------------------------------------------------------------
55
+ const ALPHANUMERIC = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
56
+ /**
57
+ * Generate a random alphanumeric ID.
58
+ * Runtime-agnostic: uses `crypto.getRandomValues`.
59
+ */
60
+ export const shortId = (length = 8) => {
61
+ const bytes = new Uint8Array(length);
62
+ crypto.getRandomValues(bytes);
63
+ let id = '';
64
+ for (let i = 0; i < length; i += 1) {
65
+ const byte = bytes[i];
66
+ if (byte !== undefined) {
67
+ id += ALPHANUMERIC[byte % ALPHANUMERIC.length];
68
+ }
69
+ }
70
+ return id;
71
+ };
72
+ /**
73
+ * Produce a deterministic hex hash from an input string.
74
+ * Uses a simple FNV-1a 32-bit hash — good enough for non-cryptographic IDs.
75
+ */
76
+ export const hashId = (input) => {
77
+ // FNV offset basis
78
+ let hash = 2_166_136_261;
79
+ for (let i = 0; i < input.length; i += 1) {
80
+ // oxlint-disable-next-line no-bitwise -- FNV-1a hash requires XOR
81
+ hash ^= input.codePointAt(i) ?? 0;
82
+ // FNV prime
83
+ hash = Math.imul(hash, 0x01_00_01_93);
84
+ }
85
+ // Convert to unsigned 32-bit then hex
86
+ // oxlint-disable-next-line no-bitwise, prefer-math-trunc -- unsigned right shift needed for u32 conversion (Math.trunc differs semantically)
87
+ return (hash >>> 0).toString(16).padStart(8, '0');
88
+ };
89
+ //# sourceMappingURL=branded.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branded.js","sourceRoot":"","sources":["../src/branded.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Collection utilities and type helpers for @ontrails/core.
3
+ */
4
+ /** Recursively make every property optional. */
5
+ export type DeepPartial<T> = {
6
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
7
+ };
8
+ /** Flatten an intersection into a single object type for better IDE display. */
9
+ export type Prettify<T> = {
10
+ [K in keyof T]: T[K];
11
+ } & {};
12
+ /** Require at least one property from T. */
13
+ export type AtLeastOne<T> = {
14
+ [K in keyof T]-?: Pick<T, K> & Partial<Omit<T, K>>;
15
+ }[keyof T];
16
+ /** A tuple with at least one element. */
17
+ export type NonEmptyArray<T> = [T, ...T[]];
18
+ /** Narrows a readonly array to a NonEmptyArray. */
19
+ export declare const isNonEmptyArray: <T>(array: readonly T[]) => array is NonEmptyArray<T>;
20
+ /** Split an array into chunks of at most `size` elements. */
21
+ export declare const chunk: <T>(array: readonly T[], size: number) => T[][];
22
+ /**
23
+ * Remove duplicate items. When `key` is provided, uniqueness is determined
24
+ * by the return value of the key function; otherwise strict equality is used.
25
+ */
26
+ export declare const dedupe: <T>(array: readonly T[], key?: (item: T) => unknown) => T[];
27
+ /** Group items by a string key. */
28
+ export declare const groupBy: <T>(array: readonly T[], key: (item: T) => string) => Record<string, T[]>;
29
+ /** Return a new sorted array based on a key function (ascending). */
30
+ export declare const sortBy: <T>(array: readonly T[], key: (item: T) => string | number) => T[];
31
+ //# sourceMappingURL=collections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collections.d.ts","sourceRoot":"","sources":["../src/collections.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,gDAAgD;AAChD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;KAC1B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAChE,CAAC;AAEF,gFAAgF;AAEhF,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAC;AAExD,4CAA4C;AAC5C,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI;KACzB,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CACnD,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX,yCAAyC;AACzC,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;AAM3C,mDAAmD;AACnD,eAAO,MAAM,eAAe,GAAI,CAAC,EAC/B,OAAO,SAAS,CAAC,EAAE,KAClB,KAAK,IAAI,aAAa,CAAC,CAAC,CAAqB,CAAC;AAMjD,6DAA6D;AAC7D,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,OAAO,SAAS,CAAC,EAAE,EAAE,MAAM,MAAM,KAAG,CAAC,EAAE,EAS/D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EACtB,OAAO,SAAS,CAAC,EAAE,EACnB,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,KACzB,CAAC,EAcH,CAAC;AAEF,mCAAmC;AACnC,eAAO,MAAM,OAAO,GAAI,CAAC,EACvB,OAAO,SAAS,CAAC,EAAE,EACnB,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,KACvB,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAOpB,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,MAAM,GAAI,CAAC,EACtB,OAAO,SAAS,CAAC,EAAE,EACnB,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,KAChC,CAAC,EAQA,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Collection utilities and type helpers for @ontrails/core.
3
+ */
4
+ // ---------------------------------------------------------------------------
5
+ // Guards
6
+ // ---------------------------------------------------------------------------
7
+ /** Narrows a readonly array to a NonEmptyArray. */
8
+ export const isNonEmptyArray = (array) => array.length > 0;
9
+ // ---------------------------------------------------------------------------
10
+ // Collection functions
11
+ // ---------------------------------------------------------------------------
12
+ /** Split an array into chunks of at most `size` elements. */
13
+ export const chunk = (array, size) => {
14
+ if (size < 1) {
15
+ throw new RangeError('chunk size must be >= 1');
16
+ }
17
+ const result = [];
18
+ for (let i = 0; i < array.length; i += size) {
19
+ result.push(array.slice(i, i + size));
20
+ }
21
+ return result;
22
+ };
23
+ /**
24
+ * Remove duplicate items. When `key` is provided, uniqueness is determined
25
+ * by the return value of the key function; otherwise strict equality is used.
26
+ */
27
+ export const dedupe = (array, key) => {
28
+ if (!key) {
29
+ return [...new Set(array)];
30
+ }
31
+ const seen = new Set();
32
+ const result = [];
33
+ for (const item of array) {
34
+ const k = key(item);
35
+ if (!seen.has(k)) {
36
+ seen.add(k);
37
+ result.push(item);
38
+ }
39
+ }
40
+ return result;
41
+ };
42
+ /** Group items by a string key. */
43
+ export const groupBy = (array, key) => {
44
+ const groups = {};
45
+ for (const item of array) {
46
+ const k = key(item);
47
+ (groups[k] ??= []).push(item);
48
+ }
49
+ return groups;
50
+ };
51
+ /** Return a new sorted array based on a key function (ascending). */
52
+ export const sortBy = (array, key) => [...array].toSorted((a, b) => {
53
+ const ka = key(a);
54
+ const kb = key(b);
55
+ if (typeof ka === 'number' && typeof kb === 'number') {
56
+ return ka - kb;
57
+ }
58
+ return String(ka).localeCompare(String(kb));
59
+ });
60
+ //# sourceMappingURL=collections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collections.js","sourceRoot":"","sources":["../src/collections.ts"],"names":[],"mappings":"AAAA;;GAEG;AAuBH,8EAA8E;AAC9E,SAAS;AACT,8EAA8E;AAE9E,mDAAmD;AACnD,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,KAAmB,EACQ,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AAEjD,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E,6DAA6D;AAC7D,MAAM,CAAC,MAAM,KAAK,GAAG,CAAI,KAAmB,EAAE,IAAY,EAAS,EAAE;IACnE,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACb,MAAM,IAAI,UAAU,CAAC,yBAAyB,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,MAAM,GAAU,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,KAAmB,EACnB,GAA0B,EACrB,EAAE;IACP,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAW,CAAC;IAChC,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,mCAAmC;AACnC,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,KAAmB,EACnB,GAAwB,EACH,EAAE;IACvB,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,qEAAqE;AACrE,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,KAAmB,EACnB,GAAiC,EAC5B,EAAE,CACP,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;IAC3B,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAClB,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAClB,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;QACrD,OAAO,EAAE,GAAG,EAAE,CAAC;IACjB,CAAC;IACD,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { TrailContext } from './types.js';
2
+ /**
3
+ * Create a TrailContext with sensible defaults.
4
+ *
5
+ * - `requestId` defaults to `Bun.randomUUIDv7()` (sortable v7 UUID)
6
+ * - `signal` defaults to a fresh, non-aborted `AbortSignal`
7
+ * - All other fields come from `overrides`
8
+ */
9
+ export declare const createTrailContext: (overrides?: Partial<TrailContext>) => TrailContext;
10
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,OAAO,CAAC,YAAY,CAAC,KAChC,YAMD,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Create a TrailContext with sensible defaults.
3
+ *
4
+ * - `requestId` defaults to `Bun.randomUUIDv7()` (sortable v7 UUID)
5
+ * - `signal` defaults to a fresh, non-aborted `AbortSignal`
6
+ * - All other fields come from `overrides`
7
+ */
8
+ export const createTrailContext = (overrides) => ({
9
+ cwd: process.cwd(),
10
+ env: process.env,
11
+ requestId: Bun.randomUUIDv7(),
12
+ signal: new AbortController().signal,
13
+ ...overrides,
14
+ });
15
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,SAAiC,EACnB,EAAE,CAAC,CAAC;IAClB,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;IAClB,GAAG,EAAE,OAAO,CAAC,GAAyC;IACtD,SAAS,EAAE,GAAG,CAAC,YAAY,EAAE;IAC7B,MAAM,EAAE,IAAI,eAAe,EAAE,CAAC,MAAM;IACpC,GAAG,SAAS;CACb,CAAC,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Schema-driven field derivation for @ontrails/core
3
+ *
4
+ * Introspects Zod v4 schemas to produce a surface-agnostic Field[] descriptor
5
+ * that UI layers (CLI prompts, web forms, etc.) can consume.
6
+ */
7
+ import type { z } from 'zod';
8
+ /** A surface-agnostic field descriptor derived from a Zod schema. */
9
+ export interface Field {
10
+ readonly name: string;
11
+ readonly type: 'string' | 'number' | 'boolean' | 'enum' | 'multiselect';
12
+ readonly label: string;
13
+ readonly required: boolean;
14
+ readonly default?: unknown | undefined;
15
+ readonly options?: readonly {
16
+ value: string;
17
+ label?: string | undefined;
18
+ hint?: string | undefined;
19
+ }[] | undefined;
20
+ }
21
+ /** Per-field overrides supplied by trail authors. */
22
+ export interface FieldOverride {
23
+ readonly label?: string | undefined;
24
+ readonly message?: string | undefined;
25
+ readonly hint?: string | undefined;
26
+ readonly options?: readonly {
27
+ value: string;
28
+ label: string;
29
+ hint?: string | undefined;
30
+ }[] | undefined;
31
+ }
32
+ export declare const deriveFields: (schema: z.ZodType, overrides?: Record<string, FieldOverride>) => Field[];
33
+ //# sourceMappingURL=derive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAM7B,qEAAqE;AACrE,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,aAAa,CAAC;IACxE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,OAAO,CAAC,EACb,SAAS;QACP,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC3B,EAAE,GACH,SAAS,CAAC;CACf;AAED,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EACb,SAAS;QACP,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC3B,EAAE,GACH,SAAS,CAAC;CACf;AAmKD,eAAO,MAAM,YAAY,GACvB,QAAQ,CAAC,CAAC,OAAO,EACjB,YAAY,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,KACxC,KAAK,EAeP,CAAC"}