@verdant-web/store 3.12.1 → 4.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 (279) hide show
  1. package/dist/bundle/index.js +11 -13
  2. package/dist/bundle/index.js.map +4 -4
  3. package/dist/esm/__tests__/batching.test.js +5 -5
  4. package/dist/esm/__tests__/batching.test.js.map +1 -1
  5. package/dist/esm/__tests__/entities.test.js +1 -1
  6. package/dist/esm/__tests__/entities.test.js.map +1 -1
  7. package/dist/esm/__tests__/fixtures/testStorage.d.ts +1 -3
  8. package/dist/esm/__tests__/fixtures/testStorage.js +3 -3
  9. package/dist/esm/__tests__/fixtures/testStorage.js.map +1 -1
  10. package/dist/esm/__tests__/queries.test.js.map +1 -1
  11. package/dist/esm/backup.d.ts +3 -4
  12. package/dist/esm/backup.js.map +1 -1
  13. package/dist/esm/client/Client.d.ts +28 -33
  14. package/dist/esm/client/Client.js +50 -161
  15. package/dist/esm/client/Client.js.map +1 -1
  16. package/dist/esm/client/ClientDescriptor.d.ts +8 -11
  17. package/dist/esm/client/ClientDescriptor.js +39 -141
  18. package/dist/esm/client/ClientDescriptor.js.map +1 -1
  19. package/dist/esm/context/Time.d.ts +13 -0
  20. package/dist/esm/context/Time.js +27 -0
  21. package/dist/esm/context/Time.js.map +1 -0
  22. package/dist/esm/context/context.d.ts +170 -0
  23. package/dist/esm/{context.js.map → context/context.js.map} +1 -1
  24. package/dist/esm/entities/DocumentManager.js.map +1 -1
  25. package/dist/esm/entities/Entity.d.ts +4 -5
  26. package/dist/esm/entities/Entity.js +5 -3
  27. package/dist/esm/entities/Entity.js.map +1 -1
  28. package/dist/esm/entities/Entity.test.js +4 -3
  29. package/dist/esm/entities/Entity.test.js.map +1 -1
  30. package/dist/esm/entities/EntityCache.d.ts +0 -3
  31. package/dist/esm/entities/EntityCache.js +0 -9
  32. package/dist/esm/entities/EntityCache.js.map +1 -1
  33. package/dist/esm/entities/EntityMetadata.d.ts +1 -1
  34. package/dist/esm/entities/EntityMetadata.js +6 -5
  35. package/dist/esm/entities/EntityMetadata.js.map +1 -1
  36. package/dist/esm/entities/EntityStore.d.ts +2 -6
  37. package/dist/esm/entities/EntityStore.js +22 -16
  38. package/dist/esm/entities/EntityStore.js.map +1 -1
  39. package/dist/esm/entities/OperationBatcher.d.ts +2 -5
  40. package/dist/esm/entities/OperationBatcher.js +9 -7
  41. package/dist/esm/entities/OperationBatcher.js.map +1 -1
  42. package/dist/esm/errors.d.ts +8 -0
  43. package/dist/esm/errors.js +12 -0
  44. package/dist/esm/errors.js.map +1 -0
  45. package/dist/esm/files/EntityFile.d.ts +6 -3
  46. package/dist/esm/files/EntityFile.js +22 -19
  47. package/dist/esm/files/EntityFile.js.map +1 -1
  48. package/dist/esm/files/FileManager.d.ts +8 -39
  49. package/dist/esm/files/FileManager.js +15 -170
  50. package/dist/esm/files/FileManager.js.map +1 -1
  51. package/dist/esm/files/utils.d.ts +0 -1
  52. package/dist/esm/files/utils.js +0 -14
  53. package/dist/esm/files/utils.js.map +1 -1
  54. package/dist/esm/index.d.ts +1 -2
  55. package/dist/esm/index.js +0 -1
  56. package/dist/esm/index.js.map +1 -1
  57. package/dist/esm/{metadata → persistence}/MessageCreator.d.ts +5 -6
  58. package/dist/esm/{metadata → persistence}/MessageCreator.js +31 -38
  59. package/dist/esm/persistence/MessageCreator.js.map +1 -0
  60. package/dist/esm/persistence/PersistenceFiles.d.ts +48 -0
  61. package/dist/esm/persistence/PersistenceFiles.js +160 -0
  62. package/dist/esm/persistence/PersistenceFiles.js.map +1 -0
  63. package/dist/esm/persistence/PersistenceMetadata.d.ts +69 -0
  64. package/dist/esm/persistence/PersistenceMetadata.js +302 -0
  65. package/dist/esm/persistence/PersistenceMetadata.js.map +1 -0
  66. package/dist/esm/persistence/PersistenceQueries.d.ts +34 -0
  67. package/dist/esm/persistence/PersistenceQueries.js +15 -0
  68. package/dist/esm/persistence/PersistenceQueries.js.map +1 -0
  69. package/dist/esm/persistence/PersistenceRebaser.d.ts +32 -0
  70. package/dist/esm/persistence/PersistenceRebaser.js +120 -0
  71. package/dist/esm/persistence/PersistenceRebaser.js.map +1 -0
  72. package/dist/esm/{IDBService.d.ts → persistence/idb/IdbService.d.ts} +9 -7
  73. package/dist/esm/{IDBService.js → persistence/idb/IdbService.js} +29 -8
  74. package/dist/esm/persistence/idb/IdbService.js.map +1 -0
  75. package/dist/esm/persistence/idb/files/IdbPersistenceFileDb.d.ts +58 -0
  76. package/dist/esm/{files/FileStorage.js → persistence/idb/files/IdbPersistenceFileDb.js} +85 -50
  77. package/dist/esm/persistence/idb/files/IdbPersistenceFileDb.js.map +1 -0
  78. package/dist/esm/persistence/idb/idbPersistence.d.ts +19 -0
  79. package/dist/esm/persistence/idb/idbPersistence.js +80 -0
  80. package/dist/esm/persistence/idb/idbPersistence.js.map +1 -0
  81. package/dist/esm/persistence/idb/metadata/IdbMetadataDb.d.ts +72 -0
  82. package/dist/esm/persistence/idb/metadata/IdbMetadataDb.js +235 -0
  83. package/dist/esm/persistence/idb/metadata/IdbMetadataDb.js.map +1 -0
  84. package/dist/esm/{metadata → persistence/idb/metadata}/openMetadataDatabase.d.ts +3 -1
  85. package/dist/esm/{metadata → persistence/idb/metadata}/openMetadataDatabase.js +12 -3
  86. package/dist/esm/persistence/idb/metadata/openMetadataDatabase.js.map +1 -0
  87. package/dist/esm/persistence/idb/queries/IdbQueryDb.d.ts +41 -0
  88. package/dist/esm/persistence/idb/queries/IdbQueryDb.js +174 -0
  89. package/dist/esm/persistence/idb/queries/IdbQueryDb.js.map +1 -0
  90. package/dist/esm/{migration → persistence/idb/queries/migration}/db.d.ts +1 -1
  91. package/dist/esm/{migration → persistence/idb/queries/migration}/db.js +10 -48
  92. package/dist/esm/persistence/idb/queries/migration/db.js.map +1 -0
  93. package/dist/esm/persistence/idb/queries/migration/engine.d.ts +12 -0
  94. package/dist/esm/{migration → persistence/idb/queries/migration}/engine.js +29 -46
  95. package/dist/esm/persistence/idb/queries/migration/engine.js.map +1 -0
  96. package/dist/esm/{migration → persistence/idb/queries/migration}/migrations.d.ts +1 -3
  97. package/dist/esm/{migration → persistence/idb/queries/migration}/migrations.js +11 -10
  98. package/dist/esm/persistence/idb/queries/migration/migrations.js.map +1 -0
  99. package/dist/esm/{migration → persistence/idb/queries/migration}/openQueryDatabase.d.ts +1 -3
  100. package/dist/esm/{migration → persistence/idb/queries/migration}/openQueryDatabase.js +4 -7
  101. package/dist/esm/persistence/idb/queries/migration/openQueryDatabase.js.map +1 -0
  102. package/dist/esm/{migration → persistence/idb/queries/migration}/paths.js +2 -2
  103. package/dist/esm/persistence/idb/queries/migration/paths.js.map +1 -0
  104. package/dist/esm/persistence/idb/queries/migration/paths.test.js.map +1 -0
  105. package/dist/esm/persistence/idb/queries/migration/types.d.ts +6 -0
  106. package/dist/esm/persistence/idb/queries/migration/types.js.map +1 -0
  107. package/dist/esm/persistence/idb/queries/ranges.d.ts +2 -0
  108. package/dist/esm/persistence/idb/queries/ranges.js +66 -0
  109. package/dist/esm/persistence/idb/queries/ranges.js.map +1 -0
  110. package/dist/esm/{idb.d.ts → persistence/idb/util.d.ts} +11 -0
  111. package/dist/esm/{idb.js → persistence/idb/util.js} +58 -1
  112. package/dist/esm/persistence/idb/util.js.map +1 -0
  113. package/dist/esm/persistence/interfaces.d.ts +181 -0
  114. package/dist/esm/persistence/interfaces.js +2 -0
  115. package/dist/esm/persistence/interfaces.js.map +1 -0
  116. package/dist/esm/persistence/persistence.d.ts +4 -0
  117. package/dist/esm/persistence/persistence.js +126 -0
  118. package/dist/esm/persistence/persistence.js.map +1 -0
  119. package/dist/esm/queries/BaseQuery.d.ts +2 -1
  120. package/dist/esm/queries/BaseQuery.js +3 -0
  121. package/dist/esm/queries/BaseQuery.js.map +1 -1
  122. package/dist/esm/queries/CollectionQueries.d.ts +1 -1
  123. package/dist/esm/queries/FindAllQuery.js +1 -3
  124. package/dist/esm/queries/FindAllQuery.js.map +1 -1
  125. package/dist/esm/queries/FindInfiniteQuery.js +2 -5
  126. package/dist/esm/queries/FindInfiniteQuery.js.map +1 -1
  127. package/dist/esm/queries/FindOneQuery.js +1 -3
  128. package/dist/esm/queries/FindOneQuery.js.map +1 -1
  129. package/dist/esm/queries/FindPageQuery.js +1 -3
  130. package/dist/esm/queries/FindPageQuery.js.map +1 -1
  131. package/dist/esm/queries/QueryCache.d.ts +1 -1
  132. package/dist/esm/queries/QueryCache.js +4 -0
  133. package/dist/esm/queries/QueryCache.js.map +1 -1
  134. package/dist/esm/sync/FileSync.d.ts +23 -8
  135. package/dist/esm/sync/FileSync.js +76 -28
  136. package/dist/esm/sync/FileSync.js.map +1 -1
  137. package/dist/esm/sync/PresenceManager.d.ts +4 -3
  138. package/dist/esm/sync/PresenceManager.js +2 -2
  139. package/dist/esm/sync/PresenceManager.js.map +1 -1
  140. package/dist/esm/sync/PushPullSync.d.ts +4 -6
  141. package/dist/esm/sync/PushPullSync.js +13 -12
  142. package/dist/esm/sync/PushPullSync.js.map +1 -1
  143. package/dist/esm/sync/Sync.d.ts +9 -11
  144. package/dist/esm/sync/Sync.js +34 -29
  145. package/dist/esm/sync/Sync.js.map +1 -1
  146. package/dist/esm/sync/WebSocketSync.d.ts +4 -6
  147. package/dist/esm/sync/WebSocketSync.js +20 -22
  148. package/dist/esm/sync/WebSocketSync.js.map +1 -1
  149. package/dist/esm/utils/Disposable.d.ts +5 -2
  150. package/dist/esm/utils/Disposable.js +3 -2
  151. package/dist/esm/utils/Disposable.js.map +1 -1
  152. package/dist/esm/utils/wip.d.ts +2 -0
  153. package/dist/esm/utils/wip.js +5 -0
  154. package/dist/esm/utils/wip.js.map +1 -0
  155. package/package.json +2 -2
  156. package/src/__tests__/batching.test.ts +6 -6
  157. package/src/__tests__/entities.test.ts +1 -1
  158. package/src/__tests__/fixtures/testStorage.ts +2 -10
  159. package/src/__tests__/queries.test.ts +1 -1
  160. package/src/backup.ts +3 -4
  161. package/src/client/Client.ts +69 -226
  162. package/src/client/ClientDescriptor.ts +53 -184
  163. package/src/context/Time.ts +35 -0
  164. package/src/context/context.ts +200 -0
  165. package/src/entities/DocumentManager.ts +0 -3
  166. package/src/entities/Entity.test.ts +9 -9
  167. package/src/entities/Entity.ts +6 -12
  168. package/src/entities/EntityCache.ts +0 -9
  169. package/src/entities/EntityMetadata.ts +4 -4
  170. package/src/entities/EntityStore.ts +26 -29
  171. package/src/entities/OperationBatcher.ts +9 -11
  172. package/src/errors.ts +13 -0
  173. package/src/files/EntityFile.ts +16 -5
  174. package/src/files/FileManager.ts +18 -245
  175. package/src/files/utils.ts +0 -15
  176. package/src/index.ts +2 -1
  177. package/src/{metadata → persistence}/MessageCreator.ts +46 -36
  178. package/src/persistence/PersistenceFiles.ts +227 -0
  179. package/src/persistence/PersistenceMetadata.ts +425 -0
  180. package/src/persistence/PersistenceQueries.ts +22 -0
  181. package/src/persistence/PersistenceRebaser.ts +171 -0
  182. package/src/{IDBService.ts → persistence/idb/IdbService.ts} +45 -12
  183. package/src/{files/FileStorage.ts → persistence/idb/files/IdbPersistenceFileDb.ts} +128 -86
  184. package/src/persistence/idb/idbPersistence.ts +116 -0
  185. package/src/persistence/idb/metadata/IdbMetadataDb.ts +460 -0
  186. package/src/{metadata → persistence/idb/metadata}/openMetadataDatabase.ts +21 -4
  187. package/src/persistence/idb/queries/IdbQueryDb.ts +251 -0
  188. package/src/{migration → persistence/idb/queries/migration}/db.ts +18 -72
  189. package/src/{migration → persistence/idb/queries/migration}/engine.ts +39 -62
  190. package/src/{migration → persistence/idb/queries/migration}/migrations.ts +13 -18
  191. package/src/{migration → persistence/idb/queries/migration}/openQueryDatabase.ts +5 -14
  192. package/src/{migration → persistence/idb/queries/migration}/paths.ts +4 -3
  193. package/src/persistence/idb/queries/migration/types.ts +8 -0
  194. package/src/persistence/idb/queries/ranges.ts +107 -0
  195. package/src/{idb.ts → persistence/idb/util.ts} +75 -0
  196. package/src/persistence/interfaces.ts +240 -0
  197. package/src/persistence/persistence.ts +223 -0
  198. package/src/queries/BaseQuery.ts +5 -1
  199. package/src/queries/CollectionQueries.ts +2 -2
  200. package/src/queries/FindAllQuery.ts +1 -3
  201. package/src/queries/FindInfiniteQuery.ts +2 -5
  202. package/src/queries/FindOneQuery.ts +1 -3
  203. package/src/queries/FindPageQuery.ts +1 -3
  204. package/src/queries/QueryCache.ts +20 -1
  205. package/src/sync/FileSync.ts +93 -30
  206. package/src/sync/PresenceManager.ts +5 -7
  207. package/src/sync/PushPullSync.ts +23 -19
  208. package/src/sync/Sync.ts +45 -36
  209. package/src/sync/WebSocketSync.ts +41 -27
  210. package/src/utils/Disposable.ts +7 -4
  211. package/src/utils/wip.ts +5 -0
  212. package/dist/esm/IDBService.js.map +0 -1
  213. package/dist/esm/__tests__/legacyOids.test.d.ts +0 -1
  214. package/dist/esm/__tests__/legacyOids.test.js +0 -352
  215. package/dist/esm/__tests__/legacyOids.test.js.map +0 -1
  216. package/dist/esm/context.d.ts +0 -45
  217. package/dist/esm/files/FileStorage.d.ts +0 -47
  218. package/dist/esm/files/FileStorage.js.map +0 -1
  219. package/dist/esm/idb.js.map +0 -1
  220. package/dist/esm/metadata/AckInfoStore.d.ts +0 -10
  221. package/dist/esm/metadata/AckInfoStore.js +0 -22
  222. package/dist/esm/metadata/AckInfoStore.js.map +0 -1
  223. package/dist/esm/metadata/BaselinesStore.d.ts +0 -40
  224. package/dist/esm/metadata/BaselinesStore.js +0 -102
  225. package/dist/esm/metadata/BaselinesStore.js.map +0 -1
  226. package/dist/esm/metadata/LocalReplicaStore.d.ts +0 -19
  227. package/dist/esm/metadata/LocalReplicaStore.js +0 -56
  228. package/dist/esm/metadata/LocalReplicaStore.js.map +0 -1
  229. package/dist/esm/metadata/MessageCreator.js.map +0 -1
  230. package/dist/esm/metadata/Metadata.d.ts +0 -146
  231. package/dist/esm/metadata/Metadata.js +0 -452
  232. package/dist/esm/metadata/Metadata.js.map +0 -1
  233. package/dist/esm/metadata/OperationsStore.d.ts +0 -62
  234. package/dist/esm/metadata/OperationsStore.js +0 -175
  235. package/dist/esm/metadata/OperationsStore.js.map +0 -1
  236. package/dist/esm/metadata/SchemaStore.d.ts +0 -9
  237. package/dist/esm/metadata/SchemaStore.js +0 -35
  238. package/dist/esm/metadata/SchemaStore.js.map +0 -1
  239. package/dist/esm/metadata/openMetadataDatabase.js.map +0 -1
  240. package/dist/esm/migration/db.js.map +0 -1
  241. package/dist/esm/migration/engine.d.ts +0 -15
  242. package/dist/esm/migration/engine.js.map +0 -1
  243. package/dist/esm/migration/errors.d.ts +0 -5
  244. package/dist/esm/migration/errors.js +0 -8
  245. package/dist/esm/migration/errors.js.map +0 -1
  246. package/dist/esm/migration/migrations.js.map +0 -1
  247. package/dist/esm/migration/openQueryDatabase.js.map +0 -1
  248. package/dist/esm/migration/openWIPDatabase.d.ts +0 -11
  249. package/dist/esm/migration/openWIPDatabase.js +0 -65
  250. package/dist/esm/migration/openWIPDatabase.js.map +0 -1
  251. package/dist/esm/migration/paths.js.map +0 -1
  252. package/dist/esm/migration/paths.test.js.map +0 -1
  253. package/dist/esm/migration/types.d.ts +0 -3
  254. package/dist/esm/migration/types.js.map +0 -1
  255. package/dist/esm/queries/QueryableStorage.d.ts +0 -20
  256. package/dist/esm/queries/QueryableStorage.js +0 -90
  257. package/dist/esm/queries/QueryableStorage.js.map +0 -1
  258. package/dist/esm/queries/dbQueries.d.ts +0 -22
  259. package/dist/esm/queries/dbQueries.js +0 -130
  260. package/dist/esm/queries/dbQueries.js.map +0 -1
  261. package/src/__tests__/legacyOids.test.ts +0 -375
  262. package/src/context.ts +0 -55
  263. package/src/metadata/AckInfoStore.ts +0 -30
  264. package/src/metadata/BaselinesStore.ts +0 -188
  265. package/src/metadata/LocalReplicaStore.ts +0 -79
  266. package/src/metadata/Metadata.ts +0 -685
  267. package/src/metadata/OperationsStore.ts +0 -332
  268. package/src/metadata/SchemaStore.ts +0 -47
  269. package/src/migration/errors.ts +0 -7
  270. package/src/migration/openWIPDatabase.ts +0 -97
  271. package/src/migration/types.ts +0 -4
  272. package/src/queries/QueryableStorage.ts +0 -122
  273. package/src/queries/dbQueries.ts +0 -161
  274. /package/dist/esm/{context.js → context/context.js} +0 -0
  275. /package/dist/esm/{migration → persistence/idb/queries/migration}/paths.d.ts +0 -0
  276. /package/dist/esm/{migration → persistence/idb/queries/migration}/paths.test.d.ts +0 -0
  277. /package/dist/esm/{migration → persistence/idb/queries/migration}/paths.test.js +0 -0
  278. /package/dist/esm/{migration → persistence/idb/queries/migration}/types.js +0 -0
  279. /package/src/{migration → persistence/idb/queries/migration}/paths.test.ts +0 -0
@@ -1,332 +0,0 @@
1
- import {
2
- createCompoundIndexValue,
3
- createLowerBoundIndexValue,
4
- createUpperBoundIndexValue,
5
- getOidRoot,
6
- ObjectIdentifier,
7
- Operation,
8
- assert,
9
- } from '@verdant-web/common';
10
- import { IDBService } from '../IDBService.js';
11
- import { isAbortError } from '../idb.js';
12
-
13
- export type ClientOperation = Operation & {
14
- isLocal: boolean;
15
- };
16
-
17
- export type StoredClientOperation = ClientOperation & {
18
- oid_timestamp: string;
19
- l_t: string;
20
- d_t: string;
21
- };
22
-
23
- export class OperationsStore extends IDBService {
24
- constructor(db: IDBDatabase, opts: { log?: (...args: any[]) => void }) {
25
- super(db, opts);
26
- }
27
- /**
28
- * Iterates over every patch for the root and every sub-object
29
- * of a given document. Optionally limit by timestamp.
30
- */
31
- iterateOverAllOperationsForDocument = async (
32
- oid: ObjectIdentifier,
33
- iterator: (patch: StoredClientOperation, store: IDBObjectStore) => void,
34
- {
35
- to,
36
- from,
37
- after,
38
- mode = 'readonly',
39
- transaction: providedTx,
40
- }: {
41
- to?: string;
42
- from?: string;
43
- after?: string;
44
- mode?: 'readwrite' | 'readonly';
45
- transaction?: IDBTransaction;
46
- } = {},
47
- ): Promise<void> => {
48
- const transaction =
49
- providedTx ||
50
- this.createTransaction(['operations'], {
51
- mode,
52
- });
53
- const store = transaction.objectStore('operations');
54
- const index = store.index('d_t');
55
-
56
- const startTimestamp = from || after;
57
- const start = startTimestamp
58
- ? createCompoundIndexValue(oid, startTimestamp)
59
- : createLowerBoundIndexValue(oid);
60
- const end = to
61
- ? createCompoundIndexValue(oid, to)
62
- : createUpperBoundIndexValue(oid);
63
-
64
- const range = IDBKeyRange.bound(start, end, !!after, false);
65
-
66
- const request = index.openCursor(range, 'next');
67
- return new Promise<void>((resolve, reject) => {
68
- let previousTimestamp: string | undefined;
69
- request.onsuccess = (event) => {
70
- const cursor = request.result;
71
- if (cursor) {
72
- const value = cursor.value as StoredClientOperation;
73
- assert(value.oid.startsWith(oid));
74
- assert(
75
- previousTimestamp === undefined ||
76
- previousTimestamp <= value.timestamp,
77
- `expected ${previousTimestamp} <= ${value.timestamp}`,
78
- );
79
-
80
- iterator(value, store);
81
- previousTimestamp = value.timestamp;
82
- cursor.continue();
83
- } else {
84
- resolve();
85
- }
86
- };
87
- request.onerror = (event) => {
88
- if (isAbortError(request.error)) {
89
- resolve();
90
- return;
91
- } else {
92
- reject(request.error);
93
- }
94
- };
95
- });
96
- };
97
-
98
- iterateOverAllOperationsForEntity = async (
99
- oid: ObjectIdentifier,
100
- iterator: (patch: StoredClientOperation, store: IDBObjectStore) => void,
101
- {
102
- after,
103
- to,
104
- mode,
105
- transaction: providedTx,
106
- }: {
107
- after?: string;
108
- to?: string;
109
- mode?: 'readwrite' | 'readonly';
110
- transaction?: IDBTransaction;
111
- },
112
- ): Promise<void> => {
113
- const transaction =
114
- providedTx || this.createTransaction(['operations'], { mode });
115
- const store = transaction.objectStore('operations');
116
-
117
- const start = after
118
- ? createCompoundIndexValue(oid, after)
119
- : createLowerBoundIndexValue(oid);
120
- const end = to
121
- ? createCompoundIndexValue(oid, to)
122
- : createUpperBoundIndexValue(oid);
123
-
124
- const range = IDBKeyRange.bound(start, end, !!after, false);
125
-
126
- const request = store.openCursor(range, 'next');
127
- return new Promise<void>((resolve, reject) => {
128
- let previousTimestamp: string | undefined;
129
- request.onsuccess = (event) => {
130
- const cursor = request.result;
131
- if (cursor) {
132
- const value = cursor.value as StoredClientOperation;
133
- assert(value.oid.startsWith(oid));
134
- assert(
135
- previousTimestamp === undefined ||
136
- previousTimestamp <= value.timestamp,
137
- `expected ${previousTimestamp} <= ${value.timestamp}`,
138
- );
139
-
140
- iterator(value, store);
141
- previousTimestamp = value.timestamp;
142
- cursor.continue();
143
- } else {
144
- resolve();
145
- }
146
- };
147
- request.onerror = (event) => {
148
- if (isAbortError(request.error)) {
149
- resolve();
150
- } else {
151
- reject(request.error);
152
- }
153
- };
154
- });
155
- };
156
-
157
- iterateOverAllOperationsForCollection = async (
158
- collection: string,
159
- iterator: (patch: StoredClientOperation, store: IDBObjectStore) => void,
160
- {
161
- after,
162
- to,
163
- mode,
164
- transaction: providedTx,
165
- }: {
166
- after?: string;
167
- to?: string;
168
- mode?: 'readwrite' | 'readonly';
169
- transaction?: IDBTransaction;
170
- },
171
- ): Promise<void> => {
172
- const transaction =
173
- providedTx || this.createTransaction(['operations'], { mode });
174
-
175
- return this.iterate(
176
- 'operations',
177
- (store) => {
178
- return store.openCursor(
179
- IDBKeyRange.bound(collection, collection + '\uffff', false, false),
180
- 'next',
181
- );
182
- },
183
- iterator,
184
- { mode, transaction },
185
- );
186
- };
187
-
188
- iterateOverAllLocalOperations = async (
189
- iterator: (patch: ClientOperation, store: IDBObjectStore) => void,
190
- {
191
- before,
192
- after,
193
- mode = 'readonly',
194
- transaction: providedTx,
195
- }: {
196
- before?: string | null;
197
- after?: string | null;
198
- mode?: 'readwrite' | 'readonly';
199
- transaction?: IDBTransaction;
200
- },
201
- ): Promise<void> => {
202
- const transaction =
203
- providedTx || this.createTransaction(['operations'], { mode });
204
- const store = transaction.objectStore('operations');
205
- const index = store.index('l_t');
206
-
207
- const start = after
208
- ? createCompoundIndexValue(true, after)
209
- : createLowerBoundIndexValue(true);
210
- const end = before
211
- ? createCompoundIndexValue(true, before)
212
- : createUpperBoundIndexValue(true);
213
-
214
- const range = IDBKeyRange.bound(start, end, !!after, true);
215
-
216
- const request = index.openCursor(range, 'next');
217
- return new Promise<void>((resolve, reject) => {
218
- let previousTimestamp: string | undefined;
219
- request.onsuccess = (event) => {
220
- const cursor = request.result;
221
- if (cursor) {
222
- const value = cursor.value as StoredClientOperation;
223
- assert(
224
- previousTimestamp === undefined ||
225
- previousTimestamp <= value.timestamp,
226
- `expected ${previousTimestamp} <= ${value.timestamp}`,
227
- );
228
-
229
- iterator(value, store);
230
- previousTimestamp = value.timestamp;
231
- cursor.continue();
232
- } else {
233
- resolve();
234
- }
235
- };
236
- request.onerror = (event) => {
237
- if (isAbortError(request.error)) {
238
- resolve();
239
- } else {
240
- reject(request.error);
241
- }
242
- };
243
- });
244
- };
245
-
246
- iterateOverAllOperations = async (
247
- iterator: (patch: ClientOperation, store: IDBObjectStore) => void,
248
- {
249
- before,
250
- transaction,
251
- mode,
252
- from,
253
- }: {
254
- /** Ending timestamp, exclusive */
255
- before?: string | null;
256
- /** Starting timestamp, inclusive */
257
- from?: string | null;
258
- transaction?: IDBTransaction;
259
- mode?: 'readwrite' | 'readonly';
260
- },
261
- ): Promise<void> => {
262
- await this.iterate(
263
- 'operations',
264
- (store) => {
265
- const start = from ? createLowerBoundIndexValue(from) : undefined;
266
- const end = before ? createUpperBoundIndexValue(before) : undefined;
267
-
268
- const range =
269
- start && end
270
- ? IDBKeyRange.bound(start, end, false, true)
271
- : start
272
- ? IDBKeyRange.lowerBound(start, false)
273
- : end
274
- ? IDBKeyRange.upperBound(end, true)
275
- : undefined;
276
- const index = store.index('timestamp');
277
- return index.openCursor(range, 'next');
278
- },
279
- iterator,
280
- { mode, transaction },
281
- );
282
- };
283
-
284
- /**
285
- * Adds a set of patches to the database.
286
- * @returns a list of affected root document OIDs.
287
- */
288
- addOperations = async (
289
- patches: ClientOperation[],
290
- { transaction }: { transaction?: IDBTransaction } = {},
291
- ): Promise<ObjectIdentifier[]> => {
292
- return this.insert(patches.map(this.addCompoundIndexes), { transaction });
293
- };
294
-
295
- private addCompoundIndexes = (
296
- patch: ClientOperation,
297
- ): StoredClientOperation => {
298
- return {
299
- ...patch,
300
- oid_timestamp: createCompoundIndexValue(
301
- patch.oid,
302
- patch.timestamp,
303
- ) as string,
304
- l_t: createCompoundIndexValue(patch.isLocal, patch.timestamp) as string,
305
- d_t: createCompoundIndexValue(
306
- getOidRoot(patch.oid),
307
- patch.timestamp,
308
- ) as string,
309
- };
310
- };
311
-
312
- private insert = async (
313
- operations: StoredClientOperation[],
314
- { transaction }: { transaction?: IDBTransaction },
315
- ): Promise<ObjectIdentifier[]> => {
316
- const affected = new Set<ObjectIdentifier>();
317
- await this.runAll(
318
- 'operations',
319
- (store) =>
320
- operations.map((op) => {
321
- affected.add(getOidRoot(op.oid));
322
- return store.put(op);
323
- }),
324
- { mode: 'readwrite', transaction },
325
- );
326
- return Array.from(affected);
327
- };
328
-
329
- reset = () => {
330
- return this.clear('operations');
331
- };
332
- }
@@ -1,47 +0,0 @@
1
- import { StorageSchema } from '@verdant-web/common';
2
- import { storeRequestPromise } from '../idb.js';
3
-
4
- type StoredSchema = {
5
- type: 'schema';
6
- schema: string;
7
- };
8
-
9
- export class SchemaStore {
10
- private cached: StorageSchema<any> | null = null;
11
-
12
- constructor(
13
- private readonly db: IDBDatabase,
14
- public readonly currentVersion: number,
15
- ) {}
16
-
17
- get = async (): Promise<StorageSchema<any> | null> => {
18
- if (this.cached) {
19
- return this.cached;
20
- }
21
-
22
- const db = this.db;
23
- const transaction = db.transaction('info', 'readonly');
24
- const store = transaction.objectStore('info');
25
- const request = store.get('schema');
26
- const value = (await storeRequestPromise(request)) as
27
- | StoredSchema
28
- | undefined;
29
- if (!value) {
30
- return null;
31
- }
32
- this.cached = JSON.parse(value.schema);
33
- return this.cached;
34
- };
35
-
36
- set = async (schema: StorageSchema<any>): Promise<void> => {
37
- const db = this.db;
38
- const transaction = db.transaction('info', 'readwrite');
39
- const store = transaction.objectStore('info');
40
- const request = store.put({
41
- type: 'schema',
42
- schema: JSON.stringify(schema),
43
- } as StoredSchema);
44
- this.cached = schema;
45
- await storeRequestPromise(request);
46
- };
47
- }
@@ -1,7 +0,0 @@
1
- export class MigrationPathError extends Error {
2
- readonly name = 'MigrationPathError';
3
-
4
- constructor(public readonly message: string) {
5
- super(message);
6
- }
7
- }
@@ -1,97 +0,0 @@
1
- import { Migration } from '@verdant-web/common';
2
- import { Metadata } from '../metadata/Metadata.js';
3
- import { copyAll, getDatabaseVersion, openDatabase } from './db.js';
4
- import { runMigrations } from './migrations.js';
5
- import { getMigrationPath } from './paths.js';
6
- import { OpenDocumentDbContext } from './types.js';
7
-
8
- const globalIDB =
9
- typeof window !== 'undefined' ? window.indexedDB : (undefined as any);
10
-
11
- export async function openWIPDatabase({
12
- version,
13
- indexedDB = globalIDB,
14
- migrations,
15
- meta,
16
- context,
17
- wipNamespace,
18
- }: {
19
- version: number;
20
- migrations: Migration<any>[];
21
- indexedDB?: IDBFactory;
22
- meta: Metadata;
23
- context: OpenDocumentDbContext;
24
- wipNamespace: string;
25
- }) {
26
- context.log('debug', 'Opening WIP database', wipNamespace);
27
- const currentWIPVersion = await getDatabaseVersion(
28
- indexedDB,
29
- wipNamespace,
30
- version,
31
- context.log,
32
- );
33
-
34
- if (currentWIPVersion === version) {
35
- context.log('info', `WIP schema is up-to-date; not refreshing database`);
36
- } else {
37
- context.log('info', `WIP schema is out-of-date; refreshing database`);
38
-
39
- // first we need to copy the data from the production database to the WIP database
40
- // at the current (non-wip) version.
41
-
42
- const initialToRun = getMigrationPath({
43
- currentVersion: currentWIPVersion,
44
- targetVersion: version - 1,
45
- migrations,
46
- });
47
-
48
- if (initialToRun.length > 0) {
49
- await runMigrations({
50
- context,
51
- toRun: initialToRun,
52
- meta,
53
- indexedDB,
54
- namespace: wipNamespace,
55
- });
56
-
57
- // now, we copy the data from the main database.
58
- const mainDatabase = await openDatabase({
59
- indexedDB,
60
- namespace: context.namespace,
61
- version: version - 1,
62
- context,
63
- });
64
-
65
- const wipDatabase = await openDatabase({
66
- indexedDB,
67
- namespace: wipNamespace,
68
- version: version - 1,
69
- context,
70
- });
71
- await copyAll(mainDatabase, wipDatabase);
72
- }
73
-
74
- const toRun = getMigrationPath({
75
- currentVersion: version - 1,
76
- targetVersion: version,
77
- migrations,
78
- });
79
-
80
- if (toRun.length > 0) {
81
- await runMigrations({
82
- context,
83
- toRun,
84
- meta,
85
- indexedDB,
86
- namespace: wipNamespace,
87
- });
88
- }
89
- }
90
-
91
- return openDatabase({
92
- indexedDB,
93
- namespace: wipNamespace,
94
- version,
95
- context,
96
- });
97
- }
@@ -1,4 +0,0 @@
1
- import { Context } from '../context.js';
2
-
3
- /** During migration, only a partial context is available */
4
- export type OpenDocumentDbContext = Omit<Context, 'documentDb'>;
@@ -1,122 +0,0 @@
1
- import {
2
- ObjectIdentifier,
3
- decomposeOid,
4
- getIndexValues,
5
- } from '@verdant-web/common';
6
- import { IDBService } from '../IDBService.js';
7
- import { Context } from '../context.js';
8
- import { storeRequestPromise } from '../idb.js';
9
-
10
- export class QueryableStorage extends IDBService {
11
- private ctx;
12
-
13
- constructor({ ctx }: { ctx: Context }) {
14
- super(ctx.documentDb, { log: ctx.log });
15
- this.ctx = ctx;
16
- this.addDispose(
17
- this.ctx.internalEvents.subscribe('documentDbChanged', (db) => {
18
- this.db = db;
19
- }),
20
- );
21
- }
22
-
23
- /**
24
- * DELETES EVERYTHING IN THE QUERYABLE DATABASE
25
- */
26
- reset = async () => {
27
- const allCollections = Object.keys(this.ctx.schema.collections);
28
- const tx = this.createTransaction(allCollections, { mode: 'readwrite' });
29
- await Promise.all(
30
- allCollections.map((collection) => {
31
- const store = tx.objectStore(collection);
32
- return storeRequestPromise(store.clear());
33
- }),
34
- );
35
- // notify queries to re-run now.
36
- this.ctx.entityEvents.emit('collectionsChanged', allCollections);
37
- this.ctx.log('info', '💨 Reset queryable storage');
38
- };
39
-
40
- saveEntities = async (
41
- entities: { oid: ObjectIdentifier; getSnapshot: () => any }[],
42
- opts?: { abort?: AbortSignal },
43
- ) => {
44
- if (entities.length === 0) {
45
- return;
46
- }
47
-
48
- let collections = Array.from(
49
- new Set(entities.map((e) => decomposeOid(e.oid).collection)),
50
- );
51
- // we could theoretically receive entities from old schema versions which no
52
- // longer have collections. We should ignore these. If we don't, the transaction
53
- // will fail, because the object store doesn't exist.
54
- const toRemove = collections.filter((c) => !this.ctx.schema.collections[c]);
55
- if (toRemove.length > 0) {
56
- this.ctx.log(
57
- 'warn',
58
- `Ignoring entities from collections that no longer exist: ${toRemove.join(
59
- ', ',
60
- )}`,
61
- );
62
- const withRemoved = new Set(collections);
63
- toRemove.forEach((c) => withRemoved.delete(c));
64
- collections = Array.from(withRemoved);
65
- }
66
- const options = {
67
- transaction: this.createTransaction(collections, {
68
- mode: 'readwrite',
69
- abort: opts?.abort,
70
- }),
71
- };
72
- await Promise.all(
73
- entities.map(async (e) => {
74
- const snapshot = e.getSnapshot();
75
- try {
76
- await this.saveDocument(e.oid, snapshot, options);
77
- } catch (err) {
78
- this.ctx.log(
79
- 'error',
80
- `Error saving document ${e.oid} (${JSON.stringify(snapshot)})`,
81
- err,
82
- );
83
- if (err instanceof Error) {
84
- throw err;
85
- } else {
86
- throw new Error('Unknown error saving document');
87
- }
88
- }
89
- }),
90
- );
91
- options.transaction.commit();
92
- this.ctx.entityEvents.emit('collectionsChanged', collections);
93
- for (const entity of entities) {
94
- this.ctx.entityEvents.emit('documentChanged', entity.oid);
95
- }
96
- };
97
-
98
- private saveDocument = async (
99
- oid: ObjectIdentifier,
100
- doc: any,
101
- { transaction }: { transaction?: IDBTransaction },
102
- ) => {
103
- this.ctx.log('debug', `Saving document indexes for querying ${oid}`, doc);
104
- const { collection, id } = decomposeOid(oid);
105
- if (!doc) {
106
- await this.run(collection, (store) => store.delete(id), {
107
- mode: 'readwrite',
108
- transaction,
109
- });
110
- this.ctx.log('debug', `Deleted document indexes for querying ${oid}`);
111
- } else {
112
- const schema = this.ctx.schema.collections[collection];
113
- // no need to validate before storing; the entity's snapshot is already validated.
114
- const indexes = getIndexValues(schema, doc);
115
- await this.run(collection, (store) => store.put(indexes), {
116
- mode: 'readwrite',
117
- transaction,
118
- });
119
- this.ctx.log('debug', `Saved document indexes for querying ${oid}`, doc);
120
- }
121
- };
122
- }