muya 2.4.91 → 2.4.93
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.
- package/esm/select.js +1 -1
- package/esm/sqlite/use-sqlite.js +1 -1
- package/package.json +1 -1
- package/src/__tests__/select.test.tsx +19 -0
- package/src/select.ts +1 -1
- package/src/sqlite/__tests__/use-sqlite.more.test.tsx +206 -0
- package/src/sqlite/use-sqlite.ts +16 -11
- package/types/select.d.ts +1 -1
package/esm/select.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{STATE_SCHEDULER as p}from"./create";import{createState as k}from"./create-state";import{subscribeToDevelopmentTools as
|
|
1
|
+
import{STATE_SCHEDULER as p}from"./create";import{createState as k}from"./create-state";import{subscribeToDevelopmentTools as b}from"./debug/development-tools";import{AbortError as E,canUpdate as D,handleAsyncUpdate as s}from"./utils/common";import{isPromise as T,isUndefined as c}from"./utils/is";function U(i,d,h){function o(){let t=!1;const n=i.map(u=>{const r=u.get();return T(r)&&(t=!0),r});return t?new Promise((u,r)=>{Promise.all(n).then(m=>{if(m.some(A=>c(A)))return r(new E);const w=d(...m);u(w)})}):d(...n)}function S(){if(c(e.cache.current)){const t=o();e.cache.current=s(e,t)}return e.cache.current}function f(){if(c(e.cache.current)){const n=o();e.cache.current=s(e,n)}const{current:t}=e.cache;return T(t)?new Promise(n=>{t.then(a=>{if(c(a)){n(f());return}n(a)})}):e.cache.current}const l=[];for(const t of i){const n=t.emitter.subscribe(()=>{p.schedule(e.id,null)});l.push(n)}const e=k({destroy(){for(const t of l)t();y(),e.emitter.clear(),e.cache.current=void 0},get:f,getSnapshot:S}),y=p.add(e.id,{onScheduleDone(){const t=o();e.cache.current=s(e,t),D(e.cache,h)&&e.emitter.emit()}});return b(e),e}export{U as select};
|
package/esm/sqlite/use-sqlite.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useCallback as f,useLayoutEffect as P,useReducer as
|
|
1
|
+
import{useCallback as f,useLayoutEffect as P,useReducer as _,useRef as D}from"react";import{DEFAULT_PAGE_SIZE as C}from"./table";import{shallow as E}from"../utils/shallow";const T=1e4;function z(a,w={},I=[]){const{select:m,pageSize:q=C}=w,e=D(null),[,k]=_(c=>c+1,0),n=D(new Map),x=D(null),A=f(()=>{const{select:c,...i}=w;x.current=a.search({select:(u,l)=>({doc:u,meta:l}),...i})},[a,...I]),d=f(()=>{e.current=[],n.current.clear(),A()},[A]),y=f(async c=>{e.current===null&&(e.current=[]),c===!0&&d();const{current:i}=x;if(!i)return!0;let u=!1;for(let l=0;l<q;l++){const o=await i.next();if(o.done){x.current=null,u=!0;break}n.current.has(o.value.meta.key)||(e.current.push(m?m(o.value.doc):o.value.doc),n.current.set(o.value.meta.key,e.current.length-1))}return e.current=[...e.current],u},[]),p=f(async()=>{const c=await y(!1);return k(),c},[y]);P(()=>{const c=a.subscribe(async i=>{const{mutations:u,removedAll:l}=i;if(l&&d(),!u)return;const o=e.current?.length??0;let g=o,h=!1;const S=new Set;for(const s of u){const{key:r,op:b}=s;switch(b){case"insert":{g+=1;break}case"delete":{if(e.current&&e.current.length>0&&n.current.has(r)){const t=n.current.get(r);if(t===void 0)break;S.add(t),h=!0}break}case"update":{if(n.current.has(r)){const t=n.current.get(r);if(t!==void 0&&e.current){const v=await a.get(r,m),O=e.current[t];E(O,v)||(e.current[t]=v,e.current=[...e.current],h=!0)}}else{const t=await a.get(r,m);t&&(e.current=[...e.current??[],t],n.current.set(r,e.current.length-1),h=!0)}break}}}if(S.size>0&&e.current&&e.current.length>0){const s=new Map;e.current=e.current?.filter((b,t)=>!S.has(t));let r=0;for(const[b,t]of n.current)S.has(t)||(s.set(b,r),r++);n.current=s}const L=o!==g;if(L||h){if(L){await y(!0);let s=0;for(;(e.current?.length??0)<g&&s<T;)await y(!1),s++;s===T&&console.warn("Reached maximum iterations in fillNextPage loop. Possible duplicate or data issue.")}k()}});return()=>{c()}},[a]),P(()=>{d(),p()},I);const R=f(async()=>{d(),await p()},[p,d]);return[e.current,{nextPage:p,reset:R,keysIndex:n.current}]}export{z as useSqliteValue};
|
package/package.json
CHANGED
|
@@ -27,6 +27,25 @@ describe('select', () => {
|
|
|
27
27
|
expect(selectedState.get()).toBe(5)
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
+
it('should allow to select state from another selected state', async () => {
|
|
31
|
+
const state = create<Promise<number>>(new Promise((resolve) => setTimeout(() => resolve(1), 50)))
|
|
32
|
+
const selectedState = state.select((value) => value * 2)
|
|
33
|
+
const anotherState = create(1)
|
|
34
|
+
|
|
35
|
+
const anotherSelected = anotherState.select((value) => value * 2)
|
|
36
|
+
const selectedState2 = select([anotherSelected, selectedState], (a, b) => a + b)
|
|
37
|
+
const result = renderHook(
|
|
38
|
+
() => {
|
|
39
|
+
const value = selectedState2()
|
|
40
|
+
return value
|
|
41
|
+
},
|
|
42
|
+
{ wrapper: ({ children }) => <Suspense fallback="loading">{children}</Suspense> },
|
|
43
|
+
)
|
|
44
|
+
await waitFor(() => {
|
|
45
|
+
expect(result.result.current).toBe(4)
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
30
49
|
it('should notify listeners when derived state changes', async () => {
|
|
31
50
|
const state = create(1)
|
|
32
51
|
const selectedState = select([state], (value) => value * 2)
|
package/src/select.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { AbortError, canUpdate, handleAsyncUpdate } from './utils/common'
|
|
|
6
6
|
import { isPromise, isUndefined } from './utils/is'
|
|
7
7
|
|
|
8
8
|
type StateDependencies<T extends Array<unknown>> = {
|
|
9
|
-
[K in keyof T]: GetState<T[K]>
|
|
9
|
+
[K in keyof T]: GetState<T[K], boolean>
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
type AwaitedArray<T extends Array<unknown>> = {
|
|
@@ -4,6 +4,7 @@ import { useSqliteValue } from '../use-sqlite'
|
|
|
4
4
|
import { waitFor } from '@testing-library/react'
|
|
5
5
|
import { bunMemoryBackend } from '../table/bun-backend'
|
|
6
6
|
import { StrictMode, Suspense } from 'react'
|
|
7
|
+
import type { Key } from '../table'
|
|
7
8
|
|
|
8
9
|
const backend = bunMemoryBackend()
|
|
9
10
|
|
|
@@ -428,4 +429,209 @@ describe('use-sqlite edge cases', () => {
|
|
|
428
429
|
])
|
|
429
430
|
})
|
|
430
431
|
})
|
|
432
|
+
|
|
433
|
+
it('should handle rapid consecutive updates without losing state', async () => {
|
|
434
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'RaceState', key: 'id' })
|
|
435
|
+
|
|
436
|
+
// Insert initial document
|
|
437
|
+
await sql.set({ id: '1', name: 'Alice', age: 30 })
|
|
438
|
+
|
|
439
|
+
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
440
|
+
|
|
441
|
+
await waitFor(() => {
|
|
442
|
+
expect(result.current[0]).toEqual([{ id: '1', name: 'Alice', age: 30 }])
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
// Now simulate fast consecutive updates to the same key
|
|
446
|
+
// These will hit the subscription handler while it's still awaiting `state.get`
|
|
447
|
+
await Promise.all([
|
|
448
|
+
sql.set({ id: '1', name: 'AliceV2', age: 31 }),
|
|
449
|
+
sql.set({ id: '1', name: 'AliceV3', age: 32 }),
|
|
450
|
+
sql.set({ id: '1', name: 'AliceV4', age: 33 }),
|
|
451
|
+
])
|
|
452
|
+
|
|
453
|
+
// Wait for hook to stabilize
|
|
454
|
+
await waitFor(() => {
|
|
455
|
+
// eslint-disable-next-line prefer-destructuring, unicorn/prevent-abbreviations
|
|
456
|
+
const docs = result.current[0]
|
|
457
|
+
expect(docs?.length).toBe(1)
|
|
458
|
+
// Expect the *latest* update to win
|
|
459
|
+
expect(docs?.[0]).toEqual({ id: '1', name: 'AliceV4', age: 33 })
|
|
460
|
+
})
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
it('should not overwrite newer updates with stale state.get results', async () => {
|
|
464
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'RaceFailState', key: 'id' })
|
|
465
|
+
await sql.set({ id: '1', name: 'Initial', age: 20 })
|
|
466
|
+
|
|
467
|
+
// Delay the first get call artificially to simulate slow DB
|
|
468
|
+
let callCount = 0
|
|
469
|
+
const originalGet = sql.get
|
|
470
|
+
// @ts-expect-error - We mocking the get method for testing
|
|
471
|
+
sql.get = async (key: Key, selector: ((document: Person) => Person) | undefined) => {
|
|
472
|
+
callCount++
|
|
473
|
+
if (callCount === 1) {
|
|
474
|
+
// Simulate slow resolution for first update
|
|
475
|
+
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
476
|
+
}
|
|
477
|
+
return originalGet(key, selector)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
481
|
+
|
|
482
|
+
await waitFor(() => {
|
|
483
|
+
expect(result.current[0]).toEqual([{ id: '1', name: 'Initial', age: 20 }])
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
// Trigger two consecutive updates
|
|
487
|
+
await sql.set({ id: '1', name: 'AliceV1', age: 21 })
|
|
488
|
+
await sql.set({ id: '1', name: 'AliceV2', age: 22 })
|
|
489
|
+
|
|
490
|
+
// Wait for hook to stabilize
|
|
491
|
+
await waitFor(() => {
|
|
492
|
+
// eslint-disable-next-line prefer-destructuring, unicorn/prevent-abbreviations
|
|
493
|
+
const docs = result.current[0]
|
|
494
|
+
expect(docs?.length).toBe(1)
|
|
495
|
+
// 🔥 Correct behavior: should end with the *latest* version (AliceV2)
|
|
496
|
+
// ❌ Buggy behavior: may still show AliceV1 because the delayed get resolves last
|
|
497
|
+
expect(docs?.[0]).toEqual({ id: '1', name: 'AliceV2', age: 22 })
|
|
498
|
+
})
|
|
499
|
+
})
|
|
500
|
+
it('should reset correctly during pagination', async () => {
|
|
501
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'ResetMid', key: 'id' })
|
|
502
|
+
const people = Array.from({ length: 200 }, (_, index) => ({ id: `${index + 1}`, name: `P${index + 1}`, age: index }))
|
|
503
|
+
await sql.batchSet(people)
|
|
504
|
+
|
|
505
|
+
const { result } = renderHook(() => useSqliteValue(sql, { pageSize: 50 }, []))
|
|
506
|
+
|
|
507
|
+
await waitFor(() => {
|
|
508
|
+
expect(result.current[0]?.length).toBe(50)
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
// Load next page
|
|
512
|
+
await act(async () => {
|
|
513
|
+
await result.current[1].nextPage()
|
|
514
|
+
})
|
|
515
|
+
expect(result.current[0]?.length).toBe(100)
|
|
516
|
+
|
|
517
|
+
// Reset
|
|
518
|
+
await act(async () => {
|
|
519
|
+
await result.current[1].reset()
|
|
520
|
+
})
|
|
521
|
+
await waitFor(() => {
|
|
522
|
+
// Should go back to first page only
|
|
523
|
+
expect(result.current[0]?.length).toBe(50)
|
|
524
|
+
expect(result.current[0]?.[0]?.id).toBe('1')
|
|
525
|
+
})
|
|
526
|
+
})
|
|
527
|
+
it('should overwrite duplicate keys instead of duplicating items', async () => {
|
|
528
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'DupTest', key: 'id' })
|
|
529
|
+
await sql.set({ id: '1', name: 'Alice', age: 30 })
|
|
530
|
+
await sql.set({ id: '1', name: 'Alice2', age: 35 }) // overwrite
|
|
531
|
+
|
|
532
|
+
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
533
|
+
|
|
534
|
+
await waitFor(() => {
|
|
535
|
+
expect(result.current[0]).toEqual([{ id: '1', name: 'Alice2', age: 35 }])
|
|
536
|
+
})
|
|
537
|
+
})
|
|
538
|
+
it('should not update after unmount', async () => {
|
|
539
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'UnmountTest', key: 'id' })
|
|
540
|
+
const { unmount, result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
541
|
+
|
|
542
|
+
unmount()
|
|
543
|
+
await sql.set({ id: '1', name: 'ShouldNotAppear', age: 99 })
|
|
544
|
+
|
|
545
|
+
// wait briefly to give subscription a chance
|
|
546
|
+
await new Promise((r) => setTimeout(r, 20))
|
|
547
|
+
|
|
548
|
+
expect(result.current[0]).toBeNull() // no state change after unmount
|
|
549
|
+
})
|
|
550
|
+
it('should restart iterator when sortBy changes', async () => {
|
|
551
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'SortChange', key: 'id' })
|
|
552
|
+
await sql.batchSet([
|
|
553
|
+
{ id: '1', name: 'A', age: 40 },
|
|
554
|
+
{ id: '2', name: 'B', age: 20 },
|
|
555
|
+
])
|
|
556
|
+
|
|
557
|
+
const { result, rerender } = renderHook(({ sortBy }) => useSqliteValue(sql, { sortBy }, [sortBy]), {
|
|
558
|
+
initialProps: { sortBy: 'age' as keyof Person },
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
await waitFor(() => {
|
|
562
|
+
expect(result.current[0]?.[0]?.age).toBe(20)
|
|
563
|
+
})
|
|
564
|
+
|
|
565
|
+
act(() => {
|
|
566
|
+
rerender({ sortBy: 'name' })
|
|
567
|
+
})
|
|
568
|
+
|
|
569
|
+
await waitFor(() => {
|
|
570
|
+
expect(result.current[0]?.[0]?.name).toBe('A') // sorted by name asc
|
|
571
|
+
})
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
it('should return done when nextPage is called on empty table', async () => {
|
|
575
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'EmptyNext', key: 'id' })
|
|
576
|
+
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
577
|
+
|
|
578
|
+
// @ts-expect-error - Testing internal method
|
|
579
|
+
const isDone = await act(result.current[1].nextPage)
|
|
580
|
+
expect(isDone).toBe(true)
|
|
581
|
+
expect(result.current[0]).toEqual([])
|
|
582
|
+
})
|
|
583
|
+
|
|
584
|
+
it('should handle deletion of non-visible item gracefully', async () => {
|
|
585
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'DeleteNonVisible', key: 'id' })
|
|
586
|
+
const people = Array.from({ length: 200 }, (_, index) => ({ id: `${index + 1}`, name: `P${index + 1}`, age: index }))
|
|
587
|
+
await sql.batchSet(people)
|
|
588
|
+
let renderCount = 0
|
|
589
|
+
const { result } = renderHook(() => {
|
|
590
|
+
renderCount++
|
|
591
|
+
return useSqliteValue(sql, { pageSize: 50 }, [])
|
|
592
|
+
})
|
|
593
|
+
await waitFor(() => {
|
|
594
|
+
expect(renderCount).toBe(2) // initial + after load
|
|
595
|
+
expect(result.current[0]?.length).toBe(50)
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
// Delete item outside current page
|
|
599
|
+
await act(async () => {
|
|
600
|
+
await sql.delete('150')
|
|
601
|
+
})
|
|
602
|
+
|
|
603
|
+
await waitFor(() => {
|
|
604
|
+
expect(renderCount).toBe(2) // no re-render
|
|
605
|
+
expect(result.current[0]?.length).toBe(50) // unchanged page size
|
|
606
|
+
})
|
|
607
|
+
})
|
|
608
|
+
it('should not rerender when using select if the selected value does not change', async () => {
|
|
609
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'SelectNoReRender', key: 'id' })
|
|
610
|
+
await sql.set({ id: '1', name: 'Alice', age: 30 })
|
|
611
|
+
|
|
612
|
+
let renders = 0
|
|
613
|
+
const { result } = renderHook(() => {
|
|
614
|
+
renders++
|
|
615
|
+
// Only project `name`
|
|
616
|
+
return useSqliteValue(sql, { select: (p) => p.name }, [])
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
await waitFor(() => {
|
|
620
|
+
expect(result.current[0]).toEqual(['Alice'])
|
|
621
|
+
expect(renders).toBe(2) // initial + after first load
|
|
622
|
+
})
|
|
623
|
+
|
|
624
|
+
// Update age (not part of select projection)
|
|
625
|
+
await act(async () => {
|
|
626
|
+
await sql.set({ id: '1', name: 'Alice', age: 31 })
|
|
627
|
+
})
|
|
628
|
+
|
|
629
|
+
// Wait a bit to let subscription flush
|
|
630
|
+
await new Promise((r) => setTimeout(r, 20))
|
|
631
|
+
|
|
632
|
+
// ❌ Buggy: renders increments again, even though "Alice" didn't change
|
|
633
|
+
// ✅ Expected: still 2 renders
|
|
634
|
+
expect(result.current[0]).toEqual(['Alice'])
|
|
635
|
+
expect(renders).toBe(2)
|
|
636
|
+
})
|
|
431
637
|
})
|
package/src/sqlite/use-sqlite.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { useCallback, useLayoutEffect, useReducer, useRef, type DependencyList }
|
|
|
3
3
|
import type { SyncTable } from './create-sqlite'
|
|
4
4
|
import type { DocType, Key, SqlSeachOptions } from './table/table.types'
|
|
5
5
|
import { DEFAULT_PAGE_SIZE } from './table'
|
|
6
|
+
import { shallow } from '../utils/shallow'
|
|
6
7
|
const MAX_ITERATIONS = 10_000
|
|
7
8
|
|
|
8
9
|
export interface SqLiteActions {
|
|
@@ -54,7 +55,7 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
|
|
|
54
55
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
55
56
|
}, [state, ...deps])
|
|
56
57
|
|
|
57
|
-
const
|
|
58
|
+
const resetDataAndUpdateIterator = useCallback(() => {
|
|
58
59
|
itemsRef.current = []
|
|
59
60
|
keysIndex.current.clear()
|
|
60
61
|
updateIterator()
|
|
@@ -65,7 +66,7 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
|
|
|
65
66
|
itemsRef.current = []
|
|
66
67
|
}
|
|
67
68
|
if (shouldReset === true) {
|
|
68
|
-
|
|
69
|
+
resetDataAndUpdateIterator()
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
const { current: iterator } = iteratorRef
|
|
@@ -82,8 +83,6 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
|
|
|
82
83
|
break
|
|
83
84
|
}
|
|
84
85
|
if (keysIndex.current.has(result.value.meta.key)) {
|
|
85
|
-
// eslint-disable-next-line sonarjs/updated-loop-counter
|
|
86
|
-
index += -1
|
|
87
86
|
continue
|
|
88
87
|
}
|
|
89
88
|
itemsRef.current.push(select ? select(result.value.doc) : (result.value.doc as unknown as Selected))
|
|
@@ -104,7 +103,7 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
|
|
|
104
103
|
const unsubscribe = state.subscribe(async (item) => {
|
|
105
104
|
const { mutations, removedAll } = item
|
|
106
105
|
if (removedAll) {
|
|
107
|
-
|
|
106
|
+
resetDataAndUpdateIterator()
|
|
108
107
|
}
|
|
109
108
|
if (!mutations) {
|
|
110
109
|
return
|
|
@@ -134,9 +133,15 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
|
|
|
134
133
|
if (keysIndex.current.has(key)) {
|
|
135
134
|
const index = keysIndex.current.get(key)
|
|
136
135
|
if (index !== undefined && itemsRef.current) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
const newItem = (await state.get(key, select)) as Selected
|
|
137
|
+
const previousItem = itemsRef.current[index]
|
|
138
|
+
|
|
139
|
+
// 🆕 Only update & rerender if shallow comparison fails
|
|
140
|
+
if (!shallow(previousItem, newItem)) {
|
|
141
|
+
itemsRef.current[index] = newItem
|
|
142
|
+
itemsRef.current = [...itemsRef.current]
|
|
143
|
+
hasUpdate = true
|
|
144
|
+
}
|
|
140
145
|
}
|
|
141
146
|
} else {
|
|
142
147
|
// Handle updates to non-visible items
|
|
@@ -193,15 +198,15 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
|
|
|
193
198
|
}, [state])
|
|
194
199
|
|
|
195
200
|
useLayoutEffect(() => {
|
|
196
|
-
|
|
201
|
+
resetDataAndUpdateIterator()
|
|
197
202
|
nextPage()
|
|
198
203
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
199
204
|
}, deps)
|
|
200
205
|
|
|
201
206
|
const resetCb = useCallback(async () => {
|
|
202
|
-
|
|
207
|
+
resetDataAndUpdateIterator()
|
|
203
208
|
await nextPage()
|
|
204
|
-
}, [nextPage,
|
|
209
|
+
}, [nextPage, resetDataAndUpdateIterator])
|
|
205
210
|
|
|
206
211
|
return [itemsRef.current, { nextPage, reset: resetCb, keysIndex: keysIndex.current }] as [
|
|
207
212
|
(undefined extends Selected ? Document[] : Selected[]) | null,
|
package/types/select.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { GetState, IsEqual } from './types';
|
|
2
2
|
type StateDependencies<T extends Array<unknown>> = {
|
|
3
|
-
[K in keyof T]: GetState<T[K]>;
|
|
3
|
+
[K in keyof T]: GetState<T[K], boolean>;
|
|
4
4
|
};
|
|
5
5
|
type AwaitedArray<T extends Array<unknown>> = {
|
|
6
6
|
[K in keyof T]: Awaited<T[K]>;
|