UNPKG

@adpt/core

Version:
223 lines 8.67 kB
"use strict"; /* * Copyright 2018-2019 Unbounded Systems, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const json_stable_stringify_1 = tslib_1.__importDefault(require("json-stable-stringify")); const error_1 = require("../error"); const jsx_1 = require("../jsx"); const deploy_types_1 = require("./deploy_types"); class WidgetPlugin { constructor() { this.queryDomains = new Map(); this.log = (arg, ...args) => { if (this.log_) this.log_(arg, ...args); }; } /* * Methods that implement the Plugin interface */ async start(options) { this.deployID_ = options.deployID; this.log_ = options.log; this.dataDir_ = options.dataDir; } createExpected(oldDom, newDom, createQueryDomains = false) { const ret = {}; const changes = new Map(); const addElems = (dom, type) => { this.findElems(dom).forEach((el) => { const id = this.getWidgetIdFromElem(el); let change = changes.get(id); if (change) { change[type] = el; return; } change = { id, [type]: el }; changes.set(id, change); const domain = this.getElemQueryDomain(el); const qdKey = makeQueryDomainKey(domain); const qdChangeList = ret[qdKey] || []; ret[qdKey] = qdChangeList; qdChangeList.push(change); if (createQueryDomains && this.queryDomains.get(qdKey) == null) { this.queryDomains.set(qdKey, domain); } }); }; addElems(oldDom, "from"); addElems(newDom, "to"); return ret; } async observe(oldDom, dom) { const elemsInQDomain = this.createExpected(oldDom, dom, true); const obs = {}; for (const [key, domain] of this.queryDomains.entries()) { obs[key] = await this.getObservations(domain, this.deployID, elemsInQDomain[key]); } return obs; } analyze(oldDom, dom, obs) { const deployID = this.deployID; const expected = this.createExpected(oldDom, dom); const actions = diffObservations(expected, obs, (o) => this.getWidgetIdFromObs(o), (el, o) => this.computeChanges(el, o)); const ret = []; this.translatePairs(ret, actions.create, "Creating", (d, p) => this.createWidget(d, deployID, p)); this.translatePairs(ret, actions.modify, "Modifying", (d, p) => this.modifyWidget(d, deployID, p)); this.translatePairs(ret, actions.replace, "Replacing", async (d, p) => { await this.destroyWidget(d, deployID, p); await this.createWidget(d, deployID, p); }); this.translatePairs(ret, actions.delete, "Destroying", (d, p) => this.destroyWidget(d, deployID, p)); this.translatePairs(ret, actions.none, "Not modifying", () => { }); return ret; } async finish() { this.log_ = undefined; } /* * Additional class methods */ get deployID() { if (this.deployID_ == null) { throw new error_1.InternalError(`deployID not initialized yet`); } return this.deployID_; } get dataDir() { if (this.dataDir_ == null) { throw new Error(`Internal error: dataDir not initialized yet`); } return this.dataDir_; } queryDomain(key) { return this.queryDomains.get(key); } widgetInfo(pair) { let type; let id; let key; const el = pair.element; if (el != null) { if (!jsx_1.isMountedElement(el)) throw new error_1.InternalError(`element not mounted`); type = this.getWidgetTypeFromElem(el); id = this.getWidgetIdFromElem(el); key = el.props.key; } else if (pair.observed !== undefined) { type = this.getWidgetTypeFromObs(pair.observed); id = this.getWidgetIdFromObs(pair.observed); } else { throw new error_1.InternalError(`WidgetPair with no content`); } return { type, id, key }; } /** * Translate WidgetPairs into plugin Actions */ translatePairs(actions, pairs, actionType, action) { for (const p of pairs) { const { type, id, key } = this.widgetInfo(p); const k = key ? ` '${key}'` : ""; const description = `${actionType} ${type}${k} (id=${id})`; const domain = this.queryDomain(p.queryDomainKey); if (domain == null) throw new error_1.InternalError(`domain null`); actions.push(Object.assign({}, p.actionInfo, { act: async () => { try { await action(domain, p); } catch (err) { const path = p.element ? getPath(p.element) : ""; throw new Error(`An error occurred while ${description}` + `${path}: ${err.message || err}`); } } })); } } } exports.WidgetPlugin = WidgetPlugin; function getPath(el) { if (jsx_1.isMountedElement(el)) return ` [${el.path}]`; return ""; } function makeQueryDomainKey(queryDomain) { return json_stable_stringify_1.default(queryDomain.id); } function diffArrays(queryDomainKey, expected, observed, observedId, computeChanges, actions) { const obsMap = new Map(observed.map((o) => [observedId(o), o])); let actionInfo; for (const e of expected) { const o = obsMap.get(e.id); if (o !== undefined) obsMap.delete(e.id); actionInfo = computeChanges(e, o); const pair = { queryDomainKey, actionInfo }; const actionList = actions[actionInfo.type]; switch (actionInfo.type) { case deploy_types_1.ChangeType.create: actionList.push(Object.assign({}, pair, { element: e.to })); break; case deploy_types_1.ChangeType.none: const element = e.to || e.from; if (element == null) throw new error_1.InternalError(`WidgetChange with no 'from' or 'to' properties`); actionList.push(Object.assign({}, pair, { element })); break; case deploy_types_1.ChangeType.delete: if (o == null) { throw new Error(`Error in deployment plugin. Plugin computeChanges ` + `method returned ChangeType.delete for Element type ` + `${e.from.componentName} but no corresponding ` + `object was observed in the environment.`); } actionList.push(Object.assign({}, pair, { observed: o })); break; case deploy_types_1.ChangeType.modify: case deploy_types_1.ChangeType.replace: actionList.push(Object.assign({}, pair, { element: e.to, observed: o })); break; } } for (const [id, o] of obsMap) { actionInfo = computeChanges({ id }, o); actions.delete.push({ queryDomainKey, actionInfo, observed: o }); } } function diffObservations(expected, observed, observedId, computeChanges) { const actions = { create: [], delete: [], modify: [], none: [], replace: [], }; // Clone so we can modify observed = Object.assign({}, observed); for (const key of Object.keys(expected)) { diffArrays(key, expected[key], observed[key] || [], observedId, computeChanges, actions); delete observed[key]; } for (const key of Object.keys(observed)) { diffArrays(key, [], observed[key], observedId, computeChanges, actions); } return actions; } //# sourceMappingURL=widget_plugin.js.map