muya 2.1.2 → 2.2.0
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/__tests__/create-sqlite.test.js +1 -0
- package/esm/sqlite/__tests__/use-sqlite.test.js +1 -0
- package/esm/sqlite/create-sqlite.js +1 -0
- package/esm/sqlite/index.js +1 -0
- package/esm/sqlite/select-sql.js +1 -0
- package/esm/sqlite/table/index.js +1 -0
- package/esm/sqlite/use-sqlite.js +1 -0
- package/package.json +1 -1
- package/src/sqlite/__tests__/{create-sqlite-state.test.ts → create-sqlite.test.ts} +6 -6
- package/src/sqlite/__tests__/{use-sqlite-state.test.ts → use-sqlite.test.ts} +70 -9
- package/src/sqlite/{create-sqlite-state.ts → create-sqlite.ts} +18 -5
- package/src/sqlite/index.ts +4 -0
- package/src/sqlite/select-sql.ts +55 -0
- package/src/sqlite/table/index.ts +5 -0
- package/src/sqlite/{use-sqlite-value.ts → use-sqlite.ts} +3 -9
- package/types/sqlite/{create-sqlite-state.d.ts → create-sqlite.d.ts} +3 -1
- package/types/sqlite/index.d.ts +4 -0
- package/types/sqlite/select-sql.d.ts +14 -0
- package/types/sqlite/table/index.d.ts +5 -0
- package/types/sqlite/{use-sqlite-value.d.ts → use-sqlite.d.ts} +3 -9
- package/esm/sqlite/__tests__/create-sqlite-state.test.js +0 -1
- package/esm/sqlite/__tests__/use-sqlite-state.test.js +0 -1
- package/esm/sqlite/create-sqlite-state.js +0 -1
- package/esm/sqlite/use-sqlite-value.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createSqliteState as o}from"../create-sqlite";import{bunMemoryBackend as s}from"../table/bun-backend";const n=s();describe("create-sqlite-state",()=>{it("should batchSet and update multiple documents",async()=>{const e=o({backend:n,tableName:"State2",key:"id"});await e.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25}]);const a=[];for await(const i of e.search())a.push(i);expect(a).toHaveLength(2),await e.batchSet([{id:"1",name:"Alice2",age:31},{id:"2",name:"Bob2",age:26}]);const t=[];for await(const i of e.search())t.push(i);expect(t).toEqual([{id:"1",name:"Alice2",age:31},{id:"2",name:"Bob2",age:26}])}),it("should deleteBy condition",async()=>{const e=o({backend:n,tableName:"State3",key:"id"});await e.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]);const a=await e.deleteBy({age:{gt:30}});expect(a.length).toBe(1);const t=[];for await(const i of e.search())t.push(i);expect(t.map(i=>i.id)).toEqual(["1","2"])}),it("should get by key and with selector",async()=>{const e=o({backend:n,tableName:"State4",key:"id"});await e.set({id:"1",name:"Alice",age:30});const a=await e.get("1");expect(a).toEqual({id:"1",name:"Alice",age:30});const t=await e.get("1",c=>c.name);expect(t).toBe("Alice");const i=await e.get("999");expect(i).toBeUndefined()}),it("should count documents with and without where",async()=>{const e=o({backend:n,tableName:"State5",key:"id"});await e.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]),expect(await e.count()).toBe(3),expect(await e.count({where:{age:{gt:30}}})).toBe(1)}),it("should support search with options",async()=>{const e=o({backend:n,tableName:"State6",key:"id"});await e.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]);const a=[];for await(const t of e.search({where:{age:{lt:35}}}))a.push(t);expect(a.map(t=>t.id)).toEqual(["1","2"])})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{act as s,renderHook as c}from"@testing-library/react-hooks";import{createSqliteState as i}from"../create-sqlite";import{useSqliteValue as l}from"../use-sqlite";import{waitFor as o}from"@testing-library/react";import{bunMemoryBackend as g}from"../table/bun-backend";import{useState as x}from"react";import{DEFAULT_STEP_SIZE as m}from"../table/table";const u=g();describe("use-sqlite-state",()=>{it("should get basic value states",async()=>{const a=i({backend:u,tableName:"State1",key:"id"});let t=0;const{result:n}=c(()=>(t++,l(a,{},[])));expect(t).toBe(1),s(()=>{a.set({id:"1",name:"Alice",age:30})}),await o(()=>{expect(n.current[0]).toEqual([{id:"1",name:"Alice",age:30}]),expect(t).toBe(3)}),s(()=>{a.set({id:"1",name:"Alice2",age:30})}),await o(()=>{expect(n.current[0]).toEqual([{id:"1",name:"Alice2",age:30}]),expect(t).toBe(4)}),s(()=>{a.delete("1")}),await o(()=>{expect(n.current[0]).toEqual([]),expect(t).toBe(5)}),s(()=>{a.set({id:"1",name:"Alice",age:30}),a.set({id:"2",name:"Bob",age:25})}),await o(()=>{expect(n.current[0].length).toBe(2),expect(t).toBe(6)})}),it("should use where clause changed via state",async()=>{const a=i({backend:u,tableName:"State2",key:"id"});await a.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]);let t=0;const{result:n}=c(()=>{t++;const[r,e]=x(20);return[l(a,{where:{age:{gt:r}},sorBy:"age"},[r]),e]});await o(()=>{expect(n.current[0][0].map(r=>r.name)).toEqual(["Bob","Alice","Carol"]),expect(t).toBe(2)}),s(()=>{n.current[1](29)}),await o(()=>{expect(n.current[0][0].map(r=>r.name)).toEqual(["Alice","Carol"]),expect(t).toBe(4)})}),it("should support like in where clause and update results",async()=>{const a=i({backend:u,tableName:"State3",key:"id"});await a.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Alicia",age:25},{id:"3",name:"Bob",age:40}]);let t=0;const{result:n,rerender:r}=c(({like:e})=>(t++,l(a,{where:{name:{like:e}}},[e])),{initialProps:{like:"%Ali%"}});await o(()=>{expect(n.current[0].map(e=>e.name)).toEqual(["Alice","Alicia"])}),s(()=>{r({like:"%Bob%"})}),await o(()=>{expect(n.current[0].map(e=>e.name)).toEqual(["Bob"])}),expect(t).toBeGreaterThanOrEqual(2)}),it("should update results when changing order and limit options",async()=>{const a=i({backend:u,tableName:"State4",key:"id"});await a.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]);const{result:t,rerender:n}=c(({order:r,limit:e})=>l(a,{sorBy:"age",order:r,limit:e},[r,e]),{initialProps:{order:"asc",limit:2}});await o(()=>{expect(t.current[0].map(r=>r.name)).toEqual(["Bob","Alice"])}),s(()=>{n({order:"desc",limit:2})}),await o(()=>{expect(t.current[0].map(r=>r.name)).toEqual(["Carol","Alice"])}),s(()=>{n({order:"desc",limit:1})}),await o(()=>{expect(t.current[0].map(r=>r.name)).toEqual(["Carol"])})}),it("should support actions.next and actions.refresh",async()=>{const a=i({backend:u,tableName:"State5",key:"id"});await a.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25}]);const{result:t}=c(()=>l(a,{},[]));await o(()=>{expect(typeof t.current[1].next).toBe("function"),expect(typeof t.current[1].reset).toBe("function"),expect(t.current[1].reset()).resolves.toBeUndefined(),expect(t.current[1].next()).resolves.toBeFalsy()})}),it("should handle thousands of records",async()=>{const a=i({backend:u,tableName:"State6",key:"id"}),t=[],n=1e3;for(let e=1;e<=n;e++)t.push({id:e.toString(),name:`Person${e}`,age:20+e%50});await a.batchSet(t);const{result:r}=c(()=>l(a,{},[]));await o(()=>{expect(r.current[0].length).toBe(m)});for(let e=0;e<n/m;e++)s(()=>{r.current[1].next()}),await o(()=>{expect(r.current[0].length).toBe(Math.min(m*(e+2),n))});s(()=>{r.current[1].reset()}),await o(()=>{expect(r.current[0].length).toBe(m)})}),it("should handle thousands of records with single update",async()=>{const a=i({backend:u,tableName:"State6",key:"id"}),t=[],n=1e4,r=5e3;for(let d=1;d<=n;d++)t.push({id:d.toString(),name:`Person${d}`,age:20+d%50});await a.batchSet(t);let e=0;const{result:p}=c(()=>(e++,l(a,{stepSize:r},[])));await o(()=>{expect(e).toBe(2),expect(p.current[0].length).toBe(r)}),s(()=>{for(let d=0;d<n/r;d++)p.current[1].next()}),await o(()=>{expect(e).toBe(4),expect(p.current[0].length).toBe(n)}),s(()=>{p.current[1].reset()}),await o(()=>{expect(e).toBe(5),expect(p.current[0].length).toBe(r)})}),it("should change ordering",async()=>{const a=i({backend:u,tableName:"State7",key:"id",indexes:["age"]}),t=[];for(let e=1;e<=100;e++)t.push({id:e.toString(),name:`Person${e}`,age:20+e%50});await a.batchSet(t);const{result:n,rerender:r}=c(({order:e})=>l(a,{sorBy:"age",order:e},[e]),{initialProps:{order:"asc"}});await o(()=>{expect(n.current[0][0].age).toBe(20)}),s(()=>{r({order:"desc"})}),await o(()=>{expect(n.current[0][0].age).toBe(69)})}),it("should support selector in options",async()=>{const a=i({backend:u,tableName:"State8",key:"id"});await a.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]);const{result:t}=c(()=>l(a,{sorBy:"age",select:n=>n.name},[]));await o(()=>{expect(t.current[0]).toEqual(["Bob","Alice","Carol"])})})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createScheduler as P}from"../scheduler";import{shallow as R}from"../utils/shallow";import{selectSql as M}from"./select-sql";import{createTable as O,DEFAULT_STEP_SIZE as v}from"./table/table";const f=P();let C=0;function E(){return C++}function N(g){const T=E();function l(e){return`state-${T}-search-${e}`}let m;async function o(){return m||(m=await O(g)),m}const c=new Map,i=new Map,h=new Map;async function p(e,n){const t=h.get(e),{options:a={}}=n,{stepSize:s=v}=a;if(!t)return!1;const r=[];for(let d=0;d<s;d++){const y=await t.next();if(y.done){h.delete(e);break}r.push(y.value.document),n.keys.add(String(y.value.rowId))}return r.length===0||R(n.items,r)?!1:(n.items=[...n.items,...r],!0)}function b(e){const n=i.get(e);n&&n()}async function k(e){const n=await o(),t=c.get(e);if(!t)return;const{options:a}=t,s=n.search({...a,select:(r,{rowId:d})=>({document:r,rowId:d})});h.set(e,s),t.keys=new Set,t.items=[],await p(e,t)}async function S(e){await k(e),b(e)}function x(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 u(e){const n=new Set;for(const t of e){const a=x(t);for(const s of a)n.add(s)}for(const t of n){const a=l(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&&S(e));const t=c.get(e);return n&&(t.options=n),t}const I={async set(e){const t=await(await o()).set(e);return await u([t]),t},async batchSet(e){const t=await(await o()).batchSet(e);return await u(t),t},async delete(e){const t=await(await o()).delete(e);return t&&await u([t]),t},async deleteBy(e){const t=await(await o()).deleteBy(e);return await u(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=l(e);f.schedule(a,{searchId:e})},subscribe(e,n){const t=l(e),a=f.add(t,{onScheduleDone(){S(e)}});return w.add(a),i.has(e)||i.set(e,n),()=>{i.delete(e),a(),c.delete(e)}},getSnapshot(e){return D(e).items},refresh:S,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 M(I,e)}};return I}export{N as createSqliteState};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./create-sqlite";export*from"./table";export*from"./select-sql";export*from"./use-sqlite";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createState as p}from"../create-state";let n=0;function i(){return n++,`${n.toString(36)}-sql`}function S(r,o){const{subscribe:a,updateSearchOptions:s}=r;return(...c)=>{const e=i(),m=a(e,()=>{t.emitter.emit()}),u=o(...c),t=p({destroy(){m(),t.emitter.clear(),t.cache.current=void 0},get(){return r.getSnapshot(e)},getSnapshot(){return r.getSnapshot(e)}});return s(e,u),t}}export{S as selectSql};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./backend";export*from"./table.types";export*from"./where";export*from"./table";export*from"./map-deque";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{useCallback as r,useDebugValue as S,useId as p,useLayoutEffect as a,useMemo as D}from"react";import{isError as f,isPromise as y}from"../utils/is";import{useSyncExternalStoreWithSelector as x}from"use-sync-external-store/shim/with-selector";function L(e,s={},u=[]){const{select:c}=s,t=p();a(()=>{e.updateSearchOptions(t,{...s,select:void 0})},u);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=x(d,i,i,l);if(S(n),y(n)||f(n))throw n;const m=D(()=>({next:()=>e.next(t),reset:()=>e.refresh(t)}),[t,e]);return[n,m]}export{L as useSqliteValue};
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createSqliteState } from '../create-sqlite
|
|
1
|
+
import { createSqliteState } from '../create-sqlite'
|
|
2
2
|
import { bunMemoryBackend } from '../table/bun-backend'
|
|
3
3
|
|
|
4
4
|
const backend = bunMemoryBackend()
|
|
@@ -10,7 +10,7 @@ interface Person {
|
|
|
10
10
|
|
|
11
11
|
describe('create-sqlite-state', () => {
|
|
12
12
|
it('should batchSet and update multiple documents', async () => {
|
|
13
|
-
const sql =
|
|
13
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State2', key: 'id' })
|
|
14
14
|
await sql.batchSet([
|
|
15
15
|
{ id: '1', name: 'Alice', age: 30 },
|
|
16
16
|
{ id: '2', name: 'Bob', age: 25 },
|
|
@@ -32,7 +32,7 @@ describe('create-sqlite-state', () => {
|
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
it('should deleteBy condition', async () => {
|
|
35
|
-
const sql =
|
|
35
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State3', key: 'id' })
|
|
36
36
|
await sql.batchSet([
|
|
37
37
|
{ id: '1', name: 'Alice', age: 30 },
|
|
38
38
|
{ id: '2', name: 'Bob', age: 25 },
|
|
@@ -46,7 +46,7 @@ describe('create-sqlite-state', () => {
|
|
|
46
46
|
})
|
|
47
47
|
|
|
48
48
|
it('should get by key and with selector', async () => {
|
|
49
|
-
const sql =
|
|
49
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State4', key: 'id' })
|
|
50
50
|
await sql.set({ id: '1', name: 'Alice', age: 30 })
|
|
51
51
|
const doc = await sql.get('1')
|
|
52
52
|
expect(doc).toEqual({ id: '1', name: 'Alice', age: 30 })
|
|
@@ -57,7 +57,7 @@ describe('create-sqlite-state', () => {
|
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
it('should count documents with and without where', async () => {
|
|
60
|
-
const sql =
|
|
60
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State5', key: 'id' })
|
|
61
61
|
await sql.batchSet([
|
|
62
62
|
{ id: '1', name: 'Alice', age: 30 },
|
|
63
63
|
{ id: '2', name: 'Bob', age: 25 },
|
|
@@ -68,7 +68,7 @@ describe('create-sqlite-state', () => {
|
|
|
68
68
|
})
|
|
69
69
|
|
|
70
70
|
it('should support search with options', async () => {
|
|
71
|
-
const sql =
|
|
71
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State6', key: 'id' })
|
|
72
72
|
await sql.batchSet([
|
|
73
73
|
{ id: '1', name: 'Alice', age: 30 },
|
|
74
74
|
{ id: '2', name: 'Bob', age: 25 },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { act, renderHook } from '@testing-library/react-hooks'
|
|
2
|
-
import { createSqliteState } from '../create-sqlite
|
|
3
|
-
import { useSqliteValue } from '../use-sqlite
|
|
2
|
+
import { createSqliteState } from '../create-sqlite'
|
|
3
|
+
import { useSqliteValue } from '../use-sqlite'
|
|
4
4
|
import { waitFor } from '@testing-library/react'
|
|
5
5
|
import { bunMemoryBackend } from '../table/bun-backend'
|
|
6
6
|
import { useState } from 'react'
|
|
@@ -15,7 +15,7 @@ interface Person {
|
|
|
15
15
|
|
|
16
16
|
describe('use-sqlite-state', () => {
|
|
17
17
|
it('should get basic value states', async () => {
|
|
18
|
-
const sql =
|
|
18
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State1', key: 'id' })
|
|
19
19
|
let reRenders = 0
|
|
20
20
|
const { result } = renderHook(() => {
|
|
21
21
|
reRenders++
|
|
@@ -62,7 +62,7 @@ describe('use-sqlite-state', () => {
|
|
|
62
62
|
})
|
|
63
63
|
|
|
64
64
|
it('should use where clause changed via state', async () => {
|
|
65
|
-
const sql =
|
|
65
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State2', key: 'id' })
|
|
66
66
|
await sql.batchSet([
|
|
67
67
|
{ id: '1', name: 'Alice', age: 30 },
|
|
68
68
|
{ id: '2', name: 'Bob', age: 25 },
|
|
@@ -91,7 +91,7 @@ describe('use-sqlite-state', () => {
|
|
|
91
91
|
})
|
|
92
92
|
|
|
93
93
|
it('should support like in where clause and update results', async () => {
|
|
94
|
-
const sql =
|
|
94
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State3', key: 'id' })
|
|
95
95
|
await sql.batchSet([
|
|
96
96
|
{ id: '1', name: 'Alice', age: 30 },
|
|
97
97
|
{ id: '2', name: 'Alicia', age: 25 },
|
|
@@ -118,7 +118,7 @@ describe('use-sqlite-state', () => {
|
|
|
118
118
|
})
|
|
119
119
|
|
|
120
120
|
it('should update results when changing order and limit options', async () => {
|
|
121
|
-
const sql =
|
|
121
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State4', key: 'id' })
|
|
122
122
|
await sql.batchSet([
|
|
123
123
|
{ id: '1', name: 'Alice', age: 30 },
|
|
124
124
|
{ id: '2', name: 'Bob', age: 25 },
|
|
@@ -146,7 +146,7 @@ describe('use-sqlite-state', () => {
|
|
|
146
146
|
})
|
|
147
147
|
|
|
148
148
|
it('should support actions.next and actions.refresh', async () => {
|
|
149
|
-
const sql =
|
|
149
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State5', key: 'id' })
|
|
150
150
|
await sql.batchSet([
|
|
151
151
|
{ id: '1', name: 'Alice', age: 30 },
|
|
152
152
|
{ id: '2', name: 'Bob', age: 25 },
|
|
@@ -161,7 +161,7 @@ describe('use-sqlite-state', () => {
|
|
|
161
161
|
})
|
|
162
162
|
})
|
|
163
163
|
it('should handle thousands of records', async () => {
|
|
164
|
-
const sql =
|
|
164
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State6', key: 'id' })
|
|
165
165
|
const people: Person[] = []
|
|
166
166
|
const ITEMS_COUNT = 1000
|
|
167
167
|
for (let index = 1; index <= ITEMS_COUNT; index++) {
|
|
@@ -190,8 +190,47 @@ describe('use-sqlite-state', () => {
|
|
|
190
190
|
expect(result.current[0].length).toBe(DEFAULT_STEP_SIZE)
|
|
191
191
|
})
|
|
192
192
|
})
|
|
193
|
+
|
|
194
|
+
it('should handle thousands of records with single update', async () => {
|
|
195
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State6', key: 'id' })
|
|
196
|
+
const people: Person[] = []
|
|
197
|
+
const ITEMS_COUNT = 10_000
|
|
198
|
+
const stepSize = 5000
|
|
199
|
+
for (let index = 1; index <= ITEMS_COUNT; index++) {
|
|
200
|
+
people.push({ id: index.toString(), name: `Person${index}`, age: 20 + (index % 50) })
|
|
201
|
+
}
|
|
202
|
+
await sql.batchSet(people)
|
|
203
|
+
let reRenders = 0
|
|
204
|
+
const { result } = renderHook(() => {
|
|
205
|
+
reRenders++
|
|
206
|
+
return useSqliteValue(sql, { stepSize }, [])
|
|
207
|
+
})
|
|
208
|
+
await waitFor(() => {
|
|
209
|
+
expect(reRenders).toBe(2)
|
|
210
|
+
expect(result.current[0].length).toBe(stepSize)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
act(() => {
|
|
214
|
+
for (let index = 0; index < ITEMS_COUNT / stepSize; index++) {
|
|
215
|
+
result.current[1].next()
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
await waitFor(() => {
|
|
220
|
+
expect(reRenders).toBe(4)
|
|
221
|
+
expect(result.current[0].length).toBe(ITEMS_COUNT)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
act(() => {
|
|
225
|
+
result.current[1].reset()
|
|
226
|
+
})
|
|
227
|
+
await waitFor(() => {
|
|
228
|
+
expect(reRenders).toBe(5)
|
|
229
|
+
expect(result.current[0].length).toBe(stepSize)
|
|
230
|
+
})
|
|
231
|
+
})
|
|
193
232
|
it('should change ordering', async () => {
|
|
194
|
-
const sql =
|
|
233
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State7', key: 'id', indexes: ['age'] })
|
|
195
234
|
const people: Person[] = []
|
|
196
235
|
for (let index = 1; index <= 100; index++) {
|
|
197
236
|
people.push({ id: index.toString(), name: `Person${index}`, age: 20 + (index % 50) })
|
|
@@ -210,4 +249,26 @@ describe('use-sqlite-state', () => {
|
|
|
210
249
|
expect(result.current[0][0].age).toBe(69)
|
|
211
250
|
})
|
|
212
251
|
})
|
|
252
|
+
|
|
253
|
+
it('should support selector in options', async () => {
|
|
254
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State8', key: 'id' })
|
|
255
|
+
await sql.batchSet([
|
|
256
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
257
|
+
{ id: '2', name: 'Bob', age: 25 },
|
|
258
|
+
{ id: '3', name: 'Carol', age: 40 },
|
|
259
|
+
])
|
|
260
|
+
const { result } = renderHook(() =>
|
|
261
|
+
useSqliteValue(
|
|
262
|
+
sql,
|
|
263
|
+
{
|
|
264
|
+
sorBy: 'age',
|
|
265
|
+
select: (d) => d.name,
|
|
266
|
+
},
|
|
267
|
+
[],
|
|
268
|
+
),
|
|
269
|
+
)
|
|
270
|
+
await waitFor(() => {
|
|
271
|
+
expect(result.current[0]).toEqual(['Bob', 'Alice', 'Carol'])
|
|
272
|
+
})
|
|
273
|
+
})
|
|
213
274
|
})
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/* eslint-disable no-shadow */
|
|
4
4
|
import { createScheduler } from '../scheduler'
|
|
5
5
|
import { shallow } from '../utils/shallow'
|
|
6
|
+
import { selectSql, type CreateState } from './select-sql'
|
|
6
7
|
import { createTable, DEFAULT_STEP_SIZE } from './table/table'
|
|
7
8
|
import type { DbOptions, DocType, Key, MutationResult, SearchOptions, Table } from './table/table.types'
|
|
8
9
|
import type { Where } from './table/where'
|
|
@@ -14,6 +15,7 @@ let stateId = 0
|
|
|
14
15
|
function getStateId() {
|
|
15
16
|
return stateId++
|
|
16
17
|
}
|
|
18
|
+
|
|
17
19
|
export interface SyncTable<Document extends DocType> {
|
|
18
20
|
// readonly registerSearch: <Selected = Document>(searchId: SearchId, options: SearchOptions<Document, Selected>) => () => void
|
|
19
21
|
readonly updateSearchOptions: <Selected = Document>(searchId: SearchId, options: SearchOptions<Document, Selected>) => void
|
|
@@ -31,6 +33,10 @@ export interface SyncTable<Document extends DocType> {
|
|
|
31
33
|
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult[]>
|
|
32
34
|
readonly destroy: () => void
|
|
33
35
|
readonly next: (searchId: SearchId) => Promise<boolean>
|
|
36
|
+
|
|
37
|
+
readonly select: <Params extends unknown[]>(
|
|
38
|
+
compute: (...args: Params) => SearchOptions<Document>,
|
|
39
|
+
) => CreateState<Document, Params>
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
interface DataItems<Document extends DocType> {
|
|
@@ -39,7 +45,7 @@ interface DataItems<Document extends DocType> {
|
|
|
39
45
|
options?: SearchOptions<Document, unknown>
|
|
40
46
|
}
|
|
41
47
|
|
|
42
|
-
export
|
|
48
|
+
export function createSqliteState<Document extends DocType>(options: DbOptions<Document>): SyncTable<Document> {
|
|
43
49
|
// const table = await createTable<Document>(options)
|
|
44
50
|
|
|
45
51
|
const id = getStateId()
|
|
@@ -66,7 +72,7 @@ export async function createSqliteState<Document extends DocType>(options: DbOpt
|
|
|
66
72
|
|
|
67
73
|
async function next(searchId: SearchId, data: DataItems<Document>): Promise<boolean> {
|
|
68
74
|
const iterator = iterators.get(searchId)
|
|
69
|
-
const {
|
|
75
|
+
const { options = {} } = data
|
|
70
76
|
const { stepSize = DEFAULT_STEP_SIZE } = options
|
|
71
77
|
if (!iterator) return false
|
|
72
78
|
const newItems: Document[] = []
|
|
@@ -81,9 +87,10 @@ export async function createSqliteState<Document extends DocType>(options: DbOpt
|
|
|
81
87
|
data.keys.add(String(result.value.rowId))
|
|
82
88
|
}
|
|
83
89
|
|
|
90
|
+
if (newItems.length === 0) return false
|
|
84
91
|
if (shallow(data.items, newItems)) return false
|
|
85
|
-
data.items = [...items, ...newItems]
|
|
86
|
-
return
|
|
92
|
+
data.items = [...data.items, ...newItems]
|
|
93
|
+
return true
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
function notifyListeners(searchId: SearchId) {
|
|
@@ -164,7 +171,7 @@ export async function createSqliteState<Document extends DocType>(options: DbOpt
|
|
|
164
171
|
return data
|
|
165
172
|
}
|
|
166
173
|
|
|
167
|
-
|
|
174
|
+
const state: SyncTable<Document> = {
|
|
168
175
|
async set(document) {
|
|
169
176
|
const table = await getTable()
|
|
170
177
|
const changes = await table.set(document)
|
|
@@ -252,5 +259,11 @@ export async function createSqliteState<Document extends DocType>(options: DbOpt
|
|
|
252
259
|
}
|
|
253
260
|
return false
|
|
254
261
|
},
|
|
262
|
+
|
|
263
|
+
select(compute) {
|
|
264
|
+
return selectSql(state, compute)
|
|
265
|
+
},
|
|
255
266
|
}
|
|
267
|
+
|
|
268
|
+
return state
|
|
256
269
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createState } from '../create-state'
|
|
2
|
+
import type { GetState } from '../types'
|
|
3
|
+
import type { SyncTable } from './create-sqlite'
|
|
4
|
+
import type { DocType } from './table/table.types'
|
|
5
|
+
import type { Where } from './table/where'
|
|
6
|
+
|
|
7
|
+
export type CreateState<Document, Params extends unknown[]> = (...params: Params) => GetState<Document[]>
|
|
8
|
+
|
|
9
|
+
export interface SqlSeachOptions<Document extends DocType> {
|
|
10
|
+
readonly sorBy?: keyof Document
|
|
11
|
+
readonly order?: 'asc' | 'desc'
|
|
12
|
+
readonly limit?: number
|
|
13
|
+
readonly offset?: number
|
|
14
|
+
readonly where?: Where<Document>
|
|
15
|
+
readonly stepSize?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let stateId = 0
|
|
19
|
+
function getStateId() {
|
|
20
|
+
stateId++
|
|
21
|
+
return `${stateId.toString(36)}-sql`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function selectSql<Document extends DocType, Params extends unknown[] = []>(
|
|
25
|
+
state: SyncTable<Document>,
|
|
26
|
+
compute: (...args: Params) => SqlSeachOptions<Document>,
|
|
27
|
+
): CreateState<Document, Params> {
|
|
28
|
+
const { subscribe, updateSearchOptions } = state
|
|
29
|
+
|
|
30
|
+
const result: CreateState<Document, Params> = (...params) => {
|
|
31
|
+
const searchId = getStateId()
|
|
32
|
+
const destroy = subscribe(searchId, () => {
|
|
33
|
+
getState.emitter.emit()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const options = compute(...params)
|
|
37
|
+
const getState = createState<Document[]>({
|
|
38
|
+
destroy() {
|
|
39
|
+
destroy()
|
|
40
|
+
getState.emitter.clear()
|
|
41
|
+
getState.cache.current = undefined
|
|
42
|
+
},
|
|
43
|
+
get() {
|
|
44
|
+
return state.getSnapshot(searchId)
|
|
45
|
+
},
|
|
46
|
+
getSnapshot() {
|
|
47
|
+
return state.getSnapshot(searchId)
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
updateSearchOptions<Document>(searchId, options)
|
|
51
|
+
|
|
52
|
+
return getState
|
|
53
|
+
}
|
|
54
|
+
return result
|
|
55
|
+
}
|
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import { useCallback, useDebugValue, useId, useLayoutEffect, useMemo, type DependencyList } from 'react'
|
|
2
|
-
import type { SyncTable } from './create-sqlite
|
|
2
|
+
import type { SyncTable } from './create-sqlite'
|
|
3
3
|
import type { DocType } from './table/table.types'
|
|
4
4
|
import { isError, isPromise } from '../utils/is'
|
|
5
5
|
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'
|
|
6
|
-
import type {
|
|
6
|
+
import type { SqlSeachOptions } from './select-sql'
|
|
7
7
|
|
|
8
8
|
export interface SqLiteActions {
|
|
9
9
|
readonly next: () => Promise<boolean>
|
|
10
10
|
readonly reset: () => Promise<void>
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export interface UseSearchOptions<Document extends DocType, Selected = Document> {
|
|
14
|
-
readonly sorBy?: keyof Document
|
|
15
|
-
readonly order?: 'asc' | 'desc'
|
|
16
|
-
readonly limit?: number
|
|
17
|
-
readonly offset?: number
|
|
18
|
-
readonly where?: Where<Document>
|
|
19
|
-
readonly stepSize?: number
|
|
13
|
+
export interface UseSearchOptions<Document extends DocType, Selected = Document> extends SqlSeachOptions<Document> {
|
|
20
14
|
/**
|
|
21
15
|
* Naive projection. Prefer specialized queries for heavy fan-out graphs.
|
|
22
16
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CreateState } from './select-sql';
|
|
1
2
|
import type { DbOptions, DocType, Key, MutationResult, SearchOptions } from './table/table.types';
|
|
2
3
|
import type { Where } from './table/where';
|
|
3
4
|
type SearchId = string;
|
|
@@ -17,6 +18,7 @@ export interface SyncTable<Document extends DocType> {
|
|
|
17
18
|
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult[]>;
|
|
18
19
|
readonly destroy: () => void;
|
|
19
20
|
readonly next: (searchId: SearchId) => Promise<boolean>;
|
|
21
|
+
readonly select: <Params extends unknown[]>(compute: (...args: Params) => SearchOptions<Document>) => CreateState<Document, Params>;
|
|
20
22
|
}
|
|
21
|
-
export declare function createSqliteState<Document extends DocType>(options: DbOptions<Document>):
|
|
23
|
+
export declare function createSqliteState<Document extends DocType>(options: DbOptions<Document>): SyncTable<Document>;
|
|
22
24
|
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GetState } from '../types';
|
|
2
|
+
import type { SyncTable } from './create-sqlite';
|
|
3
|
+
import type { DocType } from './table/table.types';
|
|
4
|
+
import type { Where } from './table/where';
|
|
5
|
+
export type CreateState<Document, Params extends unknown[]> = (...params: Params) => GetState<Document[]>;
|
|
6
|
+
export interface SqlSeachOptions<Document extends DocType> {
|
|
7
|
+
readonly sorBy?: keyof Document;
|
|
8
|
+
readonly order?: 'asc' | 'desc';
|
|
9
|
+
readonly limit?: number;
|
|
10
|
+
readonly offset?: number;
|
|
11
|
+
readonly where?: Where<Document>;
|
|
12
|
+
readonly stepSize?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function selectSql<Document extends DocType, Params extends unknown[] = []>(state: SyncTable<Document>, compute: (...args: Params) => SqlSeachOptions<Document>): CreateState<Document, Params>;
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import { type DependencyList } from 'react';
|
|
2
|
-
import type { SyncTable } from './create-sqlite
|
|
2
|
+
import type { SyncTable } from './create-sqlite';
|
|
3
3
|
import type { DocType } from './table/table.types';
|
|
4
|
-
import type {
|
|
4
|
+
import type { SqlSeachOptions } from './select-sql';
|
|
5
5
|
export interface SqLiteActions {
|
|
6
6
|
readonly next: () => Promise<boolean>;
|
|
7
7
|
readonly reset: () => Promise<void>;
|
|
8
8
|
}
|
|
9
|
-
export interface UseSearchOptions<Document extends DocType, Selected = Document> {
|
|
10
|
-
readonly sorBy?: keyof Document;
|
|
11
|
-
readonly order?: 'asc' | 'desc';
|
|
12
|
-
readonly limit?: number;
|
|
13
|
-
readonly offset?: number;
|
|
14
|
-
readonly where?: Where<Document>;
|
|
15
|
-
readonly stepSize?: number;
|
|
9
|
+
export interface UseSearchOptions<Document extends DocType, Selected = Document> extends SqlSeachOptions<Document> {
|
|
16
10
|
/**
|
|
17
11
|
* Naive projection. Prefer specialized queries for heavy fan-out graphs.
|
|
18
12
|
*/
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createSqliteState as o}from"../create-sqlite-state";import{bunMemoryBackend as s}from"../table/bun-backend";const n=s();describe("create-sqlite-state",()=>{it("should batchSet and update multiple documents",async()=>{const e=await o({backend:n,tableName:"State2",key:"id"});await e.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25}]);const t=[];for await(const i of e.search())t.push(i);expect(t).toHaveLength(2),await e.batchSet([{id:"1",name:"Alice2",age:31},{id:"2",name:"Bob2",age:26}]);const a=[];for await(const i of e.search())a.push(i);expect(a).toEqual([{id:"1",name:"Alice2",age:31},{id:"2",name:"Bob2",age:26}])}),it("should deleteBy condition",async()=>{const e=await o({backend:n,tableName:"State3",key:"id"});await e.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]);const t=await e.deleteBy({age:{gt:30}});expect(t.length).toBe(1);const a=[];for await(const i of e.search())a.push(i);expect(a.map(i=>i.id)).toEqual(["1","2"])}),it("should get by key and with selector",async()=>{const e=await o({backend:n,tableName:"State4",key:"id"});await e.set({id:"1",name:"Alice",age:30});const t=await e.get("1");expect(t).toEqual({id:"1",name:"Alice",age:30});const a=await e.get("1",c=>c.name);expect(a).toBe("Alice");const i=await e.get("999");expect(i).toBeUndefined()}),it("should count documents with and without where",async()=>{const e=await o({backend:n,tableName:"State5",key:"id"});await e.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]),expect(await e.count()).toBe(3),expect(await e.count({where:{age:{gt:30}}})).toBe(1)}),it("should support search with options",async()=>{const e=await o({backend:n,tableName:"State6",key:"id"});await e.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]);const t=[];for await(const a of e.search({where:{age:{lt:35}}}))t.push(a);expect(t.map(a=>a.id)).toEqual(["1","2"])})});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{act as o,renderHook as c}from"@testing-library/react-hooks";import{createSqliteState as s}from"../create-sqlite-state";import{useSqliteValue as l}from"../use-sqlite-value";import{waitFor as i}from"@testing-library/react";import{bunMemoryBackend as m}from"../table/bun-backend";import{useState as p}from"react";import{DEFAULT_STEP_SIZE as d}from"../table/table";const u=m();describe("use-sqlite-state",()=>{it("should get basic value states",async()=>{const r=await s({backend:u,tableName:"State1",key:"id"});let t=0;const{result:n}=c(()=>(t++,l(r,{},[])));expect(t).toBe(1),o(()=>{r.set({id:"1",name:"Alice",age:30})}),await i(()=>{expect(n.current[0]).toEqual([{id:"1",name:"Alice",age:30}]),expect(t).toBe(3)}),o(()=>{r.set({id:"1",name:"Alice2",age:30})}),await i(()=>{expect(n.current[0]).toEqual([{id:"1",name:"Alice2",age:30}]),expect(t).toBe(4)}),o(()=>{r.delete("1")}),await i(()=>{expect(n.current[0]).toEqual([]),expect(t).toBe(5)}),o(()=>{r.set({id:"1",name:"Alice",age:30}),r.set({id:"2",name:"Bob",age:25})}),await i(()=>{expect(n.current[0].length).toBe(2),expect(t).toBe(6)})}),it("should use where clause changed via state",async()=>{const r=await s({backend:u,tableName:"State2",key:"id"});await r.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]);let t=0;const{result:n}=c(()=>{t++;const[a,e]=p(20);return[l(r,{where:{age:{gt:a}},sorBy:"age"},[a]),e]});await i(()=>{expect(n.current[0][0].map(a=>a.name)).toEqual(["Bob","Alice","Carol"]),expect(t).toBe(2)}),o(()=>{n.current[1](29)}),await i(()=>{expect(n.current[0][0].map(a=>a.name)).toEqual(["Alice","Carol"]),expect(t).toBe(4)})}),it("should support like in where clause and update results",async()=>{const r=await s({backend:u,tableName:"State3",key:"id"});await r.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Alicia",age:25},{id:"3",name:"Bob",age:40}]);let t=0;const{result:n,rerender:a}=c(({like:e})=>(t++,l(r,{where:{name:{like:e}}},[e])),{initialProps:{like:"%Ali%"}});await i(()=>{expect(n.current[0].map(e=>e.name)).toEqual(["Alice","Alicia"])}),o(()=>{a({like:"%Bob%"})}),await i(()=>{expect(n.current[0].map(e=>e.name)).toEqual(["Bob"])}),expect(t).toBeGreaterThanOrEqual(2)}),it("should update results when changing order and limit options",async()=>{const r=await s({backend:u,tableName:"State4",key:"id"});await r.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25},{id:"3",name:"Carol",age:40}]);const{result:t,rerender:n}=c(({order:a,limit:e})=>l(r,{sorBy:"age",order:a,limit:e},[a,e]),{initialProps:{order:"asc",limit:2}});await i(()=>{expect(t.current[0].map(a=>a.name)).toEqual(["Bob","Alice"])}),o(()=>{n({order:"desc",limit:2})}),await i(()=>{expect(t.current[0].map(a=>a.name)).toEqual(["Carol","Alice"])}),o(()=>{n({order:"desc",limit:1})}),await i(()=>{expect(t.current[0].map(a=>a.name)).toEqual(["Carol"])})}),it("should support actions.next and actions.refresh",async()=>{const r=await s({backend:u,tableName:"State5",key:"id"});await r.batchSet([{id:"1",name:"Alice",age:30},{id:"2",name:"Bob",age:25}]);const{result:t}=c(()=>l(r,{},[]));await i(()=>{expect(typeof t.current[1].next).toBe("function"),expect(typeof t.current[1].reset).toBe("function"),expect(t.current[1].reset()).resolves.toBeUndefined(),expect(t.current[1].next()).resolves.toBeFalsy()})}),it("should handle thousands of records",async()=>{const r=await s({backend:u,tableName:"State6",key:"id"}),t=[],n=1e3;for(let e=1;e<=n;e++)t.push({id:e.toString(),name:`Person${e}`,age:20+e%50});await r.batchSet(t);const{result:a}=c(()=>l(r,{},[]));await i(()=>{expect(a.current[0].length).toBe(d)});for(let e=0;e<n/d;e++)o(()=>{a.current[1].next()}),await i(()=>{expect(a.current[0].length).toBe(Math.min(d*(e+2),n))});o(()=>{a.current[1].reset()}),await i(()=>{expect(a.current[0].length).toBe(d)})}),it("should change ordering",async()=>{const r=await s({backend:u,tableName:"State7",key:"id"}),t=[];for(let e=1;e<=100;e++)t.push({id:e.toString(),name:`Person${e}`,age:20+e%50});await r.batchSet(t);const{result:n,rerender:a}=c(({order:e})=>l(r,{sorBy:"age",order:e},[e]),{initialProps:{order:"asc"}});await i(()=>{expect(n.current[0][0].age).toBe(20)}),o(()=>{a({order:"desc"})}),await i(()=>{expect(n.current[0][0].age).toBe(69)})})});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createScheduler as P}from"../scheduler";import{shallow as R}from"../utils/shallow";import{createTable as M,DEFAULT_STEP_SIZE as v}from"./table/table";const f=P();let O=0;function E(){return O++}async function L(g){const T=E();function l(e){return`state-${T}-search-${e}`}let h;async function o(){return h||(h=await M(g)),h}const c=new Map,i=new Map,m=new Map;async function b(e,n){const t=m.get(e),{items:a,options:s={}}=n,{stepSize:d=v}=s;if(!t)return!1;const r=[];for(let I=0;I<d;I++){const y=await t.next();if(y.done){m.delete(e);break}r.push(y.value.document),n.keys.add(String(y.value.rowId))}return R(n.items,r)?!1:(n.items=[...a,...r],r.length>0)}function w(e){const n=i.get(e);n&&n()}async function k(e){const n=await o(),t=c.get(e);if(!t)return;const{options:a}=t,s=n.search({...a,select:(d,{rowId:r})=>({document:d,rowId:r})});m.set(e,s),t.keys=new Set,t.items=[],await b(e,t)}async function S(e){await k(e),w(e)}function x(e){const{key:n,op:t}=e,a=new Set;for(const[s,{keys:d}]of c)switch(t){case"delete":case"update":{d.has(String(n))&&a.add(s);break}case"insert":{a.add(s);break}}return a}async function u(e){const n=new Set;for(const t of e){const a=x(t);for(const s of a)n.add(s)}for(const t of n){const a=l(t);f.schedule(a,{searchId:t})}}const p=new Set;function D(e,n){c.has(e)||(c.set(e,{items:[],options:n,keys:new Set}),n&&S(e));const t=c.get(e);return n&&(t.options=n),t}return{async set(e){const t=await(await o()).set(e);return await u([t]),t},async batchSet(e){const t=await(await o()).batchSet(e);return await u(t),t},async delete(e){const t=await(await o()).delete(e);return t&&await u([t]),t},async deleteBy(e){const t=await(await o()).deleteBy(e);return await u(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=l(e);f.schedule(a,{searchId:e})},subscribe(e,n){const t=l(e),a=f.add(t,{onScheduleDone(){S(e)}});return p.add(a),i.has(e)||i.set(e,n),()=>{i.delete(e),a(),c.delete(e)}},getSnapshot(e){return D(e).items},refresh:S,destroy(){for(const e of p)e();c.clear(),i.clear()},async next(e){const n=c.get(e);if(n){const t=await b(e,n);return t&&w(e),t}return!1}}}export{L as createSqliteState};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{useCallback as c,useDebugValue as a,useId as p,useLayoutEffect as y,useMemo as S}from"react";import{isError as f,isPromise as D}from"../utils/is";import{useSyncExternalStoreWithSelector as b}from"use-sync-external-store/shim/with-selector";function q(e,s={},u=[]){const{select:r}=s,t=p();y(()=>{e.updateSearchOptions(t,{...s,select:void 0})},u);const l=c(n=>r?n.map(r):n,[r]),d=c(n=>e.subscribe(t,n),[e,t]),i=c(()=>e.getSnapshot(t),[e,t]),o=b(d,i,i,l);if(a(o),D(o)||f(o))throw o;const m=S(()=>({next:()=>e.next(t),reset:()=>e.refresh(t)}),[t,e]);return[o,m]}export{q as useSqliteValue};
|