mirror of
https://github.com/enso-org/enso.git
synced 2024-12-30 09:03:33 +03:00
Share tanstack QueryClient between dashboard and IDE (#10431)
* Share tanstack QueryClient between dashboard and IDE
Part of #10400.
* Lint
* Review: Use enso-common
* Remove outdated README
* Naming
* Fix
* Lint
* enso-common CODEOWNERS: GUI+Dashboard
* Review: Prepare for GUI to be run from cloud entry point
* Lint
* Lint
* Fix e2e tests
* Fix e2e tests in CI?
* Clean CI build
* Revert "Clean CI build"
This reverts commit 73f2fb7972
.
* Fix redundant dependency
* Work around a vue-query bug
* Lint
* fmt
This commit is contained in:
parent
e4da96e943
commit
bc92035683
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
@ -45,3 +45,6 @@ Cargo.toml
|
||||
# The data-link schema is owned by the libraries team
|
||||
/app/ide-desktop/lib/dashboard/src/data/datalinkSchema.json @radeusgd @jdunkerley @GregoryTravis @AdRiley @marthasharkey
|
||||
/app/ide-desktop/lib/dashboard/src/data/__tests__ @radeusgd @jdunkerley @GregoryTravis @AdRiley @marthasharkey @PabloBuchu @indiv0 @somebody1234
|
||||
|
||||
# GUI / Dashboard shared
|
||||
/app/ide-desktop/lib/common @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount @Frizi @farmaazon @vitvakatu @kazcw @AdRiley
|
||||
|
@ -64,6 +64,7 @@
|
||||
"@lezer/highlight": "^1.1.6",
|
||||
"@noble/hashes": "^1.3.2",
|
||||
"@open-rpc/client-js": "^1.8.1",
|
||||
"@tanstack/vue-query": ">= 5.45.0 < 5.46.0",
|
||||
"@vueuse/core": "^10.4.1",
|
||||
"ag-grid-community": "^30.2.1",
|
||||
"ag-grid-enterprise": "^30.2.1",
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { baseConfig, configValue, mergeConfig } from '@/util/config'
|
||||
import { urlParams } from '@/util/urlParams'
|
||||
import * as vueQuery from '@tanstack/vue-query'
|
||||
import { isOnLinux } from 'enso-common/src/detect'
|
||||
import * as commonQuery from 'enso-common/src/queryClient'
|
||||
import * as dashboard from 'enso-dashboard'
|
||||
import { isDevMode } from 'shared/util/detect'
|
||||
import { lazyVueInReact } from 'veaury'
|
||||
import { type App } from 'vue'
|
||||
|
||||
import 'enso-dashboard/src/tailwind.css'
|
||||
import type { EditorRunner } from '../../ide-desktop/lib/types/types'
|
||||
@ -46,8 +49,6 @@ window.addEventListener('resize', () => {
|
||||
scamWarningHandle = window.setTimeout(printScamWarning, SCAM_WARNING_TIMEOUT)
|
||||
})
|
||||
|
||||
const appRunner = lazyVueInReact(AsyncApp as any /* async VueComponent */) as EditorRunner
|
||||
|
||||
/** The entrypoint into the IDE. */
|
||||
function main() {
|
||||
/** Note: Signing out always redirects to `/`. It is impossible to make this work,
|
||||
@ -74,6 +75,15 @@ function main() {
|
||||
const projectManagerUrl = config.engine.projectManagerUrl || PROJECT_MANAGER_URL
|
||||
const ydocUrl = config.engine.ydocUrl === '' ? YDOC_SERVER_URL : config.engine.ydocUrl
|
||||
const initialProjectName = config.startup.project || null
|
||||
const queryClient = commonQuery.createQueryClient()
|
||||
|
||||
const registerPlugins = (app: App) => {
|
||||
app.use(vueQuery.VueQueryPlugin, { queryClient })
|
||||
}
|
||||
|
||||
const appRunner = lazyVueInReact(AsyncApp as any /* async VueComponent */, {
|
||||
beforeVueAppMount: (app) => registerPlugins(app as App),
|
||||
}) as EditorRunner
|
||||
|
||||
dashboard.run({
|
||||
appRunner,
|
||||
@ -96,6 +106,7 @@ function main() {
|
||||
}
|
||||
}
|
||||
},
|
||||
queryClient,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
# Common utilities
|
||||
|
||||
This module contains utilities that are used by multiple modules (or multiple
|
||||
different build commands).
|
||||
|
||||
It is highly NOT RECOMMENDED to add files to this package - prefer creating a
|
||||
new package with a narrower set of responsibilities instead.
|
@ -10,6 +10,17 @@
|
||||
"./src/buildUtils": "./src/buildUtils.js",
|
||||
"./src/detect": "./src/detect.ts",
|
||||
"./src/gtag": "./src/gtag.ts",
|
||||
"./src/load": "./src/load.ts"
|
||||
"./src/load": "./src/load.ts",
|
||||
"./src/queryClient": "./src/queryClient.ts"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/query-core": "5.45.0",
|
||||
"@tanstack/vue-query": ">= 5.45.0 < 5.46.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"idb-keyval": "^6.2.1",
|
||||
"@tanstack/query-persist-client-core": "^5.45.0",
|
||||
"@tanstack/vue-query": ">= 5.45.0 < 5.46.0",
|
||||
"vue": "^3.4.19"
|
||||
}
|
||||
}
|
||||
|
175
app/ide-desktop/lib/common/src/queryClient.ts
Normal file
175
app/ide-desktop/lib/common/src/queryClient.ts
Normal file
@ -0,0 +1,175 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Tanstack Query client for Enso IDE and dashboard.
|
||||
*/
|
||||
|
||||
import * as idbKeyval from 'idb-keyval'
|
||||
import * as persistClientCore from '@tanstack/query-persist-client-core'
|
||||
import * as queryCore from '@tanstack/query-core'
|
||||
import * as vueQuery from './vueQuery'
|
||||
|
||||
declare module '@tanstack/query-core' {
|
||||
/**
|
||||
* Query client with additional methods.
|
||||
*/
|
||||
interface QueryClient {
|
||||
/**
|
||||
* Clear the cache stored in Tanstack Query and the persister storage.
|
||||
* Please use this method with caution, as it will clear all cache data.
|
||||
* Usually you should use `queryClient.invalidateQueries` instead.
|
||||
*/
|
||||
readonly clearWithPersister: () => Promise<void>
|
||||
/**
|
||||
* Clear the cache stored in the persister storage.
|
||||
*/
|
||||
readonly nukePersister: () => Promise<void>
|
||||
}
|
||||
/**
|
||||
* Specifies the invalidation behavior of a mutation.
|
||||
*/
|
||||
interface Register {
|
||||
readonly mutationMeta: {
|
||||
/**
|
||||
* List of query keys to invalidate when the mutation succeeds.
|
||||
*/
|
||||
readonly invalidates?: queryCore.QueryKey[]
|
||||
/**
|
||||
* List of query keys to await invalidation before the mutation is considered successful.
|
||||
*
|
||||
* If `true`, all `invalidates` are awaited.
|
||||
*
|
||||
* If `false`, no invalidations are awaited.
|
||||
*
|
||||
* You can also provide an array of query keys to await.
|
||||
*
|
||||
* Queries that are not listed in invalidates will be ignored.
|
||||
* @default false
|
||||
*/
|
||||
readonly awaitInvalidates?: queryCore.QueryKey[] | boolean
|
||||
}
|
||||
|
||||
readonly queryMeta: {
|
||||
/**
|
||||
* Whether to persist the query cache in the storage. Defaults to `true`.
|
||||
* Use `false` to disable persistence for a specific query, for example for
|
||||
* a sensitive data or data that can't be persisted, e.g. class instances.
|
||||
* @default true
|
||||
*/
|
||||
readonly persist?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Query Client type suitable for shared use in React and Vue. */
|
||||
export type QueryClient = vueQuery.QueryClient
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
||||
const DEFAULT_QUERY_STALE_TIME_MS = 2 * 60 * 1000
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
||||
const DEFAULT_QUERY_PERSIST_TIME_MS = 30 * 24 * 60 * 60 * 1000 // 30 days
|
||||
|
||||
const DEFAULT_BUSTER = 'v1.1'
|
||||
|
||||
/**
|
||||
* Create a new Tanstack Query client.
|
||||
*/
|
||||
export function createQueryClient(): QueryClient {
|
||||
const store = idbKeyval.createStore('enso', 'query-persist-cache')
|
||||
queryCore.onlineManager.setOnline(navigator.onLine)
|
||||
|
||||
const persister = persistClientCore.experimental_createPersister({
|
||||
storage: {
|
||||
getItem: key => idbKeyval.get<persistClientCore.PersistedQuery>(key, store),
|
||||
setItem: (key, value) => idbKeyval.set(key, value, store),
|
||||
removeItem: key => idbKeyval.del(key, store),
|
||||
},
|
||||
// Prefer online first and don't rely on the local cache if user is online
|
||||
// fallback to the local cache only if the user is offline
|
||||
maxAge: queryCore.onlineManager.isOnline() ? -1 : DEFAULT_QUERY_PERSIST_TIME_MS,
|
||||
buster: DEFAULT_BUSTER,
|
||||
filters: { predicate: query => query.meta?.persist !== false },
|
||||
prefix: 'enso:query-persist:',
|
||||
serialize: persistedQuery => persistedQuery,
|
||||
deserialize: persistedQuery => persistedQuery,
|
||||
})
|
||||
|
||||
const queryClient: QueryClient = new vueQuery.QueryClient({
|
||||
mutationCache: new queryCore.MutationCache({
|
||||
onSuccess: (_data, _variables, _context, mutation) => {
|
||||
const shouldAwaitInvalidates = mutation.meta?.awaitInvalidates ?? false
|
||||
const invalidates = mutation.meta?.invalidates ?? []
|
||||
const invalidatesToAwait = (() => {
|
||||
if (Array.isArray(shouldAwaitInvalidates)) {
|
||||
return shouldAwaitInvalidates
|
||||
} else {
|
||||
return shouldAwaitInvalidates ? invalidates : []
|
||||
}
|
||||
})()
|
||||
const invalidatesToIgnore = invalidates.filter(
|
||||
queryKey => !invalidatesToAwait.includes(queryKey)
|
||||
)
|
||||
|
||||
for (const queryKey of invalidatesToIgnore) {
|
||||
void queryClient.invalidateQueries({
|
||||
predicate: query => queryCore.matchQuery({ queryKey }, query),
|
||||
})
|
||||
}
|
||||
|
||||
if (invalidatesToAwait.length > 0) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return Promise.all(
|
||||
invalidatesToAwait.map(queryKey =>
|
||||
queryClient.invalidateQueries({
|
||||
predicate: query => queryCore.matchQuery({ queryKey }, query),
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
}),
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
persister,
|
||||
refetchOnReconnect: 'always',
|
||||
staleTime: DEFAULT_QUERY_STALE_TIME_MS,
|
||||
retry: (failureCount, error: unknown) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
||||
const statusesToIgnore = [401, 403, 404]
|
||||
const errorStatus =
|
||||
typeof error === 'object' &&
|
||||
error != null &&
|
||||
'status' in error &&
|
||||
typeof error.status === 'number'
|
||||
? error.status
|
||||
: -1
|
||||
|
||||
if (statusesToIgnore.includes(errorStatus)) {
|
||||
return false
|
||||
} else {
|
||||
return failureCount < 3
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Object.defineProperty(queryClient, 'nukePersister', {
|
||||
value: () => idbKeyval.clear(store),
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
})
|
||||
|
||||
Object.defineProperty(queryClient, 'clearWithPersister', {
|
||||
value: () => {
|
||||
queryClient.clear()
|
||||
return queryClient.nukePersister()
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
})
|
||||
|
||||
return queryClient
|
||||
}
|
99
app/ide-desktop/lib/common/src/vueQuery.ts
Normal file
99
app/ide-desktop/lib/common/src/vueQuery.ts
Normal file
@ -0,0 +1,99 @@
|
||||
/** @file QueryClient based on the '@tanstack/vue-query' implementation. */
|
||||
|
||||
import * as vueQuery from '@tanstack/vue-query'
|
||||
import * as queryCore from '@tanstack/query-core'
|
||||
import * as vue from 'vue'
|
||||
|
||||
/** The QueryClient from vue-query, but with immediate query invalidation. */
|
||||
export class QueryClient extends vueQuery.QueryClient {
|
||||
/** Like the `invalidateQueries` method of `vueQuery.QueryClient`, but invalidates queries immediately. */
|
||||
// Workaround for https://github.com/TanStack/query/issues/7694
|
||||
override invalidateQueries(
|
||||
filters: MaybeRefDeep<queryCore.InvalidateQueryFilters> = {},
|
||||
options: MaybeRefDeep<queryCore.InvalidateOptions> = {}
|
||||
): Promise<void> {
|
||||
const filtersValue = cloneDeepUnref(filters)
|
||||
const optionsValue = cloneDeepUnref(options)
|
||||
queryCore.notifyManager.batch(() => {
|
||||
this.getQueryCache()
|
||||
.findAll(filtersValue)
|
||||
.forEach(query => {
|
||||
query.invalidate()
|
||||
})
|
||||
})
|
||||
if (filtersValue.refetchType === 'none') {
|
||||
return Promise.resolve()
|
||||
} else {
|
||||
const refetchType = filtersValue.refetchType
|
||||
return vue.nextTick(() =>
|
||||
queryCore.notifyManager.batch(() => {
|
||||
const refetchFilters: queryCore.RefetchQueryFilters = {
|
||||
...filtersValue,
|
||||
type: refetchType ?? filtersValue.type ?? 'active',
|
||||
}
|
||||
return this.refetchQueries(refetchFilters, optionsValue)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
function isPlainObject(value: unknown): value is Object {
|
||||
if (Object.prototype.toString.call(value) !== '[object Object]') {
|
||||
return false
|
||||
}
|
||||
|
||||
const prototype = Object.getPrototypeOf(value)
|
||||
return prototype === null || prototype === Object.prototype
|
||||
}
|
||||
|
||||
function cloneDeep<T>(
|
||||
value: MaybeRefDeep<T>,
|
||||
customize?: (val: MaybeRefDeep<T>) => T | undefined
|
||||
): T {
|
||||
if (customize) {
|
||||
const result = customize(value)
|
||||
// If it's a ref of undefined, return undefined
|
||||
if (result === undefined && vue.isRef(value)) {
|
||||
return result as T
|
||||
}
|
||||
if (result !== undefined) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(val => cloneDeep(val, customize)) as unknown as T
|
||||
}
|
||||
|
||||
if (typeof value === 'object' && isPlainObject(value)) {
|
||||
const entries = Object.entries(value).map(([key, val]) => [key, cloneDeep(val, customize)])
|
||||
return Object.fromEntries(entries)
|
||||
}
|
||||
|
||||
return value as T
|
||||
}
|
||||
|
||||
function cloneDeepUnref<T>(obj: MaybeRefDeep<T>): T {
|
||||
return cloneDeep(obj, val => {
|
||||
if (vue.isRef(val)) {
|
||||
return cloneDeepUnref(vue.unref(val))
|
||||
}
|
||||
|
||||
return undefined
|
||||
})
|
||||
}
|
||||
|
||||
type MaybeRefDeep<T> = vue.MaybeRef<
|
||||
T extends Function
|
||||
? T
|
||||
: T extends object
|
||||
? {
|
||||
[Property in keyof T]: MaybeRefDeep<T[Property]>
|
||||
}
|
||||
: T
|
||||
>
|
||||
|
||||
/* eslint-enable */
|
@ -36,12 +36,11 @@
|
||||
"@monaco-editor/react": "4.6.0",
|
||||
"@sentry/react": "^7.74.0",
|
||||
"@tanstack/react-query": "5.45.1",
|
||||
"@tanstack/query-persist-client-core": "5.45.0",
|
||||
"@tanstack/vue-query": ">= 5.45.0 < 5.46.0",
|
||||
"ajv": "^8.12.0",
|
||||
"clsx": "^2.1.1",
|
||||
"enso-assets": "workspace:*",
|
||||
"enso-common": "workspace:*",
|
||||
"idb-keyval": "6.2.1",
|
||||
"is-network-error": "^1.0.1",
|
||||
"monaco-editor": "0.48.0",
|
||||
"react": "^18.3.1",
|
||||
|
@ -158,6 +158,7 @@ export interface AppProps {
|
||||
readonly appRunner: types.EditorRunner | null
|
||||
readonly portalRoot: Element
|
||||
readonly httpClient: HttpClient
|
||||
readonly queryClient: reactQuery.QueryClient
|
||||
}
|
||||
|
||||
/** Component called by the parent module, returning the root React component for this
|
||||
|
@ -1,4 +1,6 @@
|
||||
/** @file Entry point into the cloud dashboard. */
|
||||
import * as commonQuery from 'enso-common/src/queryClient'
|
||||
|
||||
import '#/tailwind.css'
|
||||
|
||||
import * as main from '#/index'
|
||||
@ -27,4 +29,5 @@ main.run({
|
||||
projectManagerUrl: null,
|
||||
ydocUrl: null,
|
||||
appRunner: testAppRunner.TestAppRunner,
|
||||
queryClient: commonQuery.createQueryClient(),
|
||||
})
|
||||
|
@ -13,7 +13,6 @@ import * as detect from 'enso-common/src/detect'
|
||||
|
||||
import type * as app from '#/App'
|
||||
import App from '#/App'
|
||||
import * as reactQueryClientModule from '#/reactQueryClient'
|
||||
|
||||
import LoadingScreen from '#/pages/authentication/LoadingScreen'
|
||||
|
||||
@ -46,7 +45,7 @@ export // This export declaration must be broken up to satisfy the `require-jsdo
|
||||
// This is not a React component even though it contains JSX.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
function run(props: Omit<app.AppProps, 'httpClient' | 'portalRoot'>) {
|
||||
const { vibrancy, supportsDeepLinks } = props
|
||||
const { vibrancy, supportsDeepLinks, queryClient } = props
|
||||
if (
|
||||
!detect.IS_DEV_MODE &&
|
||||
process.env.ENSO_CLOUD_SENTRY_DSN != null &&
|
||||
@ -93,7 +92,6 @@ function run(props: Omit<app.AppProps, 'httpClient' | 'portalRoot'>) {
|
||||
: supportsDeepLinks && detect.isOnElectron()
|
||||
|
||||
const httpClient = new HttpClient()
|
||||
const queryClient = reactQueryClientModule.createReactQueryClient()
|
||||
|
||||
React.startTransition(() => {
|
||||
reactDOM.createRoot(root).render(
|
||||
|
@ -1,171 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* React Query client for the dashboard.
|
||||
*/
|
||||
|
||||
import * as persistClientCore from '@tanstack/query-persist-client-core'
|
||||
import * as reactQuery from '@tanstack/react-query'
|
||||
import * as idbKeyval from 'idb-keyval'
|
||||
|
||||
declare module '@tanstack/react-query' {
|
||||
/**
|
||||
* React Query client with additional methods.
|
||||
*/
|
||||
interface QueryClient {
|
||||
/**
|
||||
* Clear the cache stored in React Query and the persister storage.
|
||||
* Please use this method with caution, as it will clear all cache data.
|
||||
* Usually you should use `queryClient.invalidateQueries` instead.
|
||||
*/
|
||||
readonly clearWithPersister: () => Promise<void>
|
||||
/**
|
||||
* Clear the cache stored in the persister storage.
|
||||
*/
|
||||
readonly nukePersister: () => Promise<void>
|
||||
}
|
||||
/**
|
||||
* Specifies the invalidation behavior of a mutation.
|
||||
*/
|
||||
interface Register {
|
||||
readonly mutationMeta: {
|
||||
/**
|
||||
* List of query keys to invalidate when the mutation succeeds.
|
||||
*/
|
||||
readonly invalidates?: reactQuery.QueryKey[]
|
||||
/**
|
||||
* List of query keys to await invalidation before the mutation is considered successful.
|
||||
*
|
||||
* If `true`, all `invalidates` are awaited.
|
||||
*
|
||||
* If `false`, no invalidations are awaited.
|
||||
*
|
||||
* You can also provide an array of query keys to await.
|
||||
*
|
||||
* Queries that are not listed in invalidates will be ignored.
|
||||
* @default false
|
||||
*/
|
||||
readonly awaitInvalidates?: reactQuery.QueryKey[] | boolean
|
||||
}
|
||||
|
||||
readonly queryMeta: {
|
||||
/**
|
||||
* Whether to persist the query cache in the storage. Defaults to `true`.
|
||||
* Use `false` to disable persistence for a specific query, for example for
|
||||
* a sensitive data or data that can't be persisted, e.g. class instances.
|
||||
* @default true
|
||||
*/
|
||||
readonly persist?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
||||
const DEFAULT_QUERY_STALE_TIME_MS = 2 * 60 * 1000
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
||||
const DEFAULT_QUERY_PERSIST_TIME_MS = 30 * 24 * 60 * 60 * 1000 // 30 days
|
||||
|
||||
const DEFAULT_BUSTER = 'v1.1'
|
||||
|
||||
/**
|
||||
* Create a new React Query client.
|
||||
*/
|
||||
export function createReactQueryClient() {
|
||||
const store = idbKeyval.createStore('enso', 'query-persist-cache')
|
||||
reactQuery.onlineManager.setOnline(navigator.onLine)
|
||||
|
||||
const persister = persistClientCore.experimental_createPersister({
|
||||
storage: {
|
||||
getItem: key => idbKeyval.get<persistClientCore.PersistedQuery>(key, store),
|
||||
setItem: (key, value) => idbKeyval.set(key, value, store),
|
||||
removeItem: key => idbKeyval.del(key, store),
|
||||
},
|
||||
// Prefer online first and don't rely on the local cache if user is online
|
||||
// fallback to the local cache only if the user is offline
|
||||
maxAge: reactQuery.onlineManager.isOnline() ? -1 : DEFAULT_QUERY_PERSIST_TIME_MS,
|
||||
buster: DEFAULT_BUSTER,
|
||||
filters: { predicate: query => query.meta?.persist !== false },
|
||||
prefix: 'enso:query-persist:',
|
||||
serialize: persistedQuery => persistedQuery,
|
||||
deserialize: persistedQuery => persistedQuery,
|
||||
})
|
||||
|
||||
const queryClient: reactQuery.QueryClient = new reactQuery.QueryClient({
|
||||
mutationCache: new reactQuery.MutationCache({
|
||||
onSuccess: (_data, _variables, _context, mutation) => {
|
||||
const shouldAwaitInvalidates = mutation.meta?.awaitInvalidates ?? false
|
||||
const invalidates = mutation.meta?.invalidates ?? []
|
||||
const invalidatesToAwait = (() => {
|
||||
if (Array.isArray(shouldAwaitInvalidates)) {
|
||||
return shouldAwaitInvalidates
|
||||
} else {
|
||||
return shouldAwaitInvalidates ? invalidates : []
|
||||
}
|
||||
})()
|
||||
const invalidatesToIgnore = invalidates.filter(
|
||||
queryKey => !invalidatesToAwait.includes(queryKey)
|
||||
)
|
||||
|
||||
for (const queryKey of invalidatesToIgnore) {
|
||||
void queryClient.invalidateQueries({
|
||||
predicate: query => reactQuery.matchQuery({ queryKey }, query),
|
||||
})
|
||||
}
|
||||
|
||||
if (invalidatesToAwait.length > 0) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return Promise.all(
|
||||
invalidatesToAwait.map(queryKey =>
|
||||
queryClient.invalidateQueries({
|
||||
predicate: query => reactQuery.matchQuery({ queryKey }, query),
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
}),
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
persister,
|
||||
refetchOnReconnect: 'always',
|
||||
staleTime: DEFAULT_QUERY_STALE_TIME_MS,
|
||||
retry: (failureCount, error: unknown) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
||||
const statusesToIgnore = [401, 403, 404]
|
||||
const errorStatus =
|
||||
typeof error === 'object' &&
|
||||
error != null &&
|
||||
'status' in error &&
|
||||
typeof error.status === 'number'
|
||||
? error.status
|
||||
: -1
|
||||
|
||||
if (statusesToIgnore.includes(errorStatus)) {
|
||||
return false
|
||||
} else {
|
||||
return failureCount < 3
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Object.defineProperty(queryClient, 'nukePersister', {
|
||||
value: () => idbKeyval.clear(store),
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
})
|
||||
|
||||
Object.defineProperty(queryClient, 'clearWithPersister', {
|
||||
value: () => {
|
||||
queryClient.clear()
|
||||
return queryClient.nukePersister()
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
})
|
||||
|
||||
return queryClient
|
||||
}
|
@ -115,6 +115,9 @@ importers:
|
||||
'@open-rpc/client-js':
|
||||
specifier: ^1.8.1
|
||||
version: 1.8.1
|
||||
'@tanstack/vue-query':
|
||||
specifier: '>= 5.45.0 < 5.46.0'
|
||||
version: 5.45.0(vue@3.4.31(typescript@5.5.3))
|
||||
'@vueuse/core':
|
||||
specifier: ^10.4.1
|
||||
version: 10.11.0(vue@3.4.31(typescript@5.5.3))
|
||||
@ -523,7 +526,23 @@ importers:
|
||||
specifier: ^5.3.3
|
||||
version: 5.3.3(@types/node@20.11.21)(lightningcss@1.25.1)
|
||||
|
||||
app/ide-desktop/lib/common: {}
|
||||
app/ide-desktop/lib/common:
|
||||
dependencies:
|
||||
'@tanstack/query-core':
|
||||
specifier: 5.45.0
|
||||
version: 5.45.0
|
||||
'@tanstack/query-persist-client-core':
|
||||
specifier: ^5.45.0
|
||||
version: 5.45.0
|
||||
'@tanstack/vue-query':
|
||||
specifier: '>= 5.45.0 < 5.46.0'
|
||||
version: 5.45.0(vue@3.4.31(typescript@5.5.3))
|
||||
idb-keyval:
|
||||
specifier: ^6.2.1
|
||||
version: 6.2.1
|
||||
vue:
|
||||
specifier: ^3.4.19
|
||||
version: 3.4.31(typescript@5.5.3)
|
||||
|
||||
app/ide-desktop/lib/content-config:
|
||||
dependencies:
|
||||
@ -554,12 +573,12 @@ importers:
|
||||
'@sentry/react':
|
||||
specifier: ^7.74.0
|
||||
version: 7.118.0(react@18.3.1)
|
||||
'@tanstack/query-persist-client-core':
|
||||
specifier: 5.45.0
|
||||
version: 5.45.0
|
||||
'@tanstack/react-query':
|
||||
specifier: 5.45.1
|
||||
version: 5.45.1(react@18.3.1)
|
||||
'@tanstack/vue-query':
|
||||
specifier: '>= 5.45.0 < 5.46.0'
|
||||
version: 5.45.0(vue@3.4.31(typescript@5.5.3))
|
||||
ajv:
|
||||
specifier: ^8.12.0
|
||||
version: 8.16.0
|
||||
@ -572,9 +591,6 @@ importers:
|
||||
enso-common:
|
||||
specifier: workspace:*
|
||||
version: link:../common
|
||||
idb-keyval:
|
||||
specifier: 6.2.1
|
||||
version: 6.2.1
|
||||
is-network-error:
|
||||
specifier: ^1.0.1
|
||||
version: 1.1.0
|
||||
@ -2601,6 +2617,10 @@ packages:
|
||||
resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
'@tanstack/match-sorter-utils@8.15.1':
|
||||
resolution: {integrity: sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/query-core@5.45.0':
|
||||
resolution: {integrity: sha512-RVfIZQmFUTdjhSAAblvueimfngYyfN6HlwaJUPK71PKd7yi43Vs1S/rdimmZedPWX/WGppcq/U1HOj7O7FwYxw==}
|
||||
|
||||
@ -2621,6 +2641,15 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^18.0.0
|
||||
|
||||
'@tanstack/vue-query@5.45.0':
|
||||
resolution: {integrity: sha512-WogAH4+xDPWbiK9CUXAE4cQiCyvWeYZI3g3/onKbkb3tVnoEPRhbGHANgxpfAEFY165Vj4afKnI3hkVQvr7aHA==}
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.1.2
|
||||
vue: ^2.6.0 || ^3.3.0
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
'@tootallnate/once@2.0.0':
|
||||
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
|
||||
engines: {node: '>= 10'}
|
||||
@ -3009,6 +3038,9 @@ packages:
|
||||
'@vue/compiler-ssr@3.4.31':
|
||||
resolution: {integrity: sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==}
|
||||
|
||||
'@vue/devtools-api@6.6.3':
|
||||
resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==}
|
||||
|
||||
'@vue/devtools-core@7.3.5':
|
||||
resolution: {integrity: sha512-uSC3IkIp6MtyJYSh5xzY99sgqlAXLq+peE2KKXTi6JeRHOtMngFWFWENXi70IJ1EVGYztiFQoHhI9WZcgKBz8g==}
|
||||
peerDependencies:
|
||||
@ -6204,6 +6236,9 @@ packages:
|
||||
resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
remove-accents@0.5.0:
|
||||
resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==}
|
||||
|
||||
request@2.88.2:
|
||||
resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==}
|
||||
engines: {node: '>= 6'}
|
||||
@ -9832,6 +9867,10 @@ snapshots:
|
||||
dependencies:
|
||||
defer-to-connect: 2.0.1
|
||||
|
||||
'@tanstack/match-sorter-utils@8.15.1':
|
||||
dependencies:
|
||||
remove-accents: 0.5.0
|
||||
|
||||
'@tanstack/query-core@5.45.0': {}
|
||||
|
||||
'@tanstack/query-devtools@5.37.1': {}
|
||||
@ -9851,6 +9890,14 @@ snapshots:
|
||||
'@tanstack/query-core': 5.45.0
|
||||
react: 18.3.1
|
||||
|
||||
'@tanstack/vue-query@5.45.0(vue@3.4.31(typescript@5.5.3))':
|
||||
dependencies:
|
||||
'@tanstack/match-sorter-utils': 8.15.1
|
||||
'@tanstack/query-core': 5.45.0
|
||||
'@vue/devtools-api': 6.6.3
|
||||
vue: 3.4.31(typescript@5.5.3)
|
||||
vue-demi: 0.14.8(vue@3.4.31(typescript@5.5.3))
|
||||
|
||||
'@tootallnate/once@2.0.0': {}
|
||||
|
||||
'@tsconfig/node18@18.2.4': {}
|
||||
@ -10368,6 +10415,8 @@ snapshots:
|
||||
'@vue/compiler-dom': 3.4.31
|
||||
'@vue/shared': 3.4.31
|
||||
|
||||
'@vue/devtools-api@6.6.3': {}
|
||||
|
||||
'@vue/devtools-core@7.3.5(vite@5.3.3(@types/node@20.11.21)(lightningcss@1.25.1))(vue@3.4.31(typescript@5.5.3))':
|
||||
dependencies:
|
||||
'@vue/devtools-kit': 7.3.5
|
||||
@ -14043,6 +14092,8 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
set-function-name: 2.0.2
|
||||
|
||||
remove-accents@0.5.0: {}
|
||||
|
||||
request@2.88.2:
|
||||
dependencies:
|
||||
aws-sign2: 0.7.0
|
||||
|
Loading…
Reference in New Issue
Block a user