muya 2.4.94 → 2.4.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/sqlite/create-sqlite.js +1 -1
- package/esm/sqlite/table/table.js +6 -6
- package/esm/sqlite/use-sqlite.js +1 -1
- package/package.json +1 -1
- package/src/sqlite/__tests__/use-sqlite.test.tsx +31 -0
- package/src/sqlite/create-sqlite.ts +18 -11
- package/src/sqlite/table/table.ts +20 -8
- package/src/sqlite/table/table.types.ts +20 -5
- package/src/sqlite/use-sqlite.ts +2 -2
- package/types/sqlite/create-sqlite.d.ts +8 -7
- package/types/sqlite/table/table.types.d.ts +17 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
import{STATE_SCHEDULER as
|
|
1
|
+
import{STATE_SCHEDULER as u}from"../create";import{getId as m}from"../utils/id";import{createTable as d}from"./table/table";function h(l){let s;async function o(){if(!s){const{backend:e,...n}=l,t=e instanceof Promise?await e:e;s=await d({backend:t,...n})}return s}const i=m();u.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 a(e){u.schedule(i,e)}const r=new Set;return{subscribe(e){return r.add(e),()=>r.delete(e)},async clear(){const e=await o();return a({removedAll:!0}),e.clear()},async set(e){const t=await(await o()).set(e);return a({mutations:[t]}),t},async batchSet(e){const t=await(await o()).batchSet(e);return a({mutations:t}),t},async batchDelete(e){const t=await(await o()).batchDelete(e);return a({mutations:t}),t},async delete(e){const t=await(await o()).delete(e);return t&&a({mutations:[t]}),t},async deleteBy(e){const t=await(await o()).deleteBy(e);return a({mutations: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)}}}export{h as createSqliteState};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{unicodeTokenizer as _}from"./tokenizer";import{getWhereQuery as g}from"./where";const x=500,C=100;function
|
|
1
|
+
import{unicodeTokenizer as _}from"./tokenizer";import{getWhereQuery as g}from"./where";const x=500,C=100;function R(l){return"$."+l}function M(l,s){if(!(!l||!s))return s.split(".").reduce((t,w)=>{if(typeof t=="object"&&t!==null&&w in t)return t[w]},l)}async function G(l){const{backend:s,tableName:t,indexes:w,key:$,disablePragmaOptimization:L}=l,E=$!==void 0;L||(await s.execute("PRAGMA journal_mode=WAL;"),await s.execute("PRAGMA synchronous=NORMAL;"),await s.execute("PRAGMA temp_store=MEMORY;"),await s.execute("PRAGMA cache_size=-20000;")),E?await s.execute(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS ${t} (
|
|
3
3
|
key TEXT PRIMARY KEY,
|
|
4
4
|
data TEXT NOT NULL
|
|
@@ -7,8 +7,8 @@ import{unicodeTokenizer as _}from"./tokenizer";import{getWhereQuery as g}from"./
|
|
|
7
7
|
CREATE TABLE IF NOT EXISTS ${t} (
|
|
8
8
|
data TEXT NOT NULL
|
|
9
9
|
);
|
|
10
|
-
`);let d;const T=[],f={};for(const e of
|
|
11
|
-
ON ${t} (json_extract(data, '${
|
|
10
|
+
`);let d;const T=[],f={};for(const e of w??[])if(typeof e=="string"&&e.startsWith("fts:")){const n=e.slice(4),o=n.replaceAll(".","_");T.push(n),f[n]=o}else if(typeof e=="object"&&e.type==="fts"){const n=e.path,o=n.replaceAll(".","_");if(T.push(n),f[n]=o,e.tokenizer){if(!d)d=e.tokenizer;else if(d!==e.tokenizer)throw new Error(`Conflicting FTS tokenizers: already using "${d}", got "${e.tokenizer}"`)}}else{const n=String(e);await s.execute(`CREATE INDEX IF NOT EXISTS idx_${t}_${n.replaceAll(/\W/g,"_")}
|
|
11
|
+
ON ${t} (json_extract(data, '${R(n)}'));`)}if(T.length>0){let e;typeof d=="object"?e=_(d):d===void 0?e='"unicode61", "remove_diacritics=1"':e=d;const n=T.map(r=>f[r]).join(", "),o=`
|
|
12
12
|
CREATE VIRTUAL TABLE IF NOT EXISTS ${t}_fts
|
|
13
13
|
USING fts5(${n}, tokenize=${e});
|
|
14
14
|
`;await s.execute(o),await s.execute(`
|
|
@@ -18,7 +18,7 @@ import{unicodeTokenizer as _}from"./tokenizer";import{getWhereQuery as g}from"./
|
|
|
18
18
|
INSERT INTO ${t}_fts(rowid, ${n})
|
|
19
19
|
VALUES (
|
|
20
20
|
new.rowid,
|
|
21
|
-
${T.map(r=>`json_extract(new.data, '${
|
|
21
|
+
${T.map(r=>`json_extract(new.data, '${R(r)}')`).join(", ")}
|
|
22
22
|
);
|
|
23
23
|
END;
|
|
24
24
|
`),await s.execute(`
|
|
@@ -32,7 +32,7 @@ import{unicodeTokenizer as _}from"./tokenizer";import{getWhereQuery as g}from"./
|
|
|
32
32
|
AFTER UPDATE ON ${t}
|
|
33
33
|
BEGIN
|
|
34
34
|
UPDATE ${t}_fts
|
|
35
|
-
SET ${T.map(r=>`${f[r]}=json_extract(new.data, '${
|
|
35
|
+
SET ${T.map(r=>`${f[r]}=json_extract(new.data, '${R(r)}')`).join(", ")}
|
|
36
36
|
WHERE rowid = old.rowid;
|
|
37
37
|
END;
|
|
38
|
-
`)}function
|
|
38
|
+
`)}function h(e){if(E)return M(e,String($))}const A={backend:s,async set(e,n){const o=n??s,r=JSON.stringify(e);if(E){const a=h(e);if(a==null)throw new Error(`Document is missing the configured key "${String($)}".`);return(await o.select(`SELECT key FROM ${t} WHERE key = ?`,[a])).length>0?(await o.execute(`UPDATE ${t} SET data = ? WHERE key = ?`,[r,a]),{key:a,op:"update",document:e}):(await o.execute(`INSERT INTO ${t} (key, data) VALUES (?, ?)`,[a,r]),{key:a,op:"insert",document:e})}await o.execute(`INSERT INTO ${t} (data) VALUES (?)`,[r]);const u=(await o.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",document:e}},async get(e,n=o=>o){const o=E?"key = ?":"rowid = ?",r=await s.select(`SELECT rowid, data FROM ${t} WHERE ${o}`,[e]);if(r.length===0)return;const{data:c,rowid:u}=r[0],a=JSON.parse(c),i=E?h(a)??u:u;return n(a,{rowId:u,key:i})},async delete(e,n){const o=n??s,r=E?"key = ?":"rowid = ?";if(await o.execute(`DELETE FROM ${t} WHERE ${r}`,[e]),((await s.select("SELECT changes() AS c"))[0]?.c??0)>0)return{key:e,op:"delete"}},async*search(e={}){const{sortBy:n,order:o="asc",limit:r,offset:c=0,where:u,select:a=y=>y,pageSize:i=C}=e,p=g(u,t),k=`SELECT rowid, data FROM ${t} ${p}`;let S=0,D=c;for(;;){let y=k;n?y+=` ORDER BY json_extract(data, '${R(String(n))}') COLLATE NOCASE ${o.toUpperCase()}`:y+=E?` ORDER BY key COLLATE NOCASE ${o.toUpperCase()}`:` ORDER BY rowid ${o.toUpperCase()}`;const I=r?Math.min(i,r-S):i;y+=` LIMIT ${I} OFFSET ${D}`;const m=await s.select(y);if(m.length===0)break;for(const{rowid:O,data:b}of m){if(r&&S>=r)return;const N=JSON.parse(b),F=E?h(N)??O:O;yield a(N,{rowId:O,key:F}),S++}if(m.length<I||r&&S>=r)break;D+=m.length}},async count(e={}){const n=g(e.where,t),o=`SELECT COUNT(*) as count FROM ${t} ${n}`;return(await s.select(o))[0]?.count??0},async deleteBy(e){const n=g(e,t),o=E?"key":"rowid",r=[];return await s.transaction(async c=>{const u=await c.select(`SELECT ${o} AS k FROM ${t} ${n}`);if(u.length===0)return;const a=u.map(i=>i.k);for(let i=0;i<a.length;i+=x){const p=a.slice(i,i+x),k=p.map(()=>"?").join(",");await c.execute(`DELETE FROM ${t} WHERE ${o} IN (${k})`,p)}for(const i of a)r.push({key:i,op:"delete",document:void 0})}),r},async clear(){await s.execute(`DELETE FROM ${t}`)},async batchSet(e){const n=[];return await s.transaction(async o=>{for(const r of e){const c=await A.set(r,o);n.push(c)}}),n},async batchDelete(e){const n=[];return await s.transaction(async o=>{for(const r of e){const c=await A.delete(r,o);c&&n.push(c)}}),n}};return A}export{C as DEFAULT_PAGE_SIZE,G as createTable,M as getByPath,R as toJsonPath};
|
package/esm/sqlite/use-sqlite.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useCallback as
|
|
1
|
+
import{useCallback as y,useLayoutEffect as T,useReducer as C,useRef as w}from"react";import{DEFAULT_PAGE_SIZE as E}from"./table";import{shallow as K}from"../utils/shallow";const q=1e4;function F(d,k={},I=[]){const{select:f,pageSize:R=E}=k,e=w(null),[,A]=C(c=>c+1,0),t=w(new Map),g=w(null),L=y(()=>{const{select:c,...a}=k;g.current=d.search({select:(u,i)=>({doc:u,meta:i}),...a})},[d,...I]),m=y(()=>{e.current=[],t.current.clear(),L()},[L]),p=y(async c=>{e.current===null&&(e.current=[]),c===!0&&m();const{current:a}=g;if(!a)return!0;let u=!1;for(let i=0;i<R;i++){const o=await a.next();if(o.done){g.current=null,u=!0;break}t.current.has(o.value.meta.key)||(e.current.push(f?f(o.value.doc):o.value.doc),t.current.set(o.value.meta.key,e.current.length-1))}return e.current=[...e.current],u},[]),h=y(async()=>{const c=await p(!1);return A(),c},[p]);T(()=>{const c=d.subscribe(async a=>{const{mutations:u,removedAll:i}=a;if(i&&m(),!u)return;const o=e.current?.length??0;let D=o,S=!1;const b=new Set;for(const s of u){const{key:n,op:x,document:l}=s;switch(x){case"insert":{D+=1;break}case"delete":{if(e.current&&e.current.length>0&&t.current.has(n)){const r=t.current.get(n);if(r===void 0)break;b.add(r),S=!0}break}case"update":{if(t.current.has(n)){const r=t.current.get(n);if(r!==void 0&&e.current){const P=f?f(l):l,_=e.current[r];K(_,P)||(e.current[r]=P,e.current=[...e.current],S=!0)}}else{const r=await d.get(n,f);r&&(e.current=[...e.current??[],r],t.current.set(n,e.current.length-1),S=!0)}break}}}if(b.size>0&&e.current&&e.current.length>0){const s=new Map;e.current=e.current?.filter((x,l)=>!b.has(l));let n=0;for(const[x,l]of t.current)b.has(l)||(s.set(x,n),n++);t.current=s}const v=o!==D;if(v||S){if(v){await p(!0);let s=0;for(;(e.current?.length??0)<D&&s<q;)await p(!1),s++;s===q&&console.warn("Reached maximum iterations in fillNextPage loop. Possible duplicate or data issue.")}A()}});return()=>{c()}},[d]),T(()=>{m(),h()},I);const O=y(async()=>{m(),await h()},[h,m]);return[e.current,{nextPage:h,reset:O,keysIndex:t.current}]}export{F as useSqliteValue};
|
package/package.json
CHANGED
|
@@ -489,4 +489,35 @@ describe('use-sqlite-state', () => {
|
|
|
489
489
|
expect(result.current[0]?.length).toBe(ITEMS_COUNT)
|
|
490
490
|
})
|
|
491
491
|
})
|
|
492
|
+
it("should test batch delete and its impact on the hook's results", async () => {
|
|
493
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'BatchDeleteState', key: 'id' })
|
|
494
|
+
const people: Person[] = []
|
|
495
|
+
for (let index = 1; index <= 20; index++) {
|
|
496
|
+
people.push({ id: index.toString(), name: `Person${index}`, age: 20 + (index % 50) })
|
|
497
|
+
}
|
|
498
|
+
await sql.batchSet(people)
|
|
499
|
+
|
|
500
|
+
let reRenders = 0
|
|
501
|
+
const { result } = renderHook(() => {
|
|
502
|
+
reRenders++
|
|
503
|
+
return useSqliteValue(sql, {}, [])
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
await waitFor(() => {
|
|
507
|
+
expect(result.current[0]?.length).toBe(20)
|
|
508
|
+
expect(reRenders).toBe(2)
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
act(() => {
|
|
512
|
+
sql.batchDelete(['5', '10', '15'])
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
await waitFor(() => {
|
|
516
|
+
expect(result.current[0]?.length).toBe(17)
|
|
517
|
+
expect(result.current[0]?.find((p) => p.id === '5')).toBeUndefined()
|
|
518
|
+
expect(result.current[0]?.find((p) => p.id === '10')).toBeUndefined()
|
|
519
|
+
expect(result.current[0]?.find((p) => p.id === '15')).toBeUndefined()
|
|
520
|
+
expect(reRenders).toBe(3)
|
|
521
|
+
})
|
|
522
|
+
})
|
|
492
523
|
})
|
|
@@ -9,21 +9,22 @@ export interface CreateSqliteOptions<Document extends DocType> extends Omit<DbOp
|
|
|
9
9
|
readonly backend: Backend | Promise<Backend>
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export interface MutationItems {
|
|
13
|
-
mutations?: MutationResult[]
|
|
12
|
+
export interface MutationItems<Doc> {
|
|
13
|
+
mutations?: MutationResult<Doc>[]
|
|
14
14
|
removedAll?: boolean
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export interface SyncTable<Document extends DocType> {
|
|
18
|
-
readonly subscribe: (listener: (mutation: MutationItems) => void) => () => void
|
|
19
|
-
readonly set: (document: Document) => Promise<MutationResult
|
|
20
|
-
readonly batchSet: (documents: Document[]) => Promise<MutationResult[]>
|
|
18
|
+
readonly subscribe: (listener: (mutation: MutationItems<Document>) => void) => () => void
|
|
19
|
+
readonly set: (document: Document) => Promise<MutationResult<Document>>
|
|
20
|
+
readonly batchSet: (documents: Document[]) => Promise<MutationResult<Document>[]>
|
|
21
|
+
readonly batchDelete: (keys: Key[]) => Promise<MutationResult<Document>[]>
|
|
21
22
|
readonly get: <Selected = Document>(key: Key, selector?: (document: Document) => Selected) => Promise<Selected | undefined>
|
|
22
23
|
|
|
23
|
-
readonly delete: (key: Key) => Promise<MutationResult | undefined>
|
|
24
|
+
readonly delete: (key: Key) => Promise<MutationResult<Document> | undefined>
|
|
24
25
|
readonly search: <Selected = Document>(options?: SearchOptions<Document, Selected>) => AsyncIterableIterator<Selected>
|
|
25
26
|
readonly count: (options?: { where?: Where<Document> }) => Promise<number>
|
|
26
|
-
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult[]>
|
|
27
|
+
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult<Document>[]>
|
|
27
28
|
readonly clear: () => Promise<void>
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -53,8 +54,8 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
|
|
|
53
54
|
if (!unknownItems) {
|
|
54
55
|
return
|
|
55
56
|
}
|
|
56
|
-
const items = unknownItems as MutationItems[]
|
|
57
|
-
const merged: MutationItems = {}
|
|
57
|
+
const items = unknownItems as MutationItems<Document>[]
|
|
58
|
+
const merged: MutationItems<Document> = {}
|
|
58
59
|
for (const item of items) {
|
|
59
60
|
if (item.removedAll) {
|
|
60
61
|
merged.removedAll = true
|
|
@@ -76,11 +77,11 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
|
|
|
76
77
|
* Notify all subscribers of changes
|
|
77
78
|
* @param item The mutation items to notify subscribers about
|
|
78
79
|
*/
|
|
79
|
-
function handleChanges(item: MutationItems) {
|
|
80
|
+
function handleChanges(item: MutationItems<Document>) {
|
|
80
81
|
STATE_SCHEDULER.schedule(id, item)
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
const listeners = new Set<(mutation: MutationItems) => void>()
|
|
84
|
+
const listeners = new Set<(mutation: MutationItems<Document>) => void>()
|
|
84
85
|
|
|
85
86
|
const state: SyncTable<Document> = {
|
|
86
87
|
subscribe(listener) {
|
|
@@ -104,6 +105,12 @@ export function createSqliteState<Document extends DocType>(options: CreateSqlit
|
|
|
104
105
|
handleChanges({ mutations: changes })
|
|
105
106
|
return changes
|
|
106
107
|
},
|
|
108
|
+
async batchDelete(keys) {
|
|
109
|
+
const table = await getTable()
|
|
110
|
+
const changes = await table.batchDelete(keys)
|
|
111
|
+
handleChanges({ mutations: changes })
|
|
112
|
+
return changes
|
|
113
|
+
},
|
|
107
114
|
async delete(key) {
|
|
108
115
|
const table = await getTable()
|
|
109
116
|
const changes = await table.delete(key)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/* eslint-disable sonarjs/cognitive-complexity */
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-shadow */
|
|
5
5
|
/* eslint-disable no-shadow */
|
|
6
|
+
import type { Backend } from './backend'
|
|
6
7
|
import type { Table, DbOptions, DocType, Key, SearchOptions, MutationResult } from './table.types'
|
|
7
8
|
import { unicodeTokenizer, type FtsTokenizerOptions } from './tokenizer'
|
|
8
9
|
import type { Where } from './where'
|
|
@@ -181,10 +182,10 @@ export async function createTable<Document extends DocType>(options: DbOptions<D
|
|
|
181
182
|
|
|
182
183
|
if (existing.length > 0) {
|
|
183
184
|
await db.execute(`UPDATE ${tableName} SET data = ? WHERE key = ?`, [json, id])
|
|
184
|
-
return { key: id, op: 'update' }
|
|
185
|
+
return { key: id, op: 'update', document }
|
|
185
186
|
} else {
|
|
186
187
|
await db.execute(`INSERT INTO ${tableName} (key, data) VALUES (?, ?)`, [id, json])
|
|
187
|
-
return { key: id, op: 'insert' }
|
|
188
|
+
return { key: id, op: 'insert', document }
|
|
188
189
|
}
|
|
189
190
|
}
|
|
190
191
|
|
|
@@ -192,7 +193,7 @@ export async function createTable<Document extends DocType>(options: DbOptions<D
|
|
|
192
193
|
const rows = await db.select<Array<{ id: number }>>(`SELECT last_insert_rowid() AS id`)
|
|
193
194
|
const rowid = rows[0]?.id
|
|
194
195
|
if (typeof rowid !== 'number') throw new Error('Failed to retrieve last_insert_rowid()')
|
|
195
|
-
return { key: rowid, op: 'insert' }
|
|
196
|
+
return { key: rowid, op: 'insert', document }
|
|
196
197
|
},
|
|
197
198
|
|
|
198
199
|
async get<Selected = Document>(
|
|
@@ -211,9 +212,10 @@ export async function createTable<Document extends DocType>(options: DbOptions<D
|
|
|
211
212
|
return selector(document, { rowId: rowid, key: logicalKey }) as Selected
|
|
212
213
|
},
|
|
213
214
|
|
|
214
|
-
async delete(keyValue: Key) {
|
|
215
|
+
async delete(keyValue: Key, backendOverride?: Backend) {
|
|
216
|
+
const db = backendOverride ?? backend
|
|
215
217
|
const whereKey = hasUserKey ? `key = ?` : `rowid = ?`
|
|
216
|
-
await
|
|
218
|
+
await db.execute(`DELETE FROM ${tableName} WHERE ${whereKey}`, [keyValue])
|
|
217
219
|
const changed = await backend.select<Array<{ c: number }>>(`SELECT changes() AS c`)
|
|
218
220
|
if ((changed[0]?.c ?? 0) > 0) return { key: keyValue, op: 'delete' }
|
|
219
221
|
return
|
|
@@ -274,7 +276,7 @@ export async function createTable<Document extends DocType>(options: DbOptions<D
|
|
|
274
276
|
async deleteBy(where: Where<Document>) {
|
|
275
277
|
const whereSql = getWhereQuery<Document>(where, tableName)
|
|
276
278
|
const keyCol = hasUserKey ? 'key' : 'rowid'
|
|
277
|
-
const results: MutationResult[] = []
|
|
279
|
+
const results: MutationResult<Document>[] = []
|
|
278
280
|
|
|
279
281
|
await backend.transaction(async (tx) => {
|
|
280
282
|
const rows = await tx.select<Array<{ k: Key }>>(`SELECT ${keyCol} AS k FROM ${tableName} ${whereSql}`)
|
|
@@ -287,7 +289,7 @@ export async function createTable<Document extends DocType>(options: DbOptions<D
|
|
|
287
289
|
await tx.execute(`DELETE FROM ${tableName} WHERE ${keyCol} IN (${placeholders})`, chunk as unknown[])
|
|
288
290
|
}
|
|
289
291
|
|
|
290
|
-
for (const k of allKeys) results.push({ key: k, op: 'delete' })
|
|
292
|
+
for (const k of allKeys) results.push({ key: k, op: 'delete', document: undefined })
|
|
291
293
|
})
|
|
292
294
|
|
|
293
295
|
return results
|
|
@@ -298,7 +300,7 @@ export async function createTable<Document extends DocType>(options: DbOptions<D
|
|
|
298
300
|
},
|
|
299
301
|
|
|
300
302
|
async batchSet(documents: Document[]) {
|
|
301
|
-
const mutations: MutationResult[] = []
|
|
303
|
+
const mutations: MutationResult<Document>[] = []
|
|
302
304
|
await backend.transaction(async (tx) => {
|
|
303
305
|
for (const document of documents) {
|
|
304
306
|
const m = await table.set(document, tx)
|
|
@@ -307,6 +309,16 @@ export async function createTable<Document extends DocType>(options: DbOptions<D
|
|
|
307
309
|
})
|
|
308
310
|
return mutations
|
|
309
311
|
},
|
|
312
|
+
async batchDelete(keys: Key[]) {
|
|
313
|
+
const mutations: MutationResult<Document>[] = []
|
|
314
|
+
await backend.transaction(async (tx) => {
|
|
315
|
+
for (const key of keys) {
|
|
316
|
+
const m = await table.delete(key, tx)
|
|
317
|
+
if (m) mutations.push(m)
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
return mutations
|
|
321
|
+
},
|
|
310
322
|
}
|
|
311
323
|
|
|
312
324
|
return table
|
|
@@ -67,20 +67,35 @@ interface DbNotGeneric {
|
|
|
67
67
|
export type Key = string | number
|
|
68
68
|
|
|
69
69
|
export type MutationOp = 'insert' | 'update' | 'delete'
|
|
70
|
-
|
|
70
|
+
|
|
71
|
+
interface MutationResultBase<T> {
|
|
71
72
|
key: Key
|
|
72
73
|
op: MutationOp
|
|
74
|
+
document?: T
|
|
75
|
+
}
|
|
76
|
+
interface MutationResultDelete<T> extends MutationResultBase<T> {
|
|
77
|
+
key: Key
|
|
78
|
+
op: 'delete'
|
|
73
79
|
}
|
|
74
80
|
|
|
81
|
+
interface MutationResultUpdateInsert<T> extends MutationResultBase<T> {
|
|
82
|
+
key: Key
|
|
83
|
+
op: 'update' | 'insert'
|
|
84
|
+
document: T
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type MutationResult<T> = MutationResultDelete<T> | MutationResultUpdateInsert<T>
|
|
88
|
+
|
|
75
89
|
export interface Table<Document extends DocType> extends DbNotGeneric {
|
|
76
|
-
readonly set: (document: Document, backendOverride?: Backend) => Promise<MutationResult
|
|
77
|
-
readonly batchSet: (documents: Document[]) => Promise<MutationResult[]>
|
|
90
|
+
readonly set: (document: Document, backendOverride?: Backend) => Promise<MutationResult<Document>>
|
|
91
|
+
readonly batchSet: (documents: Document[]) => Promise<MutationResult<Document>[]>
|
|
92
|
+
readonly batchDelete: (keys: Key[]) => Promise<MutationResult<Document>[]>
|
|
78
93
|
readonly get: <Selected = Document>(key: Key, selector?: (document: Document) => Selected) => Promise<Selected | undefined>
|
|
79
94
|
|
|
80
|
-
readonly delete: (key: Key) => Promise<MutationResult | undefined>
|
|
95
|
+
readonly delete: (key: Key, backendOverride?: Backend) => Promise<MutationResult<Document> | undefined>
|
|
81
96
|
readonly search: <Selected = Document>(options?: SearchOptions<Document, Selected>) => AsyncIterableIterator<Selected>
|
|
82
97
|
readonly count: (options?: { where?: Where<Document> }) => Promise<number>
|
|
83
|
-
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult[]>
|
|
98
|
+
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult<Document>[]>
|
|
84
99
|
readonly clear: () => Promise<void>
|
|
85
100
|
}
|
|
86
101
|
|
package/src/sqlite/use-sqlite.ts
CHANGED
|
@@ -114,7 +114,7 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
|
|
|
114
114
|
let hasUpdate = false
|
|
115
115
|
const removeIndexes = new Set<number>()
|
|
116
116
|
for (const mutation of mutations) {
|
|
117
|
-
const { key, op } = mutation
|
|
117
|
+
const { key, op, document } = mutation
|
|
118
118
|
switch (op) {
|
|
119
119
|
case 'insert': {
|
|
120
120
|
newLength += 1
|
|
@@ -133,7 +133,7 @@ export function useSqliteValue<Document extends DocType, Selected = Document>(
|
|
|
133
133
|
if (keysIndex.current.has(key)) {
|
|
134
134
|
const index = keysIndex.current.get(key)
|
|
135
135
|
if (index !== undefined && itemsRef.current) {
|
|
136
|
-
const newItem =
|
|
136
|
+
const newItem = select ? select(document as Document) : (document as unknown as Selected)
|
|
137
137
|
const previousItem = itemsRef.current[index]
|
|
138
138
|
|
|
139
139
|
// 🆕 Only update & rerender if shallow comparison fails
|
|
@@ -4,21 +4,22 @@ import type { Where } from './table/where';
|
|
|
4
4
|
export interface CreateSqliteOptions<Document extends DocType> extends Omit<DbOptions<Document>, 'backend'> {
|
|
5
5
|
readonly backend: Backend | Promise<Backend>;
|
|
6
6
|
}
|
|
7
|
-
export interface MutationItems {
|
|
8
|
-
mutations?: MutationResult[];
|
|
7
|
+
export interface MutationItems<Doc> {
|
|
8
|
+
mutations?: MutationResult<Doc>[];
|
|
9
9
|
removedAll?: boolean;
|
|
10
10
|
}
|
|
11
11
|
export interface SyncTable<Document extends DocType> {
|
|
12
|
-
readonly subscribe: (listener: (mutation: MutationItems) => void) => () => void;
|
|
13
|
-
readonly set: (document: Document) => Promise<MutationResult
|
|
14
|
-
readonly batchSet: (documents: Document[]) => Promise<MutationResult[]>;
|
|
12
|
+
readonly subscribe: (listener: (mutation: MutationItems<Document>) => void) => () => void;
|
|
13
|
+
readonly set: (document: Document) => Promise<MutationResult<Document>>;
|
|
14
|
+
readonly batchSet: (documents: Document[]) => Promise<MutationResult<Document>[]>;
|
|
15
|
+
readonly batchDelete: (keys: Key[]) => Promise<MutationResult<Document>[]>;
|
|
15
16
|
readonly get: <Selected = Document>(key: Key, selector?: (document: Document) => Selected) => Promise<Selected | undefined>;
|
|
16
|
-
readonly delete: (key: Key) => Promise<MutationResult | undefined>;
|
|
17
|
+
readonly delete: (key: Key) => Promise<MutationResult<Document> | undefined>;
|
|
17
18
|
readonly search: <Selected = Document>(options?: SearchOptions<Document, Selected>) => AsyncIterableIterator<Selected>;
|
|
18
19
|
readonly count: (options?: {
|
|
19
20
|
where?: Where<Document>;
|
|
20
21
|
}) => Promise<number>;
|
|
21
|
-
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult[]>;
|
|
22
|
+
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult<Document>[]>;
|
|
22
23
|
readonly clear: () => Promise<void>;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
@@ -44,20 +44,32 @@ interface DbNotGeneric {
|
|
|
44
44
|
}
|
|
45
45
|
export type Key = string | number;
|
|
46
46
|
export type MutationOp = 'insert' | 'update' | 'delete';
|
|
47
|
-
|
|
47
|
+
interface MutationResultBase<T> {
|
|
48
48
|
key: Key;
|
|
49
49
|
op: MutationOp;
|
|
50
|
+
document?: T;
|
|
50
51
|
}
|
|
52
|
+
interface MutationResultDelete<T> extends MutationResultBase<T> {
|
|
53
|
+
key: Key;
|
|
54
|
+
op: 'delete';
|
|
55
|
+
}
|
|
56
|
+
interface MutationResultUpdateInsert<T> extends MutationResultBase<T> {
|
|
57
|
+
key: Key;
|
|
58
|
+
op: 'update' | 'insert';
|
|
59
|
+
document: T;
|
|
60
|
+
}
|
|
61
|
+
export type MutationResult<T> = MutationResultDelete<T> | MutationResultUpdateInsert<T>;
|
|
51
62
|
export interface Table<Document extends DocType> extends DbNotGeneric {
|
|
52
|
-
readonly set: (document: Document, backendOverride?: Backend) => Promise<MutationResult
|
|
53
|
-
readonly batchSet: (documents: Document[]) => Promise<MutationResult[]>;
|
|
63
|
+
readonly set: (document: Document, backendOverride?: Backend) => Promise<MutationResult<Document>>;
|
|
64
|
+
readonly batchSet: (documents: Document[]) => Promise<MutationResult<Document>[]>;
|
|
65
|
+
readonly batchDelete: (keys: Key[]) => Promise<MutationResult<Document>[]>;
|
|
54
66
|
readonly get: <Selected = Document>(key: Key, selector?: (document: Document) => Selected) => Promise<Selected | undefined>;
|
|
55
|
-
readonly delete: (key: Key) => Promise<MutationResult | undefined>;
|
|
67
|
+
readonly delete: (key: Key, backendOverride?: Backend) => Promise<MutationResult<Document> | undefined>;
|
|
56
68
|
readonly search: <Selected = Document>(options?: SearchOptions<Document, Selected>) => AsyncIterableIterator<Selected>;
|
|
57
69
|
readonly count: (options?: {
|
|
58
70
|
where?: Where<Document>;
|
|
59
71
|
}) => Promise<number>;
|
|
60
|
-
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult[]>;
|
|
72
|
+
readonly deleteBy: (where: Where<Document>) => Promise<MutationResult<Document>[]>;
|
|
61
73
|
readonly clear: () => Promise<void>;
|
|
62
74
|
}
|
|
63
75
|
export type MakeAllFieldAsRequired<T> = {
|