diff --git a/bun.lock b/bun.lock index 2be75ad..8d9cd35 100644 --- a/bun.lock +++ b/bun.lock @@ -17,6 +17,9 @@ "packages/effect-fc": { "name": "effect-fc", "version": "0.2.1", + "devDependencies": { + "@effect/platform-browser": "^0.74.0", + }, "peerDependencies": { "@types/react": "^19.2.0", "effect": "^3.19.0", @@ -655,6 +658,8 @@ "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "effect-fc/@effect/platform-browser": ["@effect/platform-browser@0.74.0", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/platform": "^0.94.0", "effect": "^3.19.13" } }, "sha512-PAgkg5L5cASQpScA0SZTSy543MVA4A9kmpVCjo2fCINLRpTeuCFAOQHgPmw8dKHnYS0yGs2TYn7AlrhhqQ5o3g=="], + "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], diff --git a/packages/effect-fc/package.json b/packages/effect-fc/package.json index 788ae0a..fcd03e0 100644 --- a/packages/effect-fc/package.json +++ b/packages/effect-fc/package.json @@ -37,6 +37,9 @@ "clean:dist": "rm -rf dist", "clean:modules": "rm -rf node_modules" }, + "devDependencies": { + "@effect/platform-browser": "^0.74.0" + }, "peerDependencies": { "@types/react": "^19.2.0", "effect": "^3.19.0", diff --git a/packages/effect-fc/src/Query.ts b/packages/effect-fc/src/Query.ts index 15d3efd..9aea2d7 100644 --- a/packages/effect-fc/src/Query.ts +++ b/packages/effect-fc/src/Query.ts @@ -16,6 +16,7 @@ extends Pipeable.Pipeable { readonly initialProgress: P readonly staleTime: Duration.DurationInput + readonly refreshOnWindowFocus: boolean readonly latestKey: Subscribable.Subscribable> readonly fiber: Subscribable.Subscribable>> @@ -47,6 +48,7 @@ extends Pipeable.Class() implements Query { readonly initialProgress: P, readonly staleTime: Duration.DurationInput, + readonly refreshOnWindowFocus: boolean, readonly latestKey: SubscriptionRef.SubscriptionRef>, readonly fiber: SubscriptionRef.SubscriptionRef>>, @@ -59,17 +61,22 @@ extends Pipeable.Class() implements Query { } get run(): Effect.Effect { - return Stream.runForEach(this.key, key => this.interrupt.pipe( - Effect.andThen(SubscriptionRef.set(this.latestKey, Option.some(key))), - Effect.andThen(this.latestFinalResult), - Effect.andThen(previous => this.startCached(key, Option.isSome(previous) - ? Result.willFetch(previous.value) as Result.Final - : Result.initial() - )), - Effect.andThen(sub => Effect.forkScoped(this.watch(key, sub))), - )).pipe( + return Effect.all([ + Stream.runForEach(this.key, key => this.fetchSubscribable(key)), + + Effect.promise(() => import("@effect/platform-browser")).pipe( + Effect.andThen(({ BrowserStream }) => this.refreshOnWindowFocus + ? Stream.runForEach( + BrowserStream.fromEventListenerWindow("focus"), + () => this.refreshSubscribable, + ) + : Effect.void + ), + Effect.catchAllDefect(() => Effect.void), + ), + ], { concurrency: "unbounded" }).pipe( + Effect.ignore, this.runSemaphore.withPermits(1), - Effect.provide(this.context), ) } @@ -269,6 +276,7 @@ export declare namespace make { readonly f: (key: NoInfer) => Effect.Effect>> readonly initialProgress?: P readonly staleTime?: Duration.DurationInput + readonly refreshOnWindowFocus?: boolean } } @@ -288,6 +296,7 @@ export const make = Effect.fnUntraced(function* ()), yield* SubscriptionRef.make(Option.none>()), diff --git a/packages/effect-fc/src/QueryClient.ts b/packages/effect-fc/src/QueryClient.ts index 3cc95b8..19517b7 100644 --- a/packages/effect-fc/src/QueryClient.ts +++ b/packages/effect-fc/src/QueryClient.ts @@ -11,6 +11,7 @@ export interface QueryClientService extends Pipeable.Pipeable { readonly cache: SubscriptionRef.SubscriptionRef> readonly gcTime: Duration.DurationInput readonly defaultStaleTime: Duration.DurationInput + readonly defaultRefreshOnWindowFocus: boolean } export class QueryClient extends Effect.Service()("@effect-fc/QueryClient/QueryClient", { @@ -26,6 +27,7 @@ implements QueryClientService { readonly cache: SubscriptionRef.SubscriptionRef>, readonly gcTime: Duration.DurationInput, readonly defaultStaleTime: Duration.DurationInput, + readonly defaultRefreshOnWindowFocus: boolean, readonly runSemaphore: Effect.Semaphore, ) { super() @@ -38,6 +40,7 @@ export declare namespace make { export interface Options { readonly gcTime?: Duration.DurationInput readonly defaultStaleTime?: Duration.DurationInput + readonly defaultRefreshOnWindowFocus?: boolean } } @@ -46,6 +49,7 @@ export const make = Effect.fnUntraced(function* (options: make.Options = {}): Ef yield* SubscriptionRef.make(HashMap.empty()), options.gcTime ?? "5 minutes", options.defaultStaleTime ?? "0 minutes", + options.defaultRefreshOnWindowFocus ?? true, yield* Effect.makeSemaphore(1), ) })