muya 2.2.5 → 2.2.7

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.
@@ -1 +1 @@
1
- import{createScheduler as P}from"../scheduler";import{shallow as O}from"../utils/shallow";import{selectSql as R}from"./select-sql";import{createTable as v,DEFAULT_STEP_SIZE as M}from"./table/table";const f=P();let C=0;function B(){return C++}function L(g){const k=B();function l(e){return`state-${k}-search-${e}`}let m;async function o(){if(!m){const{backend:e,...n}=g,t=e instanceof Promise?await e:e;m=await v({backend:t,...n})}return 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=M}=a;if(!t)return!1;const r=[];for(let u=0;u<s;u++){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||O(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:u})=>({document:r,rowId:u})});h.set(e,s),t.keys=new Set,t.items=[],await p(e,t)}async function S(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=l(t);f.schedule(a,{searchId:t})}}const D=new Set;function w(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 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=w(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 D.add(a),i.has(e)||i.set(e,n),()=>{i.delete(e),a(),c.delete(e)}},getSnapshot(e){return w(e).items},refresh:S,destroy(){for(const e of D)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 R(I,e)}};return I}export{L as createSqliteState};
1
+ import{createScheduler as P}from"../scheduler";import{shallow as O}from"../utils/shallow";import{selectSql as v}from"./select-sql";import{createTable as R,DEFAULT_STEP_SIZE as M}from"./table/table";const f=P();let C=0;function B(){return C++}function L(g){const k=B();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 u=0;u<s;u++){const l=await t.next();if(l.done){S.delete(e);break}n.keys.has(String(l.value.rowId))||(r.push(l.value.document),n.keys.add(String(l.value.rowId)))}return r.length===0||O(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:u})=>({document:r,rowId:u})});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 D=new Set;function w(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={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=w(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 D.add(a),i.has(e)||i.set(e,n),()=>{i.delete(e),a(),c.delete(e)}},getSnapshot(e){return w(e).items},refresh:y,destroy(){for(const e of D)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 v(I,e)}};return I}export{L as createSqliteState};
@@ -1,11 +1,11 @@
1
- import{getWhereQuery as h}from"./where";const O=500,N=100;function D(l){return"$."+l}function I(l,r){if(!(!l||!r))return r.split(".").reduce((o,y)=>{if(typeof o=="object"&&o!==null&&y in o)return o[y]},l)}async function M(l){const{backend:r,tableName:o,indexes:y,key:S,disablePragmaOptimization:k}=l,d=S!==void 0;k||(await r.execute("PRAGMA journal_mode=WAL;"),await r.execute("PRAGMA synchronous=NORMAL;"),await r.execute("PRAGMA temp_store=MEMORY;"),await r.execute("PRAGMA cache_size=-20000;")),d?await r.execute(`
2
- CREATE TABLE IF NOT EXISTS ${o} (
1
+ import{getWhereQuery as h}from"./where";const O=500,N=100;function D(E){return"$."+E}function I(E,n){if(!(!E||!n))return n.split(".").reduce((r,y)=>{if(typeof r=="object"&&r!==null&&y in r)return r[y]},E)}async function M(E){const{backend:n,tableName:r,indexes:y,key:S,disablePragmaOptimization:$}=E,d=S!==void 0;$||(await n.execute("PRAGMA journal_mode=WAL;"),await n.execute("PRAGMA synchronous=NORMAL;"),await n.execute("PRAGMA temp_store=MEMORY;"),await n.execute("PRAGMA cache_size=-20000;")),d?await n.execute(`
2
+ CREATE TABLE IF NOT EXISTS ${r} (
3
3
  key TEXT PRIMARY KEY,
4
4
  data TEXT NOT NULL
5
5
  );
6
- `):await r.execute(`
7
- CREATE TABLE IF NOT EXISTS ${o} (
6
+ `):await n.execute(`
7
+ CREATE TABLE IF NOT EXISTS ${r} (
8
8
  data TEXT NOT NULL
9
9
  );
10
- `);for(const t of y??[]){const a=String(t);await r.execute(`CREATE INDEX IF NOT EXISTS idx_${o}_${a.replaceAll(/\W/g,"_")}
11
- ON ${o} (json_extract(data, '${D(a)}'));`)}function $(t){if(d)return I(t,String(S))}async function g(t){return(await t.select("SELECT changes() AS c"))[0]?.c??0}const p={backend:r,async set(t,a){const e=a??r,n=JSON.stringify(t);if(d){const s=$(t);if(s==null)throw new Error(`Document is missing the configured key "${String(S)}". Provide it or create the table without "key".`);if(await e.execute(`UPDATE ${o} SET data = ? WHERE key = ?`,[n,s]),await g(e)===1)return{key:s,op:"update"};try{return await e.execute(`INSERT INTO ${o} (key, data) VALUES (?, ?)`,[s,n]),{key:s,op:"insert"}}catch{return await e.execute(`UPDATE ${o} SET data = ? WHERE key = ?`,[n,s]),{key:s,op:"update"}}}await e.execute(`INSERT INTO ${o} (data) VALUES (?)`,[n]);const u=(await e.select("SELECT last_insert_rowid() AS id"))[0]?.id;if(typeof u!="number")throw new Error("Failed to retrieve last_insert_rowid()");return{key:u,op:"insert"}},async get(t,a=e=>e){const e=d?"key = ?":"rowid = ?",n=await r.select(`SELECT rowid, data FROM ${o} WHERE ${e}`,[t]);if(n.length===0)return;const{data:i,rowid:u}=n[0],s=JSON.parse(i);return a(s,{rowId:u})},async delete(t){const a=d?"key = ?":"rowid = ?";if(await r.execute(`DELETE FROM ${o} WHERE ${a}`,[t]),((await r.select("SELECT changes() AS c"))[0]?.c??0)>0)return{key:t,op:"delete"}},async*search(t={}){const{sortBy:a,order:e="asc",limit:n,offset:i=0,where:u,select:s=E=>E,stepSize:c=N}=t,w=h(u),f=`SELECT rowid, data FROM ${o} ${w}`;let T=0,A=i;for(;;){let E=f;a?E+=` ORDER BY json_extract(data, '${D(String(a))}') COLLATE NOCASE ${e.toUpperCase()}`:E+=d?` ORDER BY key COLLATE NOCASE ${e.toUpperCase()}`:` ORDER BY rowid ${e.toUpperCase()}`;const R=n?Math.min(c,n-T):c;E+=` LIMIT ${R} OFFSET ${A}`;const m=await r.select(E);if(m.length===0)break;for(const{rowid:b,data:L}of m){if(n&&T>=n)return;const x=JSON.parse(L);yield s(x,{rowId:b}),T++}if(m.length<R||n&&T>=n)break;A+=m.length}},async count(t={}){const a=h(t.where),e=`SELECT COUNT(*) as count FROM ${o} ${a}`;return(await r.select(e))[0]?.count??0},async deleteBy(t){const a=h(t),e=d?"key":"rowid",n=[];return await r.transaction(async i=>{const u=await i.select(`SELECT ${e} AS k FROM ${o} ${a}`);if(u.length===0)return;const s=u.map(c=>c.k);for(let c=0;c<s.length;c+=O){const w=s.slice(c,c+O),f=w.map(()=>"?").join(",");await i.execute(`DELETE FROM ${o} WHERE ${e} IN (${f})`,w)}for(const c of s)n.push({key:c,op:"delete"})}),n},async batchSet(t){const a=[];return await r.transaction(async e=>{for(const n of t){const i=await p.set(n,e);a.push(i)}}),a}};return p}export{N as DEFAULT_STEP_SIZE,M as createTable,I as getByPath};
10
+ `);for(const t of y??[]){const o=String(t);await n.execute(`CREATE INDEX IF NOT EXISTS idx_${r}_${o.replaceAll(/\W/g,"_")}
11
+ ON ${r} (json_extract(data, '${D(o)}'));`)}function k(t){if(d)return I(t,String(S))}async function g(t){return(await t.select("SELECT changes() AS c"))[0]?.c??0}const p={backend:n,async set(t,o){const e=o??n,a=JSON.stringify(t);if(d){const s=k(t);if(s==null)throw new Error(`Document is missing the configured key "${String(S)}". Provide it or create the table without "key".`);if(await e.execute(`UPDATE ${r} SET data = ? WHERE key = ?`,[a,s]),await g(e)===1)return{key:s,op:"update"};try{return await e.execute(`INSERT INTO ${r} (key, data) VALUES (?, ?)`,[s,a]),{key:s,op:"insert"}}catch{return await e.execute(`UPDATE ${r} SET data = ? WHERE key = ?`,[a,s]),{key:s,op:"update"}}}await e.execute(`INSERT INTO ${r} (data) VALUES (?)`,[a]);const u=(await e.select("SELECT last_insert_rowid() AS id"))[0]?.id;if(typeof u!="number")throw new Error("Failed to retrieve last_insert_rowid()");return{key:u,op:"insert"}},async get(t,o=e=>e){const e=d?"key = ?":"rowid = ?",a=await n.select(`SELECT rowid, data FROM ${r} WHERE ${e}`,[t]);if(a.length===0)return;const{data:i,rowid:u}=a[0],s=JSON.parse(i);return o(s,{rowId:u})},async delete(t){const o=d?"key = ?":"rowid = ?";if(await n.execute(`DELETE FROM ${r} WHERE ${o}`,[t]),((await n.select("SELECT changes() AS c"))[0]?.c??0)>0)return{key:t,op:"delete"}},async*search(t={}){const{sortBy:o,order:e="asc",limit:a,offset:i=0,where:u,select:s=l=>l,stepSize:c=N}=t,w=h(u),f=`SELECT rowid, data FROM ${r} ${w}`;let T=0,A=i;for(;;){let l=f;o?l+=` ORDER BY json_extract(data, '${D(String(o))}') COLLATE NOCASE ${e.toUpperCase()}`:l+=d?` ORDER BY key COLLATE NOCASE ${e.toUpperCase()}`:` ORDER BY rowid ${e.toUpperCase()}`;const R=a?Math.min(c,a-T):c;l+=` LIMIT ${R} OFFSET ${A}`;const m=await n.select(l);if(m.length===0)break;for(const{rowid:b,data:L}of m){if(a&&T>=a)return;const x=JSON.parse(L);yield s(x,{rowId:b}),T++}if(m.length<R||a&&T>=a)break;A+=m.length}},async count(t={}){const o=h(t.where),e=`SELECT COUNT(*) as count FROM ${r} ${o}`;return(await n.select(e))[0]?.count??0},async deleteBy(t){const o=h(t),e=d?"key":"rowid",a=[];return await n.transaction(async i=>{const u=await i.select(`SELECT ${e} AS k FROM ${r} ${o}`);if(u.length===0)return;const s=u.map(c=>c.k);for(let c=0;c<s.length;c+=O){const w=s.slice(c,c+O),f=w.map(()=>"?").join(",");await i.execute(`DELETE FROM ${r} WHERE ${e} IN (${f})`,w)}for(const c of s)a.push({key:c,op:"delete"})}),a},async clear(){await n.execute(`DELETE FROM ${r}`)},async batchSet(t){const o=[];return await n.transaction(async e=>{for(const a of t){const i=await p.set(a,e);o.push(i)}}),o}};return p}export{N as DEFAULT_STEP_SIZE,M as createTable,I as getByPath};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muya",
3
- "version": "2.2.5",
3
+ "version": "2.2.7",
4
4
  "author": "samuel.gjabel@gmail.com",
5
5
  "repository": "https://github.com/samuelgjabel/muya",
6
6
  "main": "cjs/index.js",
@@ -39,32 +39,32 @@ describe('use-sqlite-state', () => {
39
39
  expect(reRenders).toBe(3)
40
40
  })
41
41
 
42
- // act(() => {
43
- // sql.set({ id: '1', name: 'Alice2', age: 30 })
44
- // })
45
- // await waitFor(() => {
46
- // expect(result.current[0]).toEqual([{ id: '1', name: 'Alice2', age: 30 }])
47
- // expect(reRenders).toBe(4)
48
- // })
42
+ act(() => {
43
+ sql.set({ id: '1', name: 'Alice2', age: 30 })
44
+ })
45
+ await waitFor(() => {
46
+ expect(result.current[0]).toEqual([{ id: '1', name: 'Alice2', age: 30 }])
47
+ expect(reRenders).toBe(4)
48
+ })
49
49
 
50
- // // delete item
51
- // act(() => {
52
- // sql.delete('1')
53
- // })
54
- // await waitFor(() => {
55
- // expect(result.current[0]).toEqual([])
56
- // expect(reRenders).toBe(5)
57
- // })
50
+ // delete item
51
+ act(() => {
52
+ sql.delete('1')
53
+ })
54
+ await waitFor(() => {
55
+ expect(result.current[0]).toEqual([])
56
+ expect(reRenders).toBe(5)
57
+ })
58
58
 
59
- // // add two items
60
- // act(() => {
61
- // sql.set({ id: '1', name: 'Alice', age: 30 })
62
- // sql.set({ id: '2', name: 'Bob', age: 25 })
63
- // })
64
- // await waitFor(() => {
65
- // expect(result.current[0].length).toBe(2)
66
- // expect(reRenders).toBe(6)
67
- // })
59
+ // add two items
60
+ act(() => {
61
+ sql.set({ id: '1', name: 'Alice', age: 30 })
62
+ sql.set({ id: '2', name: 'Bob', age: 25 })
63
+ })
64
+ await waitFor(() => {
65
+ expect(result.current[0].length).toBe(2)
66
+ expect(reRenders).toBe(6)
67
+ })
68
68
  })
69
69
 
70
70
  it('should use where clause changed via state', async () => {
@@ -277,4 +277,38 @@ describe('use-sqlite-state', () => {
277
277
  expect(result.current[0]).toEqual(['Alice', 'Bob', 'Carol'])
278
278
  })
279
279
  })
280
+ it('should add 50 documents and then load with another hook', async () => {
281
+ const sql = createSqliteState<Person>({ backend, tableName: 'State9', key: 'id' })
282
+ let reRenders = 0
283
+ const { result: result1 } = renderHook(() => {
284
+ reRenders++
285
+ return useSqliteValue(
286
+ sql,
287
+ {
288
+ sorBy: 'age',
289
+ order: 'desc',
290
+ },
291
+ [],
292
+ )
293
+ })
294
+ await waitFor(() => {
295
+ expect(reRenders).toBe(2)
296
+ expect(result1.current[0].length).toBe(0)
297
+ })
298
+
299
+ const people: Person[] = []
300
+ for (let index = 1; index <= 50; index++) {
301
+ people.push({ id: index.toString(), name: `Person${index}`, age: 20 + (index % 50) })
302
+ }
303
+ await sql.batchSet(people)
304
+ await waitFor(() => {
305
+ expect(reRenders).toBe(3)
306
+ expect(result1.current[0].length).toBe(50)
307
+ })
308
+
309
+ const { result: result2 } = renderHook(() => useSqliteValue(sql, {}, []))
310
+ await waitFor(() => {
311
+ expect(result2.current[0].length).toBe(50)
312
+ })
313
+ })
280
314
  })
@@ -88,8 +88,11 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
88
88
  iterators.delete(searchId)
89
89
  break
90
90
  }
91
- newItems.push(result.value.document)
92
- data.keys.add(String(result.value.rowId))
91
+
92
+ if (!data.keys.has(String(result.value.rowId))) {
93
+ newItems.push(result.value.document)
94
+ data.keys.add(String(result.value.rowId))
95
+ }
93
96
  }
94
97
 
95
98
  if (newItems.length === 0) return false
@@ -208,6 +208,9 @@ export async function createTable<Document extends DocType>(options: DbOptions<D
208
208
  return results
209
209
  },
210
210
 
211
+ async clear() {
212
+ await backend.execute(`DELETE FROM ${tableName}`)
213
+ },
211
214
  async batchSet(documents: Document[]) {
212
215
  const mutations: MutationResult[] = []
213
216
  await backend.transaction(async (tx) => {
@@ -58,4 +58,5 @@ export interface Table<Document extends DocType> extends DbNotGeneric {
58
58
  readonly search: <Selected = Document>(options?: SearchOptions<Document, Selected>) => AsyncIterableIterator<Selected>
59
59
  readonly count: (options?: { where?: Where<Document> }) => Promise<number>
60
60
  readonly deleteBy: (where: Where<Document>) => Promise<MutationResult[]>
61
+ readonly clear: () => Promise<void>
61
62
  }
@@ -46,5 +46,6 @@ export interface Table<Document extends DocType> extends DbNotGeneric {
46
46
  where?: Where<Document>;
47
47
  }) => Promise<number>;
48
48
  readonly deleteBy: (where: Where<Document>) => Promise<MutationResult[]>;
49
+ readonly clear: () => Promise<void>;
49
50
  }
50
51
  export {};