UNPKG

botbuilder-dialogs-adaptive-runtime-core

Version:

Bot Framework Adaptive Dialogs runtime core components

188 lines 7.94 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ServiceCollection = void 0; const assert_1 = __importDefault(require("assert")); const dependency_graph_1 = require("dependency-graph"); const assert_2 = require("assert"); const util_1 = require("./util"); /** * ServiceCollection is an interface that describes a set of methods to register services. This, in a lighter way, * mimics the .NET dependency injection service collection functionality, except for instances rather than types. */ class ServiceCollection { /** * Construct a Providers instance * * @template S services interface * @param defaultServices default set of services */ constructor(defaultServices = {}) { // We store the full set of dependencies as a workaround to the fact that `DepGraph` throws an error if you // attempt to register a dependency to a node that does not yet exist. this.dependencies = new Map(); /** * `DepGraph` is a dependency graph data structure. In our case, the services we support are encoded as a * dependency graph where nodes are named with a key and store a list of factory methods. */ this.graph = new dependency_graph_1.DepGraph(); /** * Cache constructed instances for reuse */ this.cache = {}; Object.entries(defaultServices).forEach(([key, instance]) => { this.addInstance(key, instance); }); } /** * Register an instance by key. This will overwrite existing instances. * * @param key key of the instance being provided * @param instance instance to provide * @returns this for chaining */ addInstance(key, instance) { this.graph.addNode(key, [() => instance]); return this; } /** * @internal */ addFactory(key, depsOrFactory, maybeFactory) { const dependencies = Array.isArray(depsOrFactory) ? depsOrFactory : undefined; let factory = maybeFactory; if (!factory && typeof depsOrFactory === 'function') { factory = (_services, value) => depsOrFactory(value); } // Asserts factory is not undefined assert_2.ok(factory, 'illegal invocation with undefined factory'); if (dependencies) { this.dependencies.set(key, dependencies); } // If the graph already has this key, fetch its data and remove it (to be replaced) let factories = []; if (this.graph.hasNode(key)) { factories = this.graph.getNodeData(key); this.graph.removeNode(key); } // Note: we have done the type checking above, so disabling no-explicit-any is okay. // eslint-disable-next-line @typescript-eslint/no-explicit-any this.graph.addNode(key, factories.concat(factory)); return this; } /** * @internal */ composeFactory(key, depsOrFactory, maybeFactory) { if (maybeFactory) { return this.addFactory(key, Array.isArray(depsOrFactory) ? depsOrFactory : [], (dependencies, value) => { assert_2.ok(value, `unable to create ${key}, initial value undefined`); return maybeFactory(dependencies, value); }); } else { assert_2.ok(typeof depsOrFactory === 'function', 'illegal invocation with undefined factory'); return this.addFactory(key, (value) => { assert_2.ok(value, `unable to create ${key}, initial value undefined`); return depsOrFactory(value); }); } } // Register dependencies and then build nodes. Note: `nodes` is a function because ordering may // depend on results of dependency registration buildNodes(generateNodes, reuseServices = {}) { // Consume all dependencies and then reset so updating registrations without re-registering // dependencies works this.dependencies.forEach((dependencies, node) => dependencies.forEach((dependency) => this.graph.addDependency(node, util_1.stringify(dependency)))); // Generate nodes after registering dependencies so ordering is correct const nodes = generateNodes(); const services = nodes.reduce((services, service) => { // Extra precaution if (!this.graph.hasNode(service)) { return services; } // Helper to generate return value const assignValue = (value) => (Object.assign(Object.assign({}, services), { [service]: value })); // Optionally reuse existing service const reusedService = reuseServices[service]; if (reusedService !== undefined) { return assignValue(reusedService); } // Each node stores a list of factory methods. const factories = this.graph.getNodeData(service); // Produce the instance by reducing those factories, passing the instance along for composition. const instance = factories.reduce((value, factory) => factory(services, value), services[service]); return assignValue(instance); }, {}); // Cache results for subsequent invocations that may desire pre-constructed instances Object.assign(this.cache, services); return services; } /** * Build a single service. * * @param key service to build * @param deep reconstruct all dependencies * @returns the service instance, or undefined */ makeInstance(key, deep = false) { // If this is not a deep reconstruction, reuse any services that `key` depends on let initialServices; if (!deep) { const _a = this.cache, _b = key, _ = _a[_b], cached = __rest(_a, [typeof _b === "symbol" ? _b : _b + ""]); initialServices = cached; } const services = this.buildNodes(() => this.graph.dependenciesOf(key).concat(key), initialServices); return services[key]; } /** * Build a single service and assert that it is not undefined. * * @param key service to build * @param deep reconstruct all dependencies * @returns the service instance */ mustMakeInstance(key, deep = false) { const instance = this.makeInstance(key, deep); assert_1.default.ok(instance, `\`${key}\` instance undefined!`); return instance; } /** * Build the full set of services. * * @returns all resolved services */ makeInstances() { return this.buildNodes(() => this.graph.overallOrder()); } /** * Build the full set of services, asserting that the specified keys are not undefined. * * @param keys instances that must be not undefined * @returns all resolve services */ mustMakeInstances(...keys) { const instances = this.makeInstances(); keys.forEach((key) => { assert_1.default.ok(instances[key], `\`${key}\` instance undefined!`); }); return instances; } } exports.ServiceCollection = ServiceCollection; //# sourceMappingURL=serviceCollection.js.map