@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,48 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { MemoryDatabase } from './memoryDatabase.js'
3
+ import { Base, Item } from '../../types/types.js'
4
+
5
+ const makeItem = (id: string, foo = 'bar'): Item => ({
6
+ baseId: id,
7
+ base: { foo } as unknown as Base
8
+ })
9
+
10
+ describe('MemoryDatabase', () => {
11
+ let db: MemoryDatabase
12
+
13
+ beforeEach(() => {
14
+ db = new MemoryDatabase()
15
+ })
16
+
17
+ it('should return undefined for missing keys', async () => {
18
+ const result = await db.getAll(['missing'])
19
+ expect(result).toEqual([undefined])
20
+ })
21
+
22
+ it('should add and retrieve a single item', async () => {
23
+ const item = makeItem('id1')
24
+ await db.cacheSaveBatch({ batch: [item] })
25
+ const result = await db.getAll(['id1'])
26
+ expect(result).toEqual([item])
27
+ })
28
+
29
+ it('should add and retrieve multiple items', async () => {
30
+ const items = [makeItem('id1'), makeItem('id2', 'baz')]
31
+ await db.cacheSaveBatch({ batch: items })
32
+ const result = await db.getAll(['id1', 'id2'])
33
+ expect(result).toEqual(items)
34
+ })
35
+
36
+ it('should overwrite items with the same key', async () => {
37
+ const item1 = makeItem('id1', 'foo')
38
+ const item2 = makeItem('id1', 'bar')
39
+ await db.cacheSaveBatch({ batch: [item1] })
40
+ await db.cacheSaveBatch({ batch: [item2] })
41
+ const result = await db.getAll(['id1'])
42
+ expect(result).toEqual([item2])
43
+ })
44
+
45
+ it('disposeAsync should resolve', async () => {
46
+ await expect(db.disposeAsync()).resolves.not.toThrow()
47
+ })
48
+ })
@@ -0,0 +1,35 @@
1
+ import { Base, Item } from '../../types/types.js'
2
+ import { Database } from '../interfaces.js'
3
+ import { MemoryDatabaseOptions } from '../options.js'
4
+
5
+ export class MemoryDatabase implements Database {
6
+ private items: Map<string, Base>
7
+
8
+ constructor(options?: MemoryDatabaseOptions) {
9
+ this.items = options?.items || new Map<string, Base>()
10
+ }
11
+
12
+ getAll(keys: string[]): Promise<(Item | undefined)[]> {
13
+ const found: (Item | undefined)[] = []
14
+ for (const key of keys) {
15
+ const item = this.items.get(key)
16
+ if (item) {
17
+ found.push({ baseId: key, base: item })
18
+ } else {
19
+ found.push(undefined)
20
+ }
21
+ }
22
+ return Promise.resolve(found)
23
+ }
24
+
25
+ cacheSaveBatch({ batch }: { batch: Item[] }): Promise<void> {
26
+ for (const item of batch) {
27
+ this.items.set(item.baseId, item.base)
28
+ }
29
+ return Promise.resolve()
30
+ }
31
+
32
+ disposeAsync(): Promise<void> {
33
+ return Promise.resolve()
34
+ }
35
+ }
@@ -0,0 +1,12 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`MemoryDownloader > should add found item to results queue 1`] = `
4
+ Map {
5
+ "id1" => {
6
+ "foo": "foo",
7
+ },
8
+ "id2" => {
9
+ "foo": "bar",
10
+ },
11
+ }
12
+ `;
@@ -0,0 +1,92 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`downloader > add extra header 1`] = `
4
+ {
5
+ "base": {
6
+ "__closure": {
7
+ "childIds": 1,
8
+ },
9
+ "id": "id",
10
+ "kingsy_type": "type",
11
+ },
12
+ "baseId": "id",
13
+ "size": 0,
14
+ }
15
+ `;
16
+
17
+ exports[`downloader > download batch of one 1`] = `
18
+ [
19
+ {
20
+ "base": {
21
+ "id": "id",
22
+ "kingsy_type": "type",
23
+ },
24
+ "baseId": "id",
25
+ "size": 33,
26
+ },
27
+ ]
28
+ `;
29
+
30
+ exports[`downloader > download batch of three 1`] = `
31
+ [
32
+ {
33
+ "base": {
34
+ "id": "id1",
35
+ "kingsy_type": "type",
36
+ },
37
+ "baseId": "id1",
38
+ "size": 34,
39
+ },
40
+ {
41
+ "base": {
42
+ "id": "id2",
43
+ "kingsy_type": "type",
44
+ },
45
+ "baseId": "id2",
46
+ "size": 34,
47
+ },
48
+ {
49
+ "base": {
50
+ "id": "id3",
51
+ "kingsy_type": "type",
52
+ },
53
+ "baseId": "id3",
54
+ "size": 34,
55
+ },
56
+ ]
57
+ `;
58
+
59
+ exports[`downloader > download batch of two 1`] = `
60
+ [
61
+ {
62
+ "base": {
63
+ "id": "id1",
64
+ "kingsy_type": "type",
65
+ },
66
+ "baseId": "id1",
67
+ "size": 34,
68
+ },
69
+ {
70
+ "base": {
71
+ "id": "id2",
72
+ "kingsy_type": "type",
73
+ },
74
+ "baseId": "id2",
75
+ "size": 34,
76
+ },
77
+ ]
78
+ `;
79
+
80
+ exports[`downloader > download single exists 1`] = `
81
+ {
82
+ "base": {
83
+ "__closure": {
84
+ "childIds": 1,
85
+ },
86
+ "id": "id",
87
+ "kingsy_type": "type",
88
+ },
89
+ "baseId": "id",
90
+ "size": 0,
91
+ }
92
+ `;
@@ -0,0 +1,49 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { MemoryDownloader } from './memoryDownloader.js'
3
+ import { Base, Item } from '../../types/types.js'
4
+ import Queue from '../../helpers/queue.js'
5
+ import BufferQueue from '../../helpers/bufferQueue.js'
6
+
7
+ const makeBase = (foo: string): Base => ({ foo } as unknown as Base)
8
+
9
+ describe('MemoryDownloader', () => {
10
+ let items: Map<string, Base>
11
+ let downloader: MemoryDownloader
12
+ let results: Queue<Item>
13
+
14
+ beforeEach(() => {
15
+ items = new Map([
16
+ ['id1', makeBase('foo')],
17
+ ['id2', makeBase('bar')]
18
+ ])
19
+ downloader = new MemoryDownloader('id1', items)
20
+ results = new BufferQueue<Item>()
21
+ })
22
+
23
+ it('should download the root item', async () => {
24
+ const item = await downloader.downloadSingle()
25
+ expect(item).toEqual({ baseId: 'id1', base: { foo: 'foo' } })
26
+ })
27
+
28
+ it('should throw if root item is missing', async () => {
29
+ const missingDownloader = new MemoryDownloader('missing', items)
30
+ await expect(missingDownloader.downloadSingle()).rejects.toThrow(
31
+ 'Method not implemented.'
32
+ )
33
+ })
34
+
35
+ it('should add found item to results queue', () => {
36
+ downloader.initializePool({ results, total: 2 })
37
+ downloader.add('id2')
38
+ expect(items).toMatchSnapshot()
39
+ })
40
+
41
+ it('should throw if added item is missing', () => {
42
+ downloader.initializePool({ results, total: 2 })
43
+ expect(() => downloader.add('missing')).toThrow()
44
+ })
45
+
46
+ it('disposeAsync should resolve', async () => {
47
+ await expect(downloader.disposeAsync()).resolves.not.toThrow()
48
+ })
49
+ })
@@ -0,0 +1,39 @@
1
+ import Queue from '../../helpers/queue.js'
2
+ import { Base, Item } from '../../types/types.js'
3
+ import { Downloader } from '../interfaces.js'
4
+
5
+ export class MemoryDownloader implements Downloader {
6
+ #items: Map<string, Base>
7
+ #rootId: string
8
+ #results?: Queue<Item>
9
+
10
+ constructor(rootId: string, items: Map<string, Base>) {
11
+ this.#rootId = rootId
12
+ this.#items = items
13
+ }
14
+ initializePool(params: {
15
+ results: Queue<Item>
16
+ total: number
17
+ maxDownloadBatchWait?: number
18
+ }): void {
19
+ this.#results = params.results
20
+ }
21
+ downloadSingle(): Promise<Item> {
22
+ const root = this.#items.get(this.#rootId)
23
+ if (root) {
24
+ return Promise.resolve({ baseId: this.#rootId, base: root })
25
+ }
26
+ return Promise.reject(new Error('Method not implemented.'))
27
+ }
28
+ disposeAsync(): Promise<void> {
29
+ return Promise.resolve()
30
+ }
31
+ add(id: string): void {
32
+ const base = this.#items.get(id)
33
+ if (base) {
34
+ this.#results?.add({ baseId: id, base })
35
+ return
36
+ }
37
+ throw new Error('Method not implemented.')
38
+ }
39
+ }
@@ -0,0 +1,161 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import createFetchMock from 'vitest-fetch-mock'
3
+ import { vi } from 'vitest'
4
+ import { Item } from '../../types/types.js'
5
+ import ServerDownloader from './serverDownloader.js'
6
+ import { MemoryPump } from '../../helpers/memoryPump.js'
7
+
8
+ describe('downloader', () => {
9
+ test('download batch of one', async () => {
10
+ const fetchMocker = createFetchMock(vi)
11
+ const i: Item = { baseId: 'id', base: { id: 'id', kingsy_type: 'type' } }
12
+ fetchMocker.mockResponseOnce('id\t' + JSON.stringify(i.base) + '\n')
13
+ const pump = new MemoryPump()
14
+ const downloader = new ServerDownloader({
15
+ serverUrl: 'http://kingsy.test',
16
+ streamId: 'streamId',
17
+ objectId: 'objectId',
18
+ token: 'token',
19
+ fetch: fetchMocker
20
+ })
21
+ downloader.initializePool({ results: pump, total: 1, maxDownloadBatchWait: 200 })
22
+ downloader.add('id')
23
+ await downloader.disposeAsync()
24
+ const r = []
25
+ for await (const x of pump.gather([i.baseId])) {
26
+ r.push(x)
27
+ }
28
+
29
+ expect(r).toMatchSnapshot()
30
+ await downloader.disposeAsync()
31
+ })
32
+
33
+ test('download batch of two', async () => {
34
+ const fetchMocker = createFetchMock(vi)
35
+ const i1: Item = { baseId: 'id1', base: { id: 'id1', kingsy_type: 'type' } }
36
+ const i2: Item = { baseId: 'id2', base: { id: 'id2', kingsy_type: 'type' } }
37
+ fetchMocker.mockResponseOnce(
38
+ 'id1\t' + JSON.stringify(i1.base) + '\nid2\t' + JSON.stringify(i2.base) + '\n'
39
+ )
40
+
41
+ const pump = new MemoryPump()
42
+ const downloader = new ServerDownloader({
43
+ serverUrl: 'http://kingsy.test',
44
+ streamId: 'streamId',
45
+ objectId: 'objectId',
46
+ token: 'token',
47
+
48
+ fetch: fetchMocker
49
+ })
50
+ downloader.initializePool({ results: pump, total: 2, maxDownloadBatchWait: 200 })
51
+ downloader.add('id1')
52
+ downloader.add('id2')
53
+ await downloader.disposeAsync()
54
+ const r = []
55
+ for await (const x of pump.gather([i1.baseId, i2.baseId])) {
56
+ r.push(x)
57
+ }
58
+
59
+ expect(r).toMatchSnapshot()
60
+ await downloader.disposeAsync()
61
+ })
62
+
63
+ test('download batch of three', async () => {
64
+ const fetchMocker = createFetchMock(vi)
65
+ const i1: Item = { baseId: 'id1', base: { id: 'id1', kingsy_type: 'type' } }
66
+ const i2: Item = { baseId: 'id2', base: { id: 'id2', kingsy_type: 'type' } }
67
+ const i3: Item = { baseId: 'id3', base: { id: 'id3', kingsy_type: 'type' } }
68
+ fetchMocker.mockResponseOnce(
69
+ 'id1\t' +
70
+ JSON.stringify(i1.base) +
71
+ '\nid2\t' +
72
+ JSON.stringify(i2.base) +
73
+ '\nid3\t' +
74
+ JSON.stringify(i3.base) +
75
+ '\n'
76
+ )
77
+
78
+ const pump = new MemoryPump()
79
+ const downloader = new ServerDownloader({
80
+ serverUrl: 'http://kingsy.test',
81
+ streamId: 'streamId',
82
+ objectId: 'objectId',
83
+ token: 'token',
84
+
85
+ fetch: fetchMocker
86
+ })
87
+ downloader.initializePool({ results: pump, total: 3, maxDownloadBatchWait: 200 })
88
+ downloader.add('id1')
89
+ downloader.add('id2')
90
+ downloader.add('id3')
91
+ await downloader.disposeAsync()
92
+ const r = []
93
+ for await (const x of pump.gather([i1.baseId, i2.baseId, i3.baseId])) {
94
+ r.push(x)
95
+ }
96
+
97
+ expect(r).toMatchSnapshot()
98
+ await downloader.disposeAsync()
99
+ })
100
+
101
+ test('download single exists', async () => {
102
+ const fetchMocker = createFetchMock(vi)
103
+ const i: Item = {
104
+ baseId: 'id',
105
+ base: { id: 'id', kingsy_type: 'type', __closure: { childIds: 1 } }
106
+ }
107
+ fetchMocker.mockResponseOnce(JSON.stringify(i.base))
108
+ const downloader = new ServerDownloader({
109
+ serverUrl: 'http://kingsy.test',
110
+ streamId: 'streamId',
111
+ objectId: i.baseId,
112
+ token: 'token',
113
+
114
+ fetch: fetchMocker
115
+ })
116
+ const x = await downloader.downloadSingle()
117
+ expect(x).toMatchSnapshot()
118
+ await downloader.disposeAsync()
119
+ })
120
+
121
+ test('add extra header', async () => {
122
+ const fetchMocker = createFetchMock(vi)
123
+ const i: Item = {
124
+ baseId: 'id',
125
+ base: { id: 'id', kingsy_type: 'type', __closure: { childIds: 1 } }
126
+ }
127
+ fetchMocker.mockResponseIf(
128
+ (req) => req.headers.get('x-test') === 'asdf',
129
+ JSON.stringify(i.base)
130
+ )
131
+ const headers = new Headers()
132
+ headers.set('x-test', 'asdf')
133
+ const downloader = new ServerDownloader({
134
+ serverUrl: 'http://kingsy.test',
135
+ headers,
136
+ streamId: 'streamId',
137
+ objectId: i.baseId,
138
+ token: 'token',
139
+
140
+ fetch: fetchMocker
141
+ })
142
+ const x = await downloader.downloadSingle()
143
+ expect(x).toMatchSnapshot()
144
+ await downloader.disposeAsync()
145
+ })
146
+
147
+ test('can dispose used', async () => {
148
+ const fetchMocker = createFetchMock(vi)
149
+ const headers = new Headers()
150
+ const downloader = new ServerDownloader({
151
+ serverUrl: 'http://kingsy.test',
152
+ headers,
153
+ streamId: 'streamId',
154
+ objectId: 'objectId',
155
+ token: 'token',
156
+
157
+ fetch: fetchMocker
158
+ })
159
+ await downloader.disposeAsync()
160
+ })
161
+ })
@@ -0,0 +1,217 @@
1
+ import BatchedPool from '../../helpers/batchedPool.js'
2
+ import Queue from '../../helpers/queue.js'
3
+ import { ObjectLoaderRuntimeError } from '../../types/errors.js'
4
+ import { Fetcher, isBase, Item, take } from '../../types/types.js'
5
+ import { Downloader } from '../interfaces.js'
6
+
7
+ export interface ServerDownloaderOptions {
8
+ serverUrl: string
9
+ streamId: string
10
+ objectId: string
11
+ token?: string
12
+ headers?: Headers
13
+ fetch?: Fetcher
14
+ }
15
+
16
+ export default class ServerDownloader implements Downloader {
17
+ #requestUrlRootObj: string
18
+ #requestUrlChildren: string
19
+ #headers: HeadersInit
20
+ #options: ServerDownloaderOptions
21
+ #fetch: Fetcher
22
+ #results?: Queue<Item>
23
+
24
+ #downloadQueue?: BatchedPool<string>
25
+ #decoder = new TextDecoder()
26
+
27
+ constructor(options: ServerDownloaderOptions) {
28
+ this.#options = options
29
+ this.#fetch =
30
+ options.fetch ?? ((...args): Promise<Response> => globalThis.fetch(...args))
31
+
32
+ this.#headers = {}
33
+ if (options.headers) {
34
+ for (const header of options.headers.entries()) {
35
+ this.#headers[header[0]] = header[1]
36
+ }
37
+ }
38
+ this.#headers['Accept'] = `text/plain`
39
+
40
+ if (this.#options.token) {
41
+ this.#headers['Authorization'] = `Bearer ${this.#options.token}`
42
+ }
43
+ this.#requestUrlChildren = `${this.#options.serverUrl}/api/getobjects/${
44
+ this.#options.streamId
45
+ }`
46
+ this.#requestUrlRootObj = `${this.#options.serverUrl}/objects/${
47
+ this.#options.streamId
48
+ }/${this.#options.objectId}/single`
49
+ }
50
+
51
+ #getDownloadCountAndSizes(total: number): number[] {
52
+ if (total <= 50) {
53
+ return [total]
54
+ }
55
+
56
+ return [10000, 25000, 10000, 1000]
57
+ }
58
+
59
+ initializePool(params: {
60
+ results: Queue<Item>
61
+ total: number
62
+ maxDownloadBatchWait?: number
63
+ }): void {
64
+ const { results, total } = params
65
+ this.#results = results
66
+ this.#downloadQueue = new BatchedPool<string>({
67
+ concurrencyAndSizes: this.#getDownloadCountAndSizes(total),
68
+ maxWaitTime: params.maxDownloadBatchWait,
69
+ processFunction: (batch: string[]): Promise<void> =>
70
+ this.downloadBatch({
71
+ batch,
72
+ url: this.#requestUrlChildren,
73
+ headers: this.#headers
74
+ })
75
+ })
76
+ }
77
+
78
+ #getPool(): BatchedPool<string> {
79
+ if (this.#downloadQueue) {
80
+ return this.#downloadQueue
81
+ }
82
+ throw new Error('Download pool is not initialized')
83
+ }
84
+
85
+ add(id: string): void {
86
+ this.#getPool().add(id)
87
+ }
88
+
89
+ async disposeAsync(): Promise<void> {
90
+ await this.#downloadQueue?.disposeAsync()
91
+ }
92
+
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
+ }
105
+ }
106
+
107
+ async downloadBatch(params: {
108
+ batch: string[]
109
+ url: string
110
+ headers: HeadersInit
111
+ }): Promise<void> {
112
+ const { batch, url, headers } = params
113
+ const keys = new Set<string>(batch)
114
+ const response = await this.#fetch(url, {
115
+ method: 'POST',
116
+ headers: { ...headers, 'Content-Type': 'application/json' },
117
+ body: JSON.stringify({ objects: JSON.stringify(batch) })
118
+ })
119
+
120
+ this.#validateResponse(response)
121
+ if (!response.body) {
122
+ throw new Error('ReadableStream not supported or response has no body.')
123
+ }
124
+
125
+ const reader = response.body.getReader()
126
+ let leftover = new Uint8Array(0)
127
+
128
+ let count = 0
129
+ while (true) {
130
+ const { done, value } = await reader.read()
131
+ if (done) break
132
+
133
+ leftover = await this.processArray(leftover, value, keys, async () => {
134
+ count++
135
+ if (count % 1000 === 0) {
136
+ await new Promise((resolve) => setTimeout(resolve, 100)) //allow other stuff to happen
137
+ }
138
+ })
139
+ }
140
+ if (keys.size > 0) {
141
+ throw new Error(
142
+ 'Items requested were not downloaded: ' + take(keys.values(), 10).join(',')
143
+ )
144
+ }
145
+ }
146
+
147
+ async processArray(
148
+ leftover: Uint8Array,
149
+ value: Uint8Array,
150
+ keys: Set<string>,
151
+ callback: () => Promise<void>
152
+ ): Promise<Uint8Array> {
153
+ //this concat will allocate a new array
154
+ const combined = this.concatUint8Arrays(leftover, value)
155
+ let start = 0
156
+
157
+ //subarray doesn't allocate
158
+ for (let i = 0; i < combined.length; i++) {
159
+ if (combined[i] === 0x0a) {
160
+ const line = combined.subarray(start, i) // line without \n
161
+ //strings are allocated here
162
+ const item = this.processLine(line)
163
+ this.#results?.add(item)
164
+ start = i + 1
165
+ await callback()
166
+ keys.delete(item.baseId)
167
+ }
168
+ }
169
+ return combined.subarray(start) // carry over remainder
170
+ }
171
+
172
+ processLine(line: Uint8Array): Item {
173
+ for (let i = 0; i < line.length; i++) {
174
+ if (line[i] === 0x09) {
175
+ //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)
179
+ const item = this.#processJson(baseId, base)
180
+ item.size = json.length
181
+ return item
182
+ }
183
+ }
184
+ throw new ObjectLoaderRuntimeError(
185
+ 'Invalid line format: ' + this.#decoder.decode(line)
186
+ )
187
+ }
188
+
189
+ concatUint8Arrays(a: Uint8Array, b: Uint8Array): Uint8Array {
190
+ const c = new Uint8Array(a.length + b.length)
191
+ c.set(a, 0)
192
+ c.set(b, a.length)
193
+ return c
194
+ }
195
+
196
+ async downloadSingle(): Promise<Item> {
197
+ const response = await this.#fetch(this.#requestUrlRootObj, {
198
+ headers: this.#headers
199
+ })
200
+ this.#validateResponse(response)
201
+ const responseText = await response.text()
202
+ const item = this.#processJson(this.#options.objectId, responseText)
203
+ item.size = 0
204
+ return item
205
+ }
206
+
207
+ #validateResponse(response: Response): void {
208
+ if (!response.ok) {
209
+ if ([401, 403].includes(response.status)) {
210
+ throw new ObjectLoaderRuntimeError('You do not have access!')
211
+ }
212
+ throw new ObjectLoaderRuntimeError(
213
+ `Failed to fetch objects: ${response.status} ${response.statusText})`
214
+ )
215
+ }
216
+ }
217
+ }
@@ -0,0 +1,18 @@
1
+ import Queue from '../helpers/queue.js'
2
+ import { Item } from '../types/types.js'
3
+
4
+ export interface Downloader extends Queue<string> {
5
+ initializePool(params: {
6
+ results: Queue<Item>
7
+ total: number
8
+ maxDownloadBatchWait?: number
9
+ }): void
10
+ downloadSingle(): Promise<Item>
11
+ disposeAsync(): Promise<void>
12
+ }
13
+
14
+ export interface Database {
15
+ getAll(keys: string[]): Promise<(Item | undefined)[]>
16
+ cacheSaveBatch(params: { batch: Item[] }): Promise<void>
17
+ disposeAsync(): Promise<void>
18
+ }