@orchestrator/core
Version:
> Core package of Orchestrator library.
1,040 lines (1,000 loc) • 41.8 kB
JavaScript
import { NgClass, CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, Injector, Inject, InjectFlags, SkipSelf, Optional, EventEmitter, ChangeDetectorRef, Component, ChangeDetectionStrategy, Input, Output, NgModule } from '@angular/core';
import * as i6 from 'ng-dynamic-component';
import { dynamicDirectiveDef, DynamicModule, DynamicAttributesModule, DynamicDirectivesModule } from 'ng-dynamic-component';
import { Property, genIoType, anyOf } from '@orchestrator/gen-io-ts';
import * as genIoTs from '@orchestrator/gen-io-ts';
export { genIoTs as getIoTs };
import { chain, map, fold, left, isLeft, isRight } from 'fp-ts/lib/Either';
import { fold as fold$1, none, some } from 'fp-ts/lib/Option';
import { pipe } from 'fp-ts/function';
import { PathReporter } from 'io-ts/lib/PathReporter';
import * as t from 'io-ts';
import { Int, brand, union, null as null$1, undefined as undefined$1 } from 'io-ts';
import { Subject, combineLatest } from 'rxjs';
import { takeUntil, map as map$1 } from 'rxjs/operators';
import { __decorate, __metadata } from 'tslib';
const COMPONENTS = new InjectionToken('COMPONENTS');
function createMetadataGetSet(key) {
const k = Symbol(key);
return {
set: defineMetadata.bind(null, k),
get: (type) => type[k],
};
}
function defineMetadata(key, value, target) {
if (key in target === false) {
Object.defineProperty(target, key, {
enumerable: false,
configurable: true,
writable: true,
value,
});
}
else {
target[key] = value;
}
return target;
}
const dynamicComponentMeta = createMetadataGetSet('DynamicComponentMeta');
function DynamicComponent(options) {
return target => dynamicComponentMeta.set(options, target);
}
function getDynamicComponentMeta(type) {
return dynamicComponentMeta.get(type);
}
class ComponentLocatorService {
constructor(injector, cfr) {
this.injector = injector;
this.cfr = cfr;
this.componentRegistry = this.injector.get(COMPONENTS);
this.componentArray = this.componentRegistry
.filter(isComponentArray)
.reduce((arr, reg) => [...arr, ...reg], []);
this.componentArrayMap = this.componentArray
.map(type => this.cfr.resolveComponentFactory(type))
.reduce((map, compFactory) => (Object.assign(Object.assign({}, map), { [compFactory.selector]: compFactory.componentType })), Object.create(null));
this.componentMaps = this.componentRegistry.filter(isComponentMap);
this.componentMap = this.componentMaps.reduce((obj, map) => (Object.assign(Object.assign({}, obj), map)), this.componentArrayMap);
}
resolve(component) {
if (typeof component === 'function') {
return component;
}
return this.componentMap[component];
}
getDefaultConfig(component) {
const configType = this.getConfigType(component);
if (!configType) {
return null;
}
return this.injector.get(configType, null);
}
getConfigType(component) {
if (!component) {
return null;
}
const meta = getDynamicComponentMeta(component);
if (!meta) {
return null;
}
return meta.config;
}
}
/** @nocollapse */ /** @nocollapse */ ComponentLocatorService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentLocatorService, deps: [{ token: i0.Injector }, { token: i0.ComponentFactoryResolver }], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ /** @nocollapse */ ComponentLocatorService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentLocatorService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentLocatorService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.ComponentFactoryResolver }]; } });
function isComponentArray(reg) {
return Array.isArray(reg);
}
function isComponentMap(reg) {
return !!reg && !Array.isArray(reg);
}
const configurationMeta = createMetadataGetSet('ConfigurationMeta');
/**
* @internal
*/
function addConfig(target, meta) {
const configs = getConfigs(target);
configurationMeta.set([...configs, meta], target);
}
/**
* @internal
*/
function getConfigs(type) {
return configurationMeta.get(type) || [];
}
/**
* @internal
*/
function execRegex(regex, val) {
const arr = [];
let group;
while ((group = regex.exec(val)) !== null) {
if (group.index === regex.lastIndex) {
regex.lastIndex++;
}
arr.push(...group);
}
return !arr.length ? null : arr;
}
/**
* @internal
*/
function parseFunction(fnStr) {
const fnRegex = /^function\s*(?:[A-z0-9]+)?\s*\(([\w\W]*?)\)\s*\{([\w\W]*)\}$/gm;
const arrowFnRegex = /^\(?([\w\W]*?)\)?\s*=>\s*\{([\w\W]*)\}$/gm;
const returnArrowFnRegex = /^\(?([\w\W]*?)\)?\s*=>\s*([^}{]*)$/gm;
fnStr = fnStr.trim();
const fnInfo = execRegex(fnRegex, fnStr) ||
execRegex(arrowFnRegex, fnStr) ||
execRegex(returnArrowFnRegex, fnStr);
if (!fnInfo || fnInfo.length < 2) {
return null;
}
const _args = fnInfo.length > 2 ? fnInfo[1] || '' : '';
const args = _args
.split(',')
.map(arg => arg.trim())
.filter(arg => !!arg);
const isReturnFunction = returnArrowFnRegex.test(fnStr);
const _body = fnInfo[fnInfo.length - 1];
if (!_body && isReturnFunction) {
return null;
}
const body = isReturnFunction ? `return ${_body}` : _body || '';
return { args, body };
}
/**
* @internal
*/
function isArgOptional(argExpr) {
return /^[^=]+\s*=.+/.test(argExpr);
}
/**
* @internal
*/
function getArgName(argExpr) {
return argExpr.match(/^([^=\s]+)(?:\s*=.+)?/)[1];
}
class FunctionError extends Error {
constructor(config, error, fnName, fnBody, args) {
super(`During function execution ${fnName} from config ${config.name}:
${error}
Stack:
${error.stack}
Function Body:
${fnBody}
Function Arguments: [${args.join('\n')}]`);
this.config = config;
this.error = error;
this.fnName = fnName;
this.fnBody = fnBody;
this.args = args;
}
}
class InvalidConfigurationError extends Error {
constructor(component, validation, config) {
const paths = PathReporter.report(validation).join('\n');
super(`Invalid configuration for component ${component.name}'s config:
${paths}
Actual config: ${config ? JSON.stringify(config, null, 2) : config}`);
this.component = component;
this.validation = validation;
this.config = config;
}
}
const CUSTOM_FUNCTION_ARGUMENT_PREFIX = '$';
const FunctionFromMeta = new t.Type('FunctionFromMeta', isFunctionWithMeta, (m, c) => pipe(t.UnknownRecord.validate(m, c), chain((obj) => {
if (!hasFunctionMeta(obj)) {
return t.failure(m, c);
}
// Move custom arguments to the end
obj.args.sort((arg1, arg2) => {
const is1Custom = arg1.startsWith(CUSTOM_FUNCTION_ARGUMENT_PREFIX);
const is2Custom = arg2.startsWith(CUSTOM_FUNCTION_ARGUMENT_PREFIX);
if (is1Custom === is2Custom) {
return 0;
}
return is1Custom ? 1 : -1;
});
const fn = new Function(...obj.args, obj.body);
fn.args = obj.args;
fn.body = obj.body;
return t.success(fn);
})), (fn) => ({ args: fn.args, body: fn.body }));
const FunctionFromString = new t.Type('FunctionFromString', isFunctionWithMeta, (m, c) => pipe(t.string.validate(m, c), chain((str) => {
try {
return FunctionFromMeta.validate(parseFunction(str), c);
}
catch (_a) {
return t.failure(str, c);
}
})), (fn) => fn.toString());
// eslint-disable-next-line @typescript-eslint/ban-types
const FunctionWithMeta = new t.Type('FunctionWithMeta', isFunctionWithMeta, (m, c) => pipe(t.Function.validate(m, c), chain((fn) => {
try {
// Reconstruct function from string to reorder arguments
return FunctionFromString.validate(fn.toString(), c);
}
catch (_a) {
return t.failure(fn, c);
}
})), (fn) => fn);
function OptionFunction(customInjector) {
const decorator = Property({
typeFactory: () => t.union([FunctionFromString, FunctionFromMeta, FunctionWithMeta]),
});
return (target, prop) => {
decorator(target, prop);
addConfig(target, {
prop,
decorator: OptionFunction,
args: [customInjector],
});
};
}
function hasFunctionMeta(obj) {
return obj && Array.isArray(obj.args) && typeof obj.body === 'string';
}
function isFunctionWithMeta(fn) {
return typeof fn === 'function' && hasFunctionMeta(fn);
}
class ErrorStrategy {
}
class ConfigurationService {
constructor(errorStrategy, injector) {
this.errorStrategy = errorStrategy;
this.injector = injector;
this.codecMap = new Map();
}
decode(type, config, injector) {
return pipe(this.validate(type, config), map((c) => this.processFunctions(type, c, config, injector)), fold(() => config, (decodedConfig) => decodedConfig));
}
validate(type, config) {
const validation = pipe(this.getCodecFor(type), fold$1(() => left([]), (codec) => codec.decode(config)));
if (isLeft(validation) && type) {
this.errorStrategy.handle(new InvalidConfigurationError(type, validation, config));
}
return validation;
}
getMetaOf(type) {
return getConfigs(type.prototype);
}
getCodecFor(type) {
if (!type) {
return none;
}
const codec = this.codecMap.get(type) || genIoType(type);
this.codecMap.set(type, codec); // Set codec back to cache
return some(codec);
}
processFunctions(type, config, originalConfig, injector = this.injector) {
const meta = this.getMetaOf(type);
meta
.filter((m) => m.decorator === OptionFunction && config[m.prop])
.forEach((m) => {
const customInjectorFactory = m.args[0];
const customInjector = customInjectorFactory
? customInjectorFactory(injector)
: injector;
const { args, fn } = this.bindFunction(config[m.prop], customInjector);
config[m.prop] = fn;
config[m.prop] = this.guardFunction(config[m.prop], type, String(m.prop), originalConfig[m.prop], args);
});
return config;
}
bindFunction(fn, injector) {
const { args, body } = fn;
const resolvedArgs = args
.filter((arg) => !arg.startsWith('$'))
.map((arg) => this.resolveArg(arg, injector));
const boundFn = fn.bind(null, ...resolvedArgs);
boundFn.args = args;
boundFn.body = body;
return { fn: boundFn, args: resolvedArgs };
}
guardFunction(fn, configType, fnName, fnBody, boundArgs) {
const guardedFn = ((...args) => {
try {
return fn(...args);
}
catch (e) {
this.errorStrategy.handle(new FunctionError(configType, e, fnName, fnBody, [
...boundArgs,
...args,
]));
}
});
guardedFn.args = fn.args;
guardedFn.body = fn.body;
return guardedFn;
}
resolveArg(argExpr, injector) {
const arg = getArgName(argExpr);
const isOptional = isArgOptional(argExpr);
// Dynamically resolve function arguments - no type info available
const res = injector.get(arg, isOptional ? null : Injector.THROW_IF_NOT_FOUND);
return res === null && isOptional ? undefined : res;
}
}
/** @nocollapse */ /** @nocollapse */ ConfigurationService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ConfigurationService, deps: [{ token: ErrorStrategy }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ /** @nocollapse */ ConfigurationService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ConfigurationService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ConfigurationService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: ErrorStrategy }, { type: i0.Injector }]; } });
class ThrowErrorStrategy extends ErrorStrategy {
handle(error) {
throw error;
}
}
/**
* Abstract component type that is responsible to render dynamic component
*/
class RenderComponent {
}
const LOCAL_GET_INJECTOR = new InjectionToken('LOCAL_GET_INJECTOR');
const LOCAL_GET_COMPONENT = new InjectionToken('LOCAL_GET_COMPONENT');
const LOCAL_GET_CONFIG = new InjectionToken('LOCAL_GET_CONFIGURATION');
const LOCAL_UPDATE_CONFIG = new InjectionToken('LOCAL_GET_CONFIGURATION');
const LOCAL_GET_CONFIG_VALID = new InjectionToken('LOCAL_GET_CONFIGURATION_VALID');
const LOCAL_GET_CONTEXT = new InjectionToken('LOCAL_GET_CONTEXT');
const LOCAL_INJECTOR_MAP = {
getInjector: LOCAL_GET_INJECTOR,
getComponent: LOCAL_GET_COMPONENT,
getConfig: LOCAL_GET_CONFIG,
updateConfig: LOCAL_UPDATE_CONFIG,
isConfigValid: LOCAL_GET_CONFIG_VALID,
renderComponent: RenderComponent,
getContext: LOCAL_GET_CONTEXT,
};
function getLocalProviders(data) {
return [
{
provide: LOCAL_GET_INJECTOR,
useValue: data.getInjector,
},
{
provide: LOCAL_GET_COMPONENT,
useValue: data.getComponent,
},
{
provide: LOCAL_GET_CONFIG,
useValue: data.getConfig,
},
{
provide: LOCAL_UPDATE_CONFIG,
useValue: data.updateConfig,
},
{
provide: LOCAL_GET_CONFIG_VALID,
useValue: data.isConfigValid,
},
{
provide: LOCAL_GET_CONTEXT,
useValue: data.getContext,
},
];
}
/**
* Multi-provider of {@link InjectorMap}
*/
const INJECTOR_MAP_TOKEN = new InjectionToken('INJECTOR_MAP');
/**
* Helper to provide {@link INJECTOR_MAP_TOKEN}
*/
function provideInjectorMap(map) {
return { provide: INJECTOR_MAP_TOKEN, useValue: map, multi: true };
}
/**
* Maps tokens to other tokens and then executes parent injector.
*
* NOT a Service!
* Use via {@link MappedInjectorFactory}
*/
class MappedInjector {
constructor(parent, injectorMaps) {
this.parent = parent;
this.injectorMaps = injectorMaps;
}
get(token, notFoundValue, flags) {
return this.parent.get(this.mapToken(token), notFoundValue, flags);
}
mapToken(token) {
if (typeof token !== 'string') {
return token;
}
this.maybeInitInjectorMap();
token = this.processToken(token);
return token in this.injectorMap ? this.injectorMap[token] : token;
}
maybeInitInjectorMap() {
if (!this.injectorMap) {
this.injectorMap = this.injectorMaps.reduce((acc, m) => Object.keys(m).reduce((obj, k) => (Object.assign(Object.assign({}, obj), { [this.processToken(k)]: m[k] })), acc), Object.create(null));
}
}
processToken(t) {
return t.toLowerCase();
}
}
/**
* Factory for {@link MappedInjector}
*/
class MappedInjectorFactory {
constructor(injectorMap) {
this.injectorMap = injectorMap;
}
/**
* Creates MappedInjector with parent injector and {@link INJECTOR_MAP_TOKEN} from DI
*/
create(parent) {
return new MappedInjector(parent, this.injectorMap);
}
}
/** @nocollapse */ /** @nocollapse */ MappedInjectorFactory.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: MappedInjectorFactory, deps: [{ token: INJECTOR_MAP_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ /** @nocollapse */ MappedInjectorFactory.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: MappedInjectorFactory });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: MappedInjectorFactory, decorators: [{
type: Injectable
}], ctorParameters: function () {
return [{ type: undefined, decorators: [{
type: Inject,
args: [INJECTOR_MAP_TOKEN]
}] }];
} });
const STATIC_INJECT_FLAGS = new InjectionToken('STATIC_INJECT_FLAGS');
const STATIC_INJECTOR_MAP = {
InjectFlags: STATIC_INJECT_FLAGS,
};
function getStaticProviders() {
return [{ provide: STATIC_INJECT_FLAGS, useValue: InjectFlags }];
}
/**
* Provides a map for injectors with providers
*
* @internal
*/
const INJECTOR_MAP_PROVIDERS = [
...getStaticProviders(),
provideInjectorMap(STATIC_INJECTOR_MAP),
provideInjectorMap(LOCAL_INJECTOR_MAP),
];
function OptionAllowedValues(...values) {
const decorator = Property({ type: anyOf(...values) });
return (target, prop) => {
decorator(target, prop);
addConfig(target, { prop, decorator: OptionAllowedValues, args: [values] });
};
}
function OptionInteger() {
const decorator = Property({ typeFactory: () => Int });
return (target, prop) => {
decorator(target, prop);
addConfig(target, { prop, decorator: OptionInteger, args: [] });
};
}
function OptionRange(min, max, step = 1) {
const typeFactory = (type) => brand(type, (n) => n >= min && n <= max, 'InRange');
const decorator = Property({ type: Number, typeFactory });
return (target, prop) => {
decorator(target, prop);
addConfig(target, { prop, decorator: OptionRange, args: [min, max, step] });
};
}
function OptionRequired() {
const decorator = Property({ isRequired: true });
return (target, prop) => {
decorator(target, prop);
addConfig(target, { prop, decorator: OptionRequired, args: [] });
};
}
function OptionType(type) {
const decorator = Property({ type });
return (target, prop) => {
decorator(target, prop);
addConfig(target, { prop, decorator: OptionType, args: [type] });
};
}
/* eslint-disable @typescript-eslint/no-non-null-assertion */
function Option(config = {}) {
const decorator = Property();
const decorators = Object.keys(config)
.map((key) => {
switch (key) {
case 'required':
return config.required ? OptionRequired() : null;
case 'type':
return OptionType(config.type);
case 'range':
return OptionRange(config.range.min, config.range.max, config.range.step);
case 'integer':
return config.integer ? OptionInteger() : null;
case 'allowedValues':
return OptionAllowedValues(...config.allowedValues);
}
})
.filter(Boolean);
return (target, prop) => {
decorator(target, prop);
decorators.forEach((d) => d(target, prop));
};
}
function OptionTypeFactory(typeFactory) {
const decorator = Property({ typeFactory });
return (target, prop) => {
decorator(target, prop);
addConfig(target, {
prop,
decorator: OptionTypeFactory,
args: [typeFactory],
});
};
}
function classToType(cls) {
return genIoType(cls);
}
/**
* Will set type of property to `null | undefined`.
*
* Useful for cases when you have to explicitly exclude
* specific property from type.
*
* **Example:**
* ```ts
* class A {
* @Option()
* prop1: string;
* @Option()
* prop2: string;
* @OptionNotPresent()
* prop3?: null | undefined; // This prop should be excluded!
* }
*
* class B {
* @Option()
* prop1: string;
* @Option()
* prop2: string;
* @Option()
* prop3: string;
* }
*
* type AorB = A | B;
* ```
*/
function OptionNotPresent() {
const decorator = Property({
typeFactory: () => union([null$1, undefined$1]),
});
return (target, prop) => {
decorator(target, prop);
addConfig(target, { prop, decorator: OptionNotPresent, args: [] });
};
}
class InjectorRegistryService {
constructor(parentInjector) {
this.parentInjector = parentInjector;
this.injector = this.parentInjector;
}
get(token, notFoundValue, flags) {
return this.injector.get(token, notFoundValue, flags);
}
addProviders(providers) {
this.injector = Injector.create({
providers,
parent: this.injector,
});
}
reset(parentInjector) {
this.injector = parentInjector || this.parentInjector;
}
}
/** @nocollapse */ /** @nocollapse */ InjectorRegistryService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: InjectorRegistryService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ /** @nocollapse */ InjectorRegistryService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: InjectorRegistryService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: InjectorRegistryService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.Injector }]; } });
/**
* @internal
*/
function createLocalInjector(params) {
const injector = Injector.create({
providers: getLocalProviders(Object.assign(Object.assign({}, params), { getInjector: () => injector })),
parent: params.parentInjector,
});
return injector;
}
class ComponentsRegistryService {
constructor(parentComponentsRegistryService) {
this.parentComponentsRegistryService = parentComponentsRegistryService;
this._componentsReady$ = new Subject();
this.componentsReady$ = this._componentsReady$.asObservable();
this.childComponents = [];
this.subChildComponents = [];
}
ngOnDestroy() {
this.childComponents = [];
this.subChildComponents = [];
}
waitFor(count) {
this.count = count;
this.childComponents = [];
this.subChildComponents = [];
}
add(compRef) {
if (this.parentComponentsRegistryService) {
this.parentComponentsRegistryService.addChild(compRef);
if (this.count === 0) {
this.parentComponentsRegistryService.addSubChildren([]);
}
}
}
addChildren(compRefs) {
if (this.parentComponentsRegistryService) {
this.parentComponentsRegistryService.addSubChildren(compRefs);
}
}
addChild(compRef) {
this.childComponents.push(compRef);
}
addSubChildren(compRefs) {
this.subChildComponents = this.subChildComponents.concat(compRefs);
if (this.childComponents.length >= this.count) {
this._componentsReady$.next(this.childComponents.concat(this.subChildComponents).filter(Boolean));
}
}
}
/** @nocollapse */ /** @nocollapse */ ComponentsRegistryService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentsRegistryService, deps: [{ token: ComponentsRegistryService, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ /** @nocollapse */ ComponentsRegistryService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentsRegistryService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: ComponentsRegistryService, decorators: [{
type: Injectable
}], ctorParameters: function () {
return [{ type: ComponentsRegistryService, decorators: [{
type: SkipSelf
}, {
type: Optional
}] }];
} });
class Handler {
}
__decorate([
OptionFunction(),
__metadata("design:type", Object)
], Handler.prototype, "handler", void 0);
class RenderItemComponent extends RenderComponent {
constructor(cdr, renderer, cfr, componentLocatorService, componentsRegistryService, configurationService, mappedInjectorFactory, injectorRegistryService) {
super();
this.cdr = cdr;
this.renderer = renderer;
this.cfr = cfr;
this.componentLocatorService = componentLocatorService;
this.componentsRegistryService = componentsRegistryService;
this.configurationService = configurationService;
this.mappedInjectorFactory = mappedInjectorFactory;
this.injectorRegistryService = injectorRegistryService;
this.componentCreated = new EventEmitter();
this.childComponentsCreated = new EventEmitter();
this.destroyed$ = new Subject();
this.inputs = {
items: undefined,
config: undefined,
context: undefined,
};
this.directives = [];
this.attributes = null;
this.disposableHandlers = [];
}
get itemsLength() {
return this.item && this.item.items ? this.item.items.length : 0;
}
ngOnInit() {
this.componentsRegistryService.componentsReady$
.pipe(takeUntil(this.destroyed$))
.subscribe((compRefs) => {
this.childComponentsCreated.emit(compRefs);
this.componentsRegistryService.addChildren(compRefs);
});
this.update();
}
ngOnChanges(changes) {
if ('item' in changes && !changes.item.firstChange) {
this.update();
}
else if ('context' in changes && !changes.context.firstChange) {
this.updateContextInput();
}
}
ngOnDestroy() {
this.destroyed$.next();
this.disposeHandlers();
this.compRef = this.compCdr = this.compFactory = this.config = null;
}
onComponentCreated(compRef) {
this.compRef = compRef;
this.compFactory = this.cfr.resolveComponentFactory(this.componentType);
this.componentCreated.emit(compRef);
this.componentsRegistryService.add(compRef);
this.updateHandlers();
}
getInjectorRegistryService() {
return this.injectorRegistryService;
}
markForCheck() {
if (!this.compCdr && this.compRef) {
this.compCdr = this.compRef.injector.get(ChangeDetectorRef);
}
if (this.compCdr) {
this.compCdr.markForCheck();
}
}
addItem(item) {
if (this.inputs.items) {
this.inputs.items = [...this.inputs.items, item];
}
else {
this.inputs.items = [item];
}
this.cdr.markForCheck();
}
removeItem(item) {
const idx = this.inputs.items ? this.inputs.items.indexOf(item) : -1;
if (idx === -1) {
return;
}
this.inputs.items = this.inputs.items.filter((_, i) => i !== idx);
this.cdr.markForCheck();
}
clearItems() {
this.inputs.items = [];
this.cdr.markForCheck();
}
update() {
this.updateComponent();
this.updateConfig();
this.updateInjector();
this.updateInputs();
this.updateContextInput();
this.updateAttributes();
this.updateDirectives();
}
updateComponent() {
// Invalidate late-component-refs immediately
this.compRef = this.compCdr = this.compFactory = null;
if (this.item) {
this.componentType = this.componentLocatorService.resolve(this.item.component);
this.componentsRegistryService.waitFor(this.itemsLength);
}
else {
this.componentType = null;
this.componentsRegistryService.waitFor(0);
}
}
updateConfig() {
if (this.componentType) {
this.config = Object.assign(Object.assign({}, this.componentLocatorService.getDefaultConfig(this.componentType)), this.item.config);
}
else {
this.config = null;
}
}
updateInjector() {
if (this.componentType) {
this.injector = this.createInjector();
}
else {
this.injector = null;
}
}
updateInputs() {
if (this.componentType) {
this.inputs.items = this.item.items;
this.inputs.config = this.getConfig();
}
else {
this.inputs.items = this.inputs.config = null;
}
}
updateAttributes() {
if (this.componentType) {
this.attributes = this.item.attributes || null;
if (this.item.id) {
this.attributes = Object.assign(Object.assign({}, this.attributes), { id: this.item.id });
}
}
}
updateDirectives() {
if (this.componentType && this.item.classes) {
this.directives = [
dynamicDirectiveDef(NgClass, { ngClass: this.item.classes }),
];
}
else {
this.directives = [];
}
}
getConfig() {
return (this.configurationService.decode(this.componentLocatorService.getConfigType(this.componentType), this.config, this.injector) || {});
}
createInjector() {
return this.mappedInjectorFactory.create(this.createLocalInjector());
}
createLocalInjector() {
return createLocalInjector({
parentInjector: this.injectorRegistryService,
getComponent: () => this.compRef.instance,
getConfig: () => this.inputs.config,
updateConfig: (config) => {
this.markForCheck();
return (this.inputs.config = Object.assign(Object.assign({}, this.inputs.config), config));
},
isConfigValid: () => isRight(this.configurationService.validate(this.componentLocatorService.getConfigType(this.componentType), this.inputs.config)),
getContext: () => this.context,
});
}
updateHandlers() {
this.disposeHandlers();
if (!this.item.handlers || !this.compRef || !this.compFactory) {
return;
}
const { handlers } = this.item;
this.disposableHandlers = Object.keys(handlers)
.map((event) => ({
event,
handler: this.decodeHandler(handlers[event]),
}))
.filter(({ handler }) => handler)
.map(({ event, handler }) => this.attachHandler(event, handler));
}
decodeHandler(handler) {
const fn = this.configurationService.decode(Handler, { handler }, this.injector).handler;
return typeof fn === 'function' ? fn : null;
}
attachHandler(event, handler) {
const outputInfo = this.compFactory.outputs.find((output) => output.templateName === event);
if (outputInfo) {
const output = this.compRef.instance[outputInfo.propName];
const sub = output.subscribe(handler);
return () => sub.unsubscribe();
}
return this.renderer.listen(this.compRef.location.nativeElement, event, handler);
}
disposeHandlers() {
this.disposableHandlers.forEach((disposeHandler) => disposeHandler());
this.disposableHandlers = [];
}
updateContextInput() {
if (this.componentType) {
this.inputs.context = this.context;
}
else {
this.inputs.context = null;
}
}
}
/** @nocollapse */ /** @nocollapse */ RenderItemComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: RenderItemComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }, { token: i0.ComponentFactoryResolver }, { token: ComponentLocatorService }, { token: ComponentsRegistryService }, { token: ConfigurationService }, { token: MappedInjectorFactory }, { token: InjectorRegistryService }], target: i0.ɵɵFactoryTarget.Component });
/** @nocollapse */ /** @nocollapse */ RenderItemComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.0", type: RenderItemComponent, selector: "orc-render-item", inputs: { item: "item", context: "context" }, outputs: { componentCreated: "componentCreated", childComponentsCreated: "childComponentsCreated" }, providers: [
{ provide: RenderComponent, useExisting: RenderItemComponent },
ComponentsRegistryService,
InjectorRegistryService,
MappedInjectorFactory,
], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<ndc-dynamic\n [ndcDynamicComponent]=\"componentType\"\n [ndcDynamicInputs]=\"inputs\"\n [ndcDynamicInjector]=\"injector\"\n [ndcDynamicAttributes]=\"attributes\"\n [ndcDynamicDirectives]=\"directives\"\n (ndcDynamicCreated)=\"onComponentCreated($event)\"\n></ndc-dynamic>\n", components: [{ type: i6.DynamicComponent, selector: "ndc-dynamic", inputs: ["ndcDynamicComponent", "ndcDynamicInjector", "ndcDynamicProviders", "ndcDynamicContent"], outputs: ["ndcDynamicCreated"] }], directives: [{ type: i6.DynamicIoDirective, selector: "[ndcDynamicInputs],[ndcDynamicOutputs],[ngComponentOutletNdcDynamicInputs],[ngComponentOutletNdcDynamicOutputs]", inputs: ["ndcDynamicInputs", "ngComponentOutletNdcDynamicInputs", "ndcDynamicOutputs", "ngComponentOutletNdcDynamicOutputs"] }, { type: i6.DynamicAttributesDirective, selector: "[ndcDynamicAttributes],[ngComponentOutletNdcDynamicAttributes]", inputs: ["ndcDynamicAttributes", "ngComponentOutletNdcDynamicAttributes"], exportAs: ["ndcDynamicAttributes"] }, { type: i6.DynamicDirectivesDirective, selector: "[ndcDynamicDirectives],[ngComponentOutletNdcDynamicDirectives]", inputs: ["ndcDynamicDirectives", "ngComponentOutletNdcDynamicDirectives"], outputs: ["ndcDynamicDirectivesCreated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: RenderItemComponent, decorators: [{
type: Component,
args: [{ selector: 'orc-render-item', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
{ provide: RenderComponent, useExisting: RenderItemComponent },
ComponentsRegistryService,
InjectorRegistryService,
MappedInjectorFactory,
], template: "<ndc-dynamic\n [ndcDynamicComponent]=\"componentType\"\n [ndcDynamicInputs]=\"inputs\"\n [ndcDynamicInjector]=\"injector\"\n [ndcDynamicAttributes]=\"attributes\"\n [ndcDynamicDirectives]=\"directives\"\n (ndcDynamicCreated)=\"onComponentCreated($event)\"\n></ndc-dynamic>\n" }]
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }, { type: i0.ComponentFactoryResolver }, { type: ComponentLocatorService }, { type: ComponentsRegistryService }, { type: ConfigurationService }, { type: MappedInjectorFactory }, { type: InjectorRegistryService }]; }, propDecorators: { item: [{
type: Input
}], context: [{
type: Input
}], componentCreated: [{
type: Output
}], childComponentsCreated: [{
type: Output
}] } });
class OrchestratorComponent {
constructor() {
this.compCreated$ = new Subject();
this.childCompsCreated$ = new Subject();
this.componentsCreated = combineLatest([
this.compCreated$,
this.childCompsCreated$,
]).pipe(map$1(([comp, comps]) => [comp, ...comps]));
}
compCreated(compRef) {
this.compCreated$.next(compRef);
}
childCompsCreated(compRefs) {
this.childCompsCreated$.next(compRefs);
}
}
/** @nocollapse */ /** @nocollapse */ OrchestratorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
/** @nocollapse */ /** @nocollapse */ OrchestratorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.0", type: OrchestratorComponent, selector: "orc-orchestrator", inputs: { config: "config", context: "context" }, outputs: { componentsCreated: "componentsCreated" }, ngImport: i0, template: "<orc-render-item\n [item]=\"config\"\n [context]=\"context\"\n (componentCreated)=\"compCreated($event)\"\n (childComponentsCreated)=\"childCompsCreated($event)\"\n></orc-render-item>\n", components: [{ type: RenderItemComponent, selector: "orc-render-item", inputs: ["item", "context"], outputs: ["componentCreated", "childComponentsCreated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorComponent, decorators: [{
type: Component,
args: [{ selector: 'orc-orchestrator', changeDetection: ChangeDetectionStrategy.OnPush, template: "<orc-render-item\n [item]=\"config\"\n [context]=\"context\"\n (componentCreated)=\"compCreated($event)\"\n (childComponentsCreated)=\"childCompsCreated($event)\"\n></orc-render-item>\n" }]
}], propDecorators: { config: [{
type: Input
}], context: [{
type: Input
}], componentsCreated: [{
type: Output
}] } });
class OrchestratorCoreModule {
/**
* Use this to import module in root application only once
*/
static forRoot() {
return {
ngModule: OrchestratorCoreModule,
providers: [...OrchestratorCoreModule.getRootProviders()],
};
}
/**
* Use this to import module with components in root application only once
*/
static withComponents(components) {
return {
ngModule: OrchestratorCoreModule,
providers: [
...OrchestratorCoreModule.getRootProviders(),
...OrchestratorCoreModule.registerComponents(components),
],
};
}
/**
* Use this to provide custom components for {@link OrchestratorCoreModule}
*/
static registerComponents(components) {
return [{ provide: COMPONENTS, useValue: components, multi: true }];
}
static getRootProviders() {
return [
{ provide: ErrorStrategy, useClass: ThrowErrorStrategy },
...INJECTOR_MAP_PROVIDERS,
ComponentLocatorService,
ConfigurationService,
];
}
}
/** @nocollapse */ /** @nocollapse */ OrchestratorCoreModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorCoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
/** @nocollapse */ /** @nocollapse */ OrchestratorCoreModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorCoreModule, declarations: [OrchestratorComponent, RenderItemComponent], imports: [CommonModule,
DynamicModule,
DynamicAttributesModule,
DynamicDirectivesModule], exports: [OrchestratorComponent, RenderItemComponent] });
/** @nocollapse */ /** @nocollapse */ OrchestratorCoreModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorCoreModule, imports: [[
CommonModule,
DynamicModule,
DynamicAttributesModule,
DynamicDirectivesModule,
]] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.0", ngImport: i0, type: OrchestratorCoreModule, decorators: [{
type: NgModule,
args: [{
imports: [
CommonModule,
DynamicModule,
DynamicAttributesModule,
DynamicDirectivesModule,
],
declarations: [OrchestratorComponent, RenderItemComponent],
exports: [OrchestratorComponent, RenderItemComponent],
}]
}] });
class SuppressErrorStrategy extends ErrorStrategy {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
handle(error) {
// Not doing anything here...
}
}
// Core
/**
* Generated bundle index. Do not edit.
*/
export { COMPONENTS, CUSTOM_FUNCTION_ARGUMENT_PREFIX, ConfigurationService, DynamicComponent, ErrorStrategy, FunctionError, FunctionFromMeta, FunctionFromString, FunctionWithMeta, INJECTOR_MAP_TOKEN, InjectorRegistryService, InvalidConfigurationError, LOCAL_GET_COMPONENT, LOCAL_GET_CONFIG, LOCAL_GET_CONFIG_VALID, LOCAL_GET_CONTEXT, LOCAL_GET_INJECTOR, LOCAL_INJECTOR_MAP, LOCAL_UPDATE_CONFIG, MappedInjector, MappedInjectorFactory, Option, OptionAllowedValues, OptionFunction, OptionInteger, OptionNotPresent, OptionRange, OptionRequired, OptionType, OptionTypeFactory, OrchestratorComponent, OrchestratorCoreModule, RenderComponent, RenderItemComponent, STATIC_INJECTOR_MAP, STATIC_INJECT_FLAGS, SuppressErrorStrategy, ThrowErrorStrategy, classToType, getLocalProviders, getStaticProviders, provideInjectorMap };
//# sourceMappingURL=orchestrator-core.mjs.map