@foxpage/foxpage-manager
Version:
foxpage resource manager
303 lines (302 loc) • 8.77 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ManagerBaseImpl = void 0;
const lodash_1 = __importDefault(require("lodash"));
const foxpage_shared_1 = require("@foxpage/foxpage-shared");
const cache_1 = require("../cache");
const common_1 = require("../common");
const event_1 = require("./event");
/**
* manager base
*
* @export
* @abstract
* @class ManagerBase
*/
class ManagerBaseImpl extends event_1.FPEventEmitterInstance {
constructor(app, opt) {
var _a, _b, _c;
super();
/**
* need update source keys
* for record the no update source key, call the thread to refresh next request
* @private
* @type {string[]}
*/
this.needUpdates = [];
this.appId = app.appId;
this.appSlug = app.slug;
this.logger = (0, common_1.createLogger)(`App@${app.appId} ${opt === null || opt === void 0 ? void 0 : opt.type}Manager`);
// DATA_PULL
app.on('DATA_PULL', async (data) => {
try {
await this.onPull(data || {});
}
catch (e) {
this.logger.error('DATA_PULL error:', e);
}
});
// DATA_STASH
app.on('DATA_STASH', (data) => {
try {
this.onStash(data);
}
catch (e) {
this.logger.error('DATA_STASH error:', e);
}
});
// cache
this.hotResources = (0, cache_1.createLRUCache)((_a = opt === null || opt === void 0 ? void 0 : opt.lruCache) === null || _a === void 0 ? void 0 : _a.size, { cloned: (_b = opt === null || opt === void 0 ? void 0 : opt.lruCache) === null || _b === void 0 ? void 0 : _b.cloned });
if ((_c = opt === null || opt === void 0 ? void 0 : opt.diskCache) === null || _c === void 0 ? void 0 : _c.enable) {
this.diskResources = (0, cache_1.createDiskCache)({
appId: this.appId,
type: `${(opt === null || opt === void 0 ? void 0 : opt.type) || 'default'}s`,
logger: this.logger,
});
}
}
/**
* DATA_PULL listener
*
* @protected
* @param {ResourceUpdateInfo} _data
*/
async onPull(_data) { }
/**
* DATA_STASH listener
*
* @protected
* @param {Relations} _data
*/
onStash(_data) { }
/**
* add source
*
* @protected
* @param {string} key
* @param {T} content
* @param {T} instance
*/
addOne(key, content, instance) {
var _a;
// cache content instance to memory
this.hotResources.set(key, instance);
// cache origin content to disk
(_a = this.diskResources) === null || _a === void 0 ? void 0 : _a.set(key, content);
// remove no updates
this.removeNeedUpdates([key]);
}
/**
* find source from local
* find from hot cache,
* if not exist will find from disk
* @protected
* @param {string} key
* @return {*}
*/
async findOneFromLocal(key) {
var _a;
let resource = this.hotResources.get(key);
if (!resource) {
resource = await ((_a = this.diskResources) === null || _a === void 0 ? void 0 : _a.get(key));
// will set to hot
if (resource) {
const resourceInstance = await this.createInstance(resource);
this.hotResources.set(key, resourceInstance);
return resourceInstance;
}
}
return resource;
}
/**
*
* @param key
* @returns
*/
findOne(key) {
const resource = this.hotResources.get(key);
return resource;
}
/**
* [batch] find all sources from local via source keys
*
* @protected
* @param {string[]} keys
* @return {*}
*/
async findFromLocal(keys) {
return (await Promise.all(keys.map(async (key) => {
const local = await this.findOneFromLocal(key);
if (!local) {
this.logger.warn(`not exist the "${key}" content`);
return null;
}
return local;
}))).filter(foxpage_shared_1.isNotNill);
}
/**
* remove source
*
* @protected
* @param {string} key
*/
removeOne(key) {
var _a;
this.hotResources.delete(key);
(_a = this.diskResources) === null || _a === void 0 ? void 0 : _a.delete(key);
}
/**
* [batch] remove all sources from via source keys
*
* @protected
* @param {string[]} keys
* @memberof ManagerBaseImpl
*/
remove(keys) {
keys.forEach(key => {
this.removeOne(key);
});
}
/**
* check if has the source
*
* @protected
* @param {string} key
* @return {*}
*/
async has(key) {
var _a;
return this.hotResources.has(key) || (await ((_a = this.diskResources) === null || _a === void 0 ? void 0 : _a.has(key)));
}
/**
* add no updates
*
* @param {string[]} keys
* @protected
*/
markNeedUpdates(keys = []) {
keys.forEach(key => {
if (!this.existInNeedUpdates(key)) {
this.needUpdates.push(key);
}
});
}
/**
* remove the no updates
*
* @param {string[]} keys
* @protected
*/
removeNeedUpdates(keys = []) {
const result = lodash_1.default.remove(this.needUpdates, item => keys.indexOf(item) > -1);
this.needUpdates = result;
}
/**
* if exist the no update source
*
* @protected
* @param {string} key
* @return {boolean}
*/
existInNeedUpdates(key) {
return this.needUpdates.indexOf(key) > -1;
}
/**
* chunk
* if exist local will add to ins, or add to outs
*
* @protected
* @param {string[]} keys
* @return {*} {Promise<string[][]>} [ins, outs, invalids] ins(in local), outs(not in local), invalids(in local but is not valid)
*/
async chunk(keys) {
const ins = [];
const invalids = [];
const outs = [];
for (const item of keys) {
if (await this.has(item)) {
ins.push(item);
}
else if (this.existInNeedUpdates(item)) {
invalids.push(item);
}
else {
outs.push(item);
}
}
return [ins, outs, invalids];
}
/**
* get sources
* first get local source, then fetch outs via fetch func with opt
* @protected
* @param {string[]} keys
* @param {{ autoFetch: boolean }} opt
* @return {*} {Promise<T[]>}
*/
async find(keys, opt = { autoFetch: true, autoCache: true }) {
const [ins, outs, invalids] = await this.chunk(keys);
const inContents = await this.findFromLocal(ins.concat(invalids));
let outContents;
if (opt.autoFetch && outs.length > 0) {
this.logger.info('fetch out contents %j from server', outs);
// fetch from server via outs
outContents = await this.onFetch(outs, opt);
}
if (invalids.length > 0) {
this.logger.info('fetch invalid contents %j from server', invalids);
// async fetch invalid sources
this.onFetch(invalids, opt);
}
if (outContents && outContents.length > 0) {
return [...inContents, ...outContents];
}
return inContents;
}
/**
* filter not exist list
*
* @protected
* @param {string[]} list
* @return {*}
*/
async filterExists(list) {
const notExists = [];
for (const item of list) {
if (await this.exist(item)) {
notExists.push(item);
}
}
return notExists;
}
/**
* exist the source
*
* @param {string} key
*/
async exist(key) {
return !!(await this.has(key));
}
/**
* memory count
*/
getHotCount() {
return this.hotResources.getCurCount();
}
getDiskCount() {
var _a;
return (_a = this.diskResources) === null || _a === void 0 ? void 0 : _a.getCurCount();
}
/**
* destroy the resource
*/
destroy() {
var _a;
this.hotResources.destroy();
(_a = this.diskResources) === null || _a === void 0 ? void 0 : _a.destroy();
}
}
exports.ManagerBaseImpl = ManagerBaseImpl;