@ontrails/core 0.2.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 (86) hide show
  1. package/CHANGELOG.md +849 -0
  2. package/README.md +190 -0
  3. package/package.json +36 -0
  4. package/src/activation-provenance.ts +116 -0
  5. package/src/activation-source-compatibility.ts +430 -0
  6. package/src/activation-source-derivation.ts +227 -0
  7. package/src/activation-source.ts +93 -0
  8. package/src/blob-ref.ts +90 -0
  9. package/src/branded.ts +135 -0
  10. package/src/collections.ts +99 -0
  11. package/src/compose-batch.ts +69 -0
  12. package/src/compose-schema.ts +36 -0
  13. package/src/context.ts +66 -0
  14. package/src/derive.ts +485 -0
  15. package/src/detours.ts +8 -0
  16. package/src/diagnostics.ts +21 -0
  17. package/src/draft.ts +350 -0
  18. package/src/entity.ts +346 -0
  19. package/src/error-rendering.ts +87 -0
  20. package/src/errors.ts +483 -0
  21. package/src/execute.ts +1577 -0
  22. package/src/fetch.ts +138 -0
  23. package/src/fire.ts +1172 -0
  24. package/src/glob.ts +81 -0
  25. package/src/guards.ts +37 -0
  26. package/src/index.ts +704 -0
  27. package/src/internal/fork-ctx.ts +69 -0
  28. package/src/layer-field-rendering.ts +193 -0
  29. package/src/layer.ts +81 -0
  30. package/src/observe.ts +361 -0
  31. package/src/path-scope.ts +66 -0
  32. package/src/path-security.ts +98 -0
  33. package/src/patterns/bulk.ts +16 -0
  34. package/src/patterns/change.ts +12 -0
  35. package/src/patterns/date-range.ts +12 -0
  36. package/src/patterns/index.ts +8 -0
  37. package/src/patterns/pagination.ts +22 -0
  38. package/src/patterns/progress.ts +13 -0
  39. package/src/patterns/sorting.ts +14 -0
  40. package/src/patterns/status.ts +11 -0
  41. package/src/patterns/timestamps.ts +12 -0
  42. package/src/permits.ts +12 -0
  43. package/src/queue.ts +163 -0
  44. package/src/redaction/index.ts +3 -0
  45. package/src/redaction/patterns.ts +50 -0
  46. package/src/redaction/redactor.ts +178 -0
  47. package/src/resilience.ts +234 -0
  48. package/src/resource-config.ts +804 -0
  49. package/src/resource.ts +194 -0
  50. package/src/result.ts +212 -0
  51. package/src/run.ts +76 -0
  52. package/src/runtime-builtins.ts +69 -0
  53. package/src/schedule-runtime.ts +689 -0
  54. package/src/schedule.ts +326 -0
  55. package/src/serialization.ts +265 -0
  56. package/src/sha256.ts +136 -0
  57. package/src/signal-diagnostics.ts +633 -0
  58. package/src/signal-ref.ts +111 -0
  59. package/src/signal.ts +104 -0
  60. package/src/store/accessor-protocol.ts +56 -0
  61. package/src/store/index.ts +4 -0
  62. package/src/structured-examples.ts +248 -0
  63. package/src/surface-derivation.ts +91 -0
  64. package/src/surface-filter.ts +101 -0
  65. package/src/surface-overlay.ts +694 -0
  66. package/src/surface-versioning.ts +42 -0
  67. package/src/topo.ts +835 -0
  68. package/src/tracing.ts +346 -0
  69. package/src/trail-id-glob.ts +15 -0
  70. package/src/trail.ts +1351 -0
  71. package/src/trails/derive-trail.ts +835 -0
  72. package/src/trails/index.ts +9 -0
  73. package/src/trails/ingest.ts +152 -0
  74. package/src/trails-db.ts +212 -0
  75. package/src/transport-error-map.ts +163 -0
  76. package/src/type-utils.ts +87 -0
  77. package/src/types.ts +300 -0
  78. package/src/validate-established-topo.ts +73 -0
  79. package/src/validate-topo.ts +725 -0
  80. package/src/validation.ts +330 -0
  81. package/src/version-marker.ts +716 -0
  82. package/src/version-resolution.ts +308 -0
  83. package/src/version-runtime.ts +120 -0
  84. package/src/webhook.ts +461 -0
  85. package/src/workspace.ts +244 -0
  86. package/src/zod-wrappers.ts +72 -0
package/src/fetch.ts ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Fetch utilities for @ontrails/core
3
+ *
4
+ * Wraps the standard fetch API, mapping errors and HTTP status codes
5
+ * to the TrailsError taxonomy.
6
+ */
7
+
8
+ import {
9
+ AuthError,
10
+ CancelledError,
11
+ ConflictError,
12
+ InternalError,
13
+ NetworkError,
14
+ NotFoundError,
15
+ PermissionError,
16
+ RateLimitError,
17
+ TimeoutError,
18
+ ValidationError,
19
+ } from './errors.js';
20
+ import { Result } from './result.js';
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Internal helpers (defined before usage)
24
+ // ---------------------------------------------------------------------------
25
+
26
+ const toError = (err: unknown): Error =>
27
+ err instanceof Error ? err : new Error(String(err));
28
+
29
+ const parseRetryAfter = (header: string | null): number | undefined => {
30
+ if (!header) {
31
+ return undefined;
32
+ }
33
+ const seconds = Number(header);
34
+ if (Number.isFinite(seconds) && seconds > 0) {
35
+ return seconds;
36
+ }
37
+ // Try parsing as HTTP-date
38
+ const date = Date.parse(header);
39
+ if (!Number.isNaN(date)) {
40
+ const delta = Math.ceil((date - Date.now()) / 1000);
41
+ return delta > 0 ? delta : undefined;
42
+ }
43
+ return undefined;
44
+ };
45
+
46
+ const mapFetchError = (err: unknown): Error => {
47
+ if (err instanceof DOMException && err.name === 'AbortError') {
48
+ return new CancelledError('Request was aborted', { cause: toError(err) });
49
+ }
50
+
51
+ // TypeError is thrown for network failures in the fetch spec
52
+ if (err instanceof TypeError) {
53
+ return new NetworkError('Network request failed', { cause: err });
54
+ }
55
+
56
+ return new NetworkError('Network request failed', {
57
+ cause: toError(err),
58
+ });
59
+ };
60
+
61
+ type StatusMapper = (
62
+ context: Record<string, unknown>,
63
+ response: Response
64
+ ) => Error;
65
+
66
+ const statusMappers: Record<number, StatusMapper> = {
67
+ 401: (ctx) => new AuthError('Unauthorized', { context: ctx }),
68
+ 403: (ctx) => new PermissionError('Forbidden', { context: ctx }),
69
+ 404: (ctx) => new NotFoundError('Not found', { context: ctx }),
70
+ 429: (ctx, response) => {
71
+ const retryAfter = parseRetryAfter(response.headers.get('retry-after'));
72
+ const opts: { context: Record<string, unknown>; retryAfter?: number } = {
73
+ context: ctx,
74
+ };
75
+ if (retryAfter !== undefined) {
76
+ opts.retryAfter = retryAfter;
77
+ }
78
+ return new RateLimitError('Rate limited', opts);
79
+ },
80
+ 500: (ctx) => new InternalError('Internal server error', { context: ctx }),
81
+ 502: (ctx) => new NetworkError('Bad gateway', { context: ctx }),
82
+ 504: (ctx) => new TimeoutError('Gateway timeout', { context: ctx }),
83
+ };
84
+
85
+ /** Map 4xx status codes not in the explicit mapper to appropriate error types. */
86
+ const mapClientError = (
87
+ status: number,
88
+ context: Record<string, unknown>
89
+ ): Error => {
90
+ if (status === 400 || status === 422) {
91
+ return new ValidationError(`Validation error (${status})`, { context });
92
+ }
93
+ if (status === 409) {
94
+ return new ConflictError(`Conflict (${status})`, { context });
95
+ }
96
+ return new InternalError(`HTTP error (${status})`, { context });
97
+ };
98
+
99
+ const mapStatusCode = (response: Response): Error => {
100
+ const context = { status: response.status, url: response.url };
101
+ const mapper = statusMappers[response.status];
102
+ if (mapper) {
103
+ return mapper(context, response);
104
+ }
105
+ if (response.status >= 500) {
106
+ return new InternalError(`Server error (${response.status})`, { context });
107
+ }
108
+ return mapClientError(response.status, context);
109
+ };
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // fromFetch
113
+ // ---------------------------------------------------------------------------
114
+
115
+ /**
116
+ * Wrap a fetch call in a Result, mapping failures to TrailsError subclasses.
117
+ *
118
+ * Network errors become NetworkError. Abort signals become CancelledError.
119
+ * HTTP error status codes map to the appropriate error category.
120
+ */
121
+ export const fromFetch = async (
122
+ input: string | URL | Request,
123
+ init?: RequestInit
124
+ ): Promise<Result<Response, Error>> => {
125
+ let response: Response;
126
+
127
+ try {
128
+ response = await fetch(input, init);
129
+ } catch (error) {
130
+ return Result.err(mapFetchError(error));
131
+ }
132
+
133
+ if (response.ok) {
134
+ return Result.ok(response);
135
+ }
136
+
137
+ return Result.err(mapStatusCode(response));
138
+ };