Created
August 10, 2021 14:46
-
-
Save kissarat/1fb7d5dbd03831b7f365ac07b52b9f86 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// import { EventEmitter } from "common.partover/src/emitter" | |
const reduce = (items = [], reducer = (item, i) => ({ number: i, ...item }), initial = {}) => items.reduce((acc, { name, ...item }, i) => { | |
acc[name] = reducer(item, i, name) | |
return acc | |
}, initial) | |
const produce = (object, getKeys = Object.keys(object)) => getKeys(object).map(name => ({ | |
name, | |
...object[name] | |
})) | |
const RepositoryStatus = { | |
Created: 'created', | |
Upgrade: 'upgrade', | |
Ready: 'ready' | |
} | |
class Repository { | |
constructor(schema, stores = {}) { | |
this.schema = schema | |
this.stores = stores || {} | |
this.init() | |
} | |
get name() { | |
return this.schema.name || 'test' | |
} | |
get version() { | |
return this.schema.version || 1 | |
} | |
addStore(store) { | |
this.stores[store.name] = store | |
} | |
eachStore(cb) { | |
for (const name in this.stores) { | |
cb(this.stores[name], name) | |
} | |
} | |
createStore(schema) { | |
const store = new RepositoryStore(schema) | |
} | |
open() { | |
return new Promise((resolve, reject) => { | |
this.request = indexedDB.open(this.name, this.version) | |
this.request.onerror = reject | |
this.request.onupgradeneeded = () => { | |
this.upgrade(this.request.result, this.version) | |
} | |
this.onsuccess = () => { | |
this.database = this.request.database | |
this.request = null | |
resolve(this) | |
} | |
}) | |
} | |
init() { | |
if (this.schema.stores) { | |
for (const name in this.schema.stores) { | |
if (!this.stores[name]) { | |
this.createStore(name, this.schema.stores) | |
} | |
} | |
} | |
} | |
upgrade(database, version) { | |
this.eachStore(store => store.upgrade(this, database, version)) | |
} | |
ready() { | |
this.eachStore(store => store.ready(this)) | |
} | |
static async open(schema, stores = {}) { | |
try { | |
const repository = new Repository(schema, stores) | |
await repository.open() | |
repository.ready() | |
Repository.instance = repository | |
return repository | |
} catch (error) { | |
Repository.error = error | |
throw error | |
} | |
} | |
} | |
const RepositoryStoreSchema = { | |
type: 'object', | |
required: [], | |
indexes: {}, | |
additionalProperties: false, | |
properties: {} | |
} | |
const createRepositoryStoreSchema = (properties = [], extra = { indexes: [] }) => { | |
const indexesDicts = reduce(indexes) | |
const requiredPropeperties = [] | |
const columns = reduce(properties, ({ required, primary = false, index, spare = false, unique = false, ...item }, i, name) => { | |
if (required) { | |
requiredPropeperties.push(name) | |
} | |
if (true === index) { | |
indexesDicts[name] = { unique, spare, primary } | |
} | |
return { | |
...item, | |
number: i | |
} | |
}) | |
return ({ | |
type: 'object', | |
required: requiredPropeperties, | |
indexes: indexesDicts, | |
properties: columns, | |
...extra | |
}) | |
} | |
class RepositoryStore { | |
constructor(name, schema = createRepositoryStoreSchema(), records = []) { | |
this.name = name | |
this.schema = schema, | |
this.records = records | |
this.fire(RepositoryStatus.Created) | |
} | |
fire(status) { | |
this.status = status | |
// this.emit(status, { target: this }) | |
} | |
get indexNames() { | |
return Object.keys(this.schema.indexes) | |
} | |
get indexSchema() { | |
return produce(this.schema.indexes) | |
} | |
get propertySchema() { | |
return produce(this.schema.properties) | |
} | |
upgrade(repository, database = repository.request.database, version = repository.version) { | |
this.fire(RepositoryStatus.Upgrade) | |
const indexSchema = this.indexSchema | |
this.store = database.createObjectStore(this.name, { keyPath: indexSchema.find(index => index.primary).name }) | |
this.indexes = {} | |
for (const { name, primary, unique } of indexSchema) { | |
if (!primary) { | |
this.indexes[name] = this.store.createIndex(name, name, { unique }) | |
} | |
} | |
this.write(this.records) | |
} | |
ready(repository) { | |
this.fire(RepositoryStatus.Ready, { target: this, repository }) | |
} | |
write(records) { | |
for (const record of records) { | |
store.put(record) | |
} | |
} | |
} | |
class RepositoryTransaction { | |
constructor(repository, storeName, readonly = false) { | |
this.repository = repository | |
if (!storeName) { | |
this.storeName = storeName | |
} | |
this.readonly = readonly | |
} | |
store(storeName = this.storeName) { | |
if (!this.transaction) { | |
this.start(storeName) | |
} | |
return this.transaction.objectStore(storeName) | |
} | |
index(key = this.key, storeName = this.objectStore) { | |
return this | |
.store(storeName || this.objectStore) | |
.index(key) | |
} | |
derive(options) { | |
return Object.assign(Object.create(this), options) | |
} | |
start(storeName = this.storeName, readonly = this.readonly) { | |
this.transaction = this.repository.database.transaction(storeName, readonly ? 'readonly' : 'readwrite') | |
return this.transaction | |
} | |
walk(key, cb) { | |
return new Promise((resolve, reject) => { | |
const request = index.openCursor(IDBKeyRange.only(key)) | |
request.onsuccess = () => { | |
try { | |
const cursor = request.result | |
if (cursor) { | |
cb(cursor) | |
} else { | |
resolve(request) | |
} | |
} catch (err) { | |
cursor.close() | |
reject(err) | |
} | |
} | |
request.onerror = reject | |
}) | |
} | |
async find(key = 'id', predicate = item => true, next = () => { }) { | |
await this.walk(key, cursor => { | |
if (predicate(cursor.value)) { | |
next(cursor.value) | |
} | |
}) | |
} | |
async findBy(key, value, next) { | |
const indexSchema = this.store().schema.indexes[key] | |
if (indexSchema.unique) { | |
this.index(key) | |
} | |
await this.find( | |
'id', | |
item => item[key] === value, | |
next | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment