@livestore/utils 0.4.0-dev.9 → 0.5.0-dev.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 (316) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/NoopTracer.d.ts +7 -8
  3. package/dist/NoopTracer.d.ts.map +1 -1
  4. package/dist/NoopTracer.js +13 -15
  5. package/dist/NoopTracer.js.map +1 -1
  6. package/dist/NoopTracer.test.d.ts +2 -0
  7. package/dist/NoopTracer.test.d.ts.map +1 -0
  8. package/dist/NoopTracer.test.js +17 -0
  9. package/dist/NoopTracer.test.js.map +1 -0
  10. package/dist/binary.js +1 -1
  11. package/dist/binary.js.map +1 -1
  12. package/dist/browser/Opfs/Opfs.d.ts +201 -0
  13. package/dist/browser/Opfs/Opfs.d.ts.map +1 -0
  14. package/dist/browser/Opfs/Opfs.js +170 -0
  15. package/dist/browser/Opfs/Opfs.js.map +1 -0
  16. package/dist/browser/Opfs/debug-utils.d.ts +20 -0
  17. package/dist/browser/Opfs/debug-utils.d.ts.map +1 -0
  18. package/dist/browser/Opfs/debug-utils.js +95 -0
  19. package/dist/browser/Opfs/debug-utils.js.map +1 -0
  20. package/dist/browser/Opfs/mod.d.ts +4 -0
  21. package/dist/browser/Opfs/mod.d.ts.map +1 -0
  22. package/dist/browser/Opfs/mod.js +4 -0
  23. package/dist/browser/Opfs/mod.js.map +1 -0
  24. package/dist/browser/Opfs/utils.d.ts +71 -0
  25. package/dist/browser/Opfs/utils.d.ts.map +1 -0
  26. package/dist/browser/Opfs/utils.js +227 -0
  27. package/dist/browser/Opfs/utils.js.map +1 -0
  28. package/dist/browser/QuotaExceededError.d.ts +60 -0
  29. package/dist/browser/QuotaExceededError.d.ts.map +1 -0
  30. package/dist/browser/QuotaExceededError.js +2 -0
  31. package/dist/browser/QuotaExceededError.js.map +1 -0
  32. package/dist/browser/WebChannelBrowser.d.ts +22 -0
  33. package/dist/browser/WebChannelBrowser.d.ts.map +1 -0
  34. package/dist/browser/WebChannelBrowser.js +87 -0
  35. package/dist/browser/WebChannelBrowser.js.map +1 -0
  36. package/dist/browser/WebError.d.ts +389 -0
  37. package/dist/browser/WebError.d.ts.map +1 -0
  38. package/dist/browser/WebError.js +423 -0
  39. package/dist/browser/WebError.js.map +1 -0
  40. package/dist/browser/WebError.test.d.ts +2 -0
  41. package/dist/browser/WebError.test.d.ts.map +1 -0
  42. package/dist/browser/WebError.test.js +46 -0
  43. package/dist/browser/WebError.test.js.map +1 -0
  44. package/dist/browser/WebLock.d.ts.map +1 -0
  45. package/dist/{effect → browser}/WebLock.js +21 -21
  46. package/dist/browser/WebLock.js.map +1 -0
  47. package/dist/{browser.d.ts → browser/detect.d.ts} +1 -1
  48. package/dist/browser/detect.d.ts.map +1 -0
  49. package/dist/{browser.js → browser/detect.js} +7 -7
  50. package/dist/browser/detect.js.map +1 -0
  51. package/dist/browser/mod.d.ts +8 -0
  52. package/dist/browser/mod.d.ts.map +1 -0
  53. package/dist/browser/mod.js +8 -0
  54. package/dist/browser/mod.js.map +1 -0
  55. package/dist/cuid/cuid.browser.js +1 -1
  56. package/dist/cuid/cuid.browser.js.map +1 -1
  57. package/dist/cuid/cuid.node.js +1 -1
  58. package/dist/cuid/cuid.node.js.map +1 -1
  59. package/dist/effect/Debug.d.ts +38 -0
  60. package/dist/effect/Debug.d.ts.map +1 -0
  61. package/dist/effect/Debug.js +350 -0
  62. package/dist/effect/Debug.js.map +1 -0
  63. package/dist/effect/Effect.d.ts +91 -22
  64. package/dist/effect/Effect.d.ts.map +1 -1
  65. package/dist/effect/Effect.js +126 -38
  66. package/dist/effect/Effect.js.map +1 -1
  67. package/dist/effect/Effect.test.d.ts +2 -0
  68. package/dist/effect/Effect.test.d.ts.map +1 -0
  69. package/dist/effect/Effect.test.js +72 -0
  70. package/dist/effect/Effect.test.js.map +1 -0
  71. package/dist/effect/Error.d.ts +4 -6
  72. package/dist/effect/Error.d.ts.map +1 -1
  73. package/dist/effect/Error.js +1 -1
  74. package/dist/effect/Error.js.map +1 -1
  75. package/dist/effect/Logger.d.ts +0 -6
  76. package/dist/effect/Logger.d.ts.map +1 -1
  77. package/dist/effect/Logger.js +0 -32
  78. package/dist/effect/Logger.js.map +1 -1
  79. package/dist/effect/OtelTracer.d.ts +1 -1
  80. package/dist/effect/OtelTracer.d.ts.map +1 -1
  81. package/dist/effect/OtelTracer.js +2 -3
  82. package/dist/effect/OtelTracer.js.map +1 -1
  83. package/dist/effect/RpcClient.d.ts +7 -7
  84. package/dist/effect/RpcClient.d.ts.map +1 -1
  85. package/dist/effect/RpcClient.js +74 -54
  86. package/dist/effect/RpcClient.js.map +1 -1
  87. package/dist/effect/Schedule.d.ts +1 -1
  88. package/dist/effect/Schedule.d.ts.map +1 -1
  89. package/dist/effect/Schedule.js +2 -2
  90. package/dist/effect/Schedule.js.map +1 -1
  91. package/dist/effect/Schema/debug-diff.d.ts +1 -1
  92. package/dist/effect/Schema/debug-diff.d.ts.map +1 -1
  93. package/dist/effect/Schema/debug-diff.js +9 -10
  94. package/dist/effect/Schema/debug-diff.js.map +1 -1
  95. package/dist/effect/Schema/debug-diff.test.js +7 -4
  96. package/dist/effect/Schema/debug-diff.test.js.map +1 -1
  97. package/dist/effect/Schema/index.d.ts +28 -18
  98. package/dist/effect/Schema/index.d.ts.map +1 -1
  99. package/dist/effect/Schema/index.js +55 -28
  100. package/dist/effect/Schema/index.js.map +1 -1
  101. package/dist/effect/Stream.d.ts +13 -5
  102. package/dist/effect/Stream.d.ts.map +1 -1
  103. package/dist/effect/Stream.js +19 -12
  104. package/dist/effect/Stream.js.map +1 -1
  105. package/dist/effect/Stream.test.js +12 -12
  106. package/dist/effect/Stream.test.js.map +1 -1
  107. package/dist/effect/Subscribable.d.ts +9 -3
  108. package/dist/effect/Subscribable.d.ts.map +1 -1
  109. package/dist/effect/Subscribable.js +20 -35
  110. package/dist/effect/Subscribable.js.map +1 -1
  111. package/dist/effect/SubscriptionRef.d.ts +5 -6
  112. package/dist/effect/SubscriptionRef.d.ts.map +1 -1
  113. package/dist/effect/SubscriptionRef.js +2 -3
  114. package/dist/effect/SubscriptionRef.js.map +1 -1
  115. package/dist/effect/TaskTracing.d.ts +1 -1
  116. package/dist/effect/TaskTracing.d.ts.map +1 -1
  117. package/dist/effect/TaskTracing.js +12 -16
  118. package/dist/effect/TaskTracing.js.map +1 -1
  119. package/dist/effect/WebChannel/WebChannel.d.ts +11 -30
  120. package/dist/effect/WebChannel/WebChannel.d.ts.map +1 -1
  121. package/dist/effect/WebChannel/WebChannel.js +70 -125
  122. package/dist/effect/WebChannel/WebChannel.js.map +1 -1
  123. package/dist/effect/WebChannel/WebChannel.test.js +16 -16
  124. package/dist/effect/WebChannel/WebChannel.test.js.map +1 -1
  125. package/dist/effect/WebChannel/broadcastChannelWithAck.d.ts.map +1 -1
  126. package/dist/effect/WebChannel/broadcastChannelWithAck.js +20 -13
  127. package/dist/effect/WebChannel/broadcastChannelWithAck.js.map +1 -1
  128. package/dist/effect/WebChannel/common.d.ts +17 -18
  129. package/dist/effect/WebChannel/common.d.ts.map +1 -1
  130. package/dist/effect/WebChannel/common.js +7 -7
  131. package/dist/effect/WebChannel/common.js.map +1 -1
  132. package/dist/effect/WebSocket.d.ts +4 -6
  133. package/dist/effect/WebSocket.d.ts.map +1 -1
  134. package/dist/effect/WebSocket.js +17 -17
  135. package/dist/effect/WebSocket.js.map +1 -1
  136. package/dist/effect/WebSocket.test.js +2 -2
  137. package/dist/effect/WebSocket.test.js.map +1 -1
  138. package/dist/effect/mod.browser-build.test.d.ts +2 -0
  139. package/dist/effect/mod.browser-build.test.d.ts.map +1 -0
  140. package/dist/effect/mod.browser-build.test.js +18 -0
  141. package/dist/effect/mod.browser-build.test.js.map +1 -0
  142. package/dist/effect/mod.d.ts +30 -0
  143. package/dist/effect/mod.d.ts.map +1 -0
  144. package/dist/effect/mod.js +30 -0
  145. package/dist/effect/mod.js.map +1 -0
  146. package/dist/effect/spanEvent.d.ts +12 -0
  147. package/dist/effect/spanEvent.d.ts.map +1 -0
  148. package/dist/effect/spanEvent.js +12 -0
  149. package/dist/effect/spanEvent.js.map +1 -0
  150. package/dist/effect/spanEvent.test.d.ts +2 -0
  151. package/dist/effect/spanEvent.test.d.ts.map +1 -0
  152. package/dist/effect/spanEvent.test.js +79 -0
  153. package/dist/effect/spanEvent.test.js.map +1 -0
  154. package/dist/env.d.ts.map +1 -1
  155. package/dist/env.js +1 -1
  156. package/dist/env.js.map +1 -1
  157. package/dist/fast-deep-equal.d.ts.map +1 -1
  158. package/dist/fast-deep-equal.js +9 -10
  159. package/dist/fast-deep-equal.js.map +1 -1
  160. package/dist/global.d.ts +3 -0
  161. package/dist/global.d.ts.map +1 -1
  162. package/dist/global.js.map +1 -1
  163. package/dist/misc.d.ts +9 -1
  164. package/dist/misc.d.ts.map +1 -1
  165. package/dist/misc.js +12 -4
  166. package/dist/misc.js.map +1 -1
  167. package/dist/mod.d.ts +177 -20
  168. package/dist/mod.d.ts.map +1 -1
  169. package/dist/mod.js +159 -22
  170. package/dist/mod.js.map +1 -1
  171. package/dist/node/mod.d.ts +45 -6
  172. package/dist/node/mod.d.ts.map +1 -1
  173. package/dist/node/mod.js +51 -9
  174. package/dist/node/mod.js.map +1 -1
  175. package/dist/object/index.d.ts +0 -9
  176. package/dist/object/index.d.ts.map +1 -1
  177. package/dist/object/index.js +0 -9
  178. package/dist/object/index.js.map +1 -1
  179. package/dist/object/stringify-object.js +2 -2
  180. package/dist/object/stringify-object.js.map +1 -1
  181. package/dist/object/stringify-object.test.js.map +1 -1
  182. package/dist/qr.d.ts +38 -0
  183. package/dist/qr.d.ts.map +1 -0
  184. package/dist/qr.js +109 -0
  185. package/dist/qr.js.map +1 -0
  186. package/dist/set.js +1 -1
  187. package/dist/set.js.map +1 -1
  188. package/dist/string.d.ts +0 -2
  189. package/dist/string.d.ts.map +1 -1
  190. package/dist/string.js +0 -2
  191. package/dist/string.js.map +1 -1
  192. package/package.json +61 -55
  193. package/src/NoopTracer.test.ts +20 -0
  194. package/src/NoopTracer.ts +28 -37
  195. package/src/binary.ts +1 -1
  196. package/src/browser/Opfs/Opfs.ts +463 -0
  197. package/src/browser/Opfs/debug-utils.ts +154 -0
  198. package/src/browser/Opfs/mod.ts +3 -0
  199. package/src/browser/Opfs/utils.ts +297 -0
  200. package/src/browser/QuotaExceededError.ts +60 -0
  201. package/src/browser/WebChannelBrowser.ts +145 -0
  202. package/src/browser/WebError.test.ts +66 -0
  203. package/src/browser/WebError.ts +587 -0
  204. package/src/{effect → browser}/WebLock.ts +27 -28
  205. package/src/{browser.ts → browser/detect.ts} +6 -6
  206. package/src/browser/mod.ts +8 -0
  207. package/src/cuid/cuid.browser.ts +1 -1
  208. package/src/cuid/cuid.node.ts +1 -1
  209. package/src/effect/Debug.ts +477 -0
  210. package/src/effect/Effect.test.ts +117 -0
  211. package/src/effect/Effect.ts +174 -79
  212. package/src/effect/Error.ts +1 -1
  213. package/src/effect/Logger.ts +0 -48
  214. package/src/effect/OtelTracer.ts +2 -3
  215. package/src/effect/RpcClient.ts +85 -65
  216. package/src/effect/Schedule.ts +4 -5
  217. package/src/effect/Schema/debug-diff.test.ts +7 -7
  218. package/src/effect/Schema/debug-diff.ts +11 -12
  219. package/src/effect/Schema/index.ts +121 -57
  220. package/src/effect/Stream.test.ts +14 -13
  221. package/src/effect/Stream.ts +23 -16
  222. package/src/effect/Subscribable.ts +31 -42
  223. package/src/effect/SubscriptionRef.ts +9 -13
  224. package/src/effect/TaskTracing.ts +13 -16
  225. package/src/effect/WebChannel/WebChannel.test.ts +24 -24
  226. package/src/effect/WebChannel/WebChannel.ts +102 -192
  227. package/src/effect/WebChannel/broadcastChannelWithAck.ts +25 -15
  228. package/src/effect/WebChannel/common.ts +16 -17
  229. package/src/effect/WebSocket.test.ts +2 -2
  230. package/src/effect/WebSocket.ts +20 -16
  231. package/src/effect/mod.browser-build.test.ts +19 -0
  232. package/src/effect/{index.ts → mod.ts} +43 -69
  233. package/src/effect/spanEvent.test.ts +92 -0
  234. package/src/effect/spanEvent.ts +17 -0
  235. package/src/env.ts +2 -1
  236. package/src/fast-deep-equal.ts +9 -10
  237. package/src/global.ts +4 -2
  238. package/src/misc.ts +13 -5
  239. package/src/mod.ts +188 -32
  240. package/src/node/mod.ts +57 -12
  241. package/src/object/index.ts +0 -24
  242. package/src/object/stringify-object.test.ts +1 -0
  243. package/src/object/stringify-object.ts +2 -2
  244. package/src/qr.ts +125 -0
  245. package/src/set.ts +1 -1
  246. package/src/string.ts +0 -3
  247. package/dist/.tsbuildinfo.json +0 -1
  248. package/dist/browser.d.ts.map +0 -1
  249. package/dist/browser.js.map +0 -1
  250. package/dist/effect/BucketQueue.d.ts +0 -20
  251. package/dist/effect/BucketQueue.d.ts.map +0 -1
  252. package/dist/effect/BucketQueue.js +0 -34
  253. package/dist/effect/BucketQueue.js.map +0 -1
  254. package/dist/effect/Scheduler.d.ts +0 -4
  255. package/dist/effect/Scheduler.d.ts.map +0 -1
  256. package/dist/effect/Scheduler.js +0 -9
  257. package/dist/effect/Scheduler.js.map +0 -1
  258. package/dist/effect/ServiceContext.d.ts +0 -37
  259. package/dist/effect/ServiceContext.d.ts.map +0 -1
  260. package/dist/effect/ServiceContext.js +0 -55
  261. package/dist/effect/ServiceContext.js.map +0 -1
  262. package/dist/effect/WebLock.d.ts.map +0 -1
  263. package/dist/effect/WebLock.js.map +0 -1
  264. package/dist/effect/index.d.ts +0 -32
  265. package/dist/effect/index.d.ts.map +0 -1
  266. package/dist/effect/index.js +0 -35
  267. package/dist/effect/index.js.map +0 -1
  268. package/dist/guards.d.ts +0 -6
  269. package/dist/guards.d.ts.map +0 -1
  270. package/dist/guards.js +0 -6
  271. package/dist/guards.js.map +0 -1
  272. package/dist/node/ChildProcessRunner/ChildProcessRunner.d.ts +0 -4
  273. package/dist/node/ChildProcessRunner/ChildProcessRunner.d.ts.map +0 -1
  274. package/dist/node/ChildProcessRunner/ChildProcessRunner.js +0 -122
  275. package/dist/node/ChildProcessRunner/ChildProcessRunner.js.map +0 -1
  276. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts +0 -2
  277. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts.map +0 -1
  278. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js +0 -218
  279. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js.map +0 -1
  280. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.d.ts +0 -84
  281. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.d.ts.map +0 -1
  282. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.js +0 -68
  283. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.js.map +0 -1
  284. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.d.ts +0 -2
  285. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.d.ts.map +0 -1
  286. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js +0 -52
  287. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js.map +0 -1
  288. package/dist/node/ChildProcessRunner/ChildProcessWorker.d.ts +0 -23
  289. package/dist/node/ChildProcessRunner/ChildProcessWorker.d.ts.map +0 -1
  290. package/dist/node/ChildProcessRunner/ChildProcessWorker.js +0 -138
  291. package/dist/node/ChildProcessRunner/ChildProcessWorker.js.map +0 -1
  292. package/dist/object/omit.d.ts +0 -3
  293. package/dist/object/omit.d.ts.map +0 -1
  294. package/dist/object/omit.js +0 -14
  295. package/dist/object/omit.js.map +0 -1
  296. package/dist/object/pick.d.ts +0 -14
  297. package/dist/object/pick.d.ts.map +0 -1
  298. package/dist/object/pick.js +0 -17
  299. package/dist/object/pick.js.map +0 -1
  300. package/dist/time.d.ts +0 -12
  301. package/dist/time.d.ts.map +0 -1
  302. package/dist/time.js +0 -22
  303. package/dist/time.js.map +0 -1
  304. package/src/effect/BucketQueue.ts +0 -49
  305. package/src/effect/Scheduler.ts +0 -13
  306. package/src/effect/ServiceContext.ts +0 -108
  307. package/src/guards.ts +0 -8
  308. package/src/node/ChildProcessRunner/ChildProcessRunner.ts +0 -135
  309. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.ts +0 -302
  310. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/schema.ts +0 -78
  311. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.ts +0 -66
  312. package/src/node/ChildProcessRunner/ChildProcessWorker.ts +0 -168
  313. package/src/object/omit.ts +0 -17
  314. package/src/object/pick.ts +0 -27
  315. package/src/time.ts +0 -25
  316. /package/dist/{effect → browser}/WebLock.d.ts +0 -0
@@ -0,0 +1,117 @@
1
+ import { describe, expect, expectTypeOf, it } from '@effect/vitest'
2
+ import { Cause, Effect } from 'effect'
3
+
4
+ import { trySyncOrPromiseOrEffect } from './Effect.ts'
5
+
6
+ interface RequiredService {
7
+ readonly RequiredService: unique symbol
8
+ }
9
+
10
+ declare const contextualEffect: Effect.Effect<number, 'effect-error', RequiredService>
11
+
12
+ describe('trySyncOrPromiseOrEffect', () => {
13
+ it.effect('returns a plain value', () =>
14
+ Effect.gen(function* () {
15
+ const result = yield* trySyncOrPromiseOrEffect(() => 42 as const)
16
+
17
+ expect(result).toBe(42)
18
+ }),
19
+ )
20
+
21
+ it.effect('evaluates the operation lazily for each execution', () =>
22
+ Effect.gen(function* () {
23
+ let executions = 0
24
+ const effect = trySyncOrPromiseOrEffect(() => ++executions)
25
+
26
+ expect(executions).toBe(0)
27
+ expect(yield* effect).toBe(1)
28
+ expect(yield* effect).toBe(2)
29
+ }),
30
+ )
31
+
32
+ it.effect('maps a synchronous throw to an UnknownError', () =>
33
+ Effect.gen(function* () {
34
+ const thrown = new Error('thrown')
35
+ const failure = yield* trySyncOrPromiseOrEffect(() => {
36
+ throw thrown
37
+ }).pipe(Effect.flip)
38
+
39
+ expect(failure).toBeInstanceOf(Cause.UnknownError)
40
+ if (failure instanceof Cause.UnknownError) {
41
+ expect(failure.cause).toBe(thrown)
42
+ }
43
+ }),
44
+ )
45
+
46
+ it.effect('awaits a resolving Promise', () =>
47
+ Effect.gen(function* () {
48
+ const result = yield* trySyncOrPromiseOrEffect(() => Promise.resolve('resolved'))
49
+
50
+ expect(result).toBe('resolved')
51
+ }),
52
+ )
53
+
54
+ it.effect('maps a rejected Promise to an UnknownError', () =>
55
+ Effect.gen(function* () {
56
+ const rejected = new Error('rejected')
57
+ const failure = yield* trySyncOrPromiseOrEffect(() => Promise.reject(rejected)).pipe(Effect.flip)
58
+
59
+ expect(failure).toBeInstanceOf(Cause.UnknownError)
60
+ if (failure instanceof Cause.UnknownError) {
61
+ expect(failure.cause).toBe(rejected)
62
+ }
63
+ }),
64
+ )
65
+
66
+ it.effect('maps a throwing Promise-like then getter to an UnknownError', () =>
67
+ Effect.gen(function* () {
68
+ const thrown = new Error('throwing then getter')
69
+ const thenable = {
70
+ // oxlint-disable-next-line eslint-plugin-unicorn(no-thenable) -- intentionally exercises Promise-like classification
71
+ get then(): PromiseLike<never>['then'] {
72
+ throw thrown
73
+ },
74
+ } satisfies PromiseLike<never>
75
+
76
+ const failure = yield* trySyncOrPromiseOrEffect(() => thenable).pipe(Effect.flip)
77
+
78
+ expect(failure).toBeInstanceOf(Cause.UnknownError)
79
+ if (failure instanceof Cause.UnknownError) {
80
+ expect(failure.cause).toBe(thrown)
81
+ }
82
+ }),
83
+ )
84
+
85
+ it.effect('executes and returns the value of a returned Effect', () =>
86
+ Effect.gen(function* () {
87
+ const result = yield* trySyncOrPromiseOrEffect(() => Effect.succeed('effect-value'))
88
+
89
+ expect(result).toBe('effect-value')
90
+ }),
91
+ )
92
+
93
+ it.effect('preserves a returned Effect failure', () =>
94
+ Effect.gen(function* () {
95
+ const expected = { _tag: 'ExpectedError' as const }
96
+ const failure = yield* trySyncOrPromiseOrEffect(() => Effect.fail(expected)).pipe(Effect.flip)
97
+
98
+ expect(failure).toBe(expected)
99
+ }),
100
+ )
101
+
102
+ it('preserves returned Effect success, error, and requirements types', () => {
103
+ const normalized = trySyncOrPromiseOrEffect(() => contextualEffect)
104
+
105
+ expectTypeOf(normalized).toEqualTypeOf<
106
+ Effect.Effect<number, 'effect-error' | Cause.UnknownError, RequiredService>
107
+ >()
108
+ })
109
+
110
+ it('returns an Effect when the operation only throws', () => {
111
+ const normalized = trySyncOrPromiseOrEffect((): never => {
112
+ throw new Error('thrown')
113
+ })
114
+
115
+ expectTypeOf(normalized).toEqualTypeOf<Effect.Effect<never, Cause.UnknownError>>()
116
+ })
117
+ })
@@ -1,27 +1,23 @@
1
- import * as OtelTracer from '@effect/opentelemetry/Tracer'
1
+ import * as OtelTracer from '@effect/opentelemetry/OtelTracer'
2
2
  import {
3
3
  Cause,
4
+ Console,
4
5
  type Context,
5
6
  Deferred,
6
7
  Duration,
7
8
  Effect,
9
+ Function,
8
10
  Fiber,
9
- FiberRef,
10
- HashSet,
11
- Logger,
12
11
  pipe,
12
+ Predicate,
13
13
  Scope,
14
14
  type Stream,
15
15
  } from 'effect'
16
- import type { UnknownException } from 'effect/Cause'
17
- import { log } from 'effect/Console'
18
- import { dual, type LazyArg } from 'effect/Function'
19
- import type { Predicate, Refinement } from 'effect/Predicate'
20
16
 
21
- import { isPromise } from '../mod.ts'
22
- import { UnknownError } from './Error.ts'
17
+ import { isDevEnv, objectToString } from '../misc.ts'
23
18
 
24
19
  export * from 'effect/Effect'
20
+ export { spanEvent } from './spanEvent.ts'
25
21
 
26
22
  // export const log = <A>(message: A, ...rest: any[]): Effect.Effect<void> =>
27
23
  // Effect.sync(() => {
@@ -38,16 +34,14 @@ export * from 'effect/Effect'
38
34
  // console.error(message, ...rest)
39
35
  // })
40
36
 
41
- /** Same as `Effect.scopeWith` but with a `CloseableScope` instead of a `Scope`. */
37
+ /** Same as `Effect.scopeWith` but with a closeable `Scope`. */
42
38
  export const scopeWithCloseable = <R, E, A>(
43
- fn: (scope: Scope.CloseableScope) => Effect.Effect<A, E, R | Scope.Scope>,
39
+ fn: (scope: Scope.Closeable) => Effect.Effect<A, E, R | Scope.Scope>,
44
40
  ): Effect.Effect<A, E, R | Scope.Scope> =>
45
41
  Effect.gen(function* () {
46
- // const parentScope = yield* Scope.Scope
47
- // const scope = yield* Scope.fork(parentScope, ExecutionStrategy.sequential)
48
42
  const scope = yield* Scope.make()
49
43
  yield* Effect.addFinalizer((exit) => Scope.close(scope, exit))
50
- return yield* fn(scope).pipe(Scope.extend(scope))
44
+ return yield* fn(scope).pipe(Scope.provide(scope))
51
45
  })
52
46
 
53
47
  export type SyncOrPromiseOrEffect<TResult, TError = never, TContext = never> =
@@ -55,22 +49,43 @@ export type SyncOrPromiseOrEffect<TResult, TError = never, TContext = never> =
55
49
  | Promise<TResult>
56
50
  | Effect.Effect<TResult, TError, TContext>
57
51
 
58
- export const tryAll = <Res>(
59
- fn: () => Res,
60
- ): Res extends Effect.Effect<infer A, infer E, never>
61
- ? Effect.Effect<A, E | UnknownException, never>
62
- : Res extends Promise<infer A>
63
- ? Effect.Effect<A, UnknownException, never>
64
- : Effect.Effect<Res, UnknownException, never> =>
65
- Effect.try(() => fn()).pipe(
66
- Effect.andThen((fnRes) =>
67
- Effect.isEffect(fnRes)
68
- ? (fnRes as any as Effect.Effect<any>)
69
- : isPromise(fnRes)
70
- ? Effect.promise(() => fnRes)
71
- : Effect.succeed(fnRes),
72
- ),
73
- ) as any
52
+ /**
53
+ * Creates an Effect from an operation that may return a value, a Promise-like value, or another Effect.
54
+ *
55
+ * @remarks
56
+ * Synchronous values succeed directly, and Promise-like values are awaited. Synchronous throws and Promise rejections
57
+ * are mapped to `Cause.UnknownError`. When the operation returns an Effect, that Effect is executed rather than
58
+ * produced as a successful value; its failures and service requirements are preserved.
59
+ *
60
+ * @param operation - A callback that may return a synchronous value, a Promise-like value, or an Effect
61
+ * @returns An Effect representing the normalized outcome of the operation
62
+ */
63
+ export function trySyncOrPromiseOrEffect<Return>(
64
+ operation: () => Return,
65
+ ): Effect.Effect<
66
+ Return extends Effect.Effect<infer A, infer _E, infer _R> ? A : Return extends PromiseLike<infer A> ? A : Return,
67
+ Cause.UnknownError | (Return extends Effect.Effect<infer _A, infer E, infer _R> ? E : never),
68
+ Return extends Effect.Effect<infer _A, infer _E, infer R> ? R : never
69
+ >
70
+ export function trySyncOrPromiseOrEffect(operation: () => unknown): Effect.Effect<unknown, unknown, unknown> {
71
+ return Effect.try({
72
+ try: () => {
73
+ const result = operation()
74
+
75
+ if (Effect.isEffect(result) === true) {
76
+ // @effect-diagnostics-next-line anyUnknownInErrorContext:off -- dynamic implementation boundary; the public overload preserves exact Effect error and requirement channels
77
+ return result
78
+ }
79
+
80
+ if (Predicate.isPromiseLike(result) === true) {
81
+ return Effect.tryPromise(() => result)
82
+ }
83
+
84
+ return Effect.succeed(result)
85
+ },
86
+ catch: (cause) => new Cause.UnknownError(cause),
87
+ }).pipe(Effect.flatten)
88
+ }
74
89
 
75
90
  export const acquireReleaseLog = (label: string) =>
76
91
  Effect.acquireRelease(Effect.log(`${label} acquire`), (_, ex) => Effect.log(`${label} release`, ex))
@@ -87,16 +102,11 @@ export const logBefore =
87
102
 
88
103
  /** Logs both on errors and defects */
89
104
  export const tapCauseLogPretty = <R, E, A>(eff: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
90
- Effect.tapErrorCause(eff, (cause) =>
105
+ Effect.tapCause(eff, (cause) =>
91
106
  Effect.gen(function* () {
92
- if (Cause.isInterruptedOnly(cause)) {
93
- // console.log('interrupted', Cause.pretty(err), err)
94
- return
95
- }
107
+ if (Cause.hasInterruptsOnly(cause) === true) return
96
108
 
97
- const span = yield* OtelTracer.currentOtelSpan.pipe(
98
- Effect.catchTag('NoSuchElementException', (_) => Effect.succeed(undefined)),
99
- )
109
+ const span = yield* OtelTracer.currentOtelSpan.pipe(Effect.catchTag('NoSuchElementError', (_) => Effect.void))
100
110
 
101
111
  const firstErrLine = cause.toString().split('\n')[0]
102
112
  yield* Effect.logError(firstErrLine, cause).pipe((_) =>
@@ -107,46 +117,82 @@ export const tapCauseLogPretty = <R, E, A>(eff: Effect.Effect<A, E, R>): Effect.
107
117
  }),
108
118
  )
109
119
 
120
+ /**
121
+ * Creates a defect, pausing at a breakpoint in development.
122
+ *
123
+ * @param msg - The error message to include in the defect.
124
+ * @param args - Arbitrary arguments available for inspection during debugging.
125
+ *
126
+ * @see {@link shouldNeverHappen} for the non-Effect equivalent that throws synchronously.
127
+ * @see {@link orDieDebugger}
128
+ */
129
+ export const dieDebugger = (msg: string, ...args: ReadonlyArray<unknown>): Effect.Effect<never> =>
130
+ Effect.suspend(() => {
131
+ if (isDevEnv() === true) {
132
+ // oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint during development
133
+ debugger
134
+ void args // Keeps the variable in scope so it's inspectable when the debugger pauses
135
+ }
136
+ return Effect.die(new Error(msg))
137
+ })
138
+
139
+ /**
140
+ * Converts a failure into a defect, pausing at a breakpoint in development.
141
+ *
142
+ * @param self - The effect on which to apply the operation.
143
+ *
144
+ * @see {@link Effect.orDie}
145
+ * @see {@link dieDebugger}
146
+ */
147
+ export const orDieDebugger = <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, never, R> =>
148
+ Effect.matchEffect(self, {
149
+ onFailure: (error) =>
150
+ // Keep the debugger hook so that `debugger` runs only when the wrapped effect actually fails, not while building the wrapper.
151
+ Effect.suspend(() => {
152
+ if (isDevEnv() === true) {
153
+ // oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint for impossible states during development
154
+ debugger
155
+ }
156
+ return Effect.die(error)
157
+ }),
158
+ onSuccess: Effect.succeed,
159
+ })
160
+
110
161
  export const ignoreIf: {
111
162
  <E, EB extends E>(
112
- refinement: Refinement<NoInfer<E>, EB>,
163
+ refinement: Predicate.Refinement<NoInfer<E>, EB>,
113
164
  ): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<void, Exclude<E, EB>, R>
114
- <E>(predicate: Predicate<NoInfer<E>>): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<void, E, R>
165
+ <E>(predicate: Predicate.Predicate<NoInfer<E>>): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<void, E, R>
115
166
  <A, E, R, EB extends E>(
116
167
  self: Effect.Effect<A, E, R>,
117
- refinement: Refinement<E, EB>,
168
+ refinement: Predicate.Refinement<E, EB>,
118
169
  ): Effect.Effect<void, Exclude<E, EB>, R>
119
- <A, E, R>(self: Effect.Effect<A, E, R>, predicate: Predicate<E>): Effect.Effect<void, E, R>
120
- } = dual(2, <A, E, R>(self: Effect.Effect<A, E, R>, predicate: Predicate<E>) =>
170
+ <A, E, R>(self: Effect.Effect<A, E, R>, predicate: Predicate.Predicate<E>): Effect.Effect<void, E, R>
171
+ } = Function.dual(2, <A, E, R>(self: Effect.Effect<A, E, R>, predicate: Predicate.Predicate<E>) =>
121
172
  self.pipe(Effect.catchIf(predicate, () => Effect.void)),
122
173
  )
123
174
 
124
175
  export const eventListener = <TEvent = unknown>(
125
176
  target: Stream.EventListener<TEvent>,
126
177
  type: string,
127
- handler: (event: TEvent) => Effect.Effect<void, never, never>,
178
+ handler: (event: TEvent) => Effect.Effect<void>,
128
179
  options?: { once?: boolean },
129
180
  ) =>
130
181
  Effect.gen(function* () {
131
- const runtime = yield* Effect.runtime<never>()
182
+ const services = yield* Effect.context()
132
183
 
133
- const handlerFn = (event: TEvent) => handler(event).pipe(Effect.provide(runtime), Effect.runFork)
184
+ const handlerFn = (event: TEvent) => handler(event).pipe(Effect.runForkWith(services))
134
185
 
135
186
  target.addEventListener(type, handlerFn, { once: options?.once ?? false })
136
187
 
137
188
  yield* Effect.addFinalizer(() => Effect.sync(() => target.removeEventListener(type, handlerFn)))
138
189
  })
139
190
 
140
- export const spanEvent = (message: any, attributes?: Record<string, any>) =>
141
- Effect.locallyWith(Effect.log(message).pipe(Effect.annotateLogs(attributes ?? {})), FiberRef.currentLoggers, () =>
142
- HashSet.make(Logger.tracerLogger),
143
- )
144
-
145
191
  export const logWarnIfTakesLongerThan =
146
- ({ label, duration }: { label: string; duration: Duration.DurationInput }) =>
192
+ ({ label, duration }: { label: string; duration: Duration.Input }) =>
147
193
  <R, E, A>(eff: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
148
194
  Effect.gen(function* () {
149
- const runtime = yield* Effect.runtime<never>()
195
+ const services = yield* Effect.context()
150
196
 
151
197
  let tookLongerThanTimer = false
152
198
 
@@ -154,10 +200,9 @@ export const logWarnIfTakesLongerThan =
154
200
  Effect.tap(() => {
155
201
  tookLongerThanTimer = true
156
202
  // TODO include span info
157
- return Effect.logWarning(`${label}: Took longer than ${duration}ms`)
203
+ return Effect.logWarning(`${label}: Took longer than ${objectToString(duration)}ms`)
158
204
  }),
159
- Effect.provide(runtime),
160
- Effect.runFork,
205
+ Effect.runForkWith(services),
161
206
  )
162
207
 
163
208
  const start = Date.now()
@@ -169,13 +214,14 @@ export const logWarnIfTakesLongerThan =
169
214
 
170
215
  yield* Fiber.interrupt(timeoutFiber)
171
216
 
172
- if (tookLongerThanTimer) {
217
+ if (tookLongerThanTimer === true) {
173
218
  yield* Effect.logWarning(`${label}: Interrupted after ${end - start}ms`)
174
219
  }
175
220
  }),
176
221
  ),
177
222
  )
178
223
 
224
+ // eslint-disable-next-line overeng/explicit-boolean-compare -- mutated in forked fiber; TS can't see the mutation
179
225
  if (tookLongerThanTimer) {
180
226
  const end = Date.now()
181
227
  yield* Effect.logWarning(`${label}: Actual duration: ${end - start}ms`)
@@ -193,7 +239,7 @@ export const logDuration =
193
239
  const start = Date.now()
194
240
  const res = yield* eff
195
241
  const end = Date.now()
196
- yield* Effect.log(`${label}: ${Duration.format(end - start)}`)
242
+ yield* Effect.log(`${label}: ${Duration.format(Duration.millis(end - start))}`)
197
243
  return res
198
244
  })
199
245
 
@@ -205,22 +251,66 @@ export const tapSync =
205
251
  export const debugLogEnv = (msg?: string): Effect.Effect<Context.Context<never>> =>
206
252
  pipe(
207
253
  Effect.context<never>(),
208
- Effect.tap((env) => log(msg ?? 'debugLogEnv', env)),
254
+ Effect.tap((env) => Console.log(msg ?? 'debugLogEnv', env)),
209
255
  )
210
256
 
211
- export const timeoutDie =
212
- <E1>(options: { onTimeout: LazyArg<E1>; duration: Duration.DurationInput }) =>
213
- <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
214
- Effect.orDie(Effect.timeoutFail(options)(self))
215
-
216
- export const timeoutDieMsg =
217
- (options: { error: string; duration: Duration.DurationInput }) =>
218
- <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
219
- Effect.orDie(
220
- Effect.timeoutFail({ onTimeout: () => new UnknownError({ cause: options.error }), duration: options.duration })(
221
- self,
222
- ),
223
- )
257
+ /**
258
+ * Enforces a time limit on an effect, triggering a defect on timeout.
259
+ *
260
+ * @remarks
261
+ *
262
+ * This function allows you to enforce a time limit on the execution of an
263
+ * effect. If the effect does not complete within the given duration, it dies
264
+ * with a {@link Cause.TimeoutError} as an unchecked defect. Unlike
265
+ * {@link Effect.timeout}, which adds `TimeoutError` to the error channel,
266
+ * this function keeps the error channel unchanged by treating the timeout as
267
+ * a defect.
268
+ *
269
+ * The returned effect will either:
270
+ * - Succeed with the original effect's result if it completes within the
271
+ * specified duration.
272
+ * - Die with a {@link Cause.TimeoutError} defect if the time limit is exceeded.
273
+ *
274
+ * @see {@link timeoutOrDieMessage} for a version with a custom message.
275
+ * @see {@link Effect.timeout} for a version that raises a `TimeoutError` as a typed error.
276
+ * @see {@link Effect.timeoutOrElse} for a version with a custom timeout branch.
277
+ */
278
+ export const timeoutOrDie =
279
+ (duration: Duration.Input) =>
280
+ <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
281
+ Effect.timeoutOrElse(self, {
282
+ duration,
283
+ orElse: () => Effect.die(new Cause.TimeoutError()),
284
+ })
285
+
286
+ /**
287
+ * Enforces a time limit on an effect, triggering a defect with a custom
288
+ * message on timeout.
289
+ *
290
+ * @remarks
291
+ *
292
+ * This function behaves like {@link timeoutOrDie}, but allows you to provide
293
+ * a custom message for the {@link Cause.TimeoutError} defect. This is useful
294
+ * for adding context about which operation timed out, making it easier to
295
+ * diagnose issues in logs or error reports.
296
+ *
297
+ * The returned effect will either:
298
+ * - Succeed with the original effect's result if it completes within the
299
+ * specified duration.
300
+ * - Die with a {@link Cause.TimeoutError} defect containing the provided
301
+ * message if the time limit is exceeded.
302
+ *
303
+ * @see {@link timeoutOrDie} for a version without a custom message.
304
+ * @see {@link Effect.timeout} for a version that raises a `TimeoutError` as a typed error.
305
+ * @see {@link Effect.timeoutOrElse} for a version with a custom timeout branch.
306
+ */
307
+ export const timeoutOrDieMessage =
308
+ (duration: Duration.Input, message: string) =>
309
+ <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
310
+ Effect.timeoutOrElse(self, {
311
+ duration,
312
+ orElse: () => Effect.die(new Cause.TimeoutError(message)),
313
+ })
224
314
 
225
315
  export const toForkedDeferred = <R, E, A>(
226
316
  eff: Effect.Effect<A, E, R>,
@@ -251,16 +341,16 @@ export const withPerformanceMeasure =
251
341
  )
252
342
 
253
343
  const getSpanTrace = () => {
254
- const fiberOption = Fiber.getCurrentFiber()
255
- if (fiberOption._tag === 'None' || fiberOption.value.currentSpan === undefined) {
344
+ const fiber = Fiber.getCurrent()
345
+ if (fiber === undefined || fiber.currentSpan === undefined) {
256
346
  return 'No current fiber'
257
347
  }
258
348
 
259
349
  return ''
260
350
  // const msg = Effect.runSync(
261
351
  // Effect.fail({ message: '' }).pipe(
262
- // Effect.withParentSpan(fiberOption.value.currentSpan),
263
- // Effect.catchAllCause((cause) => Effect.succeed(cause.toString())),
352
+ // Effect.withParentSpan(fiber.currentSpan),
353
+ // Effect.catchCause((cause) => Effect.succeed(cause.toString())),
264
354
  // ),
265
355
  // )
266
356
 
@@ -274,7 +364,12 @@ const getSpanTrace = () => {
274
364
 
275
365
  const logSpanTrace = () => console.log(getSpanTrace())
276
366
 
277
- // @ts-expect-error TODO fix types
367
+ declare global {
368
+ /** Debug helper: returns the current Effect span trace */
369
+ var getSpanTrace: () => string
370
+ /** Debug helper: logs the current Effect span trace */
371
+ var logSpanTrace: () => void
372
+ }
373
+
278
374
  globalThis.getSpanTrace = getSpanTrace
279
- // @ts-expect-error TODO fix types
280
375
  globalThis.logSpanTrace = logSpanTrace
@@ -1,6 +1,6 @@
1
1
  import { Schema } from 'effect'
2
2
 
3
- export class UnknownError extends Schema.TaggedError<UnknownError>()('UnknownError', {
3
+ export class UnknownError extends Schema.TaggedError<UnknownError>('~@livestore/utils/UnknownError')('UnknownError', {
4
4
  cause: Schema.Any,
5
5
  payload: Schema.optional(Schema.Any),
6
6
  }) {}
@@ -1,49 +1 @@
1
- import { Cause, HashMap, Logger, LogLevel } from 'effect'
2
-
3
1
  export * from 'effect/Logger'
4
-
5
- const defaultDateFormat = (date: Date): string =>
6
- `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date
7
- .getSeconds()
8
- .toString()
9
- .padStart(2, '0')}.${date.getMilliseconds().toString().padStart(3, '0')}`
10
-
11
- export const prettyWithThread = (threadName: string, options: { mode?: 'tty' | 'browser' } = {}) =>
12
- Logger.replace(
13
- Logger.defaultLogger,
14
- Logger.prettyLogger({
15
- formatDate: (date) => `${defaultDateFormat(date)} ${threadName}`,
16
- mode: options.mode,
17
- }),
18
- )
19
-
20
- export const consoleLogger = (threadName: string) =>
21
- Logger.make(({ message, annotations, date, logLevel, cause }) => {
22
- const isCloudflareWorker = typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers'
23
- const consoleFn =
24
- logLevel === LogLevel.Debug
25
- ? // Cloudflare Workers doesn't support console.debug 🤷
26
- isCloudflareWorker
27
- ? console.log
28
- : console.debug
29
- : logLevel === LogLevel.Info
30
- ? console.info
31
- : logLevel === LogLevel.Warning
32
- ? console.warn
33
- : console.error
34
-
35
- const annotationsObj = Object.fromEntries(HashMap.entries(annotations))
36
-
37
- const messages = Array.isArray(message) ? message : [message]
38
- if (Cause.isEmpty(cause) === false) {
39
- messages.push(Cause.pretty(cause, { renderErrorCause: true }))
40
- }
41
-
42
- if (Object.keys(annotationsObj).length > 0) {
43
- messages.push(annotationsObj)
44
- }
45
-
46
- consoleFn(`[${defaultDateFormat(date)} ${threadName}]`, ...messages)
47
- })
48
-
49
- export const consoleWithThread = (threadName: string) => Logger.replace(Logger.defaultLogger, consoleLogger(threadName))
@@ -1,11 +1,10 @@
1
- import { makeExternalSpan } from '@effect/opentelemetry/Tracer'
1
+ import { makeExternalSpan } from '@effect/opentelemetry/OtelTracer'
2
2
  import type { Link as OtelSpanLink } from '@opentelemetry/api'
3
3
  import type { SpanLink as EffectSpanLink } from 'effect/Tracer'
4
4
 
5
- export * from '@effect/opentelemetry/Tracer'
5
+ export * from '@effect/opentelemetry/OtelTracer'
6
6
 
7
7
  export const makeSpanLink = (otelSpanLink: OtelSpanLink): EffectSpanLink => ({
8
- _tag: 'SpanLink',
9
8
  span: makeExternalSpan(otelSpanLink.context),
10
9
  attributes: otelSpanLink.attributes ?? {},
11
10
  })