muya 2.4.2 → 2.4.4

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{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 A(k){const g=P();function m(e){return`state-${g}-search-${e}`}let h;async function o(){if(!h){const{backend:e,...n}=k,t=e instanceof Promise?await e:e;h=await R({backend:t,...n})}return h}const c=new Map,d=new Map,y=new Map;async function p(e,n){const t=y.get(e),{options:a={}}=n,{stepSize:s=M}=a;if(!t)return!1;const r=[];for(let u=0;u<s;u++){const i=await t.next();if(i.done){y.delete(e);break}n.keys.has(String(i.value.key))||(r.push(i.value.document),n.keys.add(String(i.value.key)))}return r.length===0||v(n.items,r)?!1:(n.items=[...n.items,...r],!0)}function b(e){const n=d.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,key:i})=>({document:r,rowId:u,key:i})});y.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 l(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&&S(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 l([t]),t},async batchSet(e){const t=await(await o()).batchSet(e);return await l(t),t},async delete(e){const t=await(await o()).delete(e);return t&&await l([t]),t},async deleteBy(e){const t=await(await o()).deleteBy(e);return await l(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(){S(e)}});return D.add(a),d.has(e)||d.set(e,n),()=>{d.delete(e),a()}},getSnapshot(e){return w(e).items},refresh:S,destroy(){for(const e of D)e();c.clear(),d.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{A as createSqliteState};
1
+ import{STATE_SCHEDULER as y}from"../create";import{getId as v}from"../utils/id";import{shallow as N}from"../utils/shallow";import{selectSql as C}from"./select-sql";import{createTable as M,DEFAULT_STEP_SIZE as B}from"./table/table";function L(g){const x=v();function u(e){return`state-${x}-search-${e}`}let f;async function r(){if(!f){const{backend:e,...t}=g,n=e instanceof Promise?await e:e;f=await M({backend:n,...t})}return f}const s=new Map,d=new Map,S=new Map;async function p(e,t,n){const a=S.get(e),{options:o={}}=t,{stepSize:c=B}=o;if(!a)return{isOk:!1};const i=[];n&&t.keys.clear();for(let h=0;h<c;h++){const l=await a.next();if(l.done){S.delete(e);break}t.keys.has(String(l.value.key))||(i.push(l.value.document),t.keys.add(String(l.value.key)))}return i.length===0?{isOk:!0,newItems:n?[]:t.items,isDone:!0}:N(t.items,i)?{isOk:!1}:n?{isOk:!0,newItems:i}:{newItems:[...t.items,...i],isOk:!0}}function w(e){const t=d.get(e);if(t)for(const[,n]of t)n()}async function O(e){const t=await r(),n=s.get(e);if(!n)return;const{options:a}=n,o=t.search({...a,select:(h,{rowId:l,key:P})=>({document:h,rowId:l,key:P})});S.set(e,o);const{isOk:c,newItems:i}=await p(e,n,!0);c&&(n.items=i)}async function b(e){await O(e),w(e)}function T(e){const{key:t,op:n}=e,a=new Set;for(const[o,{keys:c}]of s)switch(n){case"delete":case"update":{c.has(String(t))&&a.add(o);break}case"insert":{a.add(o);break}}return a}async function m(e){const t=new Set;for(const n of e){const a=T(n);for(const o of a)t.add(o)}for(const n of t){const a=u(n);y.schedule(a,{})}}const D=new Set;function I(e,t){if(!s.has(e)){const a={items:[],options:t,keys:new Set,wasInitialized:!1};s.set(e,a)}const n=s.get(e);return t&&(n.options=t),n}const k={load(e){const t=s.get(e);if(t&&!t.wasInitialized){t.wasInitialized=!0;const n=u(e);y.schedule(n,{searchId:e})}},clear(e){s.delete(e)},async set(e){const n=await(await r()).set(e);return await m([n]),n},async batchSet(e){const n=await(await r()).batchSet(e);return await m(n),n},async delete(e){const n=await(await r()).delete(e);return n&&await m([n]),n},async deleteBy(e){const n=await(await r()).deleteBy(e);return await m(n),n},async get(e,t){return(await r()).get(e,t)},async*search(e={}){const t=await r();for await(const n of t.search(e))yield n},async count(e){return await(await r()).count(e)},updateSearchOptions(e,t){const n=I(e,t);n.options=t;const a=u(e);y.schedule(a,{searchId:e})},subscribe(e,t,n){const a=u(e),o=y.add(a,{onScheduleDone(){b(e)}});this.load(e),D.add(o),d.has(e)||d.set(e,new Map);const c=d.get(e);return c.set(t,n),()=>{c.delete(t),c.size===0&&d.delete(e),o()}},getSnapshot(e){return I(e).items},refresh:b,destroy(){for(const e of D)e();s.clear(),d.clear()},async next(e){const t=s.get(e);if(t){const n=await p(e,t,!1);return n.isOk&&(t.items=n.newItems,w(e)),n.isDone??!1}return!1},select(e){return C(k,e)}};return k}export{L as createSqliteState};
@@ -1 +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};
1
+ import{createState as l}from"../create-state";let n=0;function o(){return n++,`${n.toString(36)}-sql`}function S(r,a){const{subscribe:c,updateSearchOptions:s}=r,m=o();return(...u)=>{const e=o(),p=c(e,m,()=>{t.emitter.emit()}),i=a(...u),t=l({destroy(){p(),t.emitter.clear(),t.cache.current=void 0},get(){return r.getSnapshot(e)},getSnapshot(){return r.getSnapshot(e)}});return s(e,i),t}}export{S as selectSql};
@@ -1 +1 @@
1
- import{useCallback as l,useDebugValue as m,useEffect as a,useLayoutEffect as p,useMemo as f}from"react";import{isError as y,isPromise as D}from"../utils/is";import{useSyncExternalStoreWithSelector as h}from"use-sync-external-store/shim/with-selector";function x(n){const{limit:o,offset:i,order:c,sortBy:t,where:s,stepSize:u,select:r}=n;let e="";return o!==void 0&&(e+=`l${o}`),i!==void 0&&(e+=`o${i}`),c!==void 0&&(e+=`r${c}`),t!==void 0&&(e+=`s${t}`),s!==void 0&&(e+=`w${JSON.stringify(s)}`),u!==void 0&&(e+=`t${u}`),r!==void 0&&(e+=`f${r.toString()}`),e}function O(n,o={},i=[]){const{select:c}=o,t=f(()=>x({...o,select:void 0}),[o]);p(()=>{n.updateSearchOptions(t,{...o,select:void 0})},i),a(()=>()=>{n.clear(t)},[]);const s=l(d=>c?d.map(c):d,[c]),u=l(d=>n.subscribe(t,d),[n,t]),r=l(()=>n.getSnapshot(t),[n,t]),e=h(u,r,r,s);if(m(e),D(e)||y(e))throw e;const S=f(()=>({next:()=>n.next(t),reset:()=>n.refresh(t)}),[t,n]);return[e,S]}export{O as useSqliteValue};
1
+ import{useCallback as f,useDebugValue as p,useEffect as a,useId as y,useLayoutEffect as D,useMemo as S}from"react";import{isError as h,isPromise as x}from"../utils/is";import{useSyncExternalStoreWithSelector as b}from"use-sync-external-store/shim/with-selector";function q(t){const{limit:o,offset:s,order:c,sortBy:e,where:i,stepSize:u,select:d}=t;let n="";return o!==void 0&&(n+=`l${o}`),s!==void 0&&(n+=`o${s}`),c!==void 0&&(n+=`r${c}`),e!==void 0&&(n+=`s${e}`),i!==void 0&&(n+=`w${JSON.stringify(i)}`),u!==void 0&&(n+=`t${u}`),d!==void 0&&(n+=`f${d.toString()}`),n}function w(t,o={},s=[]){const{select:c}=o,e=S(()=>q({...o,select:void 0}),[o]),i=y();D(()=>{t.updateSearchOptions(e,{...o,select:void 0})},s),a(()=>()=>{t.clear(e)},[]);const u=f(l=>c?l.map(c):l,[c]),d=f(l=>t.subscribe(e,i,l),[t,e,i]),n=f(()=>t.getSnapshot(e),[t,e]),r=b(d,n,n,u);if(p(r),x(r)||h(r))throw r;const m=S(()=>({next:()=>t.next(e),reset:()=>t.refresh(e)}),[e,t]);return[r,m]}export{w as useSqliteValue};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muya",
3
- "version": "2.4.2",
3
+ "version": "2.4.4",
4
4
  "author": "samuel.gjabel@gmail.com",
5
5
  "repository": "https://github.com/samuelgjabel/muya",
6
6
  "main": "cjs/index.js",
@@ -230,13 +230,13 @@ describe('use-sqlite-state', () => {
230
230
  })
231
231
 
232
232
  act(() => {
233
- for (let index = 0; index < ITEMS_COUNT / stepSize; index++) {
234
- result.current[1].next()
233
+ while (!result.current[1].next()) {
234
+ /* empty */
235
235
  }
236
236
  })
237
237
 
238
238
  await waitFor(() => {
239
- expect(reRenders).toBe(4)
239
+ expect(reRenders).toBe(3)
240
240
  expect(result.current[0].length).toBe(ITEMS_COUNT)
241
241
  })
242
242
 
@@ -244,7 +244,7 @@ describe('use-sqlite-state', () => {
244
244
  result.current[1].reset()
245
245
  })
246
246
  await waitFor(() => {
247
- expect(reRenders).toBe(5)
247
+ expect(reRenders).toBe(4)
248
248
  expect(result.current[0].length).toBe(stepSize)
249
249
  })
250
250
  })
@@ -305,7 +305,7 @@ describe('use-sqlite-state', () => {
305
305
  )
306
306
  })
307
307
  await waitFor(() => {
308
- expect(reRenders).toBe(2)
308
+ expect(reRenders).toBe(1)
309
309
  expect(result1.current[0].length).toBe(0)
310
310
  })
311
311
 
@@ -341,7 +341,7 @@ describe('use-sqlite-state', () => {
341
341
  })
342
342
 
343
343
  await waitFor(() => {
344
- expect(reRenders).toBe(2)
344
+ expect(reRenders).toBe(1)
345
345
  expect(result.current[0].length).toBe(0)
346
346
  })
347
347
 
@@ -358,7 +358,7 @@ describe('use-sqlite-state', () => {
358
358
  sql.set({ person: { id: 'some_id', name: 'Alice', age: 31 } })
359
359
  })
360
360
  await waitFor(() => {
361
- // expect(reRenders).toBe(4)
361
+ expect(reRenders).toBe(4)
362
362
  expect(result.current[0]).toEqual([{ person: { id: 'some_id', name: 'Alice', age: 31 } }])
363
363
  })
364
364
 
@@ -17,7 +17,7 @@ export interface CreateSqliteOptions<Document extends DocType> extends Omit<DbOp
17
17
  export interface SyncTable<Document extends DocType> {
18
18
  // readonly registerSearch: <Selected = Document>(searchId: SearchId, options: SearchOptions<Document, Selected>) => () => void
19
19
  readonly updateSearchOptions: <Selected = Document>(searchId: SearchId, options: SearchOptions<Document, Selected>) => void
20
- readonly subscribe: (searchId: SearchId, listener: () => void) => () => void
20
+ readonly subscribe: (searchId: SearchId, componentId: string, listener: () => void) => () => void
21
21
  readonly getSnapshot: (searchId: SearchId) => Document[]
22
22
  readonly refresh: (searchId: SearchId) => Promise<void>
23
23
 
@@ -32,16 +32,18 @@ export interface SyncTable<Document extends DocType> {
32
32
  readonly destroy: () => void
33
33
  readonly next: (searchId: SearchId) => Promise<boolean>
34
34
  readonly clear: (searchId: SearchId) => void
35
+ readonly load: (searchId: SearchId) => void
35
36
 
36
37
  readonly select: <Params extends unknown[]>(
37
38
  compute: (...args: Params) => SearchOptions<Document>,
38
39
  ) => CreateState<Document, Params>
39
40
  }
40
41
 
41
- interface DataItems<Document extends DocType> {
42
+ interface CachedItem<Document extends DocType> {
42
43
  items: Document[]
43
44
  keys: Set<Key>
44
45
  options?: SearchOptions<Document, unknown>
46
+ wasInitialized: boolean
45
47
  }
46
48
 
47
49
  /**
@@ -80,22 +82,40 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
80
82
  key: Key
81
83
  }
82
84
  // const emitter = createEmitter<Table<Document>>()
83
- const cachedData = new Map<SearchId, DataItems<Document>>()
84
- const listeners = new Map<SearchId, () => void>()
85
+ const cachedData = new Map<SearchId, CachedItem<Document>>()
86
+ const listeners = new Map<SearchId, Map<string, () => void>>()
85
87
  const iterators = new Map<SearchId, AsyncIterableIterator<NextResult>>()
86
88
 
89
+ interface GetNextBase {
90
+ readonly newItems?: Document[]
91
+ readonly isOk: boolean
92
+ readonly isDone?: boolean
93
+ }
94
+
95
+ interface GetNextTrue extends GetNextBase {
96
+ readonly isOk: true
97
+ readonly newItems: Document[]
98
+ }
99
+ interface GetNextFalse extends GetNextBase {
100
+ readonly isOk: false
101
+ }
102
+ type GetNext = GetNextTrue | GetNextFalse
87
103
  /**
88
104
  * Next step in the iterator
89
105
  * @param searchId The search ID
90
106
  * @param data The data items to process
107
+ * @param shouldReset Whether to reset the items
91
108
  * @returns boolean indicating if new items were added
92
109
  */
93
- async function next(searchId: SearchId, data: DataItems<Document>): Promise<boolean> {
110
+ async function getNext(searchId: SearchId, data: CachedItem<Document>, shouldReset: boolean): Promise<GetNext> {
94
111
  const iterator = iterators.get(searchId)
95
112
  const { options: nextOptions = {} } = data
96
113
  const { stepSize = DEFAULT_STEP_SIZE } = nextOptions
97
- if (!iterator) return false
114
+ if (!iterator) return { isOk: false }
98
115
  const newItems: Document[] = []
116
+ if (shouldReset) {
117
+ data.keys.clear()
118
+ }
99
119
 
100
120
  for (let index = 0; index < stepSize; index++) {
101
121
  const result = await iterator.next()
@@ -110,10 +130,10 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
110
130
  }
111
131
  }
112
132
 
113
- if (newItems.length === 0) return false
114
- if (shallow(data.items, newItems)) return false
115
- data.items = [...data.items, ...newItems]
116
- return true
133
+ if (newItems.length === 0) return { isOk: true, newItems: shouldReset ? [] : data.items, isDone: true }
134
+ if (shallow(data.items, newItems)) return { isOk: false }
135
+ if (shouldReset) return { isOk: true, newItems }
136
+ return { newItems: [...data.items, ...newItems], isOk: true }
117
137
  }
118
138
 
119
139
  /**
@@ -123,7 +143,9 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
123
143
  function notifyListeners(searchId: SearchId) {
124
144
  const searchListeners = listeners.get(searchId)
125
145
  if (searchListeners) {
126
- searchListeners()
146
+ for (const [, listener] of searchListeners) {
147
+ listener()
148
+ }
127
149
  }
128
150
  }
129
151
 
@@ -138,9 +160,10 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
138
160
  const { options: refreshOptions } = data
139
161
  const iterator = table.search({ ...refreshOptions, select: (document, { rowId, key }) => ({ document, rowId, key }) })
140
162
  iterators.set(searchId, iterator)
141
- data.keys = new Set()
142
- data.items = []
143
- await next(searchId, data)
163
+ const { isOk, newItems } = await getNext(searchId, data, true)
164
+ if (isOk) {
165
+ data.items = newItems
166
+ }
144
167
  }
145
168
  /**
146
169
  * Refresh the data and notify listeners
@@ -156,7 +179,7 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
156
179
  * @param mutationResult The mutation result
157
180
  * @returns A set of search IDs that need to be updated
158
181
  */
159
- function handleChange(mutationResult: MutationResult) {
182
+ function getChangedKeys(mutationResult: MutationResult) {
160
183
  const { key, op } = mutationResult
161
184
  // find all cached data with key
162
185
  const searchIds = new Set<SearchId>()
@@ -186,16 +209,15 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
186
209
  async function handleChanges(mutationResults: MutationResult[]) {
187
210
  const updateSearchIds = new Set<SearchId>()
188
211
  for (const mutationResult of mutationResults) {
189
- const searchIds = handleChange(mutationResult)
212
+ const searchIds = getChangedKeys(mutationResult)
190
213
  for (const searchId of searchIds) {
191
214
  updateSearchIds.add(searchId)
192
215
  }
193
216
  }
194
217
 
195
- // const promises = []
196
218
  for (const searchId of updateSearchIds) {
197
219
  const scheduleId = getScheduleId(searchId)
198
- STATE_SCHEDULER.schedule(scheduleId, { searchId })
220
+ STATE_SCHEDULER.schedule(scheduleId, {})
199
221
  }
200
222
  }
201
223
 
@@ -209,19 +231,32 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
209
231
  */
210
232
  function registerData(searchId: SearchId, registerDataOptions?: SearchOptions<Document, unknown>) {
211
233
  if (!cachedData.has(searchId)) {
212
- cachedData.set(searchId, { items: [], options: registerDataOptions, keys: new Set() })
213
- if (registerDataOptions) {
214
- refresh(searchId)
234
+ const cachedItem: CachedItem<Document> = {
235
+ items: [],
236
+ options: registerDataOptions,
237
+ keys: new Set(),
238
+ wasInitialized: false,
215
239
  }
240
+ cachedData.set(searchId, cachedItem)
216
241
  }
217
- const data = cachedData.get(searchId)!
242
+ const cachedItem = cachedData.get(searchId)!
243
+
218
244
  if (registerDataOptions) {
219
- data.options = registerDataOptions
245
+ cachedItem.options = registerDataOptions
220
246
  }
221
- return data
247
+ return cachedItem
222
248
  }
223
249
 
224
250
  const state: SyncTable<Document> = {
251
+ load(searchId: SearchId) {
252
+ const cachedItem = cachedData.get(searchId)
253
+ if (!cachedItem) return
254
+ if (!cachedItem.wasInitialized) {
255
+ cachedItem.wasInitialized = true
256
+ const scheduleId = getScheduleId(searchId)
257
+ STATE_SCHEDULER.schedule(scheduleId, { searchId })
258
+ }
259
+ },
225
260
  clear(searchId: SearchId) {
226
261
  cachedData.delete(searchId)
227
262
  },
@@ -273,20 +308,28 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
273
308
  STATE_SCHEDULER.schedule(scheduleId, { searchId })
274
309
  },
275
310
 
276
- subscribe(searchId, listener) {
311
+ subscribe(searchId, componentId, listener) {
277
312
  const scheduleId = getScheduleId(searchId)
278
313
  const clearScheduler = STATE_SCHEDULER.add(scheduleId, {
279
314
  onScheduleDone() {
280
315
  refresh(searchId)
281
316
  },
282
317
  })
318
+ // console.log('Subscribing to searchId:', searchId)
319
+ this.load(searchId)
283
320
  clearSchedulers.add(clearScheduler)
284
321
 
285
322
  if (!listeners.has(searchId)) {
286
- listeners.set(searchId, listener)
323
+ listeners.set(searchId, new Map())
287
324
  }
325
+ const searchListeners = listeners.get(searchId)!
326
+ searchListeners.set(componentId, listener)
327
+
288
328
  return () => {
289
- listeners.delete(searchId)
329
+ searchListeners.delete(componentId)
330
+ if (searchListeners.size === 0) {
331
+ listeners.delete(searchId)
332
+ }
290
333
  clearScheduler()
291
334
  }
292
335
  },
@@ -303,11 +346,12 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
303
346
  async next(searchId) {
304
347
  const data = cachedData.get(searchId)
305
348
  if (data) {
306
- const hasNext = await next(searchId, data)
307
- if (hasNext) {
349
+ const hasNext = await getNext(searchId, data, false)
350
+ if (hasNext.isOk) {
351
+ data.items = hasNext.newItems
308
352
  notifyListeners(searchId)
309
353
  }
310
- return hasNext
354
+ return hasNext.isDone ?? false
311
355
  }
312
356
  return false
313
357
  },
@@ -36,10 +36,10 @@ export function selectSql<Document extends DocType, Params extends unknown[] = [
36
36
  compute: (...args: Params) => SqlSeachOptions<Document>,
37
37
  ): CreateState<Document, Params> {
38
38
  const { subscribe, updateSearchOptions } = state
39
-
39
+ const componentId = getStateId()
40
40
  const result: CreateState<Document, Params> = (...params) => {
41
41
  const searchId = getStateId()
42
- const destroy = subscribe(searchId, () => {
42
+ const destroy = subscribe(searchId, componentId, () => {
43
43
  getState.emitter.emit()
44
44
  })
45
45
 
@@ -1,4 +1,4 @@
1
- import { useCallback, useDebugValue, useEffect, 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'
@@ -6,7 +6,15 @@ import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/w
6
6
  import type { SqlSeachOptions } from './select-sql'
7
7
 
8
8
  export interface SqLiteActions {
9
+ /**
10
+ * Load the next page of results and return if isDone to show more results.
11
+ * @returns isDone: boolean
12
+ */
9
13
  readonly next: () => Promise<boolean>
14
+ /**
15
+ * Reset the pagination and load the first page of results.
16
+ * @returns void
17
+ */
10
18
  readonly reset: () => Promise<void>
11
19
  }
12
20
 
@@ -65,6 +73,7 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
65
73
  const { select } = options
66
74
 
67
75
  const searchId = useMemo(() => generateCacheKey({ ...options, select: undefined }), [options])
76
+ const componentId = useId()
68
77
 
69
78
  useLayoutEffect(() => {
70
79
  state.updateSearchOptions(searchId, { ...options, select: undefined })
@@ -72,6 +81,7 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
72
81
  }, deps)
73
82
 
74
83
  useEffect(() => {
84
+ // state.load(searchId)
75
85
  return () => {
76
86
  state.clear(searchId)
77
87
  }
@@ -88,9 +98,9 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
88
98
 
89
99
  const subscribe = useCallback(
90
100
  (onStorageChange: () => void) => {
91
- return state.subscribe(searchId, onStorageChange)
101
+ return state.subscribe(searchId, componentId, onStorageChange)
92
102
  },
93
- [state, searchId],
103
+ [state, searchId, componentId],
94
104
  )
95
105
 
96
106
  const getSnapshot = useCallback(() => {
@@ -8,7 +8,7 @@ export interface CreateSqliteOptions<Document extends DocType> extends Omit<DbOp
8
8
  }
9
9
  export interface SyncTable<Document extends DocType> {
10
10
  readonly updateSearchOptions: <Selected = Document>(searchId: SearchId, options: SearchOptions<Document, Selected>) => void;
11
- readonly subscribe: (searchId: SearchId, listener: () => void) => () => void;
11
+ readonly subscribe: (searchId: SearchId, componentId: string, listener: () => void) => () => void;
12
12
  readonly getSnapshot: (searchId: SearchId) => Document[];
13
13
  readonly refresh: (searchId: SearchId) => Promise<void>;
14
14
  readonly set: (document: Document) => Promise<MutationResult>;
@@ -23,6 +23,7 @@ export interface SyncTable<Document extends DocType> {
23
23
  readonly destroy: () => void;
24
24
  readonly next: (searchId: SearchId) => Promise<boolean>;
25
25
  readonly clear: (searchId: SearchId) => void;
26
+ readonly load: (searchId: SearchId) => void;
26
27
  readonly select: <Params extends unknown[]>(compute: (...args: Params) => SearchOptions<Document>) => CreateState<Document, Params>;
27
28
  }
28
29
  /**
@@ -3,7 +3,15 @@ import type { SyncTable } from './create-sqlite';
3
3
  import type { DocType } from './table/table.types';
4
4
  import type { SqlSeachOptions } from './select-sql';
5
5
  export interface SqLiteActions {
6
+ /**
7
+ * Load the next page of results and return if isDone to show more results.
8
+ * @returns isDone: boolean
9
+ */
6
10
  readonly next: () => Promise<boolean>;
11
+ /**
12
+ * Reset the pagination and load the first page of results.
13
+ * @returns void
14
+ */
7
15
  readonly reset: () => Promise<void>;
8
16
  }
9
17
  export interface UseSearchOptions<Document extends DocType, Selected = Document> extends SqlSeachOptions<Document> {