Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c150864292 | |||
| e412943e64 |
@@ -40,6 +40,13 @@ jobs:
|
|||||||
access: public
|
access: public
|
||||||
token: ${{ secrets.NPM_TOKEN }}
|
token: ${{ secrets.NPM_TOKEN }}
|
||||||
registry: https://registry.npmjs.org
|
registry: https://registry.npmjs.org
|
||||||
|
- name: Publish @effect-view/vite-plugin
|
||||||
|
uses: JS-DevTools/npm-publish@v4
|
||||||
|
with:
|
||||||
|
package: packages/vite-plugin
|
||||||
|
access: public
|
||||||
|
token: ${{ secrets.NPM_TOKEN }}
|
||||||
|
registry: https://registry.npmjs.org
|
||||||
- name: Publish effect-fc
|
- name: Publish effect-fc
|
||||||
uses: JS-DevTools/npm-publish@v4
|
uses: JS-DevTools/npm-publish@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: "24"
|
node-version: "22"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
FROM oven/bun:1.3.12-debian@sha256:1b709c9dd883fc1af38c210f7ea5222c552a8d470ea73efbd4b8fcfee798a64b AS bun
|
FROM oven/bun:1.3.14-debian@sha256:9dba1a1b43ce28c9d7931bfc4eb00feb63b0114720a0277a8f939ae4dfc9db6f AS bun
|
||||||
|
|
||||||
FROM node:24.18.0-trixie-slim@sha256:ae91dcc111a68c9d2d81ff2a17bda61be126426176fde6fe7d08ab13b7f50573
|
FROM node:22.23.2-trixie-slim@sha256:517aa41d78545cb1b8c67b13655b4c13ede1ee9df1da8aab54cd7434aefbcaf8
|
||||||
COPY --from=bun /usr/local/bin/bun /usr/local/bin/bunx /usr/local/bin/
|
COPY --from=bun /usr/local/bin/bun /usr/local/bin/bunx /usr/local/bin/
|
||||||
COPY . /app
|
COPY . /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
@@ -248,6 +248,33 @@ workflows that need to wait for the final success or failure state.
|
|||||||
Invalidating does not itself refetch. Follow it with `refreshView`, change the
|
Invalidating does not itself refetch. Follow it with `refreshView`, change the
|
||||||
key, or allow a later fetch to repopulate the cache.
|
key, or allow a later fetch to repopulate the cache.
|
||||||
|
|
||||||
|
### Refresh on an interval
|
||||||
|
|
||||||
|
Refresh every five minutes, starting after five minutes:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { Schedule } from "effect"
|
||||||
|
|
||||||
|
yield* query.pipe(
|
||||||
|
Query.withScheduledRefresh(Schedule.spaced("5 minutes")),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Limit the number of refreshes:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
yield* query.pipe(
|
||||||
|
Query.withScheduledRefresh(
|
||||||
|
Schedule.spaced("5 minutes").pipe(
|
||||||
|
Schedule.upTo({ times: 3 }),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
The refresh fiber stops with the surrounding scope. The Effect returns the
|
||||||
|
original query. Cache and `staleTime` rules still apply.
|
||||||
|
|
||||||
## Staleness and cache lifetime
|
## Staleness and cache lifetime
|
||||||
|
|
||||||
`staleTime` controls how long a successful result can satisfy a fetch without
|
`staleTime` controls how long a successful result can satisfy a fetch without
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Effect, type Scope, Stream } from "effect"
|
import { Effect, Schedule, type Scope, Stream } from "effect"
|
||||||
|
import { TestClock } from "effect/testing"
|
||||||
import { AsyncResult } from "effect/unstable/reactivity"
|
import { AsyncResult } from "effect/unstable/reactivity"
|
||||||
import { describe, expect, it } from "vitest"
|
import { describe, expect, it } from "vitest"
|
||||||
import * as Query from "./Query.js"
|
import * as Query from "./Query.js"
|
||||||
@@ -79,6 +80,49 @@ describe("Query", () => {
|
|||||||
expect(expectSuccessValue(result[1])).toBe("value:1:2")
|
expect(expectSuccessValue(result[1])).toBe("value:1:2")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("withScheduledRefresh lets the Schedule control the first refresh", async () => {
|
||||||
|
let calls = 0
|
||||||
|
const key = staticKey<readonly [number]>([1])
|
||||||
|
|
||||||
|
const result = await runQueryTest(Effect.gen(function*() {
|
||||||
|
const query = yield* Query.make({
|
||||||
|
key,
|
||||||
|
f: () => Effect.sync(() => {
|
||||||
|
calls += 1
|
||||||
|
return calls
|
||||||
|
}),
|
||||||
|
staleTime: "0 millis",
|
||||||
|
})
|
||||||
|
|
||||||
|
yield* query.fetch([1])
|
||||||
|
const returnedQuery = yield* query.pipe(Query.withScheduledRefresh(
|
||||||
|
Schedule.spaced("1 second").pipe(
|
||||||
|
Schedule.upTo({ times: 1 }),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
|
||||||
|
yield* TestClock.adjust("999 millis")
|
||||||
|
const beforeInterval = calls
|
||||||
|
|
||||||
|
yield* TestClock.adjust("1 millis")
|
||||||
|
const afterFirstInterval = calls
|
||||||
|
|
||||||
|
return {
|
||||||
|
returnsQuery: returnedQuery === query,
|
||||||
|
beforeInterval,
|
||||||
|
afterFirstInterval,
|
||||||
|
}
|
||||||
|
}).pipe(
|
||||||
|
Effect.provide(TestClock.layer()),
|
||||||
|
))
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
returnsQuery: true,
|
||||||
|
beforeInterval: 1,
|
||||||
|
afterFirstInterval: 2,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("invalidateCacheEntry forces the next fetch for that key to rerun", async () => {
|
it("invalidateCacheEntry forces the next fetch for that key to rerun", async () => {
|
||||||
let calls = 0
|
let calls = 0
|
||||||
const key = staticKey<readonly [number]>([1])
|
const key = staticKey<readonly [number]>([1])
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Cause, type Context, Duration, Effect, Equal, type Equivalence, Exit, Fiber, Option, Pipeable, Predicate, PubSub, Ref, type Scope, Semaphore, Stream, SubscriptionRef } from "effect"
|
import { Cause, type Context, Duration, Effect, Equal, type Equivalence, Exit, Fiber, Function, Option, Pipeable, Predicate, PubSub, Ref, type Schedule, type Scope, Semaphore, Stream, SubscriptionRef } from "effect"
|
||||||
import { AsyncResult } from "effect/unstable/reactivity"
|
import { AsyncResult } from "effect/unstable/reactivity"
|
||||||
import * as Lens from "./Lens.js"
|
import * as Lens from "./Lens.js"
|
||||||
import * as QueryClient from "./QueryClient.js"
|
import * as QueryClient from "./QueryClient.js"
|
||||||
@@ -421,6 +421,43 @@ export const service = <K, A, E = never, R = never>(
|
|||||||
query => Effect.forkScoped(query.run),
|
query => Effect.forkScoped(query.run),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the Query on a schedule and returns it.
|
||||||
|
*
|
||||||
|
* @example Refresh every five minutes, starting after five minutes
|
||||||
|
* ```ts
|
||||||
|
* yield* query.pipe(
|
||||||
|
* Query.withScheduledRefresh(Schedule.spaced("5 minutes")),
|
||||||
|
* )
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example Refresh at most three times
|
||||||
|
* ```ts
|
||||||
|
* yield* query.pipe(
|
||||||
|
* Query.withScheduledRefresh(
|
||||||
|
* Schedule.spaced("5 minutes").pipe(Schedule.upTo({ times: 3 })),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const withScheduledRefresh: {
|
||||||
|
<Output, Error, Env>(
|
||||||
|
schedule: Schedule.Schedule<Output, unknown, Error, Env>,
|
||||||
|
): <K, A, E, R>(
|
||||||
|
self: Query<K, A, E, R>,
|
||||||
|
) => Effect.Effect<Query<K, A, E, R>, never, Scope.Scope | Env>
|
||||||
|
<K, A, E, R, Output, Error, Env>(
|
||||||
|
self: Query<K, A, E, R>,
|
||||||
|
schedule: Schedule.Schedule<Output, unknown, Error, Env>,
|
||||||
|
): Effect.Effect<Query<K, A, E, R>, never, Scope.Scope | Env>
|
||||||
|
} = Function.dual(2, <K, A, E, R, Output, Error, Env>(
|
||||||
|
self: Query<K, A, E, R>,
|
||||||
|
schedule: Schedule.Schedule<Output, unknown, Error, Env>,
|
||||||
|
) => Effect.schedule(self.refresh, schedule).pipe(
|
||||||
|
Effect.forkScoped,
|
||||||
|
Effect.as(self),
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
export class QueryStateLens<in out K, in out A, in out E = never>
|
export class QueryStateLens<in out K, in out A, in out E = never>
|
||||||
extends Lens.LensImpl<QueryState<K, A, E>, never, never, never, never> {
|
extends Lens.LensImpl<QueryState<K, A, E>, never, never, never, never> {
|
||||||
|
|||||||
Reference in New Issue
Block a user