muya 2.4.4 → 2.4.5
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/cjs/index.js +1 -1
- package/esm/scheduler.js +1 -1
- package/esm/sqlite/create-sqlite.js +1 -1
- package/esm/sqlite/index.js +1 -1
- package/esm/sqlite/table/table.js +2 -2
- package/esm/sqlite/use-sqlite-count.js +1 -0
- package/esm/sqlite/use-sqlite.js +1 -1
- package/package.json +1 -1
- package/src/scheduler.ts +8 -2
- package/src/sqlite/__tests__/use-slite-count.test.tsx +96 -0
- package/src/sqlite/__tests__/use-sqlite.test.tsx +123 -40
- package/src/sqlite/create-sqlite.ts +43 -271
- package/src/sqlite/index.ts +1 -1
- package/src/sqlite/table/table.ts +3 -3
- package/src/sqlite/table/table.types.ts +9 -1
- package/src/sqlite/use-sqlite-count.ts +69 -0
- package/src/sqlite/use-sqlite.ts +149 -90
- package/types/scheduler.d.ts +1 -1
- package/types/sqlite/create-sqlite.d.ts +6 -12
- package/types/sqlite/index.d.ts +1 -1
- package/types/sqlite/table/table.d.ts +1 -1
- package/types/sqlite/table/table.types.d.ts +8 -1
- package/types/sqlite/use-sqlite-count.d.ts +17 -0
- package/types/sqlite/use-sqlite.d.ts +10 -10
package/cjs/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var V=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var Y=Object.prototype.hasOwnProperty;var W=(e,
|
|
1
|
+
"use strict";var V=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var Y=Object.prototype.hasOwnProperty;var W=(e,t)=>{for(var o in t)V(e,o,{get:t[o],enumerable:!0})},J=(e,t,o,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of K(t))!Y.call(e,s)&&s!==o&&V(e,s,{get:()=>t[s],enumerable:!(a=N(t,s))||a.enumerable});return e};var Q=e=>J(V({},"__esModule",{value:!0}),e);var $={};W($,{EMPTY_SELECTOR:()=>D,create:()=>z,isAbortError:()=>I,isArray:()=>g,isEqualBase:()=>m,isError:()=>v,isFunction:()=>h,isMap:()=>x,isPromise:()=>f,isSet:()=>E,isSetValueFunction:()=>O,isState:()=>Z,isUndefined:()=>d,select:()=>k,shallow:()=>F,useValue:()=>C});module.exports=Q($);var T=class extends Error{static Error="AbortError"};function X(e,t){t&&t.abort();let o=new AbortController,{signal:a}=o;return{promise:new Promise((n,u)=>{a.addEventListener("abort",()=>{u(new T)}),e.then(n).catch(u)}),controller:o}}function y(e,t=m){if(!d(e.current)){if(!d(e.previous)&&t(e.current,e.previous))return!1;e.previous=e.current}return!0}function p(e,t){let{cache:o,emitter:{emit:a}}=e;if(!f(t))return t;o.abortController&&o.abortController.abort();let{promise:s,controller:n}=X(t,o.abortController);return o.abortController=n,s.then(u=>{o.current=u,a()}).catch(u=>{I(u)||(o.current=u,a())})}function f(e){return e instanceof Promise}function h(e){return typeof e=="function"}function x(e){return e instanceof Map}function E(e){return e instanceof Set}function g(e){return Array.isArray(e)}function m(e,t){return e===t?!0:!!Object.is(e,t)}function O(e){return typeof e=="function"}function I(e){return e instanceof T}function v(e){return e instanceof Error}function d(e){return e===void 0}function Z(e){return h(e)&&"get"in e&&"set"in e&&"isSet"in e&&e.isSet===!0}var D=e=>e;function U(){let e=new Map,t=new Set,o=performance.now(),a=!1;function s(){let u=performance.now(),r=u-o,{size:i}=t;if(r<.2&&i>0&&i<10){o=u,n();return}a||(a=!0,Promise.resolve().then(()=>{a=!1,o=performance.now(),n()}))}function n(){if(t.size===0)return;let u=new Set,r=new Map;for(let i of t){if(e.has(i.id)){u.add(i.id);let{onResolveItem:c}=e.get(i.id);c&&c(i.value),r.has(i.id)||r.set(i.id,[]),r.get(i.id).push(i.value)}t.delete(i)}if(t.size>0){s();return}for(let i of u){let c=r.get(i);e.get(i)?.onScheduleDone(c)}}return{add(u,r){return e.set(u,r),()=>{e.delete(u)}},schedule(u,r){t.add({value:r,id:u}),s()}}}function k(e,t,o){function a(){let c=!1,l=e.map(A=>{let w=A.get();return f(w)&&(c=!0),w});return c?new Promise((A,w)=>{Promise.all(l).then(G=>{if(G.some(_=>d(_)))return w(new T);let j=t(...G);A(j)})}):t(...l)}function s(){if(d(r.cache.current)){let c=a();r.cache.current=p(r,c)}return r.cache.current}function n(){if(d(r.cache.current)){let l=a();r.cache.current=p(r,l)}let{current:c}=r.cache;return f(c)?new Promise(l=>{c.then(S=>{if(d(S)){l(n());return}l(S)})}):r.cache.current}let u=[];for(let c of e){let l=c.emitter.subscribe(()=>{b.schedule(r.id,null)});u.push(l)}let r=P({destroy(){for(let c of u)c();i(),r.emitter.clear(),r.cache.current=void 0},get:n,getSnapshot:s}),i=b.add(r.id,{onScheduleDone(){let c=a();r.cache.current=p(r,c),y(r.cache,o)&&r.emitter.emit()}});return r}var R=require("react");var L=require("use-sync-external-store/shim/with-selector");function C(e,t=D){let{emitter:o}=e,a=(0,L.useSyncExternalStoreWithSelector)(e.emitter.subscribe,o.getSnapshot,o.getInitialSnapshot,t);if((0,R.useDebugValue)(a),f(a)||v(a))throw a;return a}function q(e,t){let o=new Set,a=[];return{clear:()=>{for(let s of a)s();o.clear()},subscribe:s=>(o.add(s),()=>{o.delete(s)}),emit:(...s)=>{for(let n of o)n(...s)},contains:s=>o.has(s),getSnapshot:e,getInitialSnapshot:t,getSize:()=>o.size,subscribeToOtherEmitter(s){let n=s.subscribe(()=>{this.emit()});a.push(n)}}}var M=0;function H(){return M++,M.toString(36)}function P(e){let{get:t,destroy:o,set:a,getSnapshot:s}=e,n=!!a,u={},r=function(i){return C(r,i)};return r.isSet=n,r.id=H(),r.emitter=q(s),r.destroy=o,r.listen=function(i){return this.emitter.subscribe(()=>{let c=t();f(c)||i(t())})},r.withName=function(i){return this.stateName=i,this},r.select=function(i,c=m){return k([r],i,c)},r.get=t,r.set=a,r.cache=u,r}var b=U();function z(e,t=m){function o(){try{if(d(n.cache.current)){let r=h(e)?e():e,i=p(n,r);return n.cache.current=i,n.cache.current}return n.cache.current}catch(r){n.cache.current=r}return n.cache.current}async function a(r,i){await r;let c=i(n.cache.current),l=p(n,c);n.cache.current=l}function s(r){let i=o(),c=O(r);if(c&&f(i)){a(i,r);return}n.cache.abortController&&n.cache.abortController.abort();let l=c?r(i):r,S=p(n,l);n.cache.current=S}let n=P({get:o,destroy(){o(),u(),n.emitter.clear(),n.cache.current=void 0},set(r){b.schedule(n.id,r)},getSnapshot:o}),u=b.add(n.id,{onScheduleDone(){n.cache.current=o(),y(n.cache,t)&&n.emitter.emit()},onResolveItem:s});return h(e)||o(),n}function F(e,t){if(e==t)return!0;if(typeof e!="object"||e==null||typeof t!="object"||t==null)return!1;if(x(e)&&x(t)){if(e.size!==t.size)return!1;for(let[s,n]of e)if(!Object.is(n,t.get(s)))return!1;return!0}if(E(e)&&E(t)){if(e.size!==t.size)return!1;for(let s of e)if(!t.has(s))return!1;return!0}if(g(e)&&g(t)){if(e.length!==t.length)return!1;for(let[s,n]of e.entries())if(!Object.is(n,t[s]))return!1;return!0}let o=Object.keys(e),a=Object.keys(t);if(o.length!==a.length)return!1;for(let s of o)if(!Object.prototype.hasOwnProperty.call(t,s)||!Object.is(e[s],t[s]))return!1;return!0}
|
package/esm/scheduler.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const i=.2,a=10,f=0;function S(){const s=new Map,o=new Set;let r=performance.now(),c=!1;function l(){const n=performance.now(),t=n-r,{size:e}=o;if(t<.2&&e>0&&e<10){r=n,u();return}c||(c=!0,Promise.resolve().then(()=>{c=!1,r=performance.now(),u()}))}function u(){if(o.size===0)return;const n=new Set,t=new Map;for(const e of o){if(s.has(e.id)){n.add(e.id);const{onResolveItem:d}=s.get(e.id);d&&d(e.value),t.has(e.id)||t.set(e.id,[]),t.get(e.id).push(e.value)}o.delete(e)}if(o.size>0){l();return}for(const e of n){const d=t.get(e);s.get(e)?.onScheduleDone(d)}}return{add(n,t){return s.set(n,t),()=>{s.delete(n)}},schedule(n,t){o.add({value:t,id:n}),l()}}}export{f as RESCHEDULE_COUNT,i as THRESHOLD,a as THRESHOLD_ITEMS,S as createScheduler};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{STATE_SCHEDULER as
|
|
1
|
+
import{STATE_SCHEDULER as l}from"../create";import{getId as m}from"../utils/id";import{createTable as d}from"./table/table";function D(u){let s;async function a(){if(!s){const{backend:e,...n}=u,t=e instanceof Promise?await e:e;s=await d({backend:t,...n})}return s}const i=m();l.add(i,{onScheduleDone(e){if(!e)return;const n=e,t={};for(const c of n)c.removedAll&&(t.removedAll=!0),c.mutations&&(t.mutations||(t.mutations=[]),t.mutations.push(...c.mutations));for(const c of r)c(t)}});function o(e){l.schedule(i,e)}const r=new Set;return{subscribe(e){return r.add(e),()=>r.delete(e)},clear(){s?.clear(),o({removedAll:!0})},async set(e){const t=await(await a()).set(e);return o({mutations:[t]}),t},async batchSet(e){const t=await(await a()).batchSet(e);return o({mutations:t}),t},async delete(e){const t=await(await a()).delete(e);return t&&o({mutations:[t]}),t},async deleteBy(e){const t=await(await a()).deleteBy(e);return o({mutations:t}),t},async get(e,n){return(await a()).get(e,n)},async*search(e={}){const n=await a();for await(const t of n.search(e))yield t},async count(e){return await(await a()).count(e)}}}export{D as createSqliteState};
|
package/esm/sqlite/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./create-sqlite";export*from"./table";export*from"./
|
|
1
|
+
export*from"./create-sqlite";export*from"./table";export*from"./use-sqlite-count";export*from"./use-sqlite";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{unicodeTokenizer as C}from"./tokenizer";import{getWhereQuery as k}from"./where";const L=500,M=100;function R(l){return"$."+l}function U(l,a){if(!(!l||!a))return a.split(".").reduce((t,y)=>{if(typeof t=="object"&&t!==null&&y in t)return t[y]},l)}async function
|
|
1
|
+
import{unicodeTokenizer as C}from"./tokenizer";import{getWhereQuery as k}from"./where";const L=500,M=100;function R(l){return"$."+l}function U(l,a){if(!(!l||!a))return a.split(".").reduce((t,y)=>{if(typeof t=="object"&&t!==null&&y in t)return t[y]},l)}async function P(l){const{backend:a,tableName:t,indexes:y,key:A,disablePragmaOptimization:x}=l,u=A!==void 0;x||(await a.execute("PRAGMA journal_mode=WAL;"),await a.execute("PRAGMA synchronous=NORMAL;"),await a.execute("PRAGMA temp_store=MEMORY;"),await a.execute("PRAGMA cache_size=-20000;")),u?await a.execute(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS ${t} (
|
|
3
3
|
key TEXT PRIMARY KEY,
|
|
4
4
|
data TEXT NOT NULL
|
|
@@ -35,4 +35,4 @@ import{unicodeTokenizer as C}from"./tokenizer";import{getWhereQuery as k}from"./
|
|
|
35
35
|
SET ${T.map(o=>`${f[o]}=json_extract(new.data, '${R(o)}')`).join(", ")}
|
|
36
36
|
WHERE rowid = old.rowid;
|
|
37
37
|
END;
|
|
38
|
-
`)}function
|
|
38
|
+
`)}function $(e){if(u)return U(e,String(A))}async function b(e){return(await e.select("SELECT changes() AS c"))[0]?.c??0}const g={backend:a,async set(e,n){const r=n??a,o=JSON.stringify(e);if(u){const s=$(e);if(s==null)throw new Error(`Document is missing the configured key "${String(A)}".`);if(await r.execute(`UPDATE ${t} SET data = ? WHERE key = ?`,[o,s]),await b(r)===1)return{key:s,op:"update"};try{return await r.execute(`INSERT INTO ${t} (key, data) VALUES (?, ?)`,[s,o]),{key:s,op:"insert"}}catch{return await r.execute(`UPDATE ${t} SET data = ? WHERE key = ?`,[o,s]),{key:s,op:"update"}}}await r.execute(`INSERT INTO ${t} (data) VALUES (?)`,[o]);const c=(await r.select("SELECT last_insert_rowid() AS id"))[0]?.id;if(typeof c!="number")throw new Error("Failed to retrieve last_insert_rowid()");return{key:c,op:"insert"}},async get(e,n=r=>r){const r=u?"key = ?":"rowid = ?",o=await a.select(`SELECT rowid, data FROM ${t} WHERE ${r}`,[e]);if(o.length===0)return;const{data:E,rowid:c}=o[0],s=JSON.parse(E),i=u?$(s)??c:c;return n(s,{rowId:c,key:i})},async delete(e){const n=u?"key = ?":"rowid = ?";if(await a.execute(`DELETE FROM ${t} WHERE ${n}`,[e]),((await a.select("SELECT changes() AS c"))[0]?.c??0)>0)return{key:e,op:"delete"}},async*search(e={}){const{sortBy:n,order:r="asc",limit:o,offset:E=0,where:c,select:s=w=>w,pageSize:i=M}=e,p=k(c,t),h=`SELECT rowid, data FROM ${t} ${p}`;let S=0,D=E;for(;;){let w=h;n?w+=` ORDER BY json_extract(data, '${R(String(n))}') COLLATE NOCASE ${r.toUpperCase()}`:w+=u?` ORDER BY key COLLATE NOCASE ${r.toUpperCase()}`:` ORDER BY rowid ${r.toUpperCase()}`;const I=o?Math.min(i,o-S):i;w+=` LIMIT ${I} OFFSET ${D}`;const m=await a.select(w);if(m.length===0)break;for(const{rowid:O,data:F}of m){if(o&&S>=o)return;const N=JSON.parse(F),_=u?$(N)??O:O;yield s(N,{rowId:O,key:_}),S++}if(m.length<I||o&&S>=o)break;D+=m.length}},async count(e={}){const n=k(e.where,t),r=`SELECT COUNT(*) as count FROM ${t} ${n}`;return(await a.select(r))[0]?.count??0},async deleteBy(e){const n=k(e,t),r=u?"key":"rowid",o=[];return await a.transaction(async E=>{const c=await E.select(`SELECT ${r} AS k FROM ${t} ${n}`);if(c.length===0)return;const s=c.map(i=>i.k);for(let i=0;i<s.length;i+=L){const p=s.slice(i,i+L),h=p.map(()=>"?").join(",");await E.execute(`DELETE FROM ${t} WHERE ${r} IN (${h})`,p)}for(const i of s)o.push({key:i,op:"delete"})}),o},async clear(){await a.execute(`DELETE FROM ${t}`)},async batchSet(e){const n=[];return await a.transaction(async r=>{for(const o of e){const E=await g.set(o,r);n.push(E)}}),n}};return g}export{M as DEFAULT_PAGE_SIZE,P as createTable,U as getByPath,R as toJsonPath};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{useCallback as l,useEffect as b,useLayoutEffect as y,useReducer as d,useRef as D}from"react";function w(t,f={},r=[]){const n=D(0),[,o]=d(e=>e+1,0),u=l(async()=>{const e=await t.count(f);n.current=e,o()},r);return b(()=>{u()},r),y(()=>{const e=t.subscribe(a=>{const{mutations:c,removedAll:m}=a;if(m){n.current=0,o();return}if(!c)return;let s=!1;for(const p of c){const{op:i}=p;if(i==="insert"||i==="delete"){s=!0;break}}s&&u()});return()=>{e()}},[t]),n.current}export{w as useSqliteCount};
|
package/esm/sqlite/use-sqlite.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useCallback as
|
|
1
|
+
import{useCallback as l,useLayoutEffect as A,useReducer as v,useRef as D}from"react";import{DEFAULT_PAGE_SIZE as O}from"./table";const I=1e4;function _(u,g={},k=[]){const{select:p,pageSize:P=O}=g,e=D(),[,w]=v(n=>n+1,0),t=D(new Map),S=D(),b=l(()=>{const{select:n,...o}=g;S.current=u.search({select:(c,s)=>({doc:c,meta:s}),...o})},[u,...k]),f=l(()=>{e.current=[],t.current.clear(),b()},[b]),m=l(async n=>{e.current===void 0&&(e.current=[]),n===!0&&f();const{current:o}=S;if(!o)return!0;let c=!1;for(let s=0;s<P;s++){const r=await o.next();if(r.done){S.current=void 0,c=!0;break}if(t.current.has(r.value.meta.key)){s+=-1;continue}e.current.push(p?p(r.value.doc):r.value.doc),t.current.set(r.value.meta.key,e.current.length-1)}return c},[]),y=l(async()=>{const n=await m(!1);return w(),n},[m]);A(()=>{const n=u.subscribe(async o=>{const{mutations:c,removedAll:s}=o;if(s&&f(),!c)return;const r=e.current?.length??0;let h=r,x=!1;for(const i of c){const{key:a,op:q}=i;switch(q){case"insert":{h+=1;break}case"delete":{if(e.current&&t.current.has(a)){const d=t.current.get(a);if(d===void 0)break;e.current.splice(d,1),t.current.delete(a),x=!0}break}case"update":{if(e.current&&t.current.has(a)){const d=t.current.get(a);if(d===void 0)break;e.current[d]=await u.get(a,p),x=!0}break}}}const L=r!==h;if(L||x){if(L){await m(!0);let i=0;for(;(e.current?.length??0)<h&&i<I;)await m(!1),i++;i===I&&console.warn("Reached maximum iterations in fillNextPage loop. Possible duplicate or data issue.")}w()}});return()=>{n()}},[u]),A(()=>{b(),e.current=void 0,t.current.clear(),y()},k);const T=l(async()=>{f(),await y()},[y,f]);return[e.current,{nextPage:y,reset:T,keysIndex:t.current}]}export{_ as useSqliteValue};
|
package/package.json
CHANGED
package/src/scheduler.ts
CHANGED
|
@@ -10,7 +10,7 @@ interface GlobalSchedulerItem<T> {
|
|
|
10
10
|
|
|
11
11
|
export interface SchedulerOptions<T> {
|
|
12
12
|
readonly onResolveItem?: (item: T) => void
|
|
13
|
-
readonly onScheduleDone: () => void | Promise<void>
|
|
13
|
+
readonly onScheduleDone: (values?: unknown[]) => void | Promise<void>
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -59,6 +59,7 @@ export function createScheduler() {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
const effectedListeners = new Set<ScheduleId>()
|
|
62
|
+
const valuesMap = new Map<ScheduleId, unknown[]>()
|
|
62
63
|
for (const value of batches) {
|
|
63
64
|
if (listeners.has(value.id)) {
|
|
64
65
|
effectedListeners.add(value.id)
|
|
@@ -66,6 +67,10 @@ export function createScheduler() {
|
|
|
66
67
|
if (onResolveItem) {
|
|
67
68
|
onResolveItem(value.value)
|
|
68
69
|
}
|
|
70
|
+
if (!valuesMap.has(value.id)) {
|
|
71
|
+
valuesMap.set(value.id, [])
|
|
72
|
+
}
|
|
73
|
+
valuesMap.get(value.id)!.push(value.value)
|
|
69
74
|
}
|
|
70
75
|
batches.delete(value)
|
|
71
76
|
}
|
|
@@ -76,7 +81,8 @@ export function createScheduler() {
|
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
for (const id of effectedListeners) {
|
|
79
|
-
|
|
84
|
+
const values = valuesMap.get(id)
|
|
85
|
+
listeners.get(id)?.onScheduleDone(values)
|
|
80
86
|
}
|
|
81
87
|
}
|
|
82
88
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react-hooks'
|
|
2
|
+
import { createSqliteState } from '../create-sqlite'
|
|
3
|
+
import { useSqliteCount } from '../use-sqlite-count'
|
|
4
|
+
import { bunMemoryBackend } from '../table/bun-backend'
|
|
5
|
+
import { waitFor } from '@testing-library/react'
|
|
6
|
+
|
|
7
|
+
const backend = bunMemoryBackend()
|
|
8
|
+
interface Person {
|
|
9
|
+
id: string
|
|
10
|
+
name: string
|
|
11
|
+
age: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('useSqliteCount', () => {
|
|
15
|
+
it('should count items in the table', async () => {
|
|
16
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'CountTest1', key: 'id' })
|
|
17
|
+
const { result } = renderHook(() => useSqliteCount(sql))
|
|
18
|
+
|
|
19
|
+
await waitFor(() => {
|
|
20
|
+
expect(result.current).toBe(0)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
act(() => {
|
|
24
|
+
sql.set({ id: '1', name: 'Alice', age: 30 })
|
|
25
|
+
sql.set({ id: '2', name: 'Bob', age: 25 })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
await waitFor(() => {
|
|
29
|
+
expect(result.current).toBe(2)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
act(() => {
|
|
33
|
+
sql.delete('1')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
await waitFor(() => {
|
|
37
|
+
expect(result.current).toBe(1)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should support filtering with where clause', async () => {
|
|
42
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'CountTest2', key: 'id' })
|
|
43
|
+
await sql.batchSet([
|
|
44
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
45
|
+
{ id: '2', name: 'Bob', age: 25 },
|
|
46
|
+
{ id: '3', name: 'Carol', age: 40 },
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
const { result } = renderHook(() => useSqliteCount(sql, { where: { age: { gt: 30 } } }))
|
|
50
|
+
|
|
51
|
+
await waitFor(() => {
|
|
52
|
+
expect(result.current).toBe(1)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
act(() => {
|
|
56
|
+
sql.set({ id: '4', name: 'Dave', age: 35 })
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
await waitFor(() => {
|
|
60
|
+
expect(result.current).toBe(2)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
act(() => {
|
|
64
|
+
sql.delete('3')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
await waitFor(() => {
|
|
68
|
+
expect(result.current).toBe(1)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should react to dependency changes', async () => {
|
|
73
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'CountTest3', key: 'id' })
|
|
74
|
+
await sql.batchSet([
|
|
75
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
76
|
+
{ id: '2', name: 'Bob', age: 25 },
|
|
77
|
+
{ id: '3', name: 'Carol', age: 40 },
|
|
78
|
+
])
|
|
79
|
+
|
|
80
|
+
const { result, rerender } = renderHook(({ minAge }) => useSqliteCount(sql, { where: { age: { gt: minAge } } }, [minAge]), {
|
|
81
|
+
initialProps: { minAge: 20 },
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
await waitFor(() => {
|
|
85
|
+
expect(result.current).toBe(3)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
act(() => {
|
|
89
|
+
rerender({ minAge: 30 })
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
await waitFor(() => {
|
|
93
|
+
expect(result.current).toBe(1)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|
|
@@ -5,7 +5,7 @@ import { useSqliteValue } from '../use-sqlite'
|
|
|
5
5
|
import { waitFor } from '@testing-library/react'
|
|
6
6
|
import { bunMemoryBackend } from '../table/bun-backend'
|
|
7
7
|
import { StrictMode, Suspense, useState } from 'react'
|
|
8
|
-
import {
|
|
8
|
+
import { DEFAULT_PAGE_SIZE } from '../table/table'
|
|
9
9
|
|
|
10
10
|
const backend = bunMemoryBackend()
|
|
11
11
|
interface Person {
|
|
@@ -28,7 +28,7 @@ describe('use-sqlite-state', () => {
|
|
|
28
28
|
const { result, rerender } = renderHook(
|
|
29
29
|
() => {
|
|
30
30
|
reRenders++
|
|
31
|
-
const aha = useSqliteValue(sql
|
|
31
|
+
const aha = useSqliteValue(sql)
|
|
32
32
|
return aha
|
|
33
33
|
},
|
|
34
34
|
{ wrapper: Wrapper },
|
|
@@ -67,7 +67,7 @@ describe('use-sqlite-state', () => {
|
|
|
67
67
|
sql.set({ id: '2', name: 'Bob', age: 25 })
|
|
68
68
|
})
|
|
69
69
|
await waitFor(() => {
|
|
70
|
-
expect(result.current[0]
|
|
70
|
+
expect(result.current[0]?.length).toBe(2)
|
|
71
71
|
expect(reRenders).toBe(6)
|
|
72
72
|
})
|
|
73
73
|
|
|
@@ -76,7 +76,7 @@ describe('use-sqlite-state', () => {
|
|
|
76
76
|
})
|
|
77
77
|
await waitFor(() => {
|
|
78
78
|
expect(reRenders).toBe(7)
|
|
79
|
-
expect(result.current[0]
|
|
79
|
+
expect(result.current[0]?.length).toBe(2)
|
|
80
80
|
})
|
|
81
81
|
})
|
|
82
82
|
|
|
@@ -95,7 +95,8 @@ describe('use-sqlite-state', () => {
|
|
|
95
95
|
})
|
|
96
96
|
|
|
97
97
|
await waitFor(() => {
|
|
98
|
-
|
|
98
|
+
const names = result.current?.[0][0]?.map((p) => p.name)
|
|
99
|
+
expect(names).toEqual(['Bob', 'Alice', 'Carol'])
|
|
99
100
|
expect(reRenders).toBe(2)
|
|
100
101
|
})
|
|
101
102
|
|
|
@@ -104,7 +105,8 @@ describe('use-sqlite-state', () => {
|
|
|
104
105
|
result.current[1](29)
|
|
105
106
|
})
|
|
106
107
|
await waitFor(() => {
|
|
107
|
-
|
|
108
|
+
const names = result.current?.[0][0]?.map((p) => p.name)
|
|
109
|
+
expect(names).toEqual(['Alice', 'Carol'])
|
|
108
110
|
expect(reRenders).toBe(4)
|
|
109
111
|
})
|
|
110
112
|
})
|
|
@@ -125,13 +127,13 @@ describe('use-sqlite-state', () => {
|
|
|
125
127
|
{ initialProps: { like: '%Ali%' } },
|
|
126
128
|
)
|
|
127
129
|
await waitFor(() => {
|
|
128
|
-
expect(result.current[0]
|
|
130
|
+
expect(result.current?.[0]?.map((p) => p.name)).toEqual(['Alice', 'Alicia'])
|
|
129
131
|
})
|
|
130
132
|
act(() => {
|
|
131
133
|
rerender({ like: '%Bob%' })
|
|
132
134
|
})
|
|
133
135
|
await waitFor(() => {
|
|
134
|
-
expect(result.current[0]
|
|
136
|
+
expect(result.current?.[0]?.map((p) => p.name)).toEqual(['Bob'])
|
|
135
137
|
})
|
|
136
138
|
expect(reRenders).toBeGreaterThanOrEqual(2)
|
|
137
139
|
})
|
|
@@ -148,19 +150,19 @@ describe('use-sqlite-state', () => {
|
|
|
148
150
|
{ initialProps: { order: 'asc' as 'asc' | 'desc', limit: 2 } },
|
|
149
151
|
)
|
|
150
152
|
await waitFor(() => {
|
|
151
|
-
expect(result.current[0]
|
|
153
|
+
expect(result.current?.[0]?.map((p) => p.name)).toEqual(['Bob', 'Alice'])
|
|
152
154
|
})
|
|
153
155
|
act(() => {
|
|
154
156
|
rerender({ order: 'desc', limit: 2 })
|
|
155
157
|
})
|
|
156
158
|
await waitFor(() => {
|
|
157
|
-
expect(result.current[0]
|
|
159
|
+
expect(result.current?.[0]?.map((p) => p.name)).toEqual(['Carol', 'Alice'])
|
|
158
160
|
})
|
|
159
161
|
act(() => {
|
|
160
162
|
rerender({ order: 'desc', limit: 1 })
|
|
161
163
|
})
|
|
162
164
|
await waitFor(() => {
|
|
163
|
-
expect(result.current[0]
|
|
165
|
+
expect(result.current?.[0]?.map((p) => p.name)).toEqual(['Carol'])
|
|
164
166
|
})
|
|
165
167
|
})
|
|
166
168
|
|
|
@@ -173,13 +175,11 @@ describe('use-sqlite-state', () => {
|
|
|
173
175
|
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
174
176
|
// actions.next and actions.refresh should be functions
|
|
175
177
|
await waitFor(() => {
|
|
176
|
-
expect(typeof result.current[1].
|
|
178
|
+
expect(typeof result.current[1].nextPage).toBe('function')
|
|
177
179
|
expect(typeof result.current[1].reset).toBe('function')
|
|
178
|
-
expect(result.current[1].reset()).resolves.toBeUndefined()
|
|
179
|
-
expect(result.current[1].next()).resolves.toBeFalsy()
|
|
180
180
|
})
|
|
181
181
|
})
|
|
182
|
-
it('should handle thousands of records', async () => {
|
|
182
|
+
it('should handle thousands of records Here', async () => {
|
|
183
183
|
const sql = createSqliteState<Person>({ backend, tableName: 'State6Hook', key: 'id' })
|
|
184
184
|
const people: Person[] = []
|
|
185
185
|
const ITEMS_COUNT = 1000
|
|
@@ -189,16 +189,16 @@ describe('use-sqlite-state', () => {
|
|
|
189
189
|
await sql.batchSet(people)
|
|
190
190
|
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
191
191
|
await waitFor(() => {
|
|
192
|
-
expect(result.current[0]
|
|
192
|
+
expect(result.current?.[0]?.length ?? 0).toBe(DEFAULT_PAGE_SIZE)
|
|
193
193
|
})
|
|
194
194
|
|
|
195
|
-
// loop until we have all ITEMS_COUNT items
|
|
196
|
-
for (let index = 0; index < ITEMS_COUNT /
|
|
195
|
+
// // loop until we have all ITEMS_COUNT items
|
|
196
|
+
for (let index = 0; index < ITEMS_COUNT / DEFAULT_PAGE_SIZE; index++) {
|
|
197
197
|
act(() => {
|
|
198
|
-
result.current[1].
|
|
198
|
+
result.current[1].nextPage()
|
|
199
199
|
})
|
|
200
200
|
await waitFor(() => {
|
|
201
|
-
expect(result.current[0]
|
|
201
|
+
expect(result.current?.[0]?.length).toBe(Math.min(DEFAULT_PAGE_SIZE * (index + 2), ITEMS_COUNT))
|
|
202
202
|
})
|
|
203
203
|
}
|
|
204
204
|
|
|
@@ -206,7 +206,7 @@ describe('use-sqlite-state', () => {
|
|
|
206
206
|
result.current[1].reset()
|
|
207
207
|
})
|
|
208
208
|
await waitFor(() => {
|
|
209
|
-
expect(result.current[0]
|
|
209
|
+
expect(result.current?.[0]?.length).toBe(DEFAULT_PAGE_SIZE)
|
|
210
210
|
})
|
|
211
211
|
})
|
|
212
212
|
|
|
@@ -214,7 +214,7 @@ describe('use-sqlite-state', () => {
|
|
|
214
214
|
const sql = createSqliteState<Person>({ backend, tableName: 'State6Hook', key: 'id' })
|
|
215
215
|
const people: Person[] = []
|
|
216
216
|
const ITEMS_COUNT = 10_000
|
|
217
|
-
const
|
|
217
|
+
const pageSize = 500
|
|
218
218
|
for (let index = 1; index <= ITEMS_COUNT; index++) {
|
|
219
219
|
people.push({ id: index.toString(), name: `Person${index}`, age: 20 + (index % 50) })
|
|
220
220
|
}
|
|
@@ -222,30 +222,30 @@ describe('use-sqlite-state', () => {
|
|
|
222
222
|
let reRenders = 0
|
|
223
223
|
const { result } = renderHook(() => {
|
|
224
224
|
reRenders++
|
|
225
|
-
return useSqliteValue(sql, {
|
|
225
|
+
return useSqliteValue(sql, { pageSize }, [])
|
|
226
226
|
})
|
|
227
227
|
await waitFor(() => {
|
|
228
228
|
expect(reRenders).toBe(2)
|
|
229
|
-
expect(result.current[0]
|
|
229
|
+
expect(result.current?.[0]?.length).toBe(pageSize)
|
|
230
230
|
})
|
|
231
231
|
|
|
232
232
|
act(() => {
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
for (let index = 0; index < (ITEMS_COUNT - pageSize) / pageSize; index++) {
|
|
234
|
+
result.current[1].nextPage()
|
|
235
235
|
}
|
|
236
236
|
})
|
|
237
237
|
|
|
238
238
|
await waitFor(() => {
|
|
239
|
-
expect(reRenders).toBe(
|
|
240
|
-
expect(result.current[0]
|
|
239
|
+
expect(reRenders).toBe(21)
|
|
240
|
+
expect(result.current?.[0]?.length).toBe(ITEMS_COUNT)
|
|
241
241
|
})
|
|
242
242
|
|
|
243
243
|
act(() => {
|
|
244
244
|
result.current[1].reset()
|
|
245
245
|
})
|
|
246
246
|
await waitFor(() => {
|
|
247
|
-
expect(reRenders).toBe(
|
|
248
|
-
expect(result.current[0]
|
|
247
|
+
expect(reRenders).toBe(22)
|
|
248
|
+
expect(result.current?.[0]?.length).toBe(pageSize)
|
|
249
249
|
})
|
|
250
250
|
})
|
|
251
251
|
it('should change ordering', async () => {
|
|
@@ -259,13 +259,13 @@ describe('use-sqlite-state', () => {
|
|
|
259
259
|
initialProps: { order: 'asc' as 'asc' | 'desc' },
|
|
260
260
|
})
|
|
261
261
|
await waitFor(() => {
|
|
262
|
-
expect(result.current[0][0]
|
|
262
|
+
expect(result.current?.[0]?.[0]?.age).toBe(20)
|
|
263
263
|
})
|
|
264
264
|
act(() => {
|
|
265
265
|
rerender({ order: 'desc' })
|
|
266
266
|
})
|
|
267
267
|
await waitFor(() => {
|
|
268
|
-
expect(result.current[0][0]
|
|
268
|
+
expect(result.current?.[0]?.[0]?.age).toBe(69)
|
|
269
269
|
})
|
|
270
270
|
})
|
|
271
271
|
|
|
@@ -306,7 +306,7 @@ describe('use-sqlite-state', () => {
|
|
|
306
306
|
})
|
|
307
307
|
await waitFor(() => {
|
|
308
308
|
expect(reRenders).toBe(1)
|
|
309
|
-
expect(result1.current[0]
|
|
309
|
+
expect(result1.current?.[0]?.length).toBe(undefined)
|
|
310
310
|
})
|
|
311
311
|
|
|
312
312
|
const people: Person[] = []
|
|
@@ -316,12 +316,12 @@ describe('use-sqlite-state', () => {
|
|
|
316
316
|
await sql.batchSet(people)
|
|
317
317
|
await waitFor(() => {
|
|
318
318
|
expect(reRenders).toBe(3)
|
|
319
|
-
expect(result1.current[0]
|
|
319
|
+
expect(result1.current?.[0]?.length).toBe(50)
|
|
320
320
|
})
|
|
321
321
|
|
|
322
322
|
const { result: result2 } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
323
323
|
await waitFor(() => {
|
|
324
|
-
expect(result2.current[0]
|
|
324
|
+
expect(result2.current?.[0]?.length).toBe(50)
|
|
325
325
|
})
|
|
326
326
|
})
|
|
327
327
|
|
|
@@ -341,8 +341,8 @@ describe('use-sqlite-state', () => {
|
|
|
341
341
|
})
|
|
342
342
|
|
|
343
343
|
await waitFor(() => {
|
|
344
|
-
expect(reRenders).toBe(
|
|
345
|
-
expect(result.current[0]
|
|
344
|
+
expect(reRenders).toBe(2)
|
|
345
|
+
expect(result.current?.[0]?.length).toBe(0)
|
|
346
346
|
})
|
|
347
347
|
|
|
348
348
|
act(() => {
|
|
@@ -387,7 +387,7 @@ describe('use-sqlite-state', () => {
|
|
|
387
387
|
|
|
388
388
|
await waitFor(() => {
|
|
389
389
|
expect(reRenders).toBe(2)
|
|
390
|
-
expect(result.current[0]
|
|
390
|
+
expect(result.current?.[0]?.length).toBe(1)
|
|
391
391
|
})
|
|
392
392
|
|
|
393
393
|
act(() => {
|
|
@@ -395,15 +395,98 @@ describe('use-sqlite-state', () => {
|
|
|
395
395
|
})
|
|
396
396
|
await waitFor(() => {
|
|
397
397
|
expect(reRenders).toBe(3)
|
|
398
|
-
expect(result.current[0]
|
|
398
|
+
expect(result.current?.[0]?.length).toBe(2)
|
|
399
399
|
})
|
|
400
400
|
|
|
401
401
|
act(() => {
|
|
402
402
|
result.current[1].reset()
|
|
403
403
|
})
|
|
404
404
|
await waitFor(() => {
|
|
405
|
-
expect(result.current[0]
|
|
405
|
+
expect(result.current?.[0]?.length).toBe(2)
|
|
406
406
|
expect(reRenders).toBe(4)
|
|
407
407
|
})
|
|
408
408
|
})
|
|
409
|
+
|
|
410
|
+
it('should handle no items in the database', async () => {
|
|
411
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'EmptyState', key: 'id' })
|
|
412
|
+
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
413
|
+
|
|
414
|
+
await waitFor(() => {
|
|
415
|
+
expect(result.current[0]).toEqual([])
|
|
416
|
+
})
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
it('should handle fewer items than page size', async () => {
|
|
420
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'FewItemsState', key: 'id' })
|
|
421
|
+
await sql.batchSet([
|
|
422
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
423
|
+
{ id: '2', name: 'Bob', age: 25 },
|
|
424
|
+
])
|
|
425
|
+
|
|
426
|
+
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
427
|
+
|
|
428
|
+
await waitFor(() => {
|
|
429
|
+
expect(result.current[0]).toEqual([
|
|
430
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
431
|
+
{ id: '2', name: 'Bob', age: 25 },
|
|
432
|
+
])
|
|
433
|
+
})
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
it('should handle exactly page size items', async () => {
|
|
437
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'ExactPageSizeState', key: 'id' })
|
|
438
|
+
const items = Array.from({ length: DEFAULT_PAGE_SIZE }, (_, index) => ({
|
|
439
|
+
id: `${index + 1}`,
|
|
440
|
+
name: `Person${index + 1}`,
|
|
441
|
+
age: 20 + (index % 50),
|
|
442
|
+
}))
|
|
443
|
+
await sql.batchSet(items)
|
|
444
|
+
|
|
445
|
+
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
446
|
+
|
|
447
|
+
await waitFor(() => {
|
|
448
|
+
expect(result.current[0]?.length).toBe(DEFAULT_PAGE_SIZE)
|
|
449
|
+
})
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
it('should have thousands items, and update in middle check', async () => {
|
|
453
|
+
let reRenders = 0
|
|
454
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'ManyItemsState', key: 'id' })
|
|
455
|
+
const ITEMS_COUNT = 1000
|
|
456
|
+
const people: Person[] = []
|
|
457
|
+
for (let index = 1; index <= ITEMS_COUNT; index++) {
|
|
458
|
+
people.push({ id: index.toString(), name: `Person${index}`, age: 20 + (index % 50) })
|
|
459
|
+
}
|
|
460
|
+
await sql.batchSet(people)
|
|
461
|
+
|
|
462
|
+
const { result } = renderHook(() => {
|
|
463
|
+
reRenders++
|
|
464
|
+
return useSqliteValue(sql, { pageSize: 100 }, [])
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
await waitFor(() => {
|
|
468
|
+
expect(result.current[0]?.length).toBe(100)
|
|
469
|
+
expect(reRenders).toBe(2)
|
|
470
|
+
})
|
|
471
|
+
act(() => {
|
|
472
|
+
for (let index = 0; index < (ITEMS_COUNT - 100) / 100; index++) {
|
|
473
|
+
result.current[1].nextPage()
|
|
474
|
+
}
|
|
475
|
+
})
|
|
476
|
+
await waitFor(() => {
|
|
477
|
+
expect(result.current[0]?.length).toBe(ITEMS_COUNT)
|
|
478
|
+
expect(reRenders).toBe(11)
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
act(() => {
|
|
482
|
+
sql.set({ id: '500', name: 'UpdatedPerson500', age: 99 })
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
await waitFor(() => {
|
|
486
|
+
const updated = result.current[0]?.find((p) => p.id === '500')
|
|
487
|
+
expect(updated).toEqual({ id: '500', name: 'UpdatedPerson500', age: 99 })
|
|
488
|
+
expect(reRenders).toBe(12)
|
|
489
|
+
expect(result.current[0]?.length).toBe(ITEMS_COUNT)
|
|
490
|
+
})
|
|
491
|
+
})
|
|
409
492
|
})
|