@speckle/objectloader2 2.25.7 → 2.25.9

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 (414) hide show
  1. package/dist/commonjs/{operations → core}/interfaces.d.ts +2 -2
  2. package/dist/commonjs/core/interfaces.d.ts.map +1 -0
  3. package/dist/commonjs/core/interfaces.js.map +1 -0
  4. package/dist/commonjs/{operations → core}/objectLoader2.d.ts +1 -1
  5. package/dist/commonjs/core/objectLoader2.d.ts.map +1 -0
  6. package/dist/commonjs/core/objectLoader2.js +128 -0
  7. package/dist/commonjs/core/objectLoader2.js.map +1 -0
  8. package/dist/commonjs/{operations → core}/objectLoader2Factory.d.ts +4 -2
  9. package/dist/commonjs/core/objectLoader2Factory.d.ts.map +1 -0
  10. package/dist/commonjs/core/objectLoader2Factory.js +74 -0
  11. package/dist/commonjs/core/objectLoader2Factory.js.map +1 -0
  12. package/dist/{esm/operations → commonjs/core}/options.d.ts +2 -1
  13. package/dist/commonjs/core/options.d.ts.map +1 -0
  14. package/dist/commonjs/{operations → core}/options.js.map +1 -1
  15. package/dist/commonjs/core/stages/ItemStore.d.ts +37 -0
  16. package/dist/commonjs/core/stages/ItemStore.d.ts.map +1 -0
  17. package/dist/commonjs/core/stages/ItemStore.js +167 -0
  18. package/dist/commonjs/core/stages/ItemStore.js.map +1 -0
  19. package/dist/commonjs/core/stages/cacheReader.d.ts +18 -0
  20. package/dist/commonjs/core/stages/cacheReader.d.ts.map +1 -0
  21. package/dist/commonjs/core/stages/cacheReader.js +72 -0
  22. package/dist/commonjs/core/stages/cacheReader.js.map +1 -0
  23. package/dist/commonjs/core/stages/cacheWriter.d.ts +15 -0
  24. package/dist/commonjs/core/stages/cacheWriter.d.ts.map +1 -0
  25. package/dist/commonjs/core/stages/cacheWriter.js +50 -0
  26. package/dist/commonjs/core/stages/cacheWriter.js.map +1 -0
  27. package/dist/commonjs/{operations/databases → core/stages}/indexedDatabase.d.ts +2 -9
  28. package/dist/commonjs/core/stages/indexedDatabase.d.ts.map +1 -0
  29. package/dist/commonjs/core/stages/indexedDatabase.js +31 -0
  30. package/dist/commonjs/core/stages/indexedDatabase.js.map +1 -0
  31. package/dist/{esm/operations/databases → commonjs/core/stages/memory}/memoryDatabase.d.ts +4 -4
  32. package/dist/commonjs/core/stages/memory/memoryDatabase.d.ts.map +1 -0
  33. package/dist/commonjs/{operations/databases → core/stages/memory}/memoryDatabase.js +4 -1
  34. package/dist/commonjs/core/stages/memory/memoryDatabase.js.map +1 -0
  35. package/dist/commonjs/{operations/downloaders → core/stages/memory}/memoryDownloader.d.ts +3 -3
  36. package/dist/commonjs/core/stages/memory/memoryDownloader.d.ts.map +1 -0
  37. package/dist/commonjs/core/stages/memory/memoryDownloader.js.map +1 -0
  38. package/dist/{esm/operations/downloaders → commonjs/core/stages}/serverDownloader.d.ts +4 -5
  39. package/dist/commonjs/core/stages/serverDownloader.d.ts.map +1 -0
  40. package/dist/commonjs/{operations/downloaders → core/stages}/serverDownloader.js +81 -31
  41. package/dist/commonjs/core/stages/serverDownloader.js.map +1 -0
  42. package/dist/commonjs/core/traverser.d.ts.map +1 -0
  43. package/dist/commonjs/{operations → core}/traverser.js +7 -7
  44. package/dist/commonjs/core/traverser.js.map +1 -0
  45. package/dist/commonjs/deferment/MemoryCache.d.ts +35 -0
  46. package/dist/commonjs/deferment/MemoryCache.d.ts.map +1 -0
  47. package/dist/commonjs/deferment/MemoryCache.js +162 -0
  48. package/dist/commonjs/deferment/MemoryCache.js.map +1 -0
  49. package/dist/commonjs/deferment/MemoryCache.test.d.ts +2 -0
  50. package/dist/commonjs/deferment/MemoryCache.test.d.ts.map +1 -0
  51. package/dist/commonjs/deferment/MemoryCache.test.js +153 -0
  52. package/dist/commonjs/deferment/MemoryCache.test.js.map +1 -0
  53. package/dist/commonjs/deferment/defermentManager.d.ts +16 -0
  54. package/dist/commonjs/deferment/defermentManager.d.ts.map +1 -0
  55. package/dist/commonjs/deferment/defermentManager.js +58 -0
  56. package/dist/commonjs/deferment/defermentManager.js.map +1 -0
  57. package/dist/commonjs/deferment/defermentManager.test.d.ts +2 -0
  58. package/dist/commonjs/deferment/defermentManager.test.d.ts.map +1 -0
  59. package/dist/commonjs/deferment/defermentManager.test.js +188 -0
  60. package/dist/commonjs/deferment/defermentManager.test.js.map +1 -0
  61. package/dist/commonjs/deferment/deferredBase.d.ts +12 -0
  62. package/dist/commonjs/deferment/deferredBase.d.ts.map +1 -0
  63. package/dist/commonjs/deferment/deferredBase.js +27 -0
  64. package/dist/commonjs/deferment/deferredBase.js.map +1 -0
  65. package/dist/commonjs/index.d.ts +3 -2
  66. package/dist/commonjs/index.d.ts.map +1 -1
  67. package/dist/commonjs/index.js +6 -3
  68. package/dist/commonjs/index.js.map +1 -1
  69. package/dist/commonjs/{helpers → queues}/aggregateQueue.d.ts +2 -1
  70. package/dist/commonjs/queues/aggregateQueue.d.ts.map +1 -0
  71. package/dist/commonjs/{helpers → queues}/aggregateQueue.js +4 -0
  72. package/dist/commonjs/queues/aggregateQueue.js.map +1 -0
  73. package/dist/{esm/helpers → commonjs/queues}/asyncGeneratorQueue.d.ts +1 -1
  74. package/dist/commonjs/queues/asyncGeneratorQueue.d.ts.map +1 -0
  75. package/dist/commonjs/{helpers → queues}/asyncGeneratorQueue.js +2 -1
  76. package/dist/commonjs/queues/asyncGeneratorQueue.js.map +1 -0
  77. package/dist/{esm/helpers → commonjs/queues}/batchedPool.d.ts +2 -1
  78. package/dist/commonjs/queues/batchedPool.d.ts.map +1 -0
  79. package/dist/commonjs/queues/batchedPool.js.map +1 -0
  80. package/dist/{esm/helpers → commonjs/queues}/batchingQueue.d.ts +4 -1
  81. package/dist/commonjs/queues/batchingQueue.d.ts.map +1 -0
  82. package/dist/commonjs/queues/batchingQueue.dispose.test.d.ts +2 -0
  83. package/dist/commonjs/queues/batchingQueue.dispose.test.d.ts.map +1 -0
  84. package/dist/commonjs/queues/batchingQueue.dispose.test.js +64 -0
  85. package/dist/commonjs/queues/batchingQueue.dispose.test.js.map +1 -0
  86. package/dist/commonjs/queues/batchingQueue.js +123 -0
  87. package/dist/commonjs/queues/batchingQueue.js.map +1 -0
  88. package/dist/commonjs/queues/batchingQueue.test.d.ts +2 -0
  89. package/dist/commonjs/queues/batchingQueue.test.d.ts.map +1 -0
  90. package/dist/commonjs/queues/batchingQueue.test.js +138 -0
  91. package/dist/commonjs/queues/batchingQueue.test.js.map +1 -0
  92. package/dist/{esm/helpers → commonjs/queues}/bufferQueue.d.ts +1 -0
  93. package/dist/commonjs/queues/bufferQueue.d.ts.map +1 -0
  94. package/dist/commonjs/{helpers → queues}/bufferQueue.js +3 -0
  95. package/dist/commonjs/queues/bufferQueue.js.map +1 -0
  96. package/dist/commonjs/{helpers → queues}/keyedQueue.d.ts +1 -0
  97. package/dist/commonjs/queues/keyedQueue.d.ts.map +1 -0
  98. package/dist/commonjs/{helpers → queues}/keyedQueue.js +11 -0
  99. package/dist/commonjs/queues/keyedQueue.js.map +1 -0
  100. package/dist/commonjs/queues/queue.d.ts +5 -0
  101. package/dist/commonjs/queues/queue.d.ts.map +1 -0
  102. package/dist/commonjs/{helpers → queues}/queue.js.map +1 -1
  103. package/dist/commonjs/types/functions.d.ts +21 -0
  104. package/dist/commonjs/types/functions.d.ts.map +1 -0
  105. package/dist/commonjs/types/functions.js +92 -0
  106. package/dist/commonjs/types/functions.js.map +1 -0
  107. package/dist/commonjs/types/types.d.ts +1 -7
  108. package/dist/commonjs/types/types.d.ts.map +1 -1
  109. package/dist/commonjs/types/types.js +0 -36
  110. package/dist/commonjs/types/types.js.map +1 -1
  111. package/dist/esm/{operations → core}/interfaces.d.ts +2 -2
  112. package/dist/esm/core/interfaces.d.ts.map +1 -0
  113. package/dist/esm/core/interfaces.js.map +1 -0
  114. package/dist/esm/{operations → core}/objectLoader2.d.ts +1 -1
  115. package/dist/esm/core/objectLoader2.d.ts.map +1 -0
  116. package/dist/esm/core/objectLoader2.js +121 -0
  117. package/dist/esm/core/objectLoader2.js.map +1 -0
  118. package/dist/esm/{operations → core}/objectLoader2Factory.d.ts +4 -2
  119. package/dist/esm/core/objectLoader2Factory.d.ts.map +1 -0
  120. package/dist/esm/core/objectLoader2Factory.js +67 -0
  121. package/dist/esm/core/objectLoader2Factory.js.map +1 -0
  122. package/dist/{commonjs/operations → esm/core}/options.d.ts +2 -1
  123. package/dist/esm/core/options.d.ts.map +1 -0
  124. package/dist/esm/{operations → core}/options.js.map +1 -1
  125. package/dist/esm/core/stages/ItemStore.d.ts +37 -0
  126. package/dist/esm/core/stages/ItemStore.d.ts.map +1 -0
  127. package/dist/esm/core/stages/ItemStore.js +163 -0
  128. package/dist/esm/core/stages/ItemStore.js.map +1 -0
  129. package/dist/esm/core/stages/cacheReader.d.ts +18 -0
  130. package/dist/esm/core/stages/cacheReader.d.ts.map +1 -0
  131. package/dist/esm/core/stages/cacheReader.js +65 -0
  132. package/dist/esm/core/stages/cacheReader.js.map +1 -0
  133. package/dist/esm/core/stages/cacheWriter.d.ts +15 -0
  134. package/dist/esm/core/stages/cacheWriter.d.ts.map +1 -0
  135. package/dist/esm/core/stages/cacheWriter.js +43 -0
  136. package/dist/esm/core/stages/cacheWriter.js.map +1 -0
  137. package/dist/esm/{operations/databases → core/stages}/indexedDatabase.d.ts +2 -9
  138. package/dist/esm/core/stages/indexedDatabase.d.ts.map +1 -0
  139. package/dist/esm/core/stages/indexedDatabase.js +28 -0
  140. package/dist/esm/core/stages/indexedDatabase.js.map +1 -0
  141. package/dist/{commonjs/operations/databases → esm/core/stages/memory}/memoryDatabase.d.ts +4 -4
  142. package/dist/esm/core/stages/memory/memoryDatabase.d.ts.map +1 -0
  143. package/dist/esm/{operations/databases → core/stages/memory}/memoryDatabase.js +4 -1
  144. package/dist/esm/core/stages/memory/memoryDatabase.js.map +1 -0
  145. package/dist/esm/{operations/downloaders → core/stages/memory}/memoryDownloader.d.ts +3 -3
  146. package/dist/esm/core/stages/memory/memoryDownloader.d.ts.map +1 -0
  147. package/dist/esm/core/stages/memory/memoryDownloader.js.map +1 -0
  148. package/dist/{commonjs/operations/downloaders → esm/core/stages}/serverDownloader.d.ts +4 -5
  149. package/dist/esm/core/stages/serverDownloader.d.ts.map +1 -0
  150. package/dist/esm/{operations/downloaders → core/stages}/serverDownloader.js +80 -30
  151. package/dist/esm/core/stages/serverDownloader.js.map +1 -0
  152. package/dist/esm/core/traverser.d.ts.map +1 -0
  153. package/dist/esm/{operations → core}/traverser.js +1 -1
  154. package/dist/esm/core/traverser.js.map +1 -0
  155. package/dist/esm/deferment/MemoryCache.d.ts +35 -0
  156. package/dist/esm/deferment/MemoryCache.d.ts.map +1 -0
  157. package/dist/esm/deferment/MemoryCache.js +157 -0
  158. package/dist/esm/deferment/MemoryCache.js.map +1 -0
  159. package/dist/esm/deferment/MemoryCache.test.d.ts +2 -0
  160. package/dist/esm/deferment/MemoryCache.test.d.ts.map +1 -0
  161. package/dist/esm/deferment/MemoryCache.test.js +151 -0
  162. package/dist/esm/deferment/MemoryCache.test.js.map +1 -0
  163. package/dist/esm/deferment/defermentManager.d.ts +16 -0
  164. package/dist/esm/deferment/defermentManager.d.ts.map +1 -0
  165. package/dist/esm/deferment/defermentManager.js +54 -0
  166. package/dist/esm/deferment/defermentManager.js.map +1 -0
  167. package/dist/esm/deferment/defermentManager.test.d.ts +2 -0
  168. package/dist/esm/deferment/defermentManager.test.d.ts.map +1 -0
  169. package/dist/esm/deferment/defermentManager.test.js +186 -0
  170. package/dist/esm/deferment/defermentManager.test.js.map +1 -0
  171. package/dist/esm/deferment/deferredBase.d.ts +12 -0
  172. package/dist/esm/deferment/deferredBase.d.ts.map +1 -0
  173. package/dist/esm/deferment/deferredBase.js +23 -0
  174. package/dist/esm/deferment/deferredBase.js.map +1 -0
  175. package/dist/esm/index.d.ts +3 -2
  176. package/dist/esm/index.d.ts.map +1 -1
  177. package/dist/esm/index.js +3 -2
  178. package/dist/esm/index.js.map +1 -1
  179. package/dist/esm/{helpers → queues}/aggregateQueue.d.ts +2 -1
  180. package/dist/esm/queues/aggregateQueue.d.ts.map +1 -0
  181. package/dist/esm/{helpers → queues}/aggregateQueue.js +4 -0
  182. package/dist/esm/queues/aggregateQueue.js.map +1 -0
  183. package/dist/{commonjs/helpers → esm/queues}/asyncGeneratorQueue.d.ts +1 -1
  184. package/dist/esm/queues/asyncGeneratorQueue.d.ts.map +1 -0
  185. package/dist/esm/{helpers → queues}/asyncGeneratorQueue.js +2 -1
  186. package/dist/esm/queues/asyncGeneratorQueue.js.map +1 -0
  187. package/dist/{commonjs/helpers → esm/queues}/batchedPool.d.ts +2 -1
  188. package/dist/esm/queues/batchedPool.d.ts.map +1 -0
  189. package/dist/esm/queues/batchedPool.js.map +1 -0
  190. package/dist/{commonjs/helpers → esm/queues}/batchingQueue.d.ts +4 -1
  191. package/dist/esm/queues/batchingQueue.d.ts.map +1 -0
  192. package/dist/esm/queues/batchingQueue.dispose.test.d.ts +2 -0
  193. package/dist/esm/queues/batchingQueue.dispose.test.d.ts.map +1 -0
  194. package/dist/esm/queues/batchingQueue.dispose.test.js +59 -0
  195. package/dist/esm/queues/batchingQueue.dispose.test.js.map +1 -0
  196. package/dist/esm/queues/batchingQueue.js +117 -0
  197. package/dist/esm/queues/batchingQueue.js.map +1 -0
  198. package/dist/esm/queues/batchingQueue.test.d.ts +2 -0
  199. package/dist/esm/queues/batchingQueue.test.d.ts.map +1 -0
  200. package/dist/esm/queues/batchingQueue.test.js +133 -0
  201. package/dist/esm/queues/batchingQueue.test.js.map +1 -0
  202. package/dist/{commonjs/helpers → esm/queues}/bufferQueue.d.ts +1 -0
  203. package/dist/esm/queues/bufferQueue.d.ts.map +1 -0
  204. package/dist/esm/{helpers → queues}/bufferQueue.js +3 -0
  205. package/dist/esm/queues/bufferQueue.js.map +1 -0
  206. package/dist/esm/{helpers → queues}/keyedQueue.d.ts +1 -0
  207. package/dist/esm/queues/keyedQueue.d.ts.map +1 -0
  208. package/dist/esm/{helpers → queues}/keyedQueue.js +11 -0
  209. package/dist/esm/queues/keyedQueue.js.map +1 -0
  210. package/dist/esm/queues/queue.d.ts +5 -0
  211. package/dist/esm/queues/queue.d.ts.map +1 -0
  212. package/dist/esm/{helpers → queues}/queue.js.map +1 -1
  213. package/dist/esm/types/functions.d.ts +21 -0
  214. package/dist/esm/types/functions.d.ts.map +1 -0
  215. package/dist/esm/types/functions.js +83 -0
  216. package/dist/esm/types/functions.js.map +1 -0
  217. package/dist/esm/types/types.d.ts +1 -7
  218. package/dist/esm/types/types.d.ts.map +1 -1
  219. package/dist/esm/types/types.js +1 -32
  220. package/dist/esm/types/types.js.map +1 -1
  221. package/package.json +2 -3
  222. package/readme.md +64 -16
  223. package/src/{operations → core}/interfaces.ts +2 -2
  224. package/src/{operations → core}/objectLoader2.spec.ts +22 -9
  225. package/src/core/objectLoader2.ts +162 -0
  226. package/src/core/objectLoader2Factory.ts +87 -0
  227. package/src/{operations → core}/options.ts +2 -1
  228. package/src/core/stages/ItemStore.ts +196 -0
  229. package/src/{operations/databases → core/stages}/__snapshots__/indexedDatabase.spec.ts.snap +4 -4
  230. package/src/{operations/downloaders → core/stages}/__snapshots__/serverDownloader.spec.ts.snap +25 -0
  231. package/src/{helpers → core/stages}/cacheReader.spec.ts +12 -7
  232. package/src/core/stages/cacheReader.ts +88 -0
  233. package/src/core/stages/cacheWriter.spec.ts +213 -0
  234. package/src/core/stages/cacheWriter.ts +64 -0
  235. package/src/{operations/databases → core/stages}/indexedDatabase.spec.ts +7 -3
  236. package/src/core/stages/indexedDatabase.ts +49 -0
  237. package/src/{operations/databases → core/stages/memory}/memoryDatabase.spec.ts +5 -5
  238. package/src/{operations/databases → core/stages/memory}/memoryDatabase.ts +7 -4
  239. package/src/{operations/downloaders → core/stages/memory}/memoryDownloader.spec.ts +3 -3
  240. package/src/{operations/downloaders → core/stages/memory}/memoryDownloader.ts +3 -3
  241. package/src/{operations/downloaders → core/stages}/serverDownloader.spec.ts +102 -11
  242. package/src/{operations/downloaders → core/stages}/serverDownloader.ts +87 -30
  243. package/src/{operations → core}/traverser.ts +2 -1
  244. package/src/deferment/MemoryCache.test.ts +187 -0
  245. package/src/deferment/MemoryCache.ts +189 -0
  246. package/src/deferment/defermentManager.test.ts +220 -0
  247. package/src/deferment/defermentManager.ts +59 -0
  248. package/src/deferment/deferredBase.ts +29 -0
  249. package/src/index.ts +3 -2
  250. package/src/{helpers → queues}/aggregateQueue.ts +5 -1
  251. package/src/{helpers → queues}/asyncGeneratorQueue.ts +2 -1
  252. package/src/{helpers → queues}/batchedPool.ts +3 -1
  253. package/src/queues/batchingQueue.dispose.test.ts +74 -0
  254. package/src/queues/batchingQueue.test.ts +149 -0
  255. package/src/queues/batchingQueue.ts +138 -0
  256. package/src/{helpers → queues}/bufferQueue.ts +3 -0
  257. package/src/{helpers → queues}/keyedQueue.ts +12 -0
  258. package/src/{helpers → queues}/queue.ts +1 -0
  259. package/src/test/e2e.spec.ts +2 -2
  260. package/src/types/functions.spec.ts +155 -0
  261. package/src/types/functions.ts +118 -0
  262. package/src/types/types.ts +1 -51
  263. package/dist/commonjs/helpers/aggregateQueue.d.ts.map +0 -1
  264. package/dist/commonjs/helpers/aggregateQueue.js.map +0 -1
  265. package/dist/commonjs/helpers/asyncGeneratorQueue.d.ts.map +0 -1
  266. package/dist/commonjs/helpers/asyncGeneratorQueue.js.map +0 -1
  267. package/dist/commonjs/helpers/batchedPool.d.ts.map +0 -1
  268. package/dist/commonjs/helpers/batchedPool.js.map +0 -1
  269. package/dist/commonjs/helpers/batchingQueue.d.ts.map +0 -1
  270. package/dist/commonjs/helpers/batchingQueue.js +0 -77
  271. package/dist/commonjs/helpers/batchingQueue.js.map +0 -1
  272. package/dist/commonjs/helpers/bufferQueue.d.ts.map +0 -1
  273. package/dist/commonjs/helpers/bufferQueue.js.map +0 -1
  274. package/dist/commonjs/helpers/cachePump.d.ts +0 -22
  275. package/dist/commonjs/helpers/cachePump.d.ts.map +0 -1
  276. package/dist/commonjs/helpers/cachePump.js +0 -86
  277. package/dist/commonjs/helpers/cachePump.js.map +0 -1
  278. package/dist/commonjs/helpers/cacheReader.d.ts +0 -14
  279. package/dist/commonjs/helpers/cacheReader.d.ts.map +0 -1
  280. package/dist/commonjs/helpers/cacheReader.js +0 -58
  281. package/dist/commonjs/helpers/cacheReader.js.map +0 -1
  282. package/dist/commonjs/helpers/defermentManager.d.ts +0 -28
  283. package/dist/commonjs/helpers/defermentManager.d.ts.map +0 -1
  284. package/dist/commonjs/helpers/defermentManager.js +0 -150
  285. package/dist/commonjs/helpers/defermentManager.js.map +0 -1
  286. package/dist/commonjs/helpers/deferredBase.d.ts +0 -19
  287. package/dist/commonjs/helpers/deferredBase.d.ts.map +0 -1
  288. package/dist/commonjs/helpers/deferredBase.js +0 -51
  289. package/dist/commonjs/helpers/deferredBase.js.map +0 -1
  290. package/dist/commonjs/helpers/keyedQueue.d.ts.map +0 -1
  291. package/dist/commonjs/helpers/keyedQueue.js.map +0 -1
  292. package/dist/commonjs/helpers/memoryPump.d.ts +0 -15
  293. package/dist/commonjs/helpers/memoryPump.d.ts.map +0 -1
  294. package/dist/commonjs/helpers/memoryPump.js +0 -34
  295. package/dist/commonjs/helpers/memoryPump.js.map +0 -1
  296. package/dist/commonjs/helpers/pump.d.ts +0 -8
  297. package/dist/commonjs/helpers/pump.d.ts.map +0 -1
  298. package/dist/commonjs/helpers/pump.js +0 -3
  299. package/dist/commonjs/helpers/pump.js.map +0 -1
  300. package/dist/commonjs/helpers/queue.d.ts +0 -4
  301. package/dist/commonjs/helpers/queue.d.ts.map +0 -1
  302. package/dist/commonjs/operations/databases/indexedDatabase.d.ts.map +0 -1
  303. package/dist/commonjs/operations/databases/indexedDatabase.js +0 -98
  304. package/dist/commonjs/operations/databases/indexedDatabase.js.map +0 -1
  305. package/dist/commonjs/operations/databases/memoryDatabase.d.ts.map +0 -1
  306. package/dist/commonjs/operations/databases/memoryDatabase.js.map +0 -1
  307. package/dist/commonjs/operations/downloaders/memoryDownloader.d.ts.map +0 -1
  308. package/dist/commonjs/operations/downloaders/memoryDownloader.js.map +0 -1
  309. package/dist/commonjs/operations/downloaders/serverDownloader.d.ts.map +0 -1
  310. package/dist/commonjs/operations/downloaders/serverDownloader.js.map +0 -1
  311. package/dist/commonjs/operations/interfaces.d.ts.map +0 -1
  312. package/dist/commonjs/operations/interfaces.js.map +0 -1
  313. package/dist/commonjs/operations/objectLoader2.d.ts.map +0 -1
  314. package/dist/commonjs/operations/objectLoader2.js +0 -101
  315. package/dist/commonjs/operations/objectLoader2.js.map +0 -1
  316. package/dist/commonjs/operations/objectLoader2Factory.d.ts.map +0 -1
  317. package/dist/commonjs/operations/objectLoader2Factory.js +0 -68
  318. package/dist/commonjs/operations/objectLoader2Factory.js.map +0 -1
  319. package/dist/commonjs/operations/options.d.ts.map +0 -1
  320. package/dist/commonjs/operations/traverser.d.ts.map +0 -1
  321. package/dist/commonjs/operations/traverser.js.map +0 -1
  322. package/dist/esm/helpers/aggregateQueue.d.ts.map +0 -1
  323. package/dist/esm/helpers/aggregateQueue.js.map +0 -1
  324. package/dist/esm/helpers/asyncGeneratorQueue.d.ts.map +0 -1
  325. package/dist/esm/helpers/asyncGeneratorQueue.js.map +0 -1
  326. package/dist/esm/helpers/batchedPool.d.ts.map +0 -1
  327. package/dist/esm/helpers/batchedPool.js.map +0 -1
  328. package/dist/esm/helpers/batchingQueue.d.ts.map +0 -1
  329. package/dist/esm/helpers/batchingQueue.js +0 -71
  330. package/dist/esm/helpers/batchingQueue.js.map +0 -1
  331. package/dist/esm/helpers/bufferQueue.d.ts.map +0 -1
  332. package/dist/esm/helpers/bufferQueue.js.map +0 -1
  333. package/dist/esm/helpers/cachePump.d.ts +0 -22
  334. package/dist/esm/helpers/cachePump.d.ts.map +0 -1
  335. package/dist/esm/helpers/cachePump.js +0 -79
  336. package/dist/esm/helpers/cachePump.js.map +0 -1
  337. package/dist/esm/helpers/cacheReader.d.ts +0 -14
  338. package/dist/esm/helpers/cacheReader.d.ts.map +0 -1
  339. package/dist/esm/helpers/cacheReader.js +0 -51
  340. package/dist/esm/helpers/cacheReader.js.map +0 -1
  341. package/dist/esm/helpers/defermentManager.d.ts +0 -28
  342. package/dist/esm/helpers/defermentManager.d.ts.map +0 -1
  343. package/dist/esm/helpers/defermentManager.js +0 -146
  344. package/dist/esm/helpers/defermentManager.js.map +0 -1
  345. package/dist/esm/helpers/deferredBase.d.ts +0 -19
  346. package/dist/esm/helpers/deferredBase.d.ts.map +0 -1
  347. package/dist/esm/helpers/deferredBase.js +0 -47
  348. package/dist/esm/helpers/deferredBase.js.map +0 -1
  349. package/dist/esm/helpers/keyedQueue.d.ts.map +0 -1
  350. package/dist/esm/helpers/keyedQueue.js.map +0 -1
  351. package/dist/esm/helpers/memoryPump.d.ts +0 -15
  352. package/dist/esm/helpers/memoryPump.d.ts.map +0 -1
  353. package/dist/esm/helpers/memoryPump.js +0 -30
  354. package/dist/esm/helpers/memoryPump.js.map +0 -1
  355. package/dist/esm/helpers/pump.d.ts +0 -8
  356. package/dist/esm/helpers/pump.d.ts.map +0 -1
  357. package/dist/esm/helpers/pump.js +0 -2
  358. package/dist/esm/helpers/pump.js.map +0 -1
  359. package/dist/esm/helpers/queue.d.ts +0 -4
  360. package/dist/esm/helpers/queue.d.ts.map +0 -1
  361. package/dist/esm/operations/databases/indexedDatabase.d.ts.map +0 -1
  362. package/dist/esm/operations/databases/indexedDatabase.js +0 -93
  363. package/dist/esm/operations/databases/indexedDatabase.js.map +0 -1
  364. package/dist/esm/operations/databases/memoryDatabase.d.ts.map +0 -1
  365. package/dist/esm/operations/databases/memoryDatabase.js.map +0 -1
  366. package/dist/esm/operations/downloaders/memoryDownloader.d.ts.map +0 -1
  367. package/dist/esm/operations/downloaders/memoryDownloader.js.map +0 -1
  368. package/dist/esm/operations/downloaders/serverDownloader.d.ts.map +0 -1
  369. package/dist/esm/operations/downloaders/serverDownloader.js.map +0 -1
  370. package/dist/esm/operations/interfaces.d.ts.map +0 -1
  371. package/dist/esm/operations/interfaces.js.map +0 -1
  372. package/dist/esm/operations/objectLoader2.d.ts.map +0 -1
  373. package/dist/esm/operations/objectLoader2.js +0 -94
  374. package/dist/esm/operations/objectLoader2.js.map +0 -1
  375. package/dist/esm/operations/objectLoader2Factory.d.ts.map +0 -1
  376. package/dist/esm/operations/objectLoader2Factory.js +0 -61
  377. package/dist/esm/operations/objectLoader2Factory.js.map +0 -1
  378. package/dist/esm/operations/options.d.ts.map +0 -1
  379. package/dist/esm/operations/traverser.d.ts.map +0 -1
  380. package/dist/esm/operations/traverser.js.map +0 -1
  381. package/src/helpers/__snapshots__/cachePump.spec.ts.snap +0 -31
  382. package/src/helpers/__snapshots__/defermentManager.spec.ts.snap +0 -8
  383. package/src/helpers/batchingQueue.ts +0 -85
  384. package/src/helpers/cachePump.disposal.spec.ts +0 -51
  385. package/src/helpers/cachePump.spec.ts +0 -104
  386. package/src/helpers/cachePump.ts +0 -107
  387. package/src/helpers/cacheReader.ts +0 -64
  388. package/src/helpers/defermentManager.defermentTotals.spec.ts +0 -53
  389. package/src/helpers/defermentManager.disposal.spec.ts +0 -28
  390. package/src/helpers/defermentManager.spec.ts +0 -37
  391. package/src/helpers/defermentManager.ts +0 -160
  392. package/src/helpers/deferredBase.ts +0 -55
  393. package/src/helpers/memoryPump.ts +0 -40
  394. package/src/helpers/pump.ts +0 -8
  395. package/src/operations/databases/indexedDatabase.ts +0 -126
  396. package/src/operations/objectLoader2.ts +0 -119
  397. package/src/operations/objectLoader2Factory.ts +0 -78
  398. package/dist/commonjs/{operations → core}/interfaces.js +0 -0
  399. package/dist/commonjs/{operations → core}/options.js +0 -0
  400. package/dist/commonjs/{operations/downloaders → core/stages/memory}/memoryDownloader.js +0 -0
  401. package/dist/commonjs/{operations → core}/traverser.d.ts +0 -0
  402. package/dist/commonjs/{helpers → queues}/batchedPool.js +0 -0
  403. package/dist/commonjs/{helpers → queues}/queue.js +0 -0
  404. package/dist/esm/{operations → core}/interfaces.js +0 -0
  405. package/dist/esm/{operations → core}/options.js +0 -0
  406. package/dist/esm/{operations/downloaders → core/stages/memory}/memoryDownloader.js +0 -0
  407. package/dist/esm/{operations → core}/traverser.d.ts +0 -0
  408. package/dist/esm/{helpers → queues}/batchedPool.js +0 -0
  409. package/dist/esm/{helpers → queues}/queue.js +0 -0
  410. package/src/{operations → core}/__snapshots__/objectLoader2.spec.ts.snap +0 -0
  411. package/src/{operations → core}/__snapshots__/traverser.spec.ts.snap +0 -0
  412. package/src/{helpers → core/stages}/__snapshots__/cacheReader.spec.ts.snap +0 -0
  413. package/src/{operations/downloaders → core/stages/memory}/__snapshots__/memoryDownloader.spec.ts.snap +0 -0
  414. package/src/{operations → core}/traverser.spec.ts +1 -1
@@ -1,7 +1,8 @@
1
- import BatchedPool from '../../helpers/batchedPool.js'
2
- import Queue from '../../helpers/queue.js'
1
+ import BatchedPool from '../../queues/batchedPool.js'
2
+ import Queue from '../../queues/queue.js'
3
3
  import { ObjectLoaderRuntimeError } from '../../types/errors.js'
4
- import { Fetcher, isBase, Item, take } from '../../types/types.js'
4
+ import { Fetcher, indexOf, isBase, take } from '../../types/functions.js'
5
+ import { Item } from '../../types/types.js'
5
6
  import { Downloader } from '../interfaces.js'
6
7
 
7
8
  export interface ServerDownloaderOptions {
@@ -13,6 +14,8 @@ export interface ServerDownloaderOptions {
13
14
  fetch?: Fetcher
14
15
  }
15
16
 
17
+ const MAX_SAFARI_DECODE_BYTES = 2 * 1024 * 1024 * 1024 - 1024 * 1024 // 2GB minus a margin
18
+
16
19
  export default class ServerDownloader implements Downloader {
17
20
  #requestUrlRootObj: string
18
21
  #requestUrlChildren: string
@@ -20,9 +23,14 @@ export default class ServerDownloader implements Downloader {
20
23
  #options: ServerDownloaderOptions
21
24
  #fetch: Fetcher
22
25
  #results?: Queue<Item>
26
+ #total?: number
23
27
 
24
28
  #downloadQueue?: BatchedPool<string>
25
- #decoder = new TextDecoder()
29
+ #decoder = new TextDecoder('utf-8', { fatal: true })
30
+ #decodedBytesCount = 0
31
+
32
+ #rawString: string = 'Objects.Other.RawEncoding'
33
+ #rawEncoding: Uint8Array
26
34
 
27
35
  constructor(options: ServerDownloaderOptions) {
28
36
  this.#options = options
@@ -46,6 +54,9 @@ export default class ServerDownloader implements Downloader {
46
54
  this.#requestUrlRootObj = `${this.#options.serverUrl}/objects/${
47
55
  this.#options.streamId
48
56
  }/${this.#options.objectId}/single`
57
+
58
+ const encoder = new TextEncoder()
59
+ this.#rawEncoding = encoder.encode(this.#rawString)
49
60
  }
50
61
 
51
62
  #getDownloadCountAndSizes(total: number): number[] {
@@ -63,6 +74,7 @@ export default class ServerDownloader implements Downloader {
63
74
  }): void {
64
75
  const { results, total } = params
65
76
  this.#results = results
77
+ this.#total = total
66
78
  this.#downloadQueue = new BatchedPool<string>({
67
79
  concurrencyAndSizes: this.#getDownloadCountAndSizes(total),
68
80
  maxWaitTime: params.maxDownloadBatchWait,
@@ -86,22 +98,26 @@ export default class ServerDownloader implements Downloader {
86
98
  this.#getPool().add(id)
87
99
  }
88
100
 
89
- async disposeAsync(): Promise<void> {
90
- await this.#downloadQueue?.disposeAsync()
91
- }
101
+ /*
102
+ This is the most frequently reported and confirmed reason for this error in Safari. There's a known bug in WebKit (Safari's rendering engine) where TextDecoder can fail or throw a RangeError after decoding around 2GB of data. Chrome and other browsers handle much larger amounts of data without this specific limitation.
92
103
 
93
- #processJson(baseId: string, unparsedBase: string): Item {
94
- let base: unknown
95
- try {
96
- base = JSON.parse(unparsedBase)
97
- } catch (e: unknown) {
98
- throw new Error(`Error parsing object ${baseId}: ${(e as Error).message}`)
99
- }
100
- if (isBase(base)) {
101
- return { baseId, base }
102
- } else {
103
- throw new ObjectLoaderRuntimeError(`${baseId} is not a base`)
104
+ Why it happens: It seems to be an internal memory or indexing limitation within Safari's TextDecoder implementation. After a certain threshold of data has been processed by a TextDecoder instance, it starts throwing this error.
105
+
106
+ Chrome's behavior: Chrome generally handles larger data sizes without this specific RangeError. It might become slow or run out of general memory, but not typically with this specific error.
107
+ */
108
+ decodeChunk(chunkBuffer: Uint8Array): string {
109
+ if (this.#decodedBytesCount + chunkBuffer.byteLength > MAX_SAFARI_DECODE_BYTES) {
110
+ // Safari is approaching its limit, create a new decoder
111
+ this.#decoder = new TextDecoder('utf-8', { fatal: true })
112
+ this.#decodedBytesCount = 0 // Reset counter for the new decoder
104
113
  }
114
+ const decodedText = this.#decoder.decode(chunkBuffer)
115
+ this.#decodedBytesCount += chunkBuffer.byteLength
116
+ return decodedText
117
+ }
118
+
119
+ async disposeAsync(): Promise<void> {
120
+ await this.#downloadQueue?.disposeAsync()
105
121
  }
106
122
 
107
123
  async downloadBatch(params: {
@@ -130,7 +146,7 @@ export default class ServerDownloader implements Downloader {
130
146
  const { done, value } = await reader.read()
131
147
  if (done) break
132
148
 
133
- leftover = await this.processArray(leftover, value, keys, async () => {
149
+ leftover = await this.#processArray(leftover, value, keys, async () => {
134
150
  count++
135
151
  if (count % 1000 === 0) {
136
152
  await new Promise((resolve) => setTimeout(resolve, 100)) //allow other stuff to happen
@@ -142,16 +158,20 @@ export default class ServerDownloader implements Downloader {
142
158
  'Items requested were not downloaded: ' + take(keys.values(), 10).join(',')
143
159
  )
144
160
  }
161
+ count += keys.size // count the leftovers
162
+ if (count >= this.#total!) {
163
+ await this.#results?.disposeAsync() // mark the queue as done
164
+ }
145
165
  }
146
166
 
147
- async processArray(
167
+ async #processArray(
148
168
  leftover: Uint8Array,
149
169
  value: Uint8Array,
150
170
  keys: Set<string>,
151
171
  callback: () => Promise<void>
152
172
  ): Promise<Uint8Array> {
153
173
  //this concat will allocate a new array
154
- const combined = this.concatUint8Arrays(leftover, value)
174
+ const combined = this.#concatUint8Arrays(leftover, value)
155
175
  let start = 0
156
176
 
157
177
  //subarray doesn't allocate
@@ -159,34 +179,65 @@ export default class ServerDownloader implements Downloader {
159
179
  if (combined[i] === 0x0a) {
160
180
  const line = combined.subarray(start, i) // line without \n
161
181
  //strings are allocated here
162
- const item = this.processLine(line)
163
- this.#results?.add(item)
182
+ const item = this.#processLine(line)
164
183
  start = i + 1
165
184
  await callback()
166
185
  keys.delete(item.baseId)
186
+ this.#results?.add(item)
167
187
  }
168
188
  }
169
189
  return combined.subarray(start) // carry over remainder
170
190
  }
171
191
 
172
- processLine(line: Uint8Array): Item {
192
+ #processLine(line: Uint8Array): Item {
173
193
  for (let i = 0; i < line.length; i++) {
174
194
  if (line[i] === 0x09) {
175
195
  //this is a tab
176
- const baseId = this.#decoder.decode(line.subarray(0, i))
177
- const json = line.subarray(i + 1)
178
- const base = this.#decoder.decode(json)
196
+ const baseId = this.decodeChunk(line.subarray(0, i))
197
+ const jsonBytes = line.subarray(i + 1)
198
+
199
+ if (!this.#isValidBytes(jsonBytes)) {
200
+ return { baseId, base: undefined }
201
+ }
202
+ const base = this.decodeChunk(jsonBytes)
179
203
  const item = this.#processJson(baseId, base)
180
- item.size = json.length
204
+ item.size = jsonBytes.length
181
205
  return item
182
206
  }
183
207
  }
184
208
  throw new ObjectLoaderRuntimeError(
185
- 'Invalid line format: ' + this.#decoder.decode(line)
209
+ 'Invalid line format in response: ' + this.decodeChunk(line)
186
210
  )
187
211
  }
188
212
 
189
- concatUint8Arrays(a: Uint8Array, b: Uint8Array): Uint8Array {
213
+ #processJson(baseId: string, unparsedBase: string): Item {
214
+ let base: unknown
215
+ try {
216
+ base = JSON.parse(unparsedBase)
217
+ } catch (e: unknown) {
218
+ throw new Error(`Error parsing object ${baseId}: ${(e as Error).message}`)
219
+ }
220
+ if (isBase(base)) {
221
+ return { baseId, base }
222
+ } else {
223
+ throw new ObjectLoaderRuntimeError(`${baseId} is not a base`)
224
+ }
225
+ }
226
+ #isValidString(json: string): boolean {
227
+ if (!json.includes(this.#rawString)) {
228
+ return true
229
+ }
230
+ return false
231
+ }
232
+
233
+ #isValidBytes(json: Uint8Array): boolean {
234
+ if (indexOf(json, this.#rawEncoding) === -1) {
235
+ return true
236
+ }
237
+ return false
238
+ }
239
+
240
+ #concatUint8Arrays(a: Uint8Array, b: Uint8Array): Uint8Array {
190
241
  const c = new Uint8Array(a.length + b.length)
191
242
  c.set(a, 0)
192
243
  c.set(b, a.length)
@@ -199,7 +250,13 @@ export default class ServerDownloader implements Downloader {
199
250
  })
200
251
  this.#validateResponse(response)
201
252
  const responseText = await response.text()
253
+ if (!this.#isValidString(responseText)) {
254
+ throw new ObjectLoaderRuntimeError('Invalid base object')
255
+ }
202
256
  const item = this.#processJson(this.#options.objectId, responseText)
257
+ if (!item.base) {
258
+ throw new ObjectLoaderRuntimeError('Invalid base object')
259
+ }
203
260
  item.size = 0
204
261
  return item
205
262
  }
@@ -1,4 +1,5 @@
1
- import { Base, DataChunk, isBase, isReference, isScalar } from '../types/types.js'
1
+ import { isScalar, isBase, isReference } from '../types/functions.js'
2
+ import { Base, DataChunk } from '../types/types.js'
2
3
  import { ObjectLoader2 } from './objectLoader2.js'
3
4
 
4
5
  export type ProgressStage = 'download' | 'construction'
@@ -0,0 +1,187 @@
1
+ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'
2
+ import { MemoryCache, MemoryCacheItem, MemoryCacheOptions } from './MemoryCache.js'
3
+ import { Item, Base } from '../types/types.js'
4
+ import { CustomLogger } from '../types/functions.js'
5
+
6
+ const logger: CustomLogger = () => {
7
+ // console.log(message, ...optionalParams)
8
+ }
9
+
10
+ const defaultOptions: MemoryCacheOptions = {
11
+ maxSizeInMb: 10,
12
+ ttlms: 1000
13
+ }
14
+
15
+ const createItem = (id: string, size: number, referencedIds: string[] = []): Item => {
16
+ const base: Base & { data?: unknown } = {
17
+ id,
18
+ // eslint-disable-next-line camelcase
19
+ speckle_type: 'Base'
20
+ }
21
+
22
+ if (referencedIds.length > 0) {
23
+ base.data = referencedIds.map((referencedId) => ({
24
+ // eslint-disable-next-line camelcase
25
+ speckle_type: 'reference',
26
+ referencedId
27
+ }))
28
+ }
29
+
30
+ return {
31
+ baseId: id,
32
+ base,
33
+ size
34
+ }
35
+ }
36
+
37
+ describe('MemoryCacheItem', () => {
38
+ it('should correctly determine if it is expired', () => {
39
+ const item = createItem('1', 100)
40
+ const now = Date.now()
41
+ const cacheItem = new MemoryCacheItem(item, now + 1000)
42
+ expect(cacheItem.isExpired(now + 500)).toBe(false)
43
+ expect(cacheItem.isExpired(now + 1500)).toBe(true)
44
+ })
45
+
46
+ it('should update its access time', () => {
47
+ const item = createItem('1', 100)
48
+ const now = Date.now()
49
+ const cacheItem = new MemoryCacheItem(item, now + 1000)
50
+ cacheItem.setAccess(now + 500, 1000)
51
+ expect(cacheItem.isExpired(now + 1000)).toBe(false)
52
+ expect(cacheItem.isExpired(now + 1600)).toBe(true)
53
+ })
54
+
55
+ it('should return the correct item', () => {
56
+ const item = createItem('1', 100)
57
+ const cacheItem = new MemoryCacheItem(item, Date.now() + 1000)
58
+ expect(cacheItem.getItem()).toEqual(item)
59
+ })
60
+
61
+ it('should be done if expired', () => {
62
+ const item = createItem('1', 100)
63
+ const now = Date.now()
64
+ const cacheItem = new MemoryCacheItem(item, now + 1000)
65
+ expect(cacheItem.done(now + 1500)).toBe(true)
66
+ expect(cacheItem.done(now + 500)).toBe(false)
67
+ })
68
+ })
69
+
70
+ describe('MemoryCache', () => {
71
+ beforeEach(() => {
72
+ vi.useFakeTimers()
73
+ })
74
+
75
+ afterEach(() => {
76
+ vi.useRealTimers()
77
+ })
78
+
79
+ it('should add and get an item', () => {
80
+ const cache = new MemoryCache(defaultOptions, logger)
81
+ const item = createItem('1', 100)
82
+ const requestItem = vi.fn()
83
+
84
+ cache.add(item, requestItem)
85
+ const retrieved = cache.get('1')
86
+
87
+ expect(retrieved).toEqual(item)
88
+ expect(requestItem).not.toHaveBeenCalled()
89
+ })
90
+
91
+ it('should update expiry on get', () => {
92
+ const cache = new MemoryCache({ ...defaultOptions, ttlms: 100 }, logger)
93
+ const item = createItem('1', 100)
94
+ cache.add(item, vi.fn())
95
+
96
+ vi.advanceTimersByTime(50)
97
+ const retrieved = cache.get('1')
98
+ expect(retrieved).toBeDefined()
99
+
100
+ vi.advanceTimersByTime(80)
101
+ const retrievedAgain = cache.get('1')
102
+ expect(retrievedAgain).toBeDefined()
103
+ })
104
+
105
+ it('should return undefined for non-existent item', () => {
106
+ const cache = new MemoryCache(defaultOptions, logger)
107
+ expect(cache.get('non-existent')).toBeUndefined()
108
+ })
109
+
110
+ it('should scan for references and request missing ones', () => {
111
+ const cache = new MemoryCache(defaultOptions, logger)
112
+ const requestItem = vi.fn()
113
+ const item = createItem('1', 100, ['2', '3'])
114
+
115
+ cache.add(createItem('3', 50), vi.fn()) // pre-cache one of the references
116
+ cache.scanForReferences(item.base, requestItem)
117
+
118
+ expect(requestItem).toHaveBeenCalledTimes(1)
119
+ expect(requestItem).toHaveBeenCalledWith('2')
120
+ expect(requestItem).not.toHaveBeenCalledWith('3')
121
+ })
122
+
123
+ it('should clean up expired items when size exceeds max', () => {
124
+ const options = { maxSizeInMb: 0, ttlms: 100 } // 100 bytes
125
+ const cache = new MemoryCache(options, logger)
126
+ const requestItem = vi.fn()
127
+
128
+ const item1 = createItem('1', 60)
129
+ const item2 = createItem('2', 60)
130
+
131
+ const now = 1
132
+ cache.add(item1, requestItem, now - 200)
133
+ cache.cleanCache(now)
134
+ cache.add(item2, requestItem, now + 100)
135
+
136
+ expect(cache.get('1')).toBeUndefined()
137
+ expect(cache.get('2')).toBeDefined()
138
+ })
139
+
140
+ it('should not clean up expired items if they have references', () => {
141
+ const options = { maxSizeInMb: 0.0001, ttlms: 100 } // 100 bytes
142
+ const cache = new MemoryCache(options, logger)
143
+ const requestItem = vi.fn()
144
+
145
+ const item1 = createItem('1', 60)
146
+ const item2 = createItem('2', 60, ['1'])
147
+
148
+ cache.add(item1, requestItem)
149
+ vi.advanceTimersByTime(110) // item1 expires
150
+ cache.add(item2, requestItem) // item2 adds a reference to item1
151
+
152
+ vi.advanceTimersByTime(100)
153
+
154
+ expect(cache.get('1')).toBeDefined()
155
+ expect(cache.get('2')).toBeDefined()
156
+ })
157
+
158
+ it('compareMaybeBasesByReferences should sort correctly', () => {
159
+ const cache = new MemoryCache(defaultOptions, logger)
160
+ const requestItem = vi.fn()
161
+
162
+ const item1 = createItem('1', 10)
163
+ const item2 = createItem('2', 10)
164
+ const item3 = createItem('3', 10, ['1', '2'])
165
+ const item4 = createItem('4', 10, ['2'])
166
+
167
+ cache.add(item1, requestItem)
168
+ cache.add(item2, requestItem)
169
+ cache.add(item3, requestItem)
170
+ cache.add(item4, requestItem)
171
+
172
+ expect(cache.compareMaybeBasesByReferences('1', '2')).toBe(-1)
173
+ expect(cache.compareMaybeBasesByReferences('2', '1')).toBe(1)
174
+ expect(cache.compareMaybeBasesByReferences('1', '1')).toBe(0)
175
+ expect(cache.compareMaybeBasesByReferences('1', '5')).toBe(1)
176
+ expect(cache.compareMaybeBasesByReferences('5', '1')).toBe(-1)
177
+ expect(cache.compareMaybeBasesByReferences('5', '6')).toBe(0)
178
+ })
179
+
180
+ it('should throw when used after dispose', () => {
181
+ const cache = new MemoryCache(defaultOptions, logger)
182
+ cache.dispose()
183
+ const item = createItem('1', 100)
184
+ expect(() => cache.add(item, vi.fn())).toThrow('MemoryCache is disposed')
185
+ expect(() => cache.get('1')).toThrow('MemoryCache is disposed')
186
+ })
187
+ })
@@ -0,0 +1,189 @@
1
+ import { CustomLogger } from '../types/functions.js'
2
+ import { Item } from '../types/types.js'
3
+
4
+ export interface MemoryCacheOptions {
5
+ maxSizeInMb: number
6
+ ttlms: number
7
+ }
8
+
9
+ export class MemoryCacheItem {
10
+ private item: Item
11
+ private expiresAt: number // Timestamp in ms
12
+
13
+ constructor(item: Item, expiresAt: number) {
14
+ this.item = item
15
+ this.expiresAt = expiresAt
16
+ }
17
+
18
+ isExpired(now: number): boolean {
19
+ return now > this.expiresAt
20
+ }
21
+ setAccess(now: number, ttl: number): void {
22
+ this.expiresAt = now + ttl
23
+ }
24
+
25
+ getItem(): Item {
26
+ return this.item
27
+ }
28
+
29
+ done(now: number): boolean {
30
+ if (this.isExpired(now)) {
31
+ return true
32
+ }
33
+ return false
34
+ }
35
+ }
36
+
37
+ export class MemoryCache {
38
+ private isGathered: Map<string, boolean> = new Map()
39
+ private references: Map<string, number> = new Map()
40
+ private cache: Map<string, MemoryCacheItem> = new Map()
41
+
42
+ private options: MemoryCacheOptions
43
+ private logger: CustomLogger
44
+ private disposed = false
45
+ private currentSize = 0
46
+ private timer?: ReturnType<typeof setTimeout>
47
+
48
+ constructor(options: MemoryCacheOptions, logger: CustomLogger) {
49
+ this.options = options
50
+ this.logger = logger
51
+ this.resetGlobalTimer()
52
+ }
53
+
54
+ add(item: Item, requestItem: (id: string) => void, testNow?: number): void {
55
+ if (this.disposed) throw new Error('MemoryCache is disposed')
56
+ this.currentSize += item.size || 0
57
+ this.cache.set(
58
+ item.baseId,
59
+ new MemoryCacheItem(item, (testNow || this.now()) + this.options.ttlms)
60
+ )
61
+
62
+ if (!this.isGathered.has(item.baseId)) {
63
+ this.isGathered.set(item.baseId, true)
64
+ this.scanForReferences(item.base!, requestItem)
65
+ }
66
+ }
67
+
68
+ get(id: string): Item | undefined {
69
+ if (this.disposed) throw new Error('MemoryCache is disposed')
70
+ const item = this.cache.get(id)
71
+ if (item) {
72
+ item.setAccess(this.now(), this.options.ttlms)
73
+ return item.getItem()
74
+ }
75
+ return undefined
76
+ }
77
+
78
+ scanForReferences(data: unknown, requestItem: (id: string) => void): void {
79
+ const scan = (item: unknown): void => {
80
+ // Stop if the item is null or not an object (i.e., primitive)
81
+ if (item === null || typeof item !== 'object') {
82
+ return
83
+ }
84
+
85
+ // If it's an array, scan each element
86
+ if (Array.isArray(item)) {
87
+ for (const element of item) {
88
+ scan(element)
89
+ }
90
+ return
91
+ }
92
+
93
+ // If it's an object, scan its properties
94
+ for (const key in item) {
95
+ if (Object.prototype.hasOwnProperty.call(item, key)) {
96
+ // We found the target property!
97
+ if (key === 'referencedId') {
98
+ const value = (item as { referencedId: unknown }).referencedId
99
+ // Ensure the value is a string before adding it
100
+ if (typeof value === 'string') {
101
+ this.references.set(value, (this.references.get(value) || 0) + 1)
102
+ if (!this.cache.has(value)) {
103
+ requestItem(value)
104
+ }
105
+ }
106
+ }
107
+
108
+ // Continue scanning deeper into the object's properties
109
+ scan((item as Record<string, unknown>)[key])
110
+ }
111
+ }
112
+ }
113
+
114
+ scan(data)
115
+ }
116
+
117
+ private resetGlobalTimer(): void {
118
+ const run = (): void => {
119
+ this.cleanCache()
120
+ this.timer = setTimeout(run, this.options.ttlms)
121
+ }
122
+ this.timer = setTimeout(run, this.options.ttlms)
123
+ }
124
+
125
+ private now(): number {
126
+ return Date.now()
127
+ }
128
+
129
+ cleanCache(testNow?: number): void {
130
+ const maxSizeBytes = this.options.maxSizeInMb * 1024 * 1024
131
+ if (this.currentSize < maxSizeBytes) {
132
+ this.logger(
133
+ `cache size (${this.currentSize} < ${maxSizeBytes}) is ok, no need to clean`
134
+ )
135
+ return
136
+ }
137
+ const now = testNow || this.now()
138
+ let cleaned = 0
139
+ const start = performance.now()
140
+ for (const deferredBase of Array.from(this.cache.values())
141
+ .filter((x) => x.isExpired(now))
142
+ .sort((a, b) =>
143
+ this.compareMaybeBasesByReferences(a.getItem().baseId, b.getItem().baseId)
144
+ )) {
145
+ if (deferredBase.done(now)) {
146
+ const id = deferredBase.getItem().baseId
147
+ const referenceCount = this.references.get(id) || 0
148
+ if (referenceCount > 0) {
149
+ // Skip eviction for items with reference counts greater than 0,
150
+ // as they are still in use and should not be removed from the cache.
151
+ continue
152
+ }
153
+ this.currentSize -= deferredBase.getItem().size || 0
154
+ this.cache.delete(id)
155
+ cleaned++
156
+ if (this.currentSize < maxSizeBytes) {
157
+ break
158
+ }
159
+ }
160
+ }
161
+ this.logger(
162
+ `cleaned cache: cleaned ${cleaned}, cached ${this.cache.size}, time ${
163
+ performance.now() - start
164
+ }`
165
+ )
166
+ return
167
+ }
168
+
169
+ compareMaybeBasesByReferences(id1: string, id2: string): number {
170
+ const a = this.references.get(id1)
171
+ const b = this.references.get(id2)
172
+ if (a === undefined && b === undefined) return 0
173
+ if (a === undefined) return -1
174
+ if (b === undefined) return 1
175
+ return a - b
176
+ }
177
+
178
+ dispose(): void {
179
+ if (this.disposed) return
180
+ this.disposed = true
181
+ if (this.timer) {
182
+ clearTimeout(this.timer)
183
+ this.timer = undefined
184
+ }
185
+ this.cache.clear()
186
+ this.isGathered.clear()
187
+ this.references.clear()
188
+ }
189
+ }