diff --git a/.changeset/ninety-trains-smash.md b/.changeset/ninety-trains-smash.md
new file mode 100644
index 0000000..9a8eb60
--- /dev/null
+++ b/.changeset/ninety-trains-smash.md
@@ -0,0 +1,5 @@
+---
+"@effect-rx/rx": patch
+---
+
+add Rx.debounce
diff --git a/docs/rx/Rx.ts.md b/docs/rx/Rx.ts.md
index 45872a4..668138c 100644
--- a/docs/rx/Rx.ts.md
+++ b/docs/rx/Rx.ts.md
@@ -15,12 +15,14 @@ Added in v1.0.0
- [batching](#batching)
- [batch](#batch)
- [combinators](#combinators)
+ - [debounce](#debounce)
- [initialValue](#initialvalue)
- [keepAlive](#keepalive)
- [map](#map)
- [mapResult](#mapresult)
- [refreshable](#refreshable)
- [setIdleTTL](#setidlettl)
+ - [transform](#transform)
- [withFallback](#withfallback)
- [withLabel](#withlabel)
- [constructors](#constructors)
@@ -94,6 +96,19 @@ Added in v1.0.0
# combinators
+## debounce
+
+**Signature**
+
+```ts
+export declare const debounce: {
+ (duration: Duration.DurationInput): >(self: A) => A
+ >(self: A, duration: Duration.DurationInput): A
+}
+```
+
+Added in v1.0.0
+
## initialValue
**Signature**
@@ -122,13 +137,15 @@ Added in v1.0.0
**Signature**
```ts
-export declare const map: (, B>(
- f: (_: Rx.Infer) => B
-) => (self: R) => [R] extends [Writable] ? Writable : Rx) &
- (, B>(
+export declare const map: {
+ , B>(
+ f: (_: Rx.Infer) => B
+ ): (self: R) => [R] extends [Writable] ? Writable : Rx
+ , B>(
self: R,
f: (_: Rx.Infer) => B
- ) => [R] extends [Writable] ? Writable : Rx)
+ ): [R] extends [Writable] ? Writable : Rx
+}
```
Added in v1.0.0
@@ -180,6 +197,24 @@ export declare const setIdleTTL: {
Added in v1.0.0
+## transform
+
+**Signature**
+
+```ts
+export declare const transform: {
+ , B>(
+ f: (get: Context) => B
+ ): (self: R) => [R] extends [Writable] ? Writable : Rx
+ , B>(
+ self: R,
+ f: (get: Context) => B
+ ): [R] extends [Writable] ? Writable : Rx
+}
+```
+
+Added in v1.0.0
+
## withFallback
**Signature**
diff --git a/packages/rx/src/Rx.ts b/packages/rx/src/Rx.ts
index 32de5ee..08a149d 100644
--- a/packages/rx/src/Rx.ts
+++ b/packages/rx/src/Rx.ts
@@ -1,7 +1,6 @@
/**
* @since 1.0.0
*/
-import type { Context } from "effect"
import { NoSuchElementException } from "effect/Cause"
import * as Chunk from "effect/Chunk"
import * as Duration from "effect/Duration"
@@ -1143,33 +1142,52 @@ export const initialValue: {
* @since 1.0.0
* @category combinators
*/
-export const map = dual<
+export const transform: {
, B>(
- f: (_: Rx.Infer) => B
- ) => (self: R) => [R] extends [Writable] ? Writable : Rx,
+ f: (get: Context) => B
+ ): (self: R) => [R] extends [Writable] ? Writable : Rx
, B>(
self: R,
- f: (_: Rx.Infer) => B
- ) => [R] extends [Writable] ? Writable : Rx
->(
+ f: (get: Context) => B
+ ): [R] extends [Writable] ? Writable : Rx
+} = dual(
2,
- ((self: Rx, f: (_: A) => B): Rx =>
+ ((self: Rx, f: (get: Context) => B): Rx =>
isWritable(self)
? writable(
- (get) => f(get(self)),
- self.write as any,
+ f,
+ function(ctx, value) {
+ ctx.set(self, value)
+ },
self.refresh ?? function(refresh) {
refresh(self)
}
)
: readable(
- (get) => f(get(self)),
+ f,
self.refresh ?? function(refresh) {
refresh(self)
}
)) as any
)
+/**
+ * @since 1.0.0
+ * @category combinators
+ */
+export const map: {
+ , B>(
+ f: (_: Rx.Infer) => B
+ ): (self: R) => [R] extends [Writable] ? Writable : Rx
+ , B>(
+ self: R,
+ f: (_: Rx.Infer) => B
+ ): [R] extends [Writable] ? Writable : Rx
+} = dual(
+ 2,
+ (self: Rx, f: (_: A) => B): Rx => transform(self, (get) => f(get(self)))
+)
+
/**
* @since 1.0.0
* @category combinators
@@ -1192,6 +1210,38 @@ export const mapResult: {
): [R] extends [Writable] ? Writable>>, RW>
: Rx>>> => map(self, Result.map(f)))
+/**
+ * @since 1.0.0
+ * @category combinators
+ */
+export const debounce: {
+ (duration: Duration.DurationInput): >(self: A) => A
+ >(self: A, duration: Duration.DurationInput): A
+} = dual(
+ 2,
+ (self: Rx, duration: Duration.DurationInput): Rx => {
+ const millis = Duration.toMillis(duration)
+ return transform(self, function(get) {
+ let timeout: number | undefined
+ let value = get.once(self)
+ function update() {
+ get.setSelfSync(get.once(self))
+ }
+ get.addFinalizer(function() {
+ if (timeout !== undefined) {
+ clearTimeout(timeout)
+ }
+ })
+ get.subscribe(self, function(val) {
+ value = val
+ if (timeout) return
+ setTimeout(update, millis)
+ })
+ return value
+ })
+ }
+)
+
/**
* @since 1.0.0
* @category batching