@verdant-web/store 2.8.5 → 3.0.0-next.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 (307) hide show
  1. package/dist/bundle/index.js +9 -10
  2. package/dist/bundle/index.js.map +4 -4
  3. package/dist/cjs/DocumentManager.d.ts +6 -5
  4. package/dist/cjs/DocumentManager.js +2 -2
  5. package/dist/cjs/DocumentManager.js.map +1 -1
  6. package/dist/cjs/IDBService.d.ts +28 -7
  7. package/dist/cjs/IDBService.js +50 -13
  8. package/dist/cjs/IDBService.js.map +1 -1
  9. package/dist/cjs/UndoHistory.d.ts +1 -1
  10. package/dist/cjs/UndoHistory.js +6 -2
  11. package/dist/cjs/UndoHistory.js.map +1 -1
  12. package/dist/cjs/__tests__/batching.test.js +3 -1
  13. package/dist/cjs/__tests__/batching.test.js.map +1 -1
  14. package/dist/cjs/__tests__/documents.test.js +37 -6
  15. package/dist/cjs/__tests__/documents.test.js.map +1 -1
  16. package/dist/cjs/__tests__/fixtures/testStorage.d.ts +2 -2
  17. package/dist/cjs/__tests__/fixtures/testStorage.js +2 -1
  18. package/dist/cjs/__tests__/fixtures/testStorage.js.map +1 -1
  19. package/dist/cjs/__tests__/legacyOids.test.js +50 -17
  20. package/dist/cjs/__tests__/legacyOids.test.js.map +1 -1
  21. package/dist/cjs/__tests__/mutations.test.js +9 -3
  22. package/dist/cjs/__tests__/mutations.test.js.map +1 -1
  23. package/dist/cjs/__tests__/queries.test.js +6 -2
  24. package/dist/cjs/__tests__/queries.test.js.map +1 -1
  25. package/dist/cjs/__tests__/setup/indexedDB.d.ts +1 -1
  26. package/dist/cjs/__tests__/setup/indexedDB.js +8 -1
  27. package/dist/cjs/__tests__/setup/indexedDB.js.map +1 -1
  28. package/dist/cjs/__tests__/undo.test.js +16 -9
  29. package/dist/cjs/__tests__/undo.test.js.map +1 -1
  30. package/dist/cjs/client/Client.d.ts +2 -3
  31. package/dist/cjs/client/Client.js +8 -4
  32. package/dist/cjs/client/Client.js.map +1 -1
  33. package/dist/cjs/client/ClientDescriptor.js +21 -6
  34. package/dist/cjs/client/ClientDescriptor.js.map +1 -1
  35. package/dist/cjs/context.d.ts +10 -1
  36. package/dist/cjs/entities/2/Entity.d.ts +148 -0
  37. package/dist/cjs/entities/2/Entity.js +711 -0
  38. package/dist/cjs/entities/2/Entity.js.map +1 -0
  39. package/dist/cjs/entities/2/Entity.test.d.ts +1 -0
  40. package/dist/cjs/entities/2/Entity.test.js +194 -0
  41. package/dist/cjs/entities/2/Entity.test.js.map +1 -0
  42. package/dist/cjs/entities/2/EntityCache.d.ts +15 -0
  43. package/dist/cjs/entities/2/EntityCache.js +39 -0
  44. package/dist/cjs/entities/2/EntityCache.js.map +1 -0
  45. package/dist/cjs/entities/2/EntityMetadata.d.ts +68 -0
  46. package/dist/cjs/entities/2/EntityMetadata.js +261 -0
  47. package/dist/cjs/entities/2/EntityMetadata.js.map +1 -0
  48. package/dist/cjs/entities/2/EntityStore.d.ts +78 -0
  49. package/dist/cjs/entities/2/EntityStore.js +352 -0
  50. package/dist/cjs/entities/2/EntityStore.js.map +1 -0
  51. package/dist/cjs/entities/2/OperationBatcher.d.ts +52 -0
  52. package/dist/cjs/entities/2/OperationBatcher.js +165 -0
  53. package/dist/cjs/entities/2/OperationBatcher.js.map +1 -0
  54. package/dist/cjs/entities/2/types.d.ts +84 -0
  55. package/dist/cjs/entities/2/types.js +3 -0
  56. package/dist/cjs/entities/2/types.js.map +1 -0
  57. package/dist/cjs/entities/Entity.d.ts +0 -7
  58. package/dist/cjs/entities/Entity.js +7 -0
  59. package/dist/cjs/entities/Entity.js.map +1 -1
  60. package/dist/cjs/entities/EntityStore.js +4 -20
  61. package/dist/cjs/entities/EntityStore.js.map +1 -1
  62. package/dist/cjs/entities/FakeWeakRef.d.ts +11 -0
  63. package/dist/cjs/entities/FakeWeakRef.js +19 -0
  64. package/dist/cjs/entities/FakeWeakRef.js.map +1 -0
  65. package/dist/cjs/files/EntityFile.d.ts +5 -2
  66. package/dist/cjs/files/EntityFile.js +8 -4
  67. package/dist/cjs/files/EntityFile.js.map +1 -1
  68. package/dist/cjs/files/FileManager.d.ts +3 -1
  69. package/dist/cjs/files/FileManager.js +5 -3
  70. package/dist/cjs/files/FileManager.js.map +1 -1
  71. package/dist/cjs/files/FileStorage.js +7 -7
  72. package/dist/cjs/files/FileStorage.js.map +1 -1
  73. package/dist/cjs/files/utils.d.ts +2 -0
  74. package/dist/cjs/files/utils.js +5 -2
  75. package/dist/cjs/files/utils.js.map +1 -1
  76. package/dist/cjs/idb.d.ts +2 -0
  77. package/dist/cjs/idb.js +50 -4
  78. package/dist/cjs/idb.js.map +1 -1
  79. package/dist/cjs/index.d.ts +2 -2
  80. package/dist/cjs/index.js +1 -1
  81. package/dist/cjs/index.js.map +1 -1
  82. package/dist/cjs/indexes.d.ts +3 -0
  83. package/dist/cjs/indexes.js +20 -0
  84. package/dist/cjs/indexes.js.map +1 -0
  85. package/dist/cjs/metadata/AckInfoStore.js +1 -1
  86. package/dist/cjs/metadata/AckInfoStore.js.map +1 -1
  87. package/dist/cjs/metadata/BaselinesStore.d.ts +4 -1
  88. package/dist/cjs/metadata/BaselinesStore.js +19 -10
  89. package/dist/cjs/metadata/BaselinesStore.js.map +1 -1
  90. package/dist/cjs/metadata/LocalReplicaStore.d.ts +1 -1
  91. package/dist/cjs/metadata/LocalReplicaStore.js +11 -5
  92. package/dist/cjs/metadata/LocalReplicaStore.js.map +1 -1
  93. package/dist/cjs/metadata/Metadata.d.ts +26 -5
  94. package/dist/cjs/metadata/Metadata.js +55 -18
  95. package/dist/cjs/metadata/Metadata.js.map +1 -1
  96. package/dist/cjs/metadata/OperationsStore.d.ts +3 -0
  97. package/dist/cjs/metadata/OperationsStore.js +35 -15
  98. package/dist/cjs/metadata/OperationsStore.js.map +1 -1
  99. package/dist/cjs/migration/openDatabase.js +31 -10
  100. package/dist/cjs/migration/openDatabase.js.map +1 -1
  101. package/dist/cjs/queries/BaseQuery.js +14 -2
  102. package/dist/cjs/queries/BaseQuery.js.map +1 -1
  103. package/dist/cjs/queries/CollectionQueries.d.ts +2 -4
  104. package/dist/cjs/queries/CollectionQueries.js +1 -1
  105. package/dist/cjs/queries/CollectionQueries.js.map +1 -1
  106. package/dist/cjs/queries/FindAllQuery.js +1 -0
  107. package/dist/cjs/queries/FindAllQuery.js.map +1 -1
  108. package/dist/cjs/queries/QueryCache.d.ts +1 -0
  109. package/dist/cjs/queries/QueryCache.js +4 -0
  110. package/dist/cjs/queries/QueryCache.js.map +1 -1
  111. package/dist/cjs/queries/QueryableStorage.d.ts +20 -0
  112. package/dist/cjs/queries/QueryableStorage.js +84 -0
  113. package/dist/cjs/queries/QueryableStorage.js.map +1 -0
  114. package/dist/cjs/queries/dbQueries.js +13 -3
  115. package/dist/cjs/queries/dbQueries.js.map +1 -1
  116. package/dist/cjs/queries/utils.js +1 -1
  117. package/dist/cjs/queries/utils.js.map +1 -1
  118. package/dist/cjs/sync/FileSync.d.ts +1 -0
  119. package/dist/cjs/sync/FileSync.js +1 -0
  120. package/dist/cjs/sync/FileSync.js.map +1 -1
  121. package/dist/cjs/sync/PushPullSync.d.ts +2 -1
  122. package/dist/cjs/sync/PushPullSync.js +7 -1
  123. package/dist/cjs/sync/PushPullSync.js.map +1 -1
  124. package/dist/cjs/sync/Sync.d.ts +6 -3
  125. package/dist/cjs/sync/Sync.js +9 -4
  126. package/dist/cjs/sync/Sync.js.map +1 -1
  127. package/dist/cjs/sync/WebSocketSync.d.ts +4 -1
  128. package/dist/cjs/sync/WebSocketSync.js +41 -11
  129. package/dist/cjs/sync/WebSocketSync.js.map +1 -1
  130. package/dist/esm/DocumentManager.d.ts +6 -5
  131. package/dist/esm/DocumentManager.js +2 -2
  132. package/dist/esm/DocumentManager.js.map +1 -1
  133. package/dist/esm/IDBService.d.ts +28 -7
  134. package/dist/esm/IDBService.js +51 -14
  135. package/dist/esm/IDBService.js.map +1 -1
  136. package/dist/esm/UndoHistory.d.ts +1 -1
  137. package/dist/esm/UndoHistory.js +6 -2
  138. package/dist/esm/UndoHistory.js.map +1 -1
  139. package/dist/esm/__tests__/batching.test.js +3 -1
  140. package/dist/esm/__tests__/batching.test.js.map +1 -1
  141. package/dist/esm/__tests__/documents.test.js +37 -6
  142. package/dist/esm/__tests__/documents.test.js.map +1 -1
  143. package/dist/esm/__tests__/fixtures/testStorage.d.ts +2 -2
  144. package/dist/esm/__tests__/fixtures/testStorage.js +2 -1
  145. package/dist/esm/__tests__/fixtures/testStorage.js.map +1 -1
  146. package/dist/esm/__tests__/legacyOids.test.js +50 -17
  147. package/dist/esm/__tests__/legacyOids.test.js.map +1 -1
  148. package/dist/esm/__tests__/mutations.test.js +9 -3
  149. package/dist/esm/__tests__/mutations.test.js.map +1 -1
  150. package/dist/esm/__tests__/queries.test.js +6 -2
  151. package/dist/esm/__tests__/queries.test.js.map +1 -1
  152. package/dist/esm/__tests__/setup/indexedDB.d.ts +1 -1
  153. package/dist/esm/__tests__/setup/indexedDB.js +8 -1
  154. package/dist/esm/__tests__/setup/indexedDB.js.map +1 -1
  155. package/dist/esm/__tests__/undo.test.js +16 -9
  156. package/dist/esm/__tests__/undo.test.js.map +1 -1
  157. package/dist/esm/client/Client.d.ts +2 -3
  158. package/dist/esm/client/Client.js +8 -4
  159. package/dist/esm/client/Client.js.map +1 -1
  160. package/dist/esm/client/ClientDescriptor.js +21 -6
  161. package/dist/esm/client/ClientDescriptor.js.map +1 -1
  162. package/dist/esm/context.d.ts +10 -1
  163. package/dist/esm/entities/2/Entity.d.ts +148 -0
  164. package/dist/esm/entities/2/Entity.js +707 -0
  165. package/dist/esm/entities/2/Entity.js.map +1 -0
  166. package/dist/esm/entities/2/Entity.test.d.ts +1 -0
  167. package/dist/esm/entities/2/Entity.test.js +192 -0
  168. package/dist/esm/entities/2/Entity.test.js.map +1 -0
  169. package/dist/esm/entities/2/EntityCache.d.ts +15 -0
  170. package/dist/esm/entities/2/EntityCache.js +35 -0
  171. package/dist/esm/entities/2/EntityCache.js.map +1 -0
  172. package/dist/esm/entities/2/EntityMetadata.d.ts +68 -0
  173. package/dist/esm/entities/2/EntityMetadata.js +256 -0
  174. package/dist/esm/entities/2/EntityMetadata.js.map +1 -0
  175. package/dist/esm/entities/2/EntityStore.d.ts +78 -0
  176. package/dist/esm/entities/2/EntityStore.js +348 -0
  177. package/dist/esm/entities/2/EntityStore.js.map +1 -0
  178. package/dist/esm/entities/2/OperationBatcher.d.ts +52 -0
  179. package/dist/esm/entities/2/OperationBatcher.js +161 -0
  180. package/dist/esm/entities/2/OperationBatcher.js.map +1 -0
  181. package/dist/esm/entities/2/types.d.ts +84 -0
  182. package/dist/esm/entities/2/types.js +2 -0
  183. package/dist/esm/entities/2/types.js.map +1 -0
  184. package/dist/esm/entities/Entity.d.ts +0 -7
  185. package/dist/esm/entities/Entity.js +7 -0
  186. package/dist/esm/entities/Entity.js.map +1 -1
  187. package/dist/esm/entities/EntityStore.js +4 -20
  188. package/dist/esm/entities/EntityStore.js.map +1 -1
  189. package/dist/esm/entities/FakeWeakRef.d.ts +11 -0
  190. package/dist/esm/entities/FakeWeakRef.js +15 -0
  191. package/dist/esm/entities/FakeWeakRef.js.map +1 -0
  192. package/dist/esm/files/EntityFile.d.ts +5 -2
  193. package/dist/esm/files/EntityFile.js +8 -4
  194. package/dist/esm/files/EntityFile.js.map +1 -1
  195. package/dist/esm/files/FileManager.d.ts +3 -1
  196. package/dist/esm/files/FileManager.js +5 -3
  197. package/dist/esm/files/FileManager.js.map +1 -1
  198. package/dist/esm/files/FileStorage.js +7 -7
  199. package/dist/esm/files/FileStorage.js.map +1 -1
  200. package/dist/esm/files/utils.d.ts +2 -0
  201. package/dist/esm/files/utils.js +4 -2
  202. package/dist/esm/files/utils.js.map +1 -1
  203. package/dist/esm/idb.d.ts +2 -0
  204. package/dist/esm/idb.js +47 -3
  205. package/dist/esm/idb.js.map +1 -1
  206. package/dist/esm/index.d.ts +2 -2
  207. package/dist/esm/index.js +1 -1
  208. package/dist/esm/index.js.map +1 -1
  209. package/dist/esm/indexes.d.ts +3 -0
  210. package/dist/esm/indexes.js +15 -0
  211. package/dist/esm/indexes.js.map +1 -0
  212. package/dist/esm/metadata/AckInfoStore.js +1 -1
  213. package/dist/esm/metadata/AckInfoStore.js.map +1 -1
  214. package/dist/esm/metadata/BaselinesStore.d.ts +4 -1
  215. package/dist/esm/metadata/BaselinesStore.js +19 -10
  216. package/dist/esm/metadata/BaselinesStore.js.map +1 -1
  217. package/dist/esm/metadata/LocalReplicaStore.d.ts +1 -1
  218. package/dist/esm/metadata/LocalReplicaStore.js +11 -5
  219. package/dist/esm/metadata/LocalReplicaStore.js.map +1 -1
  220. package/dist/esm/metadata/Metadata.d.ts +26 -5
  221. package/dist/esm/metadata/Metadata.js +56 -19
  222. package/dist/esm/metadata/Metadata.js.map +1 -1
  223. package/dist/esm/metadata/OperationsStore.d.ts +3 -0
  224. package/dist/esm/metadata/OperationsStore.js +35 -15
  225. package/dist/esm/metadata/OperationsStore.js.map +1 -1
  226. package/dist/esm/migration/openDatabase.js +32 -11
  227. package/dist/esm/migration/openDatabase.js.map +1 -1
  228. package/dist/esm/queries/BaseQuery.js +14 -2
  229. package/dist/esm/queries/BaseQuery.js.map +1 -1
  230. package/dist/esm/queries/CollectionQueries.d.ts +2 -4
  231. package/dist/esm/queries/CollectionQueries.js +1 -1
  232. package/dist/esm/queries/CollectionQueries.js.map +1 -1
  233. package/dist/esm/queries/FindAllQuery.js +1 -0
  234. package/dist/esm/queries/FindAllQuery.js.map +1 -1
  235. package/dist/esm/queries/QueryCache.d.ts +1 -0
  236. package/dist/esm/queries/QueryCache.js +4 -0
  237. package/dist/esm/queries/QueryCache.js.map +1 -1
  238. package/dist/esm/queries/QueryableStorage.d.ts +20 -0
  239. package/dist/esm/queries/QueryableStorage.js +80 -0
  240. package/dist/esm/queries/QueryableStorage.js.map +1 -0
  241. package/dist/esm/queries/dbQueries.js +13 -3
  242. package/dist/esm/queries/dbQueries.js.map +1 -1
  243. package/dist/esm/queries/utils.js +1 -1
  244. package/dist/esm/queries/utils.js.map +1 -1
  245. package/dist/esm/sync/FileSync.d.ts +1 -0
  246. package/dist/esm/sync/FileSync.js +1 -0
  247. package/dist/esm/sync/FileSync.js.map +1 -1
  248. package/dist/esm/sync/PushPullSync.d.ts +2 -1
  249. package/dist/esm/sync/PushPullSync.js +7 -1
  250. package/dist/esm/sync/PushPullSync.js.map +1 -1
  251. package/dist/esm/sync/Sync.d.ts +6 -3
  252. package/dist/esm/sync/Sync.js +9 -4
  253. package/dist/esm/sync/Sync.js.map +1 -1
  254. package/dist/esm/sync/WebSocketSync.d.ts +4 -1
  255. package/dist/esm/sync/WebSocketSync.js +41 -11
  256. package/dist/esm/sync/WebSocketSync.js.map +1 -1
  257. package/dist/tsconfig-cjs.tsbuildinfo +1 -1
  258. package/dist/tsconfig.tsbuildinfo +1 -1
  259. package/package.json +8 -7
  260. package/src/DocumentManager.ts +3 -7
  261. package/src/IDBService.ts +78 -17
  262. package/src/UndoHistory.ts +5 -3
  263. package/src/__tests__/batching.test.ts +5 -2
  264. package/src/__tests__/documents.test.ts +44 -6
  265. package/src/__tests__/fixtures/testStorage.ts +3 -0
  266. package/src/__tests__/legacyOids.test.ts +53 -17
  267. package/src/__tests__/mutations.test.ts +9 -3
  268. package/src/__tests__/queries.test.ts +6 -2
  269. package/src/__tests__/setup/indexedDB.ts +8 -1
  270. package/src/__tests__/undo.test.ts +17 -9
  271. package/src/client/Client.ts +8 -4
  272. package/src/client/ClientDescriptor.ts +24 -8
  273. package/src/context.ts +16 -1
  274. package/src/entities/2/Entity.test.ts +218 -0
  275. package/src/entities/2/Entity.ts +954 -0
  276. package/src/entities/2/EntityCache.ts +41 -0
  277. package/src/entities/2/EntityMetadata.ts +364 -0
  278. package/src/entities/2/EntityStore.ts +490 -0
  279. package/src/entities/2/NOTES.md +22 -0
  280. package/src/entities/2/OperationBatcher.ts +251 -0
  281. package/src/entities/2/types.ts +154 -0
  282. package/src/files/EntityFile.ts +9 -4
  283. package/src/files/FileManager.ts +5 -3
  284. package/src/files/FileStorage.ts +7 -13
  285. package/src/files/utils.ts +6 -2
  286. package/src/idb.ts +51 -3
  287. package/src/index.ts +2 -2
  288. package/src/metadata/AckInfoStore.ts +1 -1
  289. package/src/metadata/BaselinesStore.ts +16 -24
  290. package/src/metadata/LocalReplicaStore.ts +13 -6
  291. package/src/metadata/Metadata.ts +109 -24
  292. package/src/metadata/OperationsStore.ts +37 -16
  293. package/src/migration/openDatabase.ts +32 -10
  294. package/src/queries/BaseQuery.ts +15 -2
  295. package/src/queries/CollectionQueries.ts +3 -3
  296. package/src/queries/FindAllQuery.ts +4 -0
  297. package/src/queries/QueryCache.ts +5 -0
  298. package/src/queries/QueryableStorage.ts +107 -0
  299. package/src/queries/dbQueries.ts +10 -3
  300. package/src/queries/utils.ts +1 -1
  301. package/src/sync/FileSync.ts +2 -0
  302. package/src/sync/PushPullSync.ts +8 -1
  303. package/src/sync/Sync.ts +14 -6
  304. package/src/sync/WebSocketSync.ts +47 -10
  305. package/src/entities/DocumentFamiliyCache.ts +0 -426
  306. package/src/entities/Entity.ts +0 -874
  307. package/src/entities/EntityStore.ts +0 -731
@@ -1,426 +0,0 @@
1
- import {
2
- applyPatch,
3
- assignOid,
4
- cloneDeep,
5
- compareTimestampSchemaVersions,
6
- DocumentBaseline,
7
- EventSubscriber,
8
- ObjectIdentifier,
9
- Operation,
10
- StorageFieldSchema,
11
- } from '@verdant-web/common';
12
- import { Entity, refreshEntity, StoreTools } from './Entity.js';
13
- import type { EntityStore } from './EntityStore.js';
14
- import { Context } from '../context.js';
15
- import { TaggedOperation } from '../types.js';
16
- import { Resolvable } from '../utils/Resolvable.js';
17
-
18
- /**
19
- * Local operations: operations on this client that haven't
20
- * yet been synced and are only applied in-memory. These are
21
- * always applied after all other operations, they are conceptually
22
- * 'in the future' as they are not yet synced or stored.
23
- *
24
- * Confirmed operations: operations that have been synced and
25
- * stored in the database.
26
- *
27
- * Unconfirmed operations: operations received from sync which
28
- * have not been stored yet and are only in memory. These exist
29
- * because new incoming operations are synchronously applied to
30
- * cached entities while storage goes on async.
31
- */
32
- export class DocumentFamilyCache extends EventSubscriber<
33
- Record<`change:${string}`, () => void> & {
34
- 'change:*': (oid: ObjectIdentifier) => void;
35
- }
36
- > {
37
- readonly oid: ObjectIdentifier;
38
- private operationsMap: Map<ObjectIdentifier, TaggedOperation[]>;
39
- private localOperationsMap: Map<ObjectIdentifier, Operation[]>;
40
- private baselinesMap: Map<ObjectIdentifier, DocumentBaseline>;
41
-
42
- private entities: Map<ObjectIdentifier, WeakRef<Entity>> = new Map();
43
-
44
- private context;
45
- private storeTools: StoreTools;
46
-
47
- private _initialized = new Resolvable<boolean>();
48
- get initializedPromise() {
49
- return this._initialized.promise;
50
- }
51
- setInitialized = () => {
52
- this._initialized.resolve(true);
53
- };
54
-
55
- constructor({
56
- oid,
57
- store,
58
- context,
59
- }: {
60
- oid: ObjectIdentifier;
61
- store: EntityStore;
62
- context: Context;
63
- }) {
64
- super();
65
- this.oid = oid;
66
- this.operationsMap = new Map();
67
- this.localOperationsMap = new Map();
68
- this.baselinesMap = new Map();
69
- this.storeTools = {
70
- addLocalOperations: store.addLocalOperations,
71
- patchCreator: store.meta.patchCreator,
72
- addFile: store.files.add,
73
- getFile: store.files.get,
74
- time: store.meta.time,
75
- now: store.meta.now,
76
- };
77
- this.context = context;
78
- }
79
-
80
- get weakRef() {
81
- return this.context.weakRef;
82
- }
83
-
84
- insertLocalOperations = (operations: Operation[]) => {
85
- const oidSet = new Set<ObjectIdentifier>();
86
- for (const operation of operations) {
87
- const { oid } = operation;
88
- oidSet.add(oid);
89
- const existingOperations = this.localOperationsMap.get(oid) || [];
90
- existingOperations.push(operation);
91
- this.localOperationsMap.set(oid, existingOperations);
92
- }
93
- for (const oid of oidSet) {
94
- const entityRef = this.entities.get(oid);
95
- const entity = entityRef?.deref();
96
- if (entity) {
97
- refreshEntity(entity, { isLocal: true });
98
- this.emit(`change:${oid}`);
99
- this.emit('change:*', oid);
100
- }
101
- }
102
- };
103
-
104
- insertOperations = (
105
- operations: TaggedOperation[],
106
- info: {
107
- isLocal: boolean;
108
- affectedOids?: Set<ObjectIdentifier>;
109
- },
110
- ) => {
111
- for (const operation of operations) {
112
- const { oid } = operation;
113
- info.affectedOids?.add(oid);
114
- const existingOperations = this.operationsMap.get(oid) || [];
115
- // insert in order of timestamp
116
- const index = existingOperations.findIndex(
117
- (op) => op.timestamp >= operation.timestamp,
118
- );
119
- // ensure the operation doesn't already exist in this position
120
- if (index !== -1) {
121
- if (existingOperations[index].timestamp === operation.timestamp) {
122
- continue;
123
- }
124
- existingOperations.splice(index, 0, operation);
125
- } else {
126
- existingOperations.push(operation);
127
- }
128
- this.operationsMap.set(oid, existingOperations);
129
-
130
- // FIXME: seems inefficient
131
- const unconfirmedOperations = this.localOperationsMap.get(oid);
132
- if (unconfirmedOperations) {
133
- this.localOperationsMap.set(
134
- oid,
135
- unconfirmedOperations.filter(
136
- (op) => op.timestamp !== operation.timestamp,
137
- ),
138
- );
139
- }
140
- }
141
- };
142
-
143
- /**
144
- * Insert new baselines for objects in this family.
145
- * Automatically drops operations before the new baseline.
146
- */
147
- insertBaselines = (
148
- baselines: DocumentBaseline[],
149
- {
150
- affectedOids,
151
- }: { isLocal: boolean; affectedOids?: Set<ObjectIdentifier> },
152
- ) => {
153
- for (const baseline of baselines) {
154
- const { oid } = baseline;
155
- // opt out if our baseline is newer.
156
- const existing = this.baselinesMap.get(oid);
157
- if (existing?.timestamp && existing.timestamp >= baseline.timestamp) {
158
- continue;
159
- }
160
-
161
- affectedOids?.add(oid);
162
- this.baselinesMap.set(oid, baseline);
163
- // drop operations before the baseline
164
- const ops = this.operationsMap.get(oid) || [];
165
- while (ops[0]?.timestamp < baseline.timestamp) {
166
- ops.shift();
167
- }
168
- }
169
- };
170
-
171
- addData = ({
172
- operations,
173
- baselines,
174
- reset,
175
- isLocal,
176
- }: {
177
- operations: TaggedOperation[];
178
- baselines: DocumentBaseline[];
179
- reset?: boolean;
180
- isLocal?: boolean;
181
- }) => {
182
- if (reset) {
183
- this.operationsMap.clear();
184
- this.baselinesMap.clear();
185
- }
186
- const info = {
187
- isLocal: isLocal || false,
188
- affectedOids: new Set<ObjectIdentifier>(),
189
- };
190
- this.insertBaselines(baselines, info);
191
- // for reset scenario, don't immediately update entities;
192
- // we will update all of them in one go.
193
- this.insertOperations(operations, info);
194
- if (reset) {
195
- for (const entityRef of this.entities.values()) {
196
- const entity = entityRef.deref();
197
- if (entity) {
198
- refreshEntity(entity, info);
199
- }
200
- }
201
- } else {
202
- for (const oid of info.affectedOids) {
203
- const entityRef = this.entities.get(oid);
204
- const entity = entityRef?.deref();
205
- if (entity) {
206
- refreshEntity(entity, info);
207
- this.emit(`change:${oid}`);
208
- this.emit('change:*', oid);
209
- }
210
- }
211
- }
212
- };
213
-
214
- private applyOperations = (
215
- view: any,
216
- deleted: boolean,
217
- operations: Operation[],
218
- after?: string,
219
- ): {
220
- view: any;
221
- deleted: boolean;
222
- empty: boolean;
223
- } => {
224
- let futureSeen: string | undefined = undefined;
225
- const now = this.storeTools.now;
226
- for (const operation of operations) {
227
- if (after && operation.timestamp <= after) {
228
- continue;
229
- }
230
- if (compareTimestampSchemaVersions(operation.timestamp, now) > 0) {
231
- // we don't apply patches from future versions
232
- futureSeen = operation.timestamp;
233
- continue;
234
- }
235
- if (operation.data.op === 'delete') {
236
- deleted = true;
237
- } else {
238
- view = applyPatch(view, operation.data);
239
- if (operation.data.op === 'initialize') {
240
- deleted = false;
241
- }
242
- }
243
- }
244
- if (futureSeen) {
245
- this.context.globalEvents.emit('futureSeen', futureSeen);
246
- }
247
- return { view, deleted, empty: !view && !operations.length };
248
- };
249
-
250
- computeView = (oid: ObjectIdentifier) => {
251
- if (
252
- this.baselinesMap.size === 0 &&
253
- this.operationsMap.size === 0 &&
254
- this.localOperationsMap.size === 0
255
- ) {
256
- this.context.log('debug', `Entity ${oid} accessed with no data at all`);
257
- return { view: null, deleted: true, lastTimestamp: null };
258
- }
259
- const confirmed = this.computeConfirmedView(oid);
260
- const unconfirmedOperations = this.localOperationsMap.get(oid) || [];
261
- if (confirmed.empty && !unconfirmedOperations.length) {
262
- this.context.log(
263
- 'debug',
264
- `Entity ${oid} accessed with no local data at all`,
265
- );
266
- return { view: null, deleted: true, lastTimestamp: null };
267
- }
268
- let { view, deleted } = this.applyOperations(
269
- confirmed.view,
270
- confirmed.deleted,
271
- unconfirmedOperations,
272
- );
273
- if (view) {
274
- assignOid(view, oid);
275
- }
276
- return { view, deleted, lastTimestamp: this.getLastTimestamp(oid) };
277
- };
278
-
279
- computeConfirmedView = (
280
- oid: ObjectIdentifier,
281
- ): {
282
- view: any;
283
- deleted: boolean;
284
- empty: boolean;
285
- } => {
286
- const baseline = this.baselinesMap.get(oid);
287
- const operations = this.operationsMap.get(oid) || [];
288
- const snapshot = cloneDeep(baseline?.snapshot || undefined);
289
- const result = this.applyOperations(
290
- snapshot,
291
- !snapshot,
292
- operations,
293
- baseline?.timestamp,
294
- );
295
- if (result.view) {
296
- assignOid(result.view, oid);
297
- }
298
- if (result.empty) {
299
- this.context.log(
300
- 'debug',
301
- `Entity ${oid} accessed with no confirmed data`,
302
- );
303
- }
304
- return result;
305
- };
306
-
307
- getLastTimestamp = (oid: ObjectIdentifier) => {
308
- let operations = this.localOperationsMap.get(oid);
309
- if (!operations?.length) {
310
- operations = this.operationsMap.get(oid) || [];
311
- }
312
- let logicalTimestamp: string | null = null;
313
- if (operations.length) {
314
- logicalTimestamp = operations[operations.length - 1]?.timestamp;
315
- } else {
316
- logicalTimestamp = this.baselinesMap.get(oid)?.timestamp ?? null;
317
- }
318
- if (!logicalTimestamp) return null;
319
- return this.storeTools.time.getWallClockTime(logicalTimestamp);
320
- };
321
-
322
- getEntity = ({
323
- oid,
324
- fieldSchema: schema,
325
- parent,
326
- readonlyKeys,
327
- fieldKey,
328
- }: {
329
- oid: ObjectIdentifier;
330
- fieldSchema: StorageFieldSchema;
331
- parent?: Entity;
332
- readonlyKeys?: string[];
333
- fieldKey?: string;
334
- }): Entity => {
335
- let entityRef = this.entities.get(oid);
336
- let entity = entityRef?.deref();
337
- if (!entity) {
338
- entity = new Entity({
339
- oid,
340
- cache: this,
341
- fieldSchema: schema,
342
- store: this.storeTools,
343
- parent,
344
- readonlyKeys,
345
- fieldPath: fieldKey
346
- ? [...(parent?.fieldPath ?? []), fieldKey]
347
- : undefined,
348
- });
349
-
350
- // immediately add to cache and queue a removal if nobody subscribed
351
- this.entities.set(oid, this.context.weakRef(entity));
352
- }
353
-
354
- return entity as any;
355
- };
356
-
357
- hasOid = (oid: ObjectIdentifier) => {
358
- return this.operationsMap.has(oid) || this.baselinesMap.has(oid);
359
- };
360
-
361
- dispose = () => {
362
- this.entities.forEach((entity) => entity.deref()?.dispose());
363
- this.entities.clear();
364
- };
365
-
366
- reset = ({
367
- operations,
368
- baselines,
369
- dropExistingUnconfirmed: dropUnconfirmed = false,
370
- unconfirmedOperations,
371
- dropAll,
372
- }: {
373
- operations: TaggedOperation[];
374
- unconfirmedOperations?: Operation[];
375
- baselines: DocumentBaseline[];
376
- /**
377
- * Whether to drop operations which are only in-memory. Unconfirmed operations
378
- * will not be restored from storage until they are persisted, so it's not advisable
379
- * to use this unless the intention is to completely clear the entities.
380
- */
381
- dropExistingUnconfirmed?: boolean;
382
- /**
383
- * Drop unconfirmed and confirmed data before resetting to incoming data.
384
- * This is dangerous due to race conditions. Only use when a full reset is
385
- * required.
386
- */
387
- dropAll?: boolean;
388
- }) => {
389
- this.context.log(
390
- 'debug',
391
- `Resetting cache for ${this.oid} with ${operations.length} ops and ${baselines.length} baselines, dropUnconfirmed=${dropUnconfirmed}`,
392
- );
393
- const info = { isLocal: false, affectedOids: new Set<ObjectIdentifier>() };
394
-
395
- // NOTE: not clearing these maps... there are even more
396
- // race conditions where we begin opening a cache, queue up a
397
- // reset, then receive incoming operations before the reset
398
- // actually hits this function, so those incoming ops are
399
- // dropped.
400
- // FIXME: include this in a future refactor of this
401
- // whole system.
402
-
403
- if (dropAll) this.baselinesMap.clear();
404
- this.insertBaselines(baselines, info);
405
-
406
- if (dropAll) this.operationsMap.clear();
407
- this.insertOperations(operations, info);
408
-
409
- if (unconfirmedOperations || dropUnconfirmed) {
410
- this.localOperationsMap.clear();
411
- if (unconfirmedOperations) {
412
- this.insertLocalOperations(unconfirmedOperations);
413
- }
414
- }
415
-
416
- for (const oid of this.entities.keys()) {
417
- const entityRef = this.entities.get(oid);
418
- const entity = entityRef?.deref();
419
- if (entity) {
420
- refreshEntity(entity, info);
421
- this.emit(`change:${oid}`);
422
- this.emit('change:*', oid);
423
- }
424
- }
425
- };
426
- }