IndexedDB.ts Ekle

This commit is contained in:
saqut 2024-02-06 14:29:42 +03:00
parent 427822c478
commit d462ff4f69
1 changed files with 463 additions and 0 deletions

463
IndexedDB.ts Normal file
View File

@ -0,0 +1,463 @@
interface ModelIterface {
name:string;
version:number;
tables : TableInterface[]
}
interface TableInterface {
name:string;
key:string,
columns:string[]
}
/**
* Browser uyumluluğu için yerel eventleri kullanmak yerine manual event sistemi yazılmıştır
*/
class EventTarget
{
/**
* Olayları tutan nesne
*/
public _events : Map<string,Function[]> = new Map();
/**
* Olay gerçekleştiğinde gerçekleşmesi için yeni işlev kaydı açar
* @param {string} event olayın türü
* @param {Function} callback olay gerçekleştiğinde çağrılacak fonksiyon
*/
public on(event:string, callback:Function)
{
if(this._events.has(event))
{
this._events.get(event).push(callback)
}else{
this._events.set(event,[callback])
}
}
/**
* on ile aynı, eski tarayıcılar hala addEventListener kullanıyor
*/
public addEventListener(event:string, callback:Function)
{
this.on(event, callback)
}
/**
* Browserlar için olay tetikleyicisi
*/
public dispatchEvent(event:Event)
{
this.emitEvent(event.type, event)
}
/**
*
* @param {string} type gerçekleşen olay türü
* @param {...any[]} args olay gerçekleştiğinde çalışacak işlevler gönderilecek argümanlar
*/
public emitEvent(type:string, ...args)
{
let events = this._events.get(type);
if(events) for (const callbacks of events) {
callbacks(...args)
}
}
}
export class IndexedDB extends EventTarget
{
/**
* Veritabanının hazır olup olmadığını tutar
*/
public IsReady : Boolean = false;
/**
* Migration için tabloların yapısı tutulan veri
*/
public Model : ModelIterface;
/**
* IndexedDB verisi
*/
private DBRequest : IDBOpenDBRequest;
/**
* IndexedDB veritabanı işleci
*/
private DB : IDBDatabase;
/**
* IndexedDB yeni örneğinde sistemin desteklediği API'yi yapılandırıyoruz
*/
public constructor()
{
super()
if(!IndexedDB.Engine)
{
IndexedDB.PolyfillCompalibity()
}
}
/**
* Veritabanı ık ve işlemlere hazır olduğu anda çalıştırılır
*/
public scope(func: Function)
{
if(this.IsReady)
{
return func()
}
this.addEventListener("load",() => func())
}
/**
* Modeller hazır olduğunda bağlanmak için kullanılır
*/
public async connect()
{
this.IsReady = false;
this.DBRequest = IndexedDB.Engine.open(this.Model.name, this.Model.version || 0);
this.DBRequest.addEventListener("upgradeneeded", this.executeMigrate.bind(this));
new IEventLocate<IDBDatabase>(this.DBRequest).sync().then(async db => {
this.DB = db;
this.IsReady = true;
this.dispatchEvent(new Event("load"));
})
}
/**
* IndexedDB'de yeni tablolar ve kolonlarını açmak için çalıştırılır
*/
public executeMigrate()
{
let db : IDBDatabase = this.DBRequest.result;
for(let e of this.Model.tables)
{
let objectStore : IDBObjectStore;
if(!db.objectStoreNames.contains(e.name))
{
objectStore = db.createObjectStore(e.name, {keyPath:e.key});
}else{
objectStore = this.DBRequest.transaction.objectStore(e.name);
}
e.columns.forEach(name => {
if(!objectStore.indexNames.contains(name))
{
objectStore.createIndex(name, name)
}
})
let indexes = Array.from(objectStore.indexNames);
for(let indexName of indexes)
{
if(!e.columns.includes(indexName))
{
objectStore.deleteIndex(indexName)
}
}
}
//
for (const objectStoreName of Array.from(db.objectStoreNames)) {
let isExists = this.Model.tables.filter(e => e.name == objectStoreName).length != 0;
if(!isExists)
{
db.deleteObjectStore(objectStoreName);
}
}
this.dispatchEvent(new Event("migrate"));
}
/**
* IndexedDB'de belirli bir yapıda tablo yapısı oluşturur ve değiştirir
*/
public migrate(mi:ModelIterface)
{
this.Model = mi;
}
/**
* Tabloya veri ekler
*/
public async add(tablename:string, data:{[key:string]:any})
{
if(!this.IsReady) return;
let store = this.DB.transaction([tablename], "readwrite").objectStore(tablename).add(data);
return await (new IEventLocate<IDBValidKey>(store)).sync();
}
/**
* Tablodaki tüm verileri temizler
*/
public async clear(tablename:string)
{
if(!this.IsReady) return;
let store = this.DB.transaction([tablename], "readwrite").objectStore(tablename).clear();
return await (new IEventLocate<undefined>(store)).sync();
}
/**
* Tablodaki verilerin sayısını döner
*/
public async count(tablename:string)
{
if(!this.IsReady) return;
let store = this.DB.transaction([tablename], "readwrite").objectStore(tablename).count();
return await (new IEventLocate<number>(store)).sync();
}
/**
* Tablo bir kayıt siler
*/
public async delete(tablename:string, name:string)
{
if(!this.IsReady) return;
let store = this.DB.transaction([tablename], "readwrite").objectStore(tablename).delete(name);
return await (new IEventLocate <undefined> (store)).sync();
}
/**
* Tablodaki bir kaydı okur
*/
public async get(tablename:string, index:string)
{
if(!this.IsReady) return;
let store = this.DB.transaction([tablename], "readwrite").objectStore(tablename).get(index);
return await (new IEventLocate <{[key:string]:any}> (store)).sync();
}
/**
* Tablodaki bir kaydı okur
*/
public async getFrom(tablename:string, column:string, index:string)
{
if(!this.IsReady) return;
let store = this.DB.transaction([tablename], "readwrite").objectStore(tablename).index(column).get(index);
return await (new IEventLocate <{[key:string]:any}> (store)).sync();
}
/**
* Tablodaki tüm kayıtları alır
*/
public async getAll(tablename:string)
{
if(!this.IsReady) return;
let store = this.DB.transaction([tablename], "readwrite").objectStore(tablename).getAll();
return await (new IEventLocate <{[key:string]:any}[]> (store)).sync();
}
/**
* Tabloda veri yoksa oluşturur varsa günceller
*/
public async save(tablename:string, key:string, data:{[key:string]:any})
{
if(!this.IsReady) return;
let record = await this.get(tablename, key);
if(record)
{
await this.put(tablename, data);
}else{
await this.add(tablename, data);
}
}
/**
* Tabloda bir veriyi günceller
*/
public async put(tablename:string,data:{[key:string]:any})
{
if(!this.IsReady) return;
let store = this.DB.transaction([tablename], "readwrite").objectStore(tablename).put(data);
return await (new IEventLocate <IDBValidKey> (store)).sync();
}
/**
* Tablodaki tüm verileri tek tek verir
*/
public async each(tablename:string, callback:(value:Object,next:Function, stop:Function, primaryKey:any) => Promise<any>)
{
let store = this.DB.transaction([tablename],"readonly").objectStore(tablename);
let cursor = store.openCursor();
await new Promise(ok => {
cursor.onsuccess = function(){
if(cursor.result !== null)
{
let data = cursor.result.value;
try{
callback(
data,
() => cursor.result.continue(),
() => ok(void 0),
cursor.result.primaryKey
)
}catch{
ok(void 0)
}
}
else
{
ok(void 0)
}
};
})
}
/**
* Tablodaki tüm verileri tek tek gezer ve aktif menipülasyon sağlar
*/
public async subfilter(
tablename:string,
filterCallback: (value:Object, stop:Function) => Promise<Boolean>,
mapCallback: (value:Object) => Promise<any>,
resultCallback?: (value:any[]) => any,
) : Promise<any[]>
{
let pool : string[] = [];
let mappedObj : any[] = [];
await this.each(tablename, async (value, next, stop, id: string) => {
if(await filterCallback(
value,
stop
)){
pool.push(id);
}
next();
});
for (const item of pool)
{
let value = this.get(tablename, item);
mappedObj.push(
await mapCallback(value)
)
}
if(typeof resultCallback == "function")
{
resultCallback(mappedObj)
}
return mappedObj
}
/**
* Tablodaki tüm verileri tek tek verir
*/
public async countFilter(
tablename:string,
filterCallback: (value:Object, stop:Function) => Promise<Boolean>,
stopFindedOne : boolean = false
) : Promise<number>
{
let pool : number = 0;
await this.each(tablename, async (value, next, stop) => {
if(await filterCallback(
value,
stop
)){
pool++;
if(stopFindedOne)
{
stop()
}
}
next();
});
return pool
}
/**
* IndexedDB bağlantısını sonlandırır
*/
public close()
{
if(!this.IsReady) return;
this.dispatchEvent(new Event("close"));
this.DB.close();
this.IsReady = false;
}
/**
* IndexedDB okuma yazma için kullanılan api
*/
public static Engine : IDBFactory;
/**
* Farklı browserlarda uyumluluk için IndexedDB farklı isimlerde çağrılabilir
*/
public static PolyfillCompalibity()
{
if('indexedDB' in window)
{
IndexedDB.Engine = window['indexedDB']
}
if('mozIndexedDB' in window)
{
IndexedDB.Engine = window['mozIndexedDB'] as IDBFactory
}
if('webkitIndexedDB' in window)
{
IndexedDB.Engine = window['webkitIndexedDB'] as IDBFactory
}
if('msIndexedDB' in window)
{
IndexedDB.Engine = window['msIndexedDB'] as IDBFactory
}
}
}
interface ISheel{
onsuccess : (e:any) => any;
onerror : (e:any) => any;
}
/**
* Başarılı olduğunda onsuccess başarısız olduğunda onerror döndüren yapıları
* javascript ES6 tarafında async/await yapısına dönüştürür böylece try-catch kullanılarak yakalanabilir
*/
class IEventLocate<Type>
{
private result : {
then:any[],
catch:any[],
finally:any[]
} = {
then:[],
catch:[],
finally:[]
};
public events : {[key:string]:Function[]}= {
then: [],
catch: [],
finally: []
};
public constructor(sheel:ISheel)
{
sheel.onerror = (event) => {
this.result.catch = [event.target, event];
this.events.catch.forEach(e => {
e.call(event.target, event.target?.result);
})
this.events.finally.forEach(e => {
e.call(event.target, event.target?.result);
})
}
sheel.onsuccess = (event) => {
this.result.then = [event.target, event];
this.events.then.forEach(e => {
e.call(event.target, event.target?.result);
})
}
}
public sync() : Promise<Type>
{
return new Promise((resolve, reject) => {
this.then(resolve);
this.catch(reject);
})
}
public then(e:any)
{
if(this.result.then.length)
{
e.call(this.result.then[0],this.result.then[1])
}else{
this.events.then.push(e);
}
}
public catch(e:any)
{
if(this.result.catch.length)
{
e.call(this.result.catch[0],this.result.catch[1])
}else{
this.events.catch.push(e);
}
}
public finally(e:any)
{
if(this.result.finally.length)
{
e.call(this.result.finally[0],this.result.finally[1])
}else{
this.events.finally.push(e);
}
}
}