teamplay 0.4.0 → 0.5.0-alpha.1

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 (237) hide show
  1. package/babel-loader.cjs +1 -0
  2. package/babel.cjs +1 -0
  3. package/dist/connect/index.d.ts +1 -0
  4. package/dist/connect/index.js +9 -0
  5. package/dist/connect/lib/sharedb-crosstab-pubsub.d.cts +10 -0
  6. package/dist/connect/offline/index.d.ts +1 -0
  7. package/dist/connect/offline/index.js +126 -0
  8. package/dist/connect/offline/react-native.d.ts +10 -0
  9. package/dist/connect/offline/react-native.js +25 -0
  10. package/dist/connect/offline/web.d.ts +9 -0
  11. package/dist/connect/offline/web.js +12 -0
  12. package/dist/connect/sharedbConnection.d.cts +2 -0
  13. package/dist/connect/test.d.ts +1 -0
  14. package/dist/connect/test.js +12 -0
  15. package/dist/index.d.ts +66 -0
  16. package/dist/index.js +74 -0
  17. package/dist/orm/$.d.ts +9 -0
  18. package/dist/orm/$.js +34 -0
  19. package/dist/orm/Aggregation.d.ts +17 -0
  20. package/dist/orm/Aggregation.js +115 -0
  21. package/dist/orm/Cache.d.ts +9 -0
  22. package/dist/orm/Cache.js +32 -0
  23. package/dist/orm/Compat/SignalCompat.d.ts +3 -0
  24. package/dist/orm/Compat/SignalCompat.js +1542 -0
  25. package/dist/orm/Compat/eventsCompat.d.ts +3 -0
  26. package/dist/orm/Compat/eventsCompat.js +73 -0
  27. package/dist/orm/Compat/hooksCompat.d.ts +33 -0
  28. package/dist/orm/Compat/hooksCompat.js +360 -0
  29. package/dist/orm/Compat/modelEvents.d.ts +6 -0
  30. package/dist/orm/Compat/modelEvents.js +228 -0
  31. package/dist/orm/Compat/queryReadiness.d.ts +5 -0
  32. package/dist/orm/Compat/queryReadiness.js +185 -0
  33. package/dist/orm/Compat/refFallback.d.ts +13 -0
  34. package/dist/orm/Compat/refFallback.js +65 -0
  35. package/dist/orm/Compat/refRegistry.d.ts +6 -0
  36. package/dist/orm/Compat/refRegistry.js +54 -0
  37. package/dist/orm/Compat/silentContext.d.ts +5 -0
  38. package/dist/orm/Compat/silentContext.js +48 -0
  39. package/dist/orm/Compat/startStopCompat.d.ts +3 -0
  40. package/dist/orm/Compat/startStopCompat.js +217 -0
  41. package/dist/orm/Doc.d.ts +96 -0
  42. package/dist/orm/Doc.js +966 -0
  43. package/dist/orm/Query.d.ts +117 -0
  44. package/dist/orm/Query.js +1111 -0
  45. package/dist/orm/Reaction.d.ts +10 -0
  46. package/dist/orm/Reaction.js +41 -0
  47. package/dist/orm/Root.d.ts +22 -0
  48. package/dist/orm/Root.js +85 -0
  49. package/dist/orm/Signal.d.ts +12 -0
  50. package/dist/orm/Signal.js +7 -0
  51. package/dist/orm/SignalBase.d.ts +168 -0
  52. package/dist/orm/SignalBase.js +625 -0
  53. package/dist/orm/SubscriptionState.d.ts +17 -0
  54. package/dist/orm/SubscriptionState.js +123 -0
  55. package/dist/orm/Value.d.ts +9 -0
  56. package/dist/orm/Value.js +25 -0
  57. package/dist/orm/addModel.d.ts +6 -0
  58. package/dist/orm/addModel.js +39 -0
  59. package/dist/orm/associations.d.ts +18 -0
  60. package/dist/orm/associations.js +70 -0
  61. package/dist/orm/batchScheduler.d.ts +7 -0
  62. package/dist/orm/batchScheduler.js +62 -0
  63. package/dist/orm/compatEnv.d.ts +1 -0
  64. package/dist/orm/compatEnv.js +4 -0
  65. package/dist/orm/connection.d.ts +26 -0
  66. package/dist/orm/connection.js +38 -0
  67. package/dist/orm/dataTree.d.ts +34 -0
  68. package/dist/orm/dataTree.js +880 -0
  69. package/dist/orm/disposeRootContext.d.ts +4 -0
  70. package/dist/orm/disposeRootContext.js +59 -0
  71. package/dist/orm/getSignal.d.ts +16 -0
  72. package/dist/orm/getSignal.js +133 -0
  73. package/dist/orm/idFields.d.ts +14 -0
  74. package/dist/orm/idFields.js +95 -0
  75. package/dist/orm/index.d.ts +7 -0
  76. package/dist/orm/index.js +6 -0
  77. package/dist/orm/initModels.d.ts +5 -0
  78. package/dist/orm/initModels.js +74 -0
  79. package/dist/orm/missingDoc.d.ts +1 -0
  80. package/dist/orm/missingDoc.js +3 -0
  81. package/dist/orm/pluralize.d.ts +12 -0
  82. package/dist/orm/privateData.d.ts +22 -0
  83. package/dist/orm/privateData.js +170 -0
  84. package/dist/orm/rootContext.d.ts +78 -0
  85. package/dist/orm/rootContext.js +297 -0
  86. package/dist/orm/rootScope.d.ts +12 -0
  87. package/dist/orm/rootScope.js +46 -0
  88. package/dist/orm/signalArrayReaders.d.ts +22 -0
  89. package/dist/orm/signalArrayReaders.js +42 -0
  90. package/dist/orm/signalMetadata.d.ts +17 -0
  91. package/dist/orm/signalMetadata.js +56 -0
  92. package/dist/orm/signalMutationGuards.d.ts +4 -0
  93. package/dist/orm/signalMutationGuards.js +14 -0
  94. package/dist/orm/signalPathKind.d.ts +2 -0
  95. package/dist/orm/signalPathKind.js +10 -0
  96. package/dist/orm/signalPathRules.d.ts +6 -0
  97. package/dist/orm/signalPathRules.js +28 -0
  98. package/dist/orm/signalReads.d.ts +26 -0
  99. package/dist/orm/signalReads.js +64 -0
  100. package/dist/orm/signalRuntimeAccess.d.ts +16 -0
  101. package/dist/orm/signalRuntimeAccess.js +24 -0
  102. package/dist/orm/signalRuntimeDescriptor.d.ts +19 -0
  103. package/dist/orm/signalRuntimeDescriptor.js +97 -0
  104. package/dist/orm/signalStorageMutations.d.ts +18 -0
  105. package/dist/orm/signalStorageMutations.js +26 -0
  106. package/dist/orm/signalSymbols.d.ts +5 -0
  107. package/dist/orm/signalSymbols.js +5 -0
  108. package/dist/orm/signalValueMutations.d.ts +14 -0
  109. package/dist/orm/signalValueMutations.js +36 -0
  110. package/dist/orm/sub.d.ts +37 -0
  111. package/dist/orm/sub.js +187 -0
  112. package/dist/orm/subscriptionGcDelay.d.ts +4 -0
  113. package/dist/orm/subscriptionGcDelay.js +26 -0
  114. package/dist/orm/types/baseMethods.d.ts +43 -0
  115. package/dist/orm/types/baseMethods.js +1 -0
  116. package/dist/orm/types/jsonSchema.d.ts +75 -0
  117. package/dist/orm/types/jsonSchema.js +1 -0
  118. package/dist/orm/types/modelManifest.d.ts +37 -0
  119. package/dist/orm/types/modelManifest.js +1 -0
  120. package/dist/orm/types/path.d.ts +8 -0
  121. package/dist/orm/types/path.js +1 -0
  122. package/dist/orm/types/query.d.ts +29 -0
  123. package/dist/orm/types/query.js +1 -0
  124. package/dist/orm/types/signal.d.ts +132 -0
  125. package/dist/orm/types/signal.js +1 -0
  126. package/dist/react/compatComponentRegistry.d.ts +4 -0
  127. package/dist/react/compatComponentRegistry.js +19 -0
  128. package/dist/react/convertToObserver.d.ts +26 -0
  129. package/dist/react/convertToObserver.js +114 -0
  130. package/dist/react/executionContextTracker.d.ts +11 -0
  131. package/dist/react/executionContextTracker.js +28 -0
  132. package/dist/react/helpers.d.ts +34 -0
  133. package/dist/react/helpers.js +134 -0
  134. package/dist/react/observer.d.ts +22 -0
  135. package/dist/react/observer.js +8 -0
  136. package/dist/react/promiseBatcher.d.ts +13 -0
  137. package/dist/react/promiseBatcher.js +118 -0
  138. package/dist/react/renderAttemptDestroyer.d.ts +19 -0
  139. package/dist/react/renderAttemptDestroyer.js +46 -0
  140. package/dist/react/trapRender.d.ts +6 -0
  141. package/dist/react/trapRender.js +48 -0
  142. package/dist/react/universal$.d.ts +1 -0
  143. package/dist/react/universal$.js +18 -0
  144. package/dist/react/universalSub.d.ts +1 -0
  145. package/dist/react/universalSub.js +22 -0
  146. package/dist/react/useApi.d.ts +13 -0
  147. package/dist/react/useApi.js +67 -0
  148. package/dist/react/useSub.d.ts +102 -0
  149. package/dist/react/useSub.js +189 -0
  150. package/dist/react/useSuspendMemo.d.ts +3 -0
  151. package/dist/react/useSuspendMemo.js +102 -0
  152. package/dist/react/wrapIntoSuspense.d.ts +9 -0
  153. package/dist/react/wrapIntoSuspense.js +102 -0
  154. package/dist/server.d.ts +11 -0
  155. package/dist/server.js +28 -0
  156. package/dist/utils/MockFinalizationRegistry.d.ts +37 -0
  157. package/dist/utils/MockFinalizationRegistry.js +85 -0
  158. package/dist/utils/MockWeakRef.d.ts +12 -0
  159. package/dist/utils/MockWeakRef.js +23 -0
  160. package/dist/utils/isServer.d.ts +3 -0
  161. package/dist/utils/isServer.js +18 -0
  162. package/dist/utils/setDiffDeep.d.ts +1 -0
  163. package/dist/utils/setDiffDeep.js +61 -0
  164. package/dist/utils/useIsomorphicLayoutEffect.d.ts +3 -0
  165. package/dist/utils/useIsomorphicLayoutEffect.js +3 -0
  166. package/file-based-models.js +3 -0
  167. package/file-based-models.node.js +6 -0
  168. package/package.json +77 -32
  169. package/teamplay.models.auto-init.virtual.js +1 -0
  170. package/teamplay.models.virtual.js +1 -0
  171. package/connect/index.js +0 -9
  172. package/connect/offline/index.js +0 -124
  173. package/connect/offline/react-native.js +0 -28
  174. package/connect/offline/web.js +0 -15
  175. package/connect/test.js +0 -12
  176. package/index.d.ts +0 -106
  177. package/index.js +0 -125
  178. package/orm/$.js +0 -38
  179. package/orm/Aggregation.js +0 -117
  180. package/orm/Cache.js +0 -46
  181. package/orm/Compat/README.md +0 -1064
  182. package/orm/Compat/SignalCompat.js +0 -1479
  183. package/orm/Compat/eventsCompat.js +0 -79
  184. package/orm/Compat/hooksCompat.js +0 -374
  185. package/orm/Compat/modelEvents.js +0 -225
  186. package/orm/Compat/queryReadiness.js +0 -191
  187. package/orm/Compat/refFallback.js +0 -62
  188. package/orm/Compat/refRegistry.js +0 -61
  189. package/orm/Compat/silentContext.js +0 -51
  190. package/orm/Compat/startStopCompat.js +0 -207
  191. package/orm/Doc.js +0 -969
  192. package/orm/Query.js +0 -1127
  193. package/orm/Reaction.js +0 -48
  194. package/orm/Root.js +0 -88
  195. package/orm/Signal.js +0 -22
  196. package/orm/SignalBase.js +0 -696
  197. package/orm/SubscriptionState.js +0 -138
  198. package/orm/Value.js +0 -30
  199. package/orm/addModel.js +0 -31
  200. package/orm/associations.js +0 -97
  201. package/orm/batchScheduler.js +0 -62
  202. package/orm/compatEnv.js +0 -4
  203. package/orm/connection.js +0 -46
  204. package/orm/dataTree.js +0 -869
  205. package/orm/disposeRootContext.js +0 -68
  206. package/orm/getSignal.js +0 -130
  207. package/orm/idFields.js +0 -88
  208. package/orm/index.d.ts +0 -6
  209. package/orm/index.js +0 -5
  210. package/orm/missingDoc.js +0 -3
  211. package/orm/privateData.js +0 -181
  212. package/orm/rootContext.js +0 -313
  213. package/orm/rootScope.js +0 -51
  214. package/orm/sub.js +0 -151
  215. package/orm/subscriptionGcDelay.js +0 -32
  216. package/react/compatComponentRegistry.js +0 -20
  217. package/react/convertToObserver.js +0 -117
  218. package/react/executionContextTracker.js +0 -32
  219. package/react/helpers.js +0 -141
  220. package/react/observer.js +0 -9
  221. package/react/promiseBatcher.js +0 -115
  222. package/react/renderAttemptDestroyer.js +0 -47
  223. package/react/trapRender.js +0 -50
  224. package/react/universal$.js +0 -18
  225. package/react/universalSub.js +0 -21
  226. package/react/useApi.js +0 -63
  227. package/react/useSub.js +0 -169
  228. package/react/useSuspendMemo.js +0 -96
  229. package/react/wrapIntoSuspense.js +0 -119
  230. package/server.js +0 -31
  231. package/utils/MockFinalizationRegistry.js +0 -94
  232. package/utils/MockWeakRef.js +0 -25
  233. package/utils/isServer.js +0 -17
  234. package/utils/setDiffDeep.js +0 -58
  235. package/utils/useIsomorphicLayoutEffect.js +0 -4
  236. /package/{connect → dist/connect}/lib/sharedb-crosstab-pubsub.cjs +0 -0
  237. /package/{connect → dist/connect}/sharedbConnection.cjs +0 -0
@@ -0,0 +1,1111 @@
1
+ import { raw } from '@nx-js/observer-util';
2
+ import { set as _set, getRaw } from './dataTree.js';
3
+ import getSignal from "./getSignal.js";
4
+ import { getConnection } from "./connection.js";
5
+ import { emitModelChange, isModelEventsEnabled } from './Compat/modelEvents.js';
6
+ import { isCompatEnv } from './compatEnv.js';
7
+ import { docSubscriptions } from './Doc.js';
8
+ import FinalizationRegistry from "../utils/MockFinalizationRegistry.js";
9
+ import SubscriptionState from './SubscriptionState.js';
10
+ import { getIdFieldsForSegments, injectIdFields, isPlainObject } from "./idFields.js";
11
+ import { getSubscriptionGcDelay } from "./subscriptionGcDelay.js";
12
+ import { getScopedSignalHash, normalizeRootId } from "./rootScope.js";
13
+ import { getRoot, ROOT_ID, getRootTransportMode } from "./Root.js";
14
+ import { registerRootOwnedRuntime, unregisterRootOwnedRuntime } from "./rootContext.js";
15
+ import { setSignalRuntimeDescriptor } from "./signalRuntimeDescriptor.js";
16
+ import { delPrivateData, getPrivateData, setPrivateData } from './privateData.js';
17
+ const ERROR_ON_EXCESSIVE_UNSUBSCRIBES = false;
18
+ export const COLLECTION_NAME = Symbol('query collection name');
19
+ export const PARAMS = Symbol('query params');
20
+ export const HASH = Symbol('query hash');
21
+ export const IS_QUERY = Symbol('is query signal');
22
+ export const QUERIES = '$queries';
23
+ export class Query {
24
+ initialized;
25
+ shareQuery;
26
+ constructor(collectionName, params, { hash = hashQuery(collectionName, params) } = {}) {
27
+ this.collectionName = collectionName;
28
+ this.params = params;
29
+ this.hash = hash;
30
+ this.rootIds = new Set();
31
+ this.docSignals = new Set();
32
+ this.lifecycle = new SubscriptionState({
33
+ onSubscribe: () => this._subscribe(),
34
+ onUnsubscribe: () => this._unsubscribe()
35
+ });
36
+ this.requestedTransportMode = 'subscribe';
37
+ this.activeTransportMode = 'idle';
38
+ }
39
+ get subscribed() {
40
+ return this.activeTransportMode !== 'idle' || this.lifecycle.subscribed;
41
+ }
42
+ init() {
43
+ if (this.initialized)
44
+ return;
45
+ this.initialized = true;
46
+ this._initData();
47
+ }
48
+ async subscribe({ mode } = {}) {
49
+ if (mode)
50
+ this.requestedTransportMode = mode;
51
+ await this.lifecycle.subscribe();
52
+ this.init();
53
+ }
54
+ async unsubscribe() {
55
+ await this.lifecycle.unsubscribe();
56
+ if (!this.subscribed) {
57
+ this.initialized = undefined;
58
+ this._detachTransportData({ keepRoots: false });
59
+ }
60
+ }
61
+ attachRoot(rootId) {
62
+ if (rootId == null)
63
+ return;
64
+ if (this.rootIds.has(rootId))
65
+ return;
66
+ this.rootIds.add(rootId);
67
+ if (this.initialized)
68
+ this._syncRootData(rootId);
69
+ }
70
+ detachRoot(rootId) {
71
+ if (rootId == null)
72
+ return;
73
+ if (!this.rootIds.delete(rootId))
74
+ return;
75
+ this._removeRootData(rootId);
76
+ }
77
+ async _subscribe() {
78
+ const mode = this.requestedTransportMode;
79
+ await new Promise((resolve, reject) => {
80
+ const method = mode === 'fetch' ? 'createFetchQuery' : 'createSubscribeQuery';
81
+ this.shareQuery = getConnection()[method](this.collectionName, this.params, {}, err => {
82
+ if (err)
83
+ return reject(err);
84
+ this.activeTransportMode = mode;
85
+ resolve();
86
+ });
87
+ });
88
+ }
89
+ async _unsubscribe() {
90
+ if (!this.shareQuery) {
91
+ this.activeTransportMode = 'idle';
92
+ return;
93
+ }
94
+ await new Promise((resolve, reject) => {
95
+ this.shareQuery.destroy(err => {
96
+ if (err)
97
+ return reject(err);
98
+ this.activeTransportMode = 'idle';
99
+ resolve();
100
+ });
101
+ this.shareQuery = undefined;
102
+ });
103
+ }
104
+ _initData() {
105
+ // reference fetched docs once per transport query
106
+ maybeMaterializeQueryDocsToCollection(this.collectionName, this.shareQuery.results);
107
+ const ids = this.shareQuery.results.map(doc => doc.id);
108
+ for (const docId of ids) {
109
+ const $doc = getSignal(undefined, [this.collectionName, docId]);
110
+ docSubscriptions.retain($doc);
111
+ this.docSignals.add($doc);
112
+ }
113
+ this._syncAllRootsData();
114
+ this.shareQuery.on('insert', (shareDocs, index) => {
115
+ maybeMaterializeQueryDocsToCollection(this.collectionName, shareDocs);
116
+ const newDocs = this._mapShareDocsToRaw(shareDocs);
117
+ const ids = shareDocs.map(doc => doc.id);
118
+ for (const docId of ids) {
119
+ const $doc = getSignal(undefined, [this.collectionName, docId]);
120
+ docSubscriptions.retain($doc);
121
+ this.docSignals.add($doc);
122
+ }
123
+ this._forEachRoot(rootId => {
124
+ const docs = getPrivateData(rootId, [QUERIES, this.hash, 'docs']);
125
+ const idsState = getPrivateData(rootId, [QUERIES, this.hash, 'ids']);
126
+ if (!Array.isArray(docs) || !Array.isArray(idsState))
127
+ return;
128
+ docs.splice(index, 0, ...newDocs);
129
+ idsState.splice(index, 0, ...ids);
130
+ if (!isModelEventsEnabled())
131
+ return;
132
+ const docsPath = [QUERIES, this.hash, 'docs'];
133
+ const idsPath = [QUERIES, this.hash, 'ids'];
134
+ for (let i = 0; i < newDocs.length; i++) {
135
+ emitModelChange(rootId, docsPath.concat(index + i), newDocs[i], undefined, {
136
+ op: 'queryInsert',
137
+ index: index + i
138
+ });
139
+ }
140
+ for (let i = 0; i < ids.length; i++) {
141
+ emitModelChange(rootId, idsPath.concat(index + i), ids[i], undefined, {
142
+ op: 'queryInsert',
143
+ index: index + i
144
+ });
145
+ }
146
+ });
147
+ });
148
+ this.shareQuery.on('move', (shareDocs, from, to) => {
149
+ const movedDocs = this._mapShareDocsToRaw(shareDocs);
150
+ const movedIds = shareDocs.map(doc => doc.id);
151
+ this._forEachRoot(rootId => {
152
+ const docs = getPrivateData(rootId, [QUERIES, this.hash, 'docs']);
153
+ const ids = getPrivateData(rootId, [QUERIES, this.hash, 'ids']);
154
+ if (!Array.isArray(docs) || !Array.isArray(ids))
155
+ return;
156
+ const prevDocs = isModelEventsEnabled() ? docs.slice() : undefined;
157
+ docs.splice(from, shareDocs.length);
158
+ docs.splice(to, 0, ...movedDocs);
159
+ const prevIds = isModelEventsEnabled() ? ids.slice() : undefined;
160
+ ids.splice(from, shareDocs.length);
161
+ ids.splice(to, 0, ...movedIds);
162
+ if (!isModelEventsEnabled())
163
+ return;
164
+ emitModelChange(rootId, [QUERIES, this.hash, 'docs'], docs, prevDocs, {
165
+ op: 'queryMove',
166
+ from,
167
+ to,
168
+ howMany: shareDocs.length
169
+ });
170
+ emitModelChange(rootId, [QUERIES, this.hash, 'ids'], ids, prevIds, {
171
+ op: 'queryMove',
172
+ from,
173
+ to,
174
+ howMany: shareDocs.length
175
+ });
176
+ });
177
+ });
178
+ this.shareQuery.on('remove', (shareDocs, index) => {
179
+ const docIds = shareDocs.map(doc => doc.id);
180
+ for (const docId of docIds) {
181
+ const $doc = getSignal(undefined, [this.collectionName, docId]);
182
+ docSubscriptions.release($doc).catch(ignoreDestroyError);
183
+ this.docSignals.delete($doc);
184
+ }
185
+ this._forEachRoot(rootId => {
186
+ const docs = getPrivateData(rootId, [QUERIES, this.hash, 'docs']);
187
+ const ids = getPrivateData(rootId, [QUERIES, this.hash, 'ids']);
188
+ if (!Array.isArray(docs) || !Array.isArray(ids))
189
+ return;
190
+ const removedDocs = isModelEventsEnabled() ? docs.slice(index, index + shareDocs.length) : undefined;
191
+ docs.splice(index, shareDocs.length);
192
+ const removedIds = isModelEventsEnabled() ? ids.slice(index, index + docIds.length) : undefined;
193
+ ids.splice(index, docIds.length);
194
+ if (!isModelEventsEnabled())
195
+ return;
196
+ const docsPath = [QUERIES, this.hash, 'docs'];
197
+ const idsPath = [QUERIES, this.hash, 'ids'];
198
+ for (let i = 0; i < removedDocs.length; i++) {
199
+ emitModelChange(rootId, docsPath.concat(index + i), undefined, removedDocs[i], {
200
+ op: 'queryRemove',
201
+ index: index + i
202
+ });
203
+ }
204
+ for (let i = 0; i < removedIds.length; i++) {
205
+ emitModelChange(rootId, idsPath.concat(index + i), undefined, removedIds[i], {
206
+ op: 'queryRemove',
207
+ index: index + i
208
+ });
209
+ }
210
+ });
211
+ });
212
+ this.shareQuery.on('extra', extra => {
213
+ extra = raw(extra);
214
+ this._forEachRoot(rootId => {
215
+ if (getPrivateData(rootId, [QUERIES, this.hash]) == null)
216
+ return;
217
+ setPrivateData(rootId, [QUERIES, this.hash, 'extra'], extra);
218
+ });
219
+ });
220
+ }
221
+ _syncAllRootsData() {
222
+ this._forEachRoot(rootId => this._syncRootData(rootId));
223
+ }
224
+ _syncRootData(rootId) {
225
+ if (!this.shareQuery)
226
+ return;
227
+ maybeMaterializeQueryDocsToCollection(this.collectionName, this.shareQuery.results);
228
+ const docs = this._mapShareDocsToRaw(this.shareQuery.results);
229
+ setPrivateData(rootId, [QUERIES, this.hash, 'docs'], docs);
230
+ const ids = this.shareQuery.results.map(doc => doc.id);
231
+ setPrivateData(rootId, [QUERIES, this.hash, 'ids'], ids);
232
+ if (this.shareQuery.extra !== undefined) {
233
+ const extra = raw(this.shareQuery.extra);
234
+ setPrivateData(rootId, [QUERIES, this.hash, 'extra'], extra);
235
+ }
236
+ }
237
+ _removeRootData(rootId) {
238
+ delPrivateData(rootId, [QUERIES, this.hash]);
239
+ }
240
+ _forEachRoot(fn) {
241
+ for (const rootId of this.rootIds)
242
+ fn(rootId);
243
+ }
244
+ _mapShareDocsToRaw(shareDocs) {
245
+ return shareDocs.map(doc => {
246
+ const idFields = getIdFieldsForSegments([this.collectionName, doc.id]);
247
+ if (isPlainObject(doc.data))
248
+ injectIdFields(doc.data, idFields, doc.id);
249
+ return raw(doc.data);
250
+ });
251
+ }
252
+ _detachTransportData({ keepRoots = true } = {}) {
253
+ for (const $doc of this.docSignals) {
254
+ docSubscriptions.release($doc).catch(ignoreDestroyError);
255
+ }
256
+ this.docSignals.clear();
257
+ this._forEachRoot(rootId => this._removeRootData(rootId));
258
+ if (!keepRoots)
259
+ this.rootIds.clear();
260
+ }
261
+ _removeData() {
262
+ this._detachTransportData({ keepRoots: false });
263
+ }
264
+ }
265
+ export class QuerySubscriptions {
266
+ constructor(QueryClass = Query) {
267
+ this.QueryClass = QueryClass;
268
+ this.runtimeKind = 'query';
269
+ this.ownerRecords = new Map(); // ownerKey -> owner record
270
+ this.entries = new Map(); // transportHash -> transport entry
271
+ this.fr = new FinalizationRegistry(({ collectionName, params, ownerKey }) => {
272
+ this.scheduleDestroy(collectionName, params, ownerKey, { force: true });
273
+ });
274
+ this.subCount = createReadonlyMapView({
275
+ get: ownerKey => this.getTrackedOwnerCount(ownerKey),
276
+ has: ownerKey => this.getTrackedOwnerCount(ownerKey) !== undefined,
277
+ size: () => this.getTrackedOwnerCountSize(),
278
+ keys: () => getUnionKeys(this.ownerRecords.keys(), this.getPendingDestroyOwnerKeys())
279
+ });
280
+ this.transportSubCount = createReadonlyMapView({
281
+ get: transportHash => this.getTransportOwnerCount(transportHash),
282
+ has: transportHash => this.getTransportOwnerCount(transportHash) !== undefined,
283
+ size: () => this.getTrackedTransportCountSize(),
284
+ keys: () => filterMapKeys(this.entries, entry => entry.owners.size > 0 || !!entry.runtime)
285
+ });
286
+ this.ownerFetchCount = createReadonlyMapView({
287
+ get: ownerKey => {
288
+ const count = this.ownerRecords.get(ownerKey)?.fetchCount;
289
+ return count > 0 ? count : undefined;
290
+ },
291
+ has: ownerKey => !!this.ownerRecords.get(ownerKey)?.fetchCount,
292
+ size: () => countMapLike(this.ownerRecords, record => record.fetchCount > 0),
293
+ keys: () => filterMapKeys(this.ownerRecords, record => record.fetchCount > 0)
294
+ });
295
+ this.ownerSubscribeCount = createReadonlyMapView({
296
+ get: ownerKey => {
297
+ const count = this.ownerRecords.get(ownerKey)?.subscribeCount;
298
+ return count > 0 ? count : undefined;
299
+ },
300
+ has: ownerKey => !!this.ownerRecords.get(ownerKey)?.subscribeCount,
301
+ size: () => countMapLike(this.ownerRecords, record => record.subscribeCount > 0),
302
+ keys: () => filterMapKeys(this.ownerRecords, record => record.subscribeCount > 0)
303
+ });
304
+ this.queries = createReadonlyMapView({
305
+ get: transportHash => this.getRuntime(transportHash),
306
+ has: transportHash => this.hasRuntime(transportHash),
307
+ size: () => this.getRuntimeCount(),
308
+ keys: () => filterMapKeys(this.entries, entry => !!entry.runtime)
309
+ });
310
+ this.ownerToTransport = createReadonlyMapView({
311
+ get: ownerKey => this.ownerRecords.get(ownerKey)?.transportHash,
312
+ has: ownerKey => this.ownerRecords.has(ownerKey),
313
+ size: () => this.ownerRecords.size,
314
+ keys: () => this.ownerRecords.keys()
315
+ });
316
+ this.ownerMeta = createReadonlyMapView({
317
+ get: ownerKey => this.getOwnerMeta(ownerKey),
318
+ has: ownerKey => this.ownerRecords.has(ownerKey),
319
+ size: () => this.ownerRecords.size,
320
+ keys: () => this.ownerRecords.keys()
321
+ });
322
+ this.ownerKeysByTransport = createReadonlyMapView({
323
+ get: transportHash => this.getOwnerKeys(transportHash),
324
+ has: transportHash => !!this.getOwnerKeys(transportHash),
325
+ size: () => countMapLike(this.entries, entry => entry.owners.size > 0),
326
+ keys: () => filterMapKeys(this.entries, entry => entry.owners.size > 0)
327
+ });
328
+ this.pendingDestroyTimers = createReadonlyMapView({
329
+ get: ownerKey => this.getPendingDestroy(ownerKey),
330
+ has: ownerKey => this.hasPendingDestroy(ownerKey),
331
+ size: () => this.getPendingDestroyCount(),
332
+ keys: () => this.getPendingDestroyOwnerKeys()
333
+ });
334
+ }
335
+ getOrCreateOwnerRecord(ownerKey, meta) {
336
+ let record = this.ownerRecords.get(ownerKey);
337
+ if (!record) {
338
+ record = {
339
+ ownerKey,
340
+ rootId: meta.rootId,
341
+ collectionName: meta.collectionName,
342
+ params: meta.params,
343
+ transportHash: meta.transportHash,
344
+ fetchCount: 0,
345
+ subscribeCount: 0
346
+ };
347
+ this.ownerRecords.set(ownerKey, record);
348
+ }
349
+ else {
350
+ if (meta.rootId != null)
351
+ record.rootId = meta.rootId;
352
+ if (meta.collectionName != null)
353
+ record.collectionName = meta.collectionName;
354
+ if (meta.params != null)
355
+ record.params = meta.params;
356
+ if (meta.transportHash != null)
357
+ record.transportHash = meta.transportHash;
358
+ }
359
+ return record;
360
+ }
361
+ getOrCreateEntry(transportHash) {
362
+ let entry = this.entries.get(transportHash);
363
+ if (!entry) {
364
+ entry = {
365
+ transportHash,
366
+ mode: 'idle',
367
+ targetMode: 'idle',
368
+ phase: 'stable',
369
+ runtime: null,
370
+ owners: new Set(),
371
+ pendingDestroyByOwner: new Map(),
372
+ reconcilePromise: null
373
+ };
374
+ this.entries.set(transportHash, entry);
375
+ }
376
+ return entry;
377
+ }
378
+ getEntry(transportHash) {
379
+ return this.entries.get(transportHash);
380
+ }
381
+ syncOwnerMirror() { }
382
+ clearOwnerMirror() { }
383
+ syncEntryMirror() { }
384
+ deleteEntryIfEmpty(transportHash) {
385
+ const entry = this.entries.get(transportHash);
386
+ if (!entry)
387
+ return;
388
+ if (entry.owners.size > 0)
389
+ return;
390
+ if (entry.pendingDestroyByOwner.size > 0)
391
+ return;
392
+ if (entry.runtime)
393
+ return;
394
+ if (entry.phase === 'transition')
395
+ return;
396
+ this.entries.delete(transportHash);
397
+ }
398
+ addOwnerToEntry(record) {
399
+ const entry = this.getOrCreateEntry(record.transportHash);
400
+ if (entry.owners.has(record.ownerKey)) {
401
+ this.syncEntryMirror(entry);
402
+ return entry;
403
+ }
404
+ entry.owners.add(record.ownerKey);
405
+ attachQueryRoot(entry.runtime, record.rootId);
406
+ registerRootOwnedRuntime(record.rootId, this.runtimeKind, record.transportHash);
407
+ this.syncEntryMirror(entry);
408
+ return entry;
409
+ }
410
+ removeOwnerFromEntry(record) {
411
+ const entry = this.entries.get(record.transportHash);
412
+ if (!entry)
413
+ return;
414
+ if (!entry.owners.delete(record.ownerKey)) {
415
+ this.syncEntryMirror(entry);
416
+ return;
417
+ }
418
+ detachQueryRoot(entry.runtime, record.rootId);
419
+ unregisterRootOwnedRuntime(record.rootId, this.runtimeKind, record.transportHash);
420
+ this.syncEntryMirror(entry);
421
+ }
422
+ getEntryMeta(transportHash) {
423
+ const entry = this.entries.get(transportHash);
424
+ if (entry?.runtime) {
425
+ return {
426
+ collectionName: entry.runtime.collectionName,
427
+ params: entry.runtime.params
428
+ };
429
+ }
430
+ const ownerKey = entry?.owners.values()?.next?.().value;
431
+ if (ownerKey) {
432
+ const record = this.ownerRecords.get(ownerKey);
433
+ if (record) {
434
+ return {
435
+ collectionName: record.collectionName,
436
+ params: record.params
437
+ };
438
+ }
439
+ }
440
+ const parsed = parseQueryHash(transportHash);
441
+ return {
442
+ collectionName: parsed.collectionName,
443
+ params: parsed.params
444
+ };
445
+ }
446
+ ensureRuntime(transportHash) {
447
+ const entry = this.getOrCreateEntry(transportHash);
448
+ if (!entry.runtime) {
449
+ const { collectionName, params } = this.getEntryMeta(transportHash);
450
+ entry.runtime = new this.QueryClass(collectionName, params, { hash: transportHash });
451
+ }
452
+ this.syncRuntimeRoots(entry);
453
+ this.syncEntryMirror(entry);
454
+ return entry.runtime;
455
+ }
456
+ syncRuntimeRoots(entry) {
457
+ if (!entry?.runtime)
458
+ return;
459
+ for (const ownerKey of entry.owners) {
460
+ const record = this.ownerRecords.get(ownerKey);
461
+ if (!record)
462
+ continue;
463
+ attachQueryRoot(entry.runtime, record.rootId);
464
+ }
465
+ }
466
+ subscribe($query, { intent = 'subscribe' } = {}) {
467
+ const collectionName = $query[COLLECTION_NAME];
468
+ const params = cloneQueryParams($query[PARAMS]);
469
+ const transportHash = $query[HASH];
470
+ const rootId = getOwningRootId($query);
471
+ const ownerKey = getQueryOwnerKey(rootId, transportHash);
472
+ this.cancelDestroy(ownerKey, transportHash);
473
+ const previousCount = this.getOwnerTotalCount(ownerKey);
474
+ let record = this.ownerRecords.get(ownerKey);
475
+ record = this.getOrCreateOwnerRecord(ownerKey, {
476
+ rootId,
477
+ collectionName,
478
+ params,
479
+ transportHash
480
+ });
481
+ const entry = this.addOwnerToEntry(record);
482
+ this.incrementOwnerIntent(record, intent);
483
+ this.fr.register($query, { collectionName, params, ownerKey }, $query);
484
+ if (previousCount > 0 &&
485
+ entry.runtime &&
486
+ entry.phase === 'stable' &&
487
+ this.getDesiredTransportMode(transportHash) === entry.mode)
488
+ return;
489
+ return this.reconcileTransport(transportHash);
490
+ }
491
+ async unsubscribe($query, { intent = 'subscribe' } = {}) {
492
+ const ownerKey = getQueryOwnerKey(getOwningRootId($query), $query[HASH]);
493
+ const record = this.ownerRecords.get(ownerKey);
494
+ const currentIntentCount = this.getOwnerIntentCount(record, intent);
495
+ if (currentIntentCount <= 0) {
496
+ if (ERROR_ON_EXCESSIVE_UNSUBSCRIBES)
497
+ throw Error(ERRORS.notSubscribed($query));
498
+ return;
499
+ }
500
+ const transportHash = record?.transportHash ?? $query[HASH];
501
+ this.setOwnerIntentCount(record, intent, currentIntentCount - 1);
502
+ const count = this.getOwnerTotalCount(record);
503
+ if (count === 0) {
504
+ this.fr.unregister($query);
505
+ if (record) {
506
+ this.removeOwnerFromEntry(record);
507
+ }
508
+ }
509
+ const destroyPromise = count === 0
510
+ ? this.scheduleDestroy($query[COLLECTION_NAME], $query[PARAMS], ownerKey, { transportHash })
511
+ : undefined;
512
+ await this.reconcileTransport(transportHash);
513
+ if (count > 0)
514
+ return;
515
+ await destroyPromise;
516
+ }
517
+ async destroy(collectionName, params, options = {}) {
518
+ const transportHash = hashQuery(collectionName, params);
519
+ const ownerKeys = Array.from(this.getOwnerKeys(transportHash) || []);
520
+ for (const ownerKey of ownerKeys) {
521
+ await this.destroyByOwnerKey(ownerKey, {
522
+ collectionName,
523
+ params,
524
+ force: options.force ?? true
525
+ });
526
+ }
527
+ }
528
+ async clear() {
529
+ const ownerKeys = new Set([
530
+ ...this.getPendingDestroyOwnerKeys(),
531
+ ...this.ownerRecords.keys()
532
+ ]);
533
+ for (const ownerKey of ownerKeys) {
534
+ await this.destroyByOwnerKey(ownerKey, { force: true });
535
+ }
536
+ this.entries.clear();
537
+ this.ownerRecords.clear();
538
+ }
539
+ async flushPendingDestroys() {
540
+ const ownerKeys = Array.from(this.getPendingDestroyOwnerKeys());
541
+ for (const ownerKey of ownerKeys) {
542
+ await this.destroyByOwnerKey(ownerKey);
543
+ }
544
+ }
545
+ async scheduleDestroy(collectionName, params, ownerKey, options = {}) {
546
+ const transportHash = options.transportHash ?? hashQuery(collectionName, params);
547
+ const fallbackOwnerKey = ownerKey ?? getQueryOwnerKey(undefined, transportHash);
548
+ const delay = getSubscriptionGcDelay();
549
+ if (delay <= 0) {
550
+ await this.destroyByOwnerKey(fallbackOwnerKey, {
551
+ collectionName,
552
+ params,
553
+ transportHash,
554
+ force: !!options.force
555
+ });
556
+ return;
557
+ }
558
+ const entry = this.getOrCreateEntry(transportHash);
559
+ const existing = entry.pendingDestroyByOwner.get(fallbackOwnerKey);
560
+ if (existing) {
561
+ if (options.force)
562
+ existing.force = true;
563
+ return existing.promise;
564
+ }
565
+ const pendingDestroy = createPendingDestroyEntry();
566
+ if (options.force)
567
+ pendingDestroy.force = true;
568
+ pendingDestroy.collectionName = collectionName;
569
+ pendingDestroy.params = params;
570
+ pendingDestroy.transportHash = transportHash;
571
+ pendingDestroy.timer = setTimeout(() => {
572
+ this.destroyByOwnerKey(fallbackOwnerKey, {
573
+ collectionName,
574
+ params,
575
+ transportHash: pendingDestroy.transportHash,
576
+ force: pendingDestroy.force
577
+ })
578
+ .catch(ignoreDestroyError);
579
+ }, delay);
580
+ entry.pendingDestroyByOwner.set(fallbackOwnerKey, pendingDestroy);
581
+ return pendingDestroy.promise;
582
+ }
583
+ cancelDestroy(ownerKey, transportHash) {
584
+ const pendingDestroy = this.takePendingDestroy(ownerKey, transportHash);
585
+ if (!pendingDestroy)
586
+ return;
587
+ pendingDestroy.resolve();
588
+ }
589
+ async reconcileTransport(transportHash) {
590
+ const entry = this.getOrCreateEntry(transportHash);
591
+ entry.targetMode = this.getDesiredTransportMode(transportHash);
592
+ if (entry.phase === 'transition' && entry.reconcilePromise)
593
+ return entry.reconcilePromise;
594
+ const next = Promise.resolve()
595
+ .catch(ignoreDestroyError)
596
+ .then(() => this.reconcileTransportNow(transportHash));
597
+ entry.phase = 'transition';
598
+ entry.reconcilePromise = next;
599
+ try {
600
+ await next;
601
+ }
602
+ finally {
603
+ const currentEntry = this.entries.get(transportHash);
604
+ if (currentEntry?.reconcilePromise === next) {
605
+ currentEntry.reconcilePromise = null;
606
+ currentEntry.phase = 'stable';
607
+ }
608
+ this.deleteEntryIfEmpty(transportHash);
609
+ }
610
+ }
611
+ async reconcileTransportNow(transportHash) {
612
+ const entry = this.getOrCreateEntry(transportHash);
613
+ while (true) {
614
+ let query = entry.runtime;
615
+ const desiredMode = entry.targetMode = this.getDesiredTransportMode(transportHash);
616
+ const currentMode = query?.activeTransportMode ?? entry.mode;
617
+ entry.mode = currentMode;
618
+ if (desiredMode === currentMode)
619
+ return;
620
+ if (desiredMode === 'idle') {
621
+ if (query && currentMode !== 'idle') {
622
+ await unsubscribeQueryTransport(query, { keepRoots: true });
623
+ }
624
+ entry.mode = 'idle';
625
+ continue;
626
+ }
627
+ if (currentMode !== 'idle' && query) {
628
+ await unsubscribeQueryTransport(query, { keepRoots: true });
629
+ entry.mode = 'idle';
630
+ continue;
631
+ }
632
+ query = this.ensureRuntime(transportHash);
633
+ await subscribeQueryTransport(query, desiredMode);
634
+ entry.runtime = query;
635
+ entry.mode = query.activeTransportMode || desiredMode;
636
+ this.syncEntryMirror(entry);
637
+ }
638
+ }
639
+ getOwnerIntentCount(record, intent) {
640
+ if (!record)
641
+ return 0;
642
+ return intent === 'fetch' ? record.fetchCount : record.subscribeCount;
643
+ }
644
+ setOwnerIntentCount(record, intent, count) {
645
+ if (!record)
646
+ return;
647
+ if (intent === 'fetch')
648
+ record.fetchCount = Math.max(count, 0);
649
+ else
650
+ record.subscribeCount = Math.max(count, 0);
651
+ this.syncOwnerMirror(record);
652
+ }
653
+ incrementOwnerIntent(record, intent) {
654
+ this.setOwnerIntentCount(record, intent, this.getOwnerIntentCount(record, intent) + 1);
655
+ }
656
+ getOwnerTotalCount(recordOrOwnerKey) {
657
+ const record = typeof recordOrOwnerKey === 'string'
658
+ ? this.ownerRecords.get(recordOrOwnerKey)
659
+ : recordOrOwnerKey;
660
+ if (!record)
661
+ return 0;
662
+ return record.fetchCount + record.subscribeCount;
663
+ }
664
+ getDesiredTransportMode(transportHash) {
665
+ const entry = this.entries.get(transportHash);
666
+ if (!entry || entry.owners.size === 0)
667
+ return 'idle';
668
+ let hasFetchBackedOwner = false;
669
+ for (const ownerKey of entry.owners) {
670
+ const record = this.ownerRecords.get(ownerKey);
671
+ if (!record)
672
+ continue;
673
+ const subscribeCount = record.subscribeCount;
674
+ const fetchCount = record.fetchCount;
675
+ const rootId = record.rootId;
676
+ const subscribeMode = getRootTransportMode(rootId, 'subscribe');
677
+ if (subscribeCount > 0 && subscribeMode === 'subscribe')
678
+ return 'subscribe';
679
+ if (fetchCount > 0 || (subscribeCount > 0 && subscribeMode === 'fetch')) {
680
+ hasFetchBackedOwner = true;
681
+ }
682
+ }
683
+ return hasFetchBackedOwner ? 'fetch' : 'idle';
684
+ }
685
+ async destroyTransportEntry(transportHash, runtime) {
686
+ const activeRuntime = this.entries.get(transportHash)?.runtime || runtime;
687
+ if (!activeRuntime) {
688
+ const entry = this.entries.get(transportHash);
689
+ if (entry) {
690
+ entry.runtime = null;
691
+ entry.mode = 'idle';
692
+ }
693
+ this.deleteEntryIfEmpty(transportHash);
694
+ return;
695
+ }
696
+ if (activeRuntime.activeTransportMode !== 'idle') {
697
+ await unsubscribeQueryTransport(activeRuntime, { keepRoots: true });
698
+ }
699
+ activeRuntime._detachTransportData?.({ keepRoots: false });
700
+ const finalEntry = this.entries.get(transportHash);
701
+ if (finalEntry && finalEntry.owners.size > 0)
702
+ return;
703
+ if (finalEntry) {
704
+ finalEntry.runtime = null;
705
+ finalEntry.mode = 'idle';
706
+ }
707
+ this.deleteEntryIfEmpty(transportHash);
708
+ }
709
+ async destroyByOwnerKey(ownerKey, options = {}) {
710
+ const pendingDestroy = this.takePendingDestroy(ownerKey, options.transportHash);
711
+ if (pendingDestroy?.force)
712
+ options.force = true;
713
+ if (options.collectionName == null && pendingDestroy?.collectionName != null) {
714
+ options.collectionName = pendingDestroy.collectionName;
715
+ }
716
+ if (options.params == null && pendingDestroy?.params != null) {
717
+ options.params = pendingDestroy.params;
718
+ }
719
+ const settlePending = err => {
720
+ if (!pendingDestroy)
721
+ return;
722
+ if (err)
723
+ pendingDestroy.reject(err);
724
+ else
725
+ pendingDestroy.resolve();
726
+ };
727
+ try {
728
+ const count = this.getTrackedOwnerCount(ownerKey) || 0;
729
+ if (!options.force && count > 0) {
730
+ settlePending();
731
+ return;
732
+ }
733
+ const record = this.ownerRecords.get(ownerKey);
734
+ if (!record) {
735
+ const ownerCount = this.getTrackedOwnerCount(ownerKey) || 0;
736
+ const transportHash = options.transportHash ||
737
+ (options.collectionName && options.params ? hashQuery(options.collectionName, options.params) : undefined);
738
+ if (!transportHash) {
739
+ settlePending();
740
+ return;
741
+ }
742
+ const entry = this.entries.get(transportHash);
743
+ if (entry && ownerCount > 0) {
744
+ entry.owners.delete(ownerKey);
745
+ }
746
+ const query = entry?.runtime;
747
+ await this.reconcileTransport(transportHash);
748
+ const nextEntry = this.entries.get(transportHash);
749
+ if (!nextEntry || nextEntry.owners.size === 0) {
750
+ await this.destroyTransportEntry(transportHash, query);
751
+ }
752
+ settlePending();
753
+ return;
754
+ }
755
+ const { transportHash } = record;
756
+ const entry = this.entries.get(transportHash);
757
+ const query = entry?.runtime;
758
+ if (entry?.owners.has(ownerKey))
759
+ this.removeOwnerFromEntry(record);
760
+ this.ownerRecords.delete(ownerKey);
761
+ await this.reconcileTransport(transportHash);
762
+ const nextEntry = this.entries.get(transportHash);
763
+ if (nextEntry && nextEntry.owners.size > 0) {
764
+ settlePending();
765
+ return;
766
+ }
767
+ await this.destroyTransportEntry(transportHash, query);
768
+ settlePending();
769
+ }
770
+ catch (err) {
771
+ settlePending(err);
772
+ throw err;
773
+ }
774
+ }
775
+ async destroyByRuntimeHash(runtimeHash, options = {}) {
776
+ const rootId = options.rootId ?? options.root?.[ROOT_ID];
777
+ const ownerKey = getQueryOwnerKey(rootId, runtimeHash);
778
+ return this.destroyByOwnerKey(ownerKey, {
779
+ ...options,
780
+ transportHash: runtimeHash
781
+ });
782
+ }
783
+ takePendingDestroy(ownerKey, transportHash) {
784
+ const entry = this.getEntryForPendingDestroy(ownerKey, transportHash);
785
+ const pendingDestroy = entry?.pendingDestroyByOwner.get(ownerKey);
786
+ if (!pendingDestroy)
787
+ return;
788
+ clearTimeout(pendingDestroy.timer);
789
+ entry.pendingDestroyByOwner.delete(ownerKey);
790
+ this.deleteEntryIfEmpty(entry.transportHash);
791
+ return pendingDestroy;
792
+ }
793
+ removeOwnerMeta(ownerKey, transportHash) {
794
+ const knownTransportHash = transportHash ?? this.ownerRecords.get(ownerKey)?.transportHash;
795
+ const entry = knownTransportHash ? this.entries.get(knownTransportHash) : undefined;
796
+ if (!entry)
797
+ return;
798
+ entry.owners.delete(ownerKey);
799
+ this.deleteEntryIfEmpty(knownTransportHash);
800
+ }
801
+ cleanupStaleTransportState(transportHash) {
802
+ if (!transportHash)
803
+ return;
804
+ const entry = this.entries.get(transportHash);
805
+ if (entry) {
806
+ if (!entry.runtime && entry.owners.size === 0)
807
+ this.entries.delete(transportHash);
808
+ }
809
+ }
810
+ getRuntime(transportHash) {
811
+ return this.entries.get(transportHash)?.runtime;
812
+ }
813
+ hasRuntime(transportHash) {
814
+ return !!this.getRuntime(transportHash);
815
+ }
816
+ getRuntimeCount() {
817
+ return countMapLike(this.entries, entry => !!entry.runtime);
818
+ }
819
+ getTrackedOwnerCount(ownerKey) {
820
+ const record = this.ownerRecords.get(ownerKey);
821
+ if (record)
822
+ return record.fetchCount + record.subscribeCount;
823
+ if (this.hasPendingDestroy(ownerKey))
824
+ return 0;
825
+ return undefined;
826
+ }
827
+ getTrackedOwnerCountSize() {
828
+ return getUnionSize(this.ownerRecords.keys(), this.getPendingDestroyOwnerKeys());
829
+ }
830
+ getTransportOwnerCount(transportHash) {
831
+ const entry = this.entries.get(transportHash);
832
+ if (!entry)
833
+ return undefined;
834
+ if (entry.owners.size > 0 || entry.runtime)
835
+ return entry.owners.size;
836
+ return undefined;
837
+ }
838
+ getTrackedTransportCountSize() {
839
+ return countMapLike(this.entries, entry => entry.owners.size > 0 || !!entry.runtime);
840
+ }
841
+ getOwnerMeta(ownerKey) {
842
+ const record = this.ownerRecords.get(ownerKey);
843
+ if (!record)
844
+ return undefined;
845
+ return {
846
+ collectionName: record.collectionName,
847
+ params: record.params,
848
+ transportHash: record.transportHash,
849
+ rootId: record.rootId
850
+ };
851
+ }
852
+ getOwnerKeys(transportHash) {
853
+ const owners = this.entries.get(transportHash)?.owners;
854
+ if (!owners?.size)
855
+ return undefined;
856
+ return new Set(owners);
857
+ }
858
+ getPendingDestroy(ownerKey, transportHash) {
859
+ const entry = this.getEntryForPendingDestroy(ownerKey, transportHash);
860
+ return entry?.pendingDestroyByOwner.get(ownerKey);
861
+ }
862
+ hasPendingDestroy(ownerKey, transportHash) {
863
+ return !!this.getPendingDestroy(ownerKey, transportHash);
864
+ }
865
+ getPendingDestroyCount() {
866
+ let count = 0;
867
+ for (const entry of this.entries.values())
868
+ count += entry.pendingDestroyByOwner.size;
869
+ return count;
870
+ }
871
+ getEntryForPendingDestroy(ownerKey, transportHash) {
872
+ if (transportHash)
873
+ return this.entries.get(transportHash);
874
+ const knownTransportHash = this.ownerRecords.get(ownerKey)?.transportHash;
875
+ if (knownTransportHash)
876
+ return this.entries.get(knownTransportHash);
877
+ for (const entry of this.entries.values()) {
878
+ if (entry.pendingDestroyByOwner.has(ownerKey))
879
+ return entry;
880
+ }
881
+ }
882
+ *getPendingDestroyOwnerKeys() {
883
+ for (const entry of this.entries.values()) {
884
+ yield* entry.pendingDestroyByOwner.keys();
885
+ }
886
+ }
887
+ }
888
+ export const querySubscriptions = new QuerySubscriptions();
889
+ function maybeMaterializeQueryDocsToCollection(collectionName, shareDocs) {
890
+ if (!isCompatEnv())
891
+ return;
892
+ for (const doc of shareDocs) {
893
+ if (!doc?.id || doc.data == null)
894
+ continue;
895
+ const existing = getRaw([collectionName, doc.id]);
896
+ if (existing != null)
897
+ continue;
898
+ const idFields = getIdFieldsForSegments([collectionName, doc.id]);
899
+ if (isPlainObject(doc.data))
900
+ injectIdFields(doc.data, idFields, doc.id);
901
+ _set([collectionName, doc.id], raw(doc.data));
902
+ }
903
+ }
904
+ export function hashQuery(collectionName, params) {
905
+ params = normalizeQueryParamsForHash(params);
906
+ // TODO: probably makes sense to use fast-stable-json-stringify for this because of the params
907
+ return JSON.stringify({ query: [collectionName, params] });
908
+ }
909
+ export function parseQueryHash(hash) {
910
+ try {
911
+ const { query: [collectionName, params] } = JSON.parse(hash);
912
+ return { collectionName, params };
913
+ }
914
+ catch (err) {
915
+ return {};
916
+ }
917
+ }
918
+ export function getQuerySignal(collectionName, params, options) {
919
+ params = cloneQueryParams(params);
920
+ const transportHash = hashQuery(collectionName, params);
921
+ const { root, signalOptions } = parseQuerySignalOptions(options);
922
+ const signalHash = getScopedSignalHash(root?.[ROOT_ID] ?? signalOptions.rootId, transportHash, 'querySignal');
923
+ const $query = getSignal(root, [collectionName], {
924
+ signalHash,
925
+ ...signalOptions
926
+ });
927
+ $query[IS_QUERY] ??= true;
928
+ $query[COLLECTION_NAME] ??= collectionName;
929
+ $query[PARAMS] ??= params;
930
+ $query[HASH] ??= transportHash;
931
+ setSignalRuntimeDescriptor($query, {
932
+ kind: 'query',
933
+ collectionName,
934
+ itemPattern: [collectionName, '*']
935
+ });
936
+ return $query;
937
+ }
938
+ const ERRORS = {
939
+ notSubscribed: $query => `
940
+ Trying to unsubscribe from Query when not subscribed.
941
+ Collection: ${$query[COLLECTION_NAME]}
942
+ Params: ${$query[PARAMS]}
943
+ `
944
+ };
945
+ function ignoreDestroyError() { }
946
+ function attachQueryRoot(query, rootId) {
947
+ if (rootId == null || !query)
948
+ return;
949
+ if (typeof query.attachRoot === 'function') {
950
+ query.attachRoot(rootId);
951
+ return;
952
+ }
953
+ if (query.rootIds?.add)
954
+ query.rootIds.add(rootId);
955
+ }
956
+ function detachQueryRoot(query, rootId) {
957
+ if (rootId == null || !query)
958
+ return;
959
+ if (typeof query.detachRoot === 'function') {
960
+ query.detachRoot(rootId);
961
+ return;
962
+ }
963
+ if (query.rootIds?.delete)
964
+ query.rootIds.delete(rootId);
965
+ }
966
+ function getOwningRootId($query) {
967
+ return normalizeRootId(getRoot($query)?.[ROOT_ID]);
968
+ }
969
+ function getQueryOwnerKey(rootId, transportHash) {
970
+ return getScopedSignalHash(rootId, transportHash, 'queryOwner');
971
+ }
972
+ function cloneQueryParams(params) {
973
+ if (!isCompatEnv())
974
+ return JSON.parse(JSON.stringify(params));
975
+ return cloneQueryParamsCompat(params);
976
+ }
977
+ function parseQuerySignalOptions(options) {
978
+ if (!options || typeof options !== 'object') {
979
+ return {
980
+ root: undefined,
981
+ signalOptions: {}
982
+ };
983
+ }
984
+ const { root, ...signalOptions } = options;
985
+ return { root, signalOptions };
986
+ }
987
+ function normalizeQueryParamsForHash(params) {
988
+ if (!isCompatEnv())
989
+ return params;
990
+ return cloneQueryParamsCompat(params);
991
+ }
992
+ // Racer compat: keep query keys with undefined values by normalizing them to null
993
+ // instead of dropping them via JSON serialization.
994
+ function cloneQueryParamsCompat(value) {
995
+ if (value === undefined)
996
+ return null;
997
+ if (value == null || typeof value !== 'object')
998
+ return value;
999
+ if (Array.isArray(value))
1000
+ return value.map(item => cloneQueryParamsCompat(item));
1001
+ const object = {};
1002
+ for (const key in value) {
1003
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
1004
+ object[key] = cloneQueryParamsCompat(value[key]);
1005
+ }
1006
+ }
1007
+ return object;
1008
+ }
1009
+ function createPendingDestroyEntry() {
1010
+ let resolvePending;
1011
+ let rejectPending;
1012
+ const promise = new Promise((resolve, reject) => {
1013
+ resolvePending = resolve;
1014
+ rejectPending = reject;
1015
+ });
1016
+ promise.catch(ignoreDestroyError);
1017
+ return {
1018
+ timer: undefined,
1019
+ force: false,
1020
+ collectionName: undefined,
1021
+ params: undefined,
1022
+ promise,
1023
+ resolve: resolvePending,
1024
+ reject: rejectPending
1025
+ };
1026
+ }
1027
+ function createReadonlyMapView({ get, has, size, keys }) {
1028
+ return {
1029
+ get,
1030
+ has,
1031
+ get size() {
1032
+ return size();
1033
+ },
1034
+ *keys() {
1035
+ yield* keys();
1036
+ },
1037
+ *values() {
1038
+ for (const key of keys())
1039
+ yield get(key);
1040
+ },
1041
+ *entries() {
1042
+ for (const key of keys())
1043
+ yield [key, get(key)];
1044
+ },
1045
+ [Symbol.iterator]() {
1046
+ return this.entries();
1047
+ }
1048
+ };
1049
+ }
1050
+ function countMapLike(iterableMap, predicate) {
1051
+ let count = 0;
1052
+ for (const value of iterableMap.values()) {
1053
+ if (predicate(value))
1054
+ count++;
1055
+ }
1056
+ return count;
1057
+ }
1058
+ function getUnionSize(aKeys, bKeys) {
1059
+ const keys = new Set(aKeys);
1060
+ for (const key of bKeys)
1061
+ keys.add(key);
1062
+ return keys.size;
1063
+ }
1064
+ function* getUnionKeys(aKeys, bKeys) {
1065
+ const keys = new Set(aKeys);
1066
+ for (const key of bKeys)
1067
+ keys.add(key);
1068
+ yield* keys;
1069
+ }
1070
+ function* filterMapKeys(iterableMap, predicate) {
1071
+ for (const [key, value] of iterableMap.entries()) {
1072
+ if (predicate(value))
1073
+ yield key;
1074
+ }
1075
+ }
1076
+ async function subscribeQueryTransport(query, mode) {
1077
+ query.requestedTransportMode = mode;
1078
+ if (typeof query._subscribe === 'function') {
1079
+ query._subscribing = query._subscribe()
1080
+ .then(() => {
1081
+ query._subscribing = undefined;
1082
+ query.initialized = undefined;
1083
+ query.init?.();
1084
+ }, err => {
1085
+ query._subscribing = undefined;
1086
+ throw err;
1087
+ });
1088
+ await query._subscribing;
1089
+ return;
1090
+ }
1091
+ await query.subscribe({ mode });
1092
+ if (query.activeTransportMode == null || query.activeTransportMode === 'idle') {
1093
+ query.activeTransportMode = mode;
1094
+ }
1095
+ if (query.initialized !== true)
1096
+ query.init?.();
1097
+ }
1098
+ async function unsubscribeQueryTransport(query, { keepRoots = true } = {}) {
1099
+ if (!query)
1100
+ return;
1101
+ if (query.initialized) {
1102
+ query.initialized = undefined;
1103
+ query._detachTransportData?.({ keepRoots });
1104
+ }
1105
+ if (typeof query._unsubscribe === 'function') {
1106
+ await query._unsubscribe();
1107
+ return;
1108
+ }
1109
+ await query.unsubscribe?.();
1110
+ query.activeTransportMode = 'idle';
1111
+ }