@tanstack/lit-query 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -43
  3. package/dist/QueryClientProvider.d.ts +85 -0
  4. package/dist/QueryClientProvider.d.ts.map +1 -0
  5. package/dist/QueryClientProvider.js +137 -0
  6. package/dist/QueryClientProvider.js.map +1 -0
  7. package/dist/accessor.d.ts +32 -0
  8. package/dist/accessor.d.ts.map +1 -0
  9. package/dist/accessor.js +12 -0
  10. package/dist/accessor.js.map +1 -0
  11. package/dist/context.d.ts +59 -0
  12. package/dist/context.d.ts.map +1 -0
  13. package/dist/context.js +100 -0
  14. package/dist/context.js.map +1 -0
  15. package/dist/controllers/BaseController.d.ts +38 -0
  16. package/dist/controllers/BaseController.d.ts.map +1 -0
  17. package/dist/controllers/BaseController.js +204 -0
  18. package/dist/controllers/BaseController.js.map +1 -0
  19. package/dist/createInfiniteQueryController.d.ts +76 -0
  20. package/dist/createInfiniteQueryController.d.ts.map +1 -0
  21. package/dist/createInfiniteQueryController.js +221 -0
  22. package/dist/createInfiniteQueryController.js.map +1 -0
  23. package/dist/createMutationController.d.ts +79 -0
  24. package/dist/createMutationController.d.ts.map +1 -0
  25. package/dist/createMutationController.js +193 -0
  26. package/dist/createMutationController.js.map +1 -0
  27. package/dist/createQueriesController.d.ts +145 -0
  28. package/dist/createQueriesController.d.ts.map +1 -0
  29. package/dist/createQueriesController.js +302 -0
  30. package/dist/createQueriesController.js.map +1 -0
  31. package/dist/createQueryController.d.ts +67 -0
  32. package/dist/createQueryController.d.ts.map +1 -0
  33. package/dist/createQueryController.js +211 -0
  34. package/dist/createQueryController.js.map +1 -0
  35. package/dist/index.d.ts +24 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +15 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/infiniteQueryOptions.d.ts +21 -0
  40. package/dist/infiniteQueryOptions.d.ts.map +1 -0
  41. package/dist/infiniteQueryOptions.js +22 -0
  42. package/dist/infiniteQueryOptions.js.map +1 -0
  43. package/dist/mutationOptions.d.ts +19 -0
  44. package/dist/mutationOptions.d.ts.map +1 -0
  45. package/dist/mutationOptions.js +20 -0
  46. package/dist/mutationOptions.js.map +1 -0
  47. package/dist/queryOptions.d.ts +62 -0
  48. package/dist/queryOptions.d.ts.map +1 -0
  49. package/dist/queryOptions.js +4 -0
  50. package/dist/queryOptions.js.map +1 -0
  51. package/dist/types.d.ts +31 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +2 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/useIsFetching.d.ts +45 -0
  56. package/dist/useIsFetching.d.ts.map +1 -0
  57. package/dist/useIsFetching.js +116 -0
  58. package/dist/useIsFetching.js.map +1 -0
  59. package/dist/useIsMutating.d.ts +45 -0
  60. package/dist/useIsMutating.d.ts.map +1 -0
  61. package/dist/useIsMutating.js +116 -0
  62. package/dist/useIsMutating.js.map +1 -0
  63. package/dist/useMutationState.d.ts +57 -0
  64. package/dist/useMutationState.d.ts.map +1 -0
  65. package/dist/useMutationState.js +132 -0
  66. package/dist/useMutationState.js.map +1 -0
  67. package/dist-cjs/QueryClientProvider.d.cts +84 -0
  68. package/dist-cjs/QueryClientProvider.js +141 -0
  69. package/dist-cjs/QueryClientProvider.js.map +1 -0
  70. package/dist-cjs/accessor.d.cts +31 -0
  71. package/dist-cjs/accessor.js +16 -0
  72. package/dist-cjs/accessor.js.map +1 -0
  73. package/dist-cjs/context.d.cts +58 -0
  74. package/dist-cjs/context.js +109 -0
  75. package/dist-cjs/context.js.map +1 -0
  76. package/dist-cjs/controllers/BaseController.d.cts +37 -0
  77. package/dist-cjs/controllers/BaseController.js +208 -0
  78. package/dist-cjs/controllers/BaseController.js.map +1 -0
  79. package/dist-cjs/createInfiniteQueryController.d.cts +75 -0
  80. package/dist-cjs/createInfiniteQueryController.js +224 -0
  81. package/dist-cjs/createInfiniteQueryController.js.map +1 -0
  82. package/dist-cjs/createMutationController.d.cts +78 -0
  83. package/dist-cjs/createMutationController.js +196 -0
  84. package/dist-cjs/createMutationController.js.map +1 -0
  85. package/dist-cjs/createQueriesController.d.cts +144 -0
  86. package/dist-cjs/createQueriesController.js +305 -0
  87. package/dist-cjs/createQueriesController.js.map +1 -0
  88. package/dist-cjs/createQueryController.d.cts +66 -0
  89. package/dist-cjs/createQueryController.js +214 -0
  90. package/dist-cjs/createQueryController.js.map +1 -0
  91. package/dist-cjs/index.d.cts +23 -0
  92. package/dist-cjs/index.js +49 -0
  93. package/dist-cjs/index.js.map +1 -0
  94. package/dist-cjs/infiniteQueryOptions.d.cts +20 -0
  95. package/dist-cjs/infiniteQueryOptions.js +25 -0
  96. package/dist-cjs/infiniteQueryOptions.js.map +1 -0
  97. package/dist-cjs/mutationOptions.d.cts +18 -0
  98. package/dist-cjs/mutationOptions.js +23 -0
  99. package/dist-cjs/mutationOptions.js.map +1 -0
  100. package/dist-cjs/package.json +3 -0
  101. package/dist-cjs/queryOptions.d.cts +61 -0
  102. package/dist-cjs/queryOptions.js +7 -0
  103. package/dist-cjs/queryOptions.js.map +1 -0
  104. package/dist-cjs/types.d.cts +30 -0
  105. package/dist-cjs/types.js +3 -0
  106. package/dist-cjs/types.js.map +1 -0
  107. package/dist-cjs/useIsFetching.d.cts +44 -0
  108. package/dist-cjs/useIsFetching.js +119 -0
  109. package/dist-cjs/useIsFetching.js.map +1 -0
  110. package/dist-cjs/useIsMutating.d.cts +44 -0
  111. package/dist-cjs/useIsMutating.js +119 -0
  112. package/dist-cjs/useIsMutating.js.map +1 -0
  113. package/dist-cjs/useMutationState.d.cts +56 -0
  114. package/dist-cjs/useMutationState.js +135 -0
  115. package/dist-cjs/useMutationState.js.map +1 -0
  116. package/package.json +73 -8
  117. package/src/QueryClientProvider.ts +174 -0
  118. package/src/accessor.ts +43 -0
  119. package/src/context.ts +120 -0
  120. package/src/controllers/BaseController.ts +284 -0
  121. package/src/createInfiniteQueryController.ts +394 -0
  122. package/src/createMutationController.ts +361 -0
  123. package/src/createQueriesController.ts +633 -0
  124. package/src/createQueryController.ts +342 -0
  125. package/src/index.ts +72 -0
  126. package/src/infiniteQueryOptions.ts +48 -0
  127. package/src/mutationOptions.ts +31 -0
  128. package/src/queryOptions.ts +143 -0
  129. package/src/types.ts +77 -0
  130. package/src/useIsFetching.ts +159 -0
  131. package/src/useIsMutating.ts +159 -0
  132. package/src/useMutationState.ts +201 -0
@@ -0,0 +1,174 @@
1
+ import { ContextProvider } from '@lit/context'
2
+ import type { QueryClient } from '@tanstack/query-core'
3
+ import type { TemplateResult } from 'lit'
4
+ import { LitElement, html } from 'lit'
5
+ import {
6
+ createMissingQueryClientError,
7
+ queryClientContext,
8
+ registerDefaultQueryClient,
9
+ unregisterDefaultQueryClient,
10
+ } from './context.js'
11
+
12
+ /**
13
+ * Lit element that provides a `QueryClient` to descendant Lit Query
14
+ * controllers through Lit context.
15
+ *
16
+ * The `client` is a property, not an attribute. When rendering this element in
17
+ * a Lit template, bind it with property binding: `.client=${queryClient}`.
18
+ * The provider throws if it connects without a client, or if an already
19
+ * connected provider has its client cleared.
20
+ *
21
+ * This class is not registered as a custom element by the package. Applications
22
+ * must register either a subclass or the class itself with
23
+ * `customElements.define`.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { html, LitElement } from 'lit'
28
+ * import { QueryClient, QueryClientProvider } from '@tanstack/lit-query'
29
+ *
30
+ * const queryClient = new QueryClient()
31
+ *
32
+ * class AppQueryProvider extends QueryClientProvider {
33
+ * constructor() {
34
+ * super()
35
+ * this.client = queryClient
36
+ * }
37
+ * }
38
+ *
39
+ * customElements.define('app-query-provider', AppQueryProvider)
40
+ *
41
+ * class AppRoot extends LitElement {
42
+ * render() {
43
+ * return html`<app-query-provider><todos-view></todos-view></app-query-provider>`
44
+ * }
45
+ * }
46
+ * ```
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * import { html } from 'lit'
51
+ * import { QueryClient, QueryClientProvider } from '@tanstack/lit-query'
52
+ *
53
+ * const queryClient = new QueryClient()
54
+ *
55
+ * customElements.define('query-client-provider', QueryClientProvider)
56
+ *
57
+ * const view = html`
58
+ * <query-client-provider .client=${queryClient}>
59
+ * <todos-view></todos-view>
60
+ * </query-client-provider>
61
+ * `
62
+ * ```
63
+ */
64
+ export class QueryClientProvider extends LitElement {
65
+ /** @internal */
66
+ static properties = {
67
+ client: { attribute: false },
68
+ }
69
+
70
+ /**
71
+ * The `QueryClient` provided to descendant controllers and global fallback
72
+ * helpers while this provider is connected.
73
+ *
74
+ * Bind this as a property in Lit templates with `.client=${queryClient}`.
75
+ */
76
+ declare client: QueryClient
77
+
78
+ private readonly contextProvider: ContextProvider<typeof queryClientContext>
79
+
80
+ private mountedClient: QueryClient | undefined
81
+
82
+ constructor() {
83
+ super()
84
+ this.contextProvider = new ContextProvider(this, {
85
+ context: queryClientContext,
86
+ })
87
+ }
88
+
89
+ /** @internal */
90
+ connectedCallback(): void {
91
+ super.connectedCallback()
92
+ const client = this.requireClient()
93
+ this.contextProvider.setValue(client)
94
+ this.mountClient(client)
95
+ }
96
+
97
+ /** @internal */
98
+ disconnectedCallback(): void {
99
+ this.unmountClient(this.mountedClient)
100
+ super.disconnectedCallback()
101
+ }
102
+
103
+ /** @internal */
104
+ protected willUpdate(changedProperties: Map<PropertyKey, unknown>): void {
105
+ if (!changedProperties.has('client')) {
106
+ return
107
+ }
108
+
109
+ const nextClient = this.client
110
+ if (!nextClient) {
111
+ if (this.isConnected) {
112
+ this.unmountClient(this.mountedClient)
113
+ // Sentinel: notify active consumers that the provider is now unbound.
114
+ this.contextProvider.setValue(undefined as unknown as QueryClient)
115
+ throw createMissingQueryClientError()
116
+ }
117
+
118
+ return
119
+ }
120
+
121
+ const previousClient = changedProperties.get('client') as
122
+ | QueryClient
123
+ | undefined
124
+ if (previousClient && previousClient !== nextClient && this.isConnected) {
125
+ this.unmountClient(previousClient)
126
+ }
127
+
128
+ this.contextProvider.setValue(nextClient)
129
+
130
+ if (this.isConnected) {
131
+ this.mountClient(nextClient)
132
+ }
133
+ }
134
+
135
+ /** @internal */
136
+ render(): TemplateResult {
137
+ return html`<slot></slot>`
138
+ }
139
+
140
+ private mountClient(client: QueryClient): void {
141
+ if (this.mountedClient === client) {
142
+ return
143
+ }
144
+
145
+ if (this.mountedClient) {
146
+ this.unmountClient(this.mountedClient)
147
+ }
148
+
149
+ client.mount()
150
+ registerDefaultQueryClient(client)
151
+ this.mountedClient = client
152
+ }
153
+
154
+ private unmountClient(client?: QueryClient): void {
155
+ if (!client) {
156
+ return
157
+ }
158
+
159
+ client.unmount()
160
+ unregisterDefaultQueryClient(client)
161
+
162
+ if (this.mountedClient === client) {
163
+ this.mountedClient = undefined
164
+ }
165
+ }
166
+
167
+ private requireClient(): QueryClient {
168
+ if (!this.client) {
169
+ throw createMissingQueryClientError()
170
+ }
171
+
172
+ return this.client
173
+ }
174
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * A value that can be passed directly or read from a zero-argument getter.
3
+ *
4
+ * Lit Query APIs read function accessors during host updates, so the getter can
5
+ * depend on reactive host state.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const staticKey: Accessor<readonly unknown[]> = ['todos']
10
+ * const reactiveKey: Accessor<readonly unknown[]> = () => ['todos', this.userId]
11
+ * ```
12
+ */
13
+ export type Accessor<T> = T | (() => T)
14
+
15
+ export function readAccessor<T>(value: Accessor<T>): T {
16
+ return typeof value === 'function' ? (value as () => T)() : value
17
+ }
18
+
19
+ /**
20
+ * A callable accessor with a `current` property for reading the latest
21
+ * controller result.
22
+ *
23
+ * Controller creators and cache state helpers return this shape so render code
24
+ * can use either `result()` or `result.current`.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const query = this.todos()
29
+ * const sameQuery = this.todos.current
30
+ * ```
31
+ */
32
+ export type ValueAccessor<T> = (() => T) & {
33
+ readonly current: T
34
+ }
35
+
36
+ export function createValueAccessor<T>(getter: () => T): ValueAccessor<T> {
37
+ const accessor = (() => getter()) as ValueAccessor<T>
38
+ Object.defineProperty(accessor, 'current', {
39
+ get: getter,
40
+ enumerable: true,
41
+ })
42
+ return accessor
43
+ }
package/src/context.ts ADDED
@@ -0,0 +1,120 @@
1
+ import { createContext } from '@lit/context'
2
+ import type { QueryClient } from '@tanstack/query-core'
3
+
4
+ /**
5
+ * Lit context key used by `QueryClientProvider` and host-bound APIs to share a
6
+ * `QueryClient` through the DOM tree.
7
+ *
8
+ * Most applications use `QueryClientProvider` instead of interacting with this
9
+ * context directly.
10
+ */
11
+ export const queryClientContext = createContext<QueryClient>(
12
+ Symbol.for('tanstack-query-client'),
13
+ )
14
+
15
+ const missingQueryClientMessage =
16
+ 'No QueryClient available. Pass one explicitly or render within QueryClientProvider.'
17
+ const ambiguousQueryClientMessage =
18
+ 'Multiple QueryClients are mounted. Pass one explicitly instead of relying on global QueryClient helpers.'
19
+
20
+ const registeredClients = new Map<QueryClient, number>()
21
+ let defaultClient: QueryClient | undefined
22
+
23
+ /**
24
+ * Registers a `QueryClient` as a process-local fallback for APIs that resolve a
25
+ * client without an explicit argument.
26
+ *
27
+ * `QueryClientProvider` calls this automatically while it is connected. Prefer
28
+ * passing an explicit client or rendering under a provider when possible.
29
+ *
30
+ * @param client - The query client to register as the current default.
31
+ */
32
+ export function registerDefaultQueryClient(client: QueryClient): void {
33
+ registeredClients.set(client, (registeredClients.get(client) ?? 0) + 1)
34
+ defaultClient = client
35
+ }
36
+
37
+ /**
38
+ * Unregisters a client previously registered with
39
+ * `registerDefaultQueryClient`.
40
+ *
41
+ * `QueryClientProvider` calls this automatically when it disconnects.
42
+ *
43
+ * @param client - The query client registration to release.
44
+ */
45
+ export function unregisterDefaultQueryClient(client: QueryClient): void {
46
+ const count = registeredClients.get(client)
47
+ if (count === undefined) {
48
+ return
49
+ }
50
+
51
+ if (count > 1) {
52
+ registeredClients.set(client, count - 1)
53
+ return
54
+ }
55
+
56
+ registeredClients.delete(client)
57
+ if (defaultClient !== client) {
58
+ return
59
+ }
60
+
61
+ const remaining = [...registeredClients.keys()]
62
+ defaultClient = remaining.at(-1)
63
+ }
64
+
65
+ /**
66
+ * Returns the registered default `QueryClient`, if exactly one default client is
67
+ * available.
68
+ *
69
+ * @returns The default query client, or `undefined` when there is no registered
70
+ * client or more than one registered client.
71
+ */
72
+ export function getDefaultQueryClient(): QueryClient | undefined {
73
+ if (registeredClients.size > 1) {
74
+ return undefined
75
+ }
76
+
77
+ return defaultClient
78
+ }
79
+
80
+ export function createMissingQueryClientError(): Error {
81
+ return new Error(missingQueryClientMessage)
82
+ }
83
+
84
+ function createAmbiguousQueryClientError(): Error {
85
+ return new Error(ambiguousQueryClientMessage)
86
+ }
87
+
88
+ /**
89
+ * Resolves the current default `QueryClient` registered by a connected
90
+ * `QueryClientProvider`.
91
+ *
92
+ * This helper is useful outside a Lit reactive controller when a single
93
+ * provider is mounted. It throws if no client is registered or if multiple
94
+ * clients are mounted and the default would be ambiguous.
95
+ *
96
+ * @returns The single registered query client.
97
+ */
98
+ export function useQueryClient(): QueryClient {
99
+ const client = getDefaultQueryClient()
100
+ if (client) {
101
+ return client
102
+ }
103
+
104
+ if (registeredClients.size > 1) {
105
+ throw createAmbiguousQueryClientError()
106
+ }
107
+
108
+ throw createMissingQueryClientError()
109
+ }
110
+
111
+ /**
112
+ * Resolves an explicit `QueryClient` or falls back to `useQueryClient`.
113
+ *
114
+ * @param explicit - Optional client supplied by the caller.
115
+ * @returns The explicit client when provided, otherwise the current default
116
+ * client.
117
+ */
118
+ export function resolveQueryClient(explicit?: QueryClient): QueryClient {
119
+ return explicit ?? useQueryClient()
120
+ }
@@ -0,0 +1,284 @@
1
+ import { ContextEvent } from '@lit/context'
2
+ import type { QueryClient } from '@tanstack/query-core'
3
+ import type { ReactiveController, ReactiveControllerHost } from 'lit'
4
+ import {
5
+ createMissingQueryClientError,
6
+ queryClientContext,
7
+ } from '../context.js'
8
+
9
+ type QueryClientResolutionState =
10
+ | 'pre-connect'
11
+ | 'awaiting-context'
12
+ | 'bound'
13
+ | 'missing'
14
+
15
+ export abstract class BaseController<TResult> implements ReactiveController {
16
+ protected result: TResult
17
+
18
+ private readonly explicitClient?: QueryClient
19
+ private contextClient: QueryClient | undefined
20
+ private contextUnsubscribe: (() => void) | undefined
21
+
22
+ private connected = false
23
+ private destroyed = false
24
+ private updateQueued = false
25
+ private clientChangeQueued = false
26
+ private connectionAttempt = 0
27
+ private queryClientResolutionState: QueryClientResolutionState
28
+
29
+ protected constructor(
30
+ protected readonly host: ReactiveControllerHost,
31
+ initialResult: TResult,
32
+ queryClient?: QueryClient,
33
+ ) {
34
+ this.explicitClient = queryClient
35
+ this.result = initialResult
36
+ this.queryClientResolutionState = queryClient ? 'bound' : 'pre-connect'
37
+
38
+ host.addController(this)
39
+ }
40
+
41
+ hostConnected(): void {
42
+ if (this.connected || this.destroyed) {
43
+ return
44
+ }
45
+
46
+ this.connected = true
47
+ let contextResolutionAttempt: number | undefined
48
+
49
+ if (this.explicitClient) {
50
+ this.queryClientResolutionState = 'bound'
51
+ } else {
52
+ contextResolutionAttempt = ++this.connectionAttempt
53
+ this.beginContextResolution()
54
+ }
55
+
56
+ // Defer onConnected to ensure subclass constructors complete before
57
+ // lifecycle callbacks access subclass state. This handles the case where
58
+ // addController is called on an already-connected host (e.g., during
59
+ // willUpdate), which synchronously triggers hostConnected before
60
+ // subclass field initialization.
61
+ queueMicrotask(() => {
62
+ if (this.connected && !this.destroyed) {
63
+ this.onConnected()
64
+ }
65
+ })
66
+
67
+ if (contextResolutionAttempt !== undefined) {
68
+ // Provider-backed controllers on already-connected hosts should finish
69
+ // their deferred onConnected pass before a context client binds.
70
+ this.queueContextResolution(contextResolutionAttempt)
71
+ }
72
+ }
73
+
74
+ hostDisconnected(): void {
75
+ if (!this.connected) {
76
+ return
77
+ }
78
+
79
+ this.connected = false
80
+
81
+ if (!this.explicitClient) {
82
+ this.connectionAttempt += 1
83
+ this.clearContextClient()
84
+ this.updateQueryClientResolutionState('pre-connect')
85
+ }
86
+
87
+ this.onDisconnected()
88
+ }
89
+
90
+ hostUpdate(): void {
91
+ if (this.destroyed) {
92
+ return
93
+ }
94
+
95
+ this.onHostUpdate()
96
+ }
97
+
98
+ destroy(): void {
99
+ if (this.destroyed) {
100
+ return
101
+ }
102
+
103
+ this.destroyed = true
104
+ this.connected = false
105
+ this.connectionAttempt += 1
106
+ this.clearContextClient()
107
+ this.queryClientResolutionState = this.explicitClient
108
+ ? 'bound'
109
+ : 'pre-connect'
110
+ this.onDisconnected()
111
+
112
+ if ('removeController' in this.host) {
113
+ this.host.removeController(this)
114
+ }
115
+ }
116
+
117
+ protected tryGetQueryClient(): QueryClient | undefined {
118
+ return this.explicitClient ?? this.contextClient
119
+ }
120
+
121
+ protected getQueryClient(): QueryClient {
122
+ const client = this.tryGetQueryClient()
123
+ if (!client) {
124
+ throw createMissingQueryClientError()
125
+ }
126
+
127
+ return client
128
+ }
129
+
130
+ protected setResult(next: TResult): void {
131
+ if (Object.is(this.result, next)) {
132
+ return
133
+ }
134
+
135
+ this.result = next
136
+ this.queueUpdate()
137
+ }
138
+
139
+ get current(): TResult {
140
+ if (this.queryClientResolutionState === 'missing') {
141
+ throw createMissingQueryClientError()
142
+ }
143
+
144
+ return this.result
145
+ }
146
+
147
+ protected get connectedState(): boolean {
148
+ return this.connected
149
+ }
150
+
151
+ protected queueUpdate(): void {
152
+ if (this.updateQueued) {
153
+ return
154
+ }
155
+
156
+ this.updateQueued = true
157
+ queueMicrotask(() => {
158
+ this.updateQueued = false
159
+ if (!this.destroyed) {
160
+ this.host.requestUpdate()
161
+ }
162
+ })
163
+ }
164
+
165
+ private queueQueryClientChanged(): void {
166
+ if (this.clientChangeQueued) {
167
+ return
168
+ }
169
+
170
+ this.clientChangeQueued = true
171
+ queueMicrotask(() => {
172
+ this.clientChangeQueued = false
173
+ if (!this.destroyed) {
174
+ this.onQueryClientChanged()
175
+ }
176
+ })
177
+ }
178
+
179
+ private beginContextResolution(): void {
180
+ this.clearContextClient()
181
+ this.updateQueryClientResolutionState('awaiting-context')
182
+ }
183
+
184
+ private queueContextResolution(attempt: number): void {
185
+ queueMicrotask(() => {
186
+ if (
187
+ this.destroyed ||
188
+ !this.connected ||
189
+ attempt !== this.connectionAttempt ||
190
+ this.queryClientResolutionState !== 'awaiting-context'
191
+ ) {
192
+ return
193
+ }
194
+
195
+ this.dispatchContextRequest(attempt)
196
+ this.queueInitialContextResolutionCompletion(attempt)
197
+ })
198
+ }
199
+
200
+ private dispatchContextRequest(attempt: number): void {
201
+ if (!('dispatchEvent' in this.host)) {
202
+ return
203
+ }
204
+
205
+ const contextTarget = this.host as ReactiveControllerHost & EventTarget
206
+ contextTarget.dispatchEvent(
207
+ new ContextEvent(
208
+ queryClientContext,
209
+ contextTarget as unknown as Element,
210
+ (value, unsubscribe) => {
211
+ if (
212
+ this.destroyed ||
213
+ !this.connected ||
214
+ attempt !== this.connectionAttempt
215
+ ) {
216
+ unsubscribe?.()
217
+ return
218
+ }
219
+
220
+ if (
221
+ this.contextUnsubscribe &&
222
+ this.contextUnsubscribe !== unsubscribe
223
+ ) {
224
+ this.contextUnsubscribe()
225
+ }
226
+
227
+ const resolutionChanged = this.updateQueryClientResolutionState(
228
+ value === undefined ? 'missing' : 'bound',
229
+ )
230
+ const clientChanged = this.contextClient !== value
231
+
232
+ this.contextClient = value
233
+ this.contextUnsubscribe = unsubscribe
234
+
235
+ if (resolutionChanged || clientChanged) {
236
+ this.queueUpdate()
237
+ this.queueQueryClientChanged()
238
+ }
239
+ },
240
+ true,
241
+ ),
242
+ )
243
+ }
244
+
245
+ private queueInitialContextResolutionCompletion(attempt: number): void {
246
+ queueMicrotask(() => {
247
+ if (
248
+ this.destroyed ||
249
+ !this.connected ||
250
+ attempt !== this.connectionAttempt ||
251
+ this.queryClientResolutionState !== 'awaiting-context'
252
+ ) {
253
+ return
254
+ }
255
+
256
+ if (this.updateQueryClientResolutionState('missing')) {
257
+ this.queueUpdate()
258
+ this.queueQueryClientChanged()
259
+ }
260
+ })
261
+ }
262
+
263
+ private clearContextClient(): void {
264
+ this.contextUnsubscribe?.()
265
+ this.contextUnsubscribe = undefined
266
+ this.contextClient = undefined
267
+ }
268
+
269
+ private updateQueryClientResolutionState(
270
+ nextState: QueryClientResolutionState,
271
+ ): boolean {
272
+ if (this.queryClientResolutionState === nextState) {
273
+ return false
274
+ }
275
+
276
+ this.queryClientResolutionState = nextState
277
+ return true
278
+ }
279
+
280
+ protected abstract onConnected(): void
281
+ protected abstract onDisconnected(): void
282
+ protected abstract onHostUpdate(): void
283
+ protected abstract onQueryClientChanged(): void
284
+ }