UNPKG

ayanami

Version:
114 lines (113 loc) 5.55 kB
import { __awaiter, __generator } from "tslib"; import { from, race, timer, throwError } from 'rxjs'; import { flatMap, skip, take, tap } from 'rxjs/operators'; import { InjectableFactory } from '@asuka/di'; import { combineWithIkari } from '../core/ikari'; import { createOrGetInstanceInScope, ayanamiInstances, createScopeWithRequest, } from '../core/scope/utils'; import { SSRSymbol, CleanupSymbol, DEFAULT_SCOPE_NAME } from './constants'; import { moduleNameKey } from './ssr-module'; import { SKIP_SYMBOL, reqMap } from './express'; var skipFn = function () { return 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 */ export var emitSSREffects = function (req, modules, timeout) { if (timeout === void 0) { timeout = 3; } var stateToSerialize = {}; var cleanup = function () { // non-scope ayanami if (ayanamiInstances.has(req)) { ayanamiInstances.get(req).forEach(function (instance) { instance[CleanupSymbol].call(); }); ayanamiInstances.delete(req); } // scoped ayanami if (reqMap.has(req)) { Array.from(reqMap.get(req).values()).forEach(function (s) { ayanamiInstances.get(s).forEach(function (instance) { instance[CleanupSymbol].call(); }); ayanamiInstances.delete(s); }); reqMap.delete(req); } }; return modules.length === 0 ? Promise.resolve({ state: stateToSerialize, cleanup: cleanup }) : race(from(modules).pipe(flatMap(function (m) { return __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 __generator(this, function (_b) { switch (_b.label) { case 0: scope = DEFAULT_SCOPE_NAME; if ('scope' in m) { constructor = m.module; scope = m.scope; } else { constructor = m; } metas = Reflect.getMetadata(SSRSymbol, constructor.prototype); if (!metas) return [3 /*break*/, 7]; ayanamiInstance = InjectableFactory.initialize(constructor); moduleName = ayanamiInstance[moduleNameKey]; ikari = 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 !== 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(skip(skipCount), 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 = createOrGetInstanceInScope(constructor, createScopeWithRequest(req, scope === DEFAULT_SCOPE_NAME ? undefined : scope)); existedIkari = combineWithIkari(existedAyanami); existedIkari.state.setState(state); ayanamiInstance.destroy(); _b.label = 7; case 7: return [2 /*return*/, { state: stateToSerialize, cleanup: cleanup }]; } }); }); })), timer(timeout * 1000).pipe(tap(cleanup), flatMap(function () { return throwError(new Error('Terminate timeout')); }))).toPromise(); };