@evolu/common 8.9.0 → 8.11.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 (337) hide show
  1. package/dist/src/Bytes.d.ts +647 -0
  2. package/dist/src/Bytes.d.ts.map +1 -0
  3. package/dist/src/{Binary.js → Bytes.js} +266 -16
  4. package/dist/src/Config.d.ts +142 -0
  5. package/dist/src/Config.d.ts.map +1 -0
  6. package/dist/src/Config.js +181 -0
  7. package/dist/src/Console.d.ts +62 -7
  8. package/dist/src/Console.d.ts.map +1 -1
  9. package/dist/src/Console.js +20 -4
  10. package/dist/src/Crypto.d.ts +76 -4
  11. package/dist/src/Crypto.d.ts.map +1 -1
  12. package/dist/src/Crypto.js +55 -4
  13. package/dist/src/Error.d.ts +45 -0
  14. package/dist/src/Error.d.ts.map +1 -1
  15. package/dist/src/Error.js +69 -0
  16. package/dist/src/Fs.d.ts +376 -0
  17. package/dist/src/Fs.d.ts.map +1 -0
  18. package/dist/src/Fs.js +113 -0
  19. package/dist/src/Identicon.d.ts +2 -2
  20. package/dist/src/Identicon.js +2 -2
  21. package/dist/src/LeakDetector.d.ts +22 -3
  22. package/dist/src/LeakDetector.d.ts.map +1 -1
  23. package/dist/src/LeakDetector.js +12 -2
  24. package/dist/src/LockManager.d.ts +8 -0
  25. package/dist/src/LockManager.d.ts.map +1 -1
  26. package/dist/src/LockManager.js +6 -0
  27. package/dist/src/Number.d.ts +50 -7
  28. package/dist/src/Number.d.ts.map +1 -1
  29. package/dist/src/Number.js +47 -8
  30. package/dist/src/Object.d.ts +32 -0
  31. package/dist/src/Object.d.ts.map +1 -1
  32. package/dist/src/Object.js +46 -0
  33. package/dist/src/Platform.d.ts +47 -7
  34. package/dist/src/Platform.d.ts.map +1 -1
  35. package/dist/src/Platform.js +24 -5
  36. package/dist/src/Random.d.ts +25 -2
  37. package/dist/src/Random.d.ts.map +1 -1
  38. package/dist/src/Random.js +14 -2
  39. package/dist/src/Resource.d.ts +156 -1
  40. package/dist/src/Resource.d.ts.map +1 -1
  41. package/dist/src/Resource.js +201 -72
  42. package/dist/src/Schedule.d.ts +11 -10
  43. package/dist/src/Schedule.d.ts.map +1 -1
  44. package/dist/src/Schedule.js +1 -1
  45. package/dist/src/Sqlite.d.ts +132 -16
  46. package/dist/src/Sqlite.d.ts.map +1 -1
  47. package/dist/src/Sqlite.js +64 -10
  48. package/dist/src/Task.d.ts +15 -4
  49. package/dist/src/Task.d.ts.map +1 -1
  50. package/dist/src/Task.js +41 -15
  51. package/dist/src/Test.d.ts +9 -0
  52. package/dist/src/Test.d.ts.map +1 -1
  53. package/dist/src/Test.js +4 -0
  54. package/dist/src/Time.d.ts +179 -20
  55. package/dist/src/Time.d.ts.map +1 -1
  56. package/dist/src/Time.js +95 -6
  57. package/dist/src/Type.d.ts +3056 -1539
  58. package/dist/src/Type.d.ts.map +1 -1
  59. package/dist/src/Type.js +2548 -584
  60. package/dist/src/WebSocket.d.ts +164 -13
  61. package/dist/src/WebSocket.d.ts.map +1 -1
  62. package/dist/src/WebSocket.js +133 -24
  63. package/dist/src/Worker.d.ts +90 -8
  64. package/dist/src/Worker.d.ts.map +1 -1
  65. package/dist/src/Worker.js +28 -2
  66. package/dist/src/index.d.ts +9 -8
  67. package/dist/src/index.d.ts.map +1 -1
  68. package/dist/src/index.js +5 -4
  69. package/dist/src/intl/_en.d.ts +24 -1
  70. package/dist/src/intl/_en.d.ts.map +1 -1
  71. package/dist/src/intl/_en.js +20 -0
  72. package/dist/src/intl/ar.d.ts +24 -1
  73. package/dist/src/intl/ar.d.ts.map +1 -1
  74. package/dist/src/intl/ar.js +20 -0
  75. package/dist/src/intl/bn.d.ts +24 -1
  76. package/dist/src/intl/bn.d.ts.map +1 -1
  77. package/dist/src/intl/bn.js +20 -0
  78. package/dist/src/intl/ca.d.ts +24 -1
  79. package/dist/src/intl/ca.d.ts.map +1 -1
  80. package/dist/src/intl/ca.js +20 -0
  81. package/dist/src/intl/cs.d.ts +24 -1
  82. package/dist/src/intl/cs.d.ts.map +1 -1
  83. package/dist/src/intl/cs.js +20 -0
  84. package/dist/src/intl/da.d.ts +24 -1
  85. package/dist/src/intl/da.d.ts.map +1 -1
  86. package/dist/src/intl/da.js +20 -0
  87. package/dist/src/intl/de.d.ts +24 -1
  88. package/dist/src/intl/de.d.ts.map +1 -1
  89. package/dist/src/intl/de.js +20 -0
  90. package/dist/src/intl/el.d.ts +24 -1
  91. package/dist/src/intl/el.d.ts.map +1 -1
  92. package/dist/src/intl/el.js +20 -0
  93. package/dist/src/intl/es.d.ts +24 -1
  94. package/dist/src/intl/es.d.ts.map +1 -1
  95. package/dist/src/intl/es.js +20 -0
  96. package/dist/src/intl/fa.d.ts +24 -1
  97. package/dist/src/intl/fa.d.ts.map +1 -1
  98. package/dist/src/intl/fa.js +20 -0
  99. package/dist/src/intl/fi.d.ts +24 -1
  100. package/dist/src/intl/fi.d.ts.map +1 -1
  101. package/dist/src/intl/fi.js +20 -0
  102. package/dist/src/intl/fil.d.ts +24 -1
  103. package/dist/src/intl/fil.d.ts.map +1 -1
  104. package/dist/src/intl/fil.js +20 -0
  105. package/dist/src/intl/fr.d.ts +24 -1
  106. package/dist/src/intl/fr.d.ts.map +1 -1
  107. package/dist/src/intl/fr.js +20 -0
  108. package/dist/src/intl/he.d.ts +24 -1
  109. package/dist/src/intl/he.d.ts.map +1 -1
  110. package/dist/src/intl/he.js +20 -0
  111. package/dist/src/intl/hi.d.ts +24 -1
  112. package/dist/src/intl/hi.d.ts.map +1 -1
  113. package/dist/src/intl/hi.js +20 -0
  114. package/dist/src/intl/hr.d.ts +24 -1
  115. package/dist/src/intl/hr.d.ts.map +1 -1
  116. package/dist/src/intl/hr.js +20 -0
  117. package/dist/src/intl/hu.d.ts +22 -1
  118. package/dist/src/intl/hu.d.ts.map +1 -1
  119. package/dist/src/intl/hu.js +18 -0
  120. package/dist/src/intl/id.d.ts +24 -1
  121. package/dist/src/intl/id.d.ts.map +1 -1
  122. package/dist/src/intl/id.js +20 -0
  123. package/dist/src/intl/it.d.ts +24 -1
  124. package/dist/src/intl/it.d.ts.map +1 -1
  125. package/dist/src/intl/it.js +20 -0
  126. package/dist/src/intl/ja.d.ts +24 -1
  127. package/dist/src/intl/ja.d.ts.map +1 -1
  128. package/dist/src/intl/ja.js +20 -0
  129. package/dist/src/intl/ko.d.ts +24 -1
  130. package/dist/src/intl/ko.d.ts.map +1 -1
  131. package/dist/src/intl/ko.js +20 -0
  132. package/dist/src/intl/ml.d.ts +24 -1
  133. package/dist/src/intl/ml.d.ts.map +1 -1
  134. package/dist/src/intl/ml.js +20 -0
  135. package/dist/src/intl/mr.d.ts +24 -1
  136. package/dist/src/intl/mr.d.ts.map +1 -1
  137. package/dist/src/intl/mr.js +20 -0
  138. package/dist/src/intl/ms.d.ts +24 -1
  139. package/dist/src/intl/ms.d.ts.map +1 -1
  140. package/dist/src/intl/ms.js +20 -0
  141. package/dist/src/intl/nb.d.ts +22 -1
  142. package/dist/src/intl/nb.d.ts.map +1 -1
  143. package/dist/src/intl/nb.js +18 -0
  144. package/dist/src/intl/nl.d.ts +24 -1
  145. package/dist/src/intl/nl.d.ts.map +1 -1
  146. package/dist/src/intl/nl.js +20 -0
  147. package/dist/src/intl/pa.d.ts +24 -1
  148. package/dist/src/intl/pa.d.ts.map +1 -1
  149. package/dist/src/intl/pa.js +20 -0
  150. package/dist/src/intl/pl.d.ts +23 -0
  151. package/dist/src/intl/pl.d.ts.map +1 -1
  152. package/dist/src/intl/pl.js +20 -0
  153. package/dist/src/intl/pt-BR.d.ts +24 -1
  154. package/dist/src/intl/pt-BR.d.ts.map +1 -1
  155. package/dist/src/intl/pt-BR.js +20 -0
  156. package/dist/src/intl/pt.d.ts +24 -1
  157. package/dist/src/intl/pt.d.ts.map +1 -1
  158. package/dist/src/intl/pt.js +20 -0
  159. package/dist/src/intl/ro.d.ts +24 -1
  160. package/dist/src/intl/ro.d.ts.map +1 -1
  161. package/dist/src/intl/ro.js +20 -0
  162. package/dist/src/intl/sk.d.ts +24 -1
  163. package/dist/src/intl/sk.d.ts.map +1 -1
  164. package/dist/src/intl/sk.js +20 -0
  165. package/dist/src/intl/sl.d.ts +24 -1
  166. package/dist/src/intl/sl.d.ts.map +1 -1
  167. package/dist/src/intl/sl.js +20 -0
  168. package/dist/src/intl/sv.d.ts +24 -1
  169. package/dist/src/intl/sv.d.ts.map +1 -1
  170. package/dist/src/intl/sv.js +20 -0
  171. package/dist/src/intl/sw.d.ts +21 -0
  172. package/dist/src/intl/sw.d.ts.map +1 -1
  173. package/dist/src/intl/sw.js +18 -0
  174. package/dist/src/intl/ta.d.ts +24 -1
  175. package/dist/src/intl/ta.d.ts.map +1 -1
  176. package/dist/src/intl/ta.js +20 -0
  177. package/dist/src/intl/te.d.ts +24 -1
  178. package/dist/src/intl/te.d.ts.map +1 -1
  179. package/dist/src/intl/te.js +20 -0
  180. package/dist/src/intl/th.d.ts +24 -1
  181. package/dist/src/intl/th.d.ts.map +1 -1
  182. package/dist/src/intl/th.js +20 -0
  183. package/dist/src/intl/tr.d.ts +24 -1
  184. package/dist/src/intl/tr.d.ts.map +1 -1
  185. package/dist/src/intl/tr.js +20 -0
  186. package/dist/src/intl/uk.d.ts +80 -57
  187. package/dist/src/intl/uk.d.ts.map +1 -1
  188. package/dist/src/intl/uk.js +174 -149
  189. package/dist/src/intl/ur.d.ts +24 -1
  190. package/dist/src/intl/ur.d.ts.map +1 -1
  191. package/dist/src/intl/ur.js +20 -0
  192. package/dist/src/intl/vi.d.ts +24 -1
  193. package/dist/src/intl/vi.d.ts.map +1 -1
  194. package/dist/src/intl/vi.js +20 -0
  195. package/dist/src/intl/zh-CN.d.ts +24 -1
  196. package/dist/src/intl/zh-CN.d.ts.map +1 -1
  197. package/dist/src/intl/zh-CN.js +20 -0
  198. package/dist/src/intl/zh-TW.d.ts +24 -1
  199. package/dist/src/intl/zh-TW.d.ts.map +1 -1
  200. package/dist/src/intl/zh-TW.js +20 -0
  201. package/dist/src/local-first/Db.d.ts +52 -3
  202. package/dist/src/local-first/Db.d.ts.map +1 -1
  203. package/dist/src/local-first/Db.js +412 -137
  204. package/dist/src/local-first/Evolu.d.ts +336 -211
  205. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  206. package/dist/src/local-first/Evolu.js +102 -15
  207. package/dist/src/local-first/Owner.d.ts +13 -30
  208. package/dist/src/local-first/Owner.d.ts.map +1 -1
  209. package/dist/src/local-first/Owner.js +13 -30
  210. package/dist/src/local-first/Protocol.d.ts +95 -17
  211. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  212. package/dist/src/local-first/Protocol.js +119 -39
  213. package/dist/src/local-first/Query.d.ts +8 -15
  214. package/dist/src/local-first/Query.d.ts.map +1 -1
  215. package/dist/src/local-first/Schema.d.ts +345 -21
  216. package/dist/src/local-first/Schema.d.ts.map +1 -1
  217. package/dist/src/local-first/Schema.js +214 -17
  218. package/dist/src/local-first/Shared.d.ts +537 -22
  219. package/dist/src/local-first/Shared.d.ts.map +1 -1
  220. package/dist/src/local-first/Shared.js +1437 -234
  221. package/dist/src/local-first/Storage.d.ts +192 -14
  222. package/dist/src/local-first/Storage.d.ts.map +1 -1
  223. package/dist/src/local-first/Storage.js +82 -21
  224. package/dist/src/local-first/Timestamp.d.ts +392 -41
  225. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  226. package/dist/src/local-first/Timestamp.js +404 -82
  227. package/dist/src/local-first/index.d.ts +0 -1
  228. package/dist/src/local-first/index.d.ts.map +1 -1
  229. package/dist/src/local-first/index.js +0 -1
  230. package/package.json +1 -1
  231. package/src/Assert.test.ts +2 -5
  232. package/src/{Binary.test.ts → Bytes.test.ts} +286 -1
  233. package/src/{Binary.ts → Bytes.ts} +652 -21
  234. package/src/Config.test.ts +668 -0
  235. package/src/Config.ts +410 -0
  236. package/src/Console.ts +62 -7
  237. package/src/Crypto.ts +76 -4
  238. package/src/Eq.test.ts +2 -3
  239. package/src/Error.test.ts +76 -3
  240. package/src/Error.ts +71 -0
  241. package/src/Fs.test.ts +105 -0
  242. package/src/Fs.ts +488 -0
  243. package/src/Identicon.ts +2 -2
  244. package/src/LeakDetector.ts +22 -3
  245. package/src/LockManager.ts +8 -0
  246. package/src/Number.test.ts +82 -18
  247. package/src/Number.ts +76 -8
  248. package/src/Object.test.ts +139 -10
  249. package/src/Object.ts +49 -0
  250. package/src/Platform.ts +50 -8
  251. package/src/Random.ts +25 -2
  252. package/src/Resource.test.ts +837 -0
  253. package/src/Resource.ts +235 -15
  254. package/src/Schedule.test.ts +50 -12
  255. package/src/Schedule.ts +24 -14
  256. package/src/Sqlite.ts +138 -18
  257. package/src/Task.test.ts +189 -8
  258. package/src/Task.ts +56 -17
  259. package/src/Test.ts +9 -0
  260. package/src/Time.test.ts +82 -11
  261. package/src/Time.ts +246 -24
  262. package/src/Type.test.ts +3994 -1119
  263. package/src/Type.ts +7258 -3842
  264. package/src/Types.test.ts +4 -14
  265. package/src/WebSocket.ts +313 -40
  266. package/src/Worker.ts +90 -8
  267. package/src/index.ts +18 -7
  268. package/src/intl/_en.ts +70 -0
  269. package/src/intl/ar.ts +71 -0
  270. package/src/intl/bn.ts +70 -0
  271. package/src/intl/ca.ts +70 -0
  272. package/src/intl/cs.ts +70 -0
  273. package/src/intl/da.ts +70 -0
  274. package/src/intl/de.ts +70 -0
  275. package/src/intl/el.ts +70 -0
  276. package/src/intl/es.ts +70 -0
  277. package/src/intl/fa.ts +70 -0
  278. package/src/intl/fi.ts +70 -0
  279. package/src/intl/fil.ts +70 -0
  280. package/src/intl/fr.ts +70 -0
  281. package/src/intl/he.ts +70 -0
  282. package/src/intl/hi.ts +70 -0
  283. package/src/intl/hr.ts +70 -0
  284. package/src/intl/hu.ts +69 -0
  285. package/src/intl/id.ts +70 -0
  286. package/src/intl/intl.test.ts +819 -1
  287. package/src/intl/it.ts +70 -0
  288. package/src/intl/ja.ts +70 -0
  289. package/src/intl/ko.ts +68 -0
  290. package/src/intl/ml.ts +70 -0
  291. package/src/intl/mr.ts +70 -0
  292. package/src/intl/ms.ts +71 -0
  293. package/src/intl/nb.ts +69 -0
  294. package/src/intl/nl.ts +70 -0
  295. package/src/intl/pa.ts +70 -0
  296. package/src/intl/pl.ts +63 -0
  297. package/src/intl/pt-BR.ts +70 -0
  298. package/src/intl/pt.ts +71 -0
  299. package/src/intl/ro.ts +70 -0
  300. package/src/intl/sk.ts +71 -0
  301. package/src/intl/sl.ts +70 -0
  302. package/src/intl/sv.ts +70 -0
  303. package/src/intl/sw.ts +62 -0
  304. package/src/intl/ta.ts +70 -0
  305. package/src/intl/te.ts +70 -0
  306. package/src/intl/th.ts +68 -0
  307. package/src/intl/tr.ts +70 -0
  308. package/src/intl/uk.ts +228 -155
  309. package/src/intl/ur.ts +70 -0
  310. package/src/intl/vi.ts +70 -0
  311. package/src/intl/zh-CN.ts +68 -0
  312. package/src/intl/zh-TW.ts +68 -0
  313. package/src/local-first/Db.ts +644 -339
  314. package/src/local-first/Evolu.test.ts +686 -21
  315. package/src/local-first/Evolu.ts +450 -228
  316. package/src/local-first/Owner.ts +13 -30
  317. package/src/local-first/Protocol.test.ts +618 -11
  318. package/src/local-first/Protocol.ts +197 -73
  319. package/src/local-first/Query.ts +8 -15
  320. package/src/local-first/Schema.test.ts +143 -0
  321. package/src/local-first/Schema.ts +374 -24
  322. package/src/local-first/Shared.test.ts +7731 -559
  323. package/src/local-first/Shared.ts +2036 -267
  324. package/src/local-first/Storage.ts +219 -33
  325. package/src/local-first/Timestamp.test.ts +344 -70
  326. package/src/local-first/Timestamp.ts +435 -119
  327. package/src/local-first/index.ts +0 -1
  328. package/dist/src/Binary.d.ts +0 -254
  329. package/dist/src/Binary.d.ts.map +0 -1
  330. package/dist/src/local-first/Error.d.ts +0 -12
  331. package/dist/src/local-first/Error.d.ts.map +0 -1
  332. package/dist/src/local-first/Error.js +0 -6
  333. package/dist/src/local-first/LocalAuth.d.ts +0 -150
  334. package/dist/src/local-first/LocalAuth.d.ts.map +0 -1
  335. package/dist/src/local-first/LocalAuth.js +0 -179
  336. package/src/local-first/Error.ts +0 -17
  337. package/src/local-first/LocalAuth.ts +0 -457
package/src/Task.ts CHANGED
@@ -686,6 +686,7 @@ import {
686
686
  type RandomBytesDep,
687
687
  } from "./Crypto.ts";
688
688
  import { eqArraySameValue } from "./Eq.ts";
689
+ import type { defectToError } from "./Error.ts";
689
690
  import { constTrue, constVoid, identity } from "./Function.ts";
690
691
  import type { fetch, NativeFetch, NativeFetchDep } from "./Http.ts";
691
692
  import {
@@ -1462,10 +1463,20 @@ export interface Run<D = unknown> {
1462
1463
  callback: (abortError: AbortError) => void,
1463
1464
  ) => Disposable | null;
1464
1465
 
1465
- /** Returns the current {@link RunState} of this {@link Run}. */
1466
+ /**
1467
+ * Returns the current {@link RunState} of this {@link Run}.
1468
+ *
1469
+ * Abort requests are recorded before propagating to descendants. Observed
1470
+ * aborts are recorded before this Run's abort callbacks execute.
1471
+ */
1466
1472
  readonly getState: () => RunState;
1467
1473
 
1468
- /** Creates a memoized recursive {@link RunSnapshot} of the current Run tree. */
1474
+ /**
1475
+ * Creates a memoized recursive {@link RunSnapshot} of the current Run tree.
1476
+ *
1477
+ * Reflects abort requests and observations with the same timing as
1478
+ * {@link Run.getState}.
1479
+ */
1469
1480
  readonly snapshot: () => RunSnapshot;
1470
1481
 
1471
1482
  /**
@@ -2085,8 +2096,9 @@ export interface ReportDefectDep {
2085
2096
  * platforms without native global error reporting. Browser adapters use the
2086
2097
  * native
2087
2098
  * {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/reportError | reportError}
2088
- * API; other platform adapters can use platform-specific reporting and preserve
2089
- * nested panic defects as cause or detail data.
2099
+ * API, and other platform adapters can use platform-specific reporting.
2100
+ * Adapters report an `Error` from {@link defectToError}, which such reporting
2101
+ * shows readably.
2090
2102
  *
2091
2103
  * @group Run
2092
2104
  */
@@ -2481,6 +2493,7 @@ const createRunInternal = <D extends object>(
2481
2493
  : abortBehavior.abortMask;
2482
2494
 
2483
2495
  let state: RunState = runningRunState;
2496
+ let publishedState: RunState = state;
2484
2497
  let exit: RunExit | undefined;
2485
2498
  let snapshot: RunSnapshot | undefined;
2486
2499
 
@@ -2533,6 +2546,13 @@ const createRunInternal = <D extends object>(
2533
2546
  }
2534
2547
  };
2535
2548
 
2549
+ const publishState = (): void => {
2550
+ // A nested disposal may have published this state before the outer abort.
2551
+ if (publishedState === state) return;
2552
+ publishedState = state;
2553
+ emitEvent({ type: "StateChanged", state });
2554
+ };
2555
+
2536
2556
  // Reads abort reasons from the Run-owned controllers, which are only
2537
2557
  // aborted with AbortError.
2538
2558
  const currentAbort = (): RunAbortState["abort"] => ({
@@ -2542,17 +2562,10 @@ const createRunInternal = <D extends object>(
2542
2562
  : null,
2543
2563
  });
2544
2564
 
2545
- const commitState = (nextState: RunState): void => {
2546
- state = nextState;
2547
- emitEvent({ type: "StateChanged", state });
2548
- };
2549
-
2550
2565
  const requestAbort = (reason: AbortReason = explicitAbortReason): void => {
2551
2566
  if (requestController.signal.aborted) return;
2552
- const abortError = createAbortError(reason);
2553
- requestController.abort(abortError);
2554
- if (abortMask === abortableMask) signalController.abort(abortError);
2555
- commitState({ type: "Aborted", abort: currentAbort() });
2567
+ abortControllers(createAbortError(reason), abortMask === abortableMask);
2568
+ publishState();
2556
2569
  };
2557
2570
 
2558
2571
  // The first provided exit claims the Run exit; later exits are ignored. A
@@ -2569,7 +2582,8 @@ const createRunInternal = <D extends object>(
2569
2582
 
2570
2583
  const settle = (): RunExit => {
2571
2584
  exit ??= ok(ok());
2572
- commitState({ type: "Settled", abort: currentAbort(), exit });
2585
+ state = { type: "Settled", abort: currentAbort(), exit };
2586
+ publishState();
2573
2587
  return exit;
2574
2588
  };
2575
2589
 
@@ -2590,14 +2604,39 @@ const createRunInternal = <D extends object>(
2590
2604
 
2591
2605
  const abortError = exit?.ok === false ? exit.error : runDisposedAbortError;
2592
2606
 
2607
+ // If called from a local abort callback, let its abort operation finish
2608
+ // dispatching callbacks before publishing the state.
2593
2609
  const { aborted } = signalController.signal;
2594
- requestController.abort(abortError);
2595
- signalController.abort(abortError);
2596
- if (!aborted) commitState({ type: "Aborted", abort: currentAbort() });
2610
+ abortControllers(abortError, true);
2611
+ if (!aborted) publishState();
2597
2612
 
2598
2613
  return disposePromise;
2599
2614
  };
2600
2615
 
2616
+ const abortControllers = (abortError: AbortError, observe: boolean): void => {
2617
+ // Record the request before propagation invokes descendant callbacks.
2618
+ if (!requestController.signal.aborted) {
2619
+ state = {
2620
+ type: "Aborted",
2621
+ abort: { request: abortError.reason, observed: null },
2622
+ };
2623
+ requestController.abort(abortError);
2624
+ }
2625
+
2626
+ // A descendant callback may have disposed this Run reentrantly. Preserve
2627
+ // the first observed reason; otherwise record it before local callbacks.
2628
+ if (observe && !signalController.signal.aborted) {
2629
+ state = {
2630
+ type: "Aborted",
2631
+ abort: {
2632
+ request: (requestController.signal.reason as AbortError).reason,
2633
+ observed: abortError.reason,
2634
+ },
2635
+ };
2636
+ signalController.abort(abortError);
2637
+ }
2638
+ };
2639
+
2601
2640
  // Custom deps replace parent custom deps, so defaults must be picked from
2602
2641
  // the merged deps by key. `satisfies RunDefaultDeps` fails to compile when
2603
2642
  // a newly added required default dep is missing here; optional ones like
package/src/Test.ts CHANGED
@@ -38,6 +38,8 @@ import { createId, type Id } from "./Type.ts";
38
38
  * }
39
39
  * assertFalse(Reflect.has(globalThis, key));
40
40
  * ```
41
+ *
42
+ * @group Testing
41
43
  */
42
44
  export const testStubGlobal = (
43
45
  key: PropertyKey,
@@ -58,6 +60,11 @@ export const testStubGlobal = (
58
60
  return disposer;
59
61
  };
60
62
 
63
+ /**
64
+ * Deterministic id factory returned by {@link testCreateId}.
65
+ *
66
+ * @group Testing
67
+ */
61
68
  export type TestCreateId = <B extends string = never>() => [B] extends [never]
62
69
  ? Id
63
70
  : Id & Brand<B>;
@@ -96,6 +103,8 @@ export type TestCreateId = <B extends string = never>() => [B] extends [never]
96
103
  * assertEqual(replayCreateId(), callbackId);
97
104
  * assertType<typeof todoId, Id & Brand<"Todo">>();
98
105
  * ```
106
+ *
107
+ * @group Testing
99
108
  */
100
109
  export const testCreateId = (): TestCreateId => {
101
110
  const randomBytes = testCreateRandomBytes({
package/src/Time.test.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { afterEach, describe, it, mock } from "node:test";
2
2
  import {
3
3
  assertEqual,
4
+ assertErr,
5
+ assertOk,
4
6
  assertFalse,
5
7
  assertLength,
6
8
  assertSame,
@@ -37,19 +39,21 @@ import {
37
39
  saturateMillis,
38
40
  testCreateTime,
39
41
  } from "./Time.ts";
40
- import { assertType, type DateIso, NonNaNNumber } from "./Type.ts";
42
+ import {
43
+ localizeTypes,
44
+ object,
45
+ typeErrorToIssues,
46
+ type InferErrors,
47
+ assertType,
48
+ type DateIso,
49
+ NonNaNNumber,
50
+ } from "./Type.ts";
41
51
 
42
52
  const negativeMillisCause = {
43
53
  type: "NonNegative",
44
54
  value: -1,
45
55
  };
46
56
 
47
- const assertThrowsWithCause = (run: () => unknown, cause: unknown): void => {
48
- const error = assertThrowsInstanceOf(run, Error);
49
- assertEqual(error.message, "getOrThrow");
50
- assertEqual(error.cause, cause);
51
- };
52
-
53
57
  describe("Time", () => {
54
58
  afterEach(() => {
55
59
  mock.restoreAll();
@@ -218,14 +222,17 @@ describe("Time", () => {
218
222
  );
219
223
  mock.method(Date, "now", () => -1);
220
224
 
221
- assertThrowsWithCause(
225
+ const error = assertThrowsInstanceOf(
222
226
  () =>
223
227
  createTime().setTimeout(
224
228
  () => undefined,
225
229
  PositiveMillis.orThrow(2 ** 31),
226
230
  ),
227
- negativeMillisCause,
231
+ Error,
228
232
  );
233
+
234
+ assertEqual(error.message, "The value -1 must be non-negative (>= 0).");
235
+ assertEqual(error.cause, negativeMillisCause);
229
236
  assertEqual(setTimeout.mock.callCount(), 0);
230
237
  });
231
238
 
@@ -247,7 +254,10 @@ describe("Time", () => {
247
254
  );
248
255
  now = -1;
249
256
 
250
- assertThrowsWithCause(() => callbacks[0](), negativeMillisCause);
257
+ const error = assertThrowsInstanceOf(() => callbacks[0](), Error);
258
+
259
+ assertEqual(error.message, "The value -1 must be non-negative (>= 0).");
260
+ assertEqual(error.cause, negativeMillisCause);
251
261
  assertLength(callbacks, 1);
252
262
  });
253
263
 
@@ -445,7 +455,16 @@ describe("Time", () => {
445
455
  it("setTimeout rejects a deadline after maxMillis", () => {
446
456
  const time = testCreateTime({ startAt: maxMillis });
447
457
 
448
- assertThrowsWithCause(() => time.setTimeout(() => undefined, "1ms"), {
458
+ const error = assertThrowsInstanceOf(
459
+ () => time.setTimeout(() => undefined, "1ms"),
460
+ Error,
461
+ );
462
+
463
+ assertEqual(
464
+ error.message,
465
+ "The value 281474976710655 must be less than 281474976710655.",
466
+ );
467
+ assertEqual(error.cause, {
449
468
  type: "LessThan281474976710655",
450
469
  value: maxMillis + 1,
451
470
  max: maxMillis + 1,
@@ -727,6 +746,58 @@ describe("Time", () => {
727
746
  });
728
747
 
729
748
  describe("DurationLiteral", () => {
749
+ it("reports a dedicated error with the original validation failure", () => {
750
+ for (const value of ["60s", "", 1, null, undefined]) {
751
+ const result = DurationLiteral.fromUnknown(value);
752
+ assertErr(result);
753
+ assertType<typeof result.error.type, "DurationLiteral">();
754
+ assertSame(result.error.value, value);
755
+ assertEqual(result.error.cause.type, "Union");
756
+ assertEqual(result.error.cause.errors.length, 1);
757
+ }
758
+ const result = DurationLiteral.fromUnknown("60s", { errors: "all" });
759
+ assertErr(result);
760
+ assertEqual(result.error.cause.errors.length, 7);
761
+ assertEqual(
762
+ DurationLiteral.formatError(result.error),
763
+ 'The value "60s" is not a duration literal. Use a value such as "500ms" or "1.5s".',
764
+ );
765
+ assertType<
766
+ InferErrors<typeof DurationLiteral>["type"],
767
+ "DurationLiteral"
768
+ >();
769
+ assertOk(DurationLiteral.from("1.5s"), "1.5s");
770
+ assertEqual(DurationLiteral.to("1.5s"), "1.5s");
771
+ // @ts-expect-error DurationLiteral Input rejects "60s".
772
+ const _invalidInput: typeof DurationLiteral.Input = "60s";
773
+ });
774
+
775
+ it("localizes the named error after composition and preserves its path", async () => {
776
+ const { first } = localizeTypes(
777
+ { Value: DurationLiteral },
778
+ { first: { DurationLiteral: () => "First message." } },
779
+ );
780
+ const { second } = localizeTypes(
781
+ { Settings: object({ value: first.Value }) },
782
+ {
783
+ second: {
784
+ Object: () => "Object.",
785
+ DurationLiteral: () => "Second message.",
786
+ },
787
+ },
788
+ );
789
+ const result = second.Settings.fromUnknown({ value: "60s" });
790
+ assertErr(result);
791
+ assertEqual(second.Settings.formatError(result.error), "Second message.");
792
+ assertEqual(typeErrorToIssues(second.Settings, result.error), [
793
+ { path: ["value"], message: "Second message." },
794
+ ]);
795
+ assertEqual(
796
+ await second.Settings["~standard"].validate({ value: "60s" }),
797
+ { issues: [{ path: ["value"], message: "Second message." }] },
798
+ );
799
+ });
800
+
730
801
  it("valid durations", () => {
731
802
  // Milliseconds
732
803
  assertType<"1ms" extends DurationLiteral ? true : false, true>();