Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 214b500623 |
@@ -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:
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ jobs:
|
|||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: "22"
|
node-version: "24"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
FROM oven/bun:1.3.12-debian@sha256:1b709c9dd883fc1af38c210f7ea5222c552a8d470ea73efbd4b8fcfee798a64b AS bun
|
FROM oven/bun:1.3.12-debian@sha256:1b709c9dd883fc1af38c210f7ea5222c552a8d470ea73efbd4b8fcfee798a64b AS bun
|
||||||
|
|
||||||
FROM node:22.21.1-trixie-slim@sha256:98e1429d1a0b99378b4de43fa385f0746fd6276faf4feeb6104d91f6bad290f9
|
FROM node:24.18.0-trixie-slim@sha256:ae91dcc111a68c9d2d81ff2a17bda61be126426176fde6fe7d08ab13b7f50573
|
||||||
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,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
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
Reference in New Issue
Block a user