@ngx-cache/fs-storage
Version:
Fs storage for ngx-cache (server platform)
213 lines (206 loc) • 7.08 kB
JavaScript
import { ɵɵinject, ɵɵdefineInjectable, ɵsetClassMetadata, Injectable } from '@angular/core';
import { Storage } from '@ngx-cache/core';
import { EventEmitter } from 'events';
import { statSync, readdirSync, mkdirSync, writeFileSync, readFileSync, rmdirSync, unlinkSync } from 'fs';
import { resolve, join } from 'path';
class FsStorageLoader {
}
class FsStorageStaticLoader {
constructor(providedSettings = {
path: './.cache',
quota: 5 * 1024 * 1024
}) {
this.providedSettings = providedSettings;
}
get path() {
return this.providedSettings.path;
}
get quota() {
return this.providedSettings.quota;
}
}
class FsEvent {
constructor(key, oldValue, newValue, pid, area = 'fs-storage') {
this.key = key;
this.oldValue = oldValue;
this.newValue = newValue;
this.pid = pid;
this.area = area;
}
}
class FsItemMetadata {
constructor(key, index) {
this.key = key;
this.index = index;
}
}
class FsStorageService extends Storage {
constructor(loader) {
super();
this.loader = loader;
this.instances = {};
this.path = resolve(this.loader.path);
this.quota = this.loader.quota;
if (this.instances[this.path]) {
return this.instances[this.path];
}
this.length = 0;
this.keys = [];
this.pid = `pid:${process.pid}`;
this.metadata = new Map();
this.bytesUsed = 0;
try {
let stat = statSync(this.path);
if (!stat.hasOwnProperty('isDirectory')) {
throw new Error(`A file exists at the location ${this.path} when trying to create/open localStorage`);
}
this.length = 0;
this.keys = readdirSync(this.path);
this.bytesUsed = 0;
const decodedKeys = [];
this.keys.forEach((key, index) => {
const decodedKey = decodeURIComponent(key);
decodedKeys.push(decodedKey);
const item = new FsItemMetadata(key, index);
this.metadata[decodedKey] = item;
stat = this.getStats(key);
if (!stat.hasOwnProperty('size')) {
item.size = stat.size;
this.metadata[decodedKey] = item;
this.bytesUsed += stat.size;
}
});
this.keys = decodedKeys;
this.length = this.keys.length;
}
catch (error) {
mkdirSync(this.path);
}
this.instances[this.path] = this;
}
setItem(key, value) {
const hasListeners = EventEmitter.listenerCount(this, 'fs-storage');
const oldValue = hasListeners ? this.getItem(key) : undefined;
let item = this.metadata[key];
const oldLength = item ? item.size : 0;
if (this.bytesUsed - oldLength + Number(value.toString().length) > this.quota) {
throw new Error(`Disk quota (${this.quota / 1024}KB) has been reached!`);
}
const encodedKey = encodeURIComponent(key);
const filename = join(this.path, encodedKey);
writeFileSync(filename, value.toString(), 'utf8');
if (!item) {
item = new FsItemMetadata(encodedKey, this.keys.push(key) - 1);
item.size = value.toString().length;
this.length += 1;
this.metadata[key] = item;
this.bytesUsed += value.toString().length;
}
if (!hasListeners) {
return false;
}
const e = new FsEvent(key, oldValue, value, this.pid);
return this.emit('fs-storage', e);
}
getItem(key) {
const item = this.metadata[key];
if (item) {
const filename = join(this.path, item.key);
try {
return readFileSync(filename, 'utf8');
}
catch (error) {
this.removeItem(key);
}
}
return undefined;
}
removeItem(key) {
const hasListeners = EventEmitter.listenerCount(this, 'fs-storage');
const oldValue = hasListeners ? this.getItem(key) : undefined;
const item = this.metadata[key];
if (item) {
delete this.metadata[key];
this.length -= 1;
this.bytesUsed -= item.size;
this.keys.splice(item.index, 1);
const metadataRef = this.metadata;
metadataRef.forEach((k) => {
const i = this.metadata[k];
if (i.index > item.index) {
i.index -= 1;
}
});
const itemPath = join(this.path, item.key);
try {
this.deletePath(itemPath);
}
catch (error) {
}
if (!hasListeners) {
return false;
}
const e = new FsEvent(key, oldValue, undefined, this.pid);
return this.emit('fs-storage', e);
}
return false;
}
key(index) {
return this.keys[index];
}
clear() {
this.deleteDirectory(this.path);
this.length = 0;
this.keys = [];
this.metadata = new Map();
this.bytesUsed = 0;
const hasListeners = EventEmitter.listenerCount(this, 'fs-storage');
if (!hasListeners) {
return false;
}
const e = new FsEvent(undefined, undefined, undefined, this.pid);
return this.emit('fs-storage', e);
}
getStats(key) {
const filename = join(this.path, encodeURIComponent(key));
try {
return statSync(filename);
}
catch (error) {
return undefined;
}
}
deleteDirectory(dirPath) {
const contents = readdirSync(dirPath);
contents.forEach((path) => {
const joined = join(dirPath, path);
this.deletePath(joined);
});
}
deletePath(path) {
const isDirectory = statSync(path).isDirectory();
if (isDirectory) {
this.deleteDirectory(path);
rmdirSync(path);
}
else {
unlinkSync(path);
}
}
deleteInstance() {
delete this.instances[this.path];
this.deletePath(this.path);
this.length = 0;
this.keys = [];
this.metadata = new Map();
this.bytesUsed = 0;
}
}
FsStorageService.ɵfac = function FsStorageService_Factory(t) { return new (t || FsStorageService)(ɵɵinject(FsStorageLoader)); };
FsStorageService.ɵprov = ɵɵdefineInjectable({ token: FsStorageService, factory: FsStorageService.ɵfac });
(function () { ɵsetClassMetadata(FsStorageService, [{
type: Injectable
}], function () { return [{ type: FsStorageLoader }]; }, null); })();
const fsStorageFactory = () => new FsStorageStaticLoader();
export { FsStorageLoader, FsStorageService, FsStorageStaticLoader, fsStorageFactory };
//# sourceMappingURL=ngx-cache-fs-storage.js.map