Hyperlinkv0.8.0-beta.28

RequestResolver

RequestResolver.persistedconsteffect/RequestResolver.ts:1165
<
  A extends Request.Request<
    any,
    Persistence.PersistenceError | Schema.SchemaError,
    any
  > &
    Persistable.Any
>(options: {
  readonly storeId: string
  readonly timeToLive?:
    | ((exit: Request.Result<A>, request: A) => Duration.Input)
    | undefined
  readonly staleWhileRevalidate?:
    | ((exit: Request.Result<A>, request: A) => boolean)
    | undefined
}): (
  self: RequestResolver<A>
) => Effect.Effect<
  RequestResolver<A>,
  never,
  Persistence.Persistence | Scope
>
<
  A extends Request.Request<
    any,
    Persistence.PersistenceError | Schema.SchemaError,
    any
  > &
    Persistable.Any
>(
  self: RequestResolver<A>,
  options: {
    readonly storeId: string
    readonly timeToLive?:
      | ((exit: Request.Result<A>, request: A) => Duration.Input)
      | undefined
    readonly staleWhileRevalidate?:
      | ((exit: Request.Result<A>, request: A) => boolean)
      | undefined
  }
): Effect.Effect<
  RequestResolver<A>,
  never,
  Persistence.Persistence | Scope
>

Wraps a request resolver with persistent storage for persistable requests.

When to use

Use to keep a RequestResolver interface while reusing completed Persistable request results through a Persistence store.

Details

Cached results are loaded from the configured persistence store before running the underlying resolver. Missing entries are resolved normally and written back to the store. Entries marked stale by staleWhileRevalidate receive the stored result and are also resolved again so the refreshed result can be written back to the store. Creating the persisted resolver requires Persistence.Persistence and Scope.

PersistencewithCacheasCache
export const persisted: {
  <A extends Request.Request<any, Persistence.PersistenceError | Schema.SchemaError, any> & Persistable.Any>(
    options: {
      readonly storeId: string
      readonly timeToLive?: ((exit: Request.Result<A>, request: A) => Duration.Input) | undefined
      readonly staleWhileRevalidate?: ((exit: Request.Result<A>, request: A) => boolean) | undefined
    }
  ): (self: RequestResolver<A>) => Effect.Effect<
    RequestResolver<A>,
    never,
    Persistence.Persistence | Scope
  >
  <
    A extends Request.Request<any, Persistence.PersistenceError | Schema.SchemaError, any> & Persistable.Any
  >(
    self: RequestResolver<A>,
    options: {
      readonly storeId: string
      readonly timeToLive?: ((exit: Request.Result<A>, request: A) => Duration.Input) | undefined
      readonly staleWhileRevalidate?: ((exit: Request.Result<A>, request: A) => boolean) | undefined
    }
  ): Effect.Effect<
    RequestResolver<A>,
    never,
    Persistence.Persistence | Scope
  >
} = dual(
  2,
  Effect.fnUntraced(function*<
    A extends Request.Request<any, Persistence.PersistenceError | Schema.SchemaError, any> & Persistable.Any
  >(
    self: RequestResolver<A>,
    options: {
      readonly storeId: string
      readonly timeToLive?: ((exit: Request.Result<A>, request: A) => Duration.Input) | undefined
      readonly staleWhileRevalidate?: ((exit: Request.Result<A>, request: A) => boolean) | undefined
    }
  ) {
    const store = yield* (yield* Persistence.Persistence).make(options as any)
    return makeWith<A>({
      ...self,
      runAll: Effect.fnUntraced(function*(entries, key) {
        const results = yield* (store.getMany(Iterable.map(entries, (_) => _.request)).pipe(
          Effect.provideContext(entries[0].context)
        ) as Effect.Effect<
          Array<Exit.Exit<unknown, unknown> | undefined>,
          Request.Error<A>
        >)
        const leftover: Array<Request.Entry<A>> = []
        const toPersist = new Map<A, Request.Result<A>>()
        for (let i = 0; i < results.length; i++) {
          const entry = entries[i]
          const exit = results[i]
          if (
            exit === undefined ||
            (options.staleWhileRevalidate && options.staleWhileRevalidate(exit as any, entry.request))
          ) {
            const prevComplete = entry.completeUnsafe
            entry.completeUnsafe = function(exit) {
              toPersist.set(entry.request, exit as any)
              prevComplete(exit)
            }
            leftover.push(entry)
            if (exit === undefined) continue
          }
          entry.completeUnsafe(exit as any)
        }
        if (!Arr.isArrayNonEmpty(leftover)) {
          return
        }
        yield* Effect.catchCause(self.runAll(leftover, key), (cause) => {
          for (let i = 0; i < leftover.length; i++) {
            const entry = leftover[i]
            if (!toPersist.has(entry.request)) continue
            entry.completeUnsafe(Exit.failCause(cause) as any)
          }
          return Effect.void
        })
        yield* (store.setMany(toPersist).pipe(
          Effect.provideContext(entries[0].context)
        ) as Effect.Effect<void, Request.Error<A>>)
      })
    })
  })
)