Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 970b42be2d |
@@ -9,7 +9,7 @@ jobs:
|
|||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: bun install --frozen-lockfile
|
||||||
- name: Lint TypeScript
|
- name: Lint TypeScript
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
|
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
@@ -40,13 +40,6 @@ 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:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
|
|||||||
@@ -248,33 +248,6 @@ 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
|
||||||
|
|||||||
@@ -26,8 +26,8 @@
|
|||||||
"vite": "^8.0.16"
|
"vite": "^8.0.16"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/platform": "^0.96.1",
|
"@effect/platform": "^0.97.0",
|
||||||
"@effect/platform-browser": "^0.76.0",
|
"@effect/platform-browser": "^0.77.0",
|
||||||
"@radix-ui/themes": "^3.3.0",
|
"@radix-ui/themes": "^3.3.0",
|
||||||
"@typed/id": "^0.17.2",
|
"@typed/id": "^0.17.2",
|
||||||
"effect": "^3.21.2",
|
"effect": "^3.21.2",
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
"clean:modules": "rm -rf node_modules"
|
"clean:modules": "rm -rf node_modules"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@effect/platform-browser": "^0.76.0",
|
"@effect/platform-browser": "^0.77.0",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
|
|||||||
@@ -97,15 +97,15 @@
|
|||||||
"clean:modules": "rm -rf node_modules"
|
"clean:modules": "rm -rf node_modules"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@effect/platform-browser": "4.0.0-beta.101",
|
"@effect/platform-browser": "4.0.0-beta.102",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"effect": "4.0.0-beta.101",
|
"effect": "4.0.0-beta.102",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0",
|
"@types/react": "^19.2.0",
|
||||||
"effect": "4.0.0-beta.101",
|
"effect": "4.0.0-beta.102",
|
||||||
"react": "^19.2.0"
|
"react": "^19.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Effect, Schedule, type Scope, Stream } from "effect"
|
import { Effect, 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"
|
||||||
@@ -80,49 +79,6 @@ 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, Function, Option, Pipeable, Predicate, PubSub, Ref, type Schedule, type Scope, Semaphore, Stream, SubscriptionRef } from "effect"
|
import { Cause, type Context, Duration, Effect, Equal, type Equivalence, Exit, Fiber, Option, Pipeable, Predicate, PubSub, Ref, 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,43 +421,6 @@ 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> {
|
||||||
|
|||||||
@@ -27,15 +27,15 @@
|
|||||||
"vite": "^8.0.16"
|
"vite": "^8.0.16"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/platform-browser": "4.0.0-beta.101",
|
"@effect/platform-browser": "4.0.0-beta.102",
|
||||||
"@radix-ui/themes": "^3.3.0",
|
"@radix-ui/themes": "^3.3.0",
|
||||||
"effect": "4.0.0-beta.101",
|
"effect": "4.0.0-beta.102",
|
||||||
"effect-view": "workspace:*",
|
"effect-view": "workspace:*",
|
||||||
"react-icons": "^5.6.0"
|
"react-icons": "^5.6.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@types/react": "^19.2.15",
|
"@types/react": "^19.2.15",
|
||||||
"effect": "4.0.0-beta.101",
|
"effect": "4.0.0-beta.102",
|
||||||
"react": "^19.2.6"
|
"react": "^19.2.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user