diff --git a/src/CachingHttpClient.ts b/src/CachingHttpClient.ts index 82fe21158..fdf2553be 100644 --- a/src/CachingHttpClient.ts +++ b/src/CachingHttpClient.ts @@ -1,11 +1,4 @@ -import { - Headers, - HttpClient, - HttpClientResponse, - UrlParams, - type HttpClientError, - type HttpClientRequest, -} from '@effect/platform' +import { HttpClient, type HttpClientError, type HttpClientRequest, type HttpClientResponse } from '@effect/platform' import { diff } from 'deep-object-diff' import { DateTime, Effect, pipe, type Scope } from 'effect' import * as HttpCache from './HttpCache.js' @@ -24,8 +17,7 @@ export const CachingHttpClient: Effect.Effect< Effect.gen(function* () { const timestamp = yield* DateTime.now const req = yield* request - const key = keyForRequest(req) - const response = yield* cache.get(key) + const response = yield* cache.get(req) if (response) { if (DateTime.lessThan(timestamp, response.staleAt)) { @@ -43,13 +35,7 @@ export const CachingHttpClient: Effect.Effect< }), ) } - return HttpClientResponse.fromWeb( - req, - new Response(response.response.body, { - status: response.response.status, - headers: Headers.fromInput(response.response.headers), - }), - ) + return response.response } else { yield* Effect.logDebug('Cache miss') } @@ -60,7 +46,7 @@ export const CachingHttpClient: Effect.Effect< Effect.tap(response => pipe(cache.set(response, DateTime.addDuration(timestamp, '10 seconds')), Effect.ignore)), Effect.tap(response => Effect.gen(function* () { - const cachedValue = yield* cache.get(key) + const cachedValue = yield* cache.get(req) if (cachedValue === undefined) { return yield* Effect.logError('cache entry not found') } @@ -72,7 +58,7 @@ export const CachingHttpClient: Effect.Effect< const cachedResponse = { status: cachedValue.response.status, headers: cachedValue.response.headers, - body: cachedValue.response.body, + body: yield* cachedValue.response.text, } const difference = diff(origResponse, cachedResponse) function replacer(_: unknown, value: unknown) { @@ -93,10 +79,3 @@ export const CachingHttpClient: Effect.Effect< return HttpClient.makeWith(cachingBehaviour, Effect.succeed) }) - -const keyForRequest = (request: HttpClientRequest.HttpClientRequest): HttpCache.CacheKey => { - const url = new URL(request.url) - url.search = UrlParams.toString(request.urlParams) - - return url -} diff --git a/src/HttpCache.ts b/src/HttpCache.ts index 1f8206941..32fd9c5c1 100644 --- a/src/HttpCache.ts +++ b/src/HttpCache.ts @@ -1,5 +1,5 @@ -import { Headers, UrlParams, type HttpClientRequest, type HttpClientResponse } from '@effect/platform' -import { Context, Effect, Layer, pipe, Schema, type DateTime } from 'effect' +import { Headers, HttpClientResponse, UrlParams, type HttpClientRequest } from '@effect/platform' +import { Context, Effect, Layer, Option, pipe, Schema, type DateTime } from 'effect' interface CacheValue { staleAt: DateTime.DateTime @@ -19,7 +19,9 @@ const StoredResponseSchema = Schema.Struct({ export class HttpCache extends Context.Tag('HttpCache')< HttpCache, { - get: (key: CacheKey) => Effect.Effect + get: ( + request: HttpClientRequest.HttpClientRequest, + ) => Effect.Effect<{ staleAt: DateTime.DateTime; response: HttpClientResponse.HttpClientResponse } | undefined> set: (response: HttpClientResponse.HttpClientResponse, staleAt: DateTime.DateTime) => Effect.Effect delete: (key: CacheKey) => Effect.Effect } @@ -28,7 +30,23 @@ export class HttpCache extends Context.Tag('HttpCache')< export const layer = Layer.sync(HttpCache, () => { const cache = new Map() return { - get: key => Effect.succeed(cache.get(key.href)), + get: request => + pipe( + cache.get(keyForRequest(request).href), + Option.fromNullable, + Option.map(({ staleAt, response }) => ({ + staleAt, + response: HttpClientResponse.fromWeb( + request, + new Response(response.body, { + status: response.status, + headers: Headers.fromInput(response.headers), + }), + ), + })), + Option.getOrUndefined, + Effect.succeed, + ), set: (response, staleAt) => pipe( Effect.gen(function* () {