muya 2.3.1 → 2.3.3
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/sqlite/create-sqlite.js +1 -1
- package/esm/sqlite/use-sqlite.js +1 -1
- package/package.json +1 -1
- package/src/sqlite/__tests__/use-sqlite.test.tsx +25 -13
- package/src/sqlite/create-sqlite.ts +7 -4
- package/src/sqlite/select-sql.ts +2 -2
- package/src/sqlite/table/table.types.ts +2 -7
- package/src/sqlite/use-sqlite.ts +8 -1
- package/types/sqlite/create-sqlite.d.ts +1 -0
- package/types/sqlite/select-sql.d.ts +2 -2
- package/types/sqlite/table/table.types.d.ts +2 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
import{STATE_SCHEDULER as
|
|
1
|
+
import{STATE_SCHEDULER as f}from"../create";import{getId as P}from"../utils/id";import{shallow as v}from"../utils/shallow";import{selectSql as O}from"./select-sql";import{createTable as R,DEFAULT_STEP_SIZE as M}from"./table/table";function K(g){const k=P();function m(e){return`state-${k}-search-${e}`}let h;async function o(){if(!h){const{backend:e,...n}=g,t=e instanceof Promise?await e:e;h=await R({backend:t,...n})}return h}const c=new Map,i=new Map,S=new Map;async function p(e,n){const t=S.get(e),{options:a={}}=n,{stepSize:s=M}=a;if(!t)return!1;const r=[];for(let l=0;l<s;l++){const u=await t.next();if(u.done){S.delete(e);break}n.keys.has(String(u.value.rowId))||(r.push(u.value.document),n.keys.add(String(u.value.rowId)))}return r.length===0||v(n.items,r)?!1:(n.items=[...n.items,...r],!0)}function b(e){const n=i.get(e);n&&n()}async function x(e){const n=await o(),t=c.get(e);if(!t)return;const{options:a}=t,s=n.search({...a,select:(r,{rowId:l})=>({document:r,rowId:l})});S.set(e,s),t.keys=new Set,t.items=[],await p(e,t)}async function y(e){await x(e),b(e)}function T(e){const{key:n,op:t}=e,a=new Set;for(const[s,{keys:r}]of c)switch(t){case"delete":case"update":{r.has(String(n))&&a.add(s);break}case"insert":{a.add(s);break}}return a}async function d(e){const n=new Set;for(const t of e){const a=T(t);for(const s of a)n.add(s)}for(const t of n){const a=m(t);f.schedule(a,{searchId:t})}}const w=new Set;function D(e,n){c.has(e)||(c.set(e,{items:[],options:n,keys:new Set}),n&&y(e));const t=c.get(e);return n&&(t.options=n),t}const I={clear(e){c.delete(e)},async set(e){const t=await(await o()).set(e);return await d([t]),t},async batchSet(e){const t=await(await o()).batchSet(e);return await d(t),t},async delete(e){const t=await(await o()).delete(e);return t&&await d([t]),t},async deleteBy(e){const t=await(await o()).deleteBy(e);return await d(t),t},async get(e,n){return(await o()).get(e,n)},async*search(e={}){const n=await o();for await(const t of n.search(e))yield t},async count(e){return await(await o()).count(e)},updateSearchOptions(e,n){const t=D(e,n);t.options=n;const a=m(e);f.schedule(a,{searchId:e})},subscribe(e,n){const t=m(e),a=f.add(t,{onScheduleDone(){y(e)}});return w.add(a),i.has(e)||i.set(e,n),()=>{i.delete(e),a()}},getSnapshot(e){return D(e).items},refresh:y,destroy(){for(const e of w)e();c.clear(),i.clear()},async next(e){const n=c.get(e);if(n){const t=await p(e,n);return t&&b(e),t}return!1},select(e){return O(I,e)}};return I}export{K as createSqliteState};
|
package/esm/sqlite/use-sqlite.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useCallback as r,useDebugValue as S,
|
|
1
|
+
import{useCallback as r,useDebugValue as S,useEffect as p,useId as a,useLayoutEffect as f,useMemo as D}from"react";import{isError as y,isPromise as x}from"../utils/is";import{useSyncExternalStoreWithSelector as b}from"use-sync-external-store/shim/with-selector";function O(e,s={},u=[]){const{select:c}=s,t=a();f(()=>{e.updateSearchOptions(t,{...s,select:void 0})},u),p(()=>()=>{e.clear(t)},[]);const l=r(o=>c?o.map(c):o,[c]),d=r(o=>e.subscribe(t,o),[e,t]),i=r(()=>e.getSnapshot(t),[e,t]),n=b(d,i,i,l);if(S(n),x(n)||y(n))throw n;const m=D(()=>({next:()=>e.next(t),reset:()=>e.refresh(t)}),[t,e]);return[n,m]}export{O as useSqliteValue};
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import { createSqliteState } from '../create-sqlite'
|
|
|
4
4
|
import { useSqliteValue } from '../use-sqlite'
|
|
5
5
|
import { waitFor } from '@testing-library/react'
|
|
6
6
|
import { bunMemoryBackend } from '../table/bun-backend'
|
|
7
|
-
import { Suspense, useState } from 'react'
|
|
7
|
+
import { StrictMode, Suspense, useState } from 'react'
|
|
8
8
|
import { DEFAULT_STEP_SIZE } from '../table/table'
|
|
9
9
|
|
|
10
10
|
const backend = bunMemoryBackend()
|
|
@@ -15,13 +15,17 @@ interface Person {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function Wrapper({ children }: Readonly<{ children: React.ReactNode }>) {
|
|
18
|
-
return
|
|
18
|
+
return (
|
|
19
|
+
<StrictMode>
|
|
20
|
+
<Suspense fallback={<div>Loading...</div>}>{children}</Suspense>
|
|
21
|
+
</StrictMode>
|
|
22
|
+
)
|
|
19
23
|
}
|
|
20
24
|
describe('use-sqlite-state', () => {
|
|
21
25
|
it('should get basic value states', async () => {
|
|
22
26
|
const sql = createSqliteState<Person>({ backend, tableName: 'State1', key: 'id' })
|
|
23
27
|
let reRenders = 0
|
|
24
|
-
const { result } = renderHook(
|
|
28
|
+
const { result, rerender } = renderHook(
|
|
25
29
|
() => {
|
|
26
30
|
reRenders++
|
|
27
31
|
const aha = useSqliteValue(sql, {}, [])
|
|
@@ -66,6 +70,14 @@ describe('use-sqlite-state', () => {
|
|
|
66
70
|
expect(result.current[0].length).toBe(2)
|
|
67
71
|
expect(reRenders).toBe(6)
|
|
68
72
|
})
|
|
73
|
+
|
|
74
|
+
act(() => {
|
|
75
|
+
rerender()
|
|
76
|
+
})
|
|
77
|
+
await waitFor(() => {
|
|
78
|
+
expect(reRenders).toBe(7)
|
|
79
|
+
expect(result.current[0].length).toBe(2)
|
|
80
|
+
})
|
|
69
81
|
})
|
|
70
82
|
|
|
71
83
|
it('should use where clause changed via state', async () => {
|
|
@@ -79,11 +91,11 @@ describe('use-sqlite-state', () => {
|
|
|
79
91
|
const { result } = renderHook(() => {
|
|
80
92
|
reRenders++
|
|
81
93
|
const [minAge, setMinAge] = useState(20)
|
|
82
|
-
return [useSqliteValue(sql, { where: { age: { gt: minAge } },
|
|
94
|
+
return [useSqliteValue(sql, { where: { age: { gt: minAge } }, sortBy: 'age' }, [minAge]), setMinAge] as const
|
|
83
95
|
})
|
|
84
96
|
|
|
85
97
|
await waitFor(() => {
|
|
86
|
-
expect(result.current[0][0].map((p) => p.name)).toEqual(['
|
|
98
|
+
expect(result.current[0][0].map((p) => p.name)).toEqual(['Bob', 'Alice', 'Carol'])
|
|
87
99
|
expect(reRenders).toBe(2)
|
|
88
100
|
})
|
|
89
101
|
|
|
@@ -132,17 +144,17 @@ describe('use-sqlite-state', () => {
|
|
|
132
144
|
{ id: '3', name: 'Carol', age: 40 },
|
|
133
145
|
])
|
|
134
146
|
const { result, rerender } = renderHook(
|
|
135
|
-
({ order, limit }) => useSqliteValue(sql, {
|
|
147
|
+
({ order, limit }) => useSqliteValue(sql, { sortBy: 'age', order, limit }, [order, limit]),
|
|
136
148
|
{ initialProps: { order: 'asc' as 'asc' | 'desc', limit: 2 } },
|
|
137
149
|
)
|
|
138
150
|
await waitFor(() => {
|
|
139
|
-
expect(result.current[0].map((p) => p.name)).toEqual(['
|
|
151
|
+
expect(result.current[0].map((p) => p.name)).toEqual(['Bob', 'Alice'])
|
|
140
152
|
})
|
|
141
153
|
act(() => {
|
|
142
154
|
rerender({ order: 'desc', limit: 2 })
|
|
143
155
|
})
|
|
144
156
|
await waitFor(() => {
|
|
145
|
-
expect(result.current[0].map((p) => p.name)).toEqual(['
|
|
157
|
+
expect(result.current[0].map((p) => p.name)).toEqual(['Carol', 'Alice'])
|
|
146
158
|
})
|
|
147
159
|
act(() => {
|
|
148
160
|
rerender({ order: 'desc', limit: 1 })
|
|
@@ -243,11 +255,11 @@ describe('use-sqlite-state', () => {
|
|
|
243
255
|
people.push({ id: index.toString(), name: `Person${index}`, age: 20 + (index % 50) })
|
|
244
256
|
}
|
|
245
257
|
await sql.batchSet(people)
|
|
246
|
-
const { result, rerender } = renderHook(({ order }) => useSqliteValue(sql, {
|
|
258
|
+
const { result, rerender } = renderHook(({ order }) => useSqliteValue(sql, { sortBy: 'age', order }, [order]), {
|
|
247
259
|
initialProps: { order: 'asc' as 'asc' | 'desc' },
|
|
248
260
|
})
|
|
249
261
|
await waitFor(() => {
|
|
250
|
-
expect(result.current[0][0].age).toBe(
|
|
262
|
+
expect(result.current[0][0].age).toBe(20)
|
|
251
263
|
})
|
|
252
264
|
act(() => {
|
|
253
265
|
rerender({ order: 'desc' })
|
|
@@ -268,14 +280,14 @@ describe('use-sqlite-state', () => {
|
|
|
268
280
|
useSqliteValue(
|
|
269
281
|
sql,
|
|
270
282
|
{
|
|
271
|
-
|
|
283
|
+
sortBy: 'age',
|
|
272
284
|
select: (d) => d.name,
|
|
273
285
|
},
|
|
274
286
|
[],
|
|
275
287
|
),
|
|
276
288
|
)
|
|
277
289
|
await waitFor(() => {
|
|
278
|
-
expect(result.current[0]).toEqual(['
|
|
290
|
+
expect(result.current[0]).toEqual(['Bob', 'Alice', 'Carol'])
|
|
279
291
|
})
|
|
280
292
|
})
|
|
281
293
|
it('should add 50 documents and then load with another hook', async () => {
|
|
@@ -286,7 +298,7 @@ describe('use-sqlite-state', () => {
|
|
|
286
298
|
return useSqliteValue(
|
|
287
299
|
sql,
|
|
288
300
|
{
|
|
289
|
-
|
|
301
|
+
sortBy: 'age',
|
|
290
302
|
order: 'desc',
|
|
291
303
|
},
|
|
292
304
|
[],
|
|
@@ -31,6 +31,7 @@ export interface SyncTable<Document extends DocType> {
|
|
|
31
31
|
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult[]>
|
|
32
32
|
readonly destroy: () => void
|
|
33
33
|
readonly next: (searchId: SearchId) => Promise<boolean>
|
|
34
|
+
readonly clear: (searchId: SearchId) => void
|
|
34
35
|
|
|
35
36
|
readonly select: <Params extends unknown[]>(
|
|
36
37
|
compute: (...args: Params) => SearchOptions<Document>,
|
|
@@ -221,6 +222,9 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
|
|
|
221
222
|
}
|
|
222
223
|
|
|
223
224
|
const state: SyncTable<Document> = {
|
|
225
|
+
clear(searchId: SearchId) {
|
|
226
|
+
cachedData.delete(searchId)
|
|
227
|
+
},
|
|
224
228
|
async set(document) {
|
|
225
229
|
const table = await getTable()
|
|
226
230
|
const changes = await table.set(document)
|
|
@@ -271,20 +275,19 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
|
|
|
271
275
|
|
|
272
276
|
subscribe(searchId, listener) {
|
|
273
277
|
const scheduleId = getScheduleId(searchId)
|
|
274
|
-
const
|
|
278
|
+
const clearScheduler = STATE_SCHEDULER.add(scheduleId, {
|
|
275
279
|
onScheduleDone() {
|
|
276
280
|
refresh(searchId)
|
|
277
281
|
},
|
|
278
282
|
})
|
|
279
|
-
clearSchedulers.add(
|
|
283
|
+
clearSchedulers.add(clearScheduler)
|
|
280
284
|
|
|
281
285
|
if (!listeners.has(searchId)) {
|
|
282
286
|
listeners.set(searchId, listener)
|
|
283
287
|
}
|
|
284
288
|
return () => {
|
|
285
289
|
listeners.delete(searchId)
|
|
286
|
-
|
|
287
|
-
cachedData.delete(searchId)
|
|
290
|
+
clearScheduler()
|
|
288
291
|
}
|
|
289
292
|
},
|
|
290
293
|
getSnapshot(searchId) {
|
package/src/sqlite/select-sql.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createState } from '../create-state'
|
|
2
2
|
import type { GetState } from '../types'
|
|
3
3
|
import type { SyncTable } from './create-sqlite'
|
|
4
|
-
import type { DocType } from './table/table.types'
|
|
4
|
+
import type { DocType, DotPath } from './table/table.types'
|
|
5
5
|
import type { Where } from './table/where'
|
|
6
6
|
|
|
7
7
|
export type CreateState<Document, Params extends unknown[]> = (...params: Params) => GetState<Document[]>
|
|
8
8
|
|
|
9
9
|
export interface SqlSeachOptions<Document extends DocType> {
|
|
10
|
-
readonly
|
|
10
|
+
readonly sortBy?: DotPath<Document>
|
|
11
11
|
readonly order?: 'asc' | 'desc'
|
|
12
12
|
readonly limit?: number
|
|
13
13
|
readonly offset?: number
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// table.types.ts
|
|
2
|
+
import type { SqlSeachOptions } from '../select-sql'
|
|
2
3
|
import type { Backend } from './backend'
|
|
3
4
|
import type { Where } from './where'
|
|
4
5
|
|
|
@@ -27,13 +28,7 @@ export interface DbOptions<Document extends DocType> {
|
|
|
27
28
|
readonly disablePragmaOptimization?: boolean
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
export interface SearchOptions<Document extends DocType, Selected = Document> {
|
|
31
|
-
readonly sortBy?: DotPath<Document>
|
|
32
|
-
readonly order?: 'asc' | 'desc'
|
|
33
|
-
readonly limit?: number
|
|
34
|
-
readonly offset?: number
|
|
35
|
-
readonly where?: Where<Document>
|
|
36
|
-
readonly stepSize?: number
|
|
31
|
+
export interface SearchOptions<Document extends DocType, Selected = Document> extends SqlSeachOptions<Document> {
|
|
37
32
|
readonly select?: (document: Document, meta: { rowId: number }) => Selected
|
|
38
33
|
}
|
|
39
34
|
|
package/src/sqlite/use-sqlite.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useDebugValue, useId, useLayoutEffect, useMemo, type DependencyList } from 'react'
|
|
1
|
+
import { useCallback, useDebugValue, useEffect, useId, useLayoutEffect, useMemo, type DependencyList } from 'react'
|
|
2
2
|
import type { SyncTable } from './create-sqlite'
|
|
3
3
|
import type { DocType } from './table/table.types'
|
|
4
4
|
import { isError, isPromise } from '../utils/is'
|
|
@@ -39,6 +39,13 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
|
|
|
39
39
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
40
40
|
}, deps)
|
|
41
41
|
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
return () => {
|
|
44
|
+
state.clear(id)
|
|
45
|
+
}
|
|
46
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
47
|
+
}, [])
|
|
48
|
+
|
|
42
49
|
const selector = useCallback(
|
|
43
50
|
(documents: Document[]) => {
|
|
44
51
|
// eslint-disable-next-line unicorn/no-array-callback-reference
|
|
@@ -22,6 +22,7 @@ export interface SyncTable<Document extends DocType> {
|
|
|
22
22
|
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult[]>;
|
|
23
23
|
readonly destroy: () => void;
|
|
24
24
|
readonly next: (searchId: SearchId) => Promise<boolean>;
|
|
25
|
+
readonly clear: (searchId: SearchId) => void;
|
|
25
26
|
readonly select: <Params extends unknown[]>(compute: (...args: Params) => SearchOptions<Document>) => CreateState<Document, Params>;
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { GetState } from '../types';
|
|
2
2
|
import type { SyncTable } from './create-sqlite';
|
|
3
|
-
import type { DocType } from './table/table.types';
|
|
3
|
+
import type { DocType, DotPath } from './table/table.types';
|
|
4
4
|
import type { Where } from './table/where';
|
|
5
5
|
export type CreateState<Document, Params extends unknown[]> = (...params: Params) => GetState<Document[]>;
|
|
6
6
|
export interface SqlSeachOptions<Document extends DocType> {
|
|
7
|
-
readonly
|
|
7
|
+
readonly sortBy?: DotPath<Document>;
|
|
8
8
|
readonly order?: 'asc' | 'desc';
|
|
9
9
|
readonly limit?: number;
|
|
10
10
|
readonly offset?: number;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SqlSeachOptions } from '../select-sql';
|
|
1
2
|
import type { Backend } from './backend';
|
|
2
3
|
import type { Where } from './where';
|
|
3
4
|
export type DocType = {
|
|
@@ -16,13 +17,7 @@ export interface DbOptions<Document extends DocType> {
|
|
|
16
17
|
readonly key?: DotPath<Document>;
|
|
17
18
|
readonly disablePragmaOptimization?: boolean;
|
|
18
19
|
}
|
|
19
|
-
export interface SearchOptions<Document extends DocType, Selected = Document> {
|
|
20
|
-
readonly sortBy?: DotPath<Document>;
|
|
21
|
-
readonly order?: 'asc' | 'desc';
|
|
22
|
-
readonly limit?: number;
|
|
23
|
-
readonly offset?: number;
|
|
24
|
-
readonly where?: Where<Document>;
|
|
25
|
-
readonly stepSize?: number;
|
|
20
|
+
export interface SearchOptions<Document extends DocType, Selected = Document> extends SqlSeachOptions<Document> {
|
|
26
21
|
readonly select?: (document: Document, meta: {
|
|
27
22
|
rowId: number;
|
|
28
23
|
}) => Selected;
|