From 7f57e034e42eb4b4d3ef9dd55b718ecf56ee0fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 23 Mar 2026 01:05:34 +0100 Subject: [PATCH] Add ProxyRef --- packages/effect-fc/src/ProxyRef.ts | 67 ++++++++++++++++++++++++++++++ packages/effect-fc/src/index.ts | 1 + 2 files changed, 68 insertions(+) diff --git a/packages/effect-fc/src/ProxyRef.ts b/packages/effect-fc/src/ProxyRef.ts index e69de29..9fe7ea9 100644 --- a/packages/effect-fc/src/ProxyRef.ts +++ b/packages/effect-fc/src/ProxyRef.ts @@ -0,0 +1,67 @@ +import { Effect, Predicate, Stream, Subscribable, SubscriptionRef, type Types } from "effect" +import * as Writable from "./Writable.js" + + +export const ProxyRefTypeId: unique symbol = Symbol.for("@effect-fc/ProxyRef/ProxyRef") +export type ProxyRefTypeId = typeof ProxyRefTypeId + +export interface ProxyRef + extends ProxyRef.Variance, Subscribable.Subscribable, Writable.Writable {} + +export declare namespace ProxyRef { + export interface Variance { + readonly [ProxyRefTypeId]: { + readonly _A: Types.Invariant + readonly _EG: Types.Invariant + readonly _RG: Types.Invariant + readonly _EW: Types.Invariant + readonly _RW: Types.Invariant + } + } +} + +export const ProxyRefPrototype: Writable.WritablePrototype = Object.freeze({ + [ProxyRefTypeId]: ProxyRefTypeId, + ...Writable.WritablePrototype, +} as const) + + +export const isProxyRef = (u: unknown): u is ProxyRef => Predicate.hasProperty(u, ProxyRefTypeId) + + +/** + * Construct a `ProxyRef` from explicit get/changes/set implementations. + */ +export const makeFromGetSet = (options: { + readonly get: Effect.Effect + readonly changes: Stream.Stream + readonly set: (value: A) => Effect.Effect +}): ProxyRef => Object.setPrototypeOf({ + [Subscribable.TypeId]: Subscribable.TypeId, + get: options.get, + changes: options.changes, + modify: (f: (a: A) => readonly [B, A]) => Effect.flatMap(options.get, a => { + const [b, next] = f(a) + return Effect.map(options.set(next), () => b) + }), +}, ProxyRefPrototype) as any + + +/** + * Create a `ProxyRef` that directly proxies an `effect` SubscriptionRef. + */ +export const makeFromSubscriptionRef = (ref: SubscriptionRef.SubscriptionRef): ProxyRef => + makeFromGetSet({ + get: ref, + changes: ref.changes, + set: (v: A) => SubscriptionRef.set(ref, v), + }) + + +export const unwrap = ( + effect: Effect.Effect, E1, R1>, +): ProxyRef => makeFromGetSet({ + get: Effect.flatMap(effect, p => p.get), + changes: Stream.unwrap(Effect.map(effect, p => p.changes)), + set: (v: A) => Effect.flatMap(effect, p => p.modify(() => [undefined as void as any, v]) as any), +}) as any diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts index 4632d68..681c3e9 100644 --- a/packages/effect-fc/src/index.ts +++ b/packages/effect-fc/src/index.ts @@ -5,6 +5,7 @@ export * as Form from "./Form.js" export * as Memoized from "./Memoized.js" export * as Mutation from "./Mutation.js" export * as PropertyPath from "./PropertyPath.js" +export * as ProxyRef from "./ProxyRef.js" export * as PubSub from "./PubSub.js" export * as Query from "./Query.js" export * as QueryClient from "./QueryClient.js"