effect 3.4.9 → 3.5.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 (159) hide show
  1. package/RcMap/package.json +6 -0
  2. package/RcRef/package.json +6 -0
  3. package/dist/cjs/Cause.js +22 -1
  4. package/dist/cjs/Cause.js.map +1 -1
  5. package/dist/cjs/Channel.js.map +1 -1
  6. package/dist/cjs/Config.js.map +1 -1
  7. package/dist/cjs/Console.js.map +1 -1
  8. package/dist/cjs/Data.js +3 -1
  9. package/dist/cjs/Data.js.map +1 -1
  10. package/dist/cjs/Duration.js +23 -2
  11. package/dist/cjs/Duration.js.map +1 -1
  12. package/dist/cjs/Logger.js +11 -1
  13. package/dist/cjs/Logger.js.map +1 -1
  14. package/dist/cjs/PubSub.js.map +1 -1
  15. package/dist/cjs/Random.js +9 -1
  16. package/dist/cjs/Random.js.map +1 -1
  17. package/dist/cjs/RcMap.js +52 -0
  18. package/dist/cjs/RcMap.js.map +1 -0
  19. package/dist/cjs/RcRef.js +51 -0
  20. package/dist/cjs/RcRef.js.map +1 -0
  21. package/dist/cjs/Stream.js +29 -2
  22. package/dist/cjs/Stream.js.map +1 -1
  23. package/dist/cjs/index.js +6 -2
  24. package/dist/cjs/index.js.map +1 -1
  25. package/dist/cjs/internal/cause.js +21 -5
  26. package/dist/cjs/internal/cause.js.map +1 -1
  27. package/dist/cjs/internal/channel/channelExecutor.js.map +1 -1
  28. package/dist/cjs/internal/channel.js.map +1 -1
  29. package/dist/cjs/internal/core-effect.js +0 -5
  30. package/dist/cjs/internal/core-effect.js.map +1 -1
  31. package/dist/cjs/internal/core.js +15 -10
  32. package/dist/cjs/internal/core.js.map +1 -1
  33. package/dist/cjs/internal/defaultServices.js +1 -1
  34. package/dist/cjs/internal/defaultServices.js.map +1 -1
  35. package/dist/cjs/internal/fiberRuntime.js +3 -1
  36. package/dist/cjs/internal/fiberRuntime.js.map +1 -1
  37. package/dist/cjs/internal/logger.js +128 -27
  38. package/dist/cjs/internal/logger.js.map +1 -1
  39. package/dist/cjs/internal/pubsub.js +216 -36
  40. package/dist/cjs/internal/pubsub.js.map +1 -1
  41. package/dist/cjs/internal/random.js +2 -1
  42. package/dist/cjs/internal/random.js.map +1 -1
  43. package/dist/cjs/internal/rcMap.js +129 -0
  44. package/dist/cjs/internal/rcMap.js.map +1 -0
  45. package/dist/cjs/internal/rcRef.js +122 -0
  46. package/dist/cjs/internal/rcRef.js.map +1 -0
  47. package/dist/cjs/internal/stream.js +57 -11
  48. package/dist/cjs/internal/stream.js.map +1 -1
  49. package/dist/cjs/internal/version.js +1 -1
  50. package/dist/dts/Cause.d.ts +40 -1
  51. package/dist/dts/Cause.d.ts.map +1 -1
  52. package/dist/dts/Channel.d.ts +3 -3
  53. package/dist/dts/Channel.d.ts.map +1 -1
  54. package/dist/dts/Config.d.ts +5 -0
  55. package/dist/dts/Config.d.ts.map +1 -1
  56. package/dist/dts/Console.d.ts +2 -4
  57. package/dist/dts/Console.d.ts.map +1 -1
  58. package/dist/dts/Duration.d.ts +5 -0
  59. package/dist/dts/Duration.d.ts.map +1 -1
  60. package/dist/dts/Logger.d.ts +15 -0
  61. package/dist/dts/Logger.d.ts.map +1 -1
  62. package/dist/dts/PubSub.d.ts +15 -4
  63. package/dist/dts/PubSub.d.ts.map +1 -1
  64. package/dist/dts/Random.d.ts +8 -0
  65. package/dist/dts/Random.d.ts.map +1 -1
  66. package/dist/dts/RcMap.d.ts +93 -0
  67. package/dist/dts/RcMap.d.ts.map +1 -0
  68. package/dist/dts/RcRef.d.ts +83 -0
  69. package/dist/dts/RcRef.d.ts.map +1 -0
  70. package/dist/dts/Stream.d.ts +64 -26
  71. package/dist/dts/Stream.d.ts.map +1 -1
  72. package/dist/dts/index.d.ts +8 -0
  73. package/dist/dts/index.d.ts.map +1 -1
  74. package/dist/dts/internal/core-effect.d.ts.map +1 -1
  75. package/dist/dts/internal/fiberRuntime.d.ts.map +1 -1
  76. package/dist/dts/internal/logger.d.ts.map +1 -1
  77. package/dist/dts/internal/random.d.ts +1 -1
  78. package/dist/dts/internal/random.d.ts.map +1 -1
  79. package/dist/dts/internal/rcMap.d.ts +2 -0
  80. package/dist/dts/internal/rcMap.d.ts.map +1 -0
  81. package/dist/dts/internal/rcRef.d.ts +2 -0
  82. package/dist/dts/internal/rcRef.d.ts.map +1 -0
  83. package/dist/dts/internal/stream.d.ts +1 -0
  84. package/dist/dts/internal/stream.d.ts.map +1 -1
  85. package/dist/esm/Cause.js +21 -0
  86. package/dist/esm/Cause.js.map +1 -1
  87. package/dist/esm/Channel.js.map +1 -1
  88. package/dist/esm/Config.js.map +1 -1
  89. package/dist/esm/Console.js.map +1 -1
  90. package/dist/esm/Data.js +3 -1
  91. package/dist/esm/Data.js.map +1 -1
  92. package/dist/esm/Duration.js +20 -0
  93. package/dist/esm/Duration.js.map +1 -1
  94. package/dist/esm/Logger.js +10 -0
  95. package/dist/esm/Logger.js.map +1 -1
  96. package/dist/esm/PubSub.js.map +1 -1
  97. package/dist/esm/Random.js +8 -0
  98. package/dist/esm/Random.js.map +1 -1
  99. package/dist/esm/RcMap.js +44 -0
  100. package/dist/esm/RcMap.js.map +1 -0
  101. package/dist/esm/RcRef.js +43 -0
  102. package/dist/esm/RcRef.js.map +1 -0
  103. package/dist/esm/Stream.js +27 -0
  104. package/dist/esm/Stream.js.map +1 -1
  105. package/dist/esm/index.js +8 -0
  106. package/dist/esm/index.js.map +1 -1
  107. package/dist/esm/internal/cause.js +21 -5
  108. package/dist/esm/internal/cause.js.map +1 -1
  109. package/dist/esm/internal/channel/channelExecutor.js.map +1 -1
  110. package/dist/esm/internal/channel.js.map +1 -1
  111. package/dist/esm/internal/core-effect.js +0 -5
  112. package/dist/esm/internal/core-effect.js.map +1 -1
  113. package/dist/esm/internal/core.js +11 -7
  114. package/dist/esm/internal/core.js.map +1 -1
  115. package/dist/esm/internal/defaultServices.js +1 -1
  116. package/dist/esm/internal/defaultServices.js.map +1 -1
  117. package/dist/esm/internal/fiberRuntime.js +2 -0
  118. package/dist/esm/internal/fiberRuntime.js.map +1 -1
  119. package/dist/esm/internal/logger.js +126 -26
  120. package/dist/esm/internal/logger.js.map +1 -1
  121. package/dist/esm/internal/pubsub.js +216 -36
  122. package/dist/esm/internal/pubsub.js.map +1 -1
  123. package/dist/esm/internal/random.js +2 -1
  124. package/dist/esm/internal/random.js.map +1 -1
  125. package/dist/esm/internal/rcMap.js +120 -0
  126. package/dist/esm/internal/rcMap.js.map +1 -0
  127. package/dist/esm/internal/rcRef.js +112 -0
  128. package/dist/esm/internal/rcRef.js.map +1 -0
  129. package/dist/esm/internal/stream.js +53 -8
  130. package/dist/esm/internal/stream.js.map +1 -1
  131. package/dist/esm/internal/version.js +1 -1
  132. package/package.json +17 -1
  133. package/src/Cause.ts +47 -1
  134. package/src/Channel.ts +3 -3
  135. package/src/Config.ts +6 -0
  136. package/src/Console.ts +2 -4
  137. package/src/Data.ts +1 -1
  138. package/src/Duration.ts +18 -0
  139. package/src/Logger.ts +19 -0
  140. package/src/PubSub.ts +11 -4
  141. package/src/Random.ts +9 -0
  142. package/src/RcMap.ts +103 -0
  143. package/src/RcRef.ts +91 -0
  144. package/src/Stream.ts +83 -26
  145. package/src/index.ts +10 -0
  146. package/src/internal/cause.ts +26 -5
  147. package/src/internal/channel/channelExecutor.ts +1 -1
  148. package/src/internal/channel.ts +4 -3
  149. package/src/internal/core-effect.ts +0 -5
  150. package/src/internal/core.ts +19 -9
  151. package/src/internal/defaultServices.ts +1 -1
  152. package/src/internal/fiberRuntime.ts +6 -0
  153. package/src/internal/logger.ts +133 -27
  154. package/src/internal/pubsub.ts +249 -58
  155. package/src/internal/random.ts +2 -1
  156. package/src/internal/rcMap.ts +213 -0
  157. package/src/internal/rcRef.ts +172 -0
  158. package/src/internal/stream.ts +325 -111
  159. package/src/internal/version.ts +1 -1
@@ -28,6 +28,7 @@ export interface AtomicPubSub<in out A> {
28
28
  publishAll(elements: Iterable<A>): Chunk.Chunk<A>
29
29
  slide(): void
30
30
  subscribe(): Subscription<A>
31
+ replayWindow(): ReplayWindow<A>
31
32
  }
32
33
 
33
34
  /** @internal */
@@ -73,32 +74,49 @@ const removeSubscribers = <A>(
73
74
  }
74
75
 
75
76
  /** @internal */
76
- export const bounded = <A>(requestedCapacity: number): Effect.Effect<PubSub.PubSub<A>> =>
77
- pipe(
78
- core.sync(() => makeBoundedPubSub<A>(requestedCapacity)),
79
- core.flatMap((atomicPubSub) => makePubSub(atomicPubSub, new BackPressureStrategy()))
80
- )
77
+ export const bounded = <A>(
78
+ capacity: number | {
79
+ readonly capacity: number
80
+ readonly replay?: number | undefined
81
+ }
82
+ ): Effect.Effect<PubSub.PubSub<A>> =>
83
+ core.suspend(() => {
84
+ const pubsub = makeBoundedPubSub<A>(capacity)
85
+ return makePubSub(pubsub, new BackPressureStrategy())
86
+ })
81
87
 
82
88
  /** @internal */
83
- export const dropping = <A>(requestedCapacity: number): Effect.Effect<PubSub.PubSub<A>> =>
84
- pipe(
85
- core.sync(() => makeBoundedPubSub<A>(requestedCapacity)),
86
- core.flatMap((atomicPubSub) => makePubSub(atomicPubSub, new DroppingStrategy()))
87
- )
89
+ export const dropping = <A>(
90
+ capacity: number | {
91
+ readonly capacity: number
92
+ readonly replay?: number | undefined
93
+ }
94
+ ): Effect.Effect<PubSub.PubSub<A>> =>
95
+ core.suspend(() => {
96
+ const pubsub = makeBoundedPubSub<A>(capacity)
97
+ return makePubSub(pubsub, new DroppingStrategy())
98
+ })
88
99
 
89
100
  /** @internal */
90
- export const sliding = <A>(requestedCapacity: number): Effect.Effect<PubSub.PubSub<A>> =>
91
- pipe(
92
- core.sync(() => makeBoundedPubSub<A>(requestedCapacity)),
93
- core.flatMap((atomicPubSub) => makePubSub(atomicPubSub, new SlidingStrategy()))
94
- )
101
+ export const sliding = <A>(
102
+ capacity: number | {
103
+ readonly capacity: number
104
+ readonly replay?: number | undefined
105
+ }
106
+ ): Effect.Effect<PubSub.PubSub<A>> =>
107
+ core.suspend(() => {
108
+ const pubsub = makeBoundedPubSub<A>(capacity)
109
+ return makePubSub(pubsub, new SlidingStrategy())
110
+ })
95
111
 
96
112
  /** @internal */
97
- export const unbounded = <A>(): Effect.Effect<PubSub.PubSub<A>> =>
98
- pipe(
99
- core.sync(() => makeUnboundedPubSub<A>()),
100
- core.flatMap((atomicPubSub) => makePubSub(atomicPubSub, new DroppingStrategy()))
101
- )
113
+ export const unbounded = <A>(options?: {
114
+ readonly replay?: number | undefined
115
+ }): Effect.Effect<PubSub.PubSub<A>> =>
116
+ core.suspend(() => {
117
+ const pubsub = makeUnboundedPubSub<A>(options)
118
+ return makePubSub(pubsub, new DroppingStrategy())
119
+ })
102
120
 
103
121
  /** @internal */
104
122
  export const capacity = <A>(self: PubSub.PubSub<A>): number => self.capacity()
@@ -138,21 +156,28 @@ export const subscribe = <A>(self: PubSub.PubSub<A>): Effect.Effect<Queue.Dequeu
138
156
  self.subscribe
139
157
 
140
158
  /** @internal */
141
- const makeBoundedPubSub = <A>(requestedCapacity: number): AtomicPubSub<A> => {
142
- ensureCapacity(requestedCapacity)
143
- if (requestedCapacity === 1) {
144
- return new BoundedPubSubSingle()
145
- } else if (nextPow2(requestedCapacity) === requestedCapacity) {
146
- return new BoundedPubSubPow2(requestedCapacity)
159
+ const makeBoundedPubSub = <A>(
160
+ capacity: number | {
161
+ readonly capacity: number
162
+ readonly replay?: number | undefined
163
+ }
164
+ ): AtomicPubSub<A> => {
165
+ const options = typeof capacity === "number" ? { capacity } : capacity
166
+ ensureCapacity(options.capacity)
167
+ const replayBuffer = options.replay && options.replay > 0 ? new ReplayBuffer<A>(Math.ceil(options.replay)) : undefined
168
+ if (options.capacity === 1) {
169
+ return new BoundedPubSubSingle(replayBuffer)
170
+ } else if (nextPow2(options.capacity) === options.capacity) {
171
+ return new BoundedPubSubPow2(options.capacity, replayBuffer)
147
172
  } else {
148
- return new BoundedPubSubArb(requestedCapacity)
173
+ return new BoundedPubSubArb(options.capacity, replayBuffer)
149
174
  }
150
175
  }
151
176
 
152
177
  /** @internal */
153
- const makeUnboundedPubSub = <A>(): AtomicPubSub<A> => {
154
- return new UnboundedPubSub()
155
- }
178
+ const makeUnboundedPubSub = <A>(options?: {
179
+ readonly replay?: number | undefined
180
+ }): AtomicPubSub<A> => new UnboundedPubSub(options?.replay ? new ReplayBuffer(options.replay) : undefined)
156
181
 
157
182
  /** @internal */
158
183
  const makeSubscription = <A>(
@@ -180,17 +205,17 @@ export const unsafeMakeSubscription = <A>(
180
205
  shutdownHook: Deferred.Deferred<void>,
181
206
  shutdownFlag: MutableRef.MutableRef<boolean>,
182
207
  strategy: PubSubStrategy<A>
183
- ): Queue.Dequeue<A> => {
184
- return new SubscriptionImpl(
208
+ ): Queue.Dequeue<A> =>
209
+ new SubscriptionImpl(
185
210
  pubsub,
186
211
  subscribers,
187
212
  subscription,
188
213
  pollers,
189
214
  shutdownHook,
190
215
  shutdownFlag,
191
- strategy
216
+ strategy,
217
+ pubsub.replayWindow()
192
218
  )
193
- }
194
219
 
195
220
  /** @internal */
196
221
  class BoundedPubSubArb<in out A> implements AtomicPubSub<A> {
@@ -200,12 +225,13 @@ class BoundedPubSubArb<in out A> implements AtomicPubSub<A> {
200
225
  subscriberCount = 0
201
226
  subscribersIndex = 0
202
227
 
203
- readonly capacity: number
228
+ constructor(readonly capacity: number, readonly replayBuffer: ReplayBuffer<A> | undefined) {
229
+ this.array = Array.from({ length: capacity })
230
+ this.subscribers = Array.from({ length: capacity })
231
+ }
204
232
 
205
- constructor(requestedCapacity: number) {
206
- this.array = Array.from({ length: requestedCapacity })
207
- this.subscribers = Array.from({ length: requestedCapacity })
208
- this.capacity = requestedCapacity
233
+ replayWindow(): ReplayWindow<A> {
234
+ return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow
209
235
  }
210
236
 
211
237
  isEmpty(): boolean {
@@ -230,11 +256,17 @@ class BoundedPubSubArb<in out A> implements AtomicPubSub<A> {
230
256
  this.subscribers[index] = this.subscriberCount
231
257
  this.publisherIndex += 1
232
258
  }
259
+ if (this.replayBuffer) {
260
+ this.replayBuffer.offer(value)
261
+ }
233
262
  return true
234
263
  }
235
264
 
236
265
  publishAll(elements: Iterable<A>): Chunk.Chunk<A> {
237
266
  if (this.subscriberCount === 0) {
267
+ if (this.replayBuffer) {
268
+ this.replayBuffer.offerAll(elements)
269
+ }
238
270
  return Chunk.empty()
239
271
  }
240
272
  const chunk = Chunk.fromIterable(elements)
@@ -253,6 +285,9 @@ class BoundedPubSubArb<in out A> implements AtomicPubSub<A> {
253
285
  this.array[index] = a
254
286
  this.subscribers[index] = this.subscriberCount
255
287
  this.publisherIndex += 1
288
+ if (this.replayBuffer) {
289
+ this.replayBuffer.offer(a)
290
+ }
256
291
  }
257
292
  return Chunk.drop(chunk, iteratorIndex)
258
293
  }
@@ -264,6 +299,9 @@ class BoundedPubSubArb<in out A> implements AtomicPubSub<A> {
264
299
  this.subscribers[index] = 0
265
300
  this.subscribersIndex += 1
266
301
  }
302
+ if (this.replayBuffer) {
303
+ this.replayBuffer.slide()
304
+ }
267
305
  }
268
306
 
269
307
  subscribe(): Subscription<A> {
@@ -368,13 +406,14 @@ class BoundedPubSubPow2<in out A> implements AtomicPubSub<A> {
368
406
  subscriberCount = 0
369
407
  subscribersIndex = 0
370
408
 
371
- readonly capacity: number
409
+ constructor(readonly capacity: number, readonly replayBuffer: ReplayBuffer<A> | undefined) {
410
+ this.array = Array.from({ length: capacity })
411
+ this.mask = capacity - 1
412
+ this.subscribers = Array.from({ length: capacity })
413
+ }
372
414
 
373
- constructor(requestedCapacity: number) {
374
- this.array = Array.from({ length: requestedCapacity })
375
- this.mask = requestedCapacity - 1
376
- this.subscribers = Array.from({ length: requestedCapacity })
377
- this.capacity = requestedCapacity
415
+ replayWindow(): ReplayWindow<A> {
416
+ return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow
378
417
  }
379
418
 
380
419
  isEmpty(): boolean {
@@ -399,11 +438,17 @@ class BoundedPubSubPow2<in out A> implements AtomicPubSub<A> {
399
438
  this.subscribers[index] = this.subscriberCount
400
439
  this.publisherIndex += 1
401
440
  }
441
+ if (this.replayBuffer) {
442
+ this.replayBuffer.offer(value)
443
+ }
402
444
  return true
403
445
  }
404
446
 
405
447
  publishAll(elements: Iterable<A>): Chunk.Chunk<A> {
406
448
  if (this.subscriberCount === 0) {
449
+ if (this.replayBuffer) {
450
+ this.replayBuffer.offerAll(elements)
451
+ }
407
452
  return Chunk.empty()
408
453
  }
409
454
  const chunk = Chunk.fromIterable(elements)
@@ -422,6 +467,9 @@ class BoundedPubSubPow2<in out A> implements AtomicPubSub<A> {
422
467
  this.array[index] = elem
423
468
  this.subscribers[index] = this.subscriberCount
424
469
  this.publisherIndex += 1
470
+ if (this.replayBuffer) {
471
+ this.replayBuffer.offer(elem)
472
+ }
425
473
  }
426
474
  return Chunk.drop(chunk, iteratorIndex)
427
475
  }
@@ -433,6 +481,9 @@ class BoundedPubSubPow2<in out A> implements AtomicPubSub<A> {
433
481
  this.subscribers[index] = 0
434
482
  this.subscribersIndex += 1
435
483
  }
484
+ if (this.replayBuffer) {
485
+ this.replayBuffer.slide()
486
+ }
436
487
  }
437
488
 
438
489
  subscribe(): Subscription<A> {
@@ -536,6 +587,11 @@ class BoundedPubSubSingle<in out A> implements AtomicPubSub<A> {
536
587
  value: A = AbsentValue as unknown as A
537
588
 
538
589
  readonly capacity = 1
590
+ constructor(readonly replayBuffer: ReplayBuffer<A> | undefined) {}
591
+
592
+ replayWindow(): ReplayWindow<A> {
593
+ return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow
594
+ }
539
595
 
540
596
  pipe() {
541
597
  return pipeArguments(this, arguments)
@@ -562,11 +618,17 @@ class BoundedPubSubSingle<in out A> implements AtomicPubSub<A> {
562
618
  this.subscribers = this.subscriberCount
563
619
  this.publisherIndex += 1
564
620
  }
621
+ if (this.replayBuffer) {
622
+ this.replayBuffer.offer(value)
623
+ }
565
624
  return true
566
625
  }
567
626
 
568
627
  publishAll(elements: Iterable<A>): Chunk.Chunk<A> {
569
628
  if (this.subscriberCount === 0) {
629
+ if (this.replayBuffer) {
630
+ this.replayBuffer.offerAll(elements)
631
+ }
570
632
  return Chunk.empty()
571
633
  }
572
634
  const chunk = Chunk.fromIterable(elements)
@@ -585,6 +647,9 @@ class BoundedPubSubSingle<in out A> implements AtomicPubSub<A> {
585
647
  this.subscribers = 0
586
648
  this.value = AbsentValue as unknown as A
587
649
  }
650
+ if (this.replayBuffer) {
651
+ this.replayBuffer.slide()
652
+ }
588
653
  }
589
654
 
590
655
  subscribe(): Subscription<A> {
@@ -673,6 +738,11 @@ class UnboundedPubSub<in out A> implements AtomicPubSub<A> {
673
738
  subscribersIndex = 0
674
739
 
675
740
  readonly capacity = Number.MAX_SAFE_INTEGER
741
+ constructor(readonly replayBuffer: ReplayBuffer<A> | undefined) {}
742
+
743
+ replayWindow(): ReplayWindow<A> {
744
+ return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow
745
+ }
676
746
 
677
747
  isEmpty(): boolean {
678
748
  return this.publisherHead === this.publisherTail
@@ -697,6 +767,9 @@ class UnboundedPubSub<in out A> implements AtomicPubSub<A> {
697
767
  this.publisherTail = this.publisherTail.next
698
768
  this.publisherIndex += 1
699
769
  }
770
+ if (this.replayBuffer) {
771
+ this.replayBuffer.offer(value)
772
+ }
700
773
  return true
701
774
  }
702
775
 
@@ -705,6 +778,8 @@ class UnboundedPubSub<in out A> implements AtomicPubSub<A> {
705
778
  for (const a of elements) {
706
779
  this.publish(a)
707
780
  }
781
+ } else if (this.replayBuffer) {
782
+ this.replayBuffer.offerAll(elements)
708
783
  }
709
784
  return Chunk.empty()
710
785
  }
@@ -715,6 +790,9 @@ class UnboundedPubSub<in out A> implements AtomicPubSub<A> {
715
790
  this.publisherHead.value = AbsentValue
716
791
  this.subscribersIndex += 1
717
792
  }
793
+ if (this.replayBuffer) {
794
+ this.replayBuffer.slide()
795
+ }
718
796
  }
719
797
 
720
798
  subscribe(): Subscription<A> {
@@ -841,9 +919,9 @@ class SubscriptionImpl<in out A> implements Queue.Dequeue<A> {
841
919
  readonly pollers: MutableQueue.MutableQueue<Deferred.Deferred<A>>,
842
920
  readonly shutdownHook: Deferred.Deferred<void>,
843
921
  readonly shutdownFlag: MutableRef.MutableRef<boolean>,
844
- readonly strategy: PubSubStrategy<A>
845
- ) {
846
- }
922
+ readonly strategy: PubSubStrategy<A>,
923
+ readonly replayWindow: ReplayWindow<A>
924
+ ) {}
847
925
 
848
926
  pipe() {
849
927
  return pipeArguments(this, arguments)
@@ -861,7 +939,7 @@ class SubscriptionImpl<in out A> implements Queue.Dequeue<A> {
861
939
  return core.suspend(() =>
862
940
  MutableRef.get(this.shutdownFlag)
863
941
  ? core.interrupt
864
- : core.succeed(this.subscription.size())
942
+ : core.succeed(this.subscription.size() + this.replayWindow.remaining)
865
943
  )
866
944
  }
867
945
 
@@ -869,11 +947,15 @@ class SubscriptionImpl<in out A> implements Queue.Dequeue<A> {
869
947
  if (MutableRef.get(this.shutdownFlag)) {
870
948
  return Option.none()
871
949
  }
872
- return Option.some(this.subscription.size())
950
+ return Option.some(this.subscription.size() + this.replayWindow.remaining)
873
951
  }
874
952
 
875
953
  get isFull(): Effect.Effect<boolean> {
876
- return core.map(this.size, (size) => size === this.capacity())
954
+ return core.suspend(() =>
955
+ MutableRef.get(this.shutdownFlag)
956
+ ? core.interrupt
957
+ : core.succeed(this.subscription.size() === this.capacity())
958
+ )
877
959
  }
878
960
 
879
961
  get isEmpty(): Effect.Effect<boolean> {
@@ -915,6 +997,10 @@ class SubscriptionImpl<in out A> implements Queue.Dequeue<A> {
915
997
  if (MutableRef.get(this.shutdownFlag)) {
916
998
  return core.interrupt
917
999
  }
1000
+ if (this.replayWindow.remaining > 0) {
1001
+ const message = this.replayWindow.take()!
1002
+ return core.succeed(message)
1003
+ }
918
1004
  const message = MutableQueue.isEmpty(this.pollers)
919
1005
  ? this.subscription.poll(MutableQueue.EmptyMutableQueue)
920
1006
  : MutableQueue.EmptyMutableQueue
@@ -950,6 +1036,9 @@ class SubscriptionImpl<in out A> implements Queue.Dequeue<A> {
950
1036
  ? unsafePollAllSubscription(this.subscription)
951
1037
  : Chunk.empty()
952
1038
  this.strategy.unsafeOnPubSubEmptySpace(this.pubsub, this.subscribers)
1039
+ if (this.replayWindow.remaining > 0) {
1040
+ return core.succeed(Chunk.appendAll(this.replayWindow.takeAll(), as))
1041
+ }
953
1042
  return core.succeed(as)
954
1043
  })
955
1044
  }
@@ -959,11 +1048,19 @@ class SubscriptionImpl<in out A> implements Queue.Dequeue<A> {
959
1048
  if (MutableRef.get(this.shutdownFlag)) {
960
1049
  return core.interrupt
961
1050
  }
1051
+ let replay: Chunk.Chunk<A> | undefined = undefined
1052
+ if (this.replayWindow.remaining >= max) {
1053
+ const as = this.replayWindow.takeN(max)
1054
+ return core.succeed(as)
1055
+ } else if (this.replayWindow.remaining > 0) {
1056
+ replay = this.replayWindow.takeAll()
1057
+ max = max - replay.length
1058
+ }
962
1059
  const as = MutableQueue.isEmpty(this.pollers)
963
1060
  ? unsafePollN(this.subscription, max)
964
1061
  : Chunk.empty()
965
1062
  this.strategy.unsafeOnPubSubEmptySpace(this.pubsub, this.subscribers)
966
- return core.succeed(as)
1063
+ return replay ? core.succeed(Chunk.appendAll(replay, as)) : core.succeed(as)
967
1064
  })
968
1065
  }
969
1066
 
@@ -1019,8 +1116,7 @@ class PubSubImpl<in out A> implements PubSub.PubSub<A> {
1019
1116
  readonly shutdownHook: Deferred.Deferred<void>,
1020
1117
  readonly shutdownFlag: MutableRef.MutableRef<boolean>,
1021
1118
  readonly strategy: PubSubStrategy<A>
1022
- ) {
1023
- }
1119
+ ) {}
1024
1120
 
1025
1121
  capacity(): number {
1026
1122
  return this.pubsub.capacity
@@ -1075,7 +1171,7 @@ class PubSubImpl<in out A> implements PubSub.PubSub<A> {
1075
1171
  return core.interrupt
1076
1172
  }
1077
1173
 
1078
- if ((this.pubsub as AtomicPubSub<unknown>).publish(value)) {
1174
+ if (this.pubsub.publish(value)) {
1079
1175
  this.strategy.unsafeCompleteSubscribers(this.pubsub, this.subscribers)
1080
1176
  return core.succeed(true)
1081
1177
  }
@@ -1179,9 +1275,7 @@ export const unsafeMakePubSub = <A>(
1179
1275
  shutdownHook: Deferred.Deferred<void>,
1180
1276
  shutdownFlag: MutableRef.MutableRef<boolean>,
1181
1277
  strategy: PubSubStrategy<A>
1182
- ): PubSub.PubSub<A> => {
1183
- return new PubSubImpl(pubsub, subscribers, scope, shutdownHook, shutdownFlag, strategy)
1184
- }
1278
+ ): PubSub.PubSub<A> => new PubSubImpl(pubsub, subscribers, scope, shutdownHook, shutdownFlag, strategy)
1185
1279
 
1186
1280
  /** @internal */
1187
1281
  const ensureCapacity = (capacity: number): void => {
@@ -1562,3 +1656,100 @@ const unsafeStrategyCompleteSubscribers = <A>(
1562
1656
  }
1563
1657
  }
1564
1658
  }
1659
+
1660
+ interface ReplayNode<A> {
1661
+ value: A | AbsentValue
1662
+ next: ReplayNode<A> | null
1663
+ }
1664
+
1665
+ class ReplayBuffer<A> {
1666
+ constructor(readonly capacity: number) {}
1667
+
1668
+ head: ReplayNode<A> = { value: AbsentValue, next: null }
1669
+ tail: ReplayNode<A> = this.head
1670
+ size = 0
1671
+ index = 0
1672
+
1673
+ slide() {
1674
+ this.index++
1675
+ }
1676
+ offer(a: A): void {
1677
+ this.tail.value = a
1678
+ this.tail.next = {
1679
+ value: AbsentValue,
1680
+ next: null
1681
+ }
1682
+ this.tail = this.tail.next
1683
+ if (this.size === this.capacity) {
1684
+ this.head = this.head.next!
1685
+ } else {
1686
+ this.size += 1
1687
+ }
1688
+ }
1689
+ offerAll(as: Iterable<A>): void {
1690
+ for (const a of as) {
1691
+ this.offer(a)
1692
+ }
1693
+ }
1694
+ }
1695
+
1696
+ interface ReplayWindow<A> {
1697
+ take(): A | undefined
1698
+ takeN(n: number): Chunk.Chunk<A>
1699
+ takeAll(): Chunk.Chunk<A>
1700
+ readonly remaining: number
1701
+ }
1702
+
1703
+ class ReplayWindowImpl<A> implements ReplayWindow<A> {
1704
+ head: ReplayNode<A>
1705
+ index: number
1706
+ remaining: number
1707
+ constructor(readonly buffer: ReplayBuffer<A>) {
1708
+ this.index = buffer.index
1709
+ this.remaining = buffer.size
1710
+ this.head = buffer.head
1711
+ }
1712
+ fastForward() {
1713
+ while (this.index < this.buffer.index) {
1714
+ this.head = this.head.next!
1715
+ this.index++
1716
+ }
1717
+ }
1718
+ take(): A | undefined {
1719
+ if (this.remaining === 0) {
1720
+ return undefined
1721
+ } else if (this.index < this.buffer.index) {
1722
+ this.fastForward()
1723
+ }
1724
+ this.remaining--
1725
+ const value = this.head.value
1726
+ this.head = this.head.next!
1727
+ return value as A
1728
+ }
1729
+ takeN(n: number): Chunk.Chunk<A> {
1730
+ if (this.remaining === 0) {
1731
+ return Chunk.empty()
1732
+ } else if (this.index < this.buffer.index) {
1733
+ this.fastForward()
1734
+ }
1735
+ const len = Math.min(n, this.remaining)
1736
+ const items = new Array(len)
1737
+ for (let i = 0; i < len; i++) {
1738
+ const value = this.head.value as A
1739
+ this.head = this.head.next!
1740
+ items[i] = value
1741
+ }
1742
+ this.remaining -= len
1743
+ return Chunk.unsafeFromArray(items)
1744
+ }
1745
+ takeAll(): Chunk.Chunk<A> {
1746
+ return this.takeN(this.remaining)
1747
+ }
1748
+ }
1749
+
1750
+ const emptyReplayWindow: ReplayWindow<never> = {
1751
+ remaining: 0,
1752
+ take: () => undefined,
1753
+ takeN: () => Chunk.empty(),
1754
+ takeAll: () => Chunk.empty()
1755
+ }
@@ -2,6 +2,7 @@ import * as Chunk from "../Chunk.js"
2
2
  import * as Context from "../Context.js"
3
3
  import type * as Effect from "../Effect.js"
4
4
  import { pipe } from "../Function.js"
5
+ import * as Hash from "../Hash.js"
5
6
  import type * as Random from "../Random.js"
6
7
  import * as PCGRandom from "../Utils.js"
7
8
  import * as core from "./core.js"
@@ -85,4 +86,4 @@ const swap = <A>(buffer: Array<A>, index1: number, index2: number): Array<A> =>
85
86
  return buffer
86
87
  }
87
88
 
88
- export const make = (seed: number): Random.Random => new RandomImpl(seed)
89
+ export const make = <A>(seed: A): Random.Random => new RandomImpl(Hash.hash(seed))