@livestore/utils 0.4.0-dev.18 → 0.4.0-dev.19

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