@evolu/common 8.0.0-next.4 → 8.0.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 (237) hide show
  1. package/dist/src/Array.d.ts +279 -173
  2. package/dist/src/Array.d.ts.map +1 -1
  3. package/dist/src/Array.js +105 -101
  4. package/dist/src/Assert.d.ts +44 -44
  5. package/dist/src/Assert.d.ts.map +1 -1
  6. package/dist/src/Assert.js +44 -32
  7. package/dist/src/BigInt.d.ts +6 -3
  8. package/dist/src/BigInt.d.ts.map +1 -1
  9. package/dist/src/BigInt.js +5 -2
  10. package/dist/src/Brand.d.ts +27 -40
  11. package/dist/src/Brand.d.ts.map +1 -1
  12. package/dist/src/Buffer.d.ts +27 -11
  13. package/dist/src/Buffer.d.ts.map +1 -1
  14. package/dist/src/Cache.d.ts +9 -4
  15. package/dist/src/Cache.d.ts.map +1 -1
  16. package/dist/src/Cache.js +8 -3
  17. package/dist/src/Callbacks.d.ts +30 -19
  18. package/dist/src/Callbacks.d.ts.map +1 -1
  19. package/dist/src/Console.d.ts +145 -66
  20. package/dist/src/Console.d.ts.map +1 -1
  21. package/dist/src/Console.js +91 -43
  22. package/dist/src/Crypto.d.ts +85 -42
  23. package/dist/src/Crypto.d.ts.map +1 -1
  24. package/dist/src/Crypto.js +44 -20
  25. package/dist/src/Eq.d.ts +87 -21
  26. package/dist/src/Eq.d.ts.map +1 -1
  27. package/dist/src/Eq.js +67 -19
  28. package/dist/src/Error.d.ts +5 -3
  29. package/dist/src/Error.d.ts.map +1 -1
  30. package/dist/src/Error.js +16 -0
  31. package/dist/src/Function.d.ts +119 -58
  32. package/dist/src/Function.d.ts.map +1 -1
  33. package/dist/src/Function.js +86 -43
  34. package/dist/src/Http.d.ts +394 -0
  35. package/dist/src/Http.d.ts.map +1 -0
  36. package/dist/src/Http.js +102 -0
  37. package/dist/src/Identicon.d.ts +16 -7
  38. package/dist/src/Identicon.d.ts.map +1 -1
  39. package/dist/src/Identicon.js +14 -5
  40. package/dist/src/LeakDetector.d.ts +88 -0
  41. package/dist/src/LeakDetector.d.ts.map +1 -0
  42. package/dist/src/LeakDetector.js +86 -0
  43. package/dist/src/LockManager.d.ts +18 -9
  44. package/dist/src/LockManager.d.ts.map +1 -1
  45. package/dist/src/LockManager.js +7 -8
  46. package/dist/src/Lookup.d.ts +43 -13
  47. package/dist/src/Lookup.d.ts.map +1 -1
  48. package/dist/src/Lookup.js +28 -8
  49. package/dist/src/Microtask.d.ts +1 -1
  50. package/dist/src/Microtask.d.ts.map +1 -1
  51. package/dist/src/Microtask.js +3 -7
  52. package/dist/src/Number.d.ts +49 -13
  53. package/dist/src/Number.d.ts.map +1 -1
  54. package/dist/src/Number.js +34 -5
  55. package/dist/src/Object.d.ts +160 -40
  56. package/dist/src/Object.d.ts.map +1 -1
  57. package/dist/src/Object.js +92 -46
  58. package/dist/src/Option.d.ts +10 -7
  59. package/dist/src/Option.d.ts.map +1 -1
  60. package/dist/src/Option.js +9 -6
  61. package/dist/src/Order.d.ts +21 -21
  62. package/dist/src/Order.d.ts.map +1 -1
  63. package/dist/src/Order.js +18 -21
  64. package/dist/src/Platform.d.ts +31 -0
  65. package/dist/src/Platform.d.ts.map +1 -1
  66. package/dist/src/Platform.js +98 -0
  67. package/dist/src/Random.d.ts +32 -17
  68. package/dist/src/Random.d.ts.map +1 -1
  69. package/dist/src/Random.js +12 -8
  70. package/dist/src/Redacted.d.ts +43 -29
  71. package/dist/src/Redacted.d.ts.map +1 -1
  72. package/dist/src/Redacted.js +14 -3
  73. package/dist/src/Ref.d.ts +11 -3
  74. package/dist/src/Ref.d.ts.map +1 -1
  75. package/dist/src/Ref.js +2 -2
  76. package/dist/src/RefCount.d.ts +2 -6
  77. package/dist/src/RefCount.d.ts.map +1 -1
  78. package/dist/src/RefCount.js +33 -90
  79. package/dist/src/Relation.d.ts +70 -5
  80. package/dist/src/Relation.d.ts.map +1 -1
  81. package/dist/src/Relation.js +77 -2
  82. package/dist/src/Resource.d.ts +590 -155
  83. package/dist/src/Resource.d.ts.map +1 -1
  84. package/dist/src/Resource.js +592 -343
  85. package/dist/src/Result.d.ts +838 -191
  86. package/dist/src/Result.d.ts.map +1 -1
  87. package/dist/src/Result.js +163 -102
  88. package/dist/src/Schedule.d.ts +684 -231
  89. package/dist/src/Schedule.d.ts.map +1 -1
  90. package/dist/src/Schedule.js +766 -295
  91. package/dist/src/Set.d.ts +89 -40
  92. package/dist/src/Set.d.ts.map +1 -1
  93. package/dist/src/Set.js +50 -29
  94. package/dist/src/Sqlite.d.ts +29 -72
  95. package/dist/src/Sqlite.d.ts.map +1 -1
  96. package/dist/src/Sqlite.js +41 -46
  97. package/dist/src/StackTrace.d.ts +40 -0
  98. package/dist/src/StackTrace.d.ts.map +1 -0
  99. package/dist/src/StackTrace.js +140 -0
  100. package/dist/src/Store.d.ts +5 -4
  101. package/dist/src/Store.d.ts.map +1 -1
  102. package/dist/src/Store.js +5 -4
  103. package/dist/src/String.d.ts +7 -1
  104. package/dist/src/String.d.ts.map +1 -1
  105. package/dist/src/String.js +20 -7
  106. package/dist/src/Task.d.ts +3572 -1770
  107. package/dist/src/Task.d.ts.map +1 -1
  108. package/dist/src/Task.js +1879 -1274
  109. package/dist/src/Test.d.ts +17 -125
  110. package/dist/src/Test.d.ts.map +1 -1
  111. package/dist/src/Test.js +17 -68
  112. package/dist/src/Time.d.ts +108 -49
  113. package/dist/src/Time.d.ts.map +1 -1
  114. package/dist/src/Time.js +172 -73
  115. package/dist/src/Type.d.ts +3542 -1926
  116. package/dist/src/Type.d.ts.map +1 -1
  117. package/dist/src/Type.js +3070 -1784
  118. package/dist/src/Types.d.ts +200 -108
  119. package/dist/src/Types.d.ts.map +1 -1
  120. package/dist/src/Types.js +39 -8
  121. package/dist/src/WebSocket.d.ts +48 -23
  122. package/dist/src/WebSocket.d.ts.map +1 -1
  123. package/dist/src/WebSocket.js +15 -6
  124. package/dist/src/Worker.d.ts +94 -36
  125. package/dist/src/Worker.d.ts.map +1 -1
  126. package/dist/src/Worker.js +58 -24
  127. package/dist/src/index.d.ts +55 -52
  128. package/dist/src/index.d.ts.map +1 -1
  129. package/dist/src/index.js +3 -1
  130. package/dist/src/intl/cs.d.ts +94 -0
  131. package/dist/src/intl/cs.d.ts.map +1 -0
  132. package/dist/src/intl/cs.js +220 -0
  133. package/dist/src/intl/index.d.ts +7 -0
  134. package/dist/src/intl/index.d.ts.map +1 -0
  135. package/dist/src/intl/index.js +6 -0
  136. package/dist/src/local-first/Db.d.ts +12 -8
  137. package/dist/src/local-first/Db.d.ts.map +1 -1
  138. package/dist/src/local-first/Db.js +109 -132
  139. package/dist/src/local-first/Error.d.ts +4 -4
  140. package/dist/src/local-first/Evolu.d.ts +338 -92
  141. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  142. package/dist/src/local-first/Evolu.js +47 -55
  143. package/dist/src/local-first/LocalAuth.d.ts +4 -4
  144. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  145. package/dist/src/local-first/Owner.d.ts +61 -24
  146. package/dist/src/local-first/Owner.d.ts.map +1 -1
  147. package/dist/src/local-first/Owner.js +45 -8
  148. package/dist/src/local-first/Protocol.d.ts +44 -31
  149. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  150. package/dist/src/local-first/Protocol.js +36 -13
  151. package/dist/src/local-first/Query.d.ts +147 -62
  152. package/dist/src/local-first/Query.d.ts.map +1 -1
  153. package/dist/src/local-first/Query.js +112 -58
  154. package/dist/src/local-first/Relay.d.ts +44 -14
  155. package/dist/src/local-first/Relay.d.ts.map +1 -1
  156. package/dist/src/local-first/Schema.d.ts +92 -41
  157. package/dist/src/local-first/Schema.d.ts.map +1 -1
  158. package/dist/src/local-first/Schema.js +25 -8
  159. package/dist/src/local-first/Shared.d.ts +23 -21
  160. package/dist/src/local-first/Shared.d.ts.map +1 -1
  161. package/dist/src/local-first/Shared.js +100 -57
  162. package/dist/src/local-first/Storage.d.ts +53 -32
  163. package/dist/src/local-first/Storage.d.ts.map +1 -1
  164. package/dist/src/local-first/Storage.js +194 -234
  165. package/dist/src/local-first/Timestamp.d.ts +21 -21
  166. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  167. package/dist/src/local-first/Timestamp.js +2 -2
  168. package/dist/src/local-first/index.d.ts +11 -11
  169. package/package.json +14 -13
  170. package/src/Array.ts +306 -205
  171. package/src/Assert.ts +54 -69
  172. package/src/BigInt.ts +6 -3
  173. package/src/Brand.ts +27 -40
  174. package/src/Buffer.ts +28 -12
  175. package/src/Cache.ts +9 -4
  176. package/src/Callbacks.ts +30 -19
  177. package/src/Console.ts +156 -84
  178. package/src/Crypto.ts +83 -41
  179. package/src/Eq.ts +95 -22
  180. package/src/Error.ts +8 -4
  181. package/src/Function.ts +167 -59
  182. package/src/Http.ts +549 -0
  183. package/src/Identicon.ts +16 -7
  184. package/src/LeakDetector.ts +182 -0
  185. package/src/LockManager.ts +43 -39
  186. package/src/Lookup.ts +62 -28
  187. package/src/Microtask.ts +19 -23
  188. package/src/Number.ts +64 -12
  189. package/src/Object.ts +173 -49
  190. package/src/Option.ts +10 -7
  191. package/src/Order.ts +23 -21
  192. package/src/Platform.ts +136 -0
  193. package/src/Polyfills.ts +6 -12
  194. package/src/Random.ts +32 -19
  195. package/src/Redacted.ts +44 -30
  196. package/src/Ref.ts +14 -6
  197. package/src/RefCount.ts +38 -80
  198. package/src/Relation.ts +202 -10
  199. package/src/Resource.ts +1199 -591
  200. package/src/Result.ts +930 -271
  201. package/src/Schedule.ts +859 -343
  202. package/src/Set.ts +89 -40
  203. package/src/Sqlite.ts +144 -138
  204. package/src/StackTrace.ts +230 -0
  205. package/src/Store.ts +8 -7
  206. package/src/String.ts +22 -5
  207. package/src/Task.ts +5101 -2832
  208. package/src/Test.ts +20 -168
  209. package/src/Time.ts +293 -90
  210. package/src/Type.ts +10034 -4117
  211. package/src/Types.ts +227 -110
  212. package/src/WebSocket.ts +64 -36
  213. package/src/Worker.ts +180 -72
  214. package/src/index.ts +55 -52
  215. package/src/intl/cs.ts +388 -0
  216. package/src/intl/index.ts +7 -0
  217. package/src/local-first/Db.ts +152 -179
  218. package/src/local-first/Error.ts +4 -4
  219. package/src/local-first/Evolu.ts +495 -250
  220. package/src/local-first/LocalAuth.ts +6 -12
  221. package/src/local-first/Owner.ts +58 -21
  222. package/src/local-first/Protocol.ts +49 -31
  223. package/src/local-first/Query.ts +146 -61
  224. package/src/local-first/Relay.ts +51 -21
  225. package/src/local-first/Schema.ts +109 -51
  226. package/src/local-first/Shared.ts +266 -226
  227. package/src/local-first/Storage.ts +266 -264
  228. package/src/local-first/Timestamp.ts +25 -18
  229. package/src/local-first/index.ts +11 -11
  230. package/dist/src/Skiplist.d.ts +0 -28
  231. package/dist/src/Skiplist.d.ts.map +0 -1
  232. package/dist/src/Skiplist.js +0 -64
  233. package/dist/src/Tracer.d.ts +0 -48
  234. package/dist/src/Tracer.d.ts.map +0 -1
  235. package/dist/src/Tracer.js +0 -6
  236. package/src/Skiplist.ts +0 -108
  237. package/src/Tracer.ts +0 -54
@@ -0,0 +1,394 @@
1
+ /**
2
+ * Task-aware HTTP helpers.
3
+ *
4
+ * @module
5
+ */
6
+ import { type Result } from "./Result.ts";
7
+ import type { Task } from "./Task.ts";
8
+ import type { Typed } from "./Type.ts";
9
+ import type { Awaitable } from "./Types.ts";
10
+ /**
11
+ * WHATWG-fetch-compatible function used by {@link fetch}.
12
+ *
13
+ * A default dependency, replaceable at the composition root — for a
14
+ * platform-optimized fetch such as React Native's, a configured undici
15
+ * instance, or a test double. Implementations must be pre-bound; unbound
16
+ * `globalThis.fetch` throws in browsers.
17
+ *
18
+ * @group Fetch
19
+ */
20
+ export type NativeFetch = typeof globalThis.fetch;
21
+ /**
22
+ * Dependency wrapper for {@link NativeFetch}.
23
+ *
24
+ * @group Fetch
25
+ */
26
+ export interface NativeFetchDep {
27
+ readonly nativeFetch: NativeFetch;
28
+ }
29
+ /**
30
+ * Body mode for {@link fetch}.
31
+ *
32
+ * A mode selects a built-in consumer that reads the {@link Response} inside the
33
+ * fetch Task:
34
+ *
35
+ * - `"bytes"` — reads the body as a `Uint8Array<ArrayBuffer>`.
36
+ * - `"headers"` — cancels the body and returns {@link FetchResponse}. Pair it with
37
+ * `{ method: "HEAD" }` when the server should not send a body.
38
+ * - `"json"` — parses the body as JSON and returns `unknown`. Narrow or decode at
39
+ * the call site; there is deliberately no generic to cast through.
40
+ * - `"text"` — reads the body as a string.
41
+ *
42
+ * Body modes (`"bytes"`, `"json"`, `"text"`) return {@link FetchStatusError} for
43
+ * non-2xx responses. `"headers"` does not judge status, because the status is
44
+ * usually the value being asked for.
45
+ *
46
+ * @group Fetch
47
+ */
48
+ export type FetchMode = "text" | "json" | "bytes" | "headers";
49
+ /**
50
+ * Body-free {@link Response} view safe to return from {@link fetch}.
51
+ *
52
+ * {@link fetch} returns this view only after the native body has been drained,
53
+ * errored, or cancelled, so none of its usable members depends on the request
54
+ * signal. Body-reading members and `Response.clone` are omitted because they
55
+ * would be invalid after the fetch Task settles.
56
+ *
57
+ * This is a TypeScript contract, not a runtime security boundary: the runtime
58
+ * value can still be a native Response, and casts can access omitted members.
59
+ *
60
+ * @group Fetch
61
+ */
62
+ export type FetchResponse = Omit<Response, "arrayBuffer" | "blob" | "body" | "bodyUsed" | "bytes" | "clone" | "formData" | "json" | "text">;
63
+ /**
64
+ * Error returned by {@link fetch} body modes.
65
+ *
66
+ * The union distinguishes native request failures, non-2xx responses, and body
67
+ * read failures. Narrow on `type` when handling a specific case. The
68
+ * distinction matters for retries: {@link FetchBodyError} means a 2xx response
69
+ * failed during reading, so the server may have committed the request.
70
+ *
71
+ * @group Fetch
72
+ */
73
+ export type FetchError = FetchTransportError | FetchStatusError | FetchBodyError;
74
+ /**
75
+ * Error returned when the native fetch request fails before a response exists.
76
+ *
77
+ * @group Fetch
78
+ */
79
+ export interface FetchTransportError extends Typed<"FetchTransportError"> {
80
+ readonly error: unknown;
81
+ }
82
+ /**
83
+ * Error returned when a body mode receives a non-2xx response.
84
+ *
85
+ * The response body is drained as text before this error is returned. Draining
86
+ * buffers the whole error body; error responses are expected to be small. If
87
+ * the drain itself fails, `body` contains the read error.
88
+ *
89
+ * @group Fetch
90
+ */
91
+ export interface FetchStatusError extends Typed<"FetchStatusError"> {
92
+ readonly response: FetchResponse;
93
+ readonly body: Result<string, unknown>;
94
+ }
95
+ /**
96
+ * Error returned when a body mode cannot read or decode a 2xx response.
97
+ *
98
+ * This includes mid-stream network failures, decoding failures, and — for the
99
+ * `"json"` mode — empty bodies such as 204 responses, which are not JSON. Do
100
+ * not use `"json"` for endpoints that return no content.
101
+ *
102
+ * @group Fetch
103
+ */
104
+ export interface FetchBodyError extends Typed<"FetchBodyError"> {
105
+ readonly response: FetchResponse;
106
+ readonly error: unknown;
107
+ }
108
+ /**
109
+ * Consumes a native {@link Response} before {@link fetch} settles.
110
+ *
111
+ * The callback runs inside the fetch Task, while the request signal is still
112
+ * alive. Return a Result error for expected domain failures. Throwing or
113
+ * rejecting with a non-abort error is a defect, like any other Task body.
114
+ *
115
+ * Consumers take values, not dependencies; close over anything they need. A
116
+ * consumer that needs its own timeout, retries, or child Tasks has outgrown
117
+ * being a consumer: fetch the body with a mode and compose Tasks on the plain
118
+ * value, or write a Task that owns the whole request.
119
+ *
120
+ * Consumers do not have to normalize abort. {@link fetch} rethrows Evolu
121
+ * {@link AbortError}, normalizes host abort errors after the Run aborts, and
122
+ * treats an Err returned after abort as abort control flow — so a consumer's
123
+ * `try`/`catch` around a body read cannot accidentally turn an abort into a
124
+ * domain error. An Ok returned after abort is kept; only errors are presumed to
125
+ * be abort in disguise.
126
+ *
127
+ * The Response and anything derived from its live body, such as a reader, must
128
+ * not escape the consumer. Consume to plain values before returning.
129
+ *
130
+ * @group Fetch
131
+ */
132
+ export type FetchConsume<T, E = never> = (response: Response) => Awaitable<Result<T, E>>;
133
+ /**
134
+ * Fetches a resource and consumes the {@link Response} inside the Task, so the
135
+ * body is read while the request signal is still alive.
136
+ *
137
+ * The request runs through {@link NativeFetchDep | run.deps.nativeFetch}.
138
+ * Because native fetch is a default dependency, platforms and tests can replace
139
+ * it without changing call sites.
140
+ *
141
+ * With a {@link FetchMode}, non-2xx responses return {@link FetchStatusError}
142
+ * (except `"headers"`, which reports status as a value) and unreadable bodies
143
+ * return {@link FetchBodyError}. With a {@link FetchConsume} callback, native
144
+ * status semantics apply: HTTP error statuses resolve, and the consumer decides
145
+ * how to interpret the status and body.
146
+ *
147
+ * `signal` is not accepted in init because abort is controlled by the current
148
+ * Run.
149
+ *
150
+ * Aborting the Run aborts the underlying request, any response that arrives
151
+ * after abort, and any in-progress body read. Abort is represented as
152
+ * {@link AbortError}, not FetchError: `run(fetch(...))` rejects with AbortError,
153
+ * and `run.abortable(fetch(...))` returns it as an {@link Err}.
154
+ *
155
+ * Some runtimes reject aborted fetches with their own error instead of
156
+ * `signal.reason`. This wrapper normalizes abort rejections from native fetch,
157
+ * built-in body reads, and consumer callbacks back to the Run's AbortError.
158
+ *
159
+ * `fetch` owns request lifetime and Response containment. It does not transform
160
+ * requests or interpret app protocols beyond the built-in modes. Use Task
161
+ * helpers for resilience, app helpers for app conventions, a replacement
162
+ * {@link NativeFetch} for request-wide behavior (base URLs, auth, logging), and
163
+ * consumers for response interpretation.
164
+ *
165
+ * ### Composing fetch
166
+ *
167
+ * Resilience is ordinary Task composition: wrap `fetch(url, "json")` in
168
+ * {@link timeout}, then in {@link retry}.
169
+ *
170
+ * ```ts
171
+ * import {
172
+ * createRun,
173
+ * exponential,
174
+ * fetch,
175
+ * retry,
176
+ * take,
177
+ * timeout,
178
+ * type NativeFetch,
179
+ * } from "@evolu/common";
180
+ *
181
+ * const fetchWithRetry = (url: string) =>
182
+ * retry(
183
+ * timeout(fetch(url, "json"), "30s"),
184
+ * take(2)(exponential("100ms")),
185
+ * );
186
+ *
187
+ * let requestCount = 0;
188
+ * const nativeFetch: NativeFetch = () => {
189
+ * requestCount++;
190
+ * return Promise.resolve(
191
+ * requestCount === 1
192
+ * ? new Response("Try again", { status: 503 })
193
+ * : new Response('{"name":"Ada"}'),
194
+ * );
195
+ * };
196
+ * await using run = createRun({ nativeFetch });
197
+ *
198
+ * expectOk(await run(fetchWithRetry("/api/user")), { name: "Ada" });
199
+ * ```
200
+ *
201
+ * App conventions belong in small app-owned helpers. For example, posting JSON
202
+ * is native `init` plus two conventions worth centralizing — the content-type
203
+ * header and the stringify:
204
+ *
205
+ * ```ts
206
+ * import {
207
+ * createRun,
208
+ * fetch,
209
+ * type FetchError,
210
+ * type NativeFetch,
211
+ * type Task,
212
+ * } from "@evolu/common";
213
+ *
214
+ * const postJson = (
215
+ * url: string,
216
+ * data: unknown,
217
+ * ): Task<unknown, FetchError> =>
218
+ * fetch(url, "json", {
219
+ * method: "POST",
220
+ * headers: { "content-type": "application/json" },
221
+ * body: JSON.stringify(data),
222
+ * });
223
+ *
224
+ * const nativeFetch: NativeFetch = () =>
225
+ * Promise.resolve(new Response('{"id":"user-1"}'));
226
+ * await using run = createRun({ nativeFetch });
227
+ *
228
+ * expectOk(await run(postJson("/api/users", { name: "Ada" })), {
229
+ * id: "user-1",
230
+ * });
231
+ * ```
232
+ *
233
+ * Your app's version will grow your conventions — auth, envelopes, error
234
+ * mapping — which is why it belongs to the app, not to `fetch`.
235
+ *
236
+ * ### Intercepting requests
237
+ *
238
+ * Request-wide behavior belongs to a replacement {@link NativeFetch} installed
239
+ * at the composition root. This is the equivalent of interceptors or hooks in
240
+ * libraries that expose client instances.
241
+ *
242
+ * ```ts
243
+ * import { createRun, fetch, type NativeFetch } from "@evolu/common";
244
+ *
245
+ * const token = "secret-token";
246
+ * const baseUrl = "https://api.example.com/v1/";
247
+ * let interceptedRequest: Request | undefined;
248
+ * const baseFetch: NativeFetch = (input, init) => {
249
+ * interceptedRequest = new Request(input, init);
250
+ * return Promise.resolve(new Response("ok"));
251
+ * };
252
+ *
253
+ * const nativeFetch: NativeFetch = (input, init) => {
254
+ * const headers = new Headers(init?.headers);
255
+ * headers.set("authorization", `Bearer ${token}`);
256
+ *
257
+ * // Only string inputs are resolved against the base URL; URL and Request
258
+ * // inputs are passed through unchanged.
259
+ * const url =
260
+ * typeof input === "string" ? new URL(input, baseUrl) : input;
261
+ * return baseFetch(url, { ...init, headers });
262
+ * };
263
+ *
264
+ * await using run = createRun({ nativeFetch });
265
+ * expectOk(await run(fetch("users", "text")), "ok");
266
+ * expect({
267
+ * url: interceptedRequest?.url,
268
+ * authorization: interceptedRequest?.headers.get("authorization"),
269
+ * }).toEqual({
270
+ * url: "https://api.example.com/v1/users",
271
+ * authorization: "Bearer secret-token",
272
+ * });
273
+ * ```
274
+ *
275
+ * ### Consuming responses
276
+ *
277
+ * Built-in modes handle common bodies. Specialized response interpretation
278
+ * belongs in a consumer. Typed decoders, response envelopes, streaming, and
279
+ * custom status semantics can be built on top without changing `fetch`.
280
+ *
281
+ * ```ts
282
+ * import {
283
+ * createRun,
284
+ * fetch,
285
+ * ok,
286
+ * type FetchTransportError,
287
+ * type NativeFetch,
288
+ * type Task,
289
+ * } from "@evolu/common";
290
+ *
291
+ * const nativeFetch: NativeFetch = (input) =>
292
+ * Promise.resolve(
293
+ * String(input).endsWith("/metadata")
294
+ * ? new Response(null, {
295
+ * status: 204,
296
+ * headers: { "cache-control": "max-age=60" },
297
+ * })
298
+ * : new Response('{"name":"Ada"}'),
299
+ * );
300
+ * await using run = createRun({ nativeFetch });
301
+ *
302
+ * const user = await run(fetch("/api/user", "json"));
303
+ * const metadata = fetch("/api/user/metadata", (response) =>
304
+ * ok({
305
+ * status: response.status,
306
+ * cache: response.headers.get("cache-control"),
307
+ * }),
308
+ * );
309
+ * expectTypeOf(metadata).toEqualTypeOf<
310
+ * Task<{ status: number; cache: string | null }, FetchTransportError>
311
+ * >();
312
+ * expectOk(user, { name: "Ada" });
313
+ * expectOk(await run(metadata), { status: 204, cache: "max-age=60" });
314
+ * ```
315
+ *
316
+ * ### Aborting fetch
317
+ *
318
+ * Abort follows the standard Task rules: a Fiber from `run(fetch(...))` rejects
319
+ * with {@link AbortError}, and `run.abortable(fetch(...))` returns it as a
320
+ * Result error.
321
+ *
322
+ * ```ts
323
+ * import {
324
+ * AbortError,
325
+ * createRun,
326
+ * fetch,
327
+ * type NativeFetch,
328
+ * } from "@evolu/common";
329
+ *
330
+ * const nativeFetch: NativeFetch = (_input, init) =>
331
+ * new Promise<Response>((_resolve, reject) => {
332
+ * const signal = init?.signal;
333
+ * if (!signal) throw new Error("Missing signal");
334
+ * signal.addEventListener("abort", () => reject(signal.reason), {
335
+ * once: true,
336
+ * });
337
+ * });
338
+ * await using run = createRun({ nativeFetch });
339
+ *
340
+ * const fiber = run.abortable(fetch("/api/user", "json"));
341
+ * fiber.abort();
342
+ * const result = await fiber;
343
+ *
344
+ * expect(!result.ok && AbortError.is(result.error)).toBe(true);
345
+ * ```
346
+ *
347
+ * @group Fetch
348
+ */
349
+ export declare function fetch(input: RequestInfo | URL, mode: "text", init?: Omit<RequestInit, "signal">): Task<string, FetchError>;
350
+ export declare function fetch(input: RequestInfo | URL, mode: "json", init?: Omit<RequestInit, "signal">): Task<unknown, FetchError>;
351
+ export declare function fetch(input: RequestInfo | URL, mode: "bytes", init?: Omit<RequestInit, "signal">): Task<Uint8Array<ArrayBuffer>, FetchError>;
352
+ export declare function fetch(input: RequestInfo | URL, mode: "headers", init?: Omit<RequestInit, "signal">): Task<FetchResponse, FetchTransportError>;
353
+ export declare function fetch<T, E = never>(input: RequestInfo | URL, consume: FetchConsume<T, E>, init?: Omit<RequestInit, "signal">): Task<T, FetchTransportError | E>;
354
+ /**
355
+ * Test {@link NativeFetch} that records calls and serves queued handlers.
356
+ *
357
+ * Each call shifts the next handler. Calling with an empty queue throws, so a
358
+ * test never silently reaches an unplanned request.
359
+ *
360
+ * @group Testing
361
+ */
362
+ export interface TestNativeFetch extends NativeFetch {
363
+ readonly calls: ReadonlyArray<TestNativeFetchCall>;
364
+ readonly handle: (handler: TestNativeFetchHandler) => void;
365
+ }
366
+ /**
367
+ * Recorded {@link TestNativeFetch} call.
368
+ *
369
+ * @group Testing
370
+ */
371
+ export interface TestNativeFetchCall {
372
+ readonly input: RequestInfo | URL;
373
+ readonly init: RequestInit | undefined;
374
+ }
375
+ /**
376
+ * Handler for one {@link TestNativeFetch} call.
377
+ *
378
+ * @group Testing
379
+ */
380
+ export type TestNativeFetchHandler = (input: RequestInfo | URL, init: RequestInit | undefined) => Awaitable<Response>;
381
+ /**
382
+ * Creates {@link TestNativeFetch}.
383
+ *
384
+ * @group Testing
385
+ */
386
+ export declare const testCreateNativeFetch: (...handlers: ReadonlyArray<TestNativeFetchHandler>) => TestNativeFetch;
387
+ /**
388
+ * Creates a Response body for {@link NativeFetch} tests that errors on first
389
+ * read.
390
+ *
391
+ * @group Testing
392
+ */
393
+ export declare const testCreateNativeFetchErroringBody: (error?: unknown) => ReadableStream<Uint8Array>;
394
+ //# sourceMappingURL=Http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Http.d.ts","sourceRoot":"","sources":["../../src/Http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Task-aware HTTP helpers.
3
+ *
4
+ * @module
5
+ */
6
+ import { err, ok, tryAsync } from "./Result.js";
7
+ export function fetch(input, modeOrConsume, init) {
8
+ return async (run) => {
9
+ const { signal } = run;
10
+ // Abort control flow wins over step errors, normalizing host abort errors
11
+ // from native fetch, body reads, body cancel, and consumers.
12
+ const orAbort = (error) => {
13
+ signal.throwIfAborted();
14
+ return error;
15
+ };
16
+ const fetched = await tryAsync(() => run.deps.nativeFetch(input, { ...init, signal }), orAbort);
17
+ let result;
18
+ if (!fetched.ok) {
19
+ result = err({ type: "FetchTransportError", error: fetched.error });
20
+ }
21
+ else {
22
+ const response = fetched.value;
23
+ // A response that arrives after abort is not processed: cancel its body
24
+ // best-effort, then let abort win before any mode or consumer runs.
25
+ if (signal.aborted) {
26
+ await tryAsync(() => response.body?.cancel());
27
+ signal.throwIfAborted();
28
+ }
29
+ if (typeof modeOrConsume === "function") {
30
+ const consumed = await tryAsync(() => modeOrConsume(response), orAbort);
31
+ // A consumer throw is a defect once orAbort has ruled out abort.
32
+ if (!consumed.ok)
33
+ throw consumed.error;
34
+ result = consumed.value;
35
+ }
36
+ else if (modeOrConsume === "headers") {
37
+ // The head is already a value; body cancel failures are irrelevant.
38
+ await tryAsync(() => response.body?.cancel(), orAbort);
39
+ result = ok(response);
40
+ }
41
+ else if (!response.ok) {
42
+ result = err({
43
+ type: "FetchStatusError",
44
+ response,
45
+ // Error bodies are diagnostics; text preserves proxy/HTML/plain responses across modes.
46
+ body: await tryAsync(() => response.text(), orAbort),
47
+ });
48
+ }
49
+ else {
50
+ const body = await tryAsync(() => {
51
+ switch (modeOrConsume) {
52
+ case "text":
53
+ return response.text();
54
+ case "json":
55
+ return response.json();
56
+ case "bytes":
57
+ return response.bytes();
58
+ }
59
+ }, orAbort);
60
+ result = body.ok
61
+ ? body
62
+ : err({ type: "FetchBodyError", response, error: body.error });
63
+ }
64
+ }
65
+ // An Err produced after abort is treated as abort control flow.
66
+ if (!result.ok)
67
+ signal.throwIfAborted();
68
+ return result;
69
+ };
70
+ }
71
+ /**
72
+ * Creates {@link TestNativeFetch}.
73
+ *
74
+ * @group Testing
75
+ */
76
+ export const testCreateNativeFetch = (...handlers) => {
77
+ const calls = [];
78
+ const handlerQueue = [...handlers];
79
+ return Object.assign((input, init) => {
80
+ calls.push({ input, init });
81
+ const handler = handlerQueue.shift();
82
+ if (!handler)
83
+ throw new Error("Unexpected NativeFetch call");
84
+ return Promise.resolve(handler(input, init));
85
+ }, {
86
+ calls,
87
+ handle: (handler) => {
88
+ handlerQueue.push(handler);
89
+ },
90
+ });
91
+ };
92
+ /**
93
+ * Creates a Response body for {@link NativeFetch} tests that errors on first
94
+ * read.
95
+ *
96
+ * @group Testing
97
+ */
98
+ export const testCreateNativeFetchErroringBody = (error = new Error("stream failed")) => new ReadableStream({
99
+ pull: (controller) => {
100
+ controller.error(error);
101
+ },
102
+ });
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import type { Brand } from "./Brand.js";
7
- import { Id } from "./Type.js";
6
+ import type { Brand } from "./Brand.ts";
7
+ import { Id } from "./Type.ts";
8
8
  /**
9
9
  * SVG string representing a visual identicon for an {@link Id}, created with
10
10
  * {@link createIdenticon}.
@@ -27,13 +27,22 @@ export type IdenticonStyle = "github" | "quadrant" | "gradient" | "sutnar";
27
27
  * ### Example
28
28
  *
29
29
  * ```ts
30
+ * import { createIdFromString, createIdenticon } from "@evolu/common";
31
+ *
32
+ * const id = createIdFromString("identicon-example");
30
33
  * const svg = createIdenticon(id);
31
- * const quadrantStyle = createIdenticon(id, "quadrant");
32
- * const gradientStyle = createIdenticon(id, "gradient");
33
- * const sutnarStyle = createIdenticon(id, "sutnar");
34
+ * const styles = ["quadrant", "gradient", "sutnar"] as const;
35
+ * const alternativeSvgs = styles.map((style) =>
36
+ * createIdenticon(id, style),
37
+ * );
38
+ *
39
+ * // Branded IDs work too.
40
+ * const todoId = createIdFromString<"Todo">("todo-1");
41
+ * const todoSvg = createIdenticon(todoId);
34
42
  *
35
- * // Works with branded IDs
36
- * const ownerSvg = createIdenticon(ownerId);
43
+ * expect(svg.startsWith("<svg")).toBe(true);
44
+ * expect(new Set([svg, ...alternativeSvgs]).size).toBe(4);
45
+ * expect(todoSvg.startsWith("<svg")).toBe(true);
37
46
  * ```
38
47
  */
39
48
  export declare const createIdenticon: (id: Id, style?: IdenticonStyle) => Identicon;
@@ -1 +1 @@
1
- {"version":3,"file":"Identicon.d.ts","sourceRoot":"","sources":["../../src/Identicon.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,EAAE,EAAe,MAAM,WAAW,CAAC;AAE5C;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;AAEpD,+BAA+B;AAC/B,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,eAAe,GAC1B,IAAI,EAAE,EACN,QAAO,cAAyB,KAC/B,SA4JF,CAAC"}
1
+ {"version":3,"file":"Identicon.d.ts","sourceRoot":"","sources":["../../src/Identicon.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,EAAE,EAAe,MAAM,WAAW,CAAC;AAE5C;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;AAEpD,+BAA+B;AAC/B,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,eAAe,OACtB,EAAE,UACC,cAAc,KACpB,SA4JF,CAAC"}
@@ -20,13 +20,22 @@ import { Id, idToIdBytes } from "./Type.js";
20
20
  * ### Example
21
21
  *
22
22
  * ```ts
23
+ * import { createIdFromString, createIdenticon } from "@evolu/common";
24
+ *
25
+ * const id = createIdFromString("identicon-example");
23
26
  * const svg = createIdenticon(id);
24
- * const quadrantStyle = createIdenticon(id, "quadrant");
25
- * const gradientStyle = createIdenticon(id, "gradient");
26
- * const sutnarStyle = createIdenticon(id, "sutnar");
27
+ * const styles = ["quadrant", "gradient", "sutnar"] as const;
28
+ * const alternativeSvgs = styles.map((style) =>
29
+ * createIdenticon(id, style),
30
+ * );
31
+ *
32
+ * // Branded IDs work too.
33
+ * const todoId = createIdFromString<"Todo">("todo-1");
34
+ * const todoSvg = createIdenticon(todoId);
27
35
  *
28
- * // Works with branded IDs
29
- * const ownerSvg = createIdenticon(ownerId);
36
+ * expect(svg.startsWith("<svg")).toBe(true);
37
+ * expect(new Set([svg, ...alternativeSvgs]).size).toBe(4);
38
+ * expect(todoSvg.startsWith("<svg")).toBe(true);
30
39
  * ```
31
40
  */
32
41
  export const createIdenticon = (id, style = "github") => {
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Development-time detection of leaked handles via garbage collection.
3
+ *
4
+ * @module
5
+ */
6
+ import { type ConsoleDep } from "./Console.ts";
7
+ /**
8
+ * Detects handles that are garbage-collected while still considered held.
9
+ *
10
+ * A handle such as a Lease must be explicitly cleaned up. Nothing in JavaScript
11
+ * enforces that, so a handle whose last reference is dropped without cleanup
12
+ * leaks silently. Garbage collection proves the leak: once the handle is
13
+ * unreachable, no code can ever clean it up. A LeakDetector observes collection
14
+ * with `FinalizationRegistry` and warns when the collected handle was still
15
+ * held — a proven leak with zero false positives, pointing at the tracking
16
+ * site.
17
+ *
18
+ * Detection is best-effort: garbage collection timing is not deterministic, so
19
+ * a warning may come late or, in short-lived processes, never. It is a
20
+ * development canary, not a guarantee. Production uses
21
+ * {@link noopLeakDetector}.
22
+ */
23
+ export interface LeakDetector {
24
+ /**
25
+ * Starts tracking `target` until {@link LeakDetector.untrack | untrack} or
26
+ * garbage collection.
27
+ *
28
+ * When `target` is garbage-collected and {@link Leak.isLeaked} returns `true`,
29
+ * the leak is reported with the stack captured at this call. The leak must
30
+ * not reference `target`, or the target would never become unreachable.
31
+ */
32
+ readonly track: (target: object, leak: Leak, unregisterToken: object) => void;
33
+ /** Stops tracking the target registered with `unregisterToken`. */
34
+ readonly untrack: (unregisterToken: object) => void;
35
+ }
36
+ /** Describes a tracked handle for {@link LeakDetector.track}. */
37
+ export interface Leak {
38
+ /** Handle name used in the warning, for example `"Lease"`. */
39
+ readonly name: string;
40
+ /** Returns whether the collected handle was still considered held. */
41
+ readonly isLeaked: () => boolean;
42
+ }
43
+ /**
44
+ * Dependency wrapper for {@link LeakDetector}.
45
+ *
46
+ * @see {@link LeakDetector}
47
+ */
48
+ export interface LeakDetectorDep {
49
+ readonly leakDetector: LeakDetector;
50
+ }
51
+ /**
52
+ * Creates {@link LeakDetector} backed by `FinalizationRegistry`.
53
+ *
54
+ * Capturing a stack per track call is too expensive for production; use
55
+ * {@link noopLeakDetector} there.
56
+ */
57
+ export declare const createLeakDetector: (deps: ConsoleDep) => LeakDetector;
58
+ /** No-op {@link LeakDetector} for production. */
59
+ export declare const noopLeakDetector: LeakDetector;
60
+ /**
61
+ * Test {@link LeakDetector} with deterministic collection.
62
+ *
63
+ * @see {@link testCreateLeakDetector}
64
+ */
65
+ export interface TestLeakDetector extends LeakDetector {
66
+ /**
67
+ * Simulates garbage collection of all tracked targets.
68
+ *
69
+ * Reports every tracked handle that is still held and clears tracking.
70
+ * Returns the number of reported leaks.
71
+ */
72
+ readonly collect: () => number;
73
+ /** Returns the number of currently tracked targets. */
74
+ readonly getTrackedCount: (options?: {
75
+ name?: string;
76
+ }) => number;
77
+ }
78
+ /**
79
+ * Dependency wrapper for {@link TestLeakDetector}.
80
+ *
81
+ * @see {@link TestLeakDetector}
82
+ */
83
+ export interface TestLeakDetectorDep extends LeakDetectorDep {
84
+ readonly leakDetector: TestLeakDetector;
85
+ }
86
+ /** Creates {@link TestLeakDetector}. */
87
+ export declare const testCreateLeakDetector: (deps: ConsoleDep) => TestLeakDetector;
88
+ //# sourceMappingURL=LeakDetector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LeakDetector.d.ts","sourceRoot":"","sources":["../../src/LeakDetector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9E,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,iEAAiE;AACjE,MAAM,WAAW,IAAI;IACnB,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC;CAClC;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;CACrC;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,SAAU,UAAU,KAAG,YAoBrD,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,gBAAgB,EAAE,YAG9B,CAAC;AAyBF;;;;GAIG;AACH,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,MAAM,CAAC;IAE/B,uDAAuD;IACvD,QAAQ,CAAC,eAAe,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;CACnE;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;CACzC;AAED,wCAAwC;AACxC,eAAO,MAAM,sBAAsB,SAAU,UAAU,KAAG,gBAsCzD,CAAC"}