@livestore/utils 0.4.0 → 0.5.0-dev.1

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 (265) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/Deferred.d.ts.map +1 -1
  3. package/dist/NoopTracer.d.ts +7 -8
  4. package/dist/NoopTracer.d.ts.map +1 -1
  5. package/dist/NoopTracer.js +3 -18
  6. package/dist/NoopTracer.js.map +1 -1
  7. package/dist/NoopTracer.test.d.ts +2 -0
  8. package/dist/NoopTracer.test.d.ts.map +1 -0
  9. package/dist/NoopTracer.test.js +29 -0
  10. package/dist/NoopTracer.test.js.map +1 -0
  11. package/dist/base64.d.ts.map +1 -1
  12. package/dist/binary.d.ts.map +1 -1
  13. package/dist/browser/Opfs/Opfs.d.ts +179 -29
  14. package/dist/browser/Opfs/Opfs.d.ts.map +1 -1
  15. package/dist/browser/Opfs/Opfs.js +134 -309
  16. package/dist/browser/Opfs/Opfs.js.map +1 -1
  17. package/dist/browser/Opfs/debug-utils.d.ts.map +1 -1
  18. package/dist/browser/Opfs/debug-utils.js +6 -5
  19. package/dist/browser/Opfs/debug-utils.js.map +1 -1
  20. package/dist/browser/Opfs/mod.js +3 -3
  21. package/dist/browser/Opfs/utils.d.ts +8 -8
  22. package/dist/browser/Opfs/utils.d.ts.map +1 -1
  23. package/dist/browser/Opfs/utils.js +35 -26
  24. package/dist/browser/Opfs/utils.js.map +1 -1
  25. package/dist/browser/QuotaExceededError.d.ts +10 -9
  26. package/dist/browser/QuotaExceededError.d.ts.map +1 -1
  27. package/dist/browser/WebChannelBrowser.d.ts.map +1 -1
  28. package/dist/browser/WebChannelBrowser.js +25 -14
  29. package/dist/browser/WebChannelBrowser.js.map +1 -1
  30. package/dist/browser/WebError.d.ts +55 -87
  31. package/dist/browser/WebError.d.ts.map +1 -1
  32. package/dist/browser/WebError.js +92 -85
  33. package/dist/browser/WebError.js.map +1 -1
  34. package/dist/browser/WebError.test.js +7 -7
  35. package/dist/browser/WebError.test.js.map +1 -1
  36. package/dist/browser/WebLock.d.ts +1 -1
  37. package/dist/browser/WebLock.d.ts.map +1 -1
  38. package/dist/browser/WebLock.js +13 -13
  39. package/dist/browser/WebLock.js.map +1 -1
  40. package/dist/browser/mod.js +6 -6
  41. package/dist/cuid/cuid.browser.d.ts.map +1 -1
  42. package/dist/cuid/cuid.node.d.ts.map +1 -1
  43. package/dist/effect/Debug.d.ts +4 -7
  44. package/dist/effect/Debug.d.ts.map +1 -1
  45. package/dist/effect/Debug.js +34 -40
  46. package/dist/effect/Debug.js.map +1 -1
  47. package/dist/effect/Effect.d.ts +31 -22
  48. package/dist/effect/Effect.d.ts.map +1 -1
  49. package/dist/effect/Effect.js +54 -50
  50. package/dist/effect/Effect.js.map +1 -1
  51. package/dist/effect/Effect.test.d.ts +2 -0
  52. package/dist/effect/Effect.test.d.ts.map +1 -0
  53. package/dist/effect/Effect.test.js +72 -0
  54. package/dist/effect/Effect.test.js.map +1 -0
  55. package/dist/effect/Error.d.ts +4 -6
  56. package/dist/effect/Error.d.ts.map +1 -1
  57. package/dist/effect/Logger.d.ts +0 -6
  58. package/dist/effect/Logger.d.ts.map +1 -1
  59. package/dist/effect/Logger.js +0 -32
  60. package/dist/effect/Logger.js.map +1 -1
  61. package/dist/effect/OtelTracer.d.ts +1 -1
  62. package/dist/effect/OtelTracer.d.ts.map +1 -1
  63. package/dist/effect/OtelTracer.js +2 -3
  64. package/dist/effect/OtelTracer.js.map +1 -1
  65. package/dist/effect/RpcClient.d.ts +9 -9
  66. package/dist/effect/RpcClient.d.ts.map +1 -1
  67. package/dist/effect/RpcClient.js +113 -84
  68. package/dist/effect/RpcClient.js.map +1 -1
  69. package/dist/effect/Schedule.d.ts +1 -1
  70. package/dist/effect/Schedule.d.ts.map +1 -1
  71. package/dist/effect/Schedule.js +2 -2
  72. package/dist/effect/Schedule.js.map +1 -1
  73. package/dist/effect/Schema/debug-diff.d.ts +1 -1
  74. package/dist/effect/Schema/debug-diff.d.ts.map +1 -1
  75. package/dist/effect/Schema/debug-diff.js +5 -7
  76. package/dist/effect/Schema/debug-diff.js.map +1 -1
  77. package/dist/effect/Schema/debug-diff.test.js +8 -5
  78. package/dist/effect/Schema/debug-diff.test.js.map +1 -1
  79. package/dist/effect/Schema/index.d.ts +28 -20
  80. package/dist/effect/Schema/index.d.ts.map +1 -1
  81. package/dist/effect/Schema/index.js +56 -29
  82. package/dist/effect/Schema/index.js.map +1 -1
  83. package/dist/effect/Stream.d.ts +13 -5
  84. package/dist/effect/Stream.d.ts.map +1 -1
  85. package/dist/effect/Stream.js +19 -12
  86. package/dist/effect/Stream.js.map +1 -1
  87. package/dist/effect/Stream.test.js +10 -10
  88. package/dist/effect/Stream.test.js.map +1 -1
  89. package/dist/effect/Subscribable.d.ts +9 -3
  90. package/dist/effect/Subscribable.d.ts.map +1 -1
  91. package/dist/effect/Subscribable.js +20 -35
  92. package/dist/effect/Subscribable.js.map +1 -1
  93. package/dist/effect/SubscriptionRef.d.ts +5 -6
  94. package/dist/effect/SubscriptionRef.d.ts.map +1 -1
  95. package/dist/effect/SubscriptionRef.js +2 -3
  96. package/dist/effect/SubscriptionRef.js.map +1 -1
  97. package/dist/effect/TaskTracing.d.ts +1 -1
  98. package/dist/effect/TaskTracing.d.ts.map +1 -1
  99. package/dist/effect/TaskTracing.js +12 -16
  100. package/dist/effect/TaskTracing.js.map +1 -1
  101. package/dist/effect/WebChannel/WebChannel.d.ts +7 -7
  102. package/dist/effect/WebChannel/WebChannel.d.ts.map +1 -1
  103. package/dist/effect/WebChannel/WebChannel.js +69 -48
  104. package/dist/effect/WebChannel/WebChannel.js.map +1 -1
  105. package/dist/effect/WebChannel/WebChannel.test.js +16 -16
  106. package/dist/effect/WebChannel/WebChannel.test.js.map +1 -1
  107. package/dist/effect/WebChannel/broadcastChannelWithAck.d.ts.map +1 -1
  108. package/dist/effect/WebChannel/broadcastChannelWithAck.js +18 -11
  109. package/dist/effect/WebChannel/broadcastChannelWithAck.js.map +1 -1
  110. package/dist/effect/WebChannel/common.d.ts +16 -17
  111. package/dist/effect/WebChannel/common.d.ts.map +1 -1
  112. package/dist/effect/WebChannel/common.js +6 -6
  113. package/dist/effect/WebChannel/common.js.map +1 -1
  114. package/dist/effect/WebChannel/mod.js +3 -3
  115. package/dist/effect/WebSocket.d.ts +4 -6
  116. package/dist/effect/WebSocket.d.ts.map +1 -1
  117. package/dist/effect/WebSocket.js +4 -4
  118. package/dist/effect/WebSocket.js.map +1 -1
  119. package/dist/effect/WebSocket.test.js +3 -3
  120. package/dist/effect/WebSocket.test.js.map +1 -1
  121. package/dist/effect/mod.browser-build.test.d.ts +2 -0
  122. package/dist/effect/mod.browser-build.test.d.ts.map +1 -0
  123. package/dist/effect/mod.browser-build.test.js +18 -0
  124. package/dist/effect/mod.browser-build.test.js.map +1 -0
  125. package/dist/effect/mod.d.ts +13 -15
  126. package/dist/effect/mod.d.ts.map +1 -1
  127. package/dist/effect/mod.js +28 -33
  128. package/dist/effect/mod.js.map +1 -1
  129. package/dist/effect/spanEvent.d.ts.map +1 -1
  130. package/dist/effect/spanEvent.js +2 -2
  131. package/dist/effect/spanEvent.js.map +1 -1
  132. package/dist/effect/spanEvent.test.js +11 -14
  133. package/dist/effect/spanEvent.test.js.map +1 -1
  134. package/dist/env.d.ts.map +1 -1
  135. package/dist/env.js +1 -1
  136. package/dist/env.js.map +1 -1
  137. package/dist/fast-deep-equal.d.ts.map +1 -1
  138. package/dist/fast-deep-equal.js +1 -2
  139. package/dist/fast-deep-equal.js.map +1 -1
  140. package/dist/misc.d.ts.map +1 -1
  141. package/dist/misc.js +3 -3
  142. package/dist/misc.js.map +1 -1
  143. package/dist/mod.d.ts +1 -36
  144. package/dist/mod.d.ts.map +1 -1
  145. package/dist/mod.js +19 -27
  146. package/dist/mod.js.map +1 -1
  147. package/dist/node/mod.d.ts +11 -36
  148. package/dist/node/mod.d.ts.map +1 -1
  149. package/dist/node/mod.js +17 -42
  150. package/dist/node/mod.js.map +1 -1
  151. package/dist/object/index.d.ts +0 -9
  152. package/dist/object/index.d.ts.map +1 -1
  153. package/dist/object/index.js +1 -10
  154. package/dist/object/index.js.map +1 -1
  155. package/dist/object/stringify-object.d.ts.map +1 -1
  156. package/dist/object/stringify-object.test.js +1 -1
  157. package/dist/promise.d.ts.map +1 -1
  158. package/dist/qr.d.ts.map +1 -1
  159. package/dist/set.d.ts.map +1 -1
  160. package/dist/string.d.ts +0 -2
  161. package/dist/string.d.ts.map +1 -1
  162. package/dist/string.js +0 -2
  163. package/dist/string.js.map +1 -1
  164. package/package.json +38 -56
  165. package/src/NoopTracer.test.ts +36 -0
  166. package/src/NoopTracer.ts +18 -41
  167. package/src/browser/Opfs/Opfs.ts +304 -277
  168. package/src/browser/Opfs/debug-utils.ts +7 -6
  169. package/src/browser/Opfs/utils.ts +36 -26
  170. package/src/browser/QuotaExceededError.ts +13 -10
  171. package/src/browser/WebChannelBrowser.ts +25 -11
  172. package/src/browser/WebError.test.ts +6 -6
  173. package/src/browser/WebError.ts +107 -133
  174. package/src/browser/WebLock.ts +13 -14
  175. package/src/effect/Debug.ts +69 -78
  176. package/src/effect/Effect.test.ts +117 -0
  177. package/src/effect/Effect.ts +86 -73
  178. package/src/effect/Logger.ts +0 -48
  179. package/src/effect/OtelTracer.ts +2 -3
  180. package/src/effect/RpcClient.ts +126 -93
  181. package/src/effect/Schedule.ts +4 -5
  182. package/src/effect/Schema/debug-diff.test.ts +6 -6
  183. package/src/effect/Schema/debug-diff.ts +6 -8
  184. package/src/effect/Schema/index.ts +122 -61
  185. package/src/effect/Stream.test.ts +9 -9
  186. package/src/effect/Stream.ts +23 -16
  187. package/src/effect/Subscribable.ts +31 -42
  188. package/src/effect/SubscriptionRef.ts +8 -12
  189. package/src/effect/TaskTracing.ts +13 -16
  190. package/src/effect/WebChannel/WebChannel.test.ts +23 -23
  191. package/src/effect/WebChannel/WebChannel.ts +87 -56
  192. package/src/effect/WebChannel/broadcastChannelWithAck.ts +21 -11
  193. package/src/effect/WebChannel/common.ts +14 -15
  194. package/src/effect/WebSocket.test.ts +2 -2
  195. package/src/effect/WebSocket.ts +9 -6
  196. package/src/effect/mod.browser-build.test.ts +19 -0
  197. package/src/effect/mod.ts +37 -69
  198. package/src/effect/spanEvent.test.ts +10 -13
  199. package/src/effect/spanEvent.ts +5 -3
  200. package/src/fast-deep-equal.ts +4 -5
  201. package/src/misc.ts +3 -3
  202. package/src/mod.ts +5 -41
  203. package/src/node/mod.ts +20 -46
  204. package/src/object/index.ts +0 -24
  205. package/src/string.ts +0 -3
  206. package/dist/effect/BucketQueue.d.ts +0 -20
  207. package/dist/effect/BucketQueue.d.ts.map +0 -1
  208. package/dist/effect/BucketQueue.js +0 -34
  209. package/dist/effect/BucketQueue.js.map +0 -1
  210. package/dist/effect/Scheduler.d.ts +0 -4
  211. package/dist/effect/Scheduler.d.ts.map +0 -1
  212. package/dist/effect/Scheduler.js +0 -9
  213. package/dist/effect/Scheduler.js.map +0 -1
  214. package/dist/effect/ServiceContext.d.ts +0 -37
  215. package/dist/effect/ServiceContext.d.ts.map +0 -1
  216. package/dist/effect/ServiceContext.js +0 -55
  217. package/dist/effect/ServiceContext.js.map +0 -1
  218. package/dist/guards.d.ts +0 -20
  219. package/dist/guards.d.ts.map +0 -1
  220. package/dist/guards.js +0 -20
  221. package/dist/guards.js.map +0 -1
  222. package/dist/node/ChildProcessRunner/ChildProcessRunner.d.ts +0 -4
  223. package/dist/node/ChildProcessRunner/ChildProcessRunner.d.ts.map +0 -1
  224. package/dist/node/ChildProcessRunner/ChildProcessRunner.js +0 -128
  225. package/dist/node/ChildProcessRunner/ChildProcessRunner.js.map +0 -1
  226. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts +0 -10
  227. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts.map +0 -1
  228. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js +0 -217
  229. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js.map +0 -1
  230. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.d.ts +0 -84
  231. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.d.ts.map +0 -1
  232. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.js +0 -68
  233. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.js.map +0 -1
  234. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.d.ts +0 -2
  235. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.d.ts.map +0 -1
  236. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js +0 -52
  237. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js.map +0 -1
  238. package/dist/node/ChildProcessRunner/ChildProcessWorker.d.ts +0 -23
  239. package/dist/node/ChildProcessRunner/ChildProcessWorker.d.ts.map +0 -1
  240. package/dist/node/ChildProcessRunner/ChildProcessWorker.js +0 -138
  241. package/dist/node/ChildProcessRunner/ChildProcessWorker.js.map +0 -1
  242. package/dist/object/omit.d.ts +0 -3
  243. package/dist/object/omit.d.ts.map +0 -1
  244. package/dist/object/omit.js +0 -14
  245. package/dist/object/omit.js.map +0 -1
  246. package/dist/object/pick.d.ts +0 -14
  247. package/dist/object/pick.d.ts.map +0 -1
  248. package/dist/object/pick.js +0 -17
  249. package/dist/object/pick.js.map +0 -1
  250. package/dist/time.d.ts +0 -12
  251. package/dist/time.d.ts.map +0 -1
  252. package/dist/time.js +0 -22
  253. package/dist/time.js.map +0 -1
  254. package/src/effect/BucketQueue.ts +0 -49
  255. package/src/effect/Scheduler.ts +0 -13
  256. package/src/effect/ServiceContext.ts +0 -108
  257. package/src/guards.ts +0 -23
  258. package/src/node/ChildProcessRunner/ChildProcessRunner.ts +0 -148
  259. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.ts +0 -311
  260. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/schema.ts +0 -78
  261. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.ts +0 -65
  262. package/src/node/ChildProcessRunner/ChildProcessWorker.ts +0 -169
  263. package/src/object/omit.ts +0 -17
  264. package/src/object/pick.ts +0 -27
  265. package/src/time.ts +0 -25
@@ -1,11 +1,10 @@
1
- export * from '@effect/rpc/RpcClient'
1
+ export * from 'effect/rpc/RpcClient'
2
2
 
3
- import { Socket } from '@effect/platform'
4
- import { RpcClient, RpcClientError, RpcSerialization } from '@effect/rpc'
5
- import { Protocol } from '@effect/rpc/RpcClient'
6
- import { constPing, type FromServerEncoded } from '@effect/rpc/RpcMessage'
7
- import { Cause, Deferred, Effect, Layer, Option, Schedule, type Scope } from 'effect'
8
- import { constVoid, identity } from 'effect/Function'
3
+ import { Cause, Deferred, Effect, Function, Latch, Layer, References, Result, Schedule, type Scope } from 'effect'
4
+ import { RpcClient, RpcClientError, RpcSerialization } from 'effect/rpc'
5
+ import { Protocol } from 'effect/rpc/RpcClient'
6
+ import { constPing, type FromServerEncoded } from 'effect/rpc/RpcMessage'
7
+ import { Socket } from 'effect/socket'
9
8
 
10
9
  import * as SubscriptionRef from './SubscriptionRef.ts'
11
10
 
@@ -20,7 +19,7 @@ export const layerProtocolSocketWithIsConnected = (options: {
20
19
  readonly isConnected: SubscriptionRef.SubscriptionRef<boolean>
21
20
  readonly pingSchedule?: Schedule.Schedule<unknown> | undefined
22
21
  }): Layer.Layer<Protocol, never, RpcSerialization.RpcSerialization | Socket.Socket> =>
23
- Layer.scoped(Protocol, makeProtocolSocketWithIsConnected(options))
22
+ Layer.effect(Protocol, makeProtocolSocketWithIsConnected(options))
24
23
 
25
24
  export const makeProtocolSocketWithIsConnected = (options: {
26
25
  readonly url: string
@@ -29,135 +28,169 @@ export const makeProtocolSocketWithIsConnected = (options: {
29
28
  readonly isConnected: SubscriptionRef.SubscriptionRef<boolean>
30
29
  // CHANGED: add ping schedule
31
30
  readonly pingSchedule?: Schedule.Schedule<unknown> | undefined
32
- }): Effect.Effect<Protocol['Type'], never, Scope.Scope | RpcSerialization.RpcSerialization | Socket.Socket> =>
31
+ }): Effect.Effect<Protocol['Service'], never, Scope.Scope | RpcSerialization.RpcSerialization | Socket.Socket> =>
33
32
  Protocol.make(
34
- Effect.fnUntraced(function* (writeResponse) {
33
+ Effect.fnUntraced(function* (writeResponse, clientIds) {
35
34
  const socket = yield* Socket.Socket
36
35
  const serialization = yield* RpcSerialization.RpcSerialization
36
+ const requestClientMap = new Map<string | number, number>()
37
37
 
38
- const write = yield* socket.writer
39
- const parser = serialization.unsafeMake()
38
+ const writer = yield* socket.writer
39
+ let parser = serialization.makeUnsafe()
40
40
 
41
- const pinger = yield* makePinger(write(parser.encode(constPing)!), options?.pingSchedule)
41
+ const pinger = yield* makePinger(
42
+ Effect.suspend(() => {
43
+ const encoded = parser.encode(constPing)
44
+ return encoded === undefined ? Effect.void : writer.write(encoded)
45
+ }),
46
+ options?.pingSchedule,
47
+ )
48
+ let currentError: RpcClientError.RpcClientError | undefined
49
+
50
+ const markConnected = SubscriptionRef.set(options.isConnected, true)
51
+ const broadcast = (response: FromServerEncoded) =>
52
+ Effect.forEach(clientIds, (clientId) => writeResponse(clientId, response))
53
+
54
+ const processData = (message: Uint8Array | string): Effect.Effect<void> => {
55
+ try {
56
+ const responses = parser.decode(message) as Array<FromServerEncoded>
57
+ if (responses.length === 0) return Effect.void
58
+ let i = 0
59
+ return Effect.whileLoop({
60
+ while: () => i < responses.length,
61
+ body: () => {
62
+ const response = responses[i++]!
63
+ pinger.reset()
64
+ if (response._tag === 'Pong') {
65
+ pinger.onPong()
66
+ return markConnected
67
+ }
68
+ if ('requestId' in response) {
69
+ const clientId = requestClientMap.get(response.requestId)
70
+ if (clientId !== undefined) {
71
+ if (response._tag === 'Exit') {
72
+ requestClientMap.delete(response.requestId)
73
+ }
74
+ return markConnected.pipe(Effect.andThen(writeResponse(clientId, response)))
75
+ }
76
+ }
77
+ return markConnected.pipe(Effect.andThen(broadcast(response)))
78
+ },
79
+ step: Function.constVoid,
80
+ })
81
+ } catch (defect) {
82
+ return broadcast({
83
+ _tag: 'ClientProtocolError',
84
+ error: new RpcClientError.RpcClientError({
85
+ reason: new RpcClientError.RpcClientDefect({
86
+ message: 'Error decoding message',
87
+ cause: defect,
88
+ }),
89
+ }),
90
+ })
91
+ }
92
+ }
42
93
 
43
94
  yield* Effect.suspend(() => {
44
95
  // We rely on the heartbeat watchdog while streaming arbitrarily long payloads.
45
96
  // Reset the timer as soon as _any_ frame arrives so that large batches which
46
97
  // don't contain explicit `Pong` messages don't trigger the open-timeout defect.
47
98
  // (The actual pong handler still calls `onPong()` to resolve manual pings.)
48
- // CHANGED: don't reset parser on every message
49
- // parser = serialization.unsafeMake()
99
+ parser = serialization.makeUnsafe()
50
100
  pinger.reset()
51
- return socket
52
- .runRaw((message) => {
53
- try {
54
- const responses = parser.decode(message) as Array<FromServerEncoded>
55
- if (responses.length === 0) return
56
- let i = 0
57
- return Effect.whileLoop({
58
- while: () => i < responses.length,
59
- body: () => {
60
- const response = responses[i++]!
61
- // Keep extending the watchdog for each data frame to avoid
62
- // disconnecting mid-stream when the server is busy sending batches.
63
- pinger.reset()
64
- if (response._tag === 'Pong') {
65
- pinger.onPong()
66
- }
67
- return writeResponse(response).pipe(
68
- // CHANGED: set isConnected to true on pong
69
- Effect.tap(
70
- Effect.fn(function* () {
71
- if (options?.isConnected !== undefined) {
72
- yield* SubscriptionRef.set(options.isConnected, true)
73
- }
74
- }),
75
- ),
76
- )
77
- },
78
- step: constVoid,
79
- })
80
- } catch (defect) {
81
- return writeResponse({
82
- _tag: 'ClientProtocolError',
83
- error: new RpcClientError.RpcClientError({
84
- reason: 'Protocol',
85
- message: 'Error decoding message',
86
- cause: Cause.fail(defect),
87
- }),
88
- })
101
+ return Effect.gen(function* () {
102
+ const { pull } = yield* socket.reader
103
+ currentError = undefined
104
+ while (true) {
105
+ const frames = yield* pull
106
+ for (const frame of frames) {
107
+ // Keep extending the watchdog for each data frame to avoid disconnecting mid-stream.
108
+ yield* processData(frame)
89
109
  }
90
- })
91
- .pipe(
92
- Effect.raceFirst(
93
- Effect.zipRight(
94
- pinger.timeout,
95
- Effect.fail(
96
- new Socket.SocketGenericError({
97
- reason: 'OpenTimeout',
110
+ }
111
+ }).pipe(
112
+ Effect.scoped,
113
+ Effect.raceFirst(
114
+ Effect.flatMap(pinger.timeout, () =>
115
+ Effect.fail(
116
+ new Socket.SocketError({
117
+ reason: new Socket.SocketOpenError({
118
+ kind: 'Timeout',
98
119
  cause: new Error('ping timeout'),
99
120
  }),
100
- ),
121
+ }),
101
122
  ),
102
123
  ),
103
- )
124
+ ),
125
+ )
104
126
  }).pipe(
105
- Effect.zipRight(
127
+ Effect.flatMap(() =>
106
128
  Effect.fail(
107
- new Socket.SocketCloseError({
108
- reason: 'Close',
109
- code: 1000,
110
- closeReason: 'Closing connection',
129
+ new Socket.SocketError({
130
+ reason: new Socket.SocketCloseError({
131
+ code: 1000,
132
+ closeReason: 'Closing connection',
133
+ }),
111
134
  }),
112
135
  ),
113
136
  ),
114
- Effect.tapErrorCause(
137
+ Effect.tapCause(
115
138
  Effect.fn(function* (cause) {
116
139
  // CHANGED: set isConnected to false on error
117
- if (options?.isConnected !== undefined) {
118
- yield* SubscriptionRef.set(options.isConnected, false)
119
- }
140
+ yield* SubscriptionRef.set(options.isConnected, false)
120
141
 
121
- const error = Cause.failureOption(cause)
142
+ const error = Cause.findError(cause)
122
143
  if (
123
144
  options?.retryTransientErrors !== undefined &&
124
- Option.isSome(error) === true &&
125
- (error.value.reason === 'Open' || error.value.reason === 'OpenTimeout')
145
+ Result.isSuccess(error) === true &&
146
+ error.success.reason._tag === 'SocketOpenError'
126
147
  ) {
127
148
  return
128
149
  }
129
150
  // yield* Effect.logError('Error in socket', cause)
130
- return yield* writeResponse({
151
+ currentError = new RpcClientError.RpcClientError({
152
+ reason:
153
+ Result.isSuccess(error) === true
154
+ ? error.success.reason
155
+ : new RpcClientError.RpcClientDefect({
156
+ message: 'Unknown socket error',
157
+ cause: Cause.squash(cause),
158
+ }),
159
+ })
160
+ return yield* broadcast({
131
161
  _tag: 'ClientProtocolError',
132
- error: new RpcClientError.RpcClientError({
133
- reason: 'Protocol',
134
- message: 'Error in socket',
135
- cause: Cause.squash(cause),
136
- }),
162
+ error: currentError,
137
163
  })
138
164
  }),
139
165
  ),
140
166
  // CHANGED: make configurable via schedule
141
- options?.retryTransientErrors !== undefined ? Effect.retry(options.retryTransientErrors) : identity,
167
+ options?.retryTransientErrors !== undefined ? Effect.retry(options.retryTransientErrors) : Function.identity,
142
168
  Effect.annotateLogs({
143
169
  module: 'RpcClient',
144
170
  method: 'makeProtocolSocket',
145
171
  }),
146
172
  Effect.interruptible,
147
173
  Effect.ignore, // Errors are already handled
148
- Effect.provide(Layer.setUnhandledErrorLogLevel(Option.none())),
174
+ Effect.provideService(References.UnhandledLogLevel, undefined),
149
175
  Effect.forkScoped,
150
176
  )
151
177
 
152
178
  return {
153
- send: (request) => {
179
+ send: (clientId, request) => {
180
+ if (currentError !== undefined) {
181
+ return Effect.fail(currentError)
182
+ }
183
+ if (request._tag === 'Request') {
184
+ requestClientMap.set(request.id, clientId)
185
+ }
154
186
  const encoded = parser.encode(request)
155
187
  if (encoded === undefined) return Effect.void
156
188
 
157
- return Effect.orDie(write(encoded))
189
+ return Effect.orDie(writer.write(encoded))
158
190
  },
159
191
  supportsAck: true,
160
192
  supportsTransferables: false,
193
+ codecFor: serialization.codecFor,
161
194
  pinger,
162
195
  }
163
196
  }),
@@ -165,33 +198,33 @@ export const makeProtocolSocketWithIsConnected = (options: {
165
198
 
166
199
  export const SocketPinger = Effect.map(RpcClient.Protocol, (protocol) => (protocol as any).pinger as SocketPinger)
167
200
 
168
- export type SocketPinger = Effect.Effect.Success<ReturnType<typeof makePinger>>
201
+ export type SocketPinger = Effect.Success<ReturnType<typeof makePinger>>
169
202
 
170
203
  const makePinger = Effect.fnUntraced(function* <A, E, R>(
171
204
  writePing: Effect.Effect<A, E, R>,
172
- pingSchedule: Schedule.Schedule<unknown> = Schedule.spaced(10000).pipe(Schedule.addDelay(() => 5000)),
205
+ pingSchedule: Schedule.Schedule<unknown> = Schedule.spaced(10000).pipe(Schedule.addDelay(() => Effect.succeed(5000))),
173
206
  ) {
174
207
  // CHANGED: add manual ping deferreds
175
208
  const manualPingDeferreds = new Set<Deferred.Deferred<void>>()
176
209
 
177
210
  let recievedPong = true
178
- const latch = Effect.unsafeMakeLatch()
211
+ const latch = Latch.makeUnsafe()
179
212
  const reset = () => {
180
213
  recievedPong = true
181
- latch.unsafeClose()
214
+ latch.closeUnsafe()
182
215
  }
183
216
  const onPong = () => {
184
217
  recievedPong = true
185
218
  // CHANGED: mark all manual ping deferreds as done
186
219
  for (const deferred of manualPingDeferreds) {
187
- Deferred.unsafeDone(deferred, Effect.void)
220
+ Deferred.doneUnsafe(deferred, Effect.void)
188
221
  }
189
222
  }
190
223
  yield* Effect.suspend(() => {
191
224
  // Starting new ping
192
- if (recievedPong === false) return latch.open
225
+ if (recievedPong === false) return Effect.asVoid(latch.open)
193
226
  recievedPong = false
194
- return writePing
227
+ return Effect.asVoid(writePing)
195
228
  }).pipe(
196
229
  // CHANGED: make configurable via schedule
197
230
  Effect.schedule(pingSchedule),
@@ -205,7 +238,7 @@ const makePinger = Effect.fnUntraced(function* <A, E, R>(
205
238
  const ping = Effect.gen(function* () {
206
239
  const deferred = yield* Deferred.make<void>()
207
240
  manualPingDeferreds.add(deferred)
208
- yield* deferred
241
+ yield* Deferred.await(deferred)
209
242
  manualPingDeferreds.delete(deferred)
210
243
  })
211
244
 
@@ -1,10 +1,9 @@
1
- import { Duration, pipe, Schedule } from 'effect'
1
+ import { Duration, Effect, pipe, Schedule } from 'effect'
2
2
 
3
3
  export * from 'effect/Schedule'
4
4
 
5
- export const exponentialBackoff10Sec: Schedule.Schedule<Duration.DurationInput> = pipe(
5
+ export const exponentialBackoff10Sec = pipe(
6
6
  Schedule.exponential(Duration.millis(10), 4), // 10ms, 40ms, 160ms, 640ms, 2560ms, ...
7
- Schedule.andThenEither(Schedule.spaced(Duration.seconds(1))),
8
- Schedule.compose(Schedule.elapsed),
9
- Schedule.whileOutput(Duration.lessThanOrEqualTo(Duration.seconds(10))), // max 10 seconds
7
+ Schedule.modifyDelay(({ duration }) => Effect.succeed(Duration.min(duration, Duration.seconds(1)))),
8
+ Schedule.upTo({ duration: Duration.seconds(10) }),
10
9
  )
@@ -8,7 +8,7 @@ describe('debug-diff', () => {
8
8
  test('simple object', () => {
9
9
  const schema = Schema.Struct({
10
10
  a: Schema.String,
11
- b: Schema.Number,
11
+ b: Schema.Finite,
12
12
  })
13
13
 
14
14
  const a = { a: 'hello', b: 1 }
@@ -35,7 +35,7 @@ describe('debug-diff', () => {
35
35
  const schema = Schema.Struct({
36
36
  a: Schema.String,
37
37
  b: Schema.Struct({
38
- c: Schema.Number,
38
+ c: Schema.Finite,
39
39
  }),
40
40
  })
41
41
  const a = { a: 'hello', b: { c: 1 } }
@@ -58,7 +58,7 @@ describe('debug-diff', () => {
58
58
  })
59
59
 
60
60
  test('union', () => {
61
- const schema = Schema.Union(Schema.String, Schema.Number)
61
+ const schema = Schema.Union([Schema.String, Schema.Finite])
62
62
  const a = 'hello'
63
63
  const b = 1
64
64
  const diff = debugDiff(schema)(a, b)
@@ -74,10 +74,10 @@ describe('debug-diff', () => {
74
74
  })
75
75
 
76
76
  test('tagged union', () => {
77
- const schema = Schema.Union(
77
+ const schema = Schema.Union([
78
78
  Schema.TaggedStruct('a', { a: Schema.String }),
79
- Schema.TaggedStruct('b', { b: Schema.Number }),
80
- )
79
+ Schema.TaggedStruct('b', { b: Schema.Finite }),
80
+ ])
81
81
  const a = { _tag: 'a', a: 'hello' } as const
82
82
  const b = { _tag: 'b', b: 1 } as const
83
83
  const diff = debugDiff(schema)(a, b)
@@ -11,7 +11,7 @@ export type DiffItem = {
11
11
  * Diffs two values for a given schema and traverses downwards and returns a list of differences.
12
12
  */
13
13
  export const debugDiff =
14
- <A, I, R>(base: Schema.Schema<A, I, R>) =>
14
+ <A, I, R>(base: Schema.Top) =>
15
15
  (a: A, b: A): DiffItem[] => {
16
16
  const bag = [] as DiffItem[]
17
17
  debugDiffImpl(base.ast, a, b, '', bag)
@@ -19,7 +19,7 @@ export const debugDiff =
19
19
  }
20
20
 
21
21
  const debugDiffImpl = (ast: SchemaAST.AST, a: any, b: any, path: string, bag: DiffItem[]) => {
22
- const eq = Schema.equivalence({ ast } as any)
22
+ const eq = Schema.toEquivalence({ ast } as any)
23
23
  if (eq(a, b) === false) {
24
24
  // bag.push({ path, a, b, ast })
25
25
 
@@ -35,9 +35,8 @@ const debugDiffImpl = (ast: SchemaAST.AST, a: any, b: any, path: string, bag: Di
35
35
  } catch {}
36
36
  }
37
37
  }
38
- } else if (SchemaAST.isTypeLiteral(ast) === true) {
39
- const props = SchemaAST.getPropertySignatures(ast)
40
- for (const prop of props) {
38
+ } else if (SchemaAST.isObjects(ast) === true) {
39
+ for (const prop of ast.propertySignatures) {
41
40
  debugDiffImpl(prop.type, a[prop.name], b[prop.name], `${path}.${prop.name.toString()}`, bag)
42
41
  }
43
42
  } else {
@@ -50,9 +49,8 @@ const debugDiffImpl = (ast: SchemaAST.AST, a: any, b: any, path: string, bag: Di
50
49
  const isTaggedUnion = (ast: SchemaAST.AST): boolean => {
51
50
  if (SchemaAST.isUnion(ast) === true) {
52
51
  return ast.types.every((type) => {
53
- if (SchemaAST.isTypeLiteral(type) === false) return false
54
- const props = SchemaAST.getPropertySignatures(type)
55
- return props.some((prop) => prop.name.toString() === '_tag')
52
+ if (SchemaAST.isObjects(type) === false) return false
53
+ return type.propertySignatures.some((prop) => prop.name.toString() === '_tag')
56
54
  })
57
55
  }
58
56
  return false
@@ -1,18 +1,115 @@
1
- import { Transferable } from '@effect/platform'
2
- import { Effect, Hash, ParseResult, Schema } from 'effect'
3
- import type { ParseError } from 'effect/ParseResult'
4
- import type { ParseOptions } from 'effect/SchemaAST'
5
- import * as SchemaAST from 'effect/SchemaAST'
6
-
7
- import { shouldNeverHappen } from '../../mod.ts'
1
+ import {
2
+ Array,
3
+ Effect,
4
+ Function,
5
+ Hash,
6
+ Option,
7
+ Result,
8
+ Schema,
9
+ SchemaAST,
10
+ SchemaGetter,
11
+ SchemaIssue,
12
+ SchemaTransformation,
13
+ Struct,
14
+ } from 'effect'
15
+ import { Transferable } from 'effect/workers'
16
+
17
+ import { shouldNeverHappen } from '../../misc.ts'
8
18
 
9
19
  export * from 'effect/Schema'
10
20
  export * from './debug-diff.ts'
11
21
 
22
+ type PluckSchema<Fields extends Schema.Struct.Fields, K extends keyof Fields> = Schema.Codec<
23
+ Fields[K]['Type'],
24
+ Schema.Struct<Pick<Fields, K>>['Encoded'],
25
+ Schema.Struct<Pick<Fields, K>>['DecodingServices'],
26
+ Schema.Struct<Pick<Fields, K>>['EncodingServices']
27
+ >
28
+
29
+ export const pluck =
30
+ <const K extends PropertyKey>(key: K) =>
31
+ <Fields extends { readonly [P in K]: Schema.Top }>(
32
+ schema: Schema.Struct<Fields>,
33
+ ): PluckSchema<Fields, K & keyof Fields> => {
34
+ const field = schema.fields[key] as Fields[K & keyof Fields]
35
+
36
+ return schema.mapFields(Struct.pick([key])).pipe(
37
+ Schema.decodeTo(Schema.toType(field), {
38
+ decode: SchemaGetter.transform((whole: any) => whole[key]),
39
+ encode: SchemaGetter.transform((value) => ({ [key]: value }) as any),
40
+ }),
41
+ ) as unknown as PluckSchema<Fields, K & keyof Fields>
42
+ }
43
+
44
+ /**
45
+ * Like {@link fromJsonString}, but the ENCODED form is an *indented* JSON string
46
+ * (default 2-space) instead of compact — for committed/human-read JSON files
47
+ * (package.json, release plans, CI previews) that must stay diff-friendly while
48
+ * still round-tripping through the schema.
49
+ *
50
+ * Use a concrete schema for known shapes (adds validation) or `Schema.Unknown`
51
+ * for open-ended ones (the indented analogue of `fromJsonString(Schema.Unknown)`).
52
+ *
53
+ * TODO collapse into `fromJsonString(schema, { space })` now that Effect 4 accepts a
54
+ * `space` option; kept separate here to keep the Effect upgrade free of release-tooling changes.
55
+ */
56
+ export const jsonStringIndented = <S extends Schema.Top>(schema: S, space: number | string = 2) =>
57
+ schema.pipe(
58
+ Schema.encodeTo(Schema.String, {
59
+ decode: SchemaGetter.parseJson(),
60
+ encode: SchemaGetter.stringifyJson({ space }),
61
+ }),
62
+ )
63
+
64
+ export const head = <S extends Schema.Top>(
65
+ array: Schema.$Array<S>,
66
+ ): Schema.decodeTo<Schema.Option<Schema.toType<S>>, Schema.$Array<S>> =>
67
+ array.pipe(
68
+ Schema.decodeTo(
69
+ Schema.Option(Schema.toType(array.value)),
70
+ SchemaTransformation.transform({
71
+ decode: Array.head,
72
+ encode: Option.match({
73
+ onNone: () => [],
74
+ onSome: Array.of,
75
+ }),
76
+ }),
77
+ ),
78
+ )
79
+
80
+ type HeadOrElse<S extends Schema.Top> = Schema.decodeTo<Schema.toType<S>, Schema.$Array<S>>
81
+
82
+ export const headOrElse: {
83
+ <S extends Schema.Top>(array: Schema.$Array<S>, orElse?: () => S['Type']): HeadOrElse<S>
84
+ (): <S extends Schema.Top>(array: Schema.$Array<S>) => HeadOrElse<S>
85
+ <S extends Schema.Top>(orElse: () => S['Type']): (array: Schema.$Array<S>) => HeadOrElse<S>
86
+ } = Function.dual(
87
+ (args) => Schema.isSchema(args[0]),
88
+ <S extends Schema.Top>(array: Schema.$Array<S>, orElse?: () => S['Type']): HeadOrElse<S> =>
89
+ array.pipe(
90
+ Schema.decodeTo(
91
+ Schema.toType(array.value),
92
+ SchemaTransformation.transformEffect({
93
+ decode: (array) =>
94
+ Array.isReadonlyArrayNonEmpty(array) === true
95
+ ? Effect.succeed(Array.headNonEmpty(array))
96
+ : orElse === undefined
97
+ ? Effect.fail(
98
+ new SchemaIssue.InvalidValue({
99
+ message: 'Unable to retrieve the first element of an empty array',
100
+ }),
101
+ )
102
+ : Effect.succeed(orElse()),
103
+ encode: (value) => Effect.succeed(Array.of(value)),
104
+ }),
105
+ ),
106
+ ),
107
+ )
108
+
12
109
  // NOTE this is a temporary workaround until Effect schema has a better way to hash schemas
13
110
  // https://github.com/Effect-TS/effect/issues/2719
14
111
  // TODO remove this once the issue is resolved
15
- export const hash = (schema: Schema.Schema<any>) => {
112
+ export const hash = (schema: Schema.Top) => {
16
113
  try {
17
114
  return Hash.string(JSON.stringify(schema.ast, null, 2))
18
115
  } catch {
@@ -23,80 +120,44 @@ export const hash = (schema: Schema.Schema<any>) => {
23
120
  }
24
121
  }
25
122
 
26
- const resolveStructAst = (ast: SchemaAST.AST): SchemaAST.AST => {
27
- if (SchemaAST.isTransformation(ast) === true) {
28
- return resolveStructAst(ast.from)
29
- }
30
-
31
- return ast
123
+ export const getResolvedPropertySignatures = (schema: Schema.Top): ReadonlyArray<SchemaAST.PropertySignature> => {
124
+ const resolvedAst = SchemaAST.toType(schema.ast)
125
+ return SchemaAST.isObjects(resolvedAst) === true ? resolvedAst.propertySignatures : []
32
126
  }
33
127
 
34
- export const getResolvedPropertySignatures = (
35
- schema: Schema.Schema.AnyNoContext,
36
- ): ReadonlyArray<SchemaAST.PropertySignature> => {
37
- const resolvedAst = resolveStructAst(schema.ast)
38
- return SchemaAST.getPropertySignatures(resolvedAst)
39
- }
40
-
41
- /** Objects that can be transferred between contexts (workers, etc.) */
42
- type TransferableObject = ArrayBuffer | MessagePort
43
-
44
128
  export const encodeWithTransferables =
45
- <A, I, R>(schema: Schema.Schema<A, I, R>, options?: ParseOptions) =>
46
- (a: A, overrideOptions?: ParseOptions): Effect.Effect<[I, TransferableObject[]], ParseError, R> =>
129
+ <A, I>(schema: Schema.Codec<A, I>, options?: SchemaAST.ParseOptions) =>
130
+ (a: A, overrideOptions?: SchemaAST.ParseOptions) =>
47
131
  Effect.gen(function* () {
48
132
  const collector = yield* Transferable.makeCollector
49
133
 
50
- const encoded: I = yield* Schema.encode(schema, options)(a, overrideOptions).pipe(
134
+ const encoded = yield* Schema.encodeEffect(schema, options)(a, overrideOptions).pipe(
51
135
  Effect.provideService(Transferable.Collector, collector),
52
136
  )
53
137
 
54
- return [encoded, collector.unsafeRead() as TransferableObject[]]
138
+ return [encoded, collector.readUnsafe()] as const
55
139
  })
56
140
 
57
141
  export const decodeSyncDebug: <A, I>(
58
- schema: Schema.Schema<A, I>,
142
+ schema: Schema.Codec<A, I>,
59
143
  options?: SchemaAST.ParseOptions,
60
144
  ) => (i: I, overrideOptions?: SchemaAST.ParseOptions) => A = (schema, options) => (input, overrideOptions) => {
61
- const res = Schema.decodeEither(schema, options)(input, overrideOptions)
62
- if (res._tag === 'Left') {
63
- return shouldNeverHappen(`decodeSyncDebug failed:`, res.left)
145
+ const res = Schema.decodeResult(schema, options)(input, overrideOptions)
146
+ if (Result.isFailure(res) === true) {
147
+ return shouldNeverHappen(`decodeSyncDebug failed:`, res.failure)
64
148
  } else {
65
- return res.right
149
+ return res.success
66
150
  }
67
151
  }
68
152
 
69
153
  export const encodeSyncDebug: <A, I>(
70
- schema: Schema.Schema<A, I>,
154
+ schema: Schema.Codec<A, I>,
71
155
  options?: SchemaAST.ParseOptions,
72
156
  ) => (a: A, overrideOptions?: SchemaAST.ParseOptions) => I = (schema, options) => (input, overrideOptions) => {
73
- const res = Schema.encodeEither(schema, options)(input, overrideOptions)
74
- if (res._tag === 'Left') {
75
- return shouldNeverHappen(`encodeSyncDebug failed:`, res.left)
157
+ const res = Schema.encodeResult(schema, options)(input, overrideOptions)
158
+ if (Result.isFailure(res) === true) {
159
+ return shouldNeverHappen(`encodeSyncDebug failed:`, res.failure)
76
160
  } else {
77
- return res.right
161
+ return res.success
78
162
  }
79
163
  }
80
-
81
- export const swap = <A, I, R>(schema: Schema.Schema<A, I, R>): Schema.Schema<I, A, R> =>
82
- Schema.transformOrFail(Schema.typeSchema(schema), Schema.encodedSchema(schema), {
83
- decode: ParseResult.encode(schema),
84
- encode: ParseResult.decode(schema),
85
- })
86
-
87
- export const Base64FromUint8Array: Schema.Schema<string, Uint8Array> = swap(Schema.Uint8ArrayFromBase64)
88
-
89
- export interface JsonArray extends ReadonlyArray<JsonValue> {}
90
- export interface JsonObject {
91
- [key: string]: JsonValue
92
- }
93
- export type JsonValue = string | number | boolean | null | JsonObject | JsonArray
94
-
95
- export const JsonValue: Schema.Schema<JsonValue> = Schema.Union(
96
- Schema.String,
97
- Schema.Number,
98
- Schema.Boolean,
99
- Schema.Null,
100
- Schema.Array(Schema.suspend(() => JsonValue)),
101
- Schema.Record({ key: Schema.String, value: Schema.suspend(() => JsonValue) }),
102
- ).annotations({ identifier: 'JsonValue' })