@livestore/utils 0.4.0 → 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 (247) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/NoopTracer.d.ts +7 -8
  3. package/dist/NoopTracer.d.ts.map +1 -1
  4. package/dist/NoopTracer.js +3 -18
  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/browser/Opfs/Opfs.d.ts +179 -29
  11. package/dist/browser/Opfs/Opfs.d.ts.map +1 -1
  12. package/dist/browser/Opfs/Opfs.js +132 -307
  13. package/dist/browser/Opfs/Opfs.js.map +1 -1
  14. package/dist/browser/Opfs/debug-utils.d.ts.map +1 -1
  15. package/dist/browser/Opfs/debug-utils.js +5 -4
  16. package/dist/browser/Opfs/debug-utils.js.map +1 -1
  17. package/dist/browser/Opfs/utils.d.ts +8 -8
  18. package/dist/browser/Opfs/utils.d.ts.map +1 -1
  19. package/dist/browser/Opfs/utils.js +35 -26
  20. package/dist/browser/Opfs/utils.js.map +1 -1
  21. package/dist/browser/QuotaExceededError.d.ts +10 -9
  22. package/dist/browser/QuotaExceededError.d.ts.map +1 -1
  23. package/dist/browser/WebChannelBrowser.d.ts.map +1 -1
  24. package/dist/browser/WebChannelBrowser.js +21 -10
  25. package/dist/browser/WebChannelBrowser.js.map +1 -1
  26. package/dist/browser/WebError.d.ts +54 -86
  27. package/dist/browser/WebError.d.ts.map +1 -1
  28. package/dist/browser/WebError.js +91 -84
  29. package/dist/browser/WebError.js.map +1 -1
  30. package/dist/browser/WebError.test.js +6 -6
  31. package/dist/browser/WebError.test.js.map +1 -1
  32. package/dist/browser/WebLock.d.ts.map +1 -1
  33. package/dist/browser/WebLock.js +13 -13
  34. package/dist/browser/WebLock.js.map +1 -1
  35. package/dist/effect/Debug.d.ts +4 -7
  36. package/dist/effect/Debug.d.ts.map +1 -1
  37. package/dist/effect/Debug.js +32 -36
  38. package/dist/effect/Debug.js.map +1 -1
  39. package/dist/effect/Effect.d.ts +31 -22
  40. package/dist/effect/Effect.d.ts.map +1 -1
  41. package/dist/effect/Effect.js +51 -47
  42. package/dist/effect/Effect.js.map +1 -1
  43. package/dist/effect/Effect.test.d.ts +2 -0
  44. package/dist/effect/Effect.test.d.ts.map +1 -0
  45. package/dist/effect/Effect.test.js +72 -0
  46. package/dist/effect/Effect.test.js.map +1 -0
  47. package/dist/effect/Error.d.ts +4 -6
  48. package/dist/effect/Error.d.ts.map +1 -1
  49. package/dist/effect/Logger.d.ts +0 -6
  50. package/dist/effect/Logger.d.ts.map +1 -1
  51. package/dist/effect/Logger.js +0 -32
  52. package/dist/effect/Logger.js.map +1 -1
  53. package/dist/effect/OtelTracer.d.ts +1 -1
  54. package/dist/effect/OtelTracer.d.ts.map +1 -1
  55. package/dist/effect/OtelTracer.js +2 -3
  56. package/dist/effect/OtelTracer.js.map +1 -1
  57. package/dist/effect/RpcClient.d.ts +7 -7
  58. package/dist/effect/RpcClient.d.ts.map +1 -1
  59. package/dist/effect/RpcClient.js +72 -52
  60. package/dist/effect/RpcClient.js.map +1 -1
  61. package/dist/effect/Schedule.d.ts +1 -1
  62. package/dist/effect/Schedule.d.ts.map +1 -1
  63. package/dist/effect/Schedule.js +2 -2
  64. package/dist/effect/Schedule.js.map +1 -1
  65. package/dist/effect/Schema/debug-diff.d.ts +1 -1
  66. package/dist/effect/Schema/debug-diff.d.ts.map +1 -1
  67. package/dist/effect/Schema/debug-diff.js +5 -7
  68. package/dist/effect/Schema/debug-diff.js.map +1 -1
  69. package/dist/effect/Schema/debug-diff.test.js +7 -4
  70. package/dist/effect/Schema/debug-diff.test.js.map +1 -1
  71. package/dist/effect/Schema/index.d.ts +28 -20
  72. package/dist/effect/Schema/index.d.ts.map +1 -1
  73. package/dist/effect/Schema/index.js +55 -28
  74. package/dist/effect/Schema/index.js.map +1 -1
  75. package/dist/effect/Stream.d.ts +13 -5
  76. package/dist/effect/Stream.d.ts.map +1 -1
  77. package/dist/effect/Stream.js +19 -12
  78. package/dist/effect/Stream.js.map +1 -1
  79. package/dist/effect/Stream.test.js +9 -9
  80. package/dist/effect/Stream.test.js.map +1 -1
  81. package/dist/effect/Subscribable.d.ts +9 -3
  82. package/dist/effect/Subscribable.d.ts.map +1 -1
  83. package/dist/effect/Subscribable.js +20 -35
  84. package/dist/effect/Subscribable.js.map +1 -1
  85. package/dist/effect/SubscriptionRef.d.ts +5 -6
  86. package/dist/effect/SubscriptionRef.d.ts.map +1 -1
  87. package/dist/effect/SubscriptionRef.js +2 -3
  88. package/dist/effect/SubscriptionRef.js.map +1 -1
  89. package/dist/effect/TaskTracing.d.ts +1 -1
  90. package/dist/effect/TaskTracing.d.ts.map +1 -1
  91. package/dist/effect/TaskTracing.js +12 -16
  92. package/dist/effect/TaskTracing.js.map +1 -1
  93. package/dist/effect/WebChannel/WebChannel.d.ts +7 -7
  94. package/dist/effect/WebChannel/WebChannel.d.ts.map +1 -1
  95. package/dist/effect/WebChannel/WebChannel.js +64 -43
  96. package/dist/effect/WebChannel/WebChannel.js.map +1 -1
  97. package/dist/effect/WebChannel/WebChannel.test.js +15 -15
  98. package/dist/effect/WebChannel/WebChannel.test.js.map +1 -1
  99. package/dist/effect/WebChannel/broadcastChannelWithAck.d.ts.map +1 -1
  100. package/dist/effect/WebChannel/broadcastChannelWithAck.js +16 -9
  101. package/dist/effect/WebChannel/broadcastChannelWithAck.js.map +1 -1
  102. package/dist/effect/WebChannel/common.d.ts +16 -17
  103. package/dist/effect/WebChannel/common.d.ts.map +1 -1
  104. package/dist/effect/WebChannel/common.js +6 -6
  105. package/dist/effect/WebChannel/common.js.map +1 -1
  106. package/dist/effect/WebSocket.d.ts +4 -6
  107. package/dist/effect/WebSocket.d.ts.map +1 -1
  108. package/dist/effect/WebSocket.js +4 -4
  109. package/dist/effect/WebSocket.js.map +1 -1
  110. package/dist/effect/WebSocket.test.js +2 -2
  111. package/dist/effect/WebSocket.test.js.map +1 -1
  112. package/dist/effect/mod.browser-build.test.d.ts +2 -0
  113. package/dist/effect/mod.browser-build.test.d.ts.map +1 -0
  114. package/dist/effect/mod.browser-build.test.js +18 -0
  115. package/dist/effect/mod.browser-build.test.js.map +1 -0
  116. package/dist/effect/mod.d.ts +13 -15
  117. package/dist/effect/mod.d.ts.map +1 -1
  118. package/dist/effect/mod.js +13 -18
  119. package/dist/effect/mod.js.map +1 -1
  120. package/dist/effect/spanEvent.d.ts.map +1 -1
  121. package/dist/effect/spanEvent.js +2 -2
  122. package/dist/effect/spanEvent.js.map +1 -1
  123. package/dist/effect/spanEvent.test.js +10 -13
  124. package/dist/effect/spanEvent.test.js.map +1 -1
  125. package/dist/fast-deep-equal.d.ts.map +1 -1
  126. package/dist/fast-deep-equal.js +1 -2
  127. package/dist/fast-deep-equal.js.map +1 -1
  128. package/dist/misc.js +2 -2
  129. package/dist/misc.js.map +1 -1
  130. package/dist/mod.d.ts +0 -35
  131. package/dist/mod.d.ts.map +1 -1
  132. package/dist/mod.js +0 -8
  133. package/dist/mod.js.map +1 -1
  134. package/dist/node/mod.d.ts +17 -11
  135. package/dist/node/mod.d.ts.map +1 -1
  136. package/dist/node/mod.js +23 -16
  137. package/dist/node/mod.js.map +1 -1
  138. package/dist/object/index.d.ts +0 -9
  139. package/dist/object/index.d.ts.map +1 -1
  140. package/dist/object/index.js +0 -9
  141. package/dist/object/index.js.map +1 -1
  142. package/dist/string.d.ts +0 -2
  143. package/dist/string.d.ts.map +1 -1
  144. package/dist/string.js +0 -2
  145. package/dist/string.js.map +1 -1
  146. package/package.json +18 -36
  147. package/src/NoopTracer.test.ts +20 -0
  148. package/src/NoopTracer.ts +18 -41
  149. package/src/browser/Opfs/Opfs.ts +304 -277
  150. package/src/browser/Opfs/debug-utils.ts +7 -6
  151. package/src/browser/Opfs/utils.ts +36 -26
  152. package/src/browser/QuotaExceededError.ts +13 -10
  153. package/src/browser/WebChannelBrowser.ts +25 -11
  154. package/src/browser/WebError.test.ts +6 -6
  155. package/src/browser/WebError.ts +107 -133
  156. package/src/browser/WebLock.ts +13 -14
  157. package/src/effect/Debug.ts +66 -59
  158. package/src/effect/Effect.test.ts +117 -0
  159. package/src/effect/Effect.ts +84 -71
  160. package/src/effect/Logger.ts +0 -48
  161. package/src/effect/OtelTracer.ts +2 -3
  162. package/src/effect/RpcClient.ts +80 -61
  163. package/src/effect/Schedule.ts +4 -5
  164. package/src/effect/Schema/debug-diff.test.ts +6 -6
  165. package/src/effect/Schema/debug-diff.ts +6 -8
  166. package/src/effect/Schema/index.ts +122 -61
  167. package/src/effect/Stream.test.ts +9 -9
  168. package/src/effect/Stream.ts +23 -16
  169. package/src/effect/Subscribable.ts +31 -42
  170. package/src/effect/SubscriptionRef.ts +8 -12
  171. package/src/effect/TaskTracing.ts +13 -16
  172. package/src/effect/WebChannel/WebChannel.test.ts +23 -23
  173. package/src/effect/WebChannel/WebChannel.ts +87 -55
  174. package/src/effect/WebChannel/broadcastChannelWithAck.ts +21 -11
  175. package/src/effect/WebChannel/common.ts +14 -15
  176. package/src/effect/WebSocket.test.ts +2 -2
  177. package/src/effect/WebSocket.ts +9 -6
  178. package/src/effect/mod.browser-build.test.ts +19 -0
  179. package/src/effect/mod.ts +35 -69
  180. package/src/effect/spanEvent.test.ts +10 -13
  181. package/src/effect/spanEvent.ts +5 -3
  182. package/src/fast-deep-equal.ts +1 -2
  183. package/src/misc.ts +2 -2
  184. package/src/mod.ts +0 -36
  185. package/src/node/mod.ts +26 -20
  186. package/src/object/index.ts +0 -24
  187. package/src/string.ts +0 -3
  188. package/dist/effect/BucketQueue.d.ts +0 -20
  189. package/dist/effect/BucketQueue.d.ts.map +0 -1
  190. package/dist/effect/BucketQueue.js +0 -34
  191. package/dist/effect/BucketQueue.js.map +0 -1
  192. package/dist/effect/Scheduler.d.ts +0 -4
  193. package/dist/effect/Scheduler.d.ts.map +0 -1
  194. package/dist/effect/Scheduler.js +0 -9
  195. package/dist/effect/Scheduler.js.map +0 -1
  196. package/dist/effect/ServiceContext.d.ts +0 -37
  197. package/dist/effect/ServiceContext.d.ts.map +0 -1
  198. package/dist/effect/ServiceContext.js +0 -55
  199. package/dist/effect/ServiceContext.js.map +0 -1
  200. package/dist/guards.d.ts +0 -20
  201. package/dist/guards.d.ts.map +0 -1
  202. package/dist/guards.js +0 -20
  203. package/dist/guards.js.map +0 -1
  204. package/dist/node/ChildProcessRunner/ChildProcessRunner.d.ts +0 -4
  205. package/dist/node/ChildProcessRunner/ChildProcessRunner.d.ts.map +0 -1
  206. package/dist/node/ChildProcessRunner/ChildProcessRunner.js +0 -128
  207. package/dist/node/ChildProcessRunner/ChildProcessRunner.js.map +0 -1
  208. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts +0 -10
  209. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts.map +0 -1
  210. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js +0 -217
  211. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js.map +0 -1
  212. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.d.ts +0 -84
  213. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.d.ts.map +0 -1
  214. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.js +0 -68
  215. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.js.map +0 -1
  216. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.d.ts +0 -2
  217. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.d.ts.map +0 -1
  218. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js +0 -52
  219. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js.map +0 -1
  220. package/dist/node/ChildProcessRunner/ChildProcessWorker.d.ts +0 -23
  221. package/dist/node/ChildProcessRunner/ChildProcessWorker.d.ts.map +0 -1
  222. package/dist/node/ChildProcessRunner/ChildProcessWorker.js +0 -138
  223. package/dist/node/ChildProcessRunner/ChildProcessWorker.js.map +0 -1
  224. package/dist/object/omit.d.ts +0 -3
  225. package/dist/object/omit.d.ts.map +0 -1
  226. package/dist/object/omit.js +0 -14
  227. package/dist/object/omit.js.map +0 -1
  228. package/dist/object/pick.d.ts +0 -14
  229. package/dist/object/pick.d.ts.map +0 -1
  230. package/dist/object/pick.js +0 -17
  231. package/dist/object/pick.js.map +0 -1
  232. package/dist/time.d.ts +0 -12
  233. package/dist/time.d.ts.map +0 -1
  234. package/dist/time.js +0 -22
  235. package/dist/time.js.map +0 -1
  236. package/src/effect/BucketQueue.ts +0 -49
  237. package/src/effect/Scheduler.ts +0 -13
  238. package/src/effect/ServiceContext.ts +0 -108
  239. package/src/guards.ts +0 -23
  240. package/src/node/ChildProcessRunner/ChildProcessRunner.ts +0 -148
  241. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.ts +0 -311
  242. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/schema.ts +0 -78
  243. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.ts +0 -65
  244. package/src/node/ChildProcessRunner/ChildProcessWorker.ts +0 -169
  245. package/src/object/omit.ts +0 -17
  246. package/src/object/pick.ts +0 -27
  247. package/src/time.ts +0 -25
@@ -8,7 +8,7 @@ import { Effect, Stream } from 'effect'
8
8
  import prettyBytes from 'pretty-bytes'
9
9
 
10
10
  import type * as WebError from '../WebError.ts'
11
- import { Opfs } from './Opfs.ts'
11
+ import * as Opfs from './Opfs.ts'
12
12
  import { getDirectoryHandleByPath, getMetadata, remove } from './utils.ts'
13
13
 
14
14
  /**
@@ -33,14 +33,15 @@ const ROOT_NAME = '/'
33
33
  * Materialize the entire OPFS tree starting from the origin root.
34
34
  */
35
35
  const buildTree = Effect.fn('@livestore/utils:Opfs.buildTree')(function* () {
36
- const rootHandle = yield* Opfs.getRootDirectoryHandle
36
+ const opfs = yield* Opfs.Opfs
37
+ const rootHandle = yield* opfs.getRootDirectoryHandle
37
38
 
38
39
  const collectDirectory = (
39
40
  handle: FileSystemDirectoryHandle,
40
41
  pathSegments: ReadonlyArray<string>,
41
- ): Effect.Effect<OpfsTreeNode, WebError.WebError, Opfs> =>
42
+ ): Effect.Effect<OpfsTreeNode, WebError.WebError, Opfs.Opfs> =>
42
43
  Effect.gen(function* () {
43
- const handlesStream = yield* Opfs.values(handle)
44
+ const handlesStream = opfs.values(handle)
44
45
  const handles = yield* handlesStream.pipe(
45
46
  Stream.runCollect,
46
47
  Effect.map((chunk) => Array.from(chunk).sort((a, b) => a.name.localeCompare(b.name))),
@@ -134,8 +135,8 @@ const resetTree = remove('/')
134
135
 
135
136
  const getDirHandle = (path: string, options?: FileSystemGetDirectoryOptions) => getDirectoryHandleByPath(path, options)
136
137
 
137
- const runOpfsEffect = <A, E>(effect: Effect.Effect<A, E, Opfs>) =>
138
- effect.pipe(Effect.provide(Opfs.Default), Effect.runPromise)
138
+ const runOpfsEffect = <A, E>(effect: Effect.Effect<A, E, Opfs.Opfs>) =>
139
+ effect.pipe(Effect.provide(Opfs.layer), Effect.runPromise)
139
140
 
140
141
  export const debugUtils = {
141
142
  /**
@@ -1,6 +1,6 @@
1
1
  import { Effect, Stream } from 'effect'
2
2
 
3
- import { Opfs, OpfsError } from './Opfs.ts'
3
+ import * as Opfs from './Opfs.ts'
4
4
 
5
5
  /**
6
6
  * Set of path segments that are forbidden by the File System specification.
@@ -25,14 +25,14 @@ const parsePathSegments = (path: string) =>
25
25
  const segments = path.split('/').filter((segment) => segment.length > 0)
26
26
 
27
27
  if (segments.length === 0) {
28
- return yield* new OpfsError({
28
+ return yield* new Opfs.OpfsError({
29
29
  message: `Invalid OPFS path '${path}': path must contain at least one non-empty segment`,
30
30
  })
31
31
  }
32
32
 
33
33
  for (const segment of segments) {
34
34
  if (DISALLOWED_SEGMENTS.has(segment) === true) {
35
- return yield* new OpfsError({
35
+ return yield* new Opfs.OpfsError({
36
36
  message: `Invalid OPFS path '${path}': segment '${segment}' is not supported`,
37
37
  })
38
38
  }
@@ -65,11 +65,12 @@ const splitPathSegments = (segments: ReadonlyArray<string>) => ({
65
65
  */
66
66
  const traverseDirectoryPath = (segments: ReadonlyArray<string>, options?: FileSystemGetDirectoryOptions) =>
67
67
  Effect.gen(function* () {
68
- let currentDirHandle = yield* Opfs.getRootDirectoryHandle
68
+ const opfs = yield* Opfs.Opfs
69
+ let currentDirHandle = yield* opfs.getRootDirectoryHandle
69
70
 
70
71
  for (let index = 0; index < segments.length; index++) {
71
72
  const segment = segments[index]!
72
- currentDirHandle = yield* Opfs.getDirectoryHandle(currentDirHandle, segment, options)
73
+ currentDirHandle = yield* opfs.getDirectoryHandle(currentDirHandle, segment, options)
73
74
  }
74
75
 
75
76
  return currentDirHandle
@@ -83,14 +84,15 @@ const traverseDirectoryPath = (segments: ReadonlyArray<string>, options?: FileSy
83
84
  */
84
85
  const ensureDirectoryPath = (segments: ReadonlyArray<string>, options: { readonly recursive: boolean }) =>
85
86
  Effect.gen(function* () {
86
- let currentDirHandle = yield* Opfs.getRootDirectoryHandle
87
+ const opfs = yield* Opfs.Opfs
88
+ let currentDirHandle = yield* opfs.getRootDirectoryHandle
87
89
 
88
90
  for (let index = 0; index < segments.length; index++) {
89
91
  const segment = segments[index]!
90
92
  const isLast = index === segments.length - 1
91
93
  const shouldCreate = options.recursive || isLast
92
94
 
93
- currentDirHandle = yield* Opfs.getDirectoryHandle(
95
+ currentDirHandle = yield* opfs.getDirectoryHandle(
94
96
  currentDirHandle,
95
97
  segment,
96
98
  shouldCreate === true ? { create: true } : undefined,
@@ -111,7 +113,10 @@ export const getDirectoryHandleByPath = Effect.fn('@livestore/utils:Opfs.getDire
111
113
  path: string,
112
114
  options?: FileSystemGetDirectoryOptions,
113
115
  ) {
114
- if (isRootPath(path) === true) return yield* Opfs.getRootDirectoryHandle
116
+ if (isRootPath(path) === true) {
117
+ const opfs = yield* Opfs.Opfs
118
+ return yield* opfs.getRootDirectoryHandle
119
+ }
115
120
 
116
121
  const pathSegments = yield* parsePathSegments(path)
117
122
  return yield* traverseDirectoryPath(pathSegments, options)
@@ -128,12 +133,13 @@ export const remove = Effect.fn('@livestore/utils:Opfs.remove')(function* (
128
133
  options?: { readonly recursive?: boolean },
129
134
  ) {
130
135
  const recursive = options?.recursive ?? false
136
+ const opfs = yield* Opfs.Opfs
131
137
 
132
138
  if (isRootPath(path) === true) {
133
- const rootHandle = yield* Opfs.getRootDirectoryHandle
134
- const handlesStream = yield* Opfs.values(rootHandle)
139
+ const rootHandle = yield* opfs.getRootDirectoryHandle
140
+ const handlesStream = opfs.values(rootHandle)
135
141
  yield* handlesStream.pipe(
136
- Stream.runForEach((handle) => Opfs.removeEntry(rootHandle, handle.name, { recursive: true })),
142
+ Stream.runForEach((handle) => opfs.removeEntry(rootHandle, handle.name, { recursive: true })),
137
143
  )
138
144
  return
139
145
  }
@@ -142,7 +148,7 @@ export const remove = Effect.fn('@livestore/utils:Opfs.remove')(function* (
142
148
  const { parentSegments, leafSegment: targetName } = splitPathSegments(pathSegments)
143
149
  const parentDirHandle = yield* traverseDirectoryPath(parentSegments)
144
150
 
145
- yield* Opfs.removeEntry(parentDirHandle, targetName, { recursive })
151
+ yield* opfs.removeEntry(parentDirHandle, targetName, { recursive })
146
152
  })
147
153
 
148
154
  /**
@@ -158,13 +164,14 @@ export const exists = Effect.fn('@livestore/utils:Opfs.exists')(function* (path:
158
164
  const { parentSegments, leafSegment: targetName } = splitPathSegments(pathSegments)
159
165
 
160
166
  const parentDirHandle = yield* traverseDirectoryPath(parentSegments, { create: false }).pipe(
161
- Effect.catchTag('NotFoundError', () => Effect.succeed(undefined)),
167
+ Effect.catchTag('NotFoundError', () => Effect.void),
162
168
  )
163
169
 
164
170
  if (parentDirHandle === undefined) return false
165
171
 
166
- return yield* Opfs.getFileHandle(parentDirHandle, targetName).pipe(
167
- Effect.orElse(() => Opfs.getDirectoryHandle(parentDirHandle, targetName, { create: false })),
172
+ const opfs = yield* Opfs.Opfs
173
+ return yield* opfs.getFileHandle(parentDirHandle, targetName).pipe(
174
+ Effect.catch(() => opfs.getDirectoryHandle(parentDirHandle, targetName, { create: false })),
168
175
  Effect.as(true),
169
176
  Effect.catchTag('NotFoundError', () => Effect.succeed(false)),
170
177
  )
@@ -196,7 +203,8 @@ export const makeDirectory = Effect.fn('@livestore/utils:Opfs.makeDirectory')(fu
196
203
  * @returns Object containing name, size, MIME type, and last modification timestamp.
197
204
  */
198
205
  export const getMetadata = Effect.fn('@livestore/utils:Opfs.getMetadata')(function* (handle: FileSystemFileHandle) {
199
- return yield* Opfs.getFile(handle).pipe(
206
+ const opfs = yield* Opfs.Opfs
207
+ return yield* opfs.getFile(handle).pipe(
200
208
  Effect.map((file) => ({
201
209
  name: file.name,
202
210
  size: file.size,
@@ -217,7 +225,7 @@ export const getMetadata = Effect.fn('@livestore/utils:Opfs.getMetadata')(functi
217
225
  */
218
226
  export const writeFile = Effect.fn('@livestore/utils:Opfs.writeFile')(function* (path: string, data: Uint8Array) {
219
227
  if (isRootPath(path) === true) {
220
- return yield* new OpfsError({
228
+ return yield* new Opfs.OpfsError({
221
229
  message: `Invalid OPFS path '${path}': cannot write file directly to the OPFS root`,
222
230
  })
223
231
  }
@@ -228,9 +236,10 @@ export const writeFile = Effect.fn('@livestore/utils:Opfs.writeFile')(function*
228
236
  return yield* Effect.scoped(
229
237
  Effect.gen(function* () {
230
238
  const parentDirHandle = yield* traverseDirectoryPath(parentSegments)
231
- const fileHandle = yield* Opfs.getFileHandle(parentDirHandle, fileName, { create: true })
239
+ const opfs = yield* Opfs.Opfs
240
+ const fileHandle = yield* opfs.getFileHandle(parentDirHandle, fileName, { create: true })
232
241
 
233
- yield* Opfs.writeFile(fileHandle, new Uint8Array(data), {
242
+ yield* opfs.writeFile(fileHandle, new Uint8Array(data), {
234
243
  keepExistingData: false,
235
244
  })
236
245
  }),
@@ -254,7 +263,7 @@ export const syncWriteFile = Effect.fn('@livestore/utils:Opfs.syncWriteFile')(fu
254
263
  data: Uint8Array,
255
264
  ) {
256
265
  if (isRootPath(path) === true) {
257
- return yield* new OpfsError({
266
+ return yield* new Opfs.OpfsError({
258
267
  message: `Invalid OPFS path '${path}': cannot write file directly to the OPFS root`,
259
268
  })
260
269
  }
@@ -265,23 +274,24 @@ export const syncWriteFile = Effect.fn('@livestore/utils:Opfs.syncWriteFile')(fu
265
274
  return yield* Effect.scoped(
266
275
  Effect.gen(function* () {
267
276
  const parentDirHandle = yield* traverseDirectoryPath(parentSegments)
268
- const fileHandle = yield* Opfs.getFileHandle(parentDirHandle, fileName, { create: true })
269
- const syncHandle = yield* Opfs.createSyncAccessHandle(fileHandle)
277
+ const opfs = yield* Opfs.Opfs
278
+ const fileHandle = yield* opfs.getFileHandle(parentDirHandle, fileName, { create: true })
279
+ const syncHandle = yield* opfs.createSyncAccessHandle(fileHandle)
270
280
 
271
- yield* Opfs.syncTruncate(syncHandle, 0)
281
+ yield* opfs.syncTruncate(syncHandle, 0)
272
282
 
273
283
  let offset = 0
274
284
  while (offset < data.byteLength) {
275
- const wrote = yield* Opfs.syncWrite(syncHandle, data.subarray(offset), { at: offset })
285
+ const wrote = yield* opfs.syncWrite(syncHandle, data.subarray(offset), { at: offset })
276
286
  if (wrote === 0) {
277
- return yield* new OpfsError({
287
+ return yield* new Opfs.OpfsError({
278
288
  message: `Short write: wrote ${offset} of ${data.byteLength} bytes.`,
279
289
  })
280
290
  }
281
291
  offset += Number(wrote)
282
292
  }
283
293
 
284
- yield* Opfs.syncFlush(syncHandle)
294
+ yield* opfs.syncFlush(syncHandle)
285
295
  }),
286
296
  )
287
297
  })
@@ -26,17 +26,22 @@ declare global {
26
26
  */
27
27
  readonly message: string
28
28
  /**
29
- * A number representing the quota limit in bytes, or undefined.
29
+ * A number representing the quota limit in bytes, or null.
30
30
  *
31
31
  * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/QuotaExceededError/quota)
32
32
  */
33
- readonly quota?: number
33
+ readonly quota: number | null
34
34
  /**
35
- * A number representing the requested amount of storage in bytes, or undefined.
35
+ * A number representing the requested amount of storage in bytes, or null.
36
36
  *
37
37
  * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/QuotaExceededError/requested)
38
38
  */
39
- readonly requested?: number
39
+ readonly requested: number | null
40
+ }
41
+
42
+ interface QuotaExceededErrorOptions {
43
+ quota?: number
44
+ requested?: number
40
45
  }
41
46
 
42
47
  /**
@@ -48,10 +53,8 @@ declare global {
48
53
  *
49
54
  * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/QuotaExceededError)
50
55
  */
51
- var QuotaExceededError:
52
- | {
53
- prototype: QuotaExceededError
54
- new (message?: string, options?: { quota: number; requested: number }): QuotaExceededError
55
- }
56
- | undefined
56
+ var QuotaExceededError: {
57
+ prototype: QuotaExceededError
58
+ new (message?: string, options?: QuotaExceededErrorOptions): QuotaExceededError
59
+ }
57
60
  }
@@ -1,4 +1,4 @@
1
- import { Deferred, Exit, Scope } from 'effect'
1
+ import { Cause, Deferred, Exit, Queue, Scope } from 'effect'
2
2
 
3
3
  import * as Effect from '../effect/Effect.ts'
4
4
  import * as Schema from '../effect/Schema/index.ts'
@@ -25,21 +25,26 @@ export const broadcastChannel = <MsgListen, MsgSend, MsgListenEncoded, MsgSendEn
25
25
 
26
26
  const channel = new BroadcastChannel(channelName)
27
27
 
28
- yield* Effect.addFinalizer(() => Effect.try(() => channel.close()).pipe(Effect.ignoreLogged))
28
+ yield* Effect.addFinalizer(() =>
29
+ Effect.try({
30
+ try: () => channel.close(),
31
+ catch: (cause) => new Cause.UnknownError(cause),
32
+ }).pipe(Effect.ignore),
33
+ )
29
34
 
30
35
  const send = (message: MsgSend) =>
31
36
  Effect.gen(function* () {
32
- const messageEncoded = yield* Schema.encode(schema.send)(message)
37
+ const messageEncoded = yield* Schema.encodeEffect(schema.send)(message)
33
38
  channel.postMessage(messageEncoded)
34
39
  })
35
40
 
36
41
  // TODO also listen to `messageerror` in parallel
37
42
  const listen = Stream.fromEventListener<MessageEvent>(channel, 'message').pipe(
38
- Stream.map((_) => Schema.decodeEither(schema.listen)(_.data)),
43
+ Stream.map((_) => Schema.decodeResult(schema.listen)(_.data)),
39
44
  listenToDebugPing(channelName),
40
45
  )
41
46
 
42
- const closedDeferred = yield* Deferred.make<void>().pipe(Effect.acquireRelease(Deferred.done(Exit.void)))
47
+ const closedDeferred = yield* Effect.acquireRelease(Deferred.make<void>(), Deferred.done(Exit.void))
43
48
  const supportsTransferables = false
44
49
 
45
50
  return {
@@ -85,13 +90,22 @@ export const windowChannel = <MsgListen, MsgSend, MsgListenEncoded, MsgSendEncod
85
90
  message: schema.listen,
86
91
  from: Schema.Literal(ids.other),
87
92
  to: Schema.Literal(ids.own),
88
- }).annotations({ title: 'webmesh.WindowMessageListen' })
93
+ }).annotate({ title: 'webmesh.WindowMessageListen' })
89
94
 
90
95
  const WindowMessageSend = Schema.Struct({
91
96
  message: schema.send,
92
97
  from: Schema.Literal(ids.own),
93
98
  to: Schema.Literal(ids.other),
94
- }).annotations({ title: 'webmesh.WindowMessageSend' })
99
+ }).annotate({ title: 'webmesh.WindowMessageSend' })
100
+
101
+ // `window.postMessage` can fire before a lazily-started stream has installed its listener.
102
+ // Buffer events from channel construction time so Effect v4 scheduling cannot drop them.
103
+ const messageQueue = yield* Effect.acquireRelease(Queue.unbounded<MessageEvent>(), Queue.shutdown)
104
+ const handler = (event: MessageEvent) => {
105
+ Queue.offerUnsafe(messageQueue, event)
106
+ }
107
+ listenWindow.addEventListener('message', handler)
108
+ yield* Effect.addFinalizer(() => Effect.sync(() => listenWindow.removeEventListener('message', handler)))
95
109
 
96
110
  const send = (message: MsgSend) =>
97
111
  Effect.gen(function* () {
@@ -105,16 +119,16 @@ export const windowChannel = <MsgListen, MsgSend, MsgListenEncoded, MsgSendEncod
105
119
  sendWindow.postMessage(messageEncoded, targetOrigin, transferables)
106
120
  })
107
121
 
108
- const listen = Stream.fromEventListener<MessageEvent>(listenWindow, 'message').pipe(
109
- Stream.filter((_) => Schema.is(Schema.encodedSchema(WindowMessageListen))(_.data)),
122
+ const listen = Stream.fromQueue(messageQueue).pipe(
123
+ Stream.filter((_) => Schema.is(Schema.toEncoded(WindowMessageListen))(_.data)),
110
124
  Stream.map((_) => {
111
125
  debugInfo.listenTotal++
112
- return Schema.decodeEither(schema.listen)(_.data.message)
126
+ return Schema.decodeResult(schema.listen)(_.data.message)
113
127
  }),
114
128
  listenToDebugPing('window'),
115
129
  )
116
130
 
117
- const closedDeferred = yield* Deferred.make<void>().pipe(Effect.acquireRelease(Deferred.done(Exit.void)))
131
+ const closedDeferred = yield* Effect.acquireRelease(Deferred.make<void>(), Deferred.done(Exit.void))
118
132
  const supportsTransferables = true
119
133
 
120
134
  return {
@@ -7,7 +7,7 @@ Vitest.describe('parseWebError', () => {
7
7
  const domException = new DOMException('missing node', 'NotFoundError')
8
8
  const webError = new WebError.NotFoundError({ cause: domException })
9
9
 
10
- const result = WebError.parseWebError(webError)
10
+ const result = WebError.classifyWebError(webError)
11
11
 
12
12
  Vitest.expect(result).toBeInstanceOf(WebError.NotFoundError)
13
13
  Vitest.expect(result.cause).toBe(domException)
@@ -16,7 +16,7 @@ Vitest.describe('parseWebError', () => {
16
16
  Vitest.it('maps native errors to the corresponding WebError when expected', () => {
17
17
  const nativeError = new globalThis.TypeError('unsupported type')
18
18
 
19
- const result = WebError.parseWebError(nativeError, [WebError.TypeError])
19
+ const result = WebError.classifyWebError(nativeError, [WebError.TypeError])
20
20
 
21
21
  Vitest.expect(result).toBeInstanceOf(WebError.TypeError)
22
22
  Vitest.expect(result.cause).toBe(nativeError)
@@ -25,7 +25,7 @@ Vitest.describe('parseWebError', () => {
25
25
  Vitest.it('wraps parsed errors that are not in the expected list in UnknownError', () => {
26
26
  const nativeError = new globalThis.RangeError('value out of range')
27
27
 
28
- const result = WebError.parseWebError(nativeError, [WebError.TypeError])
28
+ const result = WebError.classifyWebError(nativeError, [WebError.TypeError])
29
29
 
30
30
  Vitest.expect(result).toBeInstanceOf(WebError.UnknownError)
31
31
  Vitest.expect(result.cause).toBeInstanceOf(WebError.RangeError)
@@ -34,7 +34,7 @@ Vitest.describe('parseWebError', () => {
34
34
  Vitest.it('translates DOMException names into the matching WebError variant', () => {
35
35
  const domException = new DOMException('permission denied', 'NotAllowedError')
36
36
 
37
- const result = WebError.parseWebError(domException, [WebError.NotAllowedError])
37
+ const result = WebError.classifyWebError(domException, [WebError.NotAllowedError])
38
38
 
39
39
  Vitest.expect(result).toBeInstanceOf(WebError.NotAllowedError)
40
40
  Vitest.expect(result.cause).toBe(domException)
@@ -43,7 +43,7 @@ Vitest.describe('parseWebError', () => {
43
43
  Vitest.it('produces UnknownError for non-error values with the default message', () => {
44
44
  const value = { reason: 'unexpected' }
45
45
 
46
- const result = WebError.parseWebError(value)
46
+ const result = WebError.classifyWebError(value)
47
47
 
48
48
  Vitest.expect(result).toBeInstanceOf(WebError.UnknownError)
49
49
  Vitest.expect(result.cause).toBeDefined()
@@ -53,7 +53,7 @@ Vitest.describe('parseWebError', () => {
53
53
  Vitest.it('returns UnknownError instances without altering their payload when expectations are provided', () => {
54
54
  const existing = new WebError.UnknownError({ description: 'pre-parsed' })
55
55
 
56
- const result = WebError.parseWebError(existing, [WebError.TypeError])
56
+ const result = WebError.classifyWebError(existing, [WebError.TypeError])
57
57
 
58
58
  Vitest.expect(result).toBeInstanceOf(WebError.UnknownError)
59
59