226 lines
6.3 KiB
TypeScript
226 lines
6.3 KiB
TypeScript
/**
|
|
* Reference-counted handles for sharing one scoped resource across many scoped
|
|
* users. An `RcRef<A, E>` acquires the resource lazily the first time `get`
|
|
* needs it, reuses that value while it is borrowed or kept idle, and finalizes
|
|
* it when the final borrowing scope closes unless an idle timeout keeps it
|
|
* available. The module also provides `invalidate` for forcing the next `get`
|
|
* to acquire a fresh resource.
|
|
*
|
|
* @since 3.5.0
|
|
*/
|
|
import type * as Duration from "./Duration.ts"
|
|
import type * as Effect from "./Effect.ts"
|
|
import * as internal from "./internal/rcRef.ts"
|
|
import type { Pipeable } from "./Pipeable.ts"
|
|
import type { Scope } from "./Scope.ts"
|
|
import type * as Types from "./Types.ts"
|
|
|
|
const TypeId = "~effect/RcRef"
|
|
|
|
/**
|
|
* A reference counted reference that manages resource lifecycle.
|
|
*
|
|
* **When to use**
|
|
*
|
|
* Use to share a scoped resource across active users with reference-counted
|
|
* acquisition and release.
|
|
*
|
|
* **Details**
|
|
*
|
|
* An RcRef wraps a resource that can be acquired and released multiple times.
|
|
* The resource is lazily acquired on the first call to `get` and automatically
|
|
* released when the last reference is released.
|
|
*
|
|
* **Example** (Sharing a lazily acquired resource)
|
|
*
|
|
* ```ts
|
|
* import { Effect, RcRef } from "effect"
|
|
*
|
|
* // Create an RcRef for a database connection
|
|
* const createConnectionRef = (connectionString: string) =>
|
|
* RcRef.make({
|
|
* acquire: Effect.acquireRelease(
|
|
* Effect.succeed(`Connected to ${connectionString}`),
|
|
* (connection) => Effect.log(`Closing connection: ${connection}`)
|
|
* )
|
|
* })
|
|
*
|
|
* // Use the RcRef in multiple operations
|
|
* const program = Effect.gen(function*() {
|
|
* const connectionRef = yield* createConnectionRef("postgres://localhost")
|
|
*
|
|
* // Multiple gets will share the same connection
|
|
* const connection1 = yield* RcRef.get(connectionRef)
|
|
* const connection2 = yield* RcRef.get(connectionRef)
|
|
*
|
|
* return [connection1, connection2]
|
|
* })
|
|
* ```
|
|
*
|
|
* @category models
|
|
* @since 3.5.0
|
|
*/
|
|
export interface RcRef<out A, out E = never> extends Pipeable {
|
|
readonly [TypeId]: RcRef.Variance<A, E>
|
|
}
|
|
|
|
/**
|
|
* Namespace containing type-level members associated with `RcRef`.
|
|
*
|
|
* **Example** (Referencing namespace types)
|
|
*
|
|
* ```ts
|
|
* import type { RcRef } from "effect"
|
|
*
|
|
* // Use RcRef namespace types
|
|
* type MyRcRef = RcRef.RcRef<string, Error>
|
|
* type MyVariance = RcRef.RcRef.Variance<string, Error>
|
|
* ```
|
|
*
|
|
* @since 3.5.0
|
|
*/
|
|
export declare namespace RcRef {
|
|
/**
|
|
* Type-level variance marker for `RcRef`.
|
|
*
|
|
* **When to use**
|
|
*
|
|
* Use to carry the value and error type parameters for `RcRef` in Effect's
|
|
* type machinery.
|
|
*
|
|
* **Details**
|
|
*
|
|
* This interface records the covariant value and error types carried by an
|
|
* `RcRef`. It is used by Effect's type machinery and is not normally
|
|
* referenced directly by users.
|
|
*
|
|
* @category models
|
|
* @since 3.5.0
|
|
*/
|
|
export interface Variance<A, E> {
|
|
readonly _A: Types.Covariant<A>
|
|
readonly _E: Types.Covariant<E>
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an `RcRef` from an acquire effect.
|
|
*
|
|
* **When to use**
|
|
*
|
|
* Use to create a lazily acquired, reference-counted resource from an acquire
|
|
* effect.
|
|
*
|
|
* **Details**
|
|
*
|
|
* The resource is acquired lazily on the first `get` and shared by subsequent
|
|
* gets while it remains cached. Each `get` adds a reference to the current
|
|
* `Scope`. When the last reference is released, the resource is closed
|
|
* immediately by default, or after `idleTimeToLive` when that option is
|
|
* provided.
|
|
*
|
|
* **Example** (Creating a reference-counted resource)
|
|
*
|
|
* ```ts
|
|
* import { Effect, RcRef } from "effect"
|
|
*
|
|
* Effect.gen(function*() {
|
|
* const ref = yield* RcRef.make({
|
|
* acquire: Effect.acquireRelease(
|
|
* Effect.succeed("foo"),
|
|
* () => Effect.log("release foo")
|
|
* )
|
|
* })
|
|
*
|
|
* // will only acquire the resource once, and release it
|
|
* // when the scope is closed
|
|
* yield* RcRef.get(ref).pipe(
|
|
* Effect.andThen(RcRef.get(ref)),
|
|
* Effect.scoped
|
|
* )
|
|
* })
|
|
* ```
|
|
*
|
|
* @category constructors
|
|
* @since 3.5.0
|
|
*/
|
|
export const make: <A, E, R>(
|
|
options: {
|
|
readonly acquire: Effect.Effect<A, E, R>
|
|
/**
|
|
* When the reference count reaches zero, the resource will be released
|
|
* after this duration.
|
|
*/
|
|
readonly idleTimeToLive?: Duration.Input | undefined
|
|
}
|
|
) => Effect.Effect<RcRef<A, E>, never, R | Scope> = internal.make
|
|
|
|
/**
|
|
* Gets the value from an `RcRef`, acquiring it first if needed.
|
|
*
|
|
* **When to use**
|
|
*
|
|
* Use to borrow the current resource within a `Scope`, acquiring it first if
|
|
* necessary.
|
|
*
|
|
* **Details**
|
|
*
|
|
* The reference count is incremented for the current `Scope`, and a release
|
|
* finalizer is added to that scope. When the current scope closes, the
|
|
* reference is released; the resource is closed when the final reference is
|
|
* released, subject to any configured idle time-to-live.
|
|
*
|
|
* **Example** (Sharing one acquired value)
|
|
*
|
|
* ```ts
|
|
* import { Effect, RcRef } from "effect"
|
|
*
|
|
* const program = Effect.gen(function*() {
|
|
* // Create an RcRef with a resource
|
|
* const ref = yield* RcRef.make({
|
|
* acquire: Effect.acquireRelease(
|
|
* Effect.succeed("shared resource"),
|
|
* (resource) => Effect.log(`Releasing ${resource}`)
|
|
* )
|
|
* })
|
|
*
|
|
* // Get the value from the RcRef
|
|
* const value1 = yield* RcRef.get(ref)
|
|
* const value2 = yield* RcRef.get(ref)
|
|
*
|
|
* // Both values are the same instance
|
|
* console.log(value1 === value2) // true
|
|
*
|
|
* return value1
|
|
* })
|
|
* ```
|
|
*
|
|
* @category combinators
|
|
* @since 3.5.0
|
|
*/
|
|
export const get: <A, E>(self: RcRef<A, E>) => Effect.Effect<A, E, Scope> = internal.get
|
|
|
|
/**
|
|
* Invalidates the currently cached resource, if one has been acquired.
|
|
*
|
|
* **When to use**
|
|
*
|
|
* Use to force future `RcRef.get` calls to acquire a fresh resource when the
|
|
* currently cached resource should no longer be reused.
|
|
*
|
|
* **Details**
|
|
*
|
|
* After invalidation, the next `get` acquires a fresh resource.
|
|
*
|
|
* **Gotchas**
|
|
*
|
|
* Invalidation does not revoke resources already borrowed by active scopes;
|
|
* those remain usable until their scopes close.
|
|
*
|
|
* @see {@link get} for acquiring the current cached resource or the fresh resource after invalidation
|
|
*
|
|
* @category combinators
|
|
* @since 3.19.6
|
|
*/
|
|
export const invalidate: <A, E>(self: RcRef<A, E>) => Effect.Effect<void> = internal.invalidate
|