@livestore/react 0.4.0-dev.21 → 0.4.0-dev.22

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 (97) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/StoreRegistryContext.d.ts +56 -0
  3. package/dist/StoreRegistryContext.d.ts.map +1 -0
  4. package/dist/StoreRegistryContext.js +61 -0
  5. package/dist/StoreRegistryContext.js.map +1 -0
  6. package/dist/__tests__/fixture.d.ts.map +1 -1
  7. package/dist/__tests__/fixture.js +1 -6
  8. package/dist/__tests__/fixture.js.map +1 -1
  9. package/dist/experimental/components/LiveList.d.ts +4 -2
  10. package/dist/experimental/components/LiveList.d.ts.map +1 -1
  11. package/dist/experimental/components/LiveList.js +6 -5
  12. package/dist/experimental/components/LiveList.js.map +1 -1
  13. package/dist/experimental/mod.d.ts +0 -1
  14. package/dist/experimental/mod.d.ts.map +1 -1
  15. package/dist/experimental/mod.js +0 -1
  16. package/dist/experimental/mod.js.map +1 -1
  17. package/dist/mod.d.ts +4 -3
  18. package/dist/mod.d.ts.map +1 -1
  19. package/dist/mod.js +3 -2
  20. package/dist/mod.js.map +1 -1
  21. package/dist/useClientDocument.d.ts.map +1 -1
  22. package/dist/useClientDocument.js +1 -4
  23. package/dist/useClientDocument.js.map +1 -1
  24. package/dist/useQuery.d.ts +1 -1
  25. package/dist/useQuery.d.ts.map +1 -1
  26. package/dist/useQuery.js +2 -5
  27. package/dist/useQuery.js.map +1 -1
  28. package/dist/useStore.d.ts +50 -46
  29. package/dist/useStore.d.ts.map +1 -1
  30. package/dist/useStore.js +66 -59
  31. package/dist/useStore.js.map +1 -1
  32. package/dist/useStore.test.d.ts.map +1 -0
  33. package/dist/{experimental/multi-store/useStore.test.js → useStore.test.js} +20 -22
  34. package/dist/useStore.test.js.map +1 -0
  35. package/package.json +7 -7
  36. package/src/StoreRegistryContext.tsx +69 -0
  37. package/src/__tests__/fixture.tsx +1 -13
  38. package/src/experimental/components/LiveList.tsx +13 -4
  39. package/src/experimental/mod.ts +0 -1
  40. package/src/mod.ts +4 -3
  41. package/src/useClientDocument.ts +1 -5
  42. package/src/useQuery.ts +2 -6
  43. package/src/{experimental/multi-store/useStore.test.tsx → useStore.test.tsx} +32 -30
  44. package/src/useStore.ts +94 -66
  45. package/dist/LiveStoreContext.d.ts +0 -40
  46. package/dist/LiveStoreContext.d.ts.map +0 -1
  47. package/dist/LiveStoreContext.js +0 -21
  48. package/dist/LiveStoreContext.js.map +0 -1
  49. package/dist/LiveStoreProvider.d.ts +0 -73
  50. package/dist/LiveStoreProvider.d.ts.map +0 -1
  51. package/dist/LiveStoreProvider.js +0 -233
  52. package/dist/LiveStoreProvider.js.map +0 -1
  53. package/dist/LiveStoreProvider.test.d.ts +0 -2
  54. package/dist/LiveStoreProvider.test.d.ts.map +0 -1
  55. package/dist/LiveStoreProvider.test.js +0 -117
  56. package/dist/LiveStoreProvider.test.js.map +0 -1
  57. package/dist/experimental/multi-store/StoreRegistry.d.ts +0 -105
  58. package/dist/experimental/multi-store/StoreRegistry.d.ts.map +0 -1
  59. package/dist/experimental/multi-store/StoreRegistry.js +0 -184
  60. package/dist/experimental/multi-store/StoreRegistry.js.map +0 -1
  61. package/dist/experimental/multi-store/StoreRegistry.test.d.ts +0 -2
  62. package/dist/experimental/multi-store/StoreRegistry.test.d.ts.map +0 -1
  63. package/dist/experimental/multi-store/StoreRegistry.test.js +0 -381
  64. package/dist/experimental/multi-store/StoreRegistry.test.js.map +0 -1
  65. package/dist/experimental/multi-store/StoreRegistryContext.d.ts +0 -10
  66. package/dist/experimental/multi-store/StoreRegistryContext.d.ts.map +0 -1
  67. package/dist/experimental/multi-store/StoreRegistryContext.js +0 -15
  68. package/dist/experimental/multi-store/StoreRegistryContext.js.map +0 -1
  69. package/dist/experimental/multi-store/mod.d.ts +0 -6
  70. package/dist/experimental/multi-store/mod.d.ts.map +0 -1
  71. package/dist/experimental/multi-store/mod.js +0 -6
  72. package/dist/experimental/multi-store/mod.js.map +0 -1
  73. package/dist/experimental/multi-store/storeOptions.d.ts +0 -4
  74. package/dist/experimental/multi-store/storeOptions.d.ts.map +0 -1
  75. package/dist/experimental/multi-store/storeOptions.js +0 -4
  76. package/dist/experimental/multi-store/storeOptions.js.map +0 -1
  77. package/dist/experimental/multi-store/types.d.ts +0 -25
  78. package/dist/experimental/multi-store/types.d.ts.map +0 -1
  79. package/dist/experimental/multi-store/types.js +0 -2
  80. package/dist/experimental/multi-store/types.js.map +0 -1
  81. package/dist/experimental/multi-store/useStore.d.ts +0 -11
  82. package/dist/experimental/multi-store/useStore.d.ts.map +0 -1
  83. package/dist/experimental/multi-store/useStore.js +0 -16
  84. package/dist/experimental/multi-store/useStore.js.map +0 -1
  85. package/dist/experimental/multi-store/useStore.test.d.ts.map +0 -1
  86. package/dist/experimental/multi-store/useStore.test.js.map +0 -1
  87. package/src/LiveStoreContext.ts +0 -41
  88. package/src/LiveStoreProvider.test.tsx +0 -248
  89. package/src/LiveStoreProvider.tsx +0 -430
  90. package/src/experimental/multi-store/StoreRegistry.test.ts +0 -518
  91. package/src/experimental/multi-store/StoreRegistry.ts +0 -253
  92. package/src/experimental/multi-store/StoreRegistryContext.tsx +0 -23
  93. package/src/experimental/multi-store/mod.ts +0 -5
  94. package/src/experimental/multi-store/storeOptions.ts +0 -8
  95. package/src/experimental/multi-store/types.ts +0 -37
  96. package/src/experimental/multi-store/useStore.ts +0 -26
  97. /package/dist/{experimental/multi-store/useStore.test.d.ts → useStore.test.d.ts} +0 -0
@@ -1,518 +0,0 @@
1
- import { makeInMemoryAdapter } from '@livestore/adapter-web'
2
- import { UnknownError } from '@livestore/common'
3
- import { StoreInternalsSymbol } from '@livestore/livestore'
4
- import { sleep } from '@livestore/utils'
5
- import { Effect } from '@livestore/utils/effect'
6
- import { describe, expect, it } from 'vitest'
7
- import { schema } from '../../__tests__/fixture.tsx'
8
- import { StoreRegistry } from './StoreRegistry.ts'
9
- import { storeOptions } from './storeOptions.ts'
10
- import type { CachedStoreOptions } from './types.ts'
11
-
12
- describe('StoreRegistry', () => {
13
- it('returns a promise when the store is loading', async () => {
14
- const registry = new StoreRegistry()
15
- const result = registry.getOrLoadPromise(testStoreOptions())
16
-
17
- expect(result).toBeInstanceOf(Promise)
18
-
19
- // Clean up
20
- const store = await result
21
- await store.shutdownPromise()
22
- })
23
-
24
- it('returns cached store synchronously after first load resolves', async () => {
25
- const registry = new StoreRegistry()
26
-
27
- const initial = registry.getOrLoadPromise(testStoreOptions())
28
- expect(initial).toBeInstanceOf(Promise)
29
-
30
- const store = await initial
31
-
32
- const cached = registry.getOrLoadPromise(testStoreOptions())
33
- expect(cached).toBe(store)
34
- expect(cached).not.toBeInstanceOf(Promise)
35
-
36
- // Clean up
37
- await store.shutdownPromise()
38
- })
39
-
40
- it('reuses the same promise for concurrent getOrLoadPromise calls while loading', async () => {
41
- const registry = new StoreRegistry()
42
- const options = testStoreOptions()
43
-
44
- const first = registry.getOrLoadPromise(options)
45
- const second = registry.getOrLoadPromise(options)
46
-
47
- // Both should be the same promise
48
- expect(first).toBe(second)
49
- expect(first).toBeInstanceOf(Promise)
50
-
51
- const store = await first
52
-
53
- // Both promises should resolve to the same store
54
- expect(await second).toBe(store)
55
-
56
- // Clean up
57
- await store.shutdownPromise()
58
- })
59
-
60
- it('throws synchronously and rethrows on subsequent calls for sync failures', () => {
61
- const registry = new StoreRegistry()
62
-
63
- const badOptions = testStoreOptions({
64
- // @ts-expect-error - intentionally passing invalid adapter to trigger error
65
- adapter: null,
66
- })
67
-
68
- // First call throws synchronously
69
- expect(() => registry.getOrLoadPromise(badOptions)).toThrow()
70
-
71
- // Subsequent call should also throw synchronously (cached error)
72
- expect(() => registry.getOrLoadPromise(badOptions)).toThrow()
73
- })
74
-
75
- it('caches and rethrows rejection on subsequent calls for async failures', async () => {
76
- const registry = new StoreRegistry()
77
-
78
- // Create an adapter that fails asynchronously (after yielding to the event loop)
79
- const failingAdapter = () =>
80
- Effect.gen(function* () {
81
- yield* Effect.sleep(0) // Force async execution
82
- return yield* UnknownError.make({ cause: new Error('Async failure') })
83
- })
84
- const badOptions = testStoreOptions({
85
- adapter: failingAdapter,
86
- })
87
-
88
- // First call returns a promise that rejects
89
- await expect(registry.getOrLoadPromise(badOptions)).rejects.toThrow()
90
-
91
- // Subsequent call should throw the cached error synchronously (RcMap caches failures)
92
- expect(() => registry.getOrLoadPromise(badOptions)).toThrow()
93
- })
94
-
95
- it('throws the same error instance on multiple calls after failure', async () => {
96
- const registry = new StoreRegistry()
97
-
98
- // Create an adapter that fails asynchronously
99
- const failingAdapter = () =>
100
- Effect.gen(function* () {
101
- yield* Effect.sleep(0) // Force async execution
102
- return yield* UnknownError.make({ cause: new Error('Async failure') })
103
- })
104
-
105
- const badOptions = testStoreOptions({
106
- adapter: failingAdapter,
107
- })
108
-
109
- // Wait for the first failure
110
- await expect(registry.getOrLoadPromise(badOptions)).rejects.toThrow()
111
-
112
- // Capture the errors from subsequent calls
113
- let error1: unknown
114
- let error2: unknown
115
-
116
- try {
117
- registry.getOrLoadPromise(badOptions)
118
- } catch (err) {
119
- error1 = err
120
- }
121
-
122
- try {
123
- registry.getOrLoadPromise(badOptions)
124
- } catch (err) {
125
- error2 = err
126
- }
127
-
128
- // Both should be the exact same error instance (cached)
129
- expect(error1).toBeDefined()
130
- expect(error1).toBe(error2)
131
- })
132
-
133
- it('disposes store after unusedCacheTime expires', async () => {
134
- const unusedCacheTime = 25
135
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime } })
136
- const options = testStoreOptions()
137
-
138
- const store = await registry.getOrLoadPromise(options)
139
-
140
- // Store should be cached
141
- expect(registry.getOrLoadPromise(options)).toBe(store)
142
-
143
- // Wait for disposal
144
- await sleep(unusedCacheTime + 50)
145
-
146
- // After disposal, store should be removed
147
- // The store is removed from cache, so next getOrLoadStore creates a new one
148
- const nextStore = await registry.getOrLoadPromise(options)
149
-
150
- // Should be a different store instance
151
- expect(nextStore).not.toBe(store)
152
- expect(nextStore[StoreInternalsSymbol].clientSession.debugInstanceId).toBeDefined()
153
-
154
- // Clean up the second store (first one was disposed)
155
- await nextStore.shutdownPromise()
156
- })
157
-
158
- it('does not dispose when unusedCacheTime is Infinity', async () => {
159
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime: Number.POSITIVE_INFINITY } })
160
- const options = testStoreOptions()
161
-
162
- const store = await registry.getOrLoadPromise(options)
163
-
164
- // Store should be cached
165
- expect(registry.getOrLoadPromise(options)).toBe(store)
166
-
167
- // Wait a reasonable duration to verify no disposal
168
- await sleep(100)
169
-
170
- // Store should still be cached (not disposed)
171
- expect(registry.getOrLoadPromise(options)).toBe(store)
172
-
173
- // Clean up manually
174
- await store.shutdownPromise()
175
- })
176
-
177
- it('schedules disposal if store becomes unused during loading', async () => {
178
- const unusedCacheTime = 50
179
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime } })
180
- const options = testStoreOptions()
181
-
182
- // Start loading without any retain
183
- const storePromise = registry.getOrLoadPromise(options)
184
-
185
- // Wait for store to load (no retain registered)
186
- const store = await storePromise
187
-
188
- // Since there were no retain when loading completed, disposal should be scheduled
189
- await sleep(unusedCacheTime + 50)
190
-
191
- // Store should be disposed
192
- const nextStore = await registry.getOrLoadPromise(options)
193
- expect(nextStore).not.toBe(store)
194
-
195
- await nextStore.shutdownPromise()
196
- })
197
-
198
- // This test is skipped because Effect doesn't yet support different `idleTimeToLive` values for each resource in `RcMap`
199
- // See https://github.com/livestorejs/livestore/issues/917
200
- it.skip('allows call-site options to override default options', async () => {
201
- const registry = new StoreRegistry({
202
- defaultOptions: {
203
- unusedCacheTime: 1000, // Default is long
204
- },
205
- })
206
-
207
- const options = testStoreOptions({
208
- unusedCacheTime: 10, // Override with shorter time
209
- })
210
-
211
- const store = await registry.getOrLoadPromise(options)
212
-
213
- // Wait for the override time (10ms)
214
- await sleep(10)
215
-
216
- // Should be disposed according to the override time, not default
217
- const nextStore = await registry.getOrLoadPromise(options)
218
- expect(nextStore).not.toBe(store)
219
-
220
- await nextStore.shutdownPromise()
221
- })
222
-
223
- // This test is skipped because we don't yet support dynamic `unusedCacheTime` updates for cached stores.
224
- // See https://github.com/livestorejs/livestore/issues/918
225
- it.skip('keeps the longest unusedCacheTime seen for a store when options vary across calls', async () => {
226
- const registry = new StoreRegistry()
227
-
228
- const options = testStoreOptions({ unusedCacheTime: 10 })
229
- const release = registry.retain(options)
230
-
231
- const store = await registry.getOrLoadPromise(options)
232
-
233
- // Call with longer unusedCacheTime
234
- await registry.getOrLoadPromise(testStoreOptions({ unusedCacheTime: 100 }))
235
-
236
- release()
237
-
238
- // After 99ms, store should still be alive (100ms unusedCacheTime used)
239
- await sleep(99)
240
-
241
- // Store should still be cached
242
- expect(registry.getOrLoadPromise(options)).toBe(store)
243
-
244
- // After the full 100ms, store should be disposed
245
- await sleep(1)
246
-
247
- // Next getOrLoadStore should create a new store
248
- const nextStore = await registry.getOrLoadPromise(options)
249
- expect(nextStore).not.toBe(store)
250
-
251
- // Clean up the second store (first one was disposed)
252
- await nextStore.shutdownPromise()
253
- })
254
-
255
- it('preload does not throw', async () => {
256
- const registry = new StoreRegistry()
257
-
258
- // Create invalid options that would cause an error
259
- const badOptions = testStoreOptions({
260
- // @ts-expect-error - intentionally passing invalid adapter to trigger error
261
- adapter: null,
262
- })
263
-
264
- // preload should not throw
265
- await expect(registry.preload(badOptions)).resolves.toBeUndefined()
266
-
267
- // But subsequent getOrLoadStore should throw the cached error
268
- expect(() => registry.getOrLoadPromise(badOptions)).toThrow()
269
- })
270
-
271
- it('handles rapid retain/release cycles without errors', async () => {
272
- const unusedCacheTime = 50
273
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime } })
274
- const options = testStoreOptions()
275
-
276
- const store = await registry.getOrLoadPromise(options)
277
-
278
- // Rapidly retain and release multiple times
279
- for (let i = 0; i < 10; i++) {
280
- const release = registry.retain(options)
281
- release()
282
- }
283
-
284
- // Wait for disposal to trigger
285
- await sleep(unusedCacheTime + 50)
286
-
287
- // Store should be disposed after the last release
288
- const nextStore = await registry.getOrLoadPromise(options)
289
- expect(nextStore).not.toBe(store)
290
-
291
- await nextStore.shutdownPromise()
292
- })
293
-
294
- it('cancels disposal when new retain', async () => {
295
- const unusedCacheTime = 50
296
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime } })
297
- const options = testStoreOptions()
298
-
299
- const store = await registry.getOrLoadPromise(options)
300
-
301
- // Wait almost to disposal threshold
302
- await sleep(unusedCacheTime - 5)
303
-
304
- // Add a new retain before disposal triggers
305
- const release = registry.retain(options)
306
-
307
- // Complete the original unusedCacheTime
308
- await sleep(5)
309
-
310
- // Store should not have been disposed because we added a retain
311
- expect(registry.getOrLoadPromise(options)).toBe(store)
312
-
313
- // Clean up
314
- release()
315
- await sleep(unusedCacheTime + 50)
316
-
317
- // Now it should be disposed
318
- const nextStore = await registry.getOrLoadPromise(options)
319
- expect(nextStore).not.toBe(store)
320
-
321
- await nextStore.shutdownPromise()
322
- })
323
-
324
- it('aborts loading when disposal fires while store is still loading', async () => {
325
- const unusedCacheTime = 10
326
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime } })
327
- const options = testStoreOptions()
328
-
329
- // Retain briefly to trigger getOrLoadStore and then release
330
- const release = registry.retain(options)
331
-
332
- // Start loading
333
- const loadPromise = registry.getOrLoadPromise(options)
334
-
335
- // Attach a catch handler to prevent unhandled rejection when the load is aborted
336
- const abortedPromise = (loadPromise as Promise<unknown>).catch(() => {
337
- // Expected: load was aborted by disposal
338
- })
339
-
340
- // Release immediately, which schedules disposal
341
- release()
342
-
343
- // Wait for disposal to trigger
344
- await sleep(unusedCacheTime + 50)
345
-
346
- // Wait for the abort to complete
347
- await abortedPromise
348
-
349
- // After abort, a new getOrLoadStore should start a fresh load
350
- const freshLoadPromise = registry.getOrLoadPromise(options)
351
-
352
- // This should be a new promise (not the aborted one)
353
- expect(freshLoadPromise).toBeInstanceOf(Promise)
354
- expect(freshLoadPromise).not.toBe(loadPromise)
355
-
356
- // Wait for fresh load to complete
357
- const store = await freshLoadPromise
358
- expect(store).toBeDefined()
359
-
360
- await store.shutdownPromise()
361
- })
362
-
363
- it('retain keeps store alive past unusedCacheTime', async () => {
364
- const unusedCacheTime = 50
365
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime } })
366
- const options = testStoreOptions()
367
-
368
- // Load the store
369
- const store = await registry.getOrLoadPromise(options)
370
-
371
- // Retain the store before disposal could fire
372
- const release = registry.retain(options)
373
-
374
- // Wait past the unusedCacheTime
375
- await sleep(unusedCacheTime + 50)
376
-
377
- // Store should still be cached because retain keeps it alive
378
- const cachedStore = registry.getOrLoadPromise(options)
379
- expect(cachedStore).toBe(store)
380
-
381
- release()
382
- await store.shutdownPromise()
383
- })
384
-
385
- it('manages multiple stores with different IDs independently', async () => {
386
- const unusedCacheTime = 50
387
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime } })
388
-
389
- const options1 = testStoreOptions({ storeId: 'store-1' })
390
- const options2 = testStoreOptions({ storeId: 'store-2' })
391
-
392
- const store1 = await registry.getOrLoadPromise(options1)
393
- const store2 = await registry.getOrLoadPromise(options2)
394
-
395
- // Should be different store instances
396
- expect(store1).not.toBe(store2)
397
-
398
- // Both should be cached independently
399
- expect(registry.getOrLoadPromise(options1)).toBe(store1)
400
- expect(registry.getOrLoadPromise(options2)).toBe(store2)
401
-
402
- // Wait for both stores to be disposed
403
- await sleep(unusedCacheTime + 50)
404
-
405
- // Both stores should be disposed, so next getOrLoadStore creates new ones
406
- const newStore1 = await registry.getOrLoadPromise(options1)
407
- expect(newStore1).not.toBe(store1)
408
-
409
- const newStore2 = await registry.getOrLoadPromise(options2)
410
- expect(newStore2).not.toBe(store2)
411
-
412
- // Clean up
413
- await newStore1.shutdownPromise()
414
- await newStore2.shutdownPromise()
415
- })
416
-
417
- it('applies default options from constructor', async () => {
418
- const registry = new StoreRegistry({
419
- defaultOptions: {
420
- unusedCacheTime: 100,
421
- },
422
- })
423
-
424
- const options = testStoreOptions()
425
-
426
- const store = await registry.getOrLoadPromise(options)
427
-
428
- // Verify the store loads successfully
429
- expect(store).toBeDefined()
430
- expect(store[StoreInternalsSymbol].clientSession.debugInstanceId).toBeDefined()
431
-
432
- // Verify configured default unusedCacheTime is applied by checking disposal doesn't happen before it
433
- await sleep(50)
434
-
435
- // Store should still be cached after 50ms (default is 100ms)
436
- expect(registry.getOrLoadPromise(options)).toBe(store)
437
-
438
- await store.shutdownPromise()
439
- })
440
-
441
- it('prevents getOrLoadStore from returning a dying store', async () => {
442
- const unusedCacheTime = 25
443
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime } })
444
- const options = testStoreOptions()
445
-
446
- // Load the store and wait for it to be ready
447
- const originalStore = await registry.getOrLoadPromise(options)
448
-
449
- // Verify store is cached
450
- expect(registry.getOrLoadPromise(options)).toBe(originalStore)
451
-
452
- // Wait for disposal to trigger
453
- await sleep(unusedCacheTime + 50)
454
-
455
- // After disposal, the cache should be cleared
456
- // Calling getOrLoadStore should start a fresh load (return Promise)
457
- const storeOrPromise = registry.getOrLoadPromise(options)
458
-
459
- if (!(storeOrPromise instanceof Promise)) {
460
- expect.fail('getOrLoadStore returned dying store synchronously instead of starting fresh load')
461
- }
462
-
463
- const freshStore = await storeOrPromise
464
- // A fresh load was triggered because cache was cleared
465
- expect(freshStore).not.toBe(originalStore)
466
- await freshStore.shutdownPromise()
467
- })
468
-
469
- it('warms the cache so subsequent getOrLoadStore is synchronous after preload', async () => {
470
- const registry = new StoreRegistry()
471
- const options = testStoreOptions()
472
-
473
- // Preload the store
474
- await registry.preload(options)
475
-
476
- // Subsequent getOrLoadStore should return synchronously (not a Promise)
477
- const store = registry.getOrLoadPromise(options)
478
- expect(store).not.toBeInstanceOf(Promise)
479
-
480
- // TypeScript doesn't narrow the type, so we need to assert
481
- if (store instanceof Promise) {
482
- throw new Error('Expected store, got Promise')
483
- }
484
-
485
- // Clean up
486
- await store.shutdownPromise()
487
- })
488
-
489
- it('schedules disposal after preload if no retainers are added', async () => {
490
- const unusedCacheTime = 50
491
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime } })
492
- const options = testStoreOptions()
493
-
494
- // Preload without retaining
495
- await registry.preload(options)
496
-
497
- // Get the store
498
- const store = registry.getOrLoadPromise(options)
499
- expect(store).not.toBeInstanceOf(Promise)
500
-
501
- // Wait for disposal to trigger
502
- await sleep(unusedCacheTime + 50)
503
-
504
- // Store should be disposed since no retainers were added
505
- const nextStore = await registry.getOrLoadPromise(options)
506
- expect(nextStore).not.toBe(store)
507
-
508
- await nextStore.shutdownPromise()
509
- })
510
- })
511
-
512
- const testStoreOptions = (overrides: Partial<CachedStoreOptions<typeof schema>> = {}) =>
513
- storeOptions({
514
- storeId: 'test-store',
515
- schema,
516
- adapter: makeInMemoryAdapter(),
517
- ...overrides,
518
- })