99import { filter , Observable } from 'rxjs' ;
1010import type { IDBClient } from './idb-client' ;
1111import type { IDBConnection } from './idb-connection' ;
12- import type { FireEvent , IDBChangeEvent } from './idb-events' ;
12+ import { generateID , type FireEvent , type IDBChangeEvent } from './idb-events' ;
1313import type { IDBTableSchema } from './idb-schema' ;
1414import { toResourceObject } from './to-resource-object' ;
1515
@@ -26,17 +26,21 @@ export type CreateIDBTableOptions<T extends Record<PropertyKey, any>> = {
2626 equal ?: ValueEqualityFn < T > ;
2727} ;
2828
29+ type AddValue < T , TKey extends keyof T > = Omit < T , TKey > & {
30+ [ K in TKey ] ?: IDBValidKey ;
31+ } ;
32+
2933/**
3034 * Represents a table handler for IndexedDB.
3135 * It provides methods to add, update, and remove items,
3236 * as well as to retrieve the table's data.
3337 * It extends ResourceRef to provide reactive data handling.
3438 * All updates happen optimistcally and are reverted in case of an error.
3539 */
36- export type IDBTable < T extends Record < PropertyKey , any > > = Omit <
37- ResourceRef < T [ ] > ,
38- 'set' | 'update'
39- > & {
40+ export type IDBTable <
41+ T extends Record < PropertyKey , any > ,
42+ TKey extends keyof T ,
43+ > = Omit < ResourceRef < T [ ] > , 'set' | 'update' > & {
4044 /**
4145 * The name of the table.
4246 */
@@ -46,26 +50,26 @@ export type IDBTable<T extends Record<PropertyKey, any>> = Omit<
4650 * @param value The item to add.
4751 * @returns A promise that resolves when the add operation is complete.
4852 */
49- add : ( value : T ) => Promise < void > ;
53+ add : ( value : AddValue < T , TKey > ) => Promise < T [ TKey ] > ;
5054 /**
5155 *
5256 * @param key The key of the item to update.
5357 * @param itemOrUpdater The new item or a function that takes the previous item and returns the new item.
5458 * @returns A promise that resolves when the update is complete.
5559 */
56- update : (
57- key : IDBValidKey ,
58- itemOrUpdater : T | ( ( prev : T ) => T ) ,
59- ) => Promise < void > ;
60+ update : ( key : T [ TKey ] , itemOrUpdater : T | ( ( prev : T ) => T ) ) => Promise < void > ;
6061 /**
6162 * Removes an item from the table by its key.
6263 * @param key The key of the item to remove.
6364 * @returns A promise that resolves when the remove operation is complete.
6465 */
65- remove : ( key : IDBValidKey ) => Promise < void > ;
66+ remove : ( key : T [ TKey ] ) => Promise < void > ;
6667} ;
6768
68- export function createNewTable < T extends Record < PropertyKey , any > > (
69+ export function createNewTable <
70+ T extends Record < PropertyKey , any > ,
71+ TKey extends keyof T ,
72+ > (
6973 tableName : string ,
7074 {
7175 client,
@@ -79,7 +83,7 @@ export function createNewTable<T extends Record<PropertyKey, any>>(
7983 fireEvent : FireEvent < T , IDBValidKey > ;
8084 events$ : Observable < IDBChangeEvent < T , IDBValidKey > > ;
8185 } ,
82- ) : IDBTable < T > {
86+ ) : IDBTable < T , TKey > {
8387 const rawTableData = toResourceObject (
8488 resource < T [ ] , IDBConnection > ( {
8589 params : ( ) => client . value ( ) ,
@@ -123,17 +127,35 @@ export function createNewTable<T extends Record<PropertyKey, any>>(
123127 } ,
124128 } ;
125129
126- const add = async ( item : T , fromEvent = false ) : Promise < void > => {
130+ const add = async (
131+ item : AddValue < T , TKey > ,
132+ fromEvent = false ,
133+ ) : Promise < T [ TKey ] > => {
127134 const prev = untracked ( tableData . value ) ;
128135
136+ let tempKey = ( item as any ) [ schema . primaryKey ] as T [ TKey ] | undefined ;
137+
138+ if ( schema . autoIncrement && tempKey === undefined && prev . length > 0 ) {
139+ const isString = typeof prev [ 0 ] ?. [ schema . primaryKey ] === 'string' ;
140+ const isNumber = typeof prev [ 0 ] ?. [ schema . primaryKey ] === 'number' ;
141+ if ( isString ) {
142+ tempKey = generateID ( ) as T [ TKey ] ;
143+ } else if ( isNumber ) {
144+ tempKey = ( Math . max (
145+ ...prev . map ( ( v ) => v [ schema . primaryKey ] as number ) ,
146+ ) + 1 ) as T [ TKey ] ;
147+ }
148+ }
149+
129150 try {
130- const tempKey = item [ schema . primaryKey ] ;
131- tableData . update ( ( cur ) => [ ...cur , item ] ) ;
132- let payload = item ;
151+ tableData . update ( ( cur ) => [ ...cur , item as T ] ) ;
152+
153+ let payload = item as T ;
154+
133155 if ( ! fromEvent ) {
134156 const key = await untracked ( client . value ) . add < T > (
135157 tableName ,
136- item ,
158+ item as T ,
137159 controller . signal ,
138160 ) ;
139161 if ( key !== tempKey ) {
@@ -146,11 +168,14 @@ export function createNewTable<T extends Record<PropertyKey, any>>(
146168 type : 'add' ,
147169 payload,
148170 } ) ;
171+ return key as T [ TKey ] ;
149172 }
173+ return tempKey as T [ TKey ] ;
150174 } catch ( err ) {
151175 if ( isDevMode ( ) )
152176 console . error ( `Error adding value to table ${ tableName } :` , err ) ;
153177 tableData . set ( prev ) ;
178+ return tempKey as T [ TKey ] ;
154179 }
155180 } ;
156181
@@ -225,9 +250,10 @@ export function createNewTable<T extends Record<PropertyKey, any>>(
225250 } ;
226251}
227252
228- export function createNoopTable < T extends Record < PropertyKey , any > > (
229- tableName : string ,
230- ) : IDBTable < T > {
253+ export function createNoopTable <
254+ T extends Record < PropertyKey , any > ,
255+ TKey extends keyof T ,
256+ > ( tableName : string ) : IDBTable < T , TKey > {
231257 const data = toResourceObject < T [ ] > (
232258 resource ( {
233259 loader : ( ) => Promise . resolve ( [ ] ) ,
@@ -239,7 +265,7 @@ export function createNoopTable<T extends Record<PropertyKey, any>>(
239265 return {
240266 ...data ,
241267 name : tableName ,
242- add : ( ) => Promise . resolve ( ) ,
268+ add : ( ) => Promise . resolve ( Math . random ( ) as T [ TKey ] ) ,
243269 update : ( ) => Promise . resolve ( ) ,
244270 remove : ( ) => Promise . resolve ( ) ,
245271 } ;
0 commit comments