Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 43x 43x 43x 43x 43x 25x 25x 25x 25x 25x 18x 18x 18x 3x 3x 3x 36x 17x 17x 68x 68x 26x 42x 17x 15x 15x 15x 15x 15x | import { EMPTY, Observable, from } from 'rxjs';
import { Logger } from 'ts-log';
import { toPouchDbDoc } from './util';
import PouchDB from 'pouchdb';
const FETCH_ALL_PAGE_SIZE = 100;
export abstract class PouchDbStore<T extends {}> {
destroyed = false;
protected idle: Promise<void> = Promise.resolve();
protected readonly logger: Logger;
public readonly db: PouchDB.Database<T>;
constructor(public dbName: string, logger: Logger) {
this.logger = logger;
this.db = new PouchDB<T>(dbName, { auto_compaction: true });
}
/**
* Only used internally and for cleaning up after tests.
* If you need to use this for other purposes, consider adding clear() or destroy() to stores interfaces.
*/
async clearDB(): Promise<void> {
const docs = await this.fetchAllDocs();
await this.db.bulkDocs(
docs.map(
(row) =>
({
_deleted: true,
_id: row.id,
_rev: row.value.rev
} as unknown as T)
)
);
}
/** Might all destroy other stores, if the underlying PouchDb database is shared. */
destroy(): Observable<void> {
if (!this.destroyed) {
this.destroyed = true;
return from(this.db.destroy());
}
return EMPTY;
}
protected toPouchDbDoc(obj: T): T {
return toPouchDbDoc(obj) as T;
}
protected async getRev(docId: string) {
const existingDoc = await this.db.get(docId).catch(() => void 0);
return existingDoc?._rev;
}
async fetchAllDocs(
options?: Omit<Partial<PouchDB.Core.AllDocsWithinRangeOptions>, 'limit'>
): Promise<PouchDB.Core.AllDocsResponse<T>['rows']> {
const response = await this.db.allDocs({ ...options, limit: FETCH_ALL_PAGE_SIZE });
if (response && response.rows.length > 0) {
return [
...response.rows,
...(await this.fetchAllDocs({
...options,
skip: 1,
startkey: response.rows[response.rows.length - 1].id
}))
];
}
return response.rows || [];
}
protected forcePut(docId: string, doc: T) {
if (this.destroyed) return EMPTY;
const serializableDoc = this.toPouchDbDoc(doc);
return from(
(this.idle = this.idle
.then(async () => {
const pouchDbDoc = {
_id: docId,
_rev: await this.getRev(docId),
...serializableDoc
};
try {
await this.db.put(pouchDbDoc, { force: true });
} catch (error) {
this.logger.warn(`PouchDbStore(${this.dbName}): failed to forcePut`, pouchDbDoc, error);
}
})
.catch(() => void 0))
);
}
}
|