1/**
2 * Reference-counted handles for sharing one scoped resource across many scoped
3 * users. An `RcRef<A, E>` acquires the resource lazily the first time `get`
4 * needs it, reuses that value while it is borrowed or kept idle, and finalizes
5 * it when the final borrowing scope closes unless an idle timeout keeps it
6 * available. The module also provides `invalidate` for forcing the next `get`
7 * to acquire a fresh resource.
8 *
9 * @since 3.5.0
10 */
11import type * as Duration from "./Duration.ts"
12import type * as Effect from "./Effect.ts"
13import * as internal from "./internal/rcRef.ts"
14import type { Pipeable } from "./Pipeable.ts"
15import type { Scope } from "./Scope.ts"
16import type * as Types from "./Types.ts"
17
18const TypeId = "~effect/RcRef"
19
20/**
21 * A reference counted reference that manages resource lifecycle.
22 *
23 * **When to use**
24 *
25 * Use to share a scoped resource across active users with reference-counted
26 * acquisition and release.
27 *
28 * **Details**
29 *
30 * An RcRef wraps a resource that can be acquired and released multiple times.
31 * The resource is lazily acquired on the first call to `get` and automatically
32 * released when the last reference is released.
33 *
34 * **Example** (Sharing a lazily acquired resource)
35 *
36 * ```ts import.meta.vitest
37 * import { Effect, RcRef } from "effect"
38 *
39 * const events: Array<string> = []
40 *
41 * // Create an RcRef for a database connection
42 * const createConnectionRef = (connectionString: string) =>
43 * RcRef.make({
44 * acquire: Effect.acquireRelease(
45 * Effect.succeed(`Connected to ${connectionString}`),
46 * (connection) => Effect.sync(() => events.push(`closed ${connection}`))
47 * )
48 * })
49 *
50 * // Use the RcRef in multiple operations
51 * const program = Effect.gen(function*() {
52 * const connectionRef = yield* createConnectionRef("postgres://localhost")
53 *
54 * // Multiple gets will share the same connection
55 * const connection1 = yield* RcRef.get(connectionRef)
56 * const connection2 = yield* RcRef.get(connectionRef)
57 *
58 * return [connection1 === connection2, events] as const
59 * })
60 *
61 * await Effect.runPromise(Effect.scoped(program)) // => [true, ["closed Connected to postgres://localhost"]]
62 * ```
63 *
64 * @category models
65 * @since 3.5.0
66 */
67export interface RcRef<out A, out E = never> extends Pipeable {
68 readonly [TypeId]: RcRef.Variance<A, E>
69}
70
71/**
72 * Namespace containing type-level members associated with `RcRef`.
73 *
74 * **Example** (Referencing namespace types)
75 *
76 * ```ts import.meta.vitest
77 * import type { RcRef } from "effect"
78 *
79 * // Use RcRef namespace types
80 * type MyRcRef = RcRef.RcRef<string, Error>
81 * type MyVariance = RcRef.RcRef.Variance<string, Error>
82 *
83 * ```
84 *
85 * @since 3.5.0
86 */
87export declare namespace RcRef {
88 /**
89 * Type-level variance marker for `RcRef`.
90 *
91 * **When to use**
92 *
93 * Use to carry the value and error type parameters for `RcRef` in Effect's
94 * type machinery.
95 *
96 * **Details**
97 *
98 * This interface records the covariant value and error types carried by an
99 * `RcRef`. It is used by Effect's type machinery and is not normally
100 * referenced directly by users.
101 *
102 * @category models
103 * @since 3.5.0
104 */
105 export interface Variance<A, E> {
106 readonly _A: Types.Covariant<A>
107 readonly _E: Types.Covariant<E>
108 }
109}
110
111/**
112 * Creates an `RcRef` from an acquire effect.
113 *
114 * **When to use**
115 *
116 * Use to create a lazily acquired, reference-counted resource from an acquire
117 * effect.
118 *
119 * **Details**
120 *
121 * The resource is acquired lazily on the first `get` and shared by subsequent
122 * gets while it remains cached. Each `get` adds a reference to the current
123 * `Scope`. When the last reference is released, the resource is closed
124 * immediately by default, or after `idleTimeToLive` when that option is
125 * provided.
126 *
127 * **Example** (Creating a reference-counted resource)
128 *
129 * ```ts import.meta.vitest
130 * import { Effect, RcRef } from "effect"
131 *
132 * const events: Array<string> = []
133 *
134 * const program = Effect.gen(function*() {
135 * const ref = yield* RcRef.make({
136 * acquire: Effect.acquireRelease(
137 * Effect.succeed("foo"),
138 * () => Effect.sync(() => events.push("released foo"))
139 * )
140 * })
141 *
142 * // will only acquire the resource once, and release it
143 * // when the scope is closed
144 * yield* RcRef.get(ref).pipe(
145 * Effect.andThen(RcRef.get(ref)),
146 * Effect.scoped
147 * )
148 * })
149 *
150 * await Effect.runPromise(Effect.scoped(program))
151 * events // => ["released foo"]
152 * ```
153 *
154 * @category constructors
155 * @since 3.5.0
156 */
157export const make: <A, E, R>(
158 options: {
159 readonly acquire: Effect.Effect<A, E, R>
160 /**
161 * How long to keep an idle resource after its last reference is released.
162 *
163 * If the resource has not been invalidated, finite durations, including `0`,
164 * schedule release in a forked fiber that the scope releasing the last
165 * reference does not await. An infinite duration keeps the idle resource
166 * until invalidation or the RcRef's scope closes.
167 *
168 * If this option is omitted or the resource has been invalidated with
169 * `RcRef.invalidate`, the scope releasing the last reference releases the
170 * resource and awaits completion.
171 */
172 readonly idleTimeToLive?: Duration.Input | undefined
173 }
174) => Effect.Effect<RcRef<A, E>, never, R | Scope> = internal.make
175
176/**
177 * Gets the value from an `RcRef`, acquiring it first if needed.
178 *
179 * **When to use**
180 *
181 * Use to borrow the current resource within a `Scope`, acquiring it first if
182 * necessary.
183 *
184 * **Details**
185 *
186 * The reference count is incremented for the current `Scope`, and a release
187 * finalizer is added to that scope. When the current scope closes, the
188 * reference is released; the resource is closed when the final reference is
189 * released, subject to any configured idle time-to-live.
190 *
191 * **Example** (Sharing one acquired value)
192 *
193 * ```ts import.meta.vitest
194 * import { Effect, RcRef } from "effect"
195 *
196 * const events: Array<string> = []
197 *
198 * const program = Effect.gen(function*() {
199 * // Create an RcRef with a resource
200 * const ref = yield* RcRef.make({
201 * acquire: Effect.acquireRelease(
202 * Effect.succeed("shared resource"),
203 * (resource) => Effect.sync(() => events.push(`released ${resource}`))
204 * )
205 * })
206 *
207 * // Get the value from the RcRef
208 * const value1 = yield* RcRef.get(ref)
209 * const value2 = yield* RcRef.get(ref)
210 *
211 * return [value1 === value2, events] as const
212 * })
213 *
214 * await Effect.runPromise(Effect.scoped(program)) // => [true, ["released shared resource"]]
215 * ```
216 *
217 * @category combinators
218 * @since 3.5.0
219 */
220export const get: <A, E>(self: RcRef<A, E>) => Effect.Effect<A, E, Scope> = internal.get
221
222/**
223 * Invalidates the currently cached resource, if one has been acquired.
224 *
225 * **When to use**
226 *
227 * Use to force future `RcRef.get` calls to acquire a fresh resource when the
228 * currently cached resource should no longer be reused.
229 *
230 * **Details**
231 *
232 * After invalidation, the next `get` acquires a fresh resource.
233 *
234 * **Gotchas**
235 *
236 * Invalidation does not revoke resources already borrowed by active scopes;
237 * those remain usable until their scopes close.
238 *
239 * @see {@link get} for acquiring the current cached resource or the fresh resource after invalidation
240 *
241 * @category combinators
242 * @since 3.19.6
243 */
244export const invalidate: <A, E>(self: RcRef<A, E>) => Effect.Effect<void> = internal.invalidate