@kingsy/objectloader2 2.25.7

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 (256) hide show
  1. package/.tshy/build.json +8 -0
  2. package/.tshy/commonjs.json +17 -0
  3. package/.tshy/esm.json +16 -0
  4. package/dist/commonjs/helpers/aggregateQueue.d.ts +8 -0
  5. package/dist/commonjs/helpers/aggregateQueue.d.ts.map +1 -0
  6. package/dist/commonjs/helpers/aggregateQueue.js +19 -0
  7. package/dist/commonjs/helpers/aggregateQueue.js.map +1 -0
  8. package/dist/commonjs/helpers/asyncGeneratorQueue.d.ts +8 -0
  9. package/dist/commonjs/helpers/asyncGeneratorQueue.d.ts.map +1 -0
  10. package/dist/commonjs/helpers/asyncGeneratorQueue.js +35 -0
  11. package/dist/commonjs/helpers/asyncGeneratorQueue.js.map +1 -0
  12. package/dist/commonjs/helpers/batchedPool.d.ts +12 -0
  13. package/dist/commonjs/helpers/batchedPool.d.ts.map +1 -0
  14. package/dist/commonjs/helpers/batchedPool.js +45 -0
  15. package/dist/commonjs/helpers/batchedPool.js.map +1 -0
  16. package/dist/commonjs/helpers/batchingQueue.d.ts +14 -0
  17. package/dist/commonjs/helpers/batchingQueue.d.ts.map +1 -0
  18. package/dist/commonjs/helpers/batchingQueue.js +77 -0
  19. package/dist/commonjs/helpers/batchingQueue.js.map +1 -0
  20. package/dist/commonjs/helpers/bufferQueue.d.ts +7 -0
  21. package/dist/commonjs/helpers/bufferQueue.d.ts.map +1 -0
  22. package/dist/commonjs/helpers/bufferQueue.js +13 -0
  23. package/dist/commonjs/helpers/bufferQueue.js.map +1 -0
  24. package/dist/commonjs/helpers/cachePump.d.ts +22 -0
  25. package/dist/commonjs/helpers/cachePump.d.ts.map +1 -0
  26. package/dist/commonjs/helpers/cachePump.js +86 -0
  27. package/dist/commonjs/helpers/cachePump.js.map +1 -0
  28. package/dist/commonjs/helpers/cacheReader.d.ts +14 -0
  29. package/dist/commonjs/helpers/cacheReader.d.ts.map +1 -0
  30. package/dist/commonjs/helpers/cacheReader.js +58 -0
  31. package/dist/commonjs/helpers/cacheReader.js.map +1 -0
  32. package/dist/commonjs/helpers/defermentManager.d.ts +28 -0
  33. package/dist/commonjs/helpers/defermentManager.d.ts.map +1 -0
  34. package/dist/commonjs/helpers/defermentManager.js +150 -0
  35. package/dist/commonjs/helpers/defermentManager.js.map +1 -0
  36. package/dist/commonjs/helpers/deferredBase.d.ts +19 -0
  37. package/dist/commonjs/helpers/deferredBase.d.ts.map +1 -0
  38. package/dist/commonjs/helpers/deferredBase.js +51 -0
  39. package/dist/commonjs/helpers/deferredBase.js.map +1 -0
  40. package/dist/commonjs/helpers/keyedQueue.d.ts +11 -0
  41. package/dist/commonjs/helpers/keyedQueue.d.ts.map +1 -0
  42. package/dist/commonjs/helpers/keyedQueue.js +41 -0
  43. package/dist/commonjs/helpers/keyedQueue.js.map +1 -0
  44. package/dist/commonjs/helpers/memoryPump.d.ts +15 -0
  45. package/dist/commonjs/helpers/memoryPump.d.ts.map +1 -0
  46. package/dist/commonjs/helpers/memoryPump.js +34 -0
  47. package/dist/commonjs/helpers/memoryPump.js.map +1 -0
  48. package/dist/commonjs/helpers/pump.d.ts +8 -0
  49. package/dist/commonjs/helpers/pump.d.ts.map +1 -0
  50. package/dist/commonjs/helpers/pump.js +3 -0
  51. package/dist/commonjs/helpers/pump.js.map +1 -0
  52. package/dist/commonjs/helpers/queue.d.ts +4 -0
  53. package/dist/commonjs/helpers/queue.d.ts.map +1 -0
  54. package/dist/commonjs/helpers/queue.js +3 -0
  55. package/dist/commonjs/helpers/queue.js.map +1 -0
  56. package/dist/commonjs/index.d.ts +3 -0
  57. package/dist/commonjs/index.d.ts.map +1 -0
  58. package/dist/commonjs/index.js +8 -0
  59. package/dist/commonjs/index.js.map +1 -0
  60. package/dist/commonjs/operations/databases/indexedDatabase.d.ts +27 -0
  61. package/dist/commonjs/operations/databases/indexedDatabase.d.ts.map +1 -0
  62. package/dist/commonjs/operations/databases/indexedDatabase.js +98 -0
  63. package/dist/commonjs/operations/databases/indexedDatabase.js.map +1 -0
  64. package/dist/commonjs/operations/databases/memoryDatabase.d.ts +13 -0
  65. package/dist/commonjs/operations/databases/memoryDatabase.d.ts.map +1 -0
  66. package/dist/commonjs/operations/databases/memoryDatabase.js +33 -0
  67. package/dist/commonjs/operations/databases/memoryDatabase.js.map +1 -0
  68. package/dist/commonjs/operations/downloaders/memoryDownloader.d.ts +16 -0
  69. package/dist/commonjs/operations/downloaders/memoryDownloader.d.ts.map +1 -0
  70. package/dist/commonjs/operations/downloaders/memoryDownloader.js +35 -0
  71. package/dist/commonjs/operations/downloaders/memoryDownloader.js.map +1 -0
  72. package/dist/commonjs/operations/downloaders/serverDownloader.d.ts +32 -0
  73. package/dist/commonjs/operations/downloaders/serverDownloader.d.ts.map +1 -0
  74. package/dist/commonjs/operations/downloaders/serverDownloader.js +169 -0
  75. package/dist/commonjs/operations/downloaders/serverDownloader.js.map +1 -0
  76. package/dist/commonjs/operations/interfaces.d.ts +19 -0
  77. package/dist/commonjs/operations/interfaces.d.ts.map +1 -0
  78. package/dist/commonjs/operations/interfaces.js +3 -0
  79. package/dist/commonjs/operations/interfaces.js.map +1 -0
  80. package/dist/commonjs/operations/objectLoader2.d.ts +16 -0
  81. package/dist/commonjs/operations/objectLoader2.d.ts.map +1 -0
  82. package/dist/commonjs/operations/objectLoader2.js +101 -0
  83. package/dist/commonjs/operations/objectLoader2.js.map +1 -0
  84. package/dist/commonjs/operations/objectLoader2Factory.d.ts +25 -0
  85. package/dist/commonjs/operations/objectLoader2Factory.d.ts.map +1 -0
  86. package/dist/commonjs/operations/objectLoader2Factory.js +68 -0
  87. package/dist/commonjs/operations/objectLoader2Factory.js.map +1 -0
  88. package/dist/commonjs/operations/options.d.ts +26 -0
  89. package/dist/commonjs/operations/options.d.ts.map +1 -0
  90. package/dist/commonjs/operations/options.js +3 -0
  91. package/dist/commonjs/operations/options.js.map +1 -0
  92. package/dist/commonjs/operations/traverser.d.ts +19 -0
  93. package/dist/commonjs/operations/traverser.d.ts.map +1 -0
  94. package/dist/commonjs/operations/traverser.js +96 -0
  95. package/dist/commonjs/operations/traverser.js.map +1 -0
  96. package/dist/commonjs/package.json +3 -0
  97. package/dist/commonjs/types/errors.d.ts +21 -0
  98. package/dist/commonjs/types/errors.d.ts.map +1 -0
  99. package/dist/commonjs/types/errors.js +28 -0
  100. package/dist/commonjs/types/errors.js.map +1 -0
  101. package/dist/commonjs/types/types.d.ts +25 -0
  102. package/dist/commonjs/types/types.d.ts.map +1 -0
  103. package/dist/commonjs/types/types.js +39 -0
  104. package/dist/commonjs/types/types.js.map +1 -0
  105. package/dist/esm/helpers/aggregateQueue.d.ts +8 -0
  106. package/dist/esm/helpers/aggregateQueue.d.ts.map +1 -0
  107. package/dist/esm/helpers/aggregateQueue.js +16 -0
  108. package/dist/esm/helpers/aggregateQueue.js.map +1 -0
  109. package/dist/esm/helpers/asyncGeneratorQueue.d.ts +8 -0
  110. package/dist/esm/helpers/asyncGeneratorQueue.d.ts.map +1 -0
  111. package/dist/esm/helpers/asyncGeneratorQueue.js +32 -0
  112. package/dist/esm/helpers/asyncGeneratorQueue.js.map +1 -0
  113. package/dist/esm/helpers/batchedPool.d.ts +12 -0
  114. package/dist/esm/helpers/batchedPool.d.ts.map +1 -0
  115. package/dist/esm/helpers/batchedPool.js +42 -0
  116. package/dist/esm/helpers/batchedPool.js.map +1 -0
  117. package/dist/esm/helpers/batchingQueue.d.ts +14 -0
  118. package/dist/esm/helpers/batchingQueue.d.ts.map +1 -0
  119. package/dist/esm/helpers/batchingQueue.js +71 -0
  120. package/dist/esm/helpers/batchingQueue.js.map +1 -0
  121. package/dist/esm/helpers/bufferQueue.d.ts +7 -0
  122. package/dist/esm/helpers/bufferQueue.d.ts.map +1 -0
  123. package/dist/esm/helpers/bufferQueue.js +10 -0
  124. package/dist/esm/helpers/bufferQueue.js.map +1 -0
  125. package/dist/esm/helpers/cachePump.d.ts +22 -0
  126. package/dist/esm/helpers/cachePump.d.ts.map +1 -0
  127. package/dist/esm/helpers/cachePump.js +79 -0
  128. package/dist/esm/helpers/cachePump.js.map +1 -0
  129. package/dist/esm/helpers/cacheReader.d.ts +14 -0
  130. package/dist/esm/helpers/cacheReader.d.ts.map +1 -0
  131. package/dist/esm/helpers/cacheReader.js +51 -0
  132. package/dist/esm/helpers/cacheReader.js.map +1 -0
  133. package/dist/esm/helpers/defermentManager.d.ts +28 -0
  134. package/dist/esm/helpers/defermentManager.d.ts.map +1 -0
  135. package/dist/esm/helpers/defermentManager.js +146 -0
  136. package/dist/esm/helpers/defermentManager.js.map +1 -0
  137. package/dist/esm/helpers/deferredBase.d.ts +19 -0
  138. package/dist/esm/helpers/deferredBase.d.ts.map +1 -0
  139. package/dist/esm/helpers/deferredBase.js +47 -0
  140. package/dist/esm/helpers/deferredBase.js.map +1 -0
  141. package/dist/esm/helpers/keyedQueue.d.ts +11 -0
  142. package/dist/esm/helpers/keyedQueue.d.ts.map +1 -0
  143. package/dist/esm/helpers/keyedQueue.js +38 -0
  144. package/dist/esm/helpers/keyedQueue.js.map +1 -0
  145. package/dist/esm/helpers/memoryPump.d.ts +15 -0
  146. package/dist/esm/helpers/memoryPump.d.ts.map +1 -0
  147. package/dist/esm/helpers/memoryPump.js +30 -0
  148. package/dist/esm/helpers/memoryPump.js.map +1 -0
  149. package/dist/esm/helpers/pump.d.ts +8 -0
  150. package/dist/esm/helpers/pump.d.ts.map +1 -0
  151. package/dist/esm/helpers/pump.js +2 -0
  152. package/dist/esm/helpers/pump.js.map +1 -0
  153. package/dist/esm/helpers/queue.d.ts +4 -0
  154. package/dist/esm/helpers/queue.d.ts.map +1 -0
  155. package/dist/esm/helpers/queue.js +2 -0
  156. package/dist/esm/helpers/queue.js.map +1 -0
  157. package/dist/esm/index.d.ts +3 -0
  158. package/dist/esm/index.d.ts.map +1 -0
  159. package/dist/esm/index.js +3 -0
  160. package/dist/esm/index.js.map +1 -0
  161. package/dist/esm/operations/databases/indexedDatabase.d.ts +27 -0
  162. package/dist/esm/operations/databases/indexedDatabase.d.ts.map +1 -0
  163. package/dist/esm/operations/databases/indexedDatabase.js +93 -0
  164. package/dist/esm/operations/databases/indexedDatabase.js.map +1 -0
  165. package/dist/esm/operations/databases/memoryDatabase.d.ts +13 -0
  166. package/dist/esm/operations/databases/memoryDatabase.d.ts.map +1 -0
  167. package/dist/esm/operations/databases/memoryDatabase.js +29 -0
  168. package/dist/esm/operations/databases/memoryDatabase.js.map +1 -0
  169. package/dist/esm/operations/downloaders/memoryDownloader.d.ts +16 -0
  170. package/dist/esm/operations/downloaders/memoryDownloader.d.ts.map +1 -0
  171. package/dist/esm/operations/downloaders/memoryDownloader.js +31 -0
  172. package/dist/esm/operations/downloaders/memoryDownloader.js.map +1 -0
  173. package/dist/esm/operations/downloaders/serverDownloader.d.ts +32 -0
  174. package/dist/esm/operations/downloaders/serverDownloader.d.ts.map +1 -0
  175. package/dist/esm/operations/downloaders/serverDownloader.js +163 -0
  176. package/dist/esm/operations/downloaders/serverDownloader.js.map +1 -0
  177. package/dist/esm/operations/interfaces.d.ts +19 -0
  178. package/dist/esm/operations/interfaces.d.ts.map +1 -0
  179. package/dist/esm/operations/interfaces.js +2 -0
  180. package/dist/esm/operations/interfaces.js.map +1 -0
  181. package/dist/esm/operations/objectLoader2.d.ts +16 -0
  182. package/dist/esm/operations/objectLoader2.d.ts.map +1 -0
  183. package/dist/esm/operations/objectLoader2.js +94 -0
  184. package/dist/esm/operations/objectLoader2.js.map +1 -0
  185. package/dist/esm/operations/objectLoader2Factory.d.ts +25 -0
  186. package/dist/esm/operations/objectLoader2Factory.d.ts.map +1 -0
  187. package/dist/esm/operations/objectLoader2Factory.js +61 -0
  188. package/dist/esm/operations/objectLoader2Factory.js.map +1 -0
  189. package/dist/esm/operations/options.d.ts +26 -0
  190. package/dist/esm/operations/options.d.ts.map +1 -0
  191. package/dist/esm/operations/options.js +2 -0
  192. package/dist/esm/operations/options.js.map +1 -0
  193. package/dist/esm/operations/traverser.d.ts +19 -0
  194. package/dist/esm/operations/traverser.d.ts.map +1 -0
  195. package/dist/esm/operations/traverser.js +93 -0
  196. package/dist/esm/operations/traverser.js.map +1 -0
  197. package/dist/esm/package.json +3 -0
  198. package/dist/esm/types/errors.d.ts +21 -0
  199. package/dist/esm/types/errors.d.ts.map +1 -0
  200. package/dist/esm/types/errors.js +23 -0
  201. package/dist/esm/types/errors.js.map +1 -0
  202. package/dist/esm/types/types.d.ts +25 -0
  203. package/dist/esm/types/types.d.ts.map +1 -0
  204. package/dist/esm/types/types.js +33 -0
  205. package/dist/esm/types/types.js.map +1 -0
  206. package/eslint.config.mjs +60 -0
  207. package/package.json +76 -0
  208. package/readme.md +42 -0
  209. package/src/helpers/__snapshots__/cachePump.spec.ts.snap +31 -0
  210. package/src/helpers/__snapshots__/cacheReader.spec.ts.snap +8 -0
  211. package/src/helpers/__snapshots__/defermentManager.spec.ts.snap +8 -0
  212. package/src/helpers/aggregateQueue.ts +20 -0
  213. package/src/helpers/asyncGeneratorQueue.ts +35 -0
  214. package/src/helpers/batchedPool.ts +56 -0
  215. package/src/helpers/batchingQueue.ts +85 -0
  216. package/src/helpers/bufferQueue.ts +12 -0
  217. package/src/helpers/cachePump.disposal.spec.ts +51 -0
  218. package/src/helpers/cachePump.spec.ts +104 -0
  219. package/src/helpers/cachePump.ts +107 -0
  220. package/src/helpers/cacheReader.spec.ts +35 -0
  221. package/src/helpers/cacheReader.ts +64 -0
  222. package/src/helpers/defermentManager.defermentTotals.spec.ts +53 -0
  223. package/src/helpers/defermentManager.disposal.spec.ts +28 -0
  224. package/src/helpers/defermentManager.spec.ts +37 -0
  225. package/src/helpers/defermentManager.ts +160 -0
  226. package/src/helpers/deferredBase.ts +55 -0
  227. package/src/helpers/keyedQueue.ts +45 -0
  228. package/src/helpers/memoryPump.ts +40 -0
  229. package/src/helpers/pump.ts +8 -0
  230. package/src/helpers/queue.ts +3 -0
  231. package/src/index.ts +2 -0
  232. package/src/operations/__snapshots__/objectLoader2.spec.ts.snap +149 -0
  233. package/src/operations/__snapshots__/traverser.spec.ts.snap +45 -0
  234. package/src/operations/databases/__snapshots__/indexedDatabase.spec.ts.snap +18 -0
  235. package/src/operations/databases/indexedDatabase.spec.ts +33 -0
  236. package/src/operations/databases/indexedDatabase.ts +126 -0
  237. package/src/operations/databases/memoryDatabase.spec.ts +48 -0
  238. package/src/operations/databases/memoryDatabase.ts +35 -0
  239. package/src/operations/downloaders/__snapshots__/memoryDownloader.spec.ts.snap +12 -0
  240. package/src/operations/downloaders/__snapshots__/serverDownloader.spec.ts.snap +92 -0
  241. package/src/operations/downloaders/memoryDownloader.spec.ts +49 -0
  242. package/src/operations/downloaders/memoryDownloader.ts +39 -0
  243. package/src/operations/downloaders/serverDownloader.spec.ts +161 -0
  244. package/src/operations/downloaders/serverDownloader.ts +217 -0
  245. package/src/operations/interfaces.ts +18 -0
  246. package/src/operations/objectLoader2.spec.ts +229 -0
  247. package/src/operations/objectLoader2.ts +119 -0
  248. package/src/operations/objectLoader2Factory.ts +78 -0
  249. package/src/operations/options.ts +29 -0
  250. package/src/operations/traverser.spec.ts +58 -0
  251. package/src/operations/traverser.ts +112 -0
  252. package/src/test/e2e.spec.ts +43 -0
  253. package/src/types/errors.ts +25 -0
  254. package/src/types/types.ts +71 -0
  255. package/tsconfig.json +103 -0
  256. package/vitest.config.ts +3 -0
@@ -0,0 +1,229 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import { ObjectLoader2 } from './objectLoader2.js'
3
+ import { Base, Item } from '../types/types.js'
4
+ import { MemoryDownloader } from './downloaders/memoryDownloader.js'
5
+ import { IDBFactory, IDBKeyRange } from 'fake-indexeddb'
6
+ import { MemoryDatabase } from './databases/memoryDatabase.js'
7
+ import IndexedDatabase from './databases/indexedDatabase.js'
8
+
9
+ describe('objectloader2', () => {
10
+ test('can get a root object from cache', async () => {
11
+ const rootId = 'baseId'
12
+ const rootBase: Base = { id: 'baseId', kingsy_type: 'type' }
13
+ const downloader = new MemoryDownloader(
14
+ rootId,
15
+ new Map<string, Base>([[rootId, rootBase]])
16
+ )
17
+ const loader = new ObjectLoader2({
18
+ rootId,
19
+ downloader,
20
+ database: new IndexedDatabase({
21
+ indexedDB: new IDBFactory(),
22
+ keyRange: IDBKeyRange
23
+ })
24
+ })
25
+ const x = await loader.getRootObject()
26
+ expect(x).toMatchSnapshot()
27
+ })
28
+
29
+ test('can get a root object from downloader', async () => {
30
+ const rootId = 'baseId'
31
+ const rootBase: Base = { id: 'baseId', kingsy_type: 'type' }
32
+ const downloader = new MemoryDownloader(
33
+ rootId,
34
+ new Map<string, Base>([[rootId, rootBase]])
35
+ )
36
+ const loader = new ObjectLoader2({
37
+ rootId,
38
+ downloader,
39
+ database: new IndexedDatabase({
40
+ indexedDB: new IDBFactory(),
41
+ keyRange: IDBKeyRange
42
+ })
43
+ })
44
+ const x = await loader.getRootObject()
45
+ expect(x).toMatchSnapshot()
46
+ })
47
+
48
+ test('can get single object from cache using iterator', async () => {
49
+ const rootId = 'baseId'
50
+ const rootBase: Base = { id: 'baseId', kingsy_type: 'type' }
51
+
52
+ const downloader = new MemoryDownloader(
53
+ rootId,
54
+ new Map<string, Base>([[rootId, rootBase]])
55
+ )
56
+ const loader = new ObjectLoader2({
57
+ rootId,
58
+ downloader,
59
+ database: new IndexedDatabase({
60
+ indexedDB: new IDBFactory(),
61
+ keyRange: IDBKeyRange
62
+ })
63
+ })
64
+ const r = []
65
+ for await (const x of loader.getObjectIterator()) {
66
+ r.push(x)
67
+ }
68
+
69
+ expect(r).toMatchSnapshot()
70
+ })
71
+
72
+ test('can get root/child object from memory cache using iterator and getObject', async () => {
73
+ const child1Base = { id: 'child1Id', kingsy_type: 'type' } as Base
74
+ const child1 = { baseId: 'child1Id', base: child1Base } as unknown as Item
75
+
76
+ const rootId = 'rootId'
77
+ const rootBase: Base = {
78
+ id: 'rootId',
79
+ kingsy_type: 'type',
80
+ __closure: { child1Id: 100 }
81
+ }
82
+ const root = {
83
+ baseId: rootId,
84
+ base: rootBase
85
+ } as Item
86
+
87
+ const records: Map<string, Base> = new Map<string, Base>()
88
+ records.set(root.baseId, rootBase)
89
+ records.set(child1.baseId, child1Base)
90
+
91
+ const loader = new ObjectLoader2({
92
+ rootId: root.baseId,
93
+ downloader: new MemoryDownloader(rootId, records),
94
+ database: new MemoryDatabase({ items: records })
95
+ })
96
+
97
+ const r = []
98
+ const obj = loader.getObject({ id: child1.baseId })
99
+ for await (const x of loader.getObjectIterator()) {
100
+ r.push(x)
101
+ }
102
+
103
+ expect(obj).toBeDefined()
104
+ expect(r).toMatchSnapshot()
105
+ const obj2 = await obj
106
+ expect(obj2).toBe(child1Base)
107
+ expect(obj2).toMatchSnapshot()
108
+ })
109
+
110
+ test('can get root/child object from memory downloader using iterator and getObject', async () => {
111
+ const child1Base = { id: 'child1Id', kingsy_type: 'type' } as Base
112
+ const child1 = { baseId: 'child1Id', base: child1Base } as unknown as Item
113
+
114
+ const rootId = 'rootId'
115
+ const rootBase: Base = {
116
+ id: 'rootId',
117
+ kingsy_type: 'type',
118
+ __closure: { child1Id: 100 }
119
+ }
120
+ const root = {
121
+ baseId: rootId,
122
+ base: rootBase
123
+ } as unknown as Item
124
+
125
+ const records: Map<string, Base> = new Map<string, Base>()
126
+ records.set(root.baseId, rootBase)
127
+ records.set(child1.baseId, child1Base)
128
+
129
+ const loader = new ObjectLoader2({
130
+ rootId: root.baseId,
131
+ downloader: new MemoryDownloader(rootId, records),
132
+ database: new IndexedDatabase({
133
+ indexedDB: new IDBFactory(),
134
+ keyRange: IDBKeyRange
135
+ })
136
+ })
137
+ const r = []
138
+ const obj = loader.getObject({ id: child1.baseId })
139
+ for await (const x of loader.getObjectIterator()) {
140
+ r.push(x)
141
+ }
142
+
143
+ expect(obj).toBeDefined()
144
+ expect(r).toMatchSnapshot()
145
+ const obj2 = await obj
146
+ expect(obj2).toBe(child1Base)
147
+ expect(obj2).toMatchSnapshot()
148
+ })
149
+
150
+ test('add extra header', async () => {
151
+ const rootId = 'rootId'
152
+ const rootBase: Base = {
153
+ id: 'rootId',
154
+ kingsy_type: 'type',
155
+ __closure: { child1Id: 100 }
156
+ }
157
+ const root = {
158
+ baseId: rootId,
159
+ base: rootBase
160
+ } as Item
161
+
162
+ const records: Map<string, Base> = new Map<string, Base>()
163
+ records.set(root.baseId, rootBase)
164
+ const headers = new Headers()
165
+ headers.set('x-test', 'asdf')
166
+ const loader = new ObjectLoader2({
167
+ rootId: root.baseId,
168
+ downloader: new MemoryDownloader(rootId, records),
169
+ database: new IndexedDatabase({
170
+ indexedDB: new IDBFactory(),
171
+ keyRange: IDBKeyRange
172
+ })
173
+ })
174
+ const x = await loader.getRootObject()
175
+ expect(x).toMatchSnapshot()
176
+ })
177
+
178
+ test('createFromJSON test', async () => {
179
+ const root = `{
180
+ "list": [{
181
+ "kingsy_type": "reference",
182
+ "referencedId": "0e61e61edee00404ec6e0f9f594bce24",
183
+ "__closure": null
184
+ }],
185
+ "list2": [{
186
+ "kingsy_type": "reference",
187
+ "referencedId": "f70738e3e3e593ac11099a6ed6b71154",
188
+ "__closure": null
189
+ }],
190
+ "arr": null,
191
+ "detachedProp": null,
192
+ "detachedProp2": null,
193
+ "attachedProp": null,
194
+ "crazyProp": null,
195
+ "applicationId": "1",
196
+ "kingsy_type": "KingSy.Core.Tests.Unit.Models.BaseTests+SampleObjectBase2",
197
+ "dynamicProp": 123,
198
+ "id": "efeadaca70a85ae6d3acfc93a8b380db",
199
+ "__closure": {
200
+ "0e61e61edee00404ec6e0f9f594bce24": 100,
201
+ "f70738e3e3e593ac11099a6ed6b71154": 100
202
+ }
203
+ }`
204
+
205
+ const list1 = `{
206
+ "data": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
207
+ "applicationId": null,
208
+ "kingsy_type": "KingSy.Core.Models.DataChunk",
209
+ "id": "0e61e61edee00404ec6e0f9f594bce24"
210
+ }`
211
+
212
+ const list2 = `{
213
+ "data": [1.0, 10.0],
214
+ "applicationId": null,
215
+ "kingsy_type": "KingSy.Core.Models.DataChunk",
216
+ "id": "f70738e3e3e593ac11099a6ed6b71154"
217
+ }`
218
+ const rootObj = JSON.parse(root) as Base
219
+ const list1Obj = JSON.parse(list1) as Base
220
+ const list2Obj = JSON.parse(list2) as Base
221
+
222
+ const loader = ObjectLoader2.createFromObjects([rootObj, list1Obj, list2Obj])
223
+ const r = []
224
+ for await (const x of loader.getObjectIterator()) {
225
+ r.push(x)
226
+ }
227
+ expect(r).toMatchSnapshot()
228
+ })
229
+ })
@@ -0,0 +1,119 @@
1
+ import AsyncGeneratorQueue from '../helpers/asyncGeneratorQueue.js'
2
+ import { Downloader, Database } from './interfaces.js'
3
+ import { CustomLogger, Base, Item } from '../types/types.js'
4
+ import { CacheOptions, ObjectLoader2Options } from './options.js'
5
+ import { DefermentManager } from '../helpers/defermentManager.js'
6
+ import { CacheReader } from '../helpers/cacheReader.js'
7
+ import { CachePump } from '../helpers/cachePump.js'
8
+ import AggregateQueue from '../helpers/aggregateQueue.js'
9
+ import { ObjectLoader2Factory } from './objectLoader2Factory.js'
10
+
11
+ export class ObjectLoader2 {
12
+ #rootId: string
13
+
14
+ #logger: CustomLogger
15
+
16
+ #database: Database
17
+ #downloader: Downloader
18
+ #pump: CachePump
19
+ #cache: CacheReader
20
+
21
+ #deferments: DefermentManager
22
+
23
+ #gathered: AsyncGeneratorQueue<Item>
24
+
25
+ #root?: Item = undefined
26
+
27
+ constructor(options: ObjectLoader2Options) {
28
+ this.#rootId = options.rootId
29
+ this.#logger = options.logger || console.log
30
+
31
+ const cacheOptions: CacheOptions = {
32
+ logger: this.#logger,
33
+ maxCacheReadSize: 10_000,
34
+ maxCacheWriteSize: 10_000,
35
+ maxWriteQueueSize: 40_000,
36
+ maxCacheBatchWriteWait: 3_000,
37
+ maxCacheBatchReadWait: 3_000
38
+ }
39
+
40
+ this.#gathered = new AsyncGeneratorQueue()
41
+ this.#database = options.database
42
+ this.#deferments = new DefermentManager({
43
+ maxSizeInMb: 2_000, // 2 GBs
44
+ ttlms: 15_000, // 15 seconds
45
+ logger: this.#logger
46
+ })
47
+ this.#cache = new CacheReader(this.#database, this.#deferments, cacheOptions)
48
+ this.#pump = new CachePump(
49
+ this.#database,
50
+ this.#gathered,
51
+ this.#deferments,
52
+ cacheOptions
53
+ )
54
+ this.#downloader = options.downloader
55
+ }
56
+
57
+ async disposeAsync(): Promise<void> {
58
+ await Promise.all([
59
+ this.#downloader.disposeAsync(),
60
+ this.#cache.disposeAsync(),
61
+ this.#pump.disposeAsync()
62
+ ])
63
+ this.#deferments.dispose()
64
+ }
65
+
66
+ async getRootObject(): Promise<Item | undefined> {
67
+ if (!this.#root) {
68
+ this.#root = (await this.#database.getAll([this.#rootId]))[0]
69
+ if (!this.#root) {
70
+ this.#root = await this.#downloader.downloadSingle()
71
+ }
72
+ }
73
+ return this.#root
74
+ }
75
+
76
+ async getObject(params: { id: string }): Promise<Base> {
77
+ return await this.#cache.getObject({ id: params.id })
78
+ }
79
+
80
+ async getTotalObjectCount(): Promise<number> {
81
+ const rootObj = await this.getRootObject()
82
+ const totalChildrenCount = Object.keys(rootObj?.base.__closure || {}).length
83
+ return totalChildrenCount + 1 //count the root
84
+ }
85
+
86
+ async *getObjectIterator(): AsyncGenerator<Base> {
87
+ const rootItem = await this.getRootObject()
88
+ if (rootItem === undefined) {
89
+ this.#logger('No root object found!')
90
+ return
91
+ }
92
+ //only for root
93
+ this.#pump.add(rootItem)
94
+ yield rootItem.base
95
+ if (!rootItem.base.__closure) return
96
+
97
+ //sort the closures by their values descending
98
+ const sortedClosures = Object.entries(rootItem.base.__closure).sort(
99
+ (a, b) => b[1] - a[1]
100
+ )
101
+ const children = sortedClosures.map((x) => x[0])
102
+ const total = children.length
103
+ this.#downloader.initializePool({
104
+ results: new AggregateQueue(this.#gathered, this.#pump),
105
+ total
106
+ })
107
+ for await (const item of this.#pump.gather(children, this.#downloader)) {
108
+ yield item.base
109
+ }
110
+ }
111
+
112
+ static createFromObjects(objects: Base[]): ObjectLoader2 {
113
+ return ObjectLoader2Factory.createFromObjects(objects)
114
+ }
115
+
116
+ static createFromJSON(json: string): ObjectLoader2 {
117
+ return ObjectLoader2Factory.createFromJSON(json)
118
+ }
119
+ }
@@ -0,0 +1,78 @@
1
+ import { Base, CustomLogger } from '../types/types.js'
2
+ import IndexedDatabase from './databases/indexedDatabase.js'
3
+ import { MemoryDatabase } from './databases/memoryDatabase.js'
4
+ import { MemoryDownloader } from './downloaders/memoryDownloader.js'
5
+ import ServerDownloader from './downloaders/serverDownloader.js'
6
+ import { ObjectLoader2 } from './objectLoader2.js'
7
+
8
+ export interface ObjectLoader2FactoryOptions {
9
+ useMemoryCache?: boolean
10
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
11
+ keyRange?: { bound: Function; lowerBound: Function; upperBound: Function }
12
+ indexedDB?: IDBFactory
13
+ logger?: CustomLogger
14
+ }
15
+
16
+ export class ObjectLoader2Factory {
17
+ static createFromObjects(objects: Base[]): ObjectLoader2 {
18
+ const root = objects[0]
19
+ const records: Map<string, Base> = new Map<string, Base>()
20
+ objects.forEach((element) => {
21
+ records.set(element.id, element)
22
+ })
23
+ const loader = new ObjectLoader2({
24
+ rootId: root.id,
25
+ database: new MemoryDatabase({ items: records }),
26
+ downloader: new MemoryDownloader(root.id, records)
27
+ })
28
+ return loader
29
+ }
30
+
31
+ static createFromJSON(json: string): ObjectLoader2 {
32
+ const jsonObj = JSON.parse(json) as Base[]
33
+ return this.createFromObjects(jsonObj)
34
+ }
35
+
36
+ static createFromUrl(params: {
37
+ serverUrl: string
38
+ streamId: string
39
+ objectId: string
40
+ token?: string
41
+ headers?: Headers
42
+ options?: ObjectLoader2FactoryOptions
43
+ }): ObjectLoader2 {
44
+ let loader: ObjectLoader2
45
+ if (params.options?.useMemoryCache) {
46
+ loader = new ObjectLoader2({
47
+ rootId: params.objectId,
48
+ downloader: new ServerDownloader({
49
+ serverUrl: params.serverUrl,
50
+ streamId: params.streamId,
51
+ objectId: params.objectId,
52
+ token: params.token,
53
+ headers: params.headers
54
+ }),
55
+ database: new MemoryDatabase({
56
+ items: new Map<string, Base>()
57
+ })
58
+ })
59
+ } else {
60
+ loader = new ObjectLoader2({
61
+ rootId: params.objectId,
62
+ downloader: new ServerDownloader({
63
+ serverUrl: params.serverUrl,
64
+ streamId: params.streamId,
65
+ objectId: params.objectId,
66
+ token: params.token,
67
+ headers: params.headers
68
+ }),
69
+ database: new IndexedDatabase({
70
+ logger: params.options?.logger,
71
+ indexedDB: params.options?.indexedDB,
72
+ keyRange: params.options?.keyRange
73
+ })
74
+ })
75
+ }
76
+ return loader
77
+ }
78
+ }
@@ -0,0 +1,29 @@
1
+ import { Base, CustomLogger } from '../types/types.js'
2
+ import { Downloader, Database } from './interfaces.js'
3
+
4
+ export interface ObjectLoader2Options {
5
+ rootId: string
6
+ downloader: Downloader
7
+ database: Database
8
+ logger?: CustomLogger
9
+ }
10
+
11
+ export interface CacheOptions {
12
+ logger?: CustomLogger
13
+ maxCacheReadSize: number
14
+ maxCacheWriteSize: number
15
+ maxCacheBatchWriteWait: number
16
+ maxCacheBatchReadWait: number
17
+ maxWriteQueueSize: number
18
+ }
19
+
20
+ export interface MemoryDatabaseOptions {
21
+ logger?: CustomLogger
22
+ items?: Map<string, Base>
23
+ }
24
+
25
+ export interface DefermentManagerOptions {
26
+ logger?: CustomLogger
27
+ maxSizeInMb: number
28
+ ttlms: number
29
+ }
@@ -0,0 +1,58 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import { Base } from '../types/types.js'
3
+ import { ObjectLoader2 } from './objectLoader2.js'
4
+ import Traverser from './traverser.js'
5
+
6
+ describe('Traverser', () => {
7
+ test('root and two children with referenceId', async () => {
8
+ const root = `{
9
+ "list": [{
10
+ "kingsy_type": "reference",
11
+ "referencedId": "0e61e61edee00404ec6e0f9f594bce24",
12
+ "__closure": null
13
+ }],
14
+ "list2": [{
15
+ "kingsy_type": "reference",
16
+ "referencedId": "f70738e3e3e593ac11099a6ed6b71154",
17
+ "__closure": null
18
+ }],
19
+ "arr": null,
20
+ "detachedProp": null,
21
+ "detachedProp2": null,
22
+ "attachedProp": null,
23
+ "crazyProp": null,
24
+ "applicationId": "1",
25
+ "kingsy_type": "KingSy.Core.Tests.Unit.Models.BaseTests+SampleObjectBase2",
26
+ "dynamicProp": 123,
27
+ "id": "efeadaca70a85ae6d3acfc93a8b380db",
28
+ "__closure": {
29
+ "0e61e61edee00404ec6e0f9f594bce24": 100,
30
+ "f70738e3e3e593ac11099a6ed6b71154": 100
31
+ }
32
+ }`
33
+
34
+ const list1 = `{
35
+ "data": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
36
+ "applicationId": null,
37
+ "kingsy_type": "KingSy.Core.Models.DataChunk",
38
+ "id": "0e61e61edee00404ec6e0f9f594bce24"
39
+ }`
40
+
41
+ const list2 = `{
42
+ "data": [1.0, 10.0],
43
+ "applicationId": null,
44
+ "kingsy_type": "KingSy.Core.Models.DataChunk",
45
+ "id": "f70738e3e3e593ac11099a6ed6b71154"
46
+ }`
47
+
48
+ const rootObj = JSON.parse(root) as Base
49
+ const list1Obj = JSON.parse(list1) as Base
50
+ const list2Obj = JSON.parse(list2) as Base
51
+
52
+ const loader = ObjectLoader2.createFromObjects([rootObj, list1Obj, list2Obj])
53
+
54
+ const traverser = new Traverser(loader)
55
+ const r = await traverser.traverse()
56
+ expect(r).toMatchSnapshot()
57
+ })
58
+ })
@@ -0,0 +1,112 @@
1
+ import { Base, DataChunk, isBase, isReference, isScalar } from '../types/types.js'
2
+ import { ObjectLoader2 } from './objectLoader2.js'
3
+
4
+ export type ProgressStage = 'download' | 'construction'
5
+ export type OnProgress = (e: {
6
+ stage: ProgressStage
7
+ current: number
8
+ total: number
9
+ }) => void
10
+
11
+ export interface TraverserOptions {
12
+ excludeProps?: string[]
13
+ }
14
+
15
+ export default class Traverser {
16
+ #loader: ObjectLoader2
17
+ #options: TraverserOptions
18
+
19
+ #totalChildrenCount = 0
20
+ #traversedReferencesCount = 0
21
+
22
+ constructor(loader: ObjectLoader2, options?: TraverserOptions) {
23
+ this.#options = options || {}
24
+ this.#loader = loader
25
+ }
26
+
27
+ async traverse(onProgress?: OnProgress): Promise<Base> {
28
+ let firstObjectPromise: Promise<Base> | undefined = undefined
29
+ for await (const obj of this.#loader.getObjectIterator()) {
30
+ if (!firstObjectPromise) {
31
+ firstObjectPromise = this.traverseBase(obj, onProgress)
32
+ }
33
+ }
34
+
35
+ if (firstObjectPromise) {
36
+ return await firstObjectPromise
37
+ } else {
38
+ throw new Error('No objects found')
39
+ }
40
+ }
41
+
42
+ async traverseArray(array: Array<unknown>, onProgress?: OnProgress): Promise<void> {
43
+ for (let i = 0; i < 10; i++) {
44
+ const prop = array[i]
45
+ if (isScalar(prop)) continue
46
+ if (isBase(prop)) {
47
+ array[i] = await this.traverseBase(prop, onProgress)
48
+ } else if (isReference(prop)) {
49
+ array[i] = await this.traverseBase(
50
+ await this.#loader.getObject({ id: prop.referencedId }),
51
+ onProgress
52
+ )
53
+ }
54
+ }
55
+ }
56
+
57
+ async traverseBase(base: Base, onProgress?: OnProgress): Promise<Base> {
58
+ for (const ignoredProp of this.#options.excludeProps || []) {
59
+ delete (base as never)[ignoredProp]
60
+ }
61
+ if (base.__closure) {
62
+ const ids = Object.keys(base.__closure)
63
+ const promises: Promise<Base>[] = []
64
+ for (const id of ids) {
65
+ promises.push(
66
+ this.traverseBase(await this.#loader.getObject({ id }), onProgress)
67
+ )
68
+ }
69
+ await Promise.all(promises)
70
+ }
71
+ delete (base as never)['__closure']
72
+
73
+ // De-chunk
74
+ if (base.kingsy_type?.includes('DataChunk')) {
75
+ const chunk = base as DataChunk
76
+ if (chunk.data) {
77
+ await this.traverseArray(chunk.data, onProgress)
78
+ }
79
+ }
80
+
81
+ //other props
82
+ for (const prop in base) {
83
+ if (prop === '__closure') continue
84
+ if (prop === 'referenceId') continue
85
+ if (prop === 'kingsy_type') continue
86
+ if (prop === 'data') continue
87
+ const baseProp = (base as unknown as Record<string, unknown>)[prop]
88
+ if (isScalar(baseProp)) continue
89
+ if (isBase(baseProp)) {
90
+ await this.traverseBase(baseProp, onProgress)
91
+ } else if (isReference(baseProp)) {
92
+ await this.traverseBase(
93
+ await this.#loader.getObject({ id: baseProp.referencedId }),
94
+ onProgress
95
+ )
96
+ } else if (Array.isArray(baseProp)) {
97
+ await this.traverseArray(baseProp, onProgress)
98
+ }
99
+ }
100
+ if (onProgress) {
101
+ onProgress({
102
+ stage: 'construction',
103
+ current:
104
+ ++this.#traversedReferencesCount > this.#totalChildrenCount
105
+ ? this.#totalChildrenCount
106
+ : this.#traversedReferencesCount,
107
+ total: this.#totalChildrenCount
108
+ })
109
+ }
110
+ return base
111
+ }
112
+ }
@@ -0,0 +1,43 @@
1
+ import { describe, test, expect } from 'vitest'
2
+ import { IDBFactory, IDBKeyRange } from 'fake-indexeddb'
3
+ import { Base } from '../types/types.js'
4
+ import { TIME_MS } from '@kingsy/shared'
5
+ import { ObjectLoader2Factory } from '../operations/objectLoader2Factory.js'
6
+
7
+ describe('e2e', () => {
8
+ test(
9
+ 'download small model',
10
+ async () => {
11
+ // Revit sample house (good for bim-like stuff with many display meshes)
12
+ //const resource = 'https://app.kingsy.systems/streams/da9e320dad/commits/5388ef24b8'
13
+ const loader = ObjectLoader2Factory.createFromUrl({
14
+ serverUrl: 'https://app.kingsy.systems',
15
+ streamId: 'da9e320dad',
16
+ objectId: '31d10c0cea569a1e26809658ed27e281',
17
+ options: {
18
+ indexedDB: new IDBFactory(),
19
+ keyRange: IDBKeyRange
20
+ }
21
+ })
22
+
23
+ const getObjectPromise = loader.getObject({
24
+ id: '1708a78e057e8115f924c620ba686db6'
25
+ })
26
+
27
+ const bases: Base[] = []
28
+ for await (const obj of loader.getObjectIterator()) {
29
+ bases.push(obj)
30
+ }
31
+
32
+ expect(await loader.getTotalObjectCount()).toBe(1328)
33
+ expect(bases.length).toBe(1328)
34
+ const base = await getObjectPromise
35
+ expect(base).toBeDefined()
36
+ expect(base.id).toBe('1708a78e057e8115f924c620ba686db6')
37
+ const base2 = await loader.getObject({ id: '3841e3cbc45d52c47bc2f1b7b0ad4eb9' })
38
+ expect(base2).toBeDefined()
39
+ expect(base2.id).toBe('3841e3cbc45d52c47bc2f1b7b0ad4eb9')
40
+ },
41
+ 10 * TIME_MS.second
42
+ )
43
+ })
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Base ObjectLoader error
3
+ */
4
+ class BaseError extends Error {
5
+ /**
6
+ * Default message if none is passed
7
+ */
8
+ static defaultMessage = 'Unexpected error occurred'
9
+
10
+ /**
11
+ * @param {string} [message]
12
+ */
13
+ constructor(message: string) {
14
+ message ||= new.target.defaultMessage
15
+ super(message)
16
+ }
17
+ }
18
+
19
+ export class ObjectLoaderConfigurationError extends BaseError {
20
+ static defaultMessage = 'Object loader configured incorrectly!'
21
+ }
22
+
23
+ export class ObjectLoaderRuntimeError extends BaseError {
24
+ static defaultMessage = 'Object loader encountered a runtime problem!'
25
+ }