UNPKG

@o3r/rules-engine

Version:

This module provides a rule engine that can be executed on your Otter application to customize your application (translations, placeholders and configs) based on a json file generated by your CMS.

2,573 lines 175 kB
import * as i0 from '@angular/core';
import { Pipe, input, signal, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, Input, NgModule, InjectionToken, Injectable, Optional, Inject, inject, DestroyRef } from '@angular/core';
import * as i1 from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i1$1 from '@angular/common';
import { CommonModule, JsonPipe } from '@angular/common';
import { Subject, of, from, ReplaySubject, Observable, debounceTime, combineLatest, BehaviorSubject, merge, firstValueFrom, fromEvent } from 'rxjs';
import { switchMap, delay, startWith, mergeMap, map, catchError, withLatestFrom, concatMap, tap, share, shareReplay, pairwise, distinctUntilChanged, takeUntil, filter, scan } from 'rxjs/operators';
import * as i1$3 from '@ngrx/store';
import { createAction, props, on, createReducer, StoreModule, createFeatureSelector, createSelector, select } from '@ngrx/store';
import * as i2 from '@o3r/logger';
import { LoggerModule } from '@o3r/logger';
import { asyncProps, fromApiEffectSwitchMap, asyncStoreItemAdapter, computeItemIdentifier, sendOtterMessage, filterMessageContent } from '@o3r/core';
import * as i1$2 from '@ngrx/effects';
import { createEffect, ofType, EffectsModule } from '@ngrx/effects';
import { createEntityAdapter } from '@ngrx/entity';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { JSONPath } from 'jsonpath-plus';

class O3rFallbackToPipe {
    transform(value, fallback = 'undefined') {
        return value === undefined ? fallback : value;
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: O3rFallbackToPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    /** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.13", ngImport: i0, type: O3rFallbackToPipe, isStandalone: true, name: "o3rFallbackTo" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: O3rFallbackToPipe, decorators: [{
            type: Pipe,
            args: [{ name: 'o3rFallbackTo' }]
        }] });

class O3rJsonOrStringPipe {
    /**
     * @inheritDoc
     */
    transform(value) {
        if (typeof value === 'string') {
            return value;
        }
        return JSON.stringify(value, null, 2);
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: O3rJsonOrStringPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    /** @nocollapse */ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.13", ngImport: i0, type: O3rJsonOrStringPipe, isStandalone: true, name: "o3rJsonOrString" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: O3rJsonOrStringPipe, decorators: [{
            type: Pipe,
            args: [{ name: 'o3rJsonOrString' }]
        }] });

/**
 * Compute the status of the execution depending on its execution event type, the output and whether the execution
 * is still active
 * @param rulesetExecution
 * @param isActive
 */
const getStatus = (rulesetExecution, isActive) => {
    if (rulesetExecution.type === 'RulesetExecutionError') {
        return 'Error';
    }
    else if (rulesetExecution.outputActions?.length === 0) {
        return 'NoEffect';
    }
    else if (isActive) {
        return 'Active';
    }
    return 'Deactivated';
};
/**
 * Transform the output of the debug reports into the model for the ruleset history debug panel
 * @param events
 * @param rulesetMap
 */
const rulesetReportToHistory = (events, rulesetMap) => {
    const availableRulesets = (events.filter((e) => e.type === 'AvailableRulesets').reverse()[0])?.availableRulesets || [];
    const lastActiveRulesets = (events.filter((e) => e.type === 'ActiveRulesets').reverse()[0])?.rulesets || [];
    return availableRulesets
        .filter((ruleset) => !!ruleset)
        .reduce((acc, ruleset) => {
        const rulesetExecutions = events
            .filter((e) => ((e.type === 'RulesetExecutionError' || e.type === 'RulesetExecution') && e.rulesetId === ruleset.id));
        if (rulesetExecutions) {
            acc.push(...rulesetExecutions);
        }
        return acc;
    }, [])
        .sort((execA, execB) => execB.timestamp - execA.timestamp)
        .map((rulesetExecution) => {
        const rulesetInformation = rulesetMap[rulesetExecution.rulesetId];
        const isActive = lastActiveRulesets.find((r) => r.id === rulesetExecution.rulesetId);
        return {
            ...rulesetExecution,
            status: getStatus(rulesetExecution, !!isActive),
            isActive: !!isActive,
            rulesetInformation,
            rulesEvaluations: (rulesetExecution.rulesEvaluations || []).sort((evalA, evalB) => (rulesetInformation?.rules.findIndex((r) => r.id === evalA.rule.id) || -1)
                - (rulesetInformation?.rules.findIndex((r) => r.id === evalB.rule.id) || -1))
        };
    });
};

class FactsSnapshotComponent {
    constructor() {
        /**
         * Full list of available facts with their current value
         */
        this.facts = input.required();
        /**
         * Search terms
         */
        this.search = signal('');
        /**
         * Filtered list of facts using search terms
         */
        this.filteredFacts = computed(() => {
            const search = this.search();
            const facts = this.facts();
            if (search) {
                const matchString = new RegExp(search.replace(/[\s#$()*+,.?[\\\]^{|}-]/g, '\\$&'), 'i');
                return facts.filter(({ factName, value }) => matchString.test(factName)
                    || (typeof value === 'object'
                        ? matchString.test(JSON.stringify(value))
                        : matchString.test(String(value))));
            }
            else {
                return facts;
            }
        });
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: FactsSnapshotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.13", type: FactsSnapshotComponent, isStandalone: true, selector: "o3r-facts-snapshot", inputs: { facts: { classPropertyName: "facts", publicName: "facts", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<section>\n  @if (facts().length < 1) {\n    <div class=\"m-2\">\n      No facts are registered.\n    </div>\n  } @else {\n    <div class=\"input-group\">\n      <label class=\"input-group-text\" for=\"search-fact\">\n        <span class=\"mx-1 fa-search\" aria-label=\"Search\"></span>\n      </label>\n      <input class=\"form-control\" [(ngModel)]=\"search\" type=\"text\" id=\"search-fact\" placeholder=\"Search for fact name or value\" />\n    </div>\n    <div class=\"mt-3\">\n      List of <b>{{facts().length}}</b> registered facts\n      @if (facts().length > filteredFacts().length) { (<b>{{filteredFacts().length}}</b> matching the search) }\n    </div>\n    <ul>\n    @for (fact of filteredFacts(); track fact.factName) {\n      <li>\n        <span class=\"fact-name\">{{fact.factName}}: </span>\n        <span class=\"fact-value\">{{fact.value | o3rJsonOrString}}</span>\n      </li>\n    }\n    </ul>\n  }\n</section>\n", styles: ["o3r-facts-snapshot .fact-name{color:#26c}o3r-facts-snapshot .fact-value{color:#c29}o3r-facts-snapshot .fact-name,o3r-facts-snapshot .fact-value{font-family:monospace}\n"], dependencies: [{ kind: "pipe", type: O3rJsonOrStringPipe, name: "o3rJsonOrString" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: FactsSnapshotComponent, decorators: [{
            type: Component,
            args: [{ selector: 'o3r-facts-snapshot', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
                        O3rJsonOrStringPipe,
                        FormsModule,
                        ReactiveFormsModule
                    ], encapsulation: ViewEncapsulation.None, template: "<section>\n  @if (facts().length < 1) {\n    <div class=\"m-2\">\n      No facts are registered.\n    </div>\n  } @else {\n    <div class=\"input-group\">\n      <label class=\"input-group-text\" for=\"search-fact\">\n        <span class=\"mx-1 fa-search\" aria-label=\"Search\"></span>\n      </label>\n      <input class=\"form-control\" [(ngModel)]=\"search\" type=\"text\" id=\"search-fact\" placeholder=\"Search for fact name or value\" />\n    </div>\n    <div class=\"mt-3\">\n      List of <b>{{facts().length}}</b> registered facts\n      @if (facts().length > filteredFacts().length) { (<b>{{filteredFacts().length}}</b> matching the search) }\n    </div>\n    <ul>\n    @for (fact of filteredFacts(); track fact.factName) {\n      <li>\n        <span class=\"fact-name\">{{fact.factName}}: </span>\n        <span class=\"fact-value\">{{fact.value | o3rJsonOrString}}</span>\n      </li>\n    }\n    </ul>\n  }\n</section>\n", styles: ["o3r-facts-snapshot .fact-name{color:#26c}o3r-facts-snapshot .fact-value{color:#c29}o3r-facts-snapshot .fact-name,o3r-facts-snapshot .fact-value{font-family:monospace}\n"] }]
        }] });

/**
 * Duration of the notification for clipboard feature (in milliseconds)
 */
const NOTIFICATION_DURATION = 1750;
/**
 * Minimal length required to enable clipboard feature
 */
const CLIPBOARD_FEATURE_LENGTH_THRESHOLD = 80;
class RuleKeyValuePresComponent {
    constructor() {
        /**
         * Type of display:
         * - 'state':      `key: value`, `key: oldValue -> value` or `oldValue -> value`
         * - 'assignment': `key = value`
         */
        this.type = 'state';
        this.shouldLimitCharactersForValue = true;
        this.isClipBoardFeatureAvailableForValue = false;
        this.isValuePrimitiveType = false;
        this.shouldLimitCharactersForOldValue = true;
        this.isClipBoardFeatureAvailableForOldValue = false;
        this.isOldValuePrimitiveType = false;
        this.triggerNotification = new Subject();
        this.showNotification$ = this.triggerNotification.asObservable().pipe(switchMap(() => of(false).pipe(delay(NOTIFICATION_DURATION), startWith(true))));
    }
    isClipBoardFeatureAvailable(value) {
        return !!(navigator.clipboard && value && value.length > CLIPBOARD_FEATURE_LENGTH_THRESHOLD);
    }
    ngOnChanges({ value, oldValue }) {
        if (value) {
            this.isValuePrimitiveType = value.currentValue === null || typeof value.currentValue !== 'object';
            this.isClipBoardFeatureAvailableForValue = this.isClipBoardFeatureAvailable(this.isValuePrimitiveType ? String(value.currentValue) : JSON.stringify(value.currentValue));
        }
        if (oldValue) {
            this.isOldValuePrimitiveType = oldValue.currentValue === null || typeof oldValue.currentValue !== 'object';
            this.isClipBoardFeatureAvailableForOldValue = this.isClipBoardFeatureAvailable(this.isOldValuePrimitiveType ? String(oldValue.currentValue) : JSON.stringify(oldValue.currentValue));
        }
    }
    async copyToClipBoard(content) {
        await navigator.clipboard.writeText(content);
        this.triggerNotification.next();
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RuleKeyValuePresComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: RuleKeyValuePresComponent, isStandalone: true, selector: "o3r-rule-key-value-pres", inputs: { key: "key", value: "value", oldValue: "oldValue", type: "type" }, usesOnChanges: true, ngImport: i0, template: "<span *ngIf=\"key\" class=\"input-key\">{{key}}<ng-container *ngIf=\"type === 'state'\">: </ng-container></span>\n<ng-container *ngIf=\"type === 'assignment'\"> = </ng-container>\n<ng-container *ngIf=\"oldValue\">\n  <pre class=\"input-value\"\n    [class.limit-characters]=\"shouldLimitCharactersForOldValue\"\n    (click)=\"shouldLimitCharactersForOldValue = !shouldLimitCharactersForOldValue\"\n    (keyup.enter)=\"shouldLimitCharactersForOldValue = !shouldLimitCharactersForOldValue\"\n    tabindex=\"0\"\n  >\n    {{isOldValuePrimitiveType ? oldValue : (oldValue | json)}}\n  </pre>\n  <button (click)=\"copyToClipBoard(oldValue)\" *ngIf=\"isClipBoardFeatureAvailableForOldValue\" title=\"Copy to clipboard\">\uD83D\uDCCB</button>\n  \u2192\n</ng-container>\n<pre class=\"input-value\"\n  [class.limit-characters]=\"shouldLimitCharactersForValue\"\n  (click)=\"shouldLimitCharactersForValue = !shouldLimitCharactersForValue\"\n  (keyup.enter)=\"shouldLimitCharactersForValue = !shouldLimitCharactersForValue\"\n  tabindex=\"0\"\n>\n  {{isValuePrimitiveType ? value : (value | json)}}\n</pre>\n<button (click)=\"copyToClipBoard(value)\" *ngIf=\"isClipBoardFeatureAvailableForValue\" title=\"Copy to clipboard\">\uD83D\uDCCB</button>\n<div role=\"alert\" class=\"notification\" *ngIf=\"showNotification$ | async\">Copied to clipboard</div>\n", styles: ["o3r-rule-key-value-pres{position:relative}o3r-rule-key-value-pres .ruleset-panel-title,o3r-rule-key-value-pres .ruleset-panel-category-title{display:flex;justify-content:space-between;align-items:center}o3r-rule-key-value-pres .ruleset-panel-title{font-size:1rem;padding:.5rem 0 .1rem}o3r-rule-key-value-pres .ruleset-expansion-action,o3r-rule-key-value-pres .icon-caret-down,o3r-rule-key-value-pres .icon-caret-up{cursor:pointer}o3r-rule-key-value-pres .ruleset-panel-subtitle{font-size:.75rem}o3r-rule-key-value-pres .ruleset-panel-title-aside{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;min-width:fit-content}o3r-rule-key-value-pres .ruleset-panel-category-title{font-size:.95rem;background:#eee;padding:.5rem;margin-bottom:.5rem;margin-top:1rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-title{font-size:.893rem;cursor:default}o3r-rule-key-value-pres .rule-description .ruleset-panel-title{font-size:.94rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-body{padding-bottom:.5rem;padding-left:1.5rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-body:empty{margin:0;padding:0 0 0 1.5rem}o3r-rule-key-value-pres .limit-characters{display:inline-block;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;max-width:38rem;vertical-align:bottom}o3r-rule-key-value-pres .input-value{margin-top:0;margin-bottom:0}o3r-rule-key-value-pres button{background:none;border:0;appearance:none}o3r-rule-key-value-pres .notification{position:absolute;padding:1rem 1.5rem;border-radius:5px;background:#444;color:#eee;right:0;z-index:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RuleKeyValuePresComponent, decorators: [{
            type: Component,
            args: [{ selector: 'o3r-rule-key-value-pres', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [CommonModule, JsonPipe], template: "<span *ngIf=\"key\" class=\"input-key\">{{key}}<ng-container *ngIf=\"type === 'state'\">: </ng-container></span>\n<ng-container *ngIf=\"type === 'assignment'\"> = </ng-container>\n<ng-container *ngIf=\"oldValue\">\n  <pre class=\"input-value\"\n    [class.limit-characters]=\"shouldLimitCharactersForOldValue\"\n    (click)=\"shouldLimitCharactersForOldValue = !shouldLimitCharactersForOldValue\"\n    (keyup.enter)=\"shouldLimitCharactersForOldValue = !shouldLimitCharactersForOldValue\"\n    tabindex=\"0\"\n  >\n    {{isOldValuePrimitiveType ? oldValue : (oldValue | json)}}\n  </pre>\n  <button (click)=\"copyToClipBoard(oldValue)\" *ngIf=\"isClipBoardFeatureAvailableForOldValue\" title=\"Copy to clipboard\">\uD83D\uDCCB</button>\n  \u2192\n</ng-container>\n<pre class=\"input-value\"\n  [class.limit-characters]=\"shouldLimitCharactersForValue\"\n  (click)=\"shouldLimitCharactersForValue = !shouldLimitCharactersForValue\"\n  (keyup.enter)=\"shouldLimitCharactersForValue = !shouldLimitCharactersForValue\"\n  tabindex=\"0\"\n>\n  {{isValuePrimitiveType ? value : (value | json)}}\n</pre>\n<button (click)=\"copyToClipBoard(value)\" *ngIf=\"isClipBoardFeatureAvailableForValue\" title=\"Copy to clipboard\">\uD83D\uDCCB</button>\n<div role=\"alert\" class=\"notification\" *ngIf=\"showNotification$ | async\">Copied to clipboard</div>\n", styles: ["o3r-rule-key-value-pres{position:relative}o3r-rule-key-value-pres .ruleset-panel-title,o3r-rule-key-value-pres .ruleset-panel-category-title{display:flex;justify-content:space-between;align-items:center}o3r-rule-key-value-pres .ruleset-panel-title{font-size:1rem;padding:.5rem 0 .1rem}o3r-rule-key-value-pres .ruleset-expansion-action,o3r-rule-key-value-pres .icon-caret-down,o3r-rule-key-value-pres .icon-caret-up{cursor:pointer}o3r-rule-key-value-pres .ruleset-panel-subtitle{font-size:.75rem}o3r-rule-key-value-pres .ruleset-panel-title-aside{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;min-width:fit-content}o3r-rule-key-value-pres .ruleset-panel-category-title{font-size:.95rem;background:#eee;padding:.5rem;margin-bottom:.5rem;margin-top:1rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-title{font-size:.893rem;cursor:default}o3r-rule-key-value-pres .rule-description .ruleset-panel-title{font-size:.94rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-body{padding-bottom:.5rem;padding-left:1.5rem}o3r-rule-key-value-pres .rule-description .ruleset-panel-category-body:empty{margin:0;padding:0 0 0 1.5rem}o3r-rule-key-value-pres .limit-characters{display:inline-block;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;max-width:38rem;vertical-align:bottom}o3r-rule-key-value-pres .input-value{margin-top:0;margin-bottom:0}o3r-rule-key-value-pres button{background:none;border:0;appearance:none}o3r-rule-key-value-pres .notification{position:absolute;padding:1rem 1.5rem;border-radius:5px;background:#444;color:#eee;right:0;z-index:1}\n"] }]
        }], propDecorators: { key: [{
                type: Input
            }], value: [{
                type: Input
            }], oldValue: [{
                type: Input
            }], type: [{
                type: Input
            }] } });

class RuleActionsPresComponent {
    constructor() {
        /**
         * List of all the output actions of a rules or ruleset execution
         */
        this.actions = [];
        /**
         * The list of temporary facts used and/or modified within the rule or the ruleset.
         * They are scoped to the ruleset and their value is the one after the rule or ruleset execution.
         */
        this.temporaryFacts = {};
        /**
         * List of temporary facts that will be modified by the ruleset or the rule.
         */
        this.runtimeOutputs = [];
    }
    /**
     * Check if a given block is of type ActionBlock
     * @param block
     */
    isActionBlock(block) {
        return !!block.actionType;
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RuleActionsPresComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.13", type: RuleActionsPresComponent, isStandalone: true, selector: "o3r-rule-actions-pres", inputs: { actions: "actions", temporaryFacts: "temporaryFacts", runtimeOutputs: "runtimeOutputs" }, ngImport: i0, template: "<div class=\"ruleset-panel-category-title\">Output Actions</div>\n@if (actions?.length === 0 && runtimeOutputs.length === 0) {\n  <div class=\"ruleset-panel-category-body empty\">\n    No action\n  </div>\n} @else {\n  <ul class=\"ruleset-panel-category-body\">\n    @for (action of actions; track $index) {\n      <li>\n        @if (isActionBlock(action)) {\n          @switch (action.actionType) {\n            @case ('SET_FACT') {\n              <div>\n                <div>Set Fact</div>\n                <div>\n                  <o3r-rule-key-value-pres\n                    [key]=\"action.fact | o3rFallbackTo: 'Missing \\'fact\\''\"\n                    [value]=\"action.value | o3rFallbackTo\"\n                    [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n                </div>\n              </div>\n            }\n            @case ('UPDATE_CONFIG') {\n              <div>\n                <div>Update Config {{action.component}} {{action.library}}</div>\n                <div>\n                  <o3r-rule-key-value-pres\n                    [key]=\"action.property | o3rFallbackTo: 'Missing \\'property\\''\"\n                    [value]=\"action.value | o3rFallbackTo\"\n                    [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n                </div>\n              </div>\n            }\n            @case ('UPDATE_ASSET') {\n              <div>\n                <div>Update Asset:</div>\n                <div>\n                  <o3r-rule-key-value-pres\n                    [oldValue]=\"action.asset | o3rFallbackTo: 'Missing \\'asset\\''\"\n                    [value]=\"action.value | o3rFallbackTo\"\n                    [type]=\"'state'\"></o3r-rule-key-value-pres>\n                </div>\n              </div>\n            }\n            @case ('UPDATE_LOCALISATION') {\n              <div>\n                <div>Update localization:</div>\n                <div>\n                  <o3r-rule-key-value-pres\n                    [oldValue]=\"action.key | o3rFallbackTo: 'Missing \\'key\\''\"\n                    [value]=\"action.value | o3rFallbackTo\"\n                    [type]=\"'state'\"></o3r-rule-key-value-pres>\n                </div>\n              </div>\n            }\n            @case ('UPDATE_PLACEHOLDER') {\n              <div [class.error]=\"!action.placeholderId\">\n                <div>Update placeholder in {{action.component}} {{action.library}}</div>\n                <div>\n                  <o3r-rule-key-value-pres\n                    [oldValue]=\"action.placeholderId | o3rFallbackTo: 'Missing \\'placeholderId\\''\"\n                    [value]=\"action.value\"\n                    [type]=\"'state'\"></o3r-rule-key-value-pres>\n                </div>\n              </div>\n            }\n            @default {\n              <div class=\"error\">\n                <div>Unrecognized action</div>\n                <div>{{action | json}}</div>\n              </div>\n            }\n          }\n        }\n      </li>\n    }\n    @for (runtimeOutput of runtimeOutputs; track $index) {\n      <li>\n        <div>Set temporary fact</div>\n        <div>\n          <o3r-rule-key-value-pres\n            [key]=\"runtimeOutput | o3rFallbackTo: 'Missing \\'fact\\''\"\n            [value]=\"temporaryFacts[runtimeOutput] | o3rFallbackTo\"\n            [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n        </div>\n      </li>\n    }\n  </ul>\n}\n", styles: ["o3r-rule-actions-pres .ruleset-panel-title,o3r-rule-actions-pres .ruleset-panel-category-title{display:flex;justify-content:space-between;align-items:center}o3r-rule-actions-pres .ruleset-panel-title{font-size:1rem;padding:.5rem 0 .1rem}o3r-rule-actions-pres .ruleset-expansion-action,o3r-rule-actions-pres .icon-caret-down,o3r-rule-actions-pres .icon-caret-up{cursor:pointer}o3r-rule-actions-pres .ruleset-panel-subtitle{font-size:.75rem}o3r-rule-actions-pres .ruleset-panel-title-aside{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;min-width:fit-content}o3r-rule-actions-pres .ruleset-panel-category-title{font-size:.95rem;background:#eee;padding:.5rem;margin-bottom:.5rem;margin-top:1rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-title{font-size:.893rem;cursor:default}o3r-rule-actions-pres .rule-description .ruleset-panel-title{font-size:.94rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-body{padding-bottom:.5rem;padding-left:1.5rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-body:empty{margin:0;padding:0 0 0 1.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "component", type: RuleKeyValuePresComponent, selector: "o3r-rule-key-value-pres", inputs: ["key", "value", "oldValue", "type"] }, { kind: "pipe", type: O3rFallbackToPipe, name: "o3rFallbackTo" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RuleActionsPresComponent, decorators: [{
            type: Component,
            args: [{ selector: 'o3r-rule-actions-pres', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [CommonModule, JsonPipe, RuleKeyValuePresComponent, O3rFallbackToPipe], template: "<div class=\"ruleset-panel-category-title\">Output Actions</div>\n@if (actions?.length === 0 && runtimeOutputs.length === 0) {\n  <div class=\"ruleset-panel-category-body empty\">\n    No action\n  </div>\n} @else {\n  <ul class=\"ruleset-panel-category-body\">\n    @for (action of actions; track $index) {\n      <li>\n        @if (isActionBlock(action)) {\n          @switch (action.actionType) {\n            @case ('SET_FACT') {\n              <div>\n                <div>Set Fact</div>\n                <div>\n                  <o3r-rule-key-value-pres\n                    [key]=\"action.fact | o3rFallbackTo: 'Missing \\'fact\\''\"\n                    [value]=\"action.value | o3rFallbackTo\"\n                    [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n                </div>\n              </div>\n            }\n            @case ('UPDATE_CONFIG') {\n              <div>\n                <div>Update Config {{action.component}} {{action.library}}</div>\n                <div>\n                  <o3r-rule-key-value-pres\n                    [key]=\"action.property | o3rFallbackTo: 'Missing \\'property\\''\"\n                    [value]=\"action.value | o3rFallbackTo\"\n                    [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n                </div>\n              </div>\n            }\n            @case ('UPDATE_ASSET') {\n              <div>\n                <div>Update Asset:</div>\n                <div>\n                  <o3r-rule-key-value-pres\n                    [oldValue]=\"action.asset | o3rFallbackTo: 'Missing \\'asset\\''\"\n                    [value]=\"action.value | o3rFallbackTo\"\n                    [type]=\"'state'\"></o3r-rule-key-value-pres>\n                </div>\n              </div>\n            }\n            @case ('UPDATE_LOCALISATION') {\n              <div>\n                <div>Update localization:</div>\n                <div>\n                  <o3r-rule-key-value-pres\n                    [oldValue]=\"action.key | o3rFallbackTo: 'Missing \\'key\\''\"\n                    [value]=\"action.value | o3rFallbackTo\"\n                    [type]=\"'state'\"></o3r-rule-key-value-pres>\n                </div>\n              </div>\n            }\n            @case ('UPDATE_PLACEHOLDER') {\n              <div [class.error]=\"!action.placeholderId\">\n                <div>Update placeholder in {{action.component}} {{action.library}}</div>\n                <div>\n                  <o3r-rule-key-value-pres\n                    [oldValue]=\"action.placeholderId | o3rFallbackTo: 'Missing \\'placeholderId\\''\"\n                    [value]=\"action.value\"\n                    [type]=\"'state'\"></o3r-rule-key-value-pres>\n                </div>\n              </div>\n            }\n            @default {\n              <div class=\"error\">\n                <div>Unrecognized action</div>\n                <div>{{action | json}}</div>\n              </div>\n            }\n          }\n        }\n      </li>\n    }\n    @for (runtimeOutput of runtimeOutputs; track $index) {\n      <li>\n        <div>Set temporary fact</div>\n        <div>\n          <o3r-rule-key-value-pres\n            [key]=\"runtimeOutput | o3rFallbackTo: 'Missing \\'fact\\''\"\n            [value]=\"temporaryFacts[runtimeOutput] | o3rFallbackTo\"\n            [type]=\"'assignment'\"></o3r-rule-key-value-pres>\n        </div>\n      </li>\n    }\n  </ul>\n}\n", styles: ["o3r-rule-actions-pres .ruleset-panel-title,o3r-rule-actions-pres .ruleset-panel-category-title{display:flex;justify-content:space-between;align-items:center}o3r-rule-actions-pres .ruleset-panel-title{font-size:1rem;padding:.5rem 0 .1rem}o3r-rule-actions-pres .ruleset-expansion-action,o3r-rule-actions-pres .icon-caret-down,o3r-rule-actions-pres .icon-caret-up{cursor:pointer}o3r-rule-actions-pres .ruleset-panel-subtitle{font-size:.75rem}o3r-rule-actions-pres .ruleset-panel-title-aside{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;min-width:fit-content}o3r-rule-actions-pres .ruleset-panel-category-title{font-size:.95rem;background:#eee;padding:.5rem;margin-bottom:.5rem;margin-top:1rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-title{font-size:.893rem;cursor:default}o3r-rule-actions-pres .rule-description .ruleset-panel-title{font-size:.94rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-body{padding-bottom:.5rem;padding-left:1.5rem}o3r-rule-actions-pres .rule-description .ruleset-panel-category-body:empty{margin:0;padding:0 0 0 1.5rem}\n"] }]
        }], propDecorators: { actions: [{
                type: Input
            }], temporaryFacts: [{
                type: Input
            }], runtimeOutputs: [{
                type: Input
            }] } });

class RuleConditionPresComponent {
    constructor() {
        /**
         * Left hand operator as it will be displayed in the template.
         * In the case of a fact with a json path, will resolve the whole fact path, else will only display the value
         */
        this.lhs = 'undefined';
    }
    /**
     * Rule condition that will be flattened by the component setter
     */
    set condition(condition) {
        this._condition = condition;
        this.lhs = condition?.lhs ? this.getOperandName(condition.lhs) : 'undefined';
        this.rhs = condition?.rhs ? this.getOperandName(condition.rhs) : undefined;
    }
    get condition() {
        return this._condition;
    }
    getOperandName(operand) {
        const value = `${operand.value ?? 'MISSING_VALUE'}`;
        return operand.path ? operand.path.replace(/^\$/, value) : value;
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RuleConditionPresComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: RuleConditionPresComponent, isStandalone: true, selector: "o3r-rule-condition-pres", inputs: { condition: "condition" }, ngImport: i0, template: "<ng-container *ngIf=\"!condition; else displayConditions\">\n  <span class=\"input-value\">true</span>\n</ng-container>\n<ng-template #displayConditions>\n  <ng-container *ngIf=\"!$any(condition).all && !$any(condition).any && !$any(condition).not\">\n    <span class=\"input-key\">{{ lhs }}</span> {{ $any(condition).operator }} <span class=\"input-value\" *ngIf=\"rhs !== undefined\">{{ rhs }}</span>\n  </ng-container>\n  <ng-container *ngIf=\"$any(condition).all || $any(condition).any || $any(condition).not\">\n    <span *ngIf=\"$any(condition).all\">ALL</span>\n    <span *ngIf=\"$any(condition).any\">ANY</span>\n    <span *ngIf=\"$any(condition).not\">NOT</span>\n    <span>(\n    <ng-container *ngFor=\"let cond of $any(condition).all || $any(condition).any || [$any(condition).not]; let last = last;\">\n      <o3r-rule-condition-pres [condition]=\"cond\"></o3r-rule-condition-pres>\n      <span *ngIf=\"!last\">, </span>\n    </ng-container>\n  )</span>\n  </ng-container>\n</ng-template>\n", styles: ["o3r-rule-condition-pres{word-break:break-word}\n"], dependencies: [{ kind: "component", type: RuleConditionPresComponent, selector: "o3r-rule-condition-pres", inputs: ["condition"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RuleConditionPresComponent, decorators: [{
            type: Component,
            args: [{ selector: 'o3r-rule-condition-pres', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [CommonModule], template: "<ng-container *ngIf=\"!condition; else displayConditions\">\n  <span class=\"input-value\">true</span>\n</ng-container>\n<ng-template #displayConditions>\n  <ng-container *ngIf=\"!$any(condition).all && !$any(condition).any && !$any(condition).not\">\n    <span class=\"input-key\">{{ lhs }}</span> {{ $any(condition).operator }} <span class=\"input-value\" *ngIf=\"rhs !== undefined\">{{ rhs }}</span>\n  </ng-container>\n  <ng-container *ngIf=\"$any(condition).all || $any(condition).any || $any(condition).not\">\n    <span *ngIf=\"$any(condition).all\">ALL</span>\n    <span *ngIf=\"$any(condition).any\">ANY</span>\n    <span *ngIf=\"$any(condition).not\">NOT</span>\n    <span>(\n    <ng-container *ngFor=\"let cond of $any(condition).all || $any(condition).any || [$any(condition).not]; let last = last;\">\n      <o3r-rule-condition-pres [condition]=\"cond\"></o3r-rule-condition-pres>\n      <span *ngIf=\"!last\">, </span>\n    </ng-container>\n  )</span>\n  </ng-container>\n</ng-template>\n", styles: ["o3r-rule-condition-pres{word-break:break-word}\n"] }]
        }], propDecorators: { condition: [{
                type: Input
            }] } });

class RuleTreePresComponent {
    constructor() {
        /**
         * Type of the block being resolved.
         * A type "IF_ELSE" will display two branches and the success and failure outputs associated
         * Else, only the successElements will be shown
         */
        this.blockType = '';
        /**
         * If case output
         */
        this.successElements = [];
        /**
         * Else case output
         */
        this.failureElements = [];
        /**
         * Should the "Else case scenario" actions be displayed
         */
        this.failureActionsExpanded = false;
        /**
         * Should the "If case scenario" actions be displayed
         */
        this.successActionsExpanded = false;
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RuleTreePresComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: RuleTreePresComponent, isStandalone: true, selector: "o3r-rule-tree-pres", inputs: { name: "name", blockType: "blockType", condition: "condition", successElements: "successElements", failureElements: "failureElements" }, ngImport: i0, template: "<span *ngIf=\"name\">{{name | titlecase}}:</span>\n<div class=\"rule-wrapper tree\">\n  <ng-container *ngIf=\"blockType === 'IF_ELSE'; else noCondition\">\n    <div class=\"tree-root\" *ngIf=\"!name\" [attr.aria-hidden]=\"true\">\n      <div></div>\n      <div></div>\n    </div>\n    <div class=\"rule-conditions\">\n      <div class=\"rule-conditions-title\">If\n        <o3r-rule-condition-pres [condition]=\"condition\"></o3r-rule-condition-pres>\n      </div>\n      <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n        <div></div>\n        <div></div>\n      </div>\n    </div>\n    <div class=\"rule-actions-wrapper tree-node\">\n      <div class=\"rule-actions tree-branch\">\n        <div class=\"tree-leaf\" [attr.aria-hidden]=\"true\">\n          <div></div>\n          <div></div>\n        </div>\n        <div class=\"rule-action-title success-actions\"\n            tabindex=\"0\"\n            (click)=\"successActionsExpanded = !successActionsExpanded\"\n            (keyup.enter)=\"successActionsExpanded = !successActionsExpanded\">\n          <i class=\"icon refx-icon-validate\"></i>\n          <span>Then</span>\n          <i class=\"icon\"\n            [class.icon-caret-down]=\"!successActionsExpanded\"\n            [class.icon-caret-up]=\"successActionsExpanded\">\n          </i>\n        </div>\n        <o3r-rule-actions-pres class=\"rule-tree-actions\" *ngIf=\"successActionsExpanded\"\n                              [actions]=\"successElements\">\n        </o3r-rule-actions-pres>\n        <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: successElements}\"></ng-container>\n      </div>\n      <div class=\"rule-actions tree-branch\">\n        <div class=\"tree-leaf\" [attr.aria-hidden]=\"true\">\n          <div></div>\n          <div></div>\n        </div>\n        <div class=\"rule-action-title error-actions\"\n            tabindex=\"0\"\n            (click)=\"failureActionsExpanded = !failureActionsExpanded\"\n            (keyup.enter)=\"failureActionsExpanded = !failureActionsExpanded\">\n          <i class=\"icon refx-icon-cross\"></i>\n          <span>Else</span>\n          <i class=\"icon\"\n            [class.icon-caret-down]=\"!failureActionsExpanded\"\n            [class.icon-caret-up]=\"failureActionsExpanded\">\n          </i>\n        </div>\n        <o3r-rule-actions-pres class=\"rule-tree-actions\"\n                              *ngIf=\"failureActionsExpanded\"\n                              [actions]=\"failureElements\">\n        </o3r-rule-actions-pres>\n        <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: failureElements}\"></ng-container>\n      </div>\n    </div>\n  </ng-container>\n</div>\n<ng-template #noCondition>\n  <div class=\"rule-conditions\">\n    <div class=\"rule-conditions-title\">If <span class=\"input-value\">true</span></div>\n    <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n      <div></div>\n      <div></div>\n    </div>\n  </div>\n  <div class=\"rule-actions-wrapper\">\n    <div class=\"rule-actions\">\n      <div class=\"rule-action-title success-actions\" tabindex=\"0\"\n          (keyup.enter)=\"successActionsExpanded = !successActionsExpanded\"\n          (click)=\"successActionsExpanded = !successActionsExpanded\">\n        <i class=\"icon refx-icon-validate\">\n        </i>\n        <span>Then</span>\n        <i class=\"icon\"\n          [class.icon-caret-down]=\"!successActionsExpanded\"\n          [class.icon-caret-up]=\"successActionsExpanded\">\n        </i>\n      </div>\n      <o3r-rule-actions-pres class=\"rule-tree-actions\"\n                            *ngIf=\"successActionsExpanded\"\n                            [actions]=\"successElements\">\n      </o3r-rule-actions-pres>\n      <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: successElements}\"></ng-container>\n    </div>\n  </div>\n</ng-template>\n\n<ng-template #subTree let-blocks=\"blocks\">\n  <div class=\"rule-sub-trees\">\n    <ng-container *ngFor=\"let block of blocks\">\n      <div *ngIf=\"block.blockType === 'IF_ELSE'\" class=\"tree-branch\">\n        <o3r-rule-tree-pres\n          [blockType]=\"'IF_ELSE'\"\n          [condition]=\"block.condition\"\n          [failureElements]=\"block.failureElements\"\n          [successElements]=\"block.successElements\"></o3r-rule-tree-pres>\n      </div>\n    </ng-container>\n  </div>\n</ng-template>\n", styles: ["o3r-rule-tree-pres{display:block;padding-bottom:1rem}o3r-rule-tree-pres .rule-sub-trees{display:flex}o3r-rule-tree-pres .rule-conditions-title,o3r-rule-tree-pres .rule-action-title{text-align:center}o3r-rule-tree-pres .rule-conditions-title{border:1px solid #999999;background:#fff;color:#000;padding:.2rem}o3r-rule-tree-pres .rule-action-title{background:#fff;border-radius:0;border:1px solid #999999;cursor:pointer;display:flex;justify-content:center;align-items:center;gap:.5rem}o3r-rule-tree-pres .rule-tree-actions{display:block;padding:.5rem}o3r-rule-tree-pres .rule-actions:first-child>.rule-action-title{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}o3r-rule-tree-pres .rule-actions:last-child>.rule-action-title{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}o3r-rule-tree-pres .rule-actions-wrapper{display:flex}o3r-rule-tree-pres .rule-actions-wrapper .rule-actions{flex:1 1 100%}o3r-rule-tree-pres .success-actions{border-color:#16aa32}o3r-rule-tree-pres .success-actions.rule-action-title{background:#16aa32;color:#fff}o3r-rule-tree-pres .error-actions{border-color:#c02020}o3r-rule-tree-pres .error-actions.rule-action-title{background:#c02020;color:#fff}o3r-rule-tree-pres .tree .tree-leaf,o3r-rule-tree-pres .tree .tree-root{display:flex;width:100%}o3r-rule-tree-pres .tree .tree-leaf>div,o3r-rule-tree-pres .tree .tree-root>div{height:1rem;width:50%}o3r-rule-tree-pres .tree .tree-root div:first-child{border-right:1px dashed}o3r-rule-tree-pres .tree .tree-root{margin-top:.2rem}o3r-rule-tree-pres .tree .tree-node>.tree-branch:last-child>.tree-leaf>div:first-child{border-right:1px dashed;border-top:1px dashed;border-top-right-radius:.2rem}o3r-rule-tree-pres .tree .tree-node>.tree-branch:first-child>.tree-leaf>div:last-child{border-left:1px dashed;border-top:1px dashed;border-top-left-radius:.2rem}o3r-rule-tree-pres .icon{cursor:pointer}\n"], dependencies: [{ kind: "component", type: RuleTreePresComponent, selector: "o3r-rule-tree-pres", inputs: ["name", "blockType", "condition", "successElements", "failureElements"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1$1.TitleCasePipe, name: "titlecase" }, { kind: "component", type: RuleActionsPresComponent, selector: "o3r-rule-actions-pres", inputs: ["actions", "temporaryFacts", "runtimeOutputs"] }, { kind: "component", type: RuleConditionPresComponent, selector: "o3r-rule-condition-pres", inputs: ["condition"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RuleTreePresComponent, decorators: [{
            type: Component,
            args: [{ selector: 'o3r-rule-tree-pres', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [CommonModule, RuleActionsPresComponent, RuleConditionPresComponent], template: "<span *ngIf=\"name\">{{name | titlecase}}:</span>\n<div class=\"rule-wrapper tree\">\n  <ng-container *ngIf=\"blockType === 'IF_ELSE'; else noCondition\">\n    <div class=\"tree-root\" *ngIf=\"!name\" [attr.aria-hidden]=\"true\">\n      <div></div>\n      <div></div>\n    </div>\n    <div class=\"rule-conditions\">\n      <div class=\"rule-conditions-title\">If\n        <o3r-rule-condition-pres [condition]=\"condition\"></o3r-rule-condition-pres>\n      </div>\n      <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n        <div></div>\n        <div></div>\n      </div>\n    </div>\n    <div class=\"rule-actions-wrapper tree-node\">\n      <div class=\"rule-actions tree-branch\">\n        <div class=\"tree-leaf\" [attr.aria-hidden]=\"true\">\n          <div></div>\n          <div></div>\n        </div>\n        <div class=\"rule-action-title success-actions\"\n            tabindex=\"0\"\n            (click)=\"successActionsExpanded = !successActionsExpanded\"\n            (keyup.enter)=\"successActionsExpanded = !successActionsExpanded\">\n          <i class=\"icon refx-icon-validate\"></i>\n          <span>Then</span>\n          <i class=\"icon\"\n            [class.icon-caret-down]=\"!successActionsExpanded\"\n            [class.icon-caret-up]=\"successActionsExpanded\">\n          </i>\n        </div>\n        <o3r-rule-actions-pres class=\"rule-tree-actions\" *ngIf=\"successActionsExpanded\"\n                              [actions]=\"successElements\">\n        </o3r-rule-actions-pres>\n        <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: successElements}\"></ng-container>\n      </div>\n      <div class=\"rule-actions tree-branch\">\n        <div class=\"tree-leaf\" [attr.aria-hidden]=\"true\">\n          <div></div>\n          <div></div>\n        </div>\n        <div class=\"rule-action-title error-actions\"\n            tabindex=\"0\"\n            (click)=\"failureActionsExpanded = !failureActionsExpanded\"\n            (keyup.enter)=\"failureActionsExpanded = !failureActionsExpanded\">\n          <i class=\"icon refx-icon-cross\"></i>\n          <span>Else</span>\n          <i class=\"icon\"\n            [class.icon-caret-down]=\"!failureActionsExpanded\"\n            [class.icon-caret-up]=\"failureActionsExpanded\">\n          </i>\n        </div>\n        <o3r-rule-actions-pres class=\"rule-tree-actions\"\n                              *ngIf=\"failureActionsExpanded\"\n                              [actions]=\"failureElements\">\n        </o3r-rule-actions-pres>\n        <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: failureElements}\"></ng-container>\n      </div>\n    </div>\n  </ng-container>\n</div>\n<ng-template #noCondition>\n  <div class=\"rule-conditions\">\n    <div class=\"rule-conditions-title\">If <span class=\"input-value\">true</span></div>\n    <div class=\"tree-root\" [attr.aria-hidden]=\"true\">\n      <div></div>\n      <div></div>\n    </div>\n  </div>\n  <div class=\"rule-actions-wrapper\">\n    <div class=\"rule-actions\">\n      <div class=\"rule-action-title success-actions\" tabindex=\"0\"\n          (keyup.enter)=\"successActionsExpanded = !successActionsExpanded\"\n          (click)=\"successActionsExpanded = !successActionsExpanded\">\n        <i class=\"icon refx-icon-validate\">\n        </i>\n        <span>Then</span>\n        <i class=\"icon\"\n          [class.icon-caret-down]=\"!successActionsExpanded\"\n          [class.icon-caret-up]=\"successActionsExpanded\">\n        </i>\n      </div>\n      <o3r-rule-actions-pres class=\"rule-tree-actions\"\n                            *ngIf=\"successActionsExpanded\"\n                            [actions]=\"successElements\">\n      </o3r-rule-actions-pres>\n      <ng-container [ngTemplateOutlet]=\"subTree\" [ngTemplateOutletContext]=\"{blocks: successElements}\"></ng-container>\n    </div>\n  </div>\n</ng-template>\n\n<ng-template #subTree let-blocks=\"blocks\">\n  <div class=\"rule-sub-trees\">\n    <ng-container *ngFor=\"let block of blocks\">\n      <div *ngIf=\"block.blockType === 'IF_ELSE'\" class=\"tree-branch\">\n        <o3r-rule-tree-pres\n          [blockType]=\"'IF_ELSE'\"\n          [condition]=\"block.condition\"\n          [failureElements]=\"block.failureElements\"\n          [successElements]=\"block.successElements\"></o3r-rule-tree-pres>\n      </div>\n    </ng-container>\n  </div>\n</ng-template>\n", styles: ["o3r-rule-tree-pres{display:block;padding-bottom:1rem}o3r-rule-tree-pres .rule-sub-trees{display:flex}o3r-rule-tree-pres .rule-conditions-title,o3r-rule-tree-pres .rule-action-title{text-align:center}o3r-rule-tree-pres .rule-conditions-title{border:1px solid #999999;background:#fff;color:#000;padding:.2rem}o3r-rule-tree-pres .rule-action-title{background:#fff;border-radius:0;border:1px solid #999999;cursor:pointer;display:flex;justify-content:center;align-items:center;gap:.5rem}o3r-rule-tree-pres .rule-tree-actions{display:block;padding:.5rem}o3r-rule-tree-pres .rule-actions:first-child>.rule-action-title{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}o3r-rule-tree-pres .rule-actions:last-child>.rule-action-title{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}o3r-rule-tree-pres .rule-actions-wrapper{display:flex}o3r-rule-tree-pres .rule-actions-wrapper .rule-actions{flex:1 1 100%}o3r-rule-tree-pres .success-actions{border-color:#16aa32}o3r-rule-tree-pres .success-actions.rule-action-title{background:#16aa32;color:#fff}o3r-rule-tree-pres .error-actions{border-color:#c02020}o3r-rule-tree-pres .error-actions.rule-action-title{background:#c02020;color:#fff}o3r-rule-tree-pres .tree .tree-leaf,o3r-rule-tree-pres .tree .tree-root{display:flex;width:100%}o3r-rule-tree-pres .tree .tree-leaf>div,o3r-rule-tree-pres .tree .tree-root>div{height:1rem;width:50%}o3r-rule-tree-pres .tree .tree-root div:first-child{border-right:1px dashed}o3r-rule-tree-pres .tree .tree-root{margin-top:.2rem}o3r-rule-tree-pres .tree .tree-node>.tree-branch:last-child>.tree-leaf>div:first-child{border-right:1px dashed;border-top:1px dashed;border-top-right-radius:.2rem}o3r-rule-tree-pres .tree .tree-node>.tree-branch:first-child>.tree-leaf>div:last-child{border-left:1px dashed;border-top:1px dashed;border-top-left-radius:.2rem}o3r-rule-tree-pres .icon{cursor:pointer}\n"] }]
        }], propDecorators: { name: [{
                type: Input
            }], blockType: [{
                type: Input
            }], condition: [{
                type: Input
            }], successElements: [{
                type: Input
            }], failureElements: [{
                type: Input
            }] } });

class RulesetHistoryPresComponent {
    constructor(cd) {
        this.cd = cd;
        /**
         * Reflects the state of each ruleset expanded elements.
         * Each ruleset entry contains a list of subpanel that can be collapsed or expanded.
         * Ruleset whole panel status is store the 'ruleset' entry.
         * @example
         * Expanded ruleset with rule overview collapsed:
         * {'rulesetId': {'ruleset' : true, 'ruleOverview': false}}
         * @note Collapsing a ruleset will not reset the subpanel expansion status
         */
        this.expansionStatus = {};
        this.rulesetExecutions = [];
        this.executionDurationFormat = '1.3-3';
    }
    /**
     * Toggle a ruleset subpanel
     * @param ruleId
     * @param subpanel element to collapse. 'ruleset' will toggle the whole panel but won't reset the subpanels states.
     */
    toggleExpansion(ruleId, subpanel) {
        if (this.expansionStatus[ruleId]) {
            this.expansionStatus[ruleId][subpanel] = !this.expansionStatus[ruleId][subpanel];
        }
        else {
            this.expansionStatus[ruleId] = { [subpanel]: true };
        }
        this.cd.detectChanges();
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetHistoryPresComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
    /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: RulesetHistoryPresComponent, isStandalone: true, selector: "o3r-ruleset-history-pres", inputs: { rulesetExecutions: "rulesetExecutions", executionDurationFormat: "executionDurationFormat" }, ngImport: i0, template: "<section>\n  <ng-template #noRulesEngine>\n    <div class=\"alert alert-danger m-2\" role=\"alert\">\n      The Rules Engine is not configured on this page.\n    </div>\n  </ng-template>\n  <ul *ngIf=\"rulesetExecutions; else noRulesEngine\" class=\"rulesets\">\n    <li *ngFor=\"let execution of rulesetExecutions\" class=\"ruleset\">\n      <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- need to refactor the div to accordion from DF #1518 -->\n      <div class=\"ruleset-panel-title ruleset-expansion-action\"\n        [class.error]=\"execution.type === 'RulesetExecutionError'\"\n        (click)=\"toggleExpansion(execution.executionId, 'ruleset')\">\n        <div><span [title]=\"'This ruleset has been evaluated ' + execution.executionCounter + ' time(s)'\">{{execution.executionCounter}}</span> - {{execution.rulesetName | titlecase }}\n          <div class=\"ruleset-panel-subtitle\" *ngIf=\"execution.rulesetInformation?.linkedComponents?.or\">\n            <ng-container *ngFor=\"let lc of execution.rulesetInformation?.linkedComponents?.or; last as isLast\">\n              <div>{{lc.name}} {{lc.library}} <span *ngIf=\"!isLast\"> OR </span></div>\n            </ng-container>\n          </div>\n          <div class=\"ruleset-panel-subtitle\" *ngIf=\"execution.rulesetInformation?.validityRange as validityRange\">\n            Date range: {{validityRange.from}} - {{validityRange.to}}\n          </div>\n        </div>\n        <div class=\"ruleset-panel-title-aside\">\n          <span class=\"error capsule\" *ngIf=\"execution.status === 'Error'\">Error</span>\n          <span class=\"success capsule\" *ngIf=\"execution.status === 'Active'\">Applied</span>\n          <span class=\"inactive capsule\" *ngIf=\"execution.status === 'Deactivated'\">Deactivated</span>\n          <span class=\"warn capsule\" *ngIf=\"execution.status === 'NoEffect'\">No effect</span>\n          <span class=\"time capsule\">\n            <span>{{execution.timestamp | date: 'HH:mm:ss SSS'}}</span>\n            <span>({{execution.duration | number: executionDurationFormat}}ms)</span>\n          </span>\n          <button\n            class=\"icon\"\n            [class.icon-caret-down]=\"!expansionStatus[execution.executionId]?.ruleset\"\n            [class.icon-caret-up]=\"expansionStatus[execution.executionId]?.ruleset\">\n          </button>\n        </div>\n      </div>\n      <div class=\"ruleset-panel-description\" *ngIf=\"expansionStatus[execution.executionId]?.ruleset\">\n        <ng-container [ngTemplateOutlet]=\"rules\"\n                      [ngTemplateOutletContext]=\"{\n                        rules: execution.rulesetInformation.rules,\n                        expansionID: execution.executionId\n                      }\"></ng-container>\n        <ng-container [ngTemplateOutlet]=\"inputs\"\n                      [ngTemplateOutletContext]=\"{\n                        inputs: execution.inputFacts\n                      }\"></ng-container>\n        <ng-container *ngIf=\"execution.type === 'RulesetExecutionError'; else success\">\n          <div class=\"ruleset-panel-category-title\">Rules:</div>\n          <ul class=\"ruleset-panel-category-body rule-description\">\n            <li *ngFor=\"let ruleEvaluation of execution.rulesEvaluations; let index=index;\">\n              <ng-container>\n                <div class=\"ruleset-panel-title\" [class.error]=\"ruleEvaluation.error\">\n                  <span>{{ruleEvaluation.rule.name | titlecase}} </span>\n                  <span class=\"capsule error\" *ngIf=\"ruleEvaluation.error\">Error</span>\n                </div>\n                <div>\n                  <ng-container *ngIf=\"ruleEvaluation.error\">\n                    <span class=\"ruleset-panel-category-title\">Error:</span>\n                    <pre class=\"ruleset-panel-category-body error\">{{ruleEvaluation.error | o3rJsonOrString}}</pre>\n                  </ng-container>\n                  <ng-container [ngTemplateOutlet]=\"inputs\"\n                                [ngTemplateOutletContext]=\"{\n                                  inputs: execution.inputFacts,\n                                  runtimeInputs: execution.rulesetInformation?.rules[index]?.inputRuntimeFacts\n                                }\"></ng-container>\n                  <o3r-rule-actions-pres *ngIf=\"!ruleEvaluation.error\"\n                    [temporaryFacts]=\"ruleEvaluation.temporaryFacts\"\n                    [runtimeOutputs]=\"execution.rulesetInformation?.rules[index]?.outputRuntimeFacts\"\n                  ></o3r-rule-actions-pres>\n                </div>\n              </ng-container>\n            </li>\n          </ul>\n        </ng-container>\n        <ng-template #success>\n          <o3r-rule-actions-pres [actions]=\"execution.outputActions\"></o3r-rule-actions-pres>\n          <div class=\"ruleset-panel-category-title\">Executed Rules</div>\n          <ul class=\"rule-description ruleset-panel-category-body\">\n            <li *ngFor=\"let ruleEvaluation of execution.rulesEvaluations; let index=index;\">\n              <div class=\"ruleset-panel-title\">\n                <span>{{ruleEvaluation.rule.name | titlecase}}</span>\n                <span class=\"capsule inactive\" *ngIf=\"ruleEvaluation.cached\">Cached</span>\n                <span class=\"capsule\">({{ruleEvaluation.duration | number: executionDurationFormat}}ms)</span>\n              </div>\n              <div>\n                <ng-container [ngTemplateOutlet]=\"triggers\"\n                              [ngTemplateOutletContext]=\"{triggers: (ruleEvaluation.triggers[ruleEvaluation.rule.id])}\"></ng-container>\n                <o3r-rule-actions-pres\n                  [actions]=\"ruleEvaluation.outputActions\"\n                  [temporaryFacts]=\"ruleEvaluation.temporaryFacts\"\n                  [runtimeOutputs]=\"execution.rulesetInformation?.rules[index]?.outputRuntimeFacts\">\n                </o3r-rule-actions-pres>\n              </div>\n            </li>\n          </ul>\n        </ng-template>\n      </div>\n    </li>\n  </ul>\n</section>\n\n<ng-template let-triggers=\"triggers\" #triggers>\n  <div class=\"ruleset-panel-category-title\">Basefacts Triggers</div>\n  <ul class=\"ruleset-panel-category-body triggers\">\n    <ng-container *ngFor=\"let trigger of (triggers | keyvalue)\">\n      <li *ngIf=\"trigger.value?.factName\">\n        <o3r-rule-key-value-pres\n          [key]=\"trigger.value.factName\"\n          [oldValue]=\"trigger.value.oldValue | o3rFallbackTo\"\n          [value]=\"trigger.value.newValue | o3rFallbackTo\"\n          [type]=\"'state'\"></o3r-rule-key-value-pres>\n      </li>\n    </ng-container>\n  </ul>\n</ng-template>\n\n<ng-template let-rules=\"rules\" let-expansionID=\"expansionID\" #rules>\n  <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- need to refactor the div to accordion from DF #1518 -->\n  <div class=\"ruleset-panel-category-title ruleset-expansion-action\"\n    (click)=\"toggleExpansion(expansionID, 'rulesOverview')\">\n    <span>Rules Overview</span>\n    <button class=\"icon\"\n            [class.icon-caret-down]=\"!expansionStatus[expansionID]?.rulesOverview\"\n            [class.icon-caret-up]=\"expansionStatus[expansionID]?.rulesOverview\">\n    </button>\n  </div>\n  <ng-container *ngIf=\"expansionStatus[expansionID]?.rulesOverview\">\n    <div *ngIf=\"rules?.length === 0\" class=\"ruleset-panel-category-body empty\">No rule</div>\n    <ul class=\"ruleset-panel-category-body\" *ngIf=\"rules?.length > 0\">\n      <li *ngFor=\"let rule of rules\">\n        <o3r-rule-tree-pres [name]=\"rule.name\"\n                            [condition]=\"rule?.rootElement?.condition\"\n                            [blockType]=\"rule?.rootElement?.blockType\"\n                            [successElements]=\"rule?.rootElement?.successElements\"\n                            [failureElements]=\"rule?.rootElement?.failureElements\">\n        </o3r-rule-tree-pres>\n      </li>\n    </ul>\n  </ng-container>\n</ng-template>\n\n<ng-template let-inputs=\"inputs\" let-runtimeInputs=\"runtimeInputs\" #inputs>\n  <div class=\"ruleset-panel-category-title\">Inputs snapshot</div>\n  <div *ngIf=\"inputs?.length === 0\" class=\"ruleset-panel-category-body empty\">No inputs</div>\n  <ul class=\"ruleset-panel-category-body\" *ngIf=\"inputs?.length > 0\">\n    <li *ngFor=\"let input of inputs\">\n      <o3r-rule-key-value-pres\n        [key]=\"input.factName\"\n        [value]=\"input.value | o3rFallbackTo\"\n        [type]=\"'state'\"></o3r-rule-key-value-pres>\n    </li>\n    <li *ngFor=\"let input of runtimeInputs\">{{input}} (scope limited to ruleset)</li>\n  </ul>\n</ng-template>\n", styles: ["o3r-ruleset-history-pres .ruleset-panel-title,o3r-ruleset-history-pres .ruleset-panel-category-title{display:flex;justify-content:space-between;align-items:center}o3r-ruleset-history-pres .ruleset-panel-title{font-size:1rem;padding:.5rem 0 .1rem}o3r-ruleset-history-pres .ruleset-expansion-action,o3r-ruleset-history-pres .icon-caret-down,o3r-ruleset-history-pres .icon-caret-up{cursor:pointer}o3r-ruleset-history-pres .ruleset-panel-subtitle{font-size:.75rem}o3r-ruleset-history-pres .ruleset-panel-title-aside{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;min-width:fit-content}o3r-ruleset-history-pres .ruleset-panel-category-title{font-size:.95rem;background:#eee;padding:.5rem;margin-bottom:.5rem;margin-top:1rem}o3r-ruleset-history-pres .rule-description .ruleset-panel-category-title{font-size:.893rem;cursor:default}o3r-ruleset-history-pres .rule-description .ruleset-panel-title{font-size:.94rem}o3r-ruleset-history-pres .rule-description .ruleset-panel-category-body{padding-bottom:.5rem;padding-left:1.5rem}o3r-ruleset-history-pres .rule-description .ruleset-panel-category-body:empty{margin:0;padding:0 0 0 1.5rem}o3r-ruleset-history-pres .rulesets{margin:0;padding:0;list-style:none}o3r-ruleset-history-pres .rulesets ul{margin:0;padding-left:2rem}o3r-ruleset-history-pres .rulesets li.ruleset:nth-child(odd){background:#fbfbfb}o3r-ruleset-history-pres .ruleset-panel-category-body li{list-style:disc}o3r-ruleset-history-pres li:empty{display:none}o3r-ruleset-history-pres .ruleset{border-bottom:1px solid #dddddd}o3r-ruleset-history-pres .ruleset .ruleset-panel-description{padding:0 1rem 0 2rem;margin-bottom:2em}o3r-ruleset-history-pres .ruleset:first-child{border-top:1px solid #dddddd}o3r-ruleset-history-pres .ruleset-panel-title.ruleset-expansion-action{padding:.5rem 1rem}o3r-ruleset-history-pres .empty{font-style:italic;padding-left:1.5rem}o3r-ruleset-history-pres .ruleset-panel-category-body:empty.triggers:after{content:\"No trigger for this rule\";display:block;font-style:italic;padding-bottom:.5rem}o3r-ruleset-history-pres .ruleset-panel-title-aside{padding-left:1rem}o3r-ruleset-history-pres .rule-description .capsule{font-size:.8em;padding:.1rem .5rem;margin:0 .5rem}o3r-ruleset-history-pres .icon{background:none;border:none;font-size:1em}o3r-ruleset-history-pres .capsule{padding:.3rem;margin:.5rem;font-size:.875em;min-width:6rem;text-align:center}o3r-ruleset-history-pres .capsule.time{display:flex;flex-direction:column}o3r-ruleset-history-pres .success{background-color:#16aa32;color:#fff}o3r-ruleset-history-pres .inactive{background-color:#aaa;color:#fff}o3r-ruleset-history-pres .error{color:#c02020}o3r-ruleset-history-pres .error .capsule.error{background-color:#c02020;color:#fff}o3r-ruleset-history-pres .warn{background-color:#b92;color:#fff}o3r-ruleset-history-pres .input-key{color:#26c}o3r-ruleset-history-pres .input-value{color:#c29}o3r-ruleset-history-pres .input-key,o3r-ruleset-history-pres .input-value{font-family:monospace}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1$1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$1.TitleCasePipe, name: "titlecase" }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "pipe", type: i1$1.KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: O3rFallbackToPipe, name: "o3rFallbackTo" }, { kind: "pipe", type: O3rJsonOrStringPipe, name: "o3rJsonOrString" }, { kind: "component", type: RuleActionsPresComponent, selector: "o3r-rule-actions-pres", inputs: ["actions", "temporaryFacts", "runtimeOutputs"] }, { kind: "component", type: RuleKeyValuePresComponent, selector: "o3r-rule-key-value-pres", inputs: ["key", "value", "oldValue", "type"] }, { kind: "component", type: RuleTreePresComponent, selector: "o3r-rule-tree-pres", inputs: ["name", "blockType", "condition", "successElements", "failureElements"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetHistoryPresComponent, decorators: [{
            type: Component,
            args: [{ selector: 'o3r-ruleset-history-pres', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
                        CommonModule,
                        O3rFallbackToPipe,
                        O3rJsonOrStringPipe,
                        RuleActionsPresComponent,
                        RuleKeyValuePresComponent,
                        RuleTreePresComponent
                    ], template: "<section>\n  <ng-template #noRulesEngine>\n    <div class=\"alert alert-danger m-2\" role=\"alert\">\n      The Rules Engine is not configured on this page.\n    </div>\n  </ng-template>\n  <ul *ngIf=\"rulesetExecutions; else noRulesEngine\" class=\"rulesets\">\n    <li *ngFor=\"let execution of rulesetExecutions\" class=\"ruleset\">\n      <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- need to refactor the div to accordion from DF #1518 -->\n      <div class=\"ruleset-panel-title ruleset-expansion-action\"\n        [class.error]=\"execution.type === 'RulesetExecutionError'\"\n        (click)=\"toggleExpansion(execution.executionId, 'ruleset')\">\n        <div><span [title]=\"'This ruleset has been evaluated ' + execution.executionCounter + ' time(s)'\">{{execution.executionCounter}}</span> - {{execution.rulesetName | titlecase }}\n          <div class=\"ruleset-panel-subtitle\" *ngIf=\"execution.rulesetInformation?.linkedComponents?.or\">\n            <ng-container *ngFor=\"let lc of execution.rulesetInformation?.linkedComponents?.or; last as isLast\">\n              <div>{{lc.name}} {{lc.library}} <span *ngIf=\"!isLast\"> OR </span></div>\n            </ng-container>\n          </div>\n          <div class=\"ruleset-panel-subtitle\" *ngIf=\"execution.rulesetInformation?.validityRange as validityRange\">\n            Date range: {{validityRange.from}} - {{validityRange.to}}\n          </div>\n        </div>\n        <div class=\"ruleset-panel-title-aside\">\n          <span class=\"error capsule\" *ngIf=\"execution.status === 'Error'\">Error</span>\n          <span class=\"success capsule\" *ngIf=\"execution.status === 'Active'\">Applied</span>\n          <span class=\"inactive capsule\" *ngIf=\"execution.status === 'Deactivated'\">Deactivated</span>\n          <span class=\"warn capsule\" *ngIf=\"execution.status === 'NoEffect'\">No effect</span>\n          <span class=\"time capsule\">\n            <span>{{execution.timestamp | date: 'HH:mm:ss SSS'}}</span>\n            <span>({{execution.duration | number: executionDurationFormat}}ms)</span>\n          </span>\n          <button\n            class=\"icon\"\n            [class.icon-caret-down]=\"!expansionStatus[execution.executionId]?.ruleset\"\n            [class.icon-caret-up]=\"expansionStatus[execution.executionId]?.ruleset\">\n          </button>\n        </div>\n      </div>\n      <div class=\"ruleset-panel-description\" *ngIf=\"expansionStatus[execution.executionId]?.ruleset\">\n        <ng-container [ngTemplateOutlet]=\"rules\"\n                      [ngTemplateOutletContext]=\"{\n                        rules: execution.rulesetInformation.rules,\n                        expansionID: execution.executionId\n                      }\"></ng-container>\n        <ng-container [ngTemplateOutlet]=\"inputs\"\n                      [ngTemplateOutletContext]=\"{\n                        inputs: execution.inputFacts\n                      }\"></ng-container>\n        <ng-container *ngIf=\"execution.type === 'RulesetExecutionError'; else success\">\n          <div class=\"ruleset-panel-category-title\">Rules:</div>\n          <ul class=\"ruleset-panel-category-body rule-description\">\n            <li *ngFor=\"let ruleEvaluation of execution.rulesEvaluations; let index=index;\">\n              <ng-container>\n                <div class=\"ruleset-panel-title\" [class.error]=\"ruleEvaluation.error\">\n                  <span>{{ruleEvaluation.rule.name | titlecase}} </span>\n                  <span class=\"capsule error\" *ngIf=\"ruleEvaluation.error\">Error</span>\n                </div>\n                <div>\n                  <ng-container *ngIf=\"ruleEvaluation.error\">\n                    <span class=\"ruleset-panel-category-title\">Error:</span>\n                    <pre class=\"ruleset-panel-category-body error\">{{ruleEvaluation.error | o3rJsonOrString}}</pre>\n                  </ng-container>\n                  <ng-container [ngTemplateOutlet]=\"inputs\"\n                                [ngTemplateOutletContext]=\"{\n                                  inputs: execution.inputFacts,\n                                  runtimeInputs: execution.rulesetInformation?.rules[index]?.inputRuntimeFacts\n                                }\"></ng-container>\n                  <o3r-rule-actions-pres *ngIf=\"!ruleEvaluation.error\"\n                    [temporaryFacts]=\"ruleEvaluation.temporaryFacts\"\n                    [runtimeOutputs]=\"execution.rulesetInformation?.rules[index]?.outputRuntimeFacts\"\n                  ></o3r-rule-actions-pres>\n                </div>\n              </ng-container>\n            </li>\n          </ul>\n        </ng-container>\n        <ng-template #success>\n          <o3r-rule-actions-pres [actions]=\"execution.outputActions\"></o3r-rule-actions-pres>\n          <div class=\"ruleset-panel-category-title\">Executed Rules</div>\n          <ul class=\"rule-description ruleset-panel-category-body\">\n            <li *ngFor=\"let ruleEvaluation of execution.rulesEvaluations; let index=index;\">\n              <div class=\"ruleset-panel-title\">\n                <span>{{ruleEvaluation.rule.name | titlecase}}</span>\n                <span class=\"capsule inactive\" *ngIf=\"ruleEvaluation.cached\">Cached</span>\n                <span class=\"capsule\">({{ruleEvaluation.duration | number: executionDurationFormat}}ms)</span>\n              </div>\n              <div>\n                <ng-container [ngTemplateOutlet]=\"triggers\"\n                              [ngTemplateOutletContext]=\"{triggers: (ruleEvaluation.triggers[ruleEvaluation.rule.id])}\"></ng-container>\n                <o3r-rule-actions-pres\n                  [actions]=\"ruleEvaluation.outputActions\"\n                  [temporaryFacts]=\"ruleEvaluation.temporaryFacts\"\n                  [runtimeOutputs]=\"execution.rulesetInformation?.rules[index]?.outputRuntimeFacts\">\n                </o3r-rule-actions-pres>\n              </div>\n            </li>\n          </ul>\n        </ng-template>\n      </div>\n    </li>\n  </ul>\n</section>\n\n<ng-template let-triggers=\"triggers\" #triggers>\n  <div class=\"ruleset-panel-category-title\">Basefacts Triggers</div>\n  <ul class=\"ruleset-panel-category-body triggers\">\n    <ng-container *ngFor=\"let trigger of (triggers | keyvalue)\">\n      <li *ngIf=\"trigger.value?.factName\">\n        <o3r-rule-key-value-pres\n          [key]=\"trigger.value.factName\"\n          [oldValue]=\"trigger.value.oldValue | o3rFallbackTo\"\n          [value]=\"trigger.value.newValue | o3rFallbackTo\"\n          [type]=\"'state'\"></o3r-rule-key-value-pres>\n      </li>\n    </ng-container>\n  </ul>\n</ng-template>\n\n<ng-template let-rules=\"rules\" let-expansionID=\"expansionID\" #rules>\n  <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- need to refactor the div to accordion from DF #1518 -->\n  <div class=\"ruleset-panel-category-title ruleset-expansion-action\"\n    (click)=\"toggleExpansion(expansionID, 'rulesOverview')\">\n    <span>Rules Overview</span>\n    <button class=\"icon\"\n            [class.icon-caret-down]=\"!expansionStatus[expansionID]?.rulesOverview\"\n            [class.icon-caret-up]=\"expansionStatus[expansionID]?.rulesOverview\">\n    </button>\n  </div>\n  <ng-container *ngIf=\"expansionStatus[expansionID]?.rulesOverview\">\n    <div *ngIf=\"rules?.length === 0\" class=\"ruleset-panel-category-body empty\">No rule</div>\n    <ul class=\"ruleset-panel-category-body\" *ngIf=\"rules?.length > 0\">\n      <li *ngFor=\"let rule of rules\">\n        <o3r-rule-tree-pres [name]=\"rule.name\"\n                            [condition]=\"rule?.rootElement?.condition\"\n                            [blockType]=\"rule?.rootElement?.blockType\"\n                            [successElements]=\"rule?.rootElement?.successElements\"\n                            [failureElements]=\"rule?.rootElement?.failureElements\">\n        </o3r-rule-tree-pres>\n      </li>\n    </ul>\n  </ng-container>\n</ng-template>\n\n<ng-template let-inputs=\"inputs\" let-runtimeInputs=\"runtimeInputs\" #inputs>\n  <div class=\"ruleset-panel-category-title\">Inputs snapshot</div>\n  <div *ngIf=\"inputs?.length === 0\" class=\"ruleset-panel-category-body empty\">No inputs</div>\n  <ul class=\"ruleset-panel-category-body\" *ngIf=\"inputs?.length > 0\">\n    <li *ngFor=\"let input of inputs\">\n      <o3r-rule-key-value-pres\n        [key]=\"input.factName\"\n        [value]=\"input.value | o3rFallbackTo\"\n        [type]=\"'state'\"></o3r-rule-key-value-pres>\n    </li>\n    <li *ngFor=\"let input of runtimeInputs\">{{input}} (scope limited to ruleset)</li>\n  </ul>\n</ng-template>\n", styles: ["o3r-ruleset-history-pres .ruleset-panel-title,o3r-ruleset-history-pres .ruleset-panel-category-title{display:flex;justify-content:space-between;align-items:center}o3r-ruleset-history-pres .ruleset-panel-title{font-size:1rem;padding:.5rem 0 .1rem}o3r-ruleset-history-pres .ruleset-expansion-action,o3r-ruleset-history-pres .icon-caret-down,o3r-ruleset-history-pres .icon-caret-up{cursor:pointer}o3r-ruleset-history-pres .ruleset-panel-subtitle{font-size:.75rem}o3r-ruleset-history-pres .ruleset-panel-title-aside{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;min-width:fit-content}o3r-ruleset-history-pres .ruleset-panel-category-title{font-size:.95rem;background:#eee;padding:.5rem;margin-bottom:.5rem;margin-top:1rem}o3r-ruleset-history-pres .rule-description .ruleset-panel-category-title{font-size:.893rem;cursor:default}o3r-ruleset-history-pres .rule-description .ruleset-panel-title{font-size:.94rem}o3r-ruleset-history-pres .rule-description .ruleset-panel-category-body{padding-bottom:.5rem;padding-left:1.5rem}o3r-ruleset-history-pres .rule-description .ruleset-panel-category-body:empty{margin:0;padding:0 0 0 1.5rem}o3r-ruleset-history-pres .rulesets{margin:0;padding:0;list-style:none}o3r-ruleset-history-pres .rulesets ul{margin:0;padding-left:2rem}o3r-ruleset-history-pres .rulesets li.ruleset:nth-child(odd){background:#fbfbfb}o3r-ruleset-history-pres .ruleset-panel-category-body li{list-style:disc}o3r-ruleset-history-pres li:empty{display:none}o3r-ruleset-history-pres .ruleset{border-bottom:1px solid #dddddd}o3r-ruleset-history-pres .ruleset .ruleset-panel-description{padding:0 1rem 0 2rem;margin-bottom:2em}o3r-ruleset-history-pres .ruleset:first-child{border-top:1px solid #dddddd}o3r-ruleset-history-pres .ruleset-panel-title.ruleset-expansion-action{padding:.5rem 1rem}o3r-ruleset-history-pres .empty{font-style:italic;padding-left:1.5rem}o3r-ruleset-history-pres .ruleset-panel-category-body:empty.triggers:after{content:\"No trigger for this rule\";display:block;font-style:italic;padding-bottom:.5rem}o3r-ruleset-history-pres .ruleset-panel-title-aside{padding-left:1rem}o3r-ruleset-history-pres .rule-description .capsule{font-size:.8em;padding:.1rem .5rem;margin:0 .5rem}o3r-ruleset-history-pres .icon{background:none;border:none;font-size:1em}o3r-ruleset-history-pres .capsule{padding:.3rem;margin:.5rem;font-size:.875em;min-width:6rem;text-align:center}o3r-ruleset-history-pres .capsule.time{display:flex;flex-direction:column}o3r-ruleset-history-pres .success{background-color:#16aa32;color:#fff}o3r-ruleset-history-pres .inactive{background-color:#aaa;color:#fff}o3r-ruleset-history-pres .error{color:#c02020}o3r-ruleset-history-pres .error .capsule.error{background-color:#c02020;color:#fff}o3r-ruleset-history-pres .warn{background-color:#b92;color:#fff}o3r-ruleset-history-pres .input-key{color:#26c}o3r-ruleset-history-pres .input-value{color:#c29}o3r-ruleset-history-pres .input-key,o3r-ruleset-history-pres .input-value{font-family:monospace}\n"] }]
        }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { rulesetExecutions: [{
                type: Input
            }], executionDurationFormat: [{
                type: Input
            }] } });

/**
 * @deprecated The Components and Pipes are now standalone, this module will be removed in v14
 */
class RulesetHistoryPresModule {
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetHistoryPresModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.13", ngImport: i0, type: RulesetHistoryPresModule, imports: [JsonPipe,
            RulesetHistoryPresComponent,
            RuleConditionPresComponent], exports: [RulesetHistoryPresComponent] }); }
    /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetHistoryPresModule, imports: [RulesetHistoryPresComponent,
            RuleConditionPresComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetHistoryPresModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        JsonPipe,
                        RulesetHistoryPresComponent,
                        RuleConditionPresComponent
                    ],
                    exports: [RulesetHistoryPresComponent]
                }]
        }] });

const isRulesEngineMessage = (message) => {
    return message && (message.dataType === 'rulesEngineEvents'
        || message.dataType === 'requestMessages'
        || message.dataType === 'connect');
};

/** Determine if the action should be executed */
const RULES_ENGINE_OPTIONS = new InjectionToken('Rules Engine Options');
/** Default Rules engine options */
const DEFAULT_RULES_ENGINE_OPTIONS = {
    dryRun: false,
    debug: false
};

/** StateDetailsActions */
const ACTION_SET = '[Rulesets] set';
const ACTION_UPDATE = '[Rulesets] update';
const ACTION_RESET = '[Rulesets] reset';
const ACTION_CANCEL_REQUEST = '[Rulesets] cancel request';
/** Entity Actions */
const ACTION_CLEAR_ENTITIES = '[Rulesets] clear entities';
const ACTION_UPSERT_ENTITIES = '[Rulesets] upsert entities';
const ACTION_SET_ENTITIES = '[Rulesets] set entities';
const ACTION_FAIL_ENTITIES = '[Rulesets] fail entities';
/** Async Actions */
const ACTION_SET_ENTITIES_FROM_API = '[Rulesets] set entities from api';
const ACTION_UPSERT_ENTITIES_FROM_API = '[Rulesets] upsert entities from api';
/** Action to clear the StateDetails of the store and replace it */
const setRulesets = createAction(ACTION_SET, props());
/** Action to change a part or the whole object in the store. */
const updateRulesets = createAction(ACTION_UPDATE, props());
/** Action to reset the whole state, by returning it to initial state. */
const resetRulesets = createAction(ACTION_RESET);
/** Action to cancel a Request ID registered in the store. Can happen from effect based on a switchMap for instance */
const cancelRulesetsRequest = createAction(ACTION_CANCEL_REQUEST, props());
/** Action to clear all rulesets and fill the store with the payload */
const setRulesetsEntities = createAction(ACTION_SET_ENTITIES, props());
/** Action to update rulesets with known IDs, insert the new ones */
const upsertRulesetsEntities = createAction(ACTION_UPSERT_ENTITIES, props());
/** Action to empty the list of entities, keeping the global state */
const clearRulesetsEntities = createAction(ACTION_CLEAR_ENTITIES);
/** Action to update failureStatus for every RulesetsModel */
const failRulesetsEntities = createAction(ACTION_FAIL_ENTITIES, props());
/**
 * Action to put the global status of the store in a pending state. Call SET action with the list of RulesetsModels received, when this action resolves.
 * If the call fails, dispatch FAIL_ENTITIES action
 */
const setRulesetsEntitiesFromApi = createAction(ACTION_SET_ENTITIES_FROM_API, asyncProps());
/**
 * Action to put global status of the store in a pending state. Call UPSERT action with the list of RulesetsModels received, when this action resolves.
 * If the call fails, dispatch FAIL_ENTITIES action
 */
const upsertRulesetsEntitiesFromApi = createAction(ACTION_UPSERT_ENTITIES_FROM_API, asyncProps());

/**
 * Service to handle async Rulesets actions
 */
class RulesetsEffect {
    constructor(actions$) {
        this.actions$ = actions$;
        /**
         * Set the entities with the reply content, dispatch failRulesetsEntities if it catches a failure
         */
        this.setEntitiesFromApi$ = createEffect(() => this.actions$.pipe(ofType(setRulesetsEntitiesFromApi), fromApiEffectSwitchMap((reply, action) => setRulesetsEntities({ entities: reply, requestId: action.requestId }), (error, action) => of(failRulesetsEntities({ error, requestId: action.requestId })), cancelRulesetsRequest)));
        /**
         * Upsert the entities with the reply content, dispatch failRulesetsEntities if it catches a failure
         */
        this.upsertEntitiesFromApi$ = createEffect(() => this.actions$.pipe(ofType(upsertRulesetsEntitiesFromApi), mergeMap((payload) => from(payload.call).pipe(map((reply) => upsertRulesetsEntities({ entities: reply, requestId: payload.requestId })), catchError((err) => of(failRulesetsEntities({ error: err, requestId: payload.requestId })))))));
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetsEffect, deps: [{ token: i1$2.Actions }], target: i0.ɵɵFactoryTarget.Injectable }); }
    /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetsEffect }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetsEffect, decorators: [{
            type: Injectable
        }], ctorParameters: () => [{ type: i1$2.Actions }] });

/**
 * Rulesets Store adapter
 */
const rulesetsAdapter = createEntityAdapter({
    selectId: (model) => model.id
});
/**
 * Rulesets Store initial value
 */
const rulesetsInitialState = rulesetsAdapter.getInitialState({
    requestIds: []
});
/**
 * List of basic actions for Rulesets Store
 */
const rulesetsReducerFeatures = [
    on(resetRulesets, () => rulesetsInitialState),
    on(setRulesets, (state, payload) => ({ ids: state.ids, entities: state.entities, ...payload.stateDetails })),
    on(cancelRulesetsRequest, (state, action) => asyncStoreItemAdapter.resolveRequest(state, action.requestId)),
    on(updateRulesets, (state, payload) => ({ ...state, ...payload.stateDetails })),
    on(setRulesetsEntities, (state, payload) => rulesetsAdapter.addMany(payload.entities, rulesetsAdapter.removeAll(asyncStoreItemAdapter.resolveRequest(state, payload.requestId)))),
    on(upsertRulesetsEntities, (state, payload) => rulesetsAdapter.upsertMany(payload.entities, asyncStoreItemAdapter.resolveRequest(state, payload.requestId))),
    on(clearRulesetsEntities, (state) => rulesetsAdapter.removeAll(state)),
    on(failRulesetsEntities, (state, payload) => asyncStoreItemAdapter.failRequest(state, payload.requestId)),
    on(setRulesetsEntitiesFromApi, upsertRulesetsEntitiesFromApi, (state, payload) => asyncStoreItemAdapter.addRequest(state, payload.requestId))
];
/**
 * Rulesets Store reducer
 */
const rulesetsReducer = createReducer(rulesetsInitialState, ...rulesetsReducerFeatures);

/**
 * Name of the Rulesets Store
 */
const RULESETS_STORE_NAME = 'rulesets';

/** Token of the Rulesets reducer */
const RULESETS_REDUCER_TOKEN = new InjectionToken('Feature Rulesets Reducer');
/** Provide default reducer for Rulesets store */
function getDefaultRulesetsReducer() {
    return rulesetsReducer;
}
class RulesetsStoreModule {
    static forRoot(reducerFactory) {
        return {
            ngModule: RulesetsStoreModule,
            providers: [
                { provide: RULESETS_REDUCER_TOKEN, useFactory: reducerFactory }
            ]
        };
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetsStoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.13", ngImport: i0, type: RulesetsStoreModule, imports: [i1$3.StoreFeatureModule, i1$2.EffectsFeatureModule] }); }
    /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetsStoreModule, providers: [
            { provide: RULESETS_REDUCER_TOKEN, useFactory: getDefaultRulesetsReducer }
        ], imports: [StoreModule.forFeature(RULESETS_STORE_NAME, RULESETS_REDUCER_TOKEN), EffectsModule.forFeature([RulesetsEffect])] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesetsStoreModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        StoreModule.forFeature(RULESETS_STORE_NAME, RULESETS_REDUCER_TOKEN), EffectsModule.forFeature([RulesetsEffect])
                    ],
                    providers: [
                        { provide: RULESETS_REDUCER_TOKEN, useFactory: getDefaultRulesetsReducer }
                    ]
                }]
        }] });

const { selectIds, selectEntities, selectAll, selectTotal } = rulesetsAdapter.getSelectors();
/** Select Rulesets State */
const selectRulesetsState = createFeatureSelector(RULESETS_STORE_NAME);
/** Select the array of Rulesets ids */
const selectRulesetsIds = createSelector(selectRulesetsState, selectIds);
/** Select the array of Rulesets */
const selectAllRulesets = createSelector(selectRulesetsState, selectAll);
/** Select the dictionary of Rulesets entities */
const selectRulesetsEntities = createSelector(selectRulesetsState, selectEntities);
/** Select the total Rulesets count */
const selectRulesetsTotal = createSelector(selectRulesetsState, selectTotal);
/** Select the store pending status */
const selectRulesetsStorePendingStatus = createSelector(selectRulesetsState, (state) => state.isPending || false);
/**
 * Check if the given value is a valid date
 * @param d
 */
const isValidDate$1 = (d) => !Number.isNaN(d) && d instanceof Date;
/**
 * Returns the rulesets which are in the validity range, if provided
 */
const selectRuleSetsInRange = createSelector(selectAllRulesets, (ruleSets) => ruleSets.filter((ruleSet) => {
    const validity = ruleSet.validityRange;
    if (!validity || (!validity.from && !validity.to)) {
        return true;
    }
    const from = validity.from && new Date(validity.from);
    const to = validity.to && new Date(validity.to);
    if ((to && !isValidDate$1(to)) || (from && !isValidDate$1(from))) {
        return false;
    }
    const time = Date.now();
    if (to && from) {
        return from.getTime() <= time && to.getTime() >= time;
    }
    if (from) {
        return from.getTime() <= time;
    }
    return to && to.getTime() >= time;
}));
/**
 * Returns the rulesets ids which are not onDemand and in the validity range
 */
const selectActiveRuleSets = createSelector(selectRuleSetsInRange, (ruleSets) => ruleSets
    .filter((ruleSet) => (!ruleSet.linkedComponents?.or?.length))
    .map((ruleSet) => ruleSet.id));
/**
 * Assign component to RulesetIds Map
 * @param compName
 * @param library
 * @param ruleSetId
 * @param acc
 */
function linkComponentToRuleset(compName, library, ruleSetId, acc = {}) {
    const configName = computeItemIdentifier(compName, library);
    acc[ruleSetId] ||= [];
    acc[ruleSetId].push(configName);
}
/**
 * Select the map of ruleSets to activate based on linked components
 */
const selectComponentsLinkedToRuleset = createSelector(selectRuleSetsInRange, (ruleSets) => ruleSets
    .reduce((acc, ruleSet) => {
    if (!ruleSet.linkedComponents?.or || ruleSet.linkedComponents.or.length === 0) {
        return acc;
    }
    if (ruleSet.linkedComponents?.or?.length) {
        ruleSet.linkedComponents.or.forEach((linkComp) => {
            linkComponentToRuleset(linkComp.name, linkComp.library, ruleSet.id, acc.or);
        });
        return acc;
    }
    return acc;
}, { or: {} }));

const rulesetsStorageSerializer = (state) => {
    return asyncStoreItemAdapter.clearAsyncStoreItem(state);
};
const rulesetsStorageDeserializer = (rawObject) => {
    if (!rawObject || !rawObject.ids) {
        return rulesetsInitialState;
    }
    const storeObject = rulesetsAdapter.getInitialState(rawObject);
    for (const id of rawObject.ids) {
        storeObject.entities[id] = rawObject.entities[id];
    }
    return storeObject;
};
const rulesetsStorageSync = {
    serialize: rulesetsStorageSerializer,
    deserialize: rulesetsStorageDeserializer
};

/**
 * Function to retrieve from 2 sequential executions only the triggers which activated the last ruleset execution
 * @param currRes Current ruleset execution object
 * @param prevRes Previous ruleset execution object
 * @returns The triggers list which activates the last ruleset execution
 */
function retrieveRulesetTriggers(currRes, prevRes) {
    let rulesetTriggers = {};
    const allCurrRulesetTriggersList = currRes.map((r) => r.evaluation).filter((e) => !!e).map((e) => e.triggers);
    const allCurrRulesetTriggers = {};
    allCurrRulesetTriggersList.forEach((rTrig) => {
        Object.keys(rTrig).forEach((ruleId) => {
            allCurrRulesetTriggers[ruleId] = rTrig[ruleId];
        });
    });
    if (prevRes) {
        const allPrevRulesetTriggersList = prevRes.map((r) => r.evaluation).filter((e) => !!e).map((e) => e.triggers);
        const allPrevRulesetTriggers = {};
        allPrevRulesetTriggersList.forEach((rTrig) => {
            Object.keys(rTrig).forEach((ruleId) => {
                allPrevRulesetTriggers[ruleId] = rTrig[ruleId];
            });
        });
        Object.entries(allCurrRulesetTriggers).forEach(([ruleId, ruleTriggers]) => {
            Object.keys(ruleTriggers).forEach((factName) => {
                if (!allPrevRulesetTriggers[ruleId]
                    || !allPrevRulesetTriggers[ruleId][factName]
                    || ruleTriggers[factName].newValue !== allPrevRulesetTriggers[ruleId][factName].newValue) {
                    (rulesetTriggers[ruleId] ||= {})[factName] = ruleTriggers[factName];
                }
            });
        });
    }
    else {
        rulesetTriggers = allCurrRulesetTriggers;
    }
    return rulesetTriggers;
}
/**
 * Flag as cached the rules evaluations which are from previous ruleset executions
 * @param rulesEvaluations all rules evaluations list
 * @param triggers Ruleset triggers object
 * @returns Rules evaluation list with flagged rules evaluation from previous ruleset executions
 */
function flagCachedRules(rulesEvaluations, triggers) {
    const rulesWhichTriggeredExecution = Object.keys(triggers);
    return rulesEvaluations.map((e) => {
        if (e && !rulesWhichTriggeredExecution.includes(e.rule.id)) {
            return { ...e, cached: true };
        }
        return { ...e };
    });
}
/**
 * Create the debug rule evaluation object
 * @param rule
 * @param rulesetName
 * @param outputActions
 * @param outputError
 * @param runtimeFactValues
 * @param factValues
 * @param oldFactValues
 * @param inputFacts
 */
function handleRuleEvaluationDebug(rule, rulesetName, outputActions, outputError, runtimeFactValues, factValues, oldFactValues, inputFacts = []) {
    const executionId = `${rulesetName} - ${rule.name}`;
    const reasons = {};
    for (let index = 0; index < factValues.length; index++) {
        if (!oldFactValues) {
            (reasons[rule.id] ||= {})[inputFacts[index]] = { factName: inputFacts[index], newValue: factValues[index] };
        }
        else if (oldFactValues[index]?.toString() !== factValues[index]?.toString()) {
            (reasons[rule.id] ||= {})[inputFacts[index]] = { factName: inputFacts[index], oldValue: oldFactValues[index], newValue: factValues[index] };
        }
    }
    const ruleEvaluation = {
        timestamp: Date.now(),
        outputActions,
        id: executionId,
        triggers: reasons,
        temporaryFacts: rule.outputRuntimeFacts.concat(rule.inputRuntimeFacts).reduce((acc, runtimeFactName) => {
            if (typeof runtimeFactValues[runtimeFactName] !== 'undefined') {
                acc[runtimeFactName] = runtimeFactValues[runtimeFactName];
            }
            return acc;
        }, {}),
        rule: { id: rule.id, name: rule.name },
        ...(outputError ? { error: outputError } : {})
    };
    return ruleEvaluation;
}

/**
 * Rules engine debugger object to emit debug events
 */
class EngineDebugger {
    /** Retrieved the rules engine plugged to the debugger */
    get rulesEngine() {
        return this.registeredRuleEngine;
    }
    /**
     * Instantiate a rules engine debugger
     * @param options Options to configure the debugger
     */
    constructor(options) {
        this.registeredRulesets = [];
        this.requestFactsSnapshot = new Subject();
        this.debugEventsSubject$ = new ReplaySubject(options?.eventsStackLimit);
        this.initializePerformanceObserver();
        this.debugEvents$ = new Observable((subscriber) => {
            const factsSnapshotSubscription = this.requestFactsSnapshot.pipe(debounceTime(1000), switchMap(async () => {
                const timestamp = Date.now();
                const facts = await this.getFactsSnapshot(this.registeredRuleEngine.getRegisteredFactsNames());
                this.debugEventsSubject$.next(() => ({
                    timestamp,
                    type: 'AvailableFactsSnapshot',
                    facts
                }));
            })).subscribe();
            const debugEventsSubscription = this.debugEventsSubject$.pipe(withLatestFrom(this.performanceMeasures$), concatMap(async ([eventFunc, performanceMeasures]) => {
                const debugEvent = await eventFunc();
                if (debugEvent.type === 'RulesetExecution' || debugEvent.type === 'RulesetExecutionError') {
                    let rulesetDuration = 0;
                    debugEvent.rulesEvaluations.forEach((rule) => {
                        const mark = `rules-engine:${this.registeredRuleEngine?.rulesEngineInstanceName || ''}:${debugEvent.rulesetName}:${rule.rule.name}`;
                        const measures = performanceMeasures.filter((m) => m.name === mark);
                        const duration = measures.at(-1)?.duration || 0;
                        rule.duration = duration;
                        rulesetDuration += duration;
                    });
                    debugEvent.duration = rulesetDuration;
                }
                return debugEvent;
            }), tap((debugEvent) => {
                if (debugEvent.type === 'RulesetExecution') {
                    this.rulesEngine?.logger?.debug?.(`${debugEvent.rulesetName} has been triggered and resulted in ${JSON.stringify(debugEvent.outputActions)}`);
                }
            })).subscribe(subscriber);
            return () => {
                factsSnapshotSubscription.unsubscribe();
                debugEventsSubscription.unsubscribe();
            };
        }).pipe(share());
    }
    initializePerformanceObserver() {
        this.performanceMeasures$ = new Observable((subscriber) => {
            const performanceObserver = new PerformanceObserver((list) => {
                subscriber.next(list.getEntries());
            });
            performanceObserver.observe({ entryTypes: ['measure'] });
            return () => performanceObserver.disconnect();
        }).pipe(startWith([]), shareReplay(1));
    }
    async createBaseExecutionOutputObject(ruleset, executionCounter, rulesetInputFacts, runtimeFactValues, rulesetTriggers, rulesExecutions) {
        const inputFacts = await this.getFactsSnapshot(rulesetInputFacts);
        const baseRulesetOutputExecution = {
            executionCounter,
            executionId: `${ruleset.id}-${executionCounter}`,
            rulesetId: ruleset.id,
            rulesetName: ruleset.name,
            inputFacts,
            triggers: rulesetTriggers,
            rulesEvaluations: flagCachedRules(rulesExecutions?.sort((a, b) => a.timestamp - b.timestamp) || [], rulesetTriggers),
            temporaryFacts: runtimeFactValues
        };
        return baseRulesetOutputExecution;
    }
    async rulesetExecution(timestamp, ruleset, executionCounter, rulesetInputFacts, allOutputActions, runtimeFactValues, rulesetTriggers, rulesExecutions) {
        const baseRulesetOutputExecution = await this.createBaseExecutionOutputObject(ruleset, executionCounter, rulesetInputFacts, runtimeFactValues, rulesetTriggers, rulesExecutions);
        const rulesetOutputExecution = {
            timestamp,
            type: 'RulesetExecution',
            outputActions: allOutputActions,
            ...baseRulesetOutputExecution
        };
        return rulesetOutputExecution;
    }
    async rulesetExecutionError(timestamp, ruleset, rulesetInputFacts, executionCounter, runtimeFactValues, rulesetTriggers, rulesExecutions) {
        const baseRulesetOutputExecution = await this.createBaseExecutionOutputObject(ruleset, executionCounter, rulesetInputFacts, runtimeFactValues, rulesetTriggers, rulesExecutions);
        const rulesExecWithErrors = rulesExecutions.filter((ex) => !!ex && !!ex.error);
        const rulesetOutputExecutionSkip = {
            timestamp,
            type: 'RulesetExecutionError',
            rulesCausingTheError: rulesExecWithErrors.map((e) => e.rule) || [],
            errors: rulesExecWithErrors.map((e) => e.error),
            ...baseRulesetOutputExecution
        };
        return rulesetOutputExecutionSkip;
    }
    /**
     * Plug the debugger to a Rule Engine
     * @param rulesEngine
     */
    registerRuleEngine(rulesEngine) {
        this.registeredRuleEngine = rulesEngine;
    }
    /**
     * Handle ruleset execution debug info
     * @param currRes
     * @param prevRes
     * @param allExecutionsValid
     * @param rulesetInputFacts
     * @param runtimeFactValues
     * @param executionCounter
     * @param ruleset
     */
    handleDebugRulesetExecutionInfo(currRes, prevRes, allExecutionsValid, rulesetInputFacts, runtimeFactValues, executionCounter, ruleset) {
        const rulesetTriggers = retrieveRulesetTriggers(currRes, prevRes);
        const rulesetOutputExecution = currRes.map((r) => r.evaluation);
        if (!allExecutionsValid) {
            this.addRulesetExecutionErrorEvent(ruleset, rulesetInputFacts, executionCounter, runtimeFactValues, rulesetTriggers, rulesetOutputExecution);
        }
        return {
            executionCounter,
            rulesetOutputExecution,
            allExecutionsValid,
            rulesetTriggers
        };
    }
    /**
     * Emits an 'AvailableRulesets' debug event when rulesets are registered to the rules engine
     * @param rulesets
     */
    addAvailableRulesets(rulesets) {
        const timestamp = Date.now();
        this.registeredRulesets = [...this.registeredRulesets, ...rulesets.map((r) => ({ name: r.name, id: r.id }))];
        this.debugEventsSubject$.next(() => ({
            timestamp,
            type: 'AvailableRulesets',
            availableRulesets: this.registeredRulesets
        }));
    }
    /**
     * Computes and emits an 'ActiveRulesets' debug event when the active rulesets are changing
     * @param ruleSetExecutorMap map off all rulesets executors
     * @param restrictiveRuleSets ids of the rulesets to activate; if not provided all registered rulesets will be considered as active
     */
    activeRulesetsChange(ruleSetExecutorMap, restrictiveRuleSets) {
        const timestamp = Date.now();
        const rulesets = Object.keys(ruleSetExecutorMap).map((rulesetId) => ruleSetExecutorMap[rulesetId].engineRuleset);
        const activeRulesets = restrictiveRuleSets
            ? Object.values(rulesets).filter((ruleSet) => restrictiveRuleSets.includes(ruleSet.id))
            : Object.values(rulesets);
        this.debugEventsSubject$.next(() => ({
            timestamp,
            type: 'ActiveRulesets',
            rulesets: activeRulesets.map((a) => ({ name: ruleSetExecutorMap[a.id].ruleset.name, id: ruleSetExecutorMap[a.id].ruleset.id }))
        }));
    }
    /**
     * Emits an 'AllActions' debug event each time the rules engine outputs the list of actions
     * @param actions list of outputted actions
     */
    allActionsChange(actions) {
        const timestamp = Date.now();
        this.debugEventsSubject$.next(() => ({ timestamp, type: 'AllActions', actions }));
    }
    /**
     * Emits a 'RulesetExecution' debug event at the output of a successful ruleset execution
     * @param ruleset
     * @param executionCounter
     * @param rulesetInputFacts
     * @param allOutputActions
     * @param runtimeFactValues
     * @param rulesetTriggers
     * @param rulesExecutions
     */
    addRulesetExecutionEvent(ruleset, executionCounter, rulesetInputFacts, allOutputActions, runtimeFactValues, rulesetTriggers, rulesExecutions) {
        const timestamp = Date.now();
        this.debugEventsSubject$.next(() => this.rulesetExecution(timestamp, ruleset, executionCounter, rulesetInputFacts, allOutputActions, runtimeFactValues, rulesetTriggers, rulesExecutions));
    }
    /**
     * Emits a 'RulesetExecutionError' debug event at the output of a failing ruleset execution
     * @param ruleset
     * @param rulesetInputFacts
     * @param executionCounter
     * @param runtimeFactValues
     * @param rulesetTriggers
     * @param rulesExecutions
     */
    addRulesetExecutionErrorEvent(ruleset, rulesetInputFacts, executionCounter, runtimeFactValues, rulesetTriggers, rulesExecutions) {
        const timestamp = Date.now();
        this.debugEventsSubject$.next(() => this.rulesetExecutionError(timestamp, ruleset, rulesetInputFacts, executionCounter, runtimeFactValues, rulesetTriggers, rulesExecutions));
    }
    /**
     * Emits a 'AvailableFactsSnapshot' debug event when a fact value is updated
     * @param _id
     * @param factValue$
     */
    addAvailableFactsSnapshotEvent(_id, factValue$) {
        factValue$.subscribe(() => this.requestFactsSnapshot.next());
    }
    /**
     * Returns a list of fact name and value pairs
     * @param factsNames List of facts names to get the value for
     */
    async getFactsSnapshot(factsNames) {
        const facts = [];
        if (!this.registeredRuleEngine) {
            throw new Error('Rule engine not plugged to the debugger');
        }
        for (const factName of factsNames) {
            facts.push({ factName, value: await this.registeredRuleEngine.retrieveFactValue(factName) });
        }
        return facts;
    }
}

/**
 * Filter the actions outputted by the rules engine, based on active rulesets
 * @param restrictiveRuleSets list of rules sets to get the event stream for
 */
function filterRulesetsEventStream(restrictiveRuleSets) {
    return (source$) => source$.pipe(switchMap((ruleSetExecutorMap) => {
        const rulesets = Object.keys(ruleSetExecutorMap).map((rulesetId) => ruleSetExecutorMap[rulesetId].engineRuleset);
        const activeRulesets = restrictiveRuleSets
            ? Object.values(rulesets).filter((ruleSet) => restrictiveRuleSets.includes(ruleSet.id))
            : Object.values(rulesets);
        return activeRulesets?.length > 0
            ? combineLatest(activeRulesets.map((ruleset) => ruleset.rulesResultsSubject$)).pipe(map((item) => item.reduce((acc, currentValue) => {
                acc.push(...currentValue);
                return acc;
            }, [])))
            : of([]);
    }), shareReplay(1));
}

/**
 * Execute Operator
 * @param lhs Left hand side
 * @param rhs Right hand side
 * @param operator Operator to compare values
 * @param operatorFacts Facts that operator can depend on
 */
function executeOperator(lhs, rhs, operator, operatorFacts) {
    const validLhs = (!operator.validateLhs || operator.validateLhs(lhs));
    const validRhs = (!operator.validateRhs || operator.validateRhs(rhs));
    let operatorFactValues;
    if (operatorFacts && operator.factImplicitDependencies) {
        operatorFactValues = operator.factImplicitDependencies.reduce((acc, dep) => {
            if (operatorFacts[dep]) {
                acc[dep] = operatorFacts[dep];
            }
            else {
                throw new Error(`The fact "${dep}" requested by ${operator.name} cannot be found.`);
            }
            return acc;
        }, {});
    }
    if (!validLhs) {
        throw new Error(`Invalid left operand: ${JSON.stringify(lhs)}`);
    }
    if (!validRhs) {
        throw new Error(`Invalid right operand: ${JSON.stringify(rhs)}`);
    }
    const obs = operator.evaluator(lhs, rhs, operatorFactValues);
    return obs;
}
/**
 * Validate a number operand
 * @param operand value of one of the operands
 */
function numberValidator(operand) {
    // eslint-disable-next-line @typescript-eslint/restrict-template-expressions -- needed to convert any variable in a number (or NaN)
    return operand !== '' && !Array.isArray(operand) && !Number.isNaN(+`${operand}`);
}
/**
 * Validate an operand is a range of numbers
 * @param operatorInput value of one of the operands
 */
function isRangeNumber(operatorInput) {
    return Array.isArray(operatorInput)
        && operatorInput.length === 2
        && numberValidator(operatorInput[0])
        && numberValidator(operatorInput[1])
        && operatorInput[0] <= operatorInput[1];
}
/**
 * Verifies if the parameter is a valid date for the operator (getTime function available returning a number)
 * @param operatorInput
 */
const isValidDate = (operatorInput) => {
    if (!operatorInput || typeof operatorInput.getTime !== 'function') {
        return false;
    }
    const getTimeResult = operatorInput.getTime();
    return typeof getTimeResult === 'number' && !Number.isNaN(getTimeResult);
};
/**
 * Verifies if the parameter is a valid input for Date constructor (new Date returns a valid date)
 * @param operatorInput
 */
const isValidDateInput = (operatorInput) => {
    return operatorInput === 0 || (!!operatorInput && isValidDate(new Date(operatorInput)));
};
/**
 * Verifies if the parameter is a valid date range
 * @param operatorInput
 */
const isValidDateRange = (operatorInput) => {
    return Array.isArray(operatorInput)
        && operatorInput.length === 2
        && isValidDateInput(operatorInput[0])
        && isValidDateInput(operatorInput[1])
        && new Date(operatorInput[0]) <= new Date(operatorInput[1]);
};
/**
 * Validate that a value is a supported simple type
 * @param value value to validate
 */
function isSupportedSimpleTypes(value) {
    return ['string', 'boolean', 'number', 'undefined'].includes(typeof value) || value === null || isValidDate(value);
}
/**
 * Validate that a value is a string
 * @param value
 */
function isString(value) {
    return typeof value === 'string';
}
/**
 * Parse input to return RegExp
 * @param inputRegExp
 */
function parseRegExp(inputRegExp) {
    if (inputRegExp.startsWith('/')) {
        const finalSlash = inputRegExp.lastIndexOf('/');
        const regexpPattern = inputRegExp.slice(1, finalSlash);
        const regexpFlags = inputRegExp.slice(finalSlash + 1);
        return new RegExp(regexpPattern, regexpFlags);
    }
    return new RegExp(inputRegExp);
}

/**
 * Check if any of the variable's value is equal to a specific value
 * @title contains
 */
const arrayContains = {
    name: 'arrayContains',
    evaluator: (value, b) => value.includes(b),
    validateLhs: Array.isArray,
    validateRhs: isSupportedSimpleTypes
};
/**
 * Check if the specified text value is included in the text variable
 * @title contains
 */
const stringContains = {
    name: 'stringContains',
    evaluator: (inputString, substring) => inputString.includes(substring),
    validateLhs: isString,
    validateRhs: isString
};
/**
 * Check if every value of the variable is different from a specific value
 * @title does not contain
 */
const notArrayContains = {
    name: 'notArrayContains',
    evaluator: (array, value) => !array.includes(value),
    validateLhs: Array.isArray,
    validateRhs: isSupportedSimpleTypes
};
/**
 * Check if the specified text value is not included in the text variable
 * @title does not contain
 */
const notStringContains = {
    name: 'notStringContains',
    evaluator: (inputString, substring) => !inputString.includes(substring),
    validateLhs: isString,
    validateRhs: isString
};
/**
 * Check if every value of the variable equals a specific value
 * @title all equal to
 */
const allEqual = {
    name: 'allEqual',
    // eslint-disable-next-line eqeqeq -- possibility of comparing string and number values
    evaluator: (array, value) => array.every((elementValue) => elementValue == value),
    validateLhs: Array.isArray,
    validateRhs: isSupportedSimpleTypes
};
/**
 * Check if every numerical value of the variable is greater than a specific value
 * @title all >
 */
const allGreater = {
    name: 'allGreater',
    evaluator: (array, value) => array.every((elementValue) => numberValidator(elementValue) && +elementValue > +value),
    validateLhs: Array.isArray,
    validateRhs: numberValidator
};
/**
 * Check if every value of the variable is in a specific list
 * @title all in
 */
const allIn = {
    name: 'allIn',
    evaluator: (array, value) => array.every((elementValue) => value.includes(elementValue)),
    validateLhs: Array.isArray,
    validateRhs: Array.isArray
};
/**
 * Check if every value of the variable is not in a specific list
 * @title none in
 */
const allNotIn = {
    name: 'allNotIn',
    evaluator: (array, value) => !array.some((elementValue) => value.includes(elementValue)),
    validateLhs: Array.isArray,
    validateRhs: Array.isArray
};
/**
 * Check if every numerical value of the variable is lower than a specific value
 * @title all <
 */
const allLower = {
    name: 'allLower',
    evaluator: (arrayNumber, number) => arrayNumber.every((elementNumber) => elementNumber < +number),
    validateLhs: Array.isArray,
    validateRhs: numberValidator
};
/**
 * Check if every string value of the variable matches a specific pattern
 * @title all match
 */
const allMatch = {
    name: 'allMatch',
    evaluator: (array, inputRegExp) => {
        const regExp = parseRegExp(inputRegExp);
        return array.every((elementValue) => regExp.test(elementValue));
    },
    validateLhs: Array.isArray,
    validateRhs: isString
};
/**
 * Check if every value of the variable is included in a specified range
 * @title all between
 */
const allRangeNumber = {
    name: 'allRangeNumber',
    evaluator: (rangeArray, [from, to]) => rangeArray.every((elementValue) => elementValue >= +from && elementValue <= +to),
    validateLhs: Array.isArray,
    validateRhs: isRangeNumber
};
/**
 * Check if at least one of the values of the variable equals a specific value
 * @title one equal to
 */
const oneEquals = {
    name: 'oneEquals',
    // eslint-disable-next-line eqeqeq -- possibility of comparing string and number values
    evaluator: (array, value) => array.some((elementValue) => elementValue == value),
    validateLhs: Array.isArray,
    validateRhs: isSupportedSimpleTypes
};
/**
 * Check if one of the values of the variable is greater than a specific value
 * @title one >
 */
const oneGreater = {
    name: 'oneGreater',
    evaluator: (arrayNumber, number) => arrayNumber.some((elementValue) => elementValue > +number),
    validateLhs: Array.isArray,
    validateRhs: numberValidator
};
/**
 * Check if at least one of the values of the variable is equal to one in a specified list
 * @title one in
 */
const oneIn = {
    name: 'oneIn',
    evaluator: (firstArray, secondArray) => firstArray.some((elementValue) => secondArray.includes(elementValue)),
    validateLhs: Array.isArray,
    validateRhs: Array.isArray
};
/**
 * Check if one of the values of the variable is lower than a specific value
 * @title one <
 */
const oneLower = {
    name: 'oneLower',
    evaluator: (arrayNumber, number) => arrayNumber.some((elementValue) => elementValue < +number),
    validateLhs: Array.isArray,
    validateRhs: numberValidator
};
/**
 * Check if one of the values of the variable matches a specific pattern
 * @title one matches
 */
const oneMatches = {
    name: 'oneMatches',
    evaluator: (arrayString, inputRegExp) => {
        const regExp = parseRegExp(inputRegExp);
        return arrayString.some((elementValue) => regExp.test(elementValue));
    },
    validateLhs: Array.isArray,
    validateRhs: isString
};
/**
 * Check if one of the values of the variable is included in a specified range
 * @title one between
 */
const oneRangeNumber = {
    name: 'oneRangeNumber',
    evaluator: (arrayNumber, [from, to]) => arrayNumber.some((elementValue) => elementValue >= +from && elementValue <= +to),
    validateLhs: Array.isArray,
    validateRhs: isRangeNumber
};
/**
 * Check if the number of values of the variable is equal to a specific value
 * @title number of =
 */
const lengthEquals = {
    name: 'lengthEquals',
    evaluator: (array, length) => array.length === Number(length),
    validateLhs: Array.isArray,
    validateRhs: numberValidator
};
/**
 * Check if the number of values of the variable is different from a specific value
 * @title number of ≠
 */
const lengthNotEquals = {
    name: 'lengthNotEquals',
    evaluator: (array, length) => array.length !== Number(length),
    validateLhs: Array.isArray,
    validateRhs: numberValidator
};
/**
 * Check if the number of values of the variable is lower or equal to a specific value
 * @title number of ≤
 */
const lengthLessThanOrEquals = {
    name: 'lengthLessThanOrEquals',
    evaluator: (array, length) => array.length <= +length,
    validateLhs: Array.isArray,
    validateRhs: numberValidator
};
/**
 * Check if the number of values of the variable is lower than a specific value
 * @title number of <
 */
const lengthLessThan = {
    name: 'lengthLessThan',
    evaluator: (array, length) => array.length < +length,
    validateLhs: Array.isArray,
    validateRhs: numberValidator
};
/**
 * Check if the number of values of the variable is greater or equal to a specific value
 * @title number of ≥
 */
const lengthGreaterThanOrEquals = {
    name: 'lengthGreaterThanOrEquals',
    evaluator: (array, length) => array.length >= +length,
    validateLhs: Array.isArray,
    validateRhs: numberValidator
};
/**
 * Check if the number of values of the variable is greater than a specific value
 * @title number of >
 */
const lengthGreaterThan = {
    name: 'lengthGreaterThan',
    evaluator: (array, length) => array.length > +length,
    validateLhs: Array.isArray,
    validateRhs: numberValidator
};
/** List of all default array operators */
const arrayBasedOperators = [
    allEqual,
    allGreater,
    allIn,
    allLower,
    allMatch,
    allNotIn,
    allRangeNumber,
    arrayContains,
    lengthEquals,
    lengthNotEquals,
    lengthGreaterThan,
    lengthGreaterThanOrEquals,
    lengthLessThan,
    lengthLessThanOrEquals,
    notArrayContains,
    notStringContains,
    oneEquals,
    oneGreater,
    oneIn,
    oneLower,
    oneMatches,
    oneRangeNumber,
    stringContains
];

/**
 * Check if a variable is equal to a specific value
 * @title is equal to
 */
const equals = {
    name: 'equals',
    // eslint-disable-next-line eqeqeq -- possibility of comparing string and number values
    evaluator: (firstValue, secondValue) => firstValue == secondValue
};
/**
 * Check if a variable is different from a specific value
 * @title is not equal to
 */
const notEquals = {
    name: 'notEquals',
    // eslint-disable-next-line eqeqeq -- possibility of comparing string and number values
    evaluator: (firstValue, secondValue) => firstValue != secondValue
};
/**
 * Check if the variable's value is included in a specified list
 * @title is in
 */
const inArray = {
    name: 'inArray',
    evaluator: (value, array) => array.includes(value),
    validateLhs: isSupportedSimpleTypes,
    validateRhs: Array.isArray
};
/**
 * Check if the variable's value is not included in the value list
 * @title is not in
 */
const notInArray = {
    name: 'notInArray',
    evaluator: (value, array) => !array.includes(value),
    validateLhs: isSupportedSimpleTypes,
    validateRhs: Array.isArray
};
/**
 * Check if the text variable is part of the specified value
 * @title within
 */
const inString = {
    name: 'inString',
    evaluator: (value, inputString) => inputString.includes(value),
    validateLhs: isString,
    validateRhs: isString
};
/**
 * Check if the text variable is not part of the specified value
 * @title not within
 */
const notInString = {
    name: 'notInString',
    evaluator: (value, inputString) => !inputString.includes(value),
    validateLhs: isString,
    validateRhs: isString
};
/**
 * Check if the variable and its value are defined
 * @title is defined
 */
const isDefined = {
    name: 'isDefined',
    evaluator: (input) => input !== undefined && input !== null
};
/**
 * Check if the variable and its value are undefined
 * @title is not defined
 */
const isUndefined = {
    name: 'isUndefined',
    evaluator: (input) => input === undefined || input === null
};
/**
 * Check if the text variable matches the specified RegExp pattern
 * @title matches the pattern
 */
const matchesPattern = {
    name: 'matchesPattern',
    evaluator: (value, inputRegExp) => {
        const regExp = parseRegExp(inputRegExp);
        return regExp.test(value);
    },
    validateLhs: isString,
    validateRhs: isString
};
/** List of all default basic operators */
const basicOperators = [
    equals, inArray, inString, isDefined, isUndefined, matchesPattern, notEquals, notInArray, notInString
];

/**
 * Check if a date variable is in a specified date range
 * @title is between
 */
const inRangeDate = {
    name: 'inRangeDate',
    evaluator: (date, [from, to]) => {
        const dateObject = new Date(date);
        return new Date(from) <= dateObject && new Date(to) >= dateObject;
    },
    validateLhs: isValidDateInput,
    validateRhs: isValidDateRange
};
/**
 * Check if the value of the variable is in the next x minutes
 * @title is in next minutes
 * @returns false for dates before `now` and for dates after `now` + `nextMinutes`, true for dates between `now` and `now` + `nextMinutes`
 */
const dateInNextMinutes = {
    name: 'dateInNextMinutes',
    evaluator: (leftDateInput, minutes, operatorFactValues) => {
        if (!operatorFactValues) {
            throw new Error('No operatorFactValues. Unable to retrieve the current time.');
        }
        if (typeof operatorFactValues.o3rCurrentTime !== 'number') {
            throw new Error('o3rCurrentTime value is not a number');
        }
        const currentTimeValue = operatorFactValues.o3rCurrentTime;
        return inRangeDate.evaluator(leftDateInput, [currentTimeValue, currentTimeValue + +minutes * 60_000]);
    },
    factImplicitDependencies: ['o3rCurrentTime'],
    validateLhs: isValidDateInput,
    validateRhs: numberValidator
};
/**
 * Check if the value of the variable is not in the next x minutes
 * @title is not in next minutes
 * @returns false for dates before `now` and for dates between `now` and `now` + `nextMinutes`, true for dates after `now` + `nextMinutes`
 */
const dateNotInNextMinutes = {
    name: 'dateNotInNextMinutes',
    evaluator: (leftDateInput, minutes, operatorFactValues) => {
        if (!operatorFactValues) {
            throw new Error('No operatorFactValues. Unable to retrieve the current time.');
        }
        if (typeof operatorFactValues.o3rCurrentTime !== 'number') {
            throw new Error('o3rCurrentTime value is not a number');
        }
        const currentTimeValue = operatorFactValues.o3rCurrentTime;
        const now = new Date(currentTimeValue);
        const leftDate = new Date(leftDateInput);
        const targetDate = new Date(new Date(currentTimeValue).setMinutes(now.getMinutes() + +minutes));
        return leftDate >= now && leftDate > targetDate;
    },
    factImplicitDependencies: ['o3rCurrentTime'],
    validateLhs: isValidDateInput,
    validateRhs: numberValidator
};
/**
 * Check if a date variable is prior than a specified date
 * @title is before
 */
const dateBefore = {
    name: 'dateBefore',
    evaluator: (leftDate, rightDate) => {
        const firstDateTime = new Date(leftDate).setHours(0, 0, 0, 0);
        const secondDateTime = new Date(rightDate).setHours(0, 0, 0, 0);
        return firstDateTime < secondDateTime;
    },
    validateLhs: isValidDateInput,
    validateRhs: isValidDateInput
};
/**
 * Check if a date variable is posterior than a specified date
 * @title is after
 */
const dateAfter = {
    name: 'dateAfter',
    evaluator: (leftDate, rightDate) => {
        const firstDateTime = new Date(leftDate).setHours(0, 0, 0, 0);
        const secondDateTime = new Date(rightDate).setHours(0, 0, 0, 0);
        return firstDateTime > secondDateTime;
    },
    validateLhs: isValidDateInput,
    validateRhs: isValidDateInput
};
/**
 * Check if a date variable is the same as a specified date
 * @title is equal to
 */
const dateEquals = {
    name: 'dateEquals',
    evaluator: (leftDate, rightDate) => {
        const firstDateIgnoringHours = new Date(leftDate).setHours(0, 0, 0, 0);
        const secondDateIgnoringHours = new Date(rightDate).setHours(0, 0, 0, 0);
        return firstDateIgnoringHours === secondDateIgnoringHours;
    },
    validateLhs: isValidDateInput,
    validateRhs: isValidDateInput
};
/**
 * Check if a date variable is different from a specified date
 * @title is not equal
 */
const dateNotEquals = {
    name: 'dateNotEquals',
    evaluator: (leftDate, rightDate) => {
        const firstDateIgnoringHours = new Date(leftDate).setHours(0, 0, 0, 0);
        const secondDateIgnoringHours = new Date(rightDate).setHours(0, 0, 0, 0);
        return firstDateIgnoringHours !== secondDateIgnoringHours;
    },
    validateLhs: isValidDateInput,
    validateRhs: isValidDateInput
};
const dateBasedOperators = [
    inRangeDate, dateInNextMinutes, dateNotInNextMinutes, dateAfter, dateBefore, dateEquals, dateNotEquals
];

/**
 * Check if the number variable is greater or equal to a specific value
 * @title ≥
 */
const greaterThanOrEqual = {
    name: 'greaterThanOrEqual',
    evaluator: (firstNumber, secondNumber) => firstNumber >= secondNumber,
    validateLhs: numberValidator,
    validateRhs: numberValidator
};
/**
 * Check if the number variable is greater than a specific value
 * @title >
 */
const greaterThan = {
    name: 'greaterThan',
    evaluator: (firstNumber, secondNumber) => firstNumber > secondNumber,
    validateLhs: numberValidator,
    validateRhs: numberValidator
};
/**
 * Check if the number variable is lower or equal to a specific value
 * @title ≤
 */
const lessOrEqual = {
    name: 'lessOrEqual',
    evaluator: (firstNumber, secondNumber) => firstNumber <= secondNumber,
    validateLhs: numberValidator,
    validateRhs: numberValidator
};
/**
 * Check if the number variable is lower than a specific value
 * @title <
 */
const lessThan = {
    name: 'lessThan',
    evaluator: (firstNumber, secondNumber) => firstNumber < secondNumber,
    validateLhs: numberValidator,
    validateRhs: numberValidator
};
/** List of all default number based operators */
const numberBasedOperators = [greaterThan, greaterThanOrEqual, lessThan, lessOrEqual];

const operatorList = [...arrayBasedOperators, ...basicOperators, ...numberBasedOperators, ...dateBasedOperators];

/**
 * Determine if the condition is a properties condition
 * @param condition Condition to analyze
 */
function isConditionProperties(condition) {
    return condition && typeof condition.operator !== 'undefined' && typeof condition.lhs !== 'undefined';
}
/**
 * Determine if the given operand is a Fact operand
 * @param operand Operand to analyze
 */
function isOperandFact(operand) {
    return operand && operand.type === 'FACT';
}
/**
 * Determine if the given operand is a Inner Fact operand
 * @param operand Operand to analyze
 */
function isOperandRuntimeFact(operand) {
    return operand && operand.type === 'RUNTIME_FACT';
}
/**
 * Determine if the given operand is a Static Value operand
 * @param operand Operand to analyze
 */
function isOperandLiteral(operand) {
    return operand && operand.type === 'LITERAL';
}
/**
 * Determine if the given condition is All based child conditions
 * @param condition Condition node
 */
function isAllConditions(condition) {
    return condition && typeof condition.all !== 'undefined';
}
/**
 * Determine if the given condition is Any based child conditions
 * @param condition Condition node
 */
function isAnyConditions(condition) {
    return condition && typeof condition.any !== 'undefined';
}
/**
 * Determine if the given condition is Not based child conditions
 * @param condition Condition node
 */
function isNotCondition(condition) {
    return condition && typeof condition.not !== 'undefined';
}

/** Ruleset executor */
class RulesetExecutor {
    /**
     * Create a new ruleset executor
     * @param ruleset Ruleset to evaluate
     * @param rulesEngine Instance of the rules engine
     */
    constructor(ruleset, rulesEngine) {
        this.executionCounter = 0;
        /**
         * Find rule input facts
         * @param obj
         */
        this.findRuleInputFacts = (obj) => {
            const ruleInputFacts = new Set();
            this.collectRuleInputFacts(obj, ruleInputFacts);
            return Array.from(ruleInputFacts);
        };
        this.ruleset = ruleset;
        this.rulesEngine = rulesEngine;
        this.operators = rulesEngine.operators;
        this.engineRuleset = this.plugRuleset();
    }
    /**
     * Recursively explores a rule to identify and collect input facts.
     * Input facts are identified based on the 'FACT' type and operator-specific implicit dependencies.
     * @param currentObject The current object being explored.
     * @param ruleInputFacts A set to store the identified input facts for the rule.
     */
    collectRuleInputFacts(currentObject, ruleInputFacts) {
        if (currentObject && isOperandFact(currentObject)) {
            ruleInputFacts.add(currentObject.value);
        }
        else if (Array.isArray(currentObject)) {
            currentObject.forEach((elem) => this.collectRuleInputFacts(elem, ruleInputFacts));
        }
        else {
            for (const key in currentObject) {
                if ((key === 'operator') && isConditionProperties(currentObject)) {
                    const op = this.operators[currentObject[key]];
                    if (op && op.factImplicitDependencies) {
                        op.factImplicitDependencies.forEach((dep) => ruleInputFacts.add(dep));
                    }
                }
                else if (typeof currentObject[key] === 'object') {
                    this.collectRuleInputFacts(currentObject[key], ruleInputFacts);
                }
            }
        }
    }
    /**
     * Report performance mark for a rule run
     * @param rule Rule to measure
     * @param status status of the rule evaluation
     */
    performanceMark(rule, status) {
        if (this.rulesEngine.performance) {
            const markName = `rules-engine:${this.rulesEngine.rulesEngineInstanceName}:${this.ruleset.name}:${rule.name}`;
            this.rulesEngine.performance.mark(`${markName}:${status}`);
            if (status === 'end') {
                this.rulesEngine.performance.measure(markName, `${markName}:start`, `${markName}:end`);
            }
        }
    }
    /**
     * Get operand value stream according to its type
     * @param operand operand of the condition
     * @param factsValue
     * @param runtimeFactValues
     */
    getOperandValue(operand, factsValue, runtimeFactValues) {
        if (typeof operand === 'undefined') {
            return undefined;
        }
        else if (isOperandFact(operand)) {
            const factValue = factsValue[operand.value];
            // eslint-disable-next-line new-cap -- convention for JSONPath
            return operand.path ? factValue && JSONPath({ wrap: false, json: factValue, path: operand.path }) : factValue;
        }
        else if (isOperandLiteral(operand)) {
            return operand.value;
        }
        else if (isOperandRuntimeFact(operand)) {
            return runtimeFactValues[operand.value];
        }
        return undefined;
    }
    /**
     * Process a root rule from a ruleset, and return the associated actions to be processed
     * Will also update the runtimeFactValues map that is ruleset wise
     * Note that runtimeFactValues will be mutated by all the runtime facts actions executed
     * @param rule
     * @param factsValue
     * @param runtimeFactValues
     * @protected
     */
    evaluateRule(rule, factsValue, runtimeFactValues) {
        return this.evaluateBlock(rule.rootElement, factsValue, runtimeFactValues);
    }
    /**
     * Recursively process a block to extract all the actions keeping the order
     * Note that runtimeFactValues will be mutated by all the runtime facts actions executed
     * @param element
     * @param factsValue
     * @param runtimeFactValues This runtime fact map will be mutated by all the runtime facts actions executed
     * @param actions
     * @protected
     */
    evaluateBlock(element, factsValue, runtimeFactValues, actions = []) {
        if (this.isIfElseBlock(element)) {
            (!element.condition || this.evaluateCondition(element.condition, factsValue, runtimeFactValues) ? element.successElements : element.failureElements)
                .forEach((elementResult) => this.evaluateBlock(elementResult, factsValue, runtimeFactValues, actions));
        }
        else if (this.isActionBlock(element)) {
            if (this.isActionSetTemporaryFactBlock(element)) {
                runtimeFactValues[element.fact] = element.value;
            }
            else {
                actions.push(element);
            }
        }
        return actions;
    }
    /**
     * Returns true if the element is a IfElse block
     * @param element
     * @protected
     */
    isIfElseBlock(element) {
        return element.blockType === 'IF_ELSE';
    }
    /**
     * Returns true if the element is an action block
     * @param element
     * @protected
     */
    isActionBlock(element) {
        return 'elementType' in element && element.elementType === 'ACTION';
    }
    /**
     * Returns true if the action sets a temporary fact
     * @param element
     * @protected
     */
    isActionSetTemporaryFactBlock(element) {
        return 'actionType' in element && element.actionType === 'SET_FACT';
    }
    /**
     * Evaluate a condition block
     * @param nestedCondition
     * @param factsValue
     * @param runtimeFactValues
     * @protected
     */
    evaluateCondition(nestedCondition, factsValue, runtimeFactValues) {
        if (isConditionProperties(nestedCondition)) {
            const operator = this.operators[nestedCondition.operator];
            if (operator === undefined) {
                throw new Error(`Unknown operator : ${nestedCondition.operator}, skipping the rule execution...`);
            }
            return executeOperator(this.getOperandValue(nestedCondition.lhs, factsValue, runtimeFactValues), this.getOperandValue('rhs' in nestedCondition ? nestedCondition.rhs : undefined, factsValue, runtimeFactValues), operator, factsValue);
        }
        if (isNotCondition(nestedCondition)) {
            return !this.evaluateCondition(nestedCondition.not, factsValue, runtimeFactValues);
        }
        if (nestedCondition.all || nestedCondition.any) {
            const evaluate = (condition) => this.evaluateCondition(condition, factsValue, runtimeFactValues);
            return isAllConditions(nestedCondition) ? nestedCondition.all.every((element) => evaluate(element)) : nestedCondition.any.some((element) => evaluate(element));
        }
        throw new Error(`Unknown condition block met : ${JSON.stringify(nestedCondition)}`);
    }
    /**
     * Plug ruleset to fact streams and trigger a first evaluation
     */
    plugRuleset() {
        const inputFactsForRule = {};
        this.ruleset.rules.forEach((rule) => inputFactsForRule[rule.id] = this.findRuleInputFacts(rule.rootElement));
        const factsThatRerunEverything = [];
        this.ruleset.rules.forEach((rule) => {
            if (rule.outputRuntimeFacts.length > 0 || rule.inputRuntimeFacts.length > 0) {
                factsThatRerunEverything.push(...inputFactsForRule[rule.id]);
            }
            else { }
        });
        const triggerFull$ = factsThatRerunEverything.length === 0
            ? of([])
            : combineLatest(factsThatRerunEverything.map((fact) => this.rulesEngine.retrieveOrCreateFactStream(fact)));
        const result$ = triggerFull$.pipe(switchMap(() => {
            const runtimeFactValues = {};
            let rulesetInputFacts;
            if (this.rulesEngine.debugMode) {
                rulesetInputFacts = Array.from(this.ruleset.rules.reduce((acc, rule) => {
                    inputFactsForRule[rule.id].forEach((factName) => acc.add(factName));
                    return acc;
                }, new Set()));
            }
            return combineLatest(this.ruleset.rules.map((rule) => {
                const inputFacts = inputFactsForRule[rule.id];
                const values$ = inputFacts.map((fact) => this.rulesEngine.retrieveOrCreateFactStream(fact));
                return (values$.length > 0 ? combineLatest(values$) : of([[]]))
                    .pipe(startWith(undefined), pairwise(), tap(() => this.performanceMark(rule, 'start')), map(([oldFactValues, factValues]) => {
                    const output = { actions: undefined };
                    try {
                        output.actions = this.evaluateRule(rule, inputFacts.reduce((acc, id, index) => {
                            acc[id] = factValues[index];
                            return acc;
                        }, {}), runtimeFactValues);
                    }
                    catch (error) {
                        output.actions = undefined;
                        output.error = error;
                    }
                    if (this.rulesEngine.debugMode) {
                        output.evaluation = handleRuleEvaluationDebug(rule, this.ruleset.name, output.actions, output.error, runtimeFactValues, factValues, oldFactValues, inputFacts);
                    }
                    else if (output.error) {
                        this.rulesEngine.logger?.error(`Error while evaluating rule ID: ${rule.id}`, output.error);
                        this.rulesEngine.logger?.warn(`Skipping rule ${rule.name}, and the associated ruleset`);
                    }
                    return output;
                }), tap(() => this.performanceMark(rule, 'end')));
            })).pipe(startWith(undefined), pairwise(), map(([prevRes, currRes]) => {
                const actionsLists = currRes.map((r) => r.actions);
                const allExecutionsValid = actionsLists.every((actions) => !!actions);
                let execInfo = { actionsLists: (allExecutionsValid ? actionsLists : [[]]) };
                if (this.rulesEngine.engineDebug) {
                    execInfo = {
                        ...execInfo,
                        ...this.rulesEngine.engineDebug.handleDebugRulesetExecutionInfo(currRes, prevRes, allExecutionsValid, rulesetInputFacts, runtimeFactValues, ++this.executionCounter, this.ruleset)
                    };
                }
                return execInfo;
            }), map((output) => {
                const outputActions = [].concat(...output.actionsLists);
                if (this.rulesEngine.engineDebug && output.allExecutionsValid) {
                    this.rulesEngine.engineDebug.addRulesetExecutionEvent(this.ruleset, output.executionCounter, rulesetInputFacts, outputActions, runtimeFactValues, output.rulesetTriggers, output.rulesetOutputExecution);
                }
                return outputActions;
            }), distinctUntilChanged((prev, curr) => prev.length === 0 && curr.length === 0));
        }), shareReplay({ bufferSize: 1, refCount: true }));
        return {
            id: this.ruleset.id,
            validityRange: this.ruleset.validityRange,
            linkedComponents: this.ruleset.linkedComponents,
            rulesResultsSubject$: result$
        };
    }
}

/** Rules engine */
class RulesEngine {
    /**
     * Flag to check if the run is in debug mode or not
     */
    get debugMode() {
        return !!this.engineDebug;
    }
    /**
     * Rules engine
     * @param options rules engine options
     */
    constructor(options) {
        /** Map of registered fact stream, this map is mutated by the ruleset executors */
        this.factMap = {};
        /** Subject containing the rulesets and the results stream*/
        this.rulesetMapSubject = new BehaviorSubject({});
        this.performance = options?.performance || (typeof window === 'undefined' ? undefined : window.performance);
        this.engineDebug = options?.debugger;
        this.engineDebug?.registerRuleEngine(this);
        this.logger = options?.logger;
        this.rulesEngineInstanceName = options?.rulesEngineInstanceName || 'RulesEngine';
        this.factDefaultDelay = options?.factDefaultDelay;
        // Load default operators
        this.operators = operatorList.reduce((acc, operator) => {
            acc[operator.name] = operator;
            return acc;
        }, {});
        this.events$ = this.rulesetMapSubject.pipe(this.prepareActionsStream(), this.handleActionsStreamOutput());
        if (options?.facts) {
            this.upsertFacts(options.facts);
        }
        if (options?.rules) {
            this.upsertRulesets(options.rules);
        }
        if (options?.operators) {
            this.upsertOperators(options.operators);
        }
    }
    /**
     * Attach debug events to actions stream if debug engine is activated
     */
    handleActionsStreamOutput() {
        return (actionsStream$) => this.engineDebug ? actionsStream$.pipe(tap((allActions) => this.engineDebug.allActionsChange(allActions))) : actionsStream$;
    }
    /**
     * Create the actions stream event based on provided active rulesets ids; Handle debug too
     * @param ruleSets
     */
    prepareActionsStream(ruleSets) {
        return (rulesetMapSubject$) => (this.engineDebug
            ? rulesetMapSubject$.pipe(tap((ruleSetExecutorMap) => this.engineDebug.activeRulesetsChange(ruleSetExecutorMap, ruleSets)), filterRulesetsEventStream(ruleSets))
            : rulesetMapSubject$.pipe(filterRulesetsEventStream(ruleSets)));
    }
    /**
     * Create or retrieve a fact stream
     * The fact stream created will be registered in the engine
     * @param id ID of the fact to retrieve
     * @param factValue$ Value stream for the fact
     */
    retrieveOrCreateFactStream(id, factValue$) {
        // trick to emit undefined if the observable is not immediately emitting (to not bloc execution)
        const obs$ = factValue$
            ? merge(factValue$, of(undefined).pipe(delay(this.factDefaultDelay || 0), takeUntil(factValue$)))
            : factValue$;
        const factObj = this.factMap[id];
        if (factObj) {
            if (factValue$) {
                factObj.subject.next(obs$);
            }
            return factObj.value$;
        }
        const subject = new BehaviorSubject(obs$);
        const value$ = subject.pipe(switchMap((value) => value || of(undefined)), distinctUntilChanged(), shareReplay(1));
        this.factMap[id] = {
            subject,
            value$
        };
        return value$;
    }
    /**
     * Retrieve the promise of the latest value of a fact.
     * Return undefined if the fact is not defined.
     * @param id ID of the fact to retrieve
     */
    retrieveFactValue(id) {
        return this.factMap[id].value$ && firstValueFrom(this.retrieveOrCreateFactStream(id), { defaultValue: undefined });
    }
    /**
     * Update or insert fact in rules engine
     * @param facts fact list to add / update
     */
    upsertFacts(facts) {
        (Array.isArray(facts) ? facts : [facts]).forEach(({ id, value$ }) => {
            this.engineDebug?.addAvailableFactsSnapshotEvent(id, value$);
            this.retrieveOrCreateFactStream(id, value$);
        });
    }
    /**
     * Update or insert rule in rules engine
     * @param rulesets
     */
    upsertRulesets(rulesets) {
        this.engineDebug?.addAvailableRulesets(rulesets);
        this.rulesetMapSubject.next(rulesets.reduce((accRuleset, ruleset) => {
            accRuleset[ruleset.id] = new RulesetExecutor(ruleset, this);
            return accRuleset;
        }, { ...this.rulesetMapSubject.value }));
    }
    /**
     * Update or insert operator in rules engine
     * @param operators operator list to add / update
     */
    upsertOperators(operators) {
        this.operators = operators.reduce((acc, operator) => {
            acc[operator.name] = operator;
            return acc;
        }, { ...this.operators });
    }
    /**
     * Operator to apply on a stream of rulesets ids
     * Returns a stream of actions outputted by the rules engine, corresponding to the rulesetsIds
     */
    getEventStream() {
        return (rulesetsIds$) => rulesetsIds$.pipe(switchMap((ruleSets) => this.rulesetMapSubject.pipe(this.prepareActionsStream(ruleSets))), this.handleActionsStreamOutput());
    }
    /** Get the list of registered facts names */
    getRegisteredFactsNames() {
        return Object.keys(this.factMap);
    }
}

class RulesEngineRunnerService {
    constructor(store, logger, engineConfig) {
        this.store = store;
        this.logger = logger;
        /** Observable of component linked to the component */
        this.linkedComponents$ = new BehaviorSubject({});
        /**
         * List of action handlers
         * @deprecated will become protected in Otter v13, instead use {@link registerActionHandlers}
         */
        this.actionHandlers = new Set();
        this.enabled = !engineConfig?.dryRun;
        this.engine = new RulesEngine({
            debugger: engineConfig?.debug ? new EngineDebugger({ eventsStackLimit: engineConfig?.debugEventsStackLimit }) : undefined,
            logger: this.logger
        });
        this.ruleSets$ = combineLatest([
            this.store.pipe(select(selectActiveRuleSets)),
            this.linkedComponents$.pipe(switchMap((linkedComponentsNamesMap) => this.store.pipe(select(selectComponentsLinkedToRuleset), map((rulesetsWithLinkedComponentsMap) => Object.keys(rulesetsWithLinkedComponentsMap.or).filter((rulesetId) => rulesetsWithLinkedComponentsMap.or[rulesetId].some((componentId) => linkedComponentsNamesMap[componentId] > 0))))))
        ]).pipe(map(([activeRulesets, linkedComponentsRulesetsIds]) => ([...activeRulesets, ...linkedComponentsRulesetsIds])));
        this.events$ = this.ruleSets$.pipe(this.engine.getEventStream(), shareReplay(1));
        this.upsertOperators(operatorList);
        this.store.pipe(select(selectAllRulesets), takeUntilDestroyed()).subscribe((rulesets) => this.engine.upsertRulesets(rulesets));
        this.events$.pipe(takeUntilDestroyed(), filter(() => this.enabled)).subscribe((events) => {
            void this.executeActions(events);
        });
    }
    /**
     * Execute the list of actions
     * @param actions
     */
    async executeActions(actions) {
        const actionHandlers = [...this.actionHandlers];
        const supportedActions = new Set(actionHandlers.flatMap((handler) => handler.supportingActions));
        const actionMaps = actions
            .filter((action) => {
            const isKnown = supportedActions.has(action.actionType);
            if (!isKnown) {
                this.logger.warn(`The action ${action.actionType} does not have registered handler`);
            }
            return isKnown;
        })
            .reduce((acc, action) => {
            acc[action.actionType] ||= [];
            acc[action.actionType].push(action);
            return acc;
        }, {});
        const handling = actionHandlers
            .map((handler) => handler.executeActions(handler.supportingActions
            .filter((supportedAction) => actionMaps[supportedAction])
            .reduce((acc, supportedAction) => acc.concat(actionMaps[supportedAction]), [])));
        await Promise.all(handling);
    }
    /**
     * Update or insert fact in the rules engine
     * @param facts fact list to add / update
     */
    upsertFacts(facts) {
        this.engine.upsertFacts(facts);
    }
    /**
     * Update or insert operator in the rules engine
     * @param operators operator list to add / update
     */
    upsertOperators(operators) {
        this.engine.upsertOperators(operators);
    }
    /**
     * Upsert a list of RuleSets to be run in the rules engine
     * @param ruleSets
     */
    upsertRulesets(ruleSets) {
        this.store.dispatch(setRulesetsEntities({ entities: ruleSets }));
    }
    /**
     * Add action handlers in the rules engine
     * @param actionHandlers
     */
    registerActionHandlers(...actionHandlers) {
        actionHandlers.forEach((actionHandler) => this.actionHandlers.add(actionHandler));
    }
    /**
     * Remove action handlers in the rules engine
     * @param actionHandlers
     */
    unregisterActionHandlers(...actionHandlers) {
        actionHandlers.forEach((actionHandler) => this.actionHandlers.delete(actionHandler));
    }
    /**
     * Enable temporary a rule set
     * @param componentComputedName Name of the component to enable the ruleset for
     */
    enableRuleSetFor(componentComputedName) {
        const newMap = this.linkedComponents$.value;
        newMap[componentComputedName] = newMap[componentComputedName] ? newMap[componentComputedName] + 1 : 1;
        this.linkedComponents$.next(newMap);
    }
    /**
     * Disable temporary a rule set
     * @param componentComputedName Name of the component to disable the ruleset for
     */
    disableRuleSetFor(componentComputedName) {
        const newMap = this.linkedComponents$.value;
        if (newMap[componentComputedName] > 0) {
            newMap[componentComputedName]--;
            this.linkedComponents$.next(newMap);
        }
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineRunnerService, deps: [{ token: i1$3.Store }, { token: i2.LoggerService }, { token: RULES_ENGINE_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
    /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineRunnerService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineRunnerService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [{ type: i1$3.Store }, { type: i2.LoggerService }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [RULES_ENGINE_OPTIONS]
                }] }] });

class RulesEngineRunnerModule {
    static forRoot(options) {
        const opts = options ? { ...DEFAULT_RULES_ENGINE_OPTIONS, ...options } : DEFAULT_RULES_ENGINE_OPTIONS;
        return {
            ngModule: RulesEngineRunnerModule,
            providers: [
                { provide: RULES_ENGINE_OPTIONS, useValue: opts },
                RulesEngineRunnerService
            ]
        };
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineRunnerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineRunnerModule, imports: [StoreModule,
            RulesetsStoreModule,
            LoggerModule] }); }
    /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineRunnerModule, imports: [StoreModule,
            RulesetsStoreModule,
            LoggerModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineRunnerModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        StoreModule,
                        RulesetsStoreModule,
                        LoggerModule
                    ]
                }]
        }] });

const OTTER_RULES_ENGINE_DEVTOOLS_DEFAULT_OPTIONS = {
    isActivatedOnBootstrap: false
};
const OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS = new InjectionToken('Otter RulesEngine Devtools options');

class OtterRulesEngineDevtools {
    /**
     * Return true if the rules engine debug option is activated
     */
    get isRulesEngineDebugActivated() {
        return !!this.rulesEngineService.engine.debugMode;
    }
    constructor(store, rulesEngineService, options) {
        this.store = store;
        this.rulesEngineService = rulesEngineService;
        const eventsStackLimit = (options || OTTER_RULES_ENGINE_DEVTOOLS_DEFAULT_OPTIONS).rulesEngineStackLimit;
        this.rulesEngineEvents$ = this.rulesEngineService.engine.engineDebug?.debugEvents$.pipe(scan((previousEvents, currentEvent) => {
            const stack = eventsStackLimit && previousEvents.length === eventsStackLimit ? previousEvents.slice(1) : previousEvents;
            return [...stack, currentEvent];
        }, []), shareReplay(1));
        this.rulesEngineReport$ = this.rulesEngineEvents$ && combineLatest([this.rulesEngineEvents$, this.store.pipe(select(selectRulesetsEntities))]).pipe(map(([events, rulesetEntities]) => {
            const rulesetMap = Object.entries(rulesetEntities)
                .reduce((acc, [id, ruleset]) => {
                if (ruleset) {
                    acc[id] = ruleset;
                }
                return acc;
            }, {});
            return { events, rulesetMap };
        }), shareReplay(1));
    }
    /** Return the list of debug events emitted by rules engine */
    async getCurrentRulesEngineEventsStack() {
        return this.rulesEngineEvents$ && firstValueFrom(this.rulesEngineEvents$);
    }
    /** Returns the list of active rulesets (name and id) at the moment when the function is called */
    async getActiveRulesets() {
        const lastActiveRulesetsEvent = (this.rulesEngineEvents$ && await firstValueFrom(this.rulesEngineEvents$))?.filter((e) => e.type === 'ActiveRulesets').reverse()[0];
        return lastActiveRulesetsEvent?.rulesets;
    }
    /** Returns the list of available rulesets (name and id) at the moment when the function is called */
    async getAvailableRulesets() {
        const lastAvailableRulesetsEvent = (this.rulesEngineEvents$ && await firstValueFrom(this.rulesEngineEvents$))?.filter((e) => e.type === 'AvailableRulesets').reverse()[0];
        return lastAvailableRulesetsEvent?.availableRulesets;
    }
    /** Returns the list of output actions emitted by the rules engine at the moment when the function is called */
    async getAllOutputActions() {
        return (this.rulesEngineEvents$ && await firstValueFrom(this.rulesEngineEvents$))?.filter((e) => e.type === 'AllActions')?.reverse()[0];
    }
    /**
     * Get the list of executions for the given ruleset
     * @param rulesetId
     */
    async getRulesetExecutions(rulesetId) {
        return (this.rulesEngineEvents$ && await firstValueFrom(this.rulesEngineEvents$))?.filter((e) => (e.type === 'RulesetExecution' || e.type === 'RulesetExecutionError')
            && e.rulesetId === rulesetId);
    }
    /**
     * Check if the ruleset is activ in the moment when the function is called
     * @param rulesetId
     * @returns True if the ruleset is active; False if the ruleset is inactive or it does not exist
     */
    async isRulesetActive(rulesetId) {
        return !!(await this.getActiveRulesets())?.find((r) => r.id === rulesetId);
    }
    /**
     * Get the list of rules executed for the specified ruleset
     * @param rulesetId
     */
    async getRulesEvaluationsForRuleset(rulesetId) {
        const rulesetExec = await this.getRulesetExecutions(rulesetId);
        return rulesetExec?.map((e) => e?.rulesEvaluations?.filter((re) => !re.cached)).flat();
    }
    /**
     * Get the list of input facts (name, current value) for the specified ruleset, at the moment when the function is called
     * @param rulesetId
     */
    async getInputFactsForRuleset(rulesetId) {
        const rulesetExecutions = await this.getRulesetExecutions(rulesetId);
        return rulesetExecutions ? rulesetExecutions.at(-1).inputFacts : undefined;
    }
    /**
     * Get the list of triggers for the specified ruleset
     * @param rulesetId
     */
    async getTriggersForRuleset(rulesetId) {
        return (await this.getRulesEvaluationsForRuleset(rulesetId))?.map((e) => e.triggers).flat().flatMap((triggersMap) => Object.values(triggersMap));
    }
    /**
     * Get the list of outputed actions emitted by the given ruleset, at the moment when the function is called
     * @param rulesetId
     */
    async getOutputActionsForRuleset(rulesetId) {
        const rulesetExecutions = await this.getRulesetExecutions(rulesetId);
        return rulesetExecutions ? rulesetExecutions.at(-1).outputActions : undefined;
    }
    /** Get the list of fact names and corresponding values */
    getAllFactsSnapshot() {
        const registeredFacts = this.rulesEngineService?.engine.getRegisteredFactsNames();
        if (registeredFacts) {
            return this.rulesEngineService.engine.engineDebug?.getFactsSnapshot(registeredFacts);
        }
    }
    /**
     * Retrieve the ruleset information (rules, linkedComponents, validity range etc.) for a ruleset id
     * @param rulesetId
     */
    getRulesetInformation(rulesetId) {
        return firstValueFrom(this.store.pipe(select(selectRulesetsEntities), map((entities) => entities[rulesetId])));
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: OtterRulesEngineDevtools, deps: [{ token: i1$3.Store }, { token: RulesEngineRunnerService }, { token: OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
    /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: OtterRulesEngineDevtools, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: OtterRulesEngineDevtools, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: i1$3.Store }, { type: RulesEngineRunnerService }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS]
                }] }] });

/* eslint-disable no-console -- purpose of the service is to log in the console */
class RulesEngineDevtoolsConsoleService {
    /** Name of the Window property to access to the devtools */
    static { this.windowModuleName = 'rulesEngine'; }
    constructor(rulesEngineDevtools, options) {
        this.rulesEngineDevtools = rulesEngineDevtools;
        this.options = {
            ...OTTER_RULES_ENGINE_DEVTOOLS_DEFAULT_OPTIONS,
            ...options
        };
        if (this.options.isActivatedOnBootstrap) {
            this.activate();
        }
    }
    /** @inheritDoc */
    activate() {
        const windowWithDevtools = window;
        windowWithDevtools._OTTER_DEVTOOLS_ ||= {};
        windowWithDevtools._OTTER_DEVTOOLS_[RulesEngineDevtoolsConsoleService.windowModuleName] = this;
        console.info(`Otter rules engine Devtools is now accessible via the _OTTER_DEVTOOLS_.${RulesEngineDevtoolsConsoleService.windowModuleName} variable`);
    }
    /** Return the list of debug events emitted by rules engine */
    async getCurrentRulesEngineEventsStack() {
        console.log(await this.rulesEngineDevtools.getCurrentRulesEngineEventsStack());
    }
    /** Returns the list of active rulesets (name and id) at the moment when the function is called */
    async getActiveRulesets() {
        console.log(await this.rulesEngineDevtools.getActiveRulesets());
    }
    /** Returns the list of available rulesets (name and id) at the moment when the function is called */
    async getAvailableRulesets() {
        console.log(await this.rulesEngineDevtools.getAvailableRulesets());
    }
    /** Returns the list of output actions emitted by the rules engine at the moment when the function is called */
    async getAllOutputActions() {
        console.log(await this.rulesEngineDevtools.getAllOutputActions());
    }
    /**
     * Get the list of executions for the given ruleset
     * @param rulesetId
     */
    async getRulesetExecutions(rulesetId) {
        console.log(await this.rulesEngineDevtools.getRulesetExecutions(rulesetId));
    }
    /**
     * Check if the ruleset is activ in the moment when the function is called
     * @param rulesetId
     * @returns True if the ruleset is active; False if the ruleset is inactive or it does not exist
     */
    async isRulesetActive(rulesetId) {
        console.log(await this.rulesEngineDevtools.isRulesetActive(rulesetId));
    }
    /**
     * Get the list of rules executed for the specified ruleset
     * @param rulesetId
     */
    async getRulesEvaluationsForRuleset(rulesetId) {
        console.log(await this.rulesEngineDevtools.getRulesEvaluationsForRuleset(rulesetId));
    }
    /**
     * Get the list of input facts (name, current value) for the specified ruleset, at the moment when the function is called
     * @param rulesetId
     */
    async getInputFactsForRuleset(rulesetId) {
        console.log(await this.rulesEngineDevtools.getInputFactsForRuleset(rulesetId));
    }
    /**
     * Get the list of triggers for the specified ruleset
     * @param rulesetId
     */
    async getTriggersForRuleset(rulesetId) {
        console.log(await this.rulesEngineDevtools.getTriggersForRuleset(rulesetId));
    }
    /**
     * Get the list of outputed actions emitted by the given ruleset, at the moment when the function is called
     * @param rulesetId
     */
    async getOutputActionsForRuleset(rulesetId) {
        console.log(await this.rulesEngineDevtools.getOutputActionsForRuleset(rulesetId));
    }
    /** Get the list of fact names and corresponding values */
    async getAllFactsSnapshot() {
        console.log(await this.rulesEngineDevtools.getAllFactsSnapshot());
    }
    /**
     * Retrieve the ruleset information (rules, linkedComponents, validity range etc.) for a ruleset id
     * @param rulesetId
     */
    async getRulesetInformation(rulesetId) {
        console.log(await this.rulesEngineDevtools.getRulesetInformation(rulesetId));
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineDevtoolsConsoleService, deps: [{ token: OtterRulesEngineDevtools }, { token: OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
    /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineDevtoolsConsoleService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineDevtoolsConsoleService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: OtterRulesEngineDevtools }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS]
                }] }] });

class RulesEngineDevtoolsMessageService {
    constructor(rulesEngineDevtools, logger, options) {
        this.rulesEngineDevtools = rulesEngineDevtools;
        this.logger = logger;
        this.forceEmitRulesEngineReport = new BehaviorSubject(undefined);
        this.sendMessage = (sendOtterMessage);
        this.destroyRef = inject(DestroyRef);
        this.serializeError = (error) => error instanceof Error ? error.toString() : error;
        this.options = {
            ...OTTER_RULES_ENGINE_DEVTOOLS_DEFAULT_OPTIONS,
            ...options
        };
        if (this.options.isActivatedOnBootstrap) {
            this.activate();
        }
    }
    /**
     * Function to trigger a re-send a requested messages to the Otter Chrome DevTools extension
     * @param only restricted list of messages to re-send
     */
    handleReEmitRequest(only) {
        if (!only || only.includes('rulesEngineEvents')) {
            this.forceEmitRulesEngineReport.next();
        }
    }
    /**
     * Function to handle the incoming messages from Otter Chrome DevTools extension
     * @param message
     */
    handleEvents(message) {
        this.logger.debug('Message handling by the configuration service', message);
        switch (message.dataType) {
            case 'connect': {
                this.connectPlugin();
                break;
            }
            case 'requestMessages': {
                this.handleReEmitRequest(message.only);
                break;
            }
            default: {
                this.logger.warn('Message ignored by the configuration service', message);
            }
        }
    }
    /**
     * Serialize exceptions in a way that will display the error message after a JSON.stringify()
     * @param debugEvent
     */
    serializeReportEvent(debugEvent) {
        if (debugEvent.type !== 'RulesetExecutionError') {
            return debugEvent;
        }
        return {
            ...debugEvent,
            rulesEvaluations: debugEvent.rulesEvaluations.map((ruleEvaluation) => ({
                ...ruleEvaluation,
                error: this.serializeError(ruleEvaluation.error)
            })),
            // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- type is explicitly `any`
            errors: debugEvent.errors.map((error) => this.serializeError(error))
        };
    }
    /**
     * Function to start the rules engine reporting to the Otter Chrome DevTools extension
     */
    startRulesEngineReport() {
        if (this.rulesEngineDevtools.rulesEngineReport$) {
            combineLatest([
                this.forceEmitRulesEngineReport,
                this.rulesEngineDevtools.rulesEngineReport$
            ]).pipe(takeUntilDestroyed(this.destroyRef)).subscribe(([, report]) => {
                const sanitizedReport = { ...report, events: report.events.map((reportEvents) => this.serializeReportEvent(reportEvents)) };
                this.sendMessage('rulesEngineEvents', sanitizedReport);
            });
        }
    }
    /**
     * Function to connect the plugin to the Otter DevTools extension
     */
    connectPlugin() {
        this.logger.info('Otter DevTools is plugged to the application');
        this.forceEmitRulesEngineReport.next();
    }
    /** Activate the Otter DevTools */
    activate() {
        this.startRulesEngineReport();
        fromEvent(window, 'message').pipe(takeUntilDestroyed(this.destroyRef), filterMessageContent(isRulesEngineMessage)).subscribe((e) => this.handleEvents(e));
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineDevtoolsMessageService, deps: [{ token: OtterRulesEngineDevtools }, { token: i2.LoggerService }, { token: OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
    /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineDevtoolsMessageService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineDevtoolsMessageService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: OtterRulesEngineDevtools }, { type: i2.LoggerService }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS]
                }] }] });

class RulesEngineDevtoolsModule {
    /**
     * Initialize Otter Devtools
     * @param options
     */
    static instrument(options) {
        return {
            ngModule: RulesEngineDevtoolsModule,
            providers: [
                { provide: OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS, useValue: { ...OTTER_RULES_ENGINE_DEVTOOLS_DEFAULT_OPTIONS, ...options }, multi: false },
                RulesEngineDevtoolsMessageService,
                RulesEngineDevtoolsConsoleService
            ]
        };
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineDevtoolsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineDevtoolsModule, imports: [StoreModule,
            RulesetsStoreModule] }); }
    /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineDevtoolsModule, providers: [
            { provide: OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS, useValue: OTTER_RULES_ENGINE_DEVTOOLS_DEFAULT_OPTIONS },
            RulesEngineDevtoolsMessageService,
            RulesEngineDevtoolsConsoleService
        ], imports: [StoreModule,
            RulesetsStoreModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: RulesEngineDevtoolsModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        StoreModule,
                        RulesetsStoreModule
                    ],
                    providers: [
                        { provide: OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS, useValue: OTTER_RULES_ENGINE_DEVTOOLS_DEFAULT_OPTIONS },
                        RulesEngineDevtoolsMessageService,
                        RulesEngineDevtoolsConsoleService
                    ]
                }]
        }] });

/** Abstract fact set service */
class FactsService {
    constructor(rulesEngine) {
        this.rulesEngine = rulesEngine;
    }
    /** Register the set of facts */
    register() {
        this.rulesEngine.upsertFacts(Object.entries(this.facts)
            .map(([id, factValue]) => ({ id, value$: factValue })));
    }
}

class CurrentTimeFactsService extends FactsService {
    constructor(rulesEngine) {
        super(rulesEngine);
        this.currentTimeSubject$ = new BehaviorSubject(Date.now());
        /** @inheritDoc */
        this.facts = {
            o3rCurrentTime: this.currentTimeSubject$.asObservable()
        };
    }
    /** Compute the current time */
    tick() {
        this.currentTimeSubject$.next(Date.now());
    }
    /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: CurrentTimeFactsService, deps: [{ token: RulesEngineRunnerService }], target: i0.ɵɵFactoryTarget.Injectable }); }
    /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: CurrentTimeFactsService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: CurrentTimeFactsService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: RulesEngineRunnerService }] });

/**
 * Generated bundle index. Do not edit.
 */

export { CurrentTimeFactsService, DEFAULT_RULES_ENGINE_OPTIONS, EngineDebugger, FactsService, FactsSnapshotComponent, O3rFallbackToPipe, O3rJsonOrStringPipe, OTTER_RULES_ENGINE_DEVTOOLS_DEFAULT_OPTIONS, OTTER_RULES_ENGINE_DEVTOOLS_OPTIONS, OtterRulesEngineDevtools, RULESETS_REDUCER_TOKEN, RULESETS_STORE_NAME, RULES_ENGINE_OPTIONS, RulesEngine, RulesEngineDevtoolsConsoleService, RulesEngineDevtoolsMessageService, RulesEngineDevtoolsModule, RulesEngineRunnerModule, RulesEngineRunnerService, RulesetHistoryPresComponent, RulesetHistoryPresModule, RulesetsEffect, RulesetsStoreModule, allEqual, allGreater, allIn, allLower, allMatch, allNotIn, allRangeNumber, arrayBasedOperators, arrayContains, basicOperators, cancelRulesetsRequest, clearRulesetsEntities, dateAfter, dateBasedOperators, dateBefore, dateEquals, dateInNextMinutes, dateNotEquals, dateNotInNextMinutes, equals, executeOperator, failRulesetsEntities, getDefaultRulesetsReducer, getStatus, greaterThan, greaterThanOrEqual, inArray, inRangeDate, inString, isAllConditions, isAnyConditions, isConditionProperties, isDefined, isNotCondition, isOperandFact, isOperandLiteral, isOperandRuntimeFact, isRangeNumber, isRulesEngineMessage, isString, isSupportedSimpleTypes, isUndefined, isValidDate, isValidDateInput, isValidDateRange, lengthEquals, lengthGreaterThan, lengthGreaterThanOrEquals, lengthLessThan, lengthLessThanOrEquals, lengthNotEquals, lessOrEqual, lessThan, matchesPattern, notArrayContains, notEquals, notInArray, notInString, notStringContains, numberBasedOperators, numberValidator, oneEquals, oneGreater, oneIn, oneLower, oneMatches, oneRangeNumber, operatorList, parseRegExp, resetRulesets, rulesetReportToHistory, rulesetsAdapter, rulesetsInitialState, rulesetsReducer, rulesetsReducerFeatures, rulesetsStorageDeserializer, rulesetsStorageSerializer, rulesetsStorageSync, selectActiveRuleSets, selectAllRulesets, selectComponentsLinkedToRuleset, selectRuleSetsInRange, selectRulesetsEntities, selectRulesetsIds, selectRulesetsState, selectRulesetsStorePendingStatus, selectRulesetsTotal, setRulesets, setRulesetsEntities, setRulesetsEntitiesFromApi, stringContains, updateRulesets, upsertRulesetsEntities, upsertRulesetsEntitiesFromApi };
//# sourceMappingURL=o3r-rules-engine.mjs.map