@livestore/utils 0.4.0-dev.21 → 0.4.0-dev.23

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 (179) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/NoopTracer.d.ts.map +1 -1
  3. package/dist/NoopTracer.js +15 -5
  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 +3 -3
  8. package/dist/browser/Opfs/Opfs.d.ts.map +1 -1
  9. package/dist/browser/Opfs/Opfs.js +1 -1
  10. package/dist/browser/Opfs/Opfs.js.map +1 -1
  11. package/dist/browser/Opfs/debug-utils.d.ts.map +1 -1
  12. package/dist/browser/Opfs/debug-utils.js +2 -2
  13. package/dist/browser/Opfs/debug-utils.js.map +1 -1
  14. package/dist/browser/Opfs/utils.d.ts.map +1 -1
  15. package/dist/browser/Opfs/utils.js +10 -10
  16. package/dist/browser/Opfs/utils.js.map +1 -1
  17. package/dist/browser/QuotaExceededError.d.ts.map +1 -1
  18. package/dist/browser/QuotaExceededError.js.map +1 -1
  19. package/dist/browser/WebChannelBrowser.d.ts +1 -1
  20. package/dist/browser/WebChannelBrowser.d.ts.map +1 -1
  21. package/dist/browser/WebError.d.ts +50 -54
  22. package/dist/browser/WebError.d.ts.map +1 -1
  23. package/dist/browser/WebError.js +25 -23
  24. package/dist/browser/WebError.js.map +1 -1
  25. package/dist/browser/WebLock.js +9 -9
  26. package/dist/browser/WebLock.js.map +1 -1
  27. package/dist/browser/detect.js +6 -6
  28. package/dist/browser/detect.js.map +1 -1
  29. package/dist/cuid/cuid.browser.js +1 -1
  30. package/dist/cuid/cuid.browser.js.map +1 -1
  31. package/dist/cuid/cuid.node.js +1 -1
  32. package/dist/cuid/cuid.node.js.map +1 -1
  33. package/dist/effect/BucketQueue.d.ts +1 -1
  34. package/dist/effect/BucketQueue.d.ts.map +1 -1
  35. package/dist/effect/BucketQueue.js.map +1 -1
  36. package/dist/effect/Debug.d.ts +7 -2
  37. package/dist/effect/Debug.d.ts.map +1 -1
  38. package/dist/effect/Debug.js +69 -61
  39. package/dist/effect/Debug.js.map +1 -1
  40. package/dist/effect/Effect.d.ts +72 -12
  41. package/dist/effect/Effect.d.ts.map +1 -1
  42. package/dist/effect/Effect.js +96 -12
  43. package/dist/effect/Effect.js.map +1 -1
  44. package/dist/effect/Error.js +1 -1
  45. package/dist/effect/Error.js.map +1 -1
  46. package/dist/effect/Logger.js +2 -2
  47. package/dist/effect/Logger.js.map +1 -1
  48. package/dist/effect/RpcClient.d.ts.map +1 -1
  49. package/dist/effect/RpcClient.js +4 -4
  50. package/dist/effect/RpcClient.js.map +1 -1
  51. package/dist/effect/Schema/debug-diff.js +5 -4
  52. package/dist/effect/Schema/debug-diff.js.map +1 -1
  53. package/dist/effect/Schema/index.d.ts +5 -3
  54. package/dist/effect/Schema/index.d.ts.map +1 -1
  55. package/dist/effect/Schema/index.js +1 -1
  56. package/dist/effect/Schema/index.js.map +1 -1
  57. package/dist/effect/ServiceContext.js +6 -6
  58. package/dist/effect/ServiceContext.js.map +1 -1
  59. package/dist/effect/Stream.test.js +3 -3
  60. package/dist/effect/Stream.test.js.map +1 -1
  61. package/dist/effect/SubscriptionRef.d.ts +4 -4
  62. package/dist/effect/SubscriptionRef.d.ts.map +1 -1
  63. package/dist/effect/WebChannel/WebChannel.d.ts +2 -2
  64. package/dist/effect/WebChannel/WebChannel.d.ts.map +1 -1
  65. package/dist/effect/WebChannel/WebChannel.js +4 -4
  66. package/dist/effect/WebChannel/WebChannel.js.map +1 -1
  67. package/dist/effect/WebChannel/broadcastChannelWithAck.js +4 -4
  68. package/dist/effect/WebChannel/broadcastChannelWithAck.js.map +1 -1
  69. package/dist/effect/WebChannel/common.d.ts +1 -1
  70. package/dist/effect/WebChannel/common.d.ts.map +1 -1
  71. package/dist/effect/WebChannel/common.js +2 -2
  72. package/dist/effect/WebChannel/common.js.map +1 -1
  73. package/dist/effect/WebSocket.js +3 -3
  74. package/dist/effect/WebSocket.js.map +1 -1
  75. package/dist/effect/mod.d.ts +1 -1
  76. package/dist/effect/mod.d.ts.map +1 -1
  77. package/dist/effect/mod.js +1 -1
  78. package/dist/effect/mod.js.map +1 -1
  79. package/dist/effect/spanEvent.d.ts +12 -0
  80. package/dist/effect/spanEvent.d.ts.map +1 -0
  81. package/dist/effect/spanEvent.js +12 -0
  82. package/dist/effect/spanEvent.js.map +1 -0
  83. package/dist/effect/spanEvent.test.d.ts +2 -0
  84. package/dist/effect/spanEvent.test.d.ts.map +1 -0
  85. package/dist/effect/spanEvent.test.js +82 -0
  86. package/dist/effect/spanEvent.test.js.map +1 -0
  87. package/dist/env.d.ts.map +1 -1
  88. package/dist/env.js +1 -1
  89. package/dist/env.js.map +1 -1
  90. package/dist/fast-deep-equal.js +9 -9
  91. package/dist/fast-deep-equal.js.map +1 -1
  92. package/dist/global.d.ts.map +1 -1
  93. package/dist/global.js.map +1 -1
  94. package/dist/misc.d.ts +9 -1
  95. package/dist/misc.d.ts.map +1 -1
  96. package/dist/misc.js +11 -3
  97. package/dist/misc.js.map +1 -1
  98. package/dist/mod.d.ts +1 -1
  99. package/dist/mod.d.ts.map +1 -1
  100. package/dist/mod.js +12 -12
  101. package/dist/mod.js.map +1 -1
  102. package/dist/node/ChildProcessRunner/ChildProcessRunner.d.ts.map +1 -1
  103. package/dist/node/ChildProcessRunner/ChildProcessRunner.js +15 -9
  104. package/dist/node/ChildProcessRunner/ChildProcessRunner.js.map +1 -1
  105. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts +8 -0
  106. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts.map +1 -1
  107. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js +10 -6
  108. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js.map +1 -1
  109. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js +2 -2
  110. package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js.map +1 -1
  111. package/dist/node/ChildProcessRunner/ChildProcessWorker.d.ts.map +1 -1
  112. package/dist/node/ChildProcessRunner/ChildProcessWorker.js +4 -4
  113. package/dist/node/ChildProcessRunner/ChildProcessWorker.js.map +1 -1
  114. package/dist/node/mod.d.ts +31 -4
  115. package/dist/node/mod.d.ts.map +1 -1
  116. package/dist/node/mod.js +33 -6
  117. package/dist/node/mod.js.map +1 -1
  118. package/dist/object/index.d.ts.map +1 -1
  119. package/dist/object/index.js.map +1 -1
  120. package/dist/object/omit.js +1 -1
  121. package/dist/object/omit.js.map +1 -1
  122. package/dist/object/stringify-object.js +2 -2
  123. package/dist/object/stringify-object.js.map +1 -1
  124. package/dist/object/stringify-object.test.js.map +1 -1
  125. package/dist/qr.js +11 -11
  126. package/dist/qr.js.map +1 -1
  127. package/dist/set.js +1 -1
  128. package/dist/set.js.map +1 -1
  129. package/dist/time.js +1 -1
  130. package/dist/time.js.map +1 -1
  131. package/package.json +66 -52
  132. package/src/NoopTracer.ts +20 -9
  133. package/src/binary.ts +1 -1
  134. package/src/browser/Opfs/Opfs.ts +12 -4
  135. package/src/browser/Opfs/debug-utils.ts +8 -6
  136. package/src/browser/Opfs/utils.ts +11 -10
  137. package/src/browser/QuotaExceededError.ts +0 -2
  138. package/src/browser/WebChannelBrowser.ts +1 -1
  139. package/src/browser/WebError.ts +100 -86
  140. package/src/browser/WebLock.ts +15 -15
  141. package/src/browser/detect.ts +6 -6
  142. package/src/cuid/cuid.browser.ts +1 -1
  143. package/src/cuid/cuid.node.ts +1 -1
  144. package/src/effect/BucketQueue.ts +1 -1
  145. package/src/effect/Debug.ts +73 -55
  146. package/src/effect/Effect.ts +118 -36
  147. package/src/effect/Error.ts +1 -1
  148. package/src/effect/Logger.ts +2 -2
  149. package/src/effect/RpcClient.ts +7 -6
  150. package/src/effect/Schema/debug-diff.ts +6 -5
  151. package/src/effect/Schema/index.ts +9 -6
  152. package/src/effect/ServiceContext.ts +6 -6
  153. package/src/effect/Stream.test.ts +5 -4
  154. package/src/effect/SubscriptionRef.ts +5 -5
  155. package/src/effect/WebChannel/WebChannel.ts +6 -6
  156. package/src/effect/WebChannel/broadcastChannelWithAck.ts +4 -4
  157. package/src/effect/WebChannel/common.ts +4 -4
  158. package/src/effect/WebSocket.ts +3 -3
  159. package/src/effect/mod.ts +1 -0
  160. package/src/effect/spanEvent.test.ts +95 -0
  161. package/src/effect/spanEvent.ts +15 -0
  162. package/src/env.ts +2 -1
  163. package/src/fast-deep-equal.ts +9 -9
  164. package/src/global.ts +0 -2
  165. package/src/misc.ts +12 -4
  166. package/src/mod.ts +11 -11
  167. package/src/node/ChildProcessRunner/ChildProcessRunner.ts +23 -10
  168. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.ts +11 -7
  169. package/src/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.ts +10 -11
  170. package/src/node/ChildProcessRunner/ChildProcessWorker.ts +5 -4
  171. package/src/node/mod.ts +38 -6
  172. package/src/object/index.ts +1 -1
  173. package/src/object/omit.ts +1 -1
  174. package/src/object/stringify-object.test.ts +1 -0
  175. package/src/object/stringify-object.ts +2 -2
  176. package/src/qr.ts +11 -11
  177. package/src/set.ts +1 -1
  178. package/src/time.ts +1 -1
  179. package/dist/.tsbuildinfo.json +0 -1
@@ -6,6 +6,7 @@ import { Protocol } from '@effect/rpc/RpcClient'
6
6
  import { constPing, type FromServerEncoded } from '@effect/rpc/RpcMessage'
7
7
  import { Cause, Deferred, Effect, Layer, Option, Schedule, type Scope } from 'effect'
8
8
  import { constVoid, identity } from 'effect/Function'
9
+
9
10
  import * as SubscriptionRef from './SubscriptionRef.ts'
10
11
 
11
12
  // This is based on `makeProtocolSocket` / `layerProtocolSocket` from `@effect/rpc` in order to:
@@ -119,8 +120,8 @@ export const makeProtocolSocketWithIsConnected = (options: {
119
120
 
120
121
  const error = Cause.failureOption(cause)
121
122
  if (
122
- options?.retryTransientErrors &&
123
- Option.isSome(error) &&
123
+ options?.retryTransientErrors !== undefined &&
124
+ Option.isSome(error) === true &&
124
125
  (error.value.reason === 'Open' || error.value.reason === 'OpenTimeout')
125
126
  ) {
126
127
  return
@@ -137,7 +138,7 @@ export const makeProtocolSocketWithIsConnected = (options: {
137
138
  }),
138
139
  ),
139
140
  // CHANGED: make configurable via schedule
140
- options?.retryTransientErrors ? Effect.retry(options.retryTransientErrors) : identity,
141
+ options?.retryTransientErrors !== undefined ? Effect.retry(options.retryTransientErrors) : identity,
141
142
  Effect.annotateLogs({
142
143
  module: 'RpcClient',
143
144
  method: 'makeProtocolSocket',
@@ -171,7 +172,7 @@ const makePinger = Effect.fnUntraced(function* <A, E, R>(
171
172
  pingSchedule: Schedule.Schedule<unknown> = Schedule.spaced(10000).pipe(Schedule.addDelay(() => 5000)),
172
173
  ) {
173
174
  // CHANGED: add manual ping deferreds
174
- const manualPingDeferreds = new Set<Deferred.Deferred<void, never>>()
175
+ const manualPingDeferreds = new Set<Deferred.Deferred<void>>()
175
176
 
176
177
  let recievedPong = true
177
178
  const latch = Effect.unsafeMakeLatch()
@@ -188,7 +189,7 @@ const makePinger = Effect.fnUntraced(function* <A, E, R>(
188
189
  }
189
190
  yield* Effect.suspend(() => {
190
191
  // Starting new ping
191
- if (!recievedPong) return latch.open
192
+ if (recievedPong === false) return latch.open
192
193
  recievedPong = false
193
194
  return writePing
194
195
  }).pipe(
@@ -202,7 +203,7 @@ const makePinger = Effect.fnUntraced(function* <A, E, R>(
202
203
 
203
204
  // CHANGED: add manual ping
204
205
  const ping = Effect.gen(function* () {
205
- const deferred = yield* Deferred.make<void, never>()
206
+ const deferred = yield* Deferred.make<void>()
206
207
  manualPingDeferreds.add(deferred)
207
208
  yield* deferred
208
209
  manualPingDeferreds.delete(deferred)
@@ -23,8 +23,8 @@ const debugDiffImpl = (ast: SchemaAST.AST, a: any, b: any, path: string, bag: Di
23
23
  if (eq(a, b) === false) {
24
24
  // bag.push({ path, a, b, ast })
25
25
 
26
- if (SchemaAST.isUnion(ast)) {
27
- if (isTaggedUnion(ast)) {
26
+ if (SchemaAST.isUnion(ast) === true) {
27
+ if (isTaggedUnion(ast) === true) {
28
28
  bag.push({ path, a, b, ast })
29
29
  return
30
30
  } else {
@@ -35,7 +35,7 @@ const debugDiffImpl = (ast: SchemaAST.AST, a: any, b: any, path: string, bag: Di
35
35
  } catch {}
36
36
  }
37
37
  }
38
- } else if (SchemaAST.isTypeLiteral(ast)) {
38
+ } else if (SchemaAST.isTypeLiteral(ast) === true) {
39
39
  const props = SchemaAST.getPropertySignatures(ast)
40
40
  for (const prop of props) {
41
41
  debugDiffImpl(prop.type, a[prop.name], b[prop.name], `${path}.${prop.name.toString()}`, bag)
@@ -47,12 +47,13 @@ const debugDiffImpl = (ast: SchemaAST.AST, a: any, b: any, path: string, bag: Di
47
47
  }
48
48
  }
49
49
 
50
- const isTaggedUnion = (ast: SchemaAST.AST) => {
51
- if (SchemaAST.isUnion(ast)) {
50
+ const isTaggedUnion = (ast: SchemaAST.AST): boolean => {
51
+ if (SchemaAST.isUnion(ast) === true) {
52
52
  return ast.types.every((type) => {
53
53
  if (SchemaAST.isTypeLiteral(type) === false) return false
54
54
  const props = SchemaAST.getPropertySignatures(type)
55
55
  return props.some((prop) => prop.name.toString() === '_tag')
56
56
  })
57
57
  }
58
+ return false
58
59
  }
@@ -24,7 +24,7 @@ export const hash = (schema: Schema.Schema<any>) => {
24
24
  }
25
25
 
26
26
  const resolveStructAst = (ast: SchemaAST.AST): SchemaAST.AST => {
27
- if (SchemaAST.isTransformation(ast)) {
27
+ if (SchemaAST.isTransformation(ast) === true) {
28
28
  return resolveStructAst(ast.from)
29
29
  }
30
30
 
@@ -38,9 +38,12 @@ export const getResolvedPropertySignatures = (
38
38
  return SchemaAST.getPropertySignatures(resolvedAst)
39
39
  }
40
40
 
41
+ /** Objects that can be transferred between contexts (workers, etc.) */
42
+ type TransferableObject = ArrayBuffer | MessagePort
43
+
41
44
  export const encodeWithTransferables =
42
- <A, I, R>(schema: Schema.Schema<A, I, R>, options?: ParseOptions | undefined) =>
43
- (a: A, overrideOptions?: ParseOptions | undefined): Effect.Effect<[I, Transferable[]], ParseError, R> =>
45
+ <A, I, R>(schema: Schema.Schema<A, I, R>, options?: ParseOptions) =>
46
+ (a: A, overrideOptions?: ParseOptions): Effect.Effect<[I, TransferableObject[]], ParseError, R> =>
44
47
  Effect.gen(function* () {
45
48
  const collector = yield* Transferable.makeCollector
46
49
 
@@ -48,11 +51,11 @@ export const encodeWithTransferables =
48
51
  Effect.provideService(Transferable.Collector, collector),
49
52
  )
50
53
 
51
- return [encoded, collector.unsafeRead() as Transferable[]]
54
+ return [encoded, collector.unsafeRead() as TransferableObject[]]
52
55
  })
53
56
 
54
57
  export const decodeSyncDebug: <A, I>(
55
- schema: Schema.Schema<A, I, never>,
58
+ schema: Schema.Schema<A, I>,
56
59
  options?: SchemaAST.ParseOptions,
57
60
  ) => (i: I, overrideOptions?: SchemaAST.ParseOptions) => A = (schema, options) => (input, overrideOptions) => {
58
61
  const res = Schema.decodeEither(schema, options)(input, overrideOptions)
@@ -64,7 +67,7 @@ export const decodeSyncDebug: <A, I>(
64
67
  }
65
68
 
66
69
  export const encodeSyncDebug: <A, I>(
67
- schema: Schema.Schema<A, I, never>,
70
+ schema: Schema.Schema<A, I>,
68
71
  options?: SchemaAST.ParseOptions,
69
72
  ) => (a: A, overrideOptions?: SchemaAST.ParseOptions) => I = (schema, options) => (input, overrideOptions) => {
70
73
  const res = Schema.encodeEither(schema, options)(input, overrideOptions)
@@ -24,13 +24,13 @@ export const make = <TStaticData, Ctx>(
24
24
  close: Effect.Effect<void> = Effect.dieMessage('close not implemented'),
25
25
  ): ServiceContext<Ctx, TStaticData> => {
26
26
  return {
27
- provide: (self) => Effect.provide(runtime)(self),
28
- runWithErrorLog: <E, A>(self: Effect.Effect<A, E, Ctx>) => runWithErrorLog(Effect.provide(runtime)(self)),
29
- runSync: <E, A>(self: Effect.Effect<A, E, Ctx>) => Effect.runSync(Effect.provide(runtime)(self)),
27
+ provide: (self) => self.pipe(Effect.provide(runtime)),
28
+ runWithErrorLog: <E, A>(self: Effect.Effect<A, E, Ctx>) => runWithErrorLog(self.pipe(Effect.provide(runtime))),
29
+ runSync: <E, A>(self: Effect.Effect<A, E, Ctx>) => Effect.runSync(self.pipe(Effect.provide(runtime))),
30
30
  runPromiseWithErrorLog: <E, A>(self: Effect.Effect<A, E, Ctx>) =>
31
- runPromiseWithErrorLog(Effect.provide(runtime)(self)),
32
- runPromiseExit: <E, A>(self: Effect.Effect<A, E, Ctx>) => Effect.runPromiseExit(Effect.provide(runtime)(self)),
33
- runPromise: <E, A>(self: Effect.Effect<A, E, Ctx>) => Effect.runPromise(Effect.provide(runtime)(self)),
31
+ runPromiseWithErrorLog(self.pipe(Effect.provide(runtime))),
32
+ runPromiseExit: <E, A>(self: Effect.Effect<A, E, Ctx>) => Effect.runPromiseExit(self.pipe(Effect.provide(runtime))),
33
+ runPromise: <E, A>(self: Effect.Effect<A, E, Ctx>) => Effect.runPromise(self.pipe(Effect.provide(runtime))),
34
34
  withRuntime: (fn) => fn(runtime),
35
35
  close: close,
36
36
  closePromise: () => Effect.runPromise(close),
@@ -1,5 +1,6 @@
1
1
  import { Effect, Option, Stream } from 'effect'
2
2
  import { describe, expect, it } from 'vitest'
3
+
3
4
  import { concatWithLastElement, runCollectReadonlyArray } from './Stream.ts'
4
5
 
5
6
  describe('concatWithLastElement', () => {
@@ -9,7 +10,7 @@ describe('concatWithLastElement', () => {
9
10
  lastElement.pipe(
10
11
  Option.match({
11
12
  onNone: () => Stream.make('no-previous'),
12
- onSome: (last) => Stream.make(`last-was-${last}`, 'continuing'),
13
+ onSome: (last) => Stream.make(`last-was-${String(last)}`, 'continuing'),
13
14
  }),
14
15
  ),
15
16
  )
@@ -24,7 +25,7 @@ describe('concatWithLastElement', () => {
24
25
  lastElement.pipe(
25
26
  Option.match({
26
27
  onNone: () => Stream.make('no-previous-element'),
27
- onSome: (last) => Stream.make(`last-was-${last}`),
28
+ onSome: (last) => Stream.make(`last-was-${String(last)}`),
28
29
  }),
29
30
  ),
30
31
  )
@@ -39,7 +40,7 @@ describe('concatWithLastElement', () => {
39
40
  lastElement.pipe(
40
41
  Option.match({
41
42
  onNone: () => Stream.make('unexpected'),
42
- onSome: (last) => Stream.make(`after-${last}`),
43
+ onSome: (last) => Stream.make(`after-${String(last)}`),
43
44
  }),
44
45
  ),
45
46
  )
@@ -83,7 +84,7 @@ describe('concatWithLastElement', () => {
83
84
  const result = concatWithLastElement(stream1, (lastElement) =>
84
85
  lastElement.pipe(
85
86
  Option.match({
86
- onNone: () => Stream.make('no-number') as Stream.Stream<number | string, never, never>,
87
+ onNone: () => Stream.make('no-number') as Stream.Stream<number | string>,
87
88
  onSome: (last) => Stream.make(last * 10, last * 100),
88
89
  }),
89
90
  ),
@@ -5,17 +5,17 @@ import type { Predicate, Refinement } from 'effect/Predicate'
5
5
  export * from 'effect/SubscriptionRef'
6
6
 
7
7
  export const waitUntil: {
8
- <A, B extends A>(
8
+ <A, B extends A>(
9
9
  refinement: Refinement<NoInfer<A>, B>,
10
- ): (sref: SubscriptionRef.SubscriptionRef<A>) => Effect.Effect<B, never, never>
10
+ ): (sref: SubscriptionRef.SubscriptionRef<A>) => Effect.Effect<B>
11
11
  <A, B extends A>(
12
12
  predicate: Predicate<B>,
13
- ): (sref: SubscriptionRef.SubscriptionRef<A>) => Effect.Effect<A, never, never>
13
+ ): (sref: SubscriptionRef.SubscriptionRef<A>) => Effect.Effect<A>
14
14
  <A, B extends A>(
15
15
  sref: SubscriptionRef.SubscriptionRef<A>,
16
16
  refinement: Refinement<NoInfer<A>, B>,
17
- ): Effect.Effect<B, never, never>
18
- <A, B extends A>(sref: SubscriptionRef.SubscriptionRef<A>, predicate: Predicate<B>): Effect.Effect<A, never, never>
17
+ ): Effect.Effect<B>
18
+ <A, B extends A>(sref: SubscriptionRef.SubscriptionRef<A>, predicate: Predicate<B>): Effect.Effect<A>
19
19
  } = dual(2, <A>(sref: SubscriptionRef.SubscriptionRef<A>, predicate: (a: A) => boolean) =>
20
20
  pipe(sref.changes, Stream.filter(predicate), Stream.take(1), Stream.runCollect, Effect.map(Chunk.unsafeHead)),
21
21
  )
@@ -41,7 +41,7 @@ export const noopChannel = <MsgListen, MsgSend>(): Effect.Effect<WebChannel<MsgL
41
41
  export const messagePortChannel: <MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>(args: {
42
42
  port: MessagePort
43
43
  schema: InputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>
44
- debugId?: string | number
44
+ debugId?: string | number | undefined
45
45
  }) => Effect.Effect<WebChannel<MsgListen, MsgSend>, never, Scope.Scope> = ({ port, schema: inputSchema, debugId }) =>
46
46
  Effect.scopeWithCloseable((scope) =>
47
47
  Effect.gen(function* () {
@@ -125,7 +125,7 @@ export const sameThreadChannel = <MsgListen, MsgSend, MsgListenEncoded, MsgSendE
125
125
  export const messagePortChannelWithAck: <MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>(args: {
126
126
  port: MessagePort
127
127
  schema: InputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>
128
- debugId?: string | number
128
+ debugId?: string | number | undefined
129
129
  }) => Effect.Effect<WebChannel<MsgListen, MsgSend>, never, Scope.Scope> = ({ port, schema: inputSchema, debugId }) =>
130
130
  Effect.scopeWithCloseable((scope) =>
131
131
  Effect.gen(function* () {
@@ -191,7 +191,7 @@ export const messagePortChannelWithAck: <MsgListen, MsgSend, MsgListenEncoded, M
191
191
  yield* Deferred.succeed(requestAckMap.get(msg.right.reqId)!, void 0)
192
192
  } else if (msg.right._tag === 'ChannelRequest') {
193
193
  debugInfo.listenTotal++
194
- port.postMessage(Schema.encodeSync(ChannelMessage)({ _tag: 'ChannelRequestAck', reqId: msg.right.id }))
194
+ port.postMessage(yield* Schema.encode(ChannelMessage)({ _tag: 'ChannelRequestAck', reqId: msg.right.id }))
195
195
  }
196
196
  }
197
197
  }),
@@ -311,10 +311,10 @@ export const toOpenChannel = <MsgListen, MsgSend>(
311
311
 
312
312
  yield* channel.listen.pipe(
313
313
  // TODO implement this on the "chunk" level for better performance
314
- options?.heartbeat
314
+ options?.heartbeat !== undefined
315
315
  ? Stream.filterEffect(
316
316
  Effect.fn(function* (msg) {
317
- if (msg._tag === 'Right' && Schema.is(WebChannelHeartbeat)(msg.right)) {
317
+ if (msg._tag === 'Right' && Schema.is(WebChannelHeartbeat)(msg.right) === true) {
318
318
  if (msg.right._tag === 'WebChannel.Ping') {
319
319
  yield* heartbeatChannel.send(WebChannelPong.make({ requestId: msg.right.requestId }))
320
320
  } else {
@@ -336,7 +336,7 @@ export const toOpenChannel = <MsgListen, MsgSend>(
336
336
  Effect.forkScoped,
337
337
  )
338
338
 
339
- if (options?.heartbeat) {
339
+ if (options?.heartbeat !== undefined) {
340
340
  const { interval, timeout } = options.heartbeat
341
341
  yield* Effect.gen(function* () {
342
342
  while (true) {
@@ -69,7 +69,7 @@ export const broadcastChannelWithAck = <MsgListen, MsgSend, MsgListenEncoded, Ms
69
69
  peerIdRef.current = data.from
70
70
  postMessage(ConnectAckMessage.make({ from: connectionId, to: data.from }))
71
71
  yield* connectedLatch.open
72
- break
72
+ return undefined
73
73
  }
74
74
  // Case: other side sends connect-ack message (because otherside was already online when this side connected)
75
75
  case 'ConnectAckMessage': {
@@ -77,7 +77,7 @@ export const broadcastChannelWithAck = <MsgListen, MsgSend, MsgListenEncoded, Ms
77
77
  peerIdRef.current = data.from
78
78
  yield* connectedLatch.open
79
79
  }
80
- break
80
+ return undefined
81
81
  }
82
82
  case 'DisconnectMessage': {
83
83
  if (data.from === peerIdRef.current) {
@@ -85,13 +85,13 @@ export const broadcastChannelWithAck = <MsgListen, MsgSend, MsgListenEncoded, Ms
85
85
  yield* connectedLatch.close
86
86
  yield* establishConnection
87
87
  }
88
- break
88
+ return undefined
89
89
  }
90
90
  case 'PayloadMessage': {
91
91
  if (data.to === connectionId) {
92
92
  return Schema.decodeEither(schema.listen)(data.payload)
93
93
  }
94
- break
94
+ return undefined
95
95
  }
96
96
  }
97
97
  }),
@@ -54,19 +54,19 @@ export type OutputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded> =
54
54
  export const mapSchema = <MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>(
55
55
  schema: InputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>,
56
56
  ): OutputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded> =>
57
- Predicate.hasProperty(schema, 'send') && Predicate.hasProperty(schema, 'listen')
57
+ Predicate.hasProperty(schema, 'send') === true && Predicate.hasProperty(schema, 'listen') === true
58
58
  ? (schemaWithWebChannelMessages(schema) as any)
59
59
  : (schemaWithWebChannelMessages({ send: schema, listen: schema }) as any)
60
60
 
61
61
  export const listenToDebugPing =
62
62
  (channelName: string) =>
63
63
  <MsgListen>(
64
- stream: Stream.Stream<Either.Either<MsgListen, ParseResult.ParseError>, never>,
65
- ): Stream.Stream<Either.Either<MsgListen, ParseResult.ParseError>, never> =>
64
+ stream: Stream.Stream<Either.Either<MsgListen, ParseResult.ParseError>>,
65
+ ): Stream.Stream<Either.Either<MsgListen, ParseResult.ParseError>> =>
66
66
  stream.pipe(
67
67
  Stream.filterEffect(
68
68
  Effect.fn(function* (msg) {
69
- if (msg._tag === 'Right' && Schema.is(DebugPingMessage)(msg.right)) {
69
+ if (msg._tag === 'Right' && Schema.is(DebugPingMessage)(msg.right) === true) {
70
70
  yield* Effect.logDebug(`WebChannel:ping [${channelName}] ${msg.right.message}`, msg.right.payload)
71
71
  return false
72
72
  }
@@ -2,7 +2,7 @@ import { HttpClient } from '@effect/platform'
2
2
  import type { Schedule, Scope } from 'effect'
3
3
  import { Effect, Exit, identity, Schema } from 'effect'
4
4
 
5
- export class WebSocketError extends Schema.TaggedError<WebSocketError>()('WebSocketError', {
5
+ export class WebSocketError extends Schema.TaggedError<WebSocketError>('~@livestore/utils/WebSocketError')('WebSocketError', {
6
6
  cause: Schema.Defect,
7
7
  }) {}
8
8
 
@@ -63,7 +63,7 @@ export const makeWebSocket = ({
63
63
  }
64
64
  }).pipe(
65
65
  Effect.tapErrorTag('WebSocketError', () => tryLogWebsocketConnectError(url)),
66
- reconnect ? Effect.retry(reconnect) : identity,
66
+ reconnect !== undefined ? Effect.retry(reconnect) : identity,
67
67
  )
68
68
 
69
69
  /**
@@ -83,7 +83,7 @@ export const makeWebSocket = ({
83
83
  Effect.fn(function* (exit) {
84
84
  yield* Effect.try({
85
85
  try: () => {
86
- if (Exit.isFailure(exit)) {
86
+ if (Exit.isFailure(exit) === true) {
87
87
  socket.close(3000)
88
88
  } else {
89
89
  socket.close(1000)
package/src/effect/mod.ts CHANGED
@@ -131,6 +131,7 @@ export {
131
131
  SortedMap,
132
132
  STM,
133
133
  SynchronizedRef,
134
+ TestClock,
134
135
  TestServices,
135
136
  TQueue,
136
137
  TRef,
@@ -0,0 +1,95 @@
1
+ import { it, describe, expect } from '@effect/vitest'
2
+ import { Effect, Tracer } from 'effect'
3
+
4
+ import { spanEvent } from './spanEvent.ts'
5
+
6
+ type RecordedEvent = { name: string; attributes: Record<string, unknown> | undefined }
7
+
8
+ /**
9
+ * Creates a test tracer that captures span events for assertion,
10
+ * using only the public `Tracer` API (no internal NativeSpan access).
11
+ * Each span records its own events, retrievable by span name.
12
+ */
13
+ const makeTestTracer = () => {
14
+ const spanEvents = new Map<string, Array<RecordedEvent>>()
15
+
16
+ const tracer = Tracer.make({
17
+ span(name, parent, context, links, startTime, kind) {
18
+ const events: Array<RecordedEvent> = []
19
+ spanEvents.set(name, events)
20
+ const attributes = new Map<string, unknown>()
21
+ return {
22
+ _tag: 'Span' as const,
23
+ name,
24
+ spanId: `test-${name}`,
25
+ traceId: 'test-trace',
26
+ parent,
27
+ context,
28
+ status: { _tag: 'Started' as const, startTime },
29
+ attributes,
30
+ links: [...links],
31
+ sampled: true,
32
+ kind,
33
+ end() {},
34
+ attribute(key: string, value: unknown) {
35
+ attributes.set(key, value)
36
+ },
37
+ event(eventName: string, _startTime: bigint, attrs?: Record<string, unknown>) {
38
+ events.push({ name: eventName, attributes: attrs })
39
+ },
40
+ addLinks() {},
41
+ }
42
+ },
43
+ context(f) {
44
+ return f()
45
+ },
46
+ })
47
+
48
+ return {
49
+ tracer,
50
+ getEvents: (spanName: string): Array<RecordedEvent> => spanEvents.get(spanName) ?? [],
51
+ }
52
+ }
53
+
54
+ describe('spanEvent', () => {
55
+ it.effect('should emit a span event with the given message', () => {
56
+ const { tracer, getEvents } = makeTestTracer()
57
+ return Effect.gen(function* () {
58
+ yield* spanEvent('test-event')
59
+ const events = getEvents('test-span')
60
+ expect(events).toHaveLength(1)
61
+ expect(events[0]!.name).toBe('test-event')
62
+ }).pipe(Effect.withSpan('test-span'), Effect.withTracer(tracer))
63
+ })
64
+
65
+ it.effect('should emit a span event with attributes', () => {
66
+ const { tracer, getEvents } = makeTestTracer()
67
+ return Effect.gen(function* () {
68
+ yield* spanEvent('event-with-attrs', { key1: 'value1', key2: 42 })
69
+ const events = getEvents('test-span')
70
+ expect(events).toHaveLength(1)
71
+ expect(events[0]!.name).toBe('event-with-attrs')
72
+ expect(events[0]!.attributes).toMatchObject({ key1: 'value1', key2: 42 })
73
+ }).pipe(Effect.withSpan('test-span'), Effect.withTracer(tracer))
74
+ })
75
+
76
+ it.effect('should emit to the nearest enclosing span', () => {
77
+ const { tracer, getEvents } = makeTestTracer()
78
+ return Effect.gen(function* () {
79
+ yield* spanEvent('outer-event')
80
+
81
+ yield* spanEvent('inner-event').pipe(Effect.withSpan('inner-span'))
82
+
83
+ const outerEvents = getEvents('outer-span')
84
+ const innerEvents = getEvents('inner-span')
85
+
86
+ expect(outerEvents).toHaveLength(1)
87
+ expect(outerEvents[0]!.name).toBe('outer-event')
88
+
89
+ expect(innerEvents).toHaveLength(1)
90
+ expect(innerEvents[0]!.name).toBe('inner-event')
91
+ }).pipe(Effect.withSpan('outer-span'), Effect.withTracer(tracer))
92
+ })
93
+
94
+ it.effect('should be a no-op when no span is in context', () => spanEvent('orphan-event'))
95
+ })
@@ -0,0 +1,15 @@
1
+ import { Effect, FiberRef, HashSet, Logger } from 'effect'
2
+
3
+ /**
4
+ * Emits a span event on the current Effect span via the tracer logger.
5
+ *
6
+ * @remarks
7
+ *
8
+ * Unlike raw `otelSpan.addEvent`, this doesn't require manual span threading —
9
+ * it automatically targets the nearest enclosing `Effect.withSpan`. If no span
10
+ * is in context, the call is a no-op.
11
+ */
12
+ export const spanEvent = (message: any, attributes?: Record<string, unknown>) =>
13
+ Effect.locallyWith(Effect.log(message).pipe(Effect.annotateLogs(attributes ?? {})), FiberRef.currentLoggers, () =>
14
+ HashSet.make(Logger.tracerLogger),
15
+ )
package/src/env.ts CHANGED
@@ -26,4 +26,5 @@ export const IS_CI = envTruish(env('CI'))
26
26
 
27
27
  export const IS_BUN = typeof Bun !== 'undefined'
28
28
 
29
- export const IS_REACT_NATIVE = typeof navigator !== 'undefined' && navigator.product === 'ReactNative'
29
+ export const IS_REACT_NATIVE =
30
+ typeof navigator !== 'undefined' && (navigator as unknown as Record<string, unknown>)['product'] === 'ReactNative'
@@ -4,36 +4,36 @@
4
4
  export const deepEqual = <T>(a: T, b: T): boolean => {
5
5
  if (a === b) return true
6
6
 
7
- if (a && b && typeof a === 'object' && typeof b === 'object') {
7
+ if (a != null && b != null && typeof a === 'object' && typeof b === 'object') {
8
8
  if (a.constructor !== b.constructor) return false
9
9
 
10
10
  let length: number
11
11
  let i: any
12
12
  let keys: any
13
- if (Array.isArray(a)) {
13
+ if (Array.isArray(a) === true) {
14
14
  length = a.length
15
15
  // @ts-expect-error ...
16
16
  if (length !== b.length) return false
17
17
  for (i = length; i-- !== 0; )
18
18
  // @ts-expect-error ...
19
- if (!deepEqual(a[i], b[i])) return false
19
+ if (deepEqual(a[i], b[i]) === false) return false
20
20
  return true
21
21
  }
22
22
 
23
23
  if (a instanceof Map && b instanceof Map) {
24
24
  if (a.size !== b.size) return false
25
- for (i of a.entries()) if (!b.has(i[0])) return false
26
- for (i of a.entries()) if (!deepEqual(i[1], b.get(i[0]))) return false
25
+ for (i of a.entries()) if (b.has(i[0]) === false) return false
26
+ for (i of a.entries()) if (deepEqual(i[1], b.get(i[0])) === false) return false
27
27
  return true
28
28
  }
29
29
 
30
30
  if (a instanceof Set && b instanceof Set) {
31
31
  if (a.size !== b.size) return false
32
- for (i of a.entries()) if (!b.has(i[0])) return false
32
+ for (i of a.entries()) if (b.has(i[0]) === false) return false
33
33
  return true
34
34
  }
35
35
 
36
- if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
36
+ if (ArrayBuffer.isView(a) === true && ArrayBuffer.isView(b) === true) {
37
37
  // @ts-expect-error ...
38
38
  length = a.length
39
39
  // @ts-expect-error ...
@@ -53,13 +53,13 @@ export const deepEqual = <T>(a: T, b: T): boolean => {
53
53
  length = keys.length
54
54
  if (length !== Object.keys(b).length) return false
55
55
 
56
- for (i = length; i-- !== 0; ) if (!Object.hasOwn(b, keys[i])) return false
56
+ for (i = length; i-- !== 0; ) if (Object.prototype.hasOwnProperty.call(b, keys[i]) === false) return false
57
57
 
58
58
  for (i = length; i-- !== 0; ) {
59
59
  const key = keys[i]
60
60
 
61
61
  // @ts-expect-error ...
62
- if (!deepEqual(a[key], b[key])) return false
62
+ if (deepEqual(a[key], b[key]) === false) return false
63
63
  }
64
64
 
65
65
  return true
package/src/global.ts CHANGED
@@ -6,5 +6,3 @@ declare global {
6
6
  main: boolean
7
7
  }
8
8
  }
9
-
10
- export {}
package/src/misc.ts CHANGED
@@ -10,7 +10,7 @@ export const isDevEnv = () => {
10
10
  }
11
11
 
12
12
  // @ts-expect-error Only exists in Expo / RN
13
- if (globalThis?.__DEV__) {
13
+ if (globalThis?.__DEV__ === true) {
14
14
  return true
15
15
  }
16
16
 
@@ -46,10 +46,18 @@ export const tryAsFunctionAndNew = <TArg, TResult>(
46
46
  export const envTruish = (env: string | undefined) =>
47
47
  env !== undefined && env.toLowerCase() !== 'false' && env.toLowerCase() !== '0'
48
48
 
49
- export const shouldNeverHappen = (msg?: string, ...args: any[]): never => {
49
+ /**
50
+ * Logs and throws for impossible states, pausing at a breakpoint in development.
51
+ *
52
+ * @param msg - The error message to log and pass to the error.
53
+ * @param args - Arbitrary arguments to include in the log.
54
+ *
55
+ * @see {@link dieDebugger} for the Effect equivalent.
56
+ */
57
+ export const shouldNeverHappen = (msg?: string, ...args: ReadonlyArray<unknown>): never => {
50
58
  console.error(msg, ...args)
51
- if (isDevEnv()) {
52
- // biome-ignore lint/suspicious/noDebugger: debugging
59
+ if (isDevEnv() === true) {
60
+ // oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint during development
53
61
  debugger
54
62
  }
55
63