Update actions/setup-node action to v7 - autoclosed #55

Closed
renovate-bot wants to merge 50 commits from renovate/actions-setup-node-7.x into master
3 changed files with 224 additions and 167 deletions
Showing only changes of commit 015aa0c816 - Show all commits
+3
View File
@@ -61,6 +61,7 @@
"effect-lens": "2.0.0-beta.0", "effect-lens": "2.0.0-beta.0",
}, },
"devDependencies": { "devDependencies": {
"@effect/platform-browser": "^4.0.0-beta.85",
"@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",
@@ -3062,6 +3063,8 @@
"dot-prop/is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], "dot-prop/is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="],
"effect-fc-next/@effect/platform-browser": ["@effect/platform-browser@4.0.0-beta.87", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^4.0.0-beta.87" } }, "sha512-sBUUdRt5PjFPLI7+ayxEX/jgnjCbOuc3I//98CmqiwhfgWbCMZ/rofYYb8Zk5tLpNsmdHnWgHWTsBLvH2SFrCw=="],
"effect-fc-next/effect": ["effect@4.0.0-beta.85", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Cjv9YQyv4CiIccmRAQIWAoeESCpCpiuHYY8zb5vqiYs3Ac2yE5RQAnKq7z4Ir/3VJYZb8kxh6rS8czDDnpTdkQ=="], "effect-fc-next/effect": ["effect@4.0.0-beta.85", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Cjv9YQyv4CiIccmRAQIWAoeESCpCpiuHYY8zb5vqiYs3Ac2yE5RQAnKq7z4Ir/3VJYZb8kxh6rS8czDDnpTdkQ=="],
"effect-fc-next/effect-lens": ["effect-lens@2.0.0-beta.0", "", { "peerDependencies": { "effect": "4.0.0-beta.85" } }, "sha512-ntsRQSrzoX+GsNpkNVEGqebl/g1YOEDPIFRAHigf+4P6AtGnnOFcklqDHLrbm4zqnaxuWEgQvvn1J6S3eyC4yw=="], "effect-fc-next/effect-lens": ["effect-lens@2.0.0-beta.0", "", { "peerDependencies": { "effect": "4.0.0-beta.85" } }, "sha512-ntsRQSrzoX+GsNpkNVEGqebl/g1YOEDPIFRAHigf+4P6AtGnnOFcklqDHLrbm4zqnaxuWEgQvvn1J6S3eyC4yw=="],
+1
View File
@@ -39,6 +39,7 @@
"clean:modules": "rm -rf node_modules" "clean:modules": "rm -rf node_modules"
}, },
"devDependencies": { "devDependencies": {
"@effect/platform-browser": "^4.0.0-beta.85",
"@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"
+220 -167
View File
@@ -1,44 +1,35 @@
import { import { type Cause, type Context, Duration, Effect, Equal, Exit, Fiber, Option, Pipeable, Predicate, type Scope, Semaphore, Stream, SubscriptionRef } from "effect"
type Cause, import { AsyncResult } from "effect/unstable/reactivity"
type Context, import * as Lens from "./Lens.js"
type Duration,
Effect,
Equal,
Fiber,
Option,
Pipeable,
Predicate,
type Scope,
Semaphore,
Stream,
SubscriptionRef,
} from "effect"
import { Subscribable } from "effect-lens"
import * as QueryClient from "./QueryClient.js" import * as QueryClient from "./QueryClient.js"
import * as Result from "./Result.js" import * as Subscribable from "./Subscribable.js"
export const QueryTypeId: unique symbol = Symbol.for("@effect-fc/Query/Query") export const QueryTypeId: unique symbol = Symbol.for("@effect-fc/Query/Query")
export type QueryTypeId = typeof QueryTypeId export type QueryTypeId = typeof QueryTypeId
export interface Query<in out K extends Query.AnyKey, in out A, in out KE = never, in out KR = never, in out E = never, in out R = never, in out P = never> export interface Query<in out K extends Query.AnyKey, in out A, in out KE = never, in out KR = never, in out E = never, in out R = never>
extends Pipeable.Pipeable { extends Pipeable.Pipeable {
readonly [QueryTypeId]: QueryTypeId readonly [QueryTypeId]: QueryTypeId
readonly context: Context.Context<Scope.Scope | QueryClient.QueryClient | KR | R> readonly context: Context.Context<Scope.Scope | QueryClient.QueryClient | KR | R>
readonly key: Stream.Stream<K, KE, KR> readonly key: Stream.Stream<K, KE, KR>
readonly f: (key: K) => Effect.Effect<A, E, R> readonly f: (key: K) => Effect.Effect<A, E, R>
readonly initialProgress: P
readonly staleTime: Duration.Input readonly staleTime: Duration.Duration
readonly refreshOnWindowFocus: boolean readonly refreshOnWindowFocus: boolean
readonly latestKey: Subscribable.Subscribable<Option.Option<K>> readonly latestKey: Subscribable.Subscribable<Option.Option<K>>
readonly fiber: Subscribable.Subscribable<Option.Option<Fiber.Fiber<A, E>>> readonly fiber: Subscribable.Subscribable<Option.Option<Fiber.Fiber<A, E>>>
readonly result: Subscribable.Subscribable<Result.Result<A, E, P>> readonly result: Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>
readonly latestFinalResult: Subscribable.Subscribable<Option.Option<Result.Final<A, E, P>>> readonly latestFinalResult: Subscribable.Subscribable<Option.Option<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>>>
readonly run: Effect.Effect<void> readonly run: Effect.Effect<void>
fetch(key: K): Effect.Effect<Result.Final<A, E, P>> fetch(key: K): Effect.Effect<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>, Cause.NoSuchElementError>
fetchSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>> fetchSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>, Cause.NoSuchElementError>
readonly refresh: Effect.Effect<Result.Final<A, E, P>, Cause.NoSuchElementError> readonly refresh: Effect.Effect<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>, Cause.NoSuchElementError>
readonly refreshSubscribable: Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>, Cause.NoSuchElementError> readonly refreshSubscribable: Effect.Effect<Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>, Cause.NoSuchElementError>
readonly invalidateCache: Effect.Effect<void> readonly invalidateCache: Effect.Effect<void>
invalidateCacheEntry(key: K): Effect.Effect<void> invalidateCacheEntry(key: K): Effect.Effect<void>
} }
@@ -47,42 +38,43 @@ export declare namespace Query {
export type AnyKey = readonly any[] export type AnyKey = readonly any[]
} }
export class QueryImpl<in out K extends Query.AnyKey, in out A, in out KE = never, in out KR = never, in out E = never, in out R = never, in out P = never> export class QueryImpl<in out K extends Query.AnyKey, in out A, in out KE = never, in out KR = never, in out E = never, in out R = never>
extends Pipeable.Class implements Query<K, A, KE, KR, E, R, P> { extends Pipeable.Class implements Query<K, A, KE, KR, E, R> {
readonly [QueryTypeId]: QueryTypeId = QueryTypeId readonly [QueryTypeId]: QueryTypeId = QueryTypeId
readonly latestKey: Subscribable.Subscribable<Option.Option<K>>
readonly fiber: Subscribable.Subscribable<Option.Option<Fiber.Fiber<A, E>>>
readonly result: Subscribable.Subscribable<Result.Result<A, E, P>>
readonly latestFinalResult: Subscribable.Subscribable<Option.Option<Result.Final<A, E, P>>>
constructor( constructor(
readonly context: Context.Context<Scope.Scope | QueryClient.QueryClient | KR | R>, readonly context: Context.Context<Scope.Scope | QueryClient.QueryClient | KR | R>,
readonly key: Stream.Stream<K, KE, KR>, readonly key: Stream.Stream<K, KE, KR>,
readonly f: (key: K) => Effect.Effect<A, E, R>, readonly f: (key: K) => Effect.Effect<A, E, R>,
readonly initialProgress: P,
readonly staleTime: Duration.Input, readonly staleTime: Duration.Duration,
readonly refreshOnWindowFocus: boolean, readonly refreshOnWindowFocus: boolean,
readonly latestKeyRef: SubscriptionRef.SubscriptionRef<Option.Option<K>>,
readonly fiberRef: SubscriptionRef.SubscriptionRef<Option.Option<Fiber.Fiber<A, E>>>, readonly latestKey: Lens.Lens<Option.Option<K>>,
readonly resultRef: SubscriptionRef.SubscriptionRef<Result.Result<A, E, P>>, readonly fiber: Lens.Lens<Option.Option<Fiber.Fiber<A, E>>>,
readonly latestFinalResultRef: SubscriptionRef.SubscriptionRef<Option.Option<Result.Final<A, E, P>>>, readonly result: Lens.Lens<AsyncResult.AsyncResult<A, E>>,
readonly latestFinalResult: Lens.Lens<Option.Option<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>>>,
readonly runSemaphore: Semaphore.Semaphore, readonly runSemaphore: Semaphore.Semaphore,
) { ) {
super() super()
this.latestKey = fromSubscriptionRef(latestKeyRef)
this.fiber = fromSubscriptionRef(fiberRef)
this.result = fromSubscriptionRef(resultRef)
this.latestFinalResult = fromSubscriptionRef(latestFinalResultRef)
} }
get run(): Effect.Effect<void> { get run(): Effect.Effect<void> {
const focus = this.refreshOnWindowFocus && typeof window !== "undefined"
? Stream.runForEach(Stream.fromEventListener<FocusEvent>(window, "focus"), () => this.refreshSubscribable)
: Effect.succeed(undefined)
return Effect.all([ return Effect.all([
Stream.runForEach(this.key, key => this.fetchSubscribable(key)), Stream.runForEach(this.key, key => this.fetchSubscribable(key)),
focus,
], { concurrency: "unbounded", discard: true }).pipe( Effect.promise(() => import("@effect/platform-browser")).pipe(
Effect.flatMap(({ BrowserStream }) => this.refreshOnWindowFocus
? Stream.runForEach(
BrowserStream.fromEventListenerWindow("focus"),
() => this.refreshSubscribable,
)
: Effect.void
),
Effect.catchDefect(() => Effect.void),
),
], { concurrency: "unbounded" }).pipe(
Effect.ignore, Effect.ignore,
this.runSemaphore.withPermits(1), this.runSemaphore.withPermits(1),
Effect.provide(this.context), Effect.provide(this.context),
@@ -90,120 +82,159 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R, P> {
} }
get interrupt(): Effect.Effect<void> { get interrupt(): Effect.Effect<void> {
return Effect.flatMap(SubscriptionRef.get(this.fiberRef), Option.match({ return Effect.flatMap(Lens.get(this.fiber), Option.match({
onSome: Fiber.interrupt, onSome: Fiber.interrupt,
onNone: () => Effect.succeed(undefined), onNone: () => Effect.void,
})) }))
} }
fetch(key: K): Effect.Effect<Result.Final<A, E, P>> { fetch(key: K): Effect.Effect<
const self = this AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>,
return Effect.gen(function*() { Cause.NoSuchElementError
yield* self.interrupt > {
yield* SubscriptionRef.set(self.latestKeyRef, Option.some(key)) return this.interrupt.pipe(
const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) Effect.andThen(Lens.set(this.latestKey, Option.some(key))),
const sub = yield* self.startCached(key, Option.isSome(previous) Effect.andThen(this.startCached(key, AsyncResult.initial())),
? Result.willFetch(previous.value) as Result.Final<A, E, P> Effect.flatMap(state => this.watch(key, state)),
: Result.initial()) Effect.provide(this.context),
return yield* self.watch(key, sub) )
}).pipe(Effect.provide(this.context))
} }
fetchSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>> { fetchSubscribable(key: K): Effect.Effect<
const self = this Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>,
return Effect.gen(function*() { Cause.NoSuchElementError
yield* self.interrupt > {
yield* SubscriptionRef.set(self.latestKeyRef, Option.some(key)) return this.interrupt.pipe(
const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) Effect.andThen(Lens.set(this.latestKey, Option.some(key))),
const sub = yield* self.startCached(key, Option.isSome(previous) Effect.andThen(this.startCached(key, AsyncResult.initial(false))),
? Result.willFetch(previous.value) as Result.Final<A, E, P> Effect.tap(state => Effect.forkScoped(this.watch(key, state))),
: Result.initial()) Effect.provide(this.context),
yield* Effect.forkScoped(self.watch(key, sub)) )
return sub
}).pipe(Effect.provide(this.context))
} }
get refresh(): Effect.Effect<Result.Final<A, E, P>, Cause.NoSuchElementError> { get refresh(): Effect.Effect<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>, Cause.NoSuchElementError> {
const self = this return this.interrupt.pipe(
return Effect.gen(function*() { Effect.andThen(Effect.Do),
yield* self.interrupt Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)),
const key = yield* Effect.fromOption(yield* SubscriptionRef.get(self.latestKeyRef)) Effect.bind("latestFinalResult", () => Lens.get(this.latestFinalResult)),
const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) Effect.bind("subscribable", ({ latestKey, latestFinalResult }) =>
const sub = yield* self.startCached(key, Option.isSome(previous) this.startCached(latestKey, Option.getOrElse(latestFinalResult, () => AsyncResult.initial(true)))
? Result.willRefresh(previous.value) as Result.Final<A, E, P> ),
: Result.initial()) Effect.flatMap(({ latestKey, subscribable }) => this.watch(latestKey, subscribable)),
return yield* self.watch(key, sub) Effect.provide(this.context),
}).pipe(Effect.provide(this.context)) )
} }
get refreshSubscribable(): Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>, Cause.NoSuchElementError> { get refreshSubscribable(): Effect.Effect<
const self = this Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>,
return Effect.gen(function*() { Cause.NoSuchElementError
yield* self.interrupt > {
const key = yield* Effect.fromOption(yield* SubscriptionRef.get(self.latestKeyRef)) return this.interrupt.pipe(
const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) Effect.andThen(Effect.Do),
const sub = yield* self.startCached(key, Option.isSome(previous) Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)),
? Result.willRefresh(previous.value) as Result.Final<A, E, P> Effect.bind("latestFinalResult", () => Lens.get(this.latestFinalResult)),
: Result.initial()) Effect.bind("subscribable", ({ latestKey, latestFinalResult }) =>
yield* Effect.forkScoped(self.watch(key, sub)) this.startCached(latestKey, Option.getOrElse(latestFinalResult, () => AsyncResult.initial(true)))
return sub ),
}).pipe(Effect.provide(this.context)) Effect.tap(({ latestKey, subscribable }) => Effect.forkScoped(this.watch(latestKey, subscribable))),
Effect.map(({ subscribable }) => subscribable),
Effect.provide(this.context),
)
} }
startCached( startCached(
key: K, key: K,
initial: Result.Initial | Result.Final<A, E, P>, previous: AsyncResult.AsyncResult<A, E>,
): Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>, never, Scope.Scope | QueryClient.QueryClient | R> { ): Effect.Effect<
Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>,
Cause.NoSuchElementError,
Scope.Scope | QueryClient.QueryClient | R
> {
return Effect.flatMap(this.getCacheEntry(key), Option.match({ return Effect.flatMap(this.getCacheEntry(key), Option.match({
onSome: entry => Effect.flatMap( onSome: entry => Effect.flatMap(
QueryClient.isQueryClientCacheEntryStale(entry), QueryClient.isQueryClientCacheEntryStale(entry),
isStale => isStale isStale => isStale
? this.start(key, Result.willRefresh(entry.result) as Result.Final<A, E, P>) ? this.start(key, entry.result as AsyncResult.AsyncResult<A, E>)
: Effect.succeed(Subscribable.make({ : Effect.succeed(Subscribable.make({
get: Effect.succeed(entry.result as Result.Result<A, E, P>), get: Effect.succeed(entry.result as AsyncResult.AsyncResult<A, E>),
changes: Stream.make(entry.result as Result.Result<A, E, P>), get changes() { return Stream.make(entry.result as AsyncResult.AsyncResult<A, E>) },
})), })),
), ),
onNone: () => this.start(key, initial), onNone: () => this.start(key, previous),
})) }))
} }
start( start(
key: K, key: K,
initial: Result.Initial | Result.Final<A, E, P>, previous: AsyncResult.AsyncResult<A, E>,
): Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>, never, Scope.Scope | R> { ): Effect.Effect<
const self = this Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>,
return Effect.gen(function*() { never,
const [sub, fiber] = yield* Result.unsafeForkEffect<A, E, R, P>( Scope.Scope | R
Effect.onExit(self.f(key), () => Effect.flatMap( > {
Effect.all([Effect.fiberId, SubscriptionRef.get(self.fiberRef)]), return Effect.gen({ self: this }, function*() {
([currentFiberId, current]) => Option.match(current, { const state = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(previous))
onSome: value => Equal.equals(currentFiberId, value.id)
? SubscriptionRef.set(self.fiberRef, Option.none()) const fiber = yield* Effect.forkScoped(Effect.andThen(
: Effect.succeed(undefined), Lens.update(state, AsyncResult.match({
onNone: () => Effect.succeed(undefined), onInitial: () => AsyncResult.initial(true),
onSuccess: v => AsyncResult.success(v.value, {
waiting: true,
}), }),
onFailure: v => AsyncResult.failure(v.cause, {
waiting: true,
previousSuccess: v.previousSuccess,
})
})),
Effect.onExit(this.f(key), exit => Lens.update(
state,
previous => Exit.match(exit, {
onSuccess: v => AsyncResult.success(v),
onFailure: c => AsyncResult.match(previous, {
onInitial: () => AsyncResult.failure(c),
onSuccess: v => AsyncResult.failure(c, {
previousSuccess: Option.some(v),
}),
onFailure: v => AsyncResult.failure(c, {
previousSuccess: v.previousSuccess,
})
}),
}),
).pipe(
Effect.andThen(Effect.all([
Effect.fiberId,
Lens.get(this.fiber),
])),
Effect.flatMap(([fiberId, fiber]) => Option.match(fiber, {
onSome: v => Equal.equals(fiberId, v.id)
? Lens.set(this.fiber, Option.none())
: Effect.void,
onNone: () => Effect.void,
})),
)), )),
{ initial, initialProgress: self.initialProgress }, ))
)
yield* SubscriptionRef.set(self.fiberRef, Option.some(fiber)) yield* Lens.set(this.fiber, Option.some(fiber))
return sub return state
}) })
} }
watch( watch(
key: K, key: K,
sub: Subscribable.Subscribable<Result.Result<A, E, P>>, state: Subscribable.Subscribable<AsyncResult.AsyncResult<A, E>>
): Effect.Effect<Result.Final<A, E, P>, never, QueryClient.QueryClient> { ): Effect.Effect<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>, never, QueryClient.QueryClient> {
return Effect.flatMap(sub.get, initial => Stream.runFoldEffect( return state.get.pipe(
Stream.takeUntil(sub.changes, result => Result.isFinal(result) && !Result.hasFlag(result)), Effect.flatMap(initial => Stream.runFoldEffect(
() => initial, state.changes,
(_, result) => Effect.as(SubscriptionRef.set(this.resultRef, result), result), () => initial,
) as Effect.Effect<Result.Final<A, E, P>>).pipe( (_, result) => Effect.as(Lens.set(this.result, result), result),
Effect.tap(result => SubscriptionRef.set(this.latestFinalResultRef, Option.some(result))), ) as Effect.Effect<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>>),
Effect.tap(result => Result.isSuccess(result) Effect.tap(result => Lens.set(this.latestFinalResult, Option.some(result))),
? Effect.asVoid(this.setCacheEntry(key, result)) Effect.tap(result => AsyncResult.isSuccess(result)
: Effect.succeed(undefined)), ? this.setCacheEntry(key, result)
: Effect.void
),
) )
} }
@@ -211,73 +242,95 @@ extends Pipeable.Class implements Query<K, A, KE, KR, E, R, P> {
return new QueryClient.QueryClientCacheKey(key, this.f as (key: Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>) return new QueryClient.QueryClientCacheKey(key, this.f as (key: Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>)
} }
getCacheEntry(key: K): Effect.Effect<Option.Option<QueryClient.QueryClientCacheEntry>, never, QueryClient.QueryClient> { getCacheEntry(
return Effect.flatMap(QueryClient.QueryClient, client => client.getCacheEntry(this.makeCacheKey(key))) key: K
): Effect.Effect<Option.Option<QueryClient.QueryClientCacheEntry>, never, QueryClient.QueryClient> {
return Effect.andThen(
Effect.all([
Effect.succeed(this.makeCacheKey(key)),
QueryClient.QueryClient,
]),
([key, client]) => client.getCacheEntry(key),
)
} }
setCacheEntry(key: K, result: Result.Success<A>): Effect.Effect<QueryClient.QueryClientCacheEntry, never, QueryClient.QueryClient> { setCacheEntry(
return Effect.flatMap(QueryClient.QueryClient, client => client.setCacheEntry(this.makeCacheKey(key), result, this.staleTime)) key: K,
result: AsyncResult.Success<A, E>,
): Effect.Effect<QueryClient.QueryClientCacheEntry, never, QueryClient.QueryClient> {
return Effect.flatMap(
Effect.all([
Effect.succeed(this.makeCacheKey(key)),
QueryClient.QueryClient,
]),
([key, client]) => client.setCacheEntry(key, result, this.staleTime),
)
} }
get invalidateCache(): Effect.Effect<void> { get invalidateCache(): Effect.Effect<void> {
return Effect.flatMap( return QueryClient.QueryClient.pipe(
QueryClient.QueryClient, Effect.andThen(client => client.invalidateCacheEntries(this.f as (key: Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>)),
client => client.invalidateCacheEntries(this.f as (key: Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>), Effect.provide(this.context),
).pipe(Effect.provide(this.context)) )
} }
invalidateCacheEntry(key: K): Effect.Effect<void> { invalidateCacheEntry(key: K): Effect.Effect<void> {
return Effect.flatMap( return Effect.all([
Effect.succeed(this.makeCacheKey(key)),
QueryClient.QueryClient, QueryClient.QueryClient,
client => client.invalidateCacheEntry(this.makeCacheKey(key)), ]).pipe(
).pipe(Effect.provide(this.context)) Effect.andThen(([key, client]) => client.invalidateCacheEntry(key)),
Effect.provide(this.context),
)
} }
} }
export const isQuery = (u: unknown): u is Query<readonly unknown[], unknown> => Predicate.hasProperty(u, QueryTypeId) export const isQuery = (u: unknown): u is Query<readonly unknown[], unknown> => Predicate.hasProperty(u, QueryTypeId)
export declare namespace make { export declare namespace make {
export interface Options<K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never, P = never> { export interface Options<K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never> {
readonly key: Stream.Stream<K, KE, KR> readonly key: Stream.Stream<K, KE, KR>
readonly f: (key: NoInfer<K>) => Effect.Effect<A, E, Result.forkEffect.InputContext<R, NoInfer<P>>> readonly f: (key: NoInfer<K>) => Effect.Effect<A, E, R>
readonly initialProgress?: P
readonly staleTime?: Duration.Input readonly staleTime?: Duration.Input
readonly refreshOnWindowFocus?: boolean readonly refreshOnWindowFocus?: boolean
} }
} }
export const make = Effect.fnUntraced(function* <K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never, P = never>( export const make = Effect.fnUntraced(function* <K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never>(
options: make.Options<K, A, KE, KR, E, R, P>, options: make.Options<K, A, KE, KR, E, R>
): Effect.fn.Return< ): Effect.fn.Return<
Query<K, A, KE, KR, E, Result.forkEffect.OutputContext<R, P>, P>, Query<K, A, KE, KR, E, R>,
never, Cause.NoSuchElementError,
Scope.Scope | QueryClient.QueryClient | KR | Result.forkEffect.OutputContext<R, P> Scope.Scope | QueryClient.QueryClient | KR | R
> { > {
const client = yield* QueryClient.QueryClient const client = yield* QueryClient.QueryClient
return new QueryImpl<K, A, KE, KR, E, Result.forkEffect.OutputContext<R, P>, P>(
yield* Effect.context<Scope.Scope | QueryClient.QueryClient | KR | Result.forkEffect.OutputContext<R, P>>(), return new QueryImpl(
yield* Effect.context<Scope.Scope | QueryClient.QueryClient | KR | R>(),
options.key, options.key,
options.f as any, options.f,
options.initialProgress as P,
options.staleTime ?? client.defaultStaleTime, options.staleTime ? yield* Effect.fromOption(Duration.fromInput(options.staleTime)) : client.defaultStaleTime,
options.refreshOnWindowFocus ?? client.defaultRefreshOnWindowFocus, options.refreshOnWindowFocus ?? client.defaultRefreshOnWindowFocus,
yield* SubscriptionRef.make(Option.none<K>()),
yield* SubscriptionRef.make(Option.none<Fiber.Fiber<A, E>>()), Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none<K>())),
yield* SubscriptionRef.make(Result.initial<A, E, P>()), Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none<Fiber.Fiber<A, E>>())),
yield* SubscriptionRef.make(Option.none<Result.Final<A, E, P>>()), Lens.fromSubscriptionRef(yield* SubscriptionRef.make<AsyncResult.AsyncResult<A, E>>(AsyncResult.initial())),
Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none<AsyncResult.Success<A, E> | AsyncResult.Failure<A, E>>())),
yield* Semaphore.make(1), yield* Semaphore.make(1),
) )
}) })
export const service = <K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never, P = never>( export const service = <K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never>(
options: make.Options<K, A, KE, KR, E, R, P>, options: make.Options<K, A, KE, KR, E, R>
): Effect.Effect< ): Effect.Effect<
Query<K, A, KE, KR, E, Result.forkEffect.OutputContext<R, P>, P>, Query<K, A, KE, KR, E, R>,
never, Cause.NoSuchElementError,
Scope.Scope | QueryClient.QueryClient | KR | Result.forkEffect.OutputContext<R, P> Scope.Scope | QueryClient.QueryClient | KR | R
> => Effect.tap(make(options), query => Effect.asVoid(Effect.forkScoped(query.run))) > => Effect.tap(
make(options),
const fromSubscriptionRef = <A>(ref: SubscriptionRef.SubscriptionRef<A>): Subscribable.Subscribable<A> => Subscribable.make({ query => Effect.forkScoped(query.run),
get: SubscriptionRef.get(ref), )
changes: SubscriptionRef.changes(ref),
})