ayanami
Version:
A better way to react with state
118 lines (117 loc) • 5.81 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.emitSSREffects = void 0;
var tslib_1 = require("tslib");
var rxjs_1 = require("rxjs");
var operators_1 = require("rxjs/operators");
var di_1 = require("@asuka/di");
var ikari_1 = require("../core/ikari");
var utils_1 = require("../core/scope/utils");
var constants_1 = require("./constants");
var ssr_module_1 = require("./ssr-module");
var express_1 = require("./express");
var skipFn = function () { return express_1.SKIP_SYMBOL; };
/**
* Run all @SSREffect decorated effects of given modules and extract latest states.
* `cleanup` function returned must be called before end of responding
*
* @param req express request object
* @param modules used ayanami modules
* @param timeout seconds to wait before all effects stream out TERMINATE_ACTION
* @returns object contains ayanami state and cleanup function
*/
var emitSSREffects = function (req, modules, timeout) {
if (timeout === void 0) { timeout = 3; }
var stateToSerialize = {};
var cleanup = function () {
// non-scope ayanami
if (utils_1.ayanamiInstances.has(req)) {
utils_1.ayanamiInstances.get(req).forEach(function (instance) {
instance[constants_1.CleanupSymbol].call();
});
utils_1.ayanamiInstances.delete(req);
}
// scoped ayanami
if (express_1.reqMap.has(req)) {
Array.from(express_1.reqMap.get(req).values()).forEach(function (s) {
utils_1.ayanamiInstances.get(s).forEach(function (instance) {
instance[constants_1.CleanupSymbol].call();
});
utils_1.ayanamiInstances.delete(s);
});
express_1.reqMap.delete(req);
}
};
return modules.length === 0
? Promise.resolve({ state: stateToSerialize, cleanup: cleanup })
: rxjs_1.race(rxjs_1.from(modules).pipe(operators_1.flatMap(function (m) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
var constructor, scope, metas, ayanamiInstance, moduleName, ikari, skipCount, _i, metas_1, meta, dispatcher, param, state, existedAyanami, existedIkari;
var _a;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
scope = constants_1.DEFAULT_SCOPE_NAME;
if ('scope' in m) {
constructor = m.module;
scope = m.scope;
}
else {
constructor = m;
}
metas = Reflect.getMetadata(constants_1.SSRSymbol, constructor.prototype);
if (!metas) return [3 /*break*/, 7];
ayanamiInstance = di_1.InjectableFactory.initialize(constructor);
moduleName = ayanamiInstance[ssr_module_1.moduleNameKey];
ikari = ikari_1.combineWithIkari(ayanamiInstance);
skipCount = metas.length - 1;
_i = 0, metas_1 = metas;
_b.label = 1;
case 1:
if (!(_i < metas_1.length)) return [3 /*break*/, 5];
meta = metas_1[_i];
dispatcher = ikari.triggerActions[meta.action];
if (!meta.middleware) return [3 /*break*/, 3];
return [4 /*yield*/, meta.middleware(req, skipFn)];
case 2:
param = _b.sent();
if (param !== express_1.SKIP_SYMBOL) {
dispatcher(param);
}
else {
skipCount -= 1;
}
return [3 /*break*/, 4];
case 3:
dispatcher(void 0);
_b.label = 4;
case 4:
_i++;
return [3 /*break*/, 1];
case 5:
if (!(skipCount > -1)) return [3 /*break*/, 7];
return [4 /*yield*/, ikari.terminate$
.pipe(operators_1.skip(skipCount), operators_1.take(1))
.toPromise()];
case 6:
_b.sent();
ikari.terminate$.next(null);
state = ikari.state.getState();
if (stateToSerialize[moduleName]) {
stateToSerialize[moduleName][scope] = state;
}
else {
stateToSerialize[moduleName] = (_a = {},
_a[scope] = state,
_a);
}
existedAyanami = utils_1.createOrGetInstanceInScope(constructor, utils_1.createScopeWithRequest(req, scope === constants_1.DEFAULT_SCOPE_NAME ? undefined : scope));
existedIkari = ikari_1.combineWithIkari(existedAyanami);
existedIkari.state.setState(state);
ayanamiInstance.destroy();
_b.label = 7;
case 7: return [2 /*return*/, { state: stateToSerialize, cleanup: cleanup }];
}
});
}); })), rxjs_1.timer(timeout * 1000).pipe(operators_1.tap(cleanup), operators_1.flatMap(function () { return rxjs_1.throwError(new Error('Terminate timeout')); }))).toPromise();
};
exports.emitSSREffects = emitSSREffects;