Files
scrypted/server/src/level.ts
2022-10-26 11:44:06 -07:00

297 lines
9.9 KiB
TypeScript

// Type definitions for abstract-leveldown 5.0
// Project: https://github.com/Level/abstract-leveldown
// Definitions by: Meirion Hughes <https://github.com/MeirionHughes>
// Daniel Byrne <https://github.com/danwbyrne>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
export interface AbstractOptions {
// wtf is this?
readonly [k: string]: any;
}
export type ErrorCallback = (err: Error | undefined) => void;
export type ErrorValueCallback<V> = (err: Error | undefined, value: V) => void;
export type ErrorKeyValueCallback<K, V> = (err: Error | undefined, key: K, value: V) => void;
export interface AbstractOpenOptions extends AbstractOptions {
createIfMissing?: boolean;
errorIfExists?: boolean;
}
export interface AbstractGetOptions extends AbstractOptions {
asBuffer?: boolean;
}
export interface LevelDocument {
_id?: any;
_documentType?: string;
}
export interface LevelDocumentConstructor<T extends LevelDocument> {
new(): T;
}
export interface AbstractLevelDOWN<K = any, V = any> /* extends AbstractOptions */ {
open(cb?: ErrorCallback): Promise<void>;
open(options: AbstractOpenOptions, cb?: ErrorCallback): Promise<void>;
close(cb?: ErrorCallback): void;
get(key: K, cb?: ErrorValueCallback<V>): Promise<V>;
get(key: K, options: AbstractGetOptions, cb?: ErrorValueCallback<V>): Promise<V>;
put(key: K, value: V, cb?: ErrorCallback): Promise<void>;
put(key: K, value: V, options: AbstractOptions, cb?: ErrorCallback): Promise<void>;
del(key: K, cb?: ErrorCallback): Promise<void>;
del(key: K, options: AbstractOptions, cb?: ErrorCallback): Promise<void>;
batch(): AbstractChainedBatch<K, V>;
batch(array: ReadonlyArray<AbstractBatch<K, V>>, cb?: ErrorCallback): AbstractChainedBatch<K, V>;
batch(
array: ReadonlyArray<AbstractBatch<K, V>>,
options: AbstractOptions,
cb?: ErrorCallback,
): AbstractChainedBatch<K, V>;
iterator(options?: AbstractIteratorOptions<K>): AbstractIterator<K, V>;
[Symbol.asyncIterator](): AsyncIterator<{ key: K, value: V }>;
nextId(): number;
tryGet<T extends LevelDocument>(documentConstructor: LevelDocumentConstructor<T>, _id: any, options?: AbstractGetOptions): Promise<T | undefined>;
getAll<T extends LevelDocument>(documentConstructor: LevelDocumentConstructor<T>, options?: AbstractGetOptions): AsyncIterable<T>;
upsert<T extends LevelDocument>(value: T, options?: AbstractOptions): Promise<T>;
remove<T extends LevelDocument>(value: T): Promise<void>;
removeId<T extends LevelDocument>(documentConstructor: LevelDocumentConstructor<T>, _id: any): Promise<void>;
removeAll<T extends LevelDocument>(documentConstructor: LevelDocumentConstructor<T>): Promise<void>;
getCount<T extends LevelDocument>(documentConstructor: LevelDocumentConstructor<T>, options?: AbstractGetOptions): Promise<number>;
}
export interface AbstractLevelDOWNConstructor {
// tslint:disable-next-line no-unnecessary-generics
new <K = any, V = any>(location: string): AbstractLevelDOWN<K, V>;
// tslint:disable-next-line no-unnecessary-generics
<K = any, V = any>(location: string): AbstractLevelDOWN<K, V>;
}
export interface AbstractIteratorOptions<K = any> extends AbstractOptions {
gt?: K;
gte?: K;
lt?: K;
lte?: K;
reverse?: boolean;
limit?: number;
keys?: boolean;
values?: boolean;
keyAsBuffer?: boolean;
valueAsBuffer?: boolean;
}
export type AbstractBatch<K = any, V = any> = PutBatch<K, V> | DelBatch<K, V>;
export interface PutBatch<K = any, V = any> {
readonly type: 'put';
readonly key: K;
readonly value: V;
}
export interface DelBatch<K = any, V = any> {
readonly type: 'del';
readonly key: K;
}
export interface AbstractChainedBatch<K = any, V = any> extends AbstractOptions {
put: (key: K, value: V) => this;
del: (key: K) => this;
clear: () => this;
write(cb?: ErrorCallback): any;
write(options: any, cb?: ErrorCallback): any;
}
export interface AbstractChainedBatchConstructor {
// tslint:disable-next-line no-unnecessary-generics
new <K = any, V = any>(db: any): AbstractChainedBatch<K, V>;
// tslint:disable-next-line no-unnecessary-generics
<K = any, V = any>(db: any): AbstractChainedBatch<K, V>;
}
export interface AbstractIterator<K, V> extends AbstractOptions {
db: AbstractLevelDOWN<K, V>;
next(cb?: ErrorKeyValueCallback<K, V>): this;
end(cb?: ErrorCallback): void;
}
export interface AbstractIteratorConstructor {
// tslint:disable-next-line no-unnecessary-generics
new <K = any, V = any>(db: any): AbstractIterator<K, V>;
// tslint:disable-next-line no-unnecessary-generics
<K = any, V = any>(db: any): AbstractIterator<K, V>;
}
export interface Level extends AbstractLevelDOWN {
readonly location: string;
readonly prefix: string;
readonly version: string | number;
destroy(location: string, cb?: (err: Error | undefined) => void): void;
destroy(location: string, prefix: string, cb?: (err: Error | undefined) => void): void;
}
interface LevelOptions {
readonly prefix?: string;
readonly version?: string | number;
}
interface LevelConstructor {
new(location: string, options?: LevelOptions): Level;
(location: string, options?: LevelOptions): Level;
}
declare const Level: LevelConstructor;
const level = require('level') as LevelConstructor;
function createLevelDocument(documentConstructor: any, json: any) {
const doc = new documentConstructor();
Object.assign(doc, JSON.parse(json));
return doc;
}
const wrapped = (location: string, options?: LevelOptions) => {
const ret = level(location, options);
ret.tryGet = async (documentConstructor: any, _id: any, options?: AbstractGetOptions): Promise<any> => {
try {
const _documentType = documentConstructor.name;
const key = `${_documentType}/${_id}`;
const json = await ret.get(key, options);
return createLevelDocument(documentConstructor, json);
}
catch (e) {
}
}
const iterable = {
async*[Symbol.asyncIterator]() {
const iterator = ret.iterator();
try {
while (true) {
const { key, value } = await new Promise<{ key: any, value: any }>((resolve, reject) => {
iterator.next((err, key, value) => {
if (err) {
reject(err);
}
else {
resolve({ key, value });
}
})
});
if (key == null && value == null)
break;
yield {
key,
value,
}
}
}
finally {
await new Promise(resolve => iterator.end(resolve));
}
}
};
ret[Symbol.asyncIterator] = iterable[Symbol.asyncIterator] as any;
ret.getAll = (documentConstructor: any, options?: AbstractGetOptions): AsyncIterable<any> => {
const _documentType = documentConstructor.name;
const prefix = `${_documentType}/`;
return {
async*[Symbol.asyncIterator]() {
for await (const entry of ret) {
if (entry.key.startsWith(prefix)) {
const doc = createLevelDocument(documentConstructor, entry.value);
if (doc._documentType === _documentType) {
yield doc;
}
}
}
}
}
}
ret.getCount = async (documentConstructor: any, options?: AbstractGetOptions): Promise<any> => {
let count = 0;
for await (const doc of ret.getAll(documentConstructor)) {
count++;
}
return count;
}
let curId: number;
const oldOpen = ret.open.bind(ret);
(ret as any).open = async (...args: any) => {
try {
curId = parseInt(await ret.get('_id'));
}
catch (e) {
}
if (!curId)
curId = 0;
return oldOpen(...args);
}
ret.nextId = () => {
if (typeof curId !== 'number')
throw new Error('curId is not a number');
return ++curId;
}
const saveId = async () => {
return ret.put("_id", curId);
}
ret.upsert = async (value: LevelDocument, options?: AbstractOptions): Promise<any> => {
const _documentType = value.constructor.name;
if (!value._id)
value._id = ret.nextId();
await saveId();
value._documentType = _documentType;
const key = `${_documentType}/${value._id}`;
await ret.put(key, JSON.stringify(value), options);
return value;
};
ret.remove = async (value: LevelDocument) => {
const _documentType = value.constructor.name;
let { _id } = value;
const key = `${_documentType}/${_id}`;
await ret.del(key);
}
ret.removeId = async (documentConstructor: LevelDocumentConstructor<any>, _id: any) => {
const _documentType = documentConstructor.name;
const key = `${_documentType}/${_id}`;
await ret.del(key);
}
ret.removeAll = async (documentConstructor: LevelDocumentConstructor<any>) => {
const _documentType = documentConstructor.name;
const prefix = `${_documentType}/`;
for await (const entry of ret) {
if (entry.key.startsWith(prefix)) {
const doc = createLevelDocument(documentConstructor, entry.value);
if (doc._documentType === _documentType) {
await ret.del(entry.key);
}
}
}
}
return ret;
};
export default wrapped as LevelConstructor;