@livestore/utils 0.4.0-dev.8 → 0.4.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 (226) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/NoopTracer.d.ts.map +1 -1
  3. package/dist/NoopTracer.js +17 -4
  4. package/dist/NoopTracer.js.map +1 -1
  5. package/dist/binary.js +1 -1
  6. package/dist/binary.js.map +1 -1
  7. package/dist/browser/Opfs/Opfs.d.ts +51 -0
  8. package/dist/browser/Opfs/Opfs.d.ts.map +1 -0
  9. package/dist/browser/Opfs/Opfs.js +345 -0
  10. package/dist/browser/Opfs/Opfs.js.map +1 -0
  11. package/dist/browser/Opfs/debug-utils.d.ts +20 -0
  12. package/dist/browser/Opfs/debug-utils.d.ts.map +1 -0
  13. package/dist/browser/Opfs/debug-utils.js +94 -0
  14. package/dist/browser/Opfs/debug-utils.js.map +1 -0
  15. package/dist/browser/Opfs/mod.d.ts +4 -0
  16. package/dist/browser/Opfs/mod.d.ts.map +1 -0
  17. package/dist/browser/Opfs/mod.js +4 -0
  18. package/dist/browser/Opfs/mod.js.map +1 -0
  19. package/dist/browser/Opfs/utils.d.ts +71 -0
  20. package/dist/browser/Opfs/utils.d.ts.map +1 -0
  21. package/dist/browser/Opfs/utils.js +218 -0
  22. package/dist/browser/Opfs/utils.js.map +1 -0
  23. package/dist/browser/QuotaExceededError.d.ts +59 -0
  24. package/dist/browser/QuotaExceededError.d.ts.map +1 -0
  25. package/dist/browser/QuotaExceededError.js +2 -0
  26. package/dist/browser/QuotaExceededError.js.map +1 -0
  27. package/dist/browser/WebChannelBrowser.d.ts +22 -0
  28. package/dist/browser/WebChannelBrowser.d.ts.map +1 -0
  29. package/dist/browser/WebChannelBrowser.js +76 -0
  30. package/dist/browser/WebChannelBrowser.js.map +1 -0
  31. package/dist/browser/WebError.d.ts +421 -0
  32. package/dist/browser/WebError.d.ts.map +1 -0
  33. package/dist/browser/WebError.js +416 -0
  34. package/dist/browser/WebError.js.map +1 -0
  35. package/dist/browser/WebError.test.d.ts +2 -0
  36. package/dist/browser/WebError.test.d.ts.map +1 -0
  37. package/dist/browser/WebError.test.js +46 -0
  38. package/dist/browser/WebError.test.js.map +1 -0
  39. package/dist/browser/WebLock.d.ts.map +1 -0
  40. package/dist/{effect → browser}/WebLock.js +9 -9
  41. package/dist/browser/WebLock.js.map +1 -0
  42. package/dist/{browser.d.ts → browser/detect.d.ts} +1 -1
  43. package/dist/browser/detect.d.ts.map +1 -0
  44. package/dist/{browser.js → browser/detect.js} +7 -7
  45. package/dist/browser/detect.js.map +1 -0
  46. package/dist/browser/mod.d.ts +8 -0
  47. package/dist/browser/mod.d.ts.map +1 -0
  48. package/dist/browser/mod.js +8 -0
  49. package/dist/browser/mod.js.map +1 -0
  50. package/dist/cuid/cuid.browser.js +1 -1
  51. package/dist/cuid/cuid.browser.js.map +1 -1
  52. package/dist/cuid/cuid.node.js +1 -1
  53. package/dist/cuid/cuid.node.js.map +1 -1
  54. package/dist/effect/BucketQueue.d.ts +1 -1
  55. package/dist/effect/BucketQueue.d.ts.map +1 -1
  56. package/dist/effect/BucketQueue.js.map +1 -1
  57. package/dist/effect/Debug.d.ts +41 -0
  58. package/dist/effect/Debug.d.ts.map +1 -0
  59. package/dist/effect/Debug.js +354 -0
  60. package/dist/effect/Debug.js.map +1 -0
  61. package/dist/effect/Effect.d.ts +72 -12
  62. package/dist/effect/Effect.d.ts.map +1 -1
  63. package/dist/effect/Effect.js +96 -12
  64. package/dist/effect/Effect.js.map +1 -1
  65. package/dist/effect/Error.js +1 -1
  66. package/dist/effect/Error.js.map +1 -1
  67. package/dist/effect/Logger.js +2 -2
  68. package/dist/effect/Logger.js.map +1 -1
  69. package/dist/effect/RpcClient.d.ts.map +1 -1
  70. package/dist/effect/RpcClient.js +11 -4
  71. package/dist/effect/RpcClient.js.map +1 -1
  72. package/dist/effect/Schema/debug-diff.js +5 -4
  73. package/dist/effect/Schema/debug-diff.js.map +1 -1
  74. package/dist/effect/Schema/debug-diff.test.js +1 -1
  75. package/dist/effect/Schema/debug-diff.test.js.map +1 -1
  76. package/dist/effect/Schema/index.d.ts +5 -3
  77. package/dist/effect/Schema/index.d.ts.map +1 -1
  78. package/dist/effect/Schema/index.js +2 -2
  79. package/dist/effect/Schema/index.js.map +1 -1
  80. package/dist/effect/ServiceContext.js +6 -6
  81. package/dist/effect/ServiceContext.js.map +1 -1
  82. package/dist/effect/Stream.test.js +3 -3
  83. package/dist/effect/Stream.test.js.map +1 -1
  84. package/dist/effect/SubscriptionRef.d.ts +4 -4
  85. package/dist/effect/SubscriptionRef.d.ts.map +1 -1
  86. package/dist/effect/WebChannel/WebChannel.d.ts +4 -23
  87. package/dist/effect/WebChannel/WebChannel.d.ts.map +1 -1
  88. package/dist/effect/WebChannel/WebChannel.js +9 -85
  89. package/dist/effect/WebChannel/WebChannel.js.map +1 -1
  90. package/dist/effect/WebChannel/WebChannel.test.js +1 -1
  91. package/dist/effect/WebChannel/WebChannel.test.js.map +1 -1
  92. package/dist/effect/WebChannel/broadcastChannelWithAck.js +4 -4
  93. package/dist/effect/WebChannel/broadcastChannelWithAck.js.map +1 -1
  94. package/dist/effect/WebChannel/common.d.ts +2 -2
  95. package/dist/effect/WebChannel/common.d.ts.map +1 -1
  96. package/dist/effect/WebChannel/common.js +2 -2
  97. package/dist/effect/WebChannel/common.js.map +1 -1
  98. package/dist/effect/WebSocket.d.ts.map +1 -1
  99. package/dist/effect/WebSocket.js +14 -14
  100. package/dist/effect/WebSocket.js.map +1 -1
  101. package/dist/effect/{index.d.ts → mod.d.ts} +4 -4
  102. package/dist/effect/mod.d.ts.map +1 -0
  103. package/dist/effect/{index.js → mod.js} +5 -5
  104. package/dist/effect/mod.js.map +1 -0
  105. package/dist/effect/spanEvent.d.ts +12 -0
  106. package/dist/effect/spanEvent.d.ts.map +1 -0
  107. package/dist/effect/spanEvent.js +12 -0
  108. package/dist/effect/spanEvent.js.map +1 -0
  109. package/dist/effect/spanEvent.test.d.ts +2 -0
  110. package/dist/effect/spanEvent.test.d.ts.map +1 -0
  111. package/dist/effect/spanEvent.test.js +82 -0
  112. package/dist/effect/spanEvent.test.js.map +1 -0
  113. package/dist/env.d.ts.map +1 -1
  114. package/dist/env.js +1 -1
  115. package/dist/env.js.map +1 -1
  116. package/dist/fast-deep-equal.js +9 -9
  117. package/dist/fast-deep-equal.js.map +1 -1
  118. package/dist/global.d.ts +3 -0
  119. package/dist/global.d.ts.map +1 -1
  120. package/dist/global.js.map +1 -1
  121. package/dist/guards.d.ts +14 -0
  122. package/dist/guards.d.ts.map +1 -1
  123. package/dist/guards.js +14 -0
  124. package/dist/guards.js.map +1 -1
  125. package/dist/misc.d.ts +9 -1
  126. package/dist/misc.d.ts.map +1 -1
  127. package/dist/misc.js +11 -3
  128. package/dist/misc.js.map +1 -1
  129. package/dist/mod.d.ts +197 -5
  130. package/dist/mod.d.ts.map +1 -1
  131. package/dist/mod.js +162 -17
  132. package/dist/mod.js.map +1 -1
  133. package/dist/node/ChildProcessRunner/ChildProcessRunner.d.ts.map +1 -1
  134. package/dist/node/ChildProcessRunner/ChildProcessRunner.js +15 -9
  135. package/dist/node/ChildProcessRunner/ChildProcessRunner.js.map +1 -1
  136. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts +8 -0
  137. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts.map +1 -1
  138. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js +16 -17
  139. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js.map +1 -1
  140. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.d.ts +4 -4
  141. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js +3 -3
  142. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js.map +1 -1
  143. package/dist/node/ChildProcessRunner/ChildProcessWorker.d.ts.map +1 -1
  144. package/dist/node/ChildProcessRunner/ChildProcessWorker.js +4 -4
  145. package/dist/node/ChildProcessRunner/ChildProcessWorker.js.map +1 -1
  146. package/dist/node/mod.d.ts +34 -1
  147. package/dist/node/mod.d.ts.map +1 -1
  148. package/dist/node/mod.js +37 -2
  149. package/dist/node/mod.js.map +1 -1
  150. package/dist/object/index.d.ts.map +1 -1
  151. package/dist/object/index.js.map +1 -1
  152. package/dist/object/omit.js +1 -1
  153. package/dist/object/omit.js.map +1 -1
  154. package/dist/object/stringify-object.js +2 -2
  155. package/dist/object/stringify-object.js.map +1 -1
  156. package/dist/object/stringify-object.test.js.map +1 -1
  157. package/dist/qr.d.ts +38 -0
  158. package/dist/qr.d.ts.map +1 -0
  159. package/dist/qr.js +109 -0
  160. package/dist/qr.js.map +1 -0
  161. package/dist/set.js +1 -1
  162. package/dist/set.js.map +1 -1
  163. package/dist/time.js +1 -1
  164. package/dist/time.js.map +1 -1
  165. package/package.json +78 -54
  166. package/src/NoopTracer.ts +22 -8
  167. package/src/binary.ts +1 -1
  168. package/src/browser/Opfs/Opfs.ts +436 -0
  169. package/src/browser/Opfs/debug-utils.ts +153 -0
  170. package/src/browser/Opfs/mod.ts +3 -0
  171. package/src/browser/Opfs/utils.ts +287 -0
  172. package/src/browser/QuotaExceededError.ts +57 -0
  173. package/src/browser/WebChannelBrowser.ts +131 -0
  174. package/src/browser/WebError.test.ts +66 -0
  175. package/src/browser/WebError.ts +613 -0
  176. package/src/{effect → browser}/WebLock.ts +15 -15
  177. package/src/{browser.ts → browser/detect.ts} +6 -6
  178. package/src/browser/mod.ts +8 -0
  179. package/src/cuid/cuid.browser.ts +1 -1
  180. package/src/cuid/cuid.node.ts +1 -1
  181. package/src/effect/BucketQueue.ts +1 -1
  182. package/src/effect/Debug.ts +470 -0
  183. package/src/effect/Effect.ts +118 -36
  184. package/src/effect/Error.ts +1 -1
  185. package/src/effect/Logger.ts +2 -2
  186. package/src/effect/RpcClient.ts +14 -6
  187. package/src/effect/Schema/debug-diff.test.ts +2 -2
  188. package/src/effect/Schema/debug-diff.ts +6 -5
  189. package/src/effect/Schema/index.ts +10 -7
  190. package/src/effect/ServiceContext.ts +6 -6
  191. package/src/effect/Stream.test.ts +5 -4
  192. package/src/effect/SubscriptionRef.ts +5 -5
  193. package/src/effect/WebChannel/WebChannel.test.ts +1 -1
  194. package/src/effect/WebChannel/WebChannel.ts +19 -141
  195. package/src/effect/WebChannel/broadcastChannelWithAck.ts +4 -4
  196. package/src/effect/WebChannel/common.ts +5 -5
  197. package/src/effect/WebSocket.ts +13 -12
  198. package/src/effect/{index.ts → mod.ts} +10 -2
  199. package/src/effect/spanEvent.test.ts +95 -0
  200. package/src/effect/spanEvent.ts +15 -0
  201. package/src/env.ts +2 -1
  202. package/src/fast-deep-equal.ts +9 -9
  203. package/src/global.ts +4 -2
  204. package/src/guards.ts +15 -0
  205. package/src/misc.ts +12 -4
  206. package/src/mod.ts +209 -17
  207. package/src/node/ChildProcessRunner/ChildProcessRunner.ts +23 -10
  208. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.ts +30 -21
  209. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.ts +10 -11
  210. package/src/node/ChildProcessRunner/ChildProcessWorker.ts +5 -4
  211. package/src/node/mod.ts +41 -2
  212. package/src/object/index.ts +1 -1
  213. package/src/object/omit.ts +1 -1
  214. package/src/object/stringify-object.test.ts +1 -0
  215. package/src/object/stringify-object.ts +2 -2
  216. package/src/qr.ts +125 -0
  217. package/src/set.ts +1 -1
  218. package/src/time.ts +1 -1
  219. package/dist/.tsbuildinfo.json +0 -1
  220. package/dist/browser.d.ts.map +0 -1
  221. package/dist/browser.js.map +0 -1
  222. package/dist/effect/WebLock.d.ts.map +0 -1
  223. package/dist/effect/WebLock.js.map +0 -1
  224. package/dist/effect/index.d.ts.map +0 -1
  225. package/dist/effect/index.js.map +0 -1
  226. /package/dist/{effect → browser}/WebLock.d.ts +0 -0
@@ -0,0 +1,613 @@
1
+ import './QuotaExceededError.ts'
2
+ import { Either, ParseResult, Predicate, Schema } from 'effect'
3
+
4
+ /**
5
+ * Unique identifier for web errors.
6
+ */
7
+ const TypeId = '~@livestore/utils/WebError' as const
8
+
9
+ /**
10
+ * Type guard to check if a value is a web error.
11
+ *
12
+ * @param u - The value to check
13
+ * @returns `true` if the value is an `WebError`, `false` otherwise
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { WebError } from "@livestore/utils/effect"
18
+ *
19
+ * const someError = new Error("generic error")
20
+ * const webError = new WebError.UnknownError({
21
+ * module: "Test",
22
+ * method: "example"
23
+ * })
24
+ *
25
+ * console.log(WebError.isWebError(someError)) // false
26
+ * console.log(WebError.isWebError(webError)) // true
27
+ * ```
28
+ */
29
+ export const isWebError = (u: unknown): u is WebError => Predicate.hasProperty(u, TypeId)
30
+
31
+ // ============================================================================
32
+ // Simple Exception Errors
33
+ // ============================================================================
34
+ //
35
+ // [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#standard_error_types)
36
+ // [Specification](https://webidl.spec.whatwg.org/#dfn-simple-exception)
37
+
38
+ /**
39
+ * Error for the web standard "EvalError" simple exception.
40
+ *
41
+ * Thrown when the `eval` function is used in a way that violates its usage restrictions.
42
+ *
43
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Evalerror | MDN Reference}
44
+ * @see {@link https://webidl.spec.whatwg.org/#exceptiondef-evalerror | Specification}
45
+ */
46
+ export class EvalError extends Schema.TaggedError<EvalError>(
47
+ `${TypeId}/EvalError`,
48
+ )('EvalError', {
49
+ cause: Schema.instanceOf(globalThis.EvalError),
50
+ }) {
51
+ readonly [TypeId] = TypeId
52
+ }
53
+
54
+ /**
55
+ * Error for the web standard "RangeError" simple exception.
56
+ *
57
+ * Indicates that a numeric value is outside the permitted range.
58
+ *
59
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Rangeerror | MDN Reference}
60
+ * @see {@link https://webidl.spec.whatwg.org/#exceptiondef-rangeerror | Specification}
61
+ */
62
+ export class RangeError extends Schema.TaggedError<RangeError>(
63
+ `${TypeId}/RangeError`,
64
+ )('RangeError', {
65
+ cause: Schema.instanceOf(globalThis.RangeError),
66
+ }) {
67
+ readonly [TypeId] = TypeId
68
+ }
69
+
70
+ /**
71
+ * Error for the web standard "ReferenceError" simple exception.
72
+ *
73
+ * Raised when code references an identifier that has not been defined.
74
+ *
75
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Referenceerror | MDN Reference}
76
+ * @see {@link https://webidl.spec.whatwg.org/#exceptiondef-referenceerror | Specification}
77
+ */
78
+ export class ReferenceError extends Schema.TaggedError<ReferenceError>(
79
+ `${TypeId}/ReferenceError`,
80
+ )('ReferenceError', {
81
+ cause: Schema.instanceOf(globalThis.ReferenceError),
82
+ }) {
83
+ readonly [TypeId] = TypeId
84
+ }
85
+
86
+ /**
87
+ * Error for the web standard "TypeError" simple exception.
88
+ *
89
+ * Occurs when an operation is applied to a value of an inappropriate type.
90
+ *
91
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Typeerror | MDN Reference}
92
+ * @see {@link https://webidl.spec.whatwg.org/#exceptiondef-typeerror | Specification}
93
+ */
94
+ export class TypeError extends Schema.TaggedError<TypeError>(
95
+ `${TypeId}/TypeError`,
96
+ )('TypeError', {
97
+ cause: Schema.instanceOf(globalThis.TypeError),
98
+ }) {
99
+ readonly [TypeId] = TypeId
100
+ }
101
+
102
+ /**
103
+ * Error for the web standard "URIError" simple exception.
104
+ *
105
+ * Signals incorrect usage of global URI handling functions such as `decodeURI` or `encodeURI`.
106
+ *
107
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIerror | MDN Reference}
108
+ * @see {@link https://webidl.spec.whatwg.org/#exceptiondef-urierror | Specification}
109
+ */
110
+ export class URIError extends Schema.TaggedError<URIError>(
111
+ `${TypeId}/URIError`,
112
+ )('URIError', {
113
+ cause: Schema.instanceOf(globalThis.URIError),
114
+ }) {
115
+ readonly [TypeId] = TypeId
116
+ }
117
+
118
+ // ============================================================================
119
+ // Predefined DOMException Errors
120
+ // ============================================================================
121
+ //
122
+ // [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
123
+ // [Specification](https://webidl.spec.whatwg.org/#idl-DOMException-derived-predefineds)
124
+
125
+ const domExceptionWithName = (expectedName: string) =>
126
+ Schema.instanceOf(DOMException).pipe(
127
+ Schema.filter((a, options) =>
128
+ ParseResult.validateEither(
129
+ Schema.Struct({
130
+ name: Schema.Literal(expectedName),
131
+ }),
132
+ )(a, options).pipe(Either.flip, Either.getOrUndefined),
133
+ ),
134
+ )
135
+
136
+ /**
137
+ * Error for the web standard "QuotaExceededError" DOMException-derived error.
138
+ *
139
+ * The quota has been exceeded.
140
+ *
141
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/QuotaExceededError | MDN Reference}
142
+ * @see {@link https://webidl.spec.whatwg.org/#quotaexceedederror | Specification}
143
+ */
144
+ export class QuotaExceededError extends Schema.TaggedError<QuotaExceededError>(
145
+ `${TypeId}/QuotaExceededError`,
146
+ )('QuotaExceededError', {
147
+ cause: Schema.Union(
148
+ typeof globalThis.QuotaExceededError === 'function'
149
+ ? Schema.instanceOf(globalThis.QuotaExceededError)
150
+ : Schema.Never,
151
+ // Deprecated but still in use in some browsers
152
+ domExceptionWithName('QuotaExceededError'),
153
+ ),
154
+ }) {
155
+ readonly [TypeId] = TypeId
156
+ override get message(): string {
157
+ return this.cause.message
158
+ }
159
+ }
160
+
161
+ // ============================================================================
162
+ // Base DOMException Errors
163
+ // ============================================================================
164
+ //
165
+ // [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/DOMException#error_names)
166
+ // [Specification](https://webidl.spec.whatwg.org/#idl-DOMException-error-names)
167
+
168
+ /**
169
+ * Error for the web standard "NoModificationAllowedError" DOMException.
170
+ *
171
+ * The object can not be modified.
172
+ *
173
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException#nomodificationallowederror | MDN Reference}
174
+ * @see {@link https://webidl.spec.whatwg.org/#nomodificationallowederror | Specification}
175
+ */
176
+ export class NoModificationAllowedError extends Schema.TaggedError<NoModificationAllowedError>(
177
+ `${TypeId}/NoModificationAllowedError`,
178
+ )('NoModificationAllowedError', {
179
+ cause: domExceptionWithName('NoModificationAllowedError'),
180
+ }) {
181
+ readonly [TypeId] = TypeId
182
+ override get message(): string {
183
+ return this.cause.message
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Error for the web standard "NotFoundError" DOMException
189
+ *
190
+ * The object can not be found here.
191
+ *
192
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException#notfounderror | MDN Reference}
193
+ * @see {@link https://webidl.spec.whatwg.org/#notfounderror | Specification}
194
+ */
195
+ export class NotFoundError extends Schema.TaggedError<NotFoundError>(
196
+ `${TypeId}/NotFoundError`,
197
+ )('NotFoundError', {
198
+ cause: domExceptionWithName('NotFoundError'),
199
+ }) {
200
+ readonly [TypeId] = TypeId
201
+ override get message(): string {
202
+ return this.cause.message
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Error for the web standard "NotAllowedError" DOMException
208
+ *
209
+ * The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
210
+ *
211
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException#notallowederror | MDN Reference}
212
+ * @see {@link https://webidl.spec.whatwg.org/#notallowederror | Specification}
213
+ */
214
+ export class NotAllowedError extends Schema.TaggedError<NotAllowedError>(
215
+ `${TypeId}/NotAllowedError`,
216
+ )('NotAllowedError', {
217
+ cause: domExceptionWithName('NotAllowedError'),
218
+ }) {
219
+ readonly [TypeId] = TypeId
220
+ override get message(): string {
221
+ return this.cause.message
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Error for the web standard "TypeMismatchError" DOMException.
227
+ *
228
+ * The object can not be converted to the expected type.
229
+ *
230
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException#typemismatcherror | MDN Reference}
231
+ * @see {@link https://webidl.spec.whatwg.org/#typemismatcherror | Specification}
232
+ */
233
+ export class TypeMismatchError extends Schema.TaggedError<TypeMismatchError>(
234
+ `${TypeId}/TypeMismatchError`,
235
+ )('TypeMismatchError', {
236
+ cause: domExceptionWithName('TypeMismatchError'),
237
+ }) {
238
+ readonly [TypeId] = TypeId
239
+ override get message(): string {
240
+ return this.cause.message
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Error for the web standard "InvalidStateError" DOMException.
246
+ *
247
+ * The object is in an invalid state.
248
+ *
249
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException#invalidstateerror | MDN Reference}
250
+ * @see {@link https://webidl.spec.whatwg.org/#invalidstateerror | Specification}
251
+ */
252
+ export class InvalidStateError extends Schema.TaggedError<InvalidStateError>(
253
+ `${TypeId}/InvalidStateError`,
254
+ )('InvalidStateError', {
255
+ cause: domExceptionWithName('InvalidStateError'),
256
+ }) {
257
+ readonly [TypeId] = TypeId
258
+ override get message(): string {
259
+ return this.cause.message
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Error for the web standard "AbortError" DOMException.
265
+ *
266
+ * The operation was aborted.
267
+ *
268
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException#aborterror | MDN Reference}
269
+ * @see {@link https://webidl.spec.whatwg.org/#aborterror | Specification}
270
+ */
271
+ export class AbortError extends Schema.TaggedError<AbortError>(
272
+ `${TypeId}/AbortError`,
273
+ )('AbortError', {
274
+ cause: domExceptionWithName('AbortError'),
275
+ }) {
276
+ readonly [TypeId] = TypeId
277
+ override get message(): string {
278
+ return this.cause.message
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Error for the web standard "InvalidModificationError" DOMException.
284
+ *
285
+ * The object can not be modified in this way.
286
+ *
287
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException#invalidmodificationerror | MDN Reference}
288
+ * @see {@link https://webidl.spec.whatwg.org/#invalidmodificationerror | Specification}
289
+ */
290
+ export class InvalidModificationError extends Schema.TaggedError<InvalidModificationError>(
291
+ `${TypeId}/InvalidModificationError`,
292
+ )('InvalidModificationError', {
293
+ cause: domExceptionWithName('InvalidModificationError'),
294
+ }) {
295
+ readonly [TypeId] = TypeId
296
+ override get message(): string {
297
+ return this.cause.message
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Error for the web standard "SecurityError" DOMException.
303
+ *
304
+ * The operation is insecure.
305
+ *
306
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException#securityerror | MDN Reference}
307
+ * @see {@link https://webidl.spec.whatwg.org/#securityerror | Specification}
308
+ */
309
+ export class SecurityError extends Schema.TaggedError<SecurityError>(
310
+ `${TypeId}/SecurityError`,
311
+ )('SecurityError', {
312
+ cause: domExceptionWithName('SecurityError'),
313
+ }) {
314
+ readonly [TypeId] = TypeId
315
+ override get message(): string {
316
+ return this.cause.message
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Error for the web standard "DataCloneError" DOMException.
322
+ *
323
+ * The object can not be cloned.
324
+ *
325
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMException#datacloneerror | MDN Reference}
326
+ * @see {@link https://webidl.spec.whatwg.org/#datacloneerror | Specification}
327
+ */
328
+ export class DataCloneError extends Schema.TaggedError<DataCloneError>(
329
+ `${TypeId}/DataCloneError`,
330
+ )('DataCloneError', {
331
+ cause: domExceptionWithName('DataCloneError'),
332
+ }) {
333
+ readonly [TypeId] = TypeId
334
+ override get message(): string {
335
+ return this.cause.message
336
+ }
337
+ }
338
+
339
+ // ============================================================================
340
+ // Custom Errors
341
+ // ============================================================================
342
+
343
+ /**
344
+ * Catch-all error for unexpected runtime errors in web environments.
345
+ *
346
+ * This error is used when an unexpected exception occurs that doesn't fit
347
+ * into the other specific error categories. It provides context about where
348
+ * the error occurred and preserves the original cause for debugging.
349
+ *
350
+ * @example
351
+ * ```ts
352
+ * import { WebError } from "@livestore/utils/effect"
353
+ * import { Effect } from "effect"
354
+ *
355
+ * const riskyOperation = () => {
356
+ * try {
357
+ * // Some operation that might throw
358
+ * throw new Error("Unexpected runtime issue")
359
+ * } catch (cause) {
360
+ * return Effect.fail(new WebError.UnknownError({
361
+ * module: "JSON",
362
+ * method: "parse",
363
+ * description: "Could not parse string as JSON",
364
+ * cause
365
+ * }))
366
+ * }
367
+ * }
368
+ *
369
+ * const program = riskyOperation().pipe(
370
+ * Effect.catchTag("UnknownError", (error) => {
371
+ * console.log(error.message)
372
+ * // "JSON.parse: Could not parse string as JSON"
373
+ * return Effect.succeed("JSON parsing not possible")
374
+ * })
375
+ * )
376
+ * ```
377
+ */
378
+ export class UnknownError extends Schema.TaggedError<UnknownError>(
379
+ `${TypeId}/UnknownError`,
380
+ )('UnknownError', {
381
+ module: Schema.optional(Schema.String),
382
+ method: Schema.optional(Schema.String),
383
+ description: Schema.optional(Schema.String),
384
+ cause: Schema.optional(Schema.Defect),
385
+ }) {
386
+ readonly [TypeId] = TypeId
387
+ override get message(): string {
388
+ const messageEnd = Predicate.isUndefined(this.description) === true ? 'A web error occurred' : this.description
389
+ const moduleMethod =
390
+ Predicate.isString(this.module) === true && Predicate.isString(this.method) === true
391
+ ? `${this.module}.${this.method}`
392
+ : undefined
393
+ return Predicate.isUndefined(moduleMethod) === true ? messageEnd : `${moduleMethod}: ${messageEnd}`
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Union type representing all possible web errors.
399
+ *
400
+ * @example
401
+ * ```ts
402
+ * import { WebError } from "@livestore/utils/effect"
403
+ * import { Effect, Match } from "effect"
404
+ *
405
+ * const handleAnyWebError = Match.type<WebError.WebError>().pipe(
406
+ * Match.tag("NotFoundError", (err) =>
407
+ * `Not found error: ${err.cause.message}`
408
+ * ),
409
+ * Match.tag("TypeError", (err) =>
410
+ * `Type error: ${err.cause.message}`
411
+ * ),
412
+ * Match.orElse((err) =>
413
+ * `Unknown error: ${err.message}`
414
+ * )
415
+ * )
416
+ * ```
417
+ */
418
+ export type WebError =
419
+ // Simple Exception Errors
420
+ | EvalError
421
+ | RangeError
422
+ | ReferenceError
423
+ | TypeError
424
+ | URIError
425
+ // Base DOMException Errors
426
+ | NoModificationAllowedError
427
+ | NotFoundError
428
+ | NotAllowedError
429
+ | TypeMismatchError
430
+ | InvalidStateError
431
+ | AbortError
432
+ | InvalidModificationError
433
+ | SecurityError
434
+ | DataCloneError
435
+ // Predefined DOMException Errors
436
+ | QuotaExceededError
437
+ // Custom Errors
438
+ | UnknownError
439
+
440
+ /**
441
+ * Schema for validating and parsing web errors.
442
+ *
443
+ * This schema can be used to decode unknown values into properly typed web
444
+ * errors, ensuring type safety when handling errors from external sources or
445
+ * serialized data.
446
+ */
447
+ export const WebError: Schema.Union<
448
+ [
449
+ // Simple Exception Errors
450
+ typeof EvalError,
451
+ typeof RangeError,
452
+ typeof ReferenceError,
453
+ typeof TypeError,
454
+ typeof URIError,
455
+ // Predefined DOMException Errors
456
+ typeof QuotaExceededError,
457
+ // Base DOMException Errors
458
+ typeof NoModificationAllowedError,
459
+ typeof NotFoundError,
460
+ typeof NotAllowedError,
461
+ typeof TypeMismatchError,
462
+ typeof InvalidStateError,
463
+ typeof AbortError,
464
+ typeof InvalidModificationError,
465
+ typeof SecurityError,
466
+ typeof DataCloneError,
467
+ // Custom Errors
468
+ typeof UnknownError,
469
+ ]
470
+ > = Schema.Union(
471
+ // Simple Exception Errors
472
+ EvalError,
473
+ RangeError,
474
+ ReferenceError,
475
+ TypeError,
476
+ URIError,
477
+ // Predefined DOMException Errors
478
+ QuotaExceededError,
479
+ // Base DOMException Errors
480
+ NoModificationAllowedError,
481
+ NotFoundError,
482
+ NotAllowedError,
483
+ TypeMismatchError,
484
+ InvalidStateError,
485
+ AbortError,
486
+ InvalidModificationError,
487
+ SecurityError,
488
+ DataCloneError,
489
+ // Custom Errors
490
+ UnknownError,
491
+ )
492
+
493
+ /**
494
+ * Constructor type for any `WebError` variant exposed by the schema union.
495
+ *
496
+ * Useful when constraining APIs (e.g. `parseWebError`) to accept only
497
+ * specific web error constructors while preserving their instance types.
498
+ */
499
+ type WebErrorConstructor = (typeof WebError.members)[number]
500
+
501
+ /**
502
+ * Schema transform for converting unknown values to WebError instances.
503
+ *
504
+ * This transform handles various web error types and converts them to
505
+ * properly typed WebError instances while preserving the original cause.
506
+ */
507
+ const WebErrorFromUnknown = Schema.transform(Schema.Unknown, WebError, {
508
+ strict: true,
509
+ decode: (value) => {
510
+ // Already a WebError
511
+ if (isWebError(value) === true) return value
512
+
513
+ // Simple Exception Errors
514
+ if (value instanceof globalThis.EvalError) return new EvalError({ cause: value })
515
+ if (value instanceof globalThis.RangeError) return new RangeError({ cause: value })
516
+ if (value instanceof globalThis.ReferenceError) return new ReferenceError({ cause: value })
517
+ if (value instanceof globalThis.TypeError) return new TypeError({ cause: value })
518
+ if (value instanceof globalThis.URIError) return new URIError({ cause: value })
519
+
520
+ // Predefined DOMException Errors
521
+ if (typeof globalThis.QuotaExceededError === 'function' && value instanceof globalThis.QuotaExceededError) {
522
+ return new QuotaExceededError({ cause: value })
523
+ }
524
+
525
+ // Base DOMException Errors
526
+ if (value instanceof DOMException) {
527
+ switch (value.name) {
528
+ case 'QuotaExceededError':
529
+ return new QuotaExceededError({ cause: value })
530
+ case 'NoModificationAllowedError':
531
+ return new NoModificationAllowedError({ cause: value })
532
+ case 'NotFoundError':
533
+ return new NotFoundError({ cause: value })
534
+ case 'NotAllowedError':
535
+ return new NotAllowedError({ cause: value })
536
+ case 'TypeMismatchError':
537
+ return new TypeMismatchError({ cause: value })
538
+ case 'InvalidStateError':
539
+ return new InvalidStateError({ cause: value })
540
+ case 'AbortError':
541
+ return new AbortError({ cause: value })
542
+ case 'InvalidModificationError':
543
+ return new InvalidModificationError({ cause: value })
544
+ case 'SecurityError':
545
+ return new SecurityError({ cause: value })
546
+ case 'DataCloneError':
547
+ return new DataCloneError({ cause: value })
548
+ default:
549
+ break
550
+ }
551
+ }
552
+
553
+ if (value instanceof Error) return new UnknownError({ description: value.message, cause: value })
554
+
555
+ return new UnknownError({ cause: value })
556
+ },
557
+ encode: (webError) => webError,
558
+ })
559
+
560
+ /**
561
+ * Parses an unknown value into a typed WebError instance.
562
+ *
563
+ * This function safely attempts to parse the provided value into one of the
564
+ * known WebError types. If the value does not match any known type, it
565
+ * defaults to return an `UnknownError` that encapsulates the value and
566
+ * the original error information.
567
+ *
568
+ * @param value - The unknown value to parse
569
+ * @param expected - The errors we expect to receive. Can be used to narrow the return type.
570
+ * @returns A union of the WebError instance. UnknownError is always included in the union
571
+ * as a fallback when the specific error type cannot be determined.
572
+ *
573
+ * @example
574
+ * ```ts
575
+ * import { WebError } from "@livestore/utils/effect"
576
+ *
577
+ * // ┌─── Effect<PermissionStatus, WebError.InvalidStateError | WebError.TypeError | WebError.UnknownError>
578
+ * // ▼
579
+ * const permissionStatus = Effect.tryPromise({
580
+ * try: () => navigator.permissions.query({ name: 'geolocation' }),
581
+ * catch: (u) => WebError.parseWebError(u, [WebError.InvalidStateError, WebError.TypeError]),
582
+ * })
583
+ * ```
584
+ *
585
+ * @example
586
+ * Passing specific expected errors narrows the return type
587
+ * ```ts
588
+ * const specificError = WebError.parseWebError(error, [WebError.InvalidStateError, WebError.TypeError])
589
+ * // specificError is typed as WebError.InvalidStateError | WebError.TypeError | WebError.UnknownError
590
+ * ```
591
+ *
592
+ * @example
593
+ * Without additional arguments the full union type is returned
594
+ * ```ts
595
+ * const anyError = WebError.parseWebError(error)
596
+ * // anyError is typed as WebError (all possible error types)
597
+ * ```
598
+ */
599
+ export function parseWebError(value: unknown): WebError
600
+ export function parseWebError<BECs extends readonly WebErrorConstructor[]>(
601
+ value: unknown,
602
+ expected: BECs,
603
+ ): InstanceType<BECs[number]> | UnknownError
604
+ export function parseWebError(value: unknown, expected: readonly WebErrorConstructor[] = []): WebError {
605
+ const parsed = Schema.decodeUnknownSync(WebErrorFromUnknown)(value)
606
+
607
+ if (expected.length === 0) return parsed
608
+
609
+ const expectedTags = new Set(expected.map((ErrorConstructor) => ErrorConstructor._tag))
610
+ if (expectedTags.has(parsed._tag) === true) return parsed
611
+
612
+ return parsed instanceof UnknownError ? parsed : new UnknownError({ cause: parsed })
613
+ }
@@ -18,16 +18,16 @@ export const withLock =
18
18
 
19
19
  const exit = yield* Effect.tryPromise<Exit.Exit<A, E>, E | E2>({
20
20
  try: (signal) => {
21
- if (signal.aborted) return 'aborted' as never
21
+ if (signal.aborted === true) return 'aborted' as never
22
22
 
23
23
  // NOTE The 'signal' and 'ifAvailable' options cannot be used together.
24
24
  const requestOptions = options?.ifAvailable === true ? options : { ...options, signal }
25
- return navigator.locks.request(lockName, requestOptions, async (lock) => {
25
+ return navigator.locks.request(lockName, requestOptions, async (lock: Lock | null) => {
26
26
  if (lock === null) {
27
- if (onTaken) {
28
- const exit = await Runtime.runPromiseExit(runtime)(onTaken)
29
- if (exit._tag === 'Failure') {
30
- return exit
27
+ if (onTaken !== undefined) {
28
+ const onTakenExit = await Runtime.runPromiseExit(runtime)(onTaken)
29
+ if (onTakenExit._tag === 'Failure') {
30
+ return onTakenExit
31
31
  }
32
32
  }
33
33
  return
@@ -49,10 +49,10 @@ export const withLock =
49
49
 
50
50
  export const waitForDeferredLock = (deferred: Deferred.Deferred<void>, lockName: string) =>
51
51
  Effect.async<void>((cb, signal) => {
52
- if (signal.aborted) return
52
+ if (signal.aborted === true) return
53
53
 
54
54
  navigator.locks
55
- .request(lockName, { signal, mode: 'exclusive', ifAvailable: false }, (_lock) => {
55
+ .request(lockName, { signal, mode: 'exclusive', ifAvailable: false }, (_lock: Lock | null) => {
56
56
  // immediately continuing calling Effect since we have the lock
57
57
  cb(Effect.void)
58
58
 
@@ -72,7 +72,7 @@ export const waitForDeferredLock = (deferred: Deferred.Deferred<void>, lockName:
72
72
 
73
73
  export const tryGetDeferredLock = (deferred: Deferred.Deferred<void>, lockName: string) =>
74
74
  Effect.async<boolean>((cb, signal) => {
75
- navigator.locks.request(lockName, { mode: 'exclusive', ifAvailable: true }, (lock) => {
75
+ navigator.locks.request(lockName, { mode: 'exclusive', ifAvailable: true }, (lock: Lock | null) => {
76
76
  cb(Effect.succeed(lock !== null))
77
77
 
78
78
  // the code below is still running
@@ -96,7 +96,7 @@ export const tryGetDeferredLock = (deferred: Deferred.Deferred<void>, lockName:
96
96
 
97
97
  export const stealDeferredLock = (deferred: Deferred.Deferred<void>, lockName: string) =>
98
98
  Effect.async<boolean>((cb, signal) => {
99
- navigator.locks.request(lockName, { mode: 'exclusive', steal: true }, (lock) => {
99
+ navigator.locks.request(lockName, { mode: 'exclusive', steal: true }, (lock: Lock | null) => {
100
100
  cb(Effect.succeed(lock !== null))
101
101
 
102
102
  // the code below is still running
@@ -117,9 +117,9 @@ export const stealDeferredLock = (deferred: Deferred.Deferred<void>, lockName: s
117
117
 
118
118
  export const waitForLock = (lockName: string) =>
119
119
  Effect.async<void>((cb, signal) => {
120
- if (signal.aborted) return
120
+ if (signal.aborted === true) return
121
121
 
122
- navigator.locks.request(lockName, { mode: 'shared', signal, ifAvailable: false }, (_lock) => {
122
+ navigator.locks.request(lockName, { mode: 'shared', signal, ifAvailable: false }, (_lock: Lock | null) => {
123
123
  cb(Effect.succeed(void 0))
124
124
  })
125
125
  })
@@ -127,10 +127,10 @@ export const waitForLock = (lockName: string) =>
127
127
  /** Attempts to get the lock if available and waits for it to be stolen */
128
128
  export const getLockAndWaitForSteal = (lockName: string) =>
129
129
  Effect.async<void>((cb, signal) => {
130
- if (signal.aborted) return
130
+ if (signal.aborted === true) return
131
131
 
132
132
  navigator.locks
133
- .request(lockName, { mode: 'exclusive', ifAvailable: true }, async (lock) => {
133
+ .request(lockName, { mode: 'exclusive', ifAvailable: true }, async (lock: Lock | null) => {
134
134
  if (lock === null) {
135
135
  // Lock wasn't available, resolve immediately
136
136
  cb(Effect.succeed(void 0))
@@ -148,7 +148,7 @@ export const getLockAndWaitForSteal = (lockName: string) =>
148
148
  resolve()
149
149
  })
150
150
 
151
- return Promise.race([holdLock, signal.aborted ? Promise.resolve() : holdLock]).catch(() => {})
151
+ return Promise.race([holdLock, signal.aborted === true ? Promise.resolve() : holdLock]).catch(() => {})
152
152
  }).catch(() => {})
153
153
 
154
154
  cb(Effect.succeed(void 0))