[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/build-and-publish.yml",
    "content": "name: Build-and-Publish\n\non:\n  workflow_dispatch:\n\njobs:\n  build-and-publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - name: Install Dependencies\n        run: npm install\n      - name: Build\n        run: npm run build\n      - name: Test\n        run: npm run test\n      - name: Copy Files\n        run: npm run copyfiles\n      - name: Change Directory to lib\n        run: cd lib\n      - name: Setup Node\n        uses: actions/setup-node@v2\n        with:\n          node-version: '12.x'\n          registry-url: 'https://registry.npmjs.org'\n      - name: Publish\n        run: cd lib && npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [ main, Min_Reviewer_Check ]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ main ]\n  schedule:\n    - cron: '30 18 * * 5'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'javascript' ]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]\n        # Learn more:\n        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v2\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v1\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n        # queries: ./path/to/local/query, your-org/your-repo/queries@main\n\n    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n    # If this step fails, then you should remove it and run the build manually (see below)\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v1\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 https://git.io/JvXDl\n\n    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n    #    and modify them (or add more) to build your code if your project\n    #    uses a compiled language\n\n    #- run: |\n    #   make bootstrap\n    #   make release\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v1\n"
  },
  {
    "path": ".github/workflows/gated-build.yml",
    "content": "# This is a gated build to verify the validity of the code\n\nname: Gated-Build\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - name: Install Dependencies\n        run: npm install\n      - name: Build\n        run: npm run build\n      - name: Test\n        run: npm run test\n        \n          \n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n\n\n# Build bits\n/lib/**/**.*\n\n# Code Coverage\n/coverage/**/**.*\n\n# Ignoring dist folders\n/sample/counterApp/dist/**.*\n/sample/todoApp/dist/**/**.*\n/sample/shell/dist/**/**.*"
  },
  {
    "path": ".npmrc",
    "content": "registry:https://registry.npmjs.org/ \nalways-auth=true \npackage-lock=false"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\n\nResources:\n\n- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)\n- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)\n- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nThis project welcomes contributions and suggestions. Most contributions require you to\nagree to a Contributor License Agreement (CLA) declaring that you have the right to,\nand actually do, grant us the rights to use your contribution. For details, visit\nhttps://cla.microsoft.com.\n\nWhen you submit a pull request, a CLA-bot will automatically determine whether you need\nto provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the\ninstructions provided by the bot. You will only need to do this once across all repositories using our CLA.\n\nIf you are adding new features please add unit test cases. For the modification of any existing feature\nensure all existing test cases are running.\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)\nor contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments."
  },
  {
    "path": "LICENSE",
    "content": "    MIT License\n\n    Copyright (c) Microsoft Corporation.\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in all\n    copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n    SOFTWARE\n"
  },
  {
    "path": "README.ko.md",
    "content": "# Redux Micro-Frontend\n\n## 더 이상 사용되지 않는 버전에 대한 경고\n만약 당신이 1.1.0 버전을 사용하고 있을 경우, 바로 최신 버전으로 업그레이드를 하십시오. 해당 버전은 파이프라인 이슈로 인해 더 이상 사용되지 않습니다.\n\n## 파이프라인 상태\n[![Build Status](https://dev.azure.com/MicrosoftIT/OneITVSO/_apis/build/status/Compliant/Core%20Services%20Engineering%20and%20Operations/Corporate%20Functions%20Engineering/Professional%20Services/Foundational%20PS%20Services/Field%20Experience%20Platform/PS-FPSS-FExP-GitHub-Redux-Micro-Frontend?branchName=azure-pipelines)](https://dev.azure.com/MicrosoftIT/OneITVSO/_build/latest?definitionId=32881&branchName=azure-pipelines)\n\n[![CodeQL](https://github.com/microsoft/redux-micro-frontend/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/microsoft/redux-micro-frontend/actions/workflows/codeql-analysis.yml)\n\n[![Build-and-Publish](https://github.com/microsoft/redux-micro-frontend/actions/workflows/build-and-publish.yml/badge.svg?branch=main)](https://github.com/microsoft/redux-micro-frontend/actions/workflows/build-and-publish.yml)\n\n![npm](https://img.shields.io/npm/dt/redux-micro-frontend)\n\n## 개요\n\n이 라이브러리는 Redux를 마이크로 프론트엔드 기반의 아키텍쳐에서 사용하기 위해 만들어졌습니다. 마이크로 프론트엔드는 모노리스 프론트엔드 애플리케이션을 벗어나 다루고 쉽고, 분리된, 또 작은 단위의 어플리케이션을 구현하기 위한 아키텍쳐 패턴입니다. 각 애플리케이션은 독립적이고 자립적인 하나의 유닛이 됩니다. 일반적으로 껍데기(shell) 애플리케이션은 엔드 유저들에게 비슷한 경험을 제공하기 위해 이러한 작은 유닛들에 대한 호스트로 사용되도록 구현됩니다.\n\n`Redux`는 예측가능한 상태 관리를 위해 만들어진 유명한 라이브러리들 중 하나입니다. 하지만, 일반적으로 리덕스를 사용할 때 하나의 store를 두고 하나의 상태 객체를 가지게 됩니다. 이 접근은 모든 마이크로 프론트엔드가 하나의 상태를 공유할 수 있다는 것을 의미합니다. 하지만 마이크로 프론트엔드 기반의 아키텍쳐에서 각 애플리케이션은 해당 스토어에 대해 독립적으로 운영되기 때문에 이러한 요소는 마이크로 프론트엔드 아키텍쳐에서 문제 요소로 작용합니다.\n\n몇몇의 개발자는 마이크로 프론트엔드의 각 애플리케이션의 분리 수준을 제공하기 위해  `combineReducer()`를 사용하기도 합니다. 마이크로 프론트엔드를 구현하기 위해 분리된 리듀서를 작성하고, 이들을 하나의 거대한 리듀서로 통합하기 위해서 말이지요. 이러한 것들이 어느 정도의 문제를 해결할 수는 있지만, 이는 단 하나의 상태 객체가 모든 각각의 앱들에 대해 공유된다는 것을 의미합니다. 이는 충분한 조치가 없을 경우, 각각의 앱들이 뜻하지 않게 다른 상태 객체들을 재정의(override)할 수도 있습니다.\n\n마이크로 프론트엔드 아키텍쳐에서, 각각의 애플리케이션은 다른 앱들의 상태에 접근하여 상태값을 수정해서는 안됩니다. 그럼에도 각각의 앱들은 다른 앱들의 상태를 알아야 할 경우가 있습니다. 애플리케이션 간의 커뮤니케이션을 가능케하는 선에서 그들은 이벤트 또는 액션을 다른 스토어에 전달할 수 있습니다. 나아가, 다른 앱들 사이의 상태 변화를 감지할 수도 있습니다. 이 라이브러리는 각 애플리케이션의 분리와 애플리케이션 간의 커뮤니케이션 두 개의 마이크로 프론트엔드 아키텍쳐 요구사항을 만족시킬 수 있는 라이브러리입니다.\n\n\n## 개념\n`전역 스토어`의 개념은 다양한 `리덕스 스토어`들을 가상으로 병합하기 위해 도입되었습니다. 엄밀히 말하면, `전역 스토어`는 스토어는 아닙니다. 오히려 이는 다양한 독립적인 `리덕스 스토어`들의 집합입니다. 각각의 물리적인 `리덕스 스토어`는 각각의 앱이 사용하는 독립된 스토어를 참조합니다. `전역 스토어`에 접근하는 마이크로 프론트엔드 앱들은 `getState()`, `dispatch()` 그리고 `subscribe()`와 같은 각각의 `리덕스 스토어`에 대한 모든 작업들을 수행할 수 있습니다.\n\n각각의 마이크로 프론트엔드 앱들은 그들만의 `리덕스 스토어`를 가질 수 있습니다. 각각의 앱들은 그들의 `리덕스 스토어`를 만들고 `전역 스토어`에 등록할 수 있습니다. 그 `전역 스토어`는  이러한 개별 리덕스 스토어를 사용하여 다른 모든 스토어의 상태를 조합한 전역 상태(Global State)를 투영합니다. 모든 마이크로 프론트엔드 앱들은 이 `전역 스토어`에 접근할 수 있고, 다른 마이크로 프론트엔드의 상태를 볼 수 있습니다. 하지만 그들을 수정할 수는 없죠. 앱에서 디스패치된 작업은 앱에서 등록된 스토어 내에서 제한되어 있어 다른 스토어에 디스패치되지 않으므로 컴포넌트화 또는 분리가 가능합니다.\n\n### 더 읽어볼 것들\n[상태 관리의 기본](https://www.devcompost.com/post/state-management-for-front-end-applications-part-i-what-and-why)\n\n\n### 전역 액션\n전역 액션은 특정 앱이 다른 마이크로 프론트엔드 앱에 의해 등록된 스토어에 액션을 디스패치할 수 있는 개념입니다. 각 마이크로 프론트엔드 앱에는 스토어와 함께 전역 액션 집합을 등록할 수 있는 기능이 있습니다. 이러한 전역 액션 집합은 다른 마이크로 프론트엔드 앱에 의해 해당 마이크로 프론트엔드의 스토어에서 디스패치될 수 있습니다. 따라서 애플리케이션 간의 통신이 가능하게 됩니다.\n![Global Store](https://github.com/microsoft/redux-micro-frontend/blob/main/assets/Global_Store_Dispatch.png)\n\n### 상태 간의 상호 콜백\n애플리케이션 간의 커뮤니케이션은 다른 마이크로 프론트엔드의 상태에 대한 변화를 구독하는 것으로부터 이루어집니다. 각각의 마이크로 프론트엔드 앱들은 다른 상태들에 대해 읽기 권한만(read-only) 가지고 있기 때문에, 그들은 상태 변화를 읽기 위해 콜백을 붙일 수 있습니다. 콜백들은 각각 스토어 레벨 또는 전역 레벨로 붙여질 수 있습니다. (전역 레벨 콜백은 어떤 스토어이던 상태 변화가 발생할 경우 콜백을 일으킨다는 것을 의미합니다.)\n\n\n## 단일 상태 공유의 문제점\n- 실수로 다른 앱의 상태를 재정의할 수 있습니다. (중복된 액션들이 여러 앱에 의해 디스패치되는 경우)\n- 앱들은 다른 마이크로 프론트엔드들을 알고 있어야 합니다.\n- 공유된 미들웨어들. 하나의 스토어만 유지되기 때문에, 모든 마이크로 프론트엔드들이 동일한 미들웨어를 공유해야 합니다. 따라서 어떤 앱은 `redux-saga`를 쓰고, 어떤 앱은 `redux-thunk`를 하는 등의 작업은 할 수 없습니다.\n\n## 설치\n```sh\nnpm install redux-micro-frontend --save\n```\n\n## 빠른 적용\n### 전역 스토어의 인스턴스 얻기\n```javascript\nimport { GlobalStore } from 'redux-micro-frontend';\n...\nthis.globalStore = GlobalStore.Get();\n```\n\n### 스토어를 생성하고 등록하기\n```javascript\nlet appStore = createStore(AppReducer); // 리덕스 스토어\nthis.globalStore.RegisterStore(\"App1\", appStore);\nthis.globalStore.RegisterGlobalActions(\"App1\", [\"Action-1\", \"Action-2\"]); // 이 액션들은 다른 앱들에 의해 이 스토어에 디스패치될 수 있습니다.\n```\n\n### 액션 디스패치하기\n```javascript\nlet action = {\n    type: 'Action-1',\n    payload: 'Some data'\n}\nthis.globalStore.DispatchAction(\"App1\", action); // 이는 현재 앱의 스토어뿐만 아니라, 'Action-1'을 전역 액션으로 등록한 다른 스토어로도 액션이 전송됩니다.\n```\n\n### 상태 구독하기\n```javascript\n// 모든 앱들의 상태 변화\nthis.globalStore.Subscribe(\"App1\", localStateChanged);\n\n// 현재 앱의 상태 변화\nthis.globalStore.SubscribeToGlobalState(\"App1\", globalStateChanged);\n\n// App2의 상태 변화\nthis.globalStore.SubscribeToPartnerState(\"App1\", \"App2\", app2StateChanged);\n\n...\n\nlocalStateChanged(localState) {\n    // 새로운 상태에 대한 작업을 수행하세요.\n}\n\nglobalStateChanged(stateChanged) {\n        // 전역 상태는 스토어에 등록된 모든 앱들을 위한 분리된 어트리뷰트를 가질 수 있습니다.\n        let app1State = globalState.App1;\n        let app2State = globalState.App2; \n}\n\napp2StateChanged(app2State) {\n    // app2의 새로운 상태에 대한 작업을 수행하세요.\n}\n```\n\n## 샘플 앱\n위치: https://github.com/microsoft/redux-micro-frontend/tree/main/sample\n\n샘플 앱을 실행하기 위한 안내서\n1. sample/counterApp으로 가서 `npm i`을 실행하세요. 그리고 `npm run start`을 하세요.\n2. sample/todoApp으로 가서 `npm i`을 실행하세요. 그리고 `npm run start`을 하세요.\n3. sample/shell으로 가서 `npm i`을 실행하세요. 그리고 `npm run start`을 하세요.\n4. http://localhost:6001을 여세요.\n\n## 문서\n[Github 위키](https://github.com/microsoft/redux-micro-frontend/wiki)\n\n## 부록\n- Redux의 기본기를 배우기 위해서는 Redux 공식 문서를 확인하세요. - https://redux.js.org/.\n- 마이크로 프론트엔드 아키텍쳐에 대해 더 알고 싶다면, [martinfowler.com](http://martinfowler.com/)에서 만든 [이 아티클](https://martinfowler.com/articles/micro-frontends.html)을 확인하세요.\n\n\n## Trademarks\n\n이 프로젝트에는 프로젝트, 제품 또는 서비스의 상표 또는 로고가 포함될 수 있습니다. Microsoft 상표 또는 로고의 승인된 사용은 Microsoft의 상표 & 브랜드 지침의 적용을 받으며 반드시 따라야 합니다. 이 프로젝트의 수정된 버전에서 Microsoft 상표 또는 로고를 사용하면 Microsoft의 후원이 혼동되거나 암시되어서는 안 됩니다. 타사 상표 또는 로고는 해당 타사 정책의 적용을 받습니다.\n"
  },
  {
    "path": "README.md",
    "content": "# Redux Micro-Frontend\n\n[한국어🇰🇷](./README.ko.md)\n\n## Version Deprecation Warning\n1.1.0 - If you are using this version, please upgrade to latest immediately. This version has been deprecated due to a pipeline issue.\n\n## Pipeline Status\n[![Build Status](https://dev.azure.com/MicrosoftIT/OneITVSO/_apis/build/status/Compliant/Core%20Services%20Engineering%20and%20Operations/Corporate%20Functions%20Engineering/Professional%20Services/Foundational%20PS%20Services/Field%20Experience%20Platform/PS-FPSS-FExP-GitHub-Redux-Micro-Frontend?branchName=azure-pipelines)](https://dev.azure.com/MicrosoftIT/OneITVSO/_build/latest?definitionId=32881&branchName=azure-pipelines)\n\n[![CodeQL](https://github.com/microsoft/redux-micro-frontend/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/microsoft/redux-micro-frontend/actions/workflows/codeql-analysis.yml)\n\n[![Build-and-Publish](https://github.com/microsoft/redux-micro-frontend/actions/workflows/build-and-publish.yml/badge.svg?branch=main)](https://github.com/microsoft/redux-micro-frontend/actions/workflows/build-and-publish.yml)\n\n![npm](https://img.shields.io/npm/dt/redux-micro-frontend)\n\n## Overview\n\nThis library can be used for using Redux in a Micro Frontend based architecture. Micro Frontends is an architectural pattern for breaking up a monolith Frontend application into manageable, decoupled and smaller applications. Each application is a self-contained and isolated unit. Generally, a common shell/platform application is used to host these small units to provide a common experience for the end-users.\n\n`Redux` is one of the most popular libraries for predictable state management. However, the general practice in using Redux is to have a single store, thereby having a single state object. This approach would mean that all the Micro Frontends would have a shared state. This is a violation of the Micro Frontend based architecture since each App is supposed to be a self-contained unit having its store.\n\nTo provide a level of isolation some developers use `combineReducer()` to write a separate reducer for each Micro Frontend and then combine them into one big reducer. Although it would solve some problems this would still imply that a single state object is shared across all the apps. In the absence of sufficient precautions, apps might accidentally override each other's state.\n\nIn a Micro Frontend architecture, an individual application should not be able to modify the state of other apps. However, they should be able to see the state of other apps. Along the same line for enabling cross-application communication, they should also be able to send events/actions to other Stores and also get notified of changes in other apps' state. This library aims to attain that sweet spot between providing isolation and cross-application communication.\n\n\n## Concept\nA concept of `Global Store` is introduced which is a virtual amalgamation of multiple `Redux Stores`. Strictly speaking, the `Global Store` is not an actual store, rather it's a collection of multiple isolated `Redux Stores`. Each physical `Redux Store` here refers to the isolated store that each app uses. Micro frontends having access to the `Global Store` would be able to perform all operations that are allowed on an individual `Redux Store` including `getState()`, `dispatch()` and `subscribe()`.\n\nEach Micro Frontend would have the capability to have its own `Redux Store`. Each app would create and register their `Redux Store` with the `Global Store`. The `Global Store` then uses these individual stores to project a Global State which is a combination of the state from all the other Stores. All the Micro Frontends would have access to the Global Store and would be able to see the state from the other Micro Frontends but won't be able to modify them. Actions dispatched by an app remains confined within the store registered by the app and is not dispatched to the other stores, thereby providing componentization and isolation.\n### Read more\n- [State Management Basics](https://www.devcompost.com/post/state-management-for-front-end-applications-part-i-what-and-why)\n\n- [State Management in Micro Frontends](https://www.devcompost.com/post/state-management-ii-world-of-micro-frontends) \n\n\n### Global Actions\nA concept of `Global Action` is available which allows other apps to dispatch actions to stores registered by other Micro Frontends. Each Micro Frontend has the capability to register a set of global actions along with the store. These set of global actions can be dispatched in this Micro Frontend's store by other Micro Frontends. This enables cross-application communication.\n![Global Store](https://github.com/microsoft/redux-micro-frontend/blob/main/assets/Global_Store_Dispatch.png)\n\n### Cross-state callbacks\nCross-application communication can also be achieved by subscribing to change notifications in other Micro Frontend's state. Since each micro-frontend has read-only permission to other states, they can also attach callbacks for listening to state changes. The callbacks can be attached either at an individual store level or at a global level (this would mean that state change in any store would invoke the callback).\n\n\n## Problems of a single shared state\n- Accidental override of state of other apps (in case duplicate actions are dispatched by multiple apps)\n- Apps would have to be aware of other Micro Frontends\n- Shared middlewares. Since only a single store is maintained, all the Micro Frontends would have to share the same middlewares. So in situations where one app wants to use `redux-saga` and other wants to use `redux-thunk` is not possible.\n\n## Installation\n```sh\nnpm install redux-micro-frontend --save\n```\n\n## Quick Guide\n### Get an instance of Global Store\n```javascript\nimport { GlobalStore } from 'redux-micro-frontend';\n...\nthis.globalStore = GlobalStore.Get();\n```\n\n### Create/Register Store\n```javascript\nlet appStore = createStore(AppReducer); // Redux Store\nthis.globalStore.RegisterStore(\"App1\", appStore);\nthis.globalStore.RegisterGlobalActions(\"App1\", [\"Action-1\", \"Action-2\"]); // These actions can be dispatched by other apps to this store\n```\n\n### Dispatch Action\n```javascript\nlet action = {\n    type: 'Action-1',\n    payload: 'Some data'\n}\nthis.globalStore.DispatchAction(\"App1\", action); // This will dispatch the action to current app's store as well other stores who might have registered 'Action-1' as a global action\n```\n\n### Subscribe to State\n```javascript\n// State change in any of the apps\nthis.globalStore.Subscribe(\"App1\", localStateChanged);\n\n// State change in the current app\nthis.globalStore.SubscribeToGlobalState(\"App1\", globalStateChanged);\n\n// State change in app2's state\nthis.globalStore.SubscribeToPartnerState(\"App1\", \"App2\", app2StateChanged);\n\n...\n\nlocalStateChanged(localState) {\n    // Do something with the new state\n}\n\nglobalStateChanged(stateChanged) {\n        // The global state has a separate attribute for all the apps registered in the store\n        let app1State = globalState.App1;\n        let app2State = globalState.App2; \n}\n\napp2StateChanged(app2State) {\n    // Do something with the new state of app 2\n}\n\n```\n\n## Sample App\nLocation: https://github.com/microsoft/redux-micro-frontend/tree/main/sample\n\nInstruction for running Sample App\n1. Go to sample/counterApp and run `npm i` and then `npm run start`\n2. Go to sample/todoApp and run `npm i` and then `npm run start`\n3. Go to sample/shell and run `npm i` and then `npm run start`\n4. Browse http://localhost:6001\n\n## Documentation\n[See Github wiki](https://github.com/microsoft/redux-micro-frontend/wiki)\n\n## Appendix\n- To learn the basics for Redux check for [official documentation of Redux](https://redux.js.org/) - https://redux.js.org/.\n- To know more about [Micro Front-end](https://martinfowler.com/articles/micro-frontends.html) style of architecture check [this article](https://martinfowler.com/articles/micro-frontends.html) from [martinfowler.com](https://martinfowler.com/articles/micro-frontends.html).\n\n\n## Trademarks\n\nThis project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft \ntrademarks or logos is subject to and must follow \n[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\nAny use of third-party trademarks or logos are subject to those third-party's policies.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->\n\n## Security\n\nMicrosoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).\n\nIf you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.\n\n## Reporting Security Issues\n\n**Please do not report security vulnerabilities through public GitHub issues.**\n\nInstead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).\n\nIf you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).\n\nYou should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). \n\nPlease include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:\n\n  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)\n  * Full paths of source file(s) related to the manifestation of the issue\n  * The location of the affected source code (tag/branch/commit or direct URL)\n  * Any special configuration required to reproduce the issue\n  * Step-by-step instructions to reproduce the issue\n  * Proof-of-concept or exploit code (if possible)\n  * Impact of the issue, including how an attacker might exploit the issue\n\nThis information will help us triage your report more quickly.\n\nIf you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.\n\n## Preferred Languages\n\nWe prefer all communications to be in English.\n\n## Policy\n\nMicrosoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).\n\n<!-- END MICROSOFT SECURITY.MD BLOCK -->"
  },
  {
    "path": "SUPPORT.md",
    "content": "# Support\r\n\r\n## How to file issues and get help  \r\n\r\nThis project uses GitHub Issues to track bugs and feature requests. Please search the existing \r\nissues before filing new issues to avoid duplicates.  For new issues, file your bug or \r\nfeature request as a new Issue.\r\n\r\nFor help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE \r\nFOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER\r\nCHANNEL. WHERE WILL YOU HELP PEOPLE?**.\r\n\r\n## Microsoft Support Policy  \r\n\r\nSupport for this **PROJECT or PRODUCT** is limited to the resources listed above.\r\n"
  },
  {
    "path": "azure-gated-build.yml",
    "content": "trigger: none\n\njobs:\n  - job: BuildLibrary\n    displayName: Build Library\n    pool:\n      vmImage: ubuntu-latest    \n    steps:\n    - task: NodeTool@0\n      inputs:\n        versionSpec: '12.x'\n        checkLatest: true\n    - task: Npm@1\n      displayName: Install Dependencies\n      inputs:\n        command: 'install'\n    - task: Npm@1\n      displayName: Build Library\n      inputs:\n        command: 'custom'\n        customCommand: 'run build'\n    - task: Npm@1\n      displayName: Test\n      inputs:\n        command: 'custom'\n        customCommand: 'run test'\n  - job: BuildSample\n    displayName: Build Sample Projects\n    pool:\n      vmImage: ubuntu-latest\n    steps:\n    - task: NodeTool@0\n      inputs:\n        versionSpec: '12.x'\n        checkLatest: true\n    - task: Npm@1\n      displayName: Install Counter App Dependencies\n      inputs:\n        command: 'install'\n        workingDir: 'sample/counterApp'\n    - task: Npm@1\n      displayName: Build Counter App\n      inputs:\n        command: 'custom'\n        customCommand: 'run build'\n        workingDir: 'sample/counterApp'\n    - task: Npm@1\n      displayName: Install Todo App Dependencies\n      inputs:\n        command: 'install'\n        workingDir: 'sample/todoApp'\n    - task: Npm@1\n      displayName: Build Todo App\n      inputs:\n        command: 'custom'\n        customCommand: 'run build'\n        workingDir: 'sample/todoApp'\n    - task: Npm@1\n      displayName: Install Shell Dependencies\n      inputs:\n        command: 'install'\n        workingDir: 'sample/shell'\n    - task: Npm@1\n      displayName: Build Shell\n      inputs:\n        command: 'custom'\n        customCommand: 'run build'\n        workingDir: 'sample/shell'\n\n\n\n"
  },
  {
    "path": "index.ts",
    "content": "export * from './src/actions';\nexport * from './src/global.store';\nexport * from './src/common/interfaces';\nexport * from './src/common/abstract.logger';\nexport * from './src/common/interfaces/global.store.interface';"
  },
  {
    "path": "karma.conf.headless.js",
    "content": "module.exports = function (config) {\n    config.set({\n        frameworks: [\"jasmine\", \"karma-typescript\"],\n        files: [\n            { pattern: \"src/**/*.ts\" },\n            { pattern: \"test/**/*.ts\" }\n        ],\n        preprocessors: {\n            \"src/**/*.ts\": [\"karma-typescript\"],\n            \"test/**/*.ts\": \"karma-typescript\"\n        },\n        reporters: [\"progress\"],\n        customLaunchers: {\n            ChromeHeadlessCustom: {\n                base: 'ChromeHeadless',\n                flags: ['--no-sandbox', '--disable-gpu']\n            }\n        },\n        browsers: [\"ChromeHeadlessCustom\"],\n        karmaTypescriptConfig: {\n            coverageReporter: {\n                instrumenterOptions: {\n                    istanbul: { noCompact: true }\n                }\n            },\n            bundlerOptions: {\n                transforms: [\n                    require(\"karma-typescript-es6-transform\")({\n                        presets: [\n                            [\"env\", {\n                                targets: {\n                                    chrome: \"60\"\n                                }\n                            }]\n                        ]\n                    })\n                ]\n            },\n            compilerOptions: {\n                module: \"commonjs\",\n                sourceMap: true,\n                target: \"es6\",\n                allowJs: false,\n                declaration: true,\n                moduleResolution: \"node\",\n                skipLibCheck: true,\n                lib: [\"es2017\", \"DOM\"],\n                downlevelIteration: true\n            },\n            typeRoots: [\n                \"node_modules/@types\"\n            ],\n            exclude: [\n                \"node_modules/**/*\"\n            ]\n        },\n        singleRun: true,\n        autoWatch: false,\n        plugins: ['karma-jasmine', 'karma-chrome-launcher', 'karma-typescript']\n    });\n};"
  },
  {
    "path": "karma.conf.js",
    "content": "module.exports = function (config) {\n    config.set({\n        frameworks: [\"jasmine\", \"karma-typescript\"],\n        files: [\n            { pattern: \"src/**/*.ts\" },\n            { pattern: \"test/**/*.ts\" }\n        ],\n        preprocessors: {\n            \"src/**/*.ts\": [\"karma-typescript\"],\n            \"test/**/*.ts\": \"karma-typescript\"\n        },\n        reporters: [\"progress\", \"karma-typescript\"],\n        browsers: [\"Chrome\"],\n        karmaTypescriptConfig: {\n            coverageReporter: {\n                instrumenterOptions: {\n                    istanbul: { noCompact: true }\n                }\n            },\n            bundlerOptions: {\n                transforms: [\n                    require(\"karma-typescript-es6-transform\")({\n                        presets: [\n                            [\"env\", {\n                                targets: {\n                                    chrome: \"60\"\n                                }\n                            }]\n                        ]\n                    })\n                ]\n            },\n            compilerOptions: {\n                module: \"commonjs\",\n                sourceMap: true,\n                target: \"es6\",\n                allowJs: false,\n                declaration: true,\n                moduleResolution: \"node\",\n                skipLibCheck: true,\n                lib: [\"es2017\", \"DOM\"],\n                downlevelIteration: true\n            },\n            typeRoots: [\n                \"node_modules/@types\"\n            ],\n            exclude: [\n                \"node_modules/**/*\"\n            ]\n        },\n        plugins: ['karma-jasmine', 'karma-chrome-launcher', 'karma-typescript']\n    });\n};"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"redux-micro-frontend\",\n    \"version\": \"1.3.0\",\n    \"license\": \"MIT\",\n    \"description\": \"This is a library for using Redux to managing state for self-contained apps in a Micro-Frontend architecture. Each self-contained isolated app can have its own isolated and decoupled Redux store. The componentized stores interact with a global store for enabling cross-application communication.\",\n    \"author\": {\n        \"name\": \"Pratik Bhattacharya\",\n        \"email\": \"pratikb@microsoft.com\",\n        \"url\": \"https://www.devcompost.com/\"\n    },\n    \"homepage\": \"https://github.com/microsoft/redux-micro-frontend\",\n    \"keywords\": [\n        \"redux\",\n        \"micro frontend\",\n        \"microfrontend\",\n        \"microfrontends\",\n        \"micro frontends\",\n        \"state\",\n        \"statemanagement\"\n    ],\n    \"bugs\": {\n        \"url\": \"https://github.com/microsoft/redux-micro-frontend/issues\",\n        \"email\": \"pratikb@microsoft.com\"\n    },\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"https://github.com/microsoft/redux-micro-frontend\"\n    },\n    \"scripts\": {\n        \"build\": \"tsc\",\n        \"test-chrome\": \"karma start karma.conf.js\",\n        \"test\": \"karma start karma.conf.headless.js\",\n        \"release:pre\": \"npm run build && npm version prerelease && npm run copyfiles:publish-beta\",\n        \"release:patch\": \"npm run build && npm version patch && npm run copyfiles:publish\",\n        \"release:minor\": \"npm run build && npm version minor && npm run copyfiles:publish\",\n        \"release:major\": \"npm run build && npm version major && npm run copyfiles:publish\",\n        \"copyfiles\": \"npm run copy:packagejson && npm run copy:npmrc\",\n        \"copyfiles:publish\": \"npm run copy:packagejson && npm run copy:npmrc && cd lib && npm publish\",\n        \"copyfiles:publish-beta\": \"npm run copy:packagejson && npm run copy:npmrc && cd lib && npm publish --tag beta\",\n        \"copy:packagejson\": \"cpr package.json lib/package.json -o\",\n        \"copy:npmrc\": \"cpr .npmrc lib/.npmrc -o\",\n        \"clean\": \"rimraf node_modules\",\n        \"rebuild\": \"npm run clean && npm i && npm build\"\n    },\n    \"private\": false,\n    \"dependencies\": {\n        \"flatted\": \"^2.0.2\",\n        \"redux\": \"^4.0.5\",\n        \"redux-devtools-extension\": \"^2.13.8\"\n    },\n    \"devDependencies\": {\n        \"@types/jasmine\": \"^3.5.14\",\n        \"cpr\": \"^3.0.1\",\n        \"jasmine\": \"^3.6.2\",\n        \"karma\": \"^6.3.4\",\n        \"karma-chrome-launcher\": \"^3.1.0\",\n        \"karma-coverage\": \"^2.0.3\",\n        \"karma-jasmine\": \"^3.3.1\",\n        \"karma-typescript\": \"^5.5.1\",\n        \"karma-typescript-es6-transform\": \"^4.1.1\",\n        \"puppeteer\": \"^5.5.0\",\n        \"rimraf\": \"^3.0.2\",\n        \"typescript\": \"^3.5.3\"\n    }\n}\n"
  },
  {
    "path": "sample/counterApp/.babelrc",
    "content": "{\n    \"presets\": [\n        \"@babel/preset-react\"\n    ]\n}"
  },
  {
    "path": "sample/counterApp/index.html",
    "content": "<html>\n    <head></head>\n\n    <body>\n        <div id=\"app\"></div>\n    </body>\n</html>"
  },
  {
    "path": "sample/counterApp/package.json",
    "content": "{\n    \"name\": \"redux-micro-frontend-sample-counter\",\n    \"version\": \"0.0.0\",\n    \"license\": \"MIT\",\n    \"description\": \"This is a sample app with counter\",\n    \"author\": {\n        \"name\": \"Pratik Bhattacharya\",\n        \"email\": \"pratikb@microsoft.com\",\n        \"url\": \"https://www.devcompost.com/\"\n    },\n    \"homepage\": \"https://github.com/microsoft/redux-micro-frontend\",\n    \"keywords\": [\n        \"redux\",\n        \"micro frontend\",\n        \"microfrontend\",\n        \"microfrontends\",\n        \"micro frontends\",\n        \"state\",\n        \"statemanagement\"\n    ],\n    \"bugs\": {\n        \"url\": \"https://github.com/microsoft/redux-micro-frontend/issues\",\n        \"email\": \"pratikb@microsoft.com\"\n    },\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"https://github.com/microsoft/redux-micro-frontend\"\n    },\n    \"scripts\": {\n        \"build\": \"webpack\",\n        \"start\": \"webpack serve\",\n        \"start-component\": \"webpack serve --config ./webpack.config.mf.js\"\n    },\n    \"private\": false,\n    \"devDependencies\": {\n        \"@babel/cli\": \"^7.12.1\",\n        \"@babel/core\": \"^7.12.3\",\n        \"@babel/preset-env\": \"^7.12.1\",\n        \"@babel/preset-react\": \"^7.12.1\",\n        \"babel-eslint\": \"^10.1.0\",\n        \"babel-loader\": \"^8.1.0\",\n        \"clean-webpack-plugin\": \"^3.0.0\",\n        \"css-loader\": \"^5.0.0\",\n        \"html-webpack-plugin\": \"^4.5.0\",\n        \"path-parse\": \"^1.0.7\",\n        \"react\": \"^16.14.0\",\n        \"react-dom\": \"^16.14.0\",\n        \"redux-micro-frontend\": \"^1.1.1\",\n        \"style-loader\": \"^2.0.0\",\n        \"webpack\": \"^5.1.3\",\n        \"webpack-cli\": \"^4.1.0\",\n        \"webpack-dev-server\": \"^3.11.2\"\n    }\n}\n"
  },
  {
    "path": "sample/counterApp/src/appCounter.js",
    "content": "import React from 'react';\nimport { Counter } from './counter';\nimport { GlobalStore } from 'redux-micro-frontend';\nimport { CounterReducer } from './store/counterReducer';\nimport { IncrementLocalCounter, DecrementLocalCounter } from './store/local.actions';\nimport { IncrementGlobalCounter, DecrementGlobalCounter } from './store/global.actions';\n\nexport class AppCounter extends React.Component {\n    constructor(props) {\n        super(props);\n        this.state = {\n            local: 0,\n            global: 0,\n            todo: 0\n        };\n\n        this.incrementLocalCounter = this.incrementLocalCounter.bind(this);\n        this.decrementLocalCounter = this.decrementLocalCounter.bind(this);\n        this.incrementGlobalCounter = this.incrementGlobalCounter.bind(this);\n        this.decrementGlobalCounter = this.decrementGlobalCounter.bind(this);\n        this.updateState = this.updateState.bind(this);\n\n        this.globalStore = GlobalStore.Get(false);\n        this.store = this.globalStore.CreateStore(\"CounterApp\", CounterReducer, []);\n        this.globalStore.RegisterGlobalActions(\"CounterApp\", [\"INCREMENT_GLOBAL\", \"DECREMENT_GLOBAL\", \"ADD_TODO\", \"REMOVE_TODO\"]);\n        this.globalStore.SubscribeToGlobalState(\"CounterApp\", this.updateState)\n    }\n\n    incrementLocalCounter() {\n        this.globalStore.DispatchAction(\"CounterApp\", IncrementLocalCounter());\n    }\n\n    decrementLocalCounter() {\n        this.globalStore.DispatchAction(\"CounterApp\", DecrementLocalCounter());\n    }\n\n    incrementGlobalCounter() {\n        this.globalStore.DispatchAction(\"CounterApp\", IncrementGlobalCounter());\n    }\n\n    decrementGlobalCounter() {\n        this.globalStore.DispatchAction(\"CounterApp\", DecrementGlobalCounter());\n    }\n\n    updateState(globalState) {\n        this.setState({\n            local: globalState.CounterApp.local,\n            global: globalState.CounterApp.global,\n            todo: globalState.CounterApp.todo\n        });\n    }\n\n\n    render() {\n        return (\n            <div>\n                <Counter count={this.state.global} header=\"Global Counter\" increment={this.incrementGlobalCounter} decrement={this.decrementGlobalCounter}></Counter>\n                <Counter count={this.state.local} header=\"Local Counter\" increment={this.incrementLocalCounter} decrement={this.decrementLocalCounter}></Counter>\n                <h2>\n                    Todo Counter\n                </h2>\n                <span>{this.state.todo}</span>\n            </div>\n        )\n    }\n}"
  },
  {
    "path": "sample/counterApp/src/counter.js",
    "content": "import React from 'react';\n\nexport class Counter extends React.Component {\n    render() {\n        return (\n            <div>\n                <h2>{this.props.header}</h2>\n                <span>{this.props.count}</span>\n                <button onClick={this.props.increment}>+</button>\n                <button onClick={this.props.decrement}>-</button>\n            </div>\n        )\n    }\n}"
  },
  {
    "path": "sample/counterApp/src/index.js",
    "content": "import React from 'react';\nimport { render } from 'react-dom';\nimport { AppCounter } from './appCounter';\n\nconst mountCounter = (elementId) => {\n    const renderElemement = document.getElementById(elementId);\n    render(<AppCounter />, renderElemement);\n}\n\nwindow[\"mountCounter\"] = mountCounter;\n\nif (!(window[\"micro-front-end-context\"])) {\n    mountCounter(\"app\");\n}"
  },
  {
    "path": "sample/counterApp/src/store/counterReducer.js",
    "content": "export const CounterReducer = (state = initialState, action) => {\n    if (action.type === \"INCREMENT_GLOBAL\") return { ...state, global: state.global + 1 };\n    if (action.type === \"DECREMENT_GLOBAL\") return { ...state, global: state.global - 1 };\n\n    if (action.type === \"INCREMENT_LOCAL\") return { ...state, local: state.local + 1 };\n    if (action.type === \"DECREMENT_LOCAL\") return { ...state, local: state.local - 1 };\n\n    if (action.type === \"ADD_TODO\") return { ...state, todo: state.todo + 1 };\n    if (action.type === \"REMOVE_TODO\") return { ...state, todo: state.todo - 1 };\n\n    return state;\n}\n\nvar initialState = {\n    global: 0,\n    local: 0,\n    todo: 0\n}"
  },
  {
    "path": "sample/counterApp/src/store/global.actions.js",
    "content": "export const IncrementGlobalCounter = () => {\n    return {\n        type: \"INCREMENT_GLOBAL\",\n        payload: null\n    }\n}\n\nexport const DecrementGlobalCounter = () => {\n    return {\n        type: \"DECREMENT_GLOBAL\",\n        payload: null\n    }\n}"
  },
  {
    "path": "sample/counterApp/src/store/local.actions.js",
    "content": "export const IncrementLocalCounter = () => {\n    return {\n        type: \"INCREMENT_LOCAL\",\n        payload: null\n    }\n}\n\nexport const DecrementLocalCounter = () => {\n    return {\n        type: \"DECREMENT_LOCAL\",\n        payload: null\n    }\n}"
  },
  {
    "path": "sample/counterApp/webpack.config.js",
    "content": "const path = require('path');\nconst { CleanWebpackPlugin } = require('clean-webpack-plugin');\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\n\nmodule.exports = {\n    entry: './src/index.js',\n    mode: 'development',\n    devtool: 'cheap-module-source-map',\n    plugins: [\n        new CleanWebpackPlugin(),\n        new HtmlWebpackPlugin({\n            title: \"React\",\n            template: 'index.html'\n        })\n    ],\n    module: {\n        rules:[{\n            test: /\\.(js|jsx)$/,\n            exclude: /node_modules/,\n            use: ['babel-loader']\n        }, {\n            test: /\\.css$/,\n            use: ['style-loader', 'css-loader']\n        }]\n    },\n    devServer: {\n        contentBase: './dist',\n        port: 4001\n    },\n    output: {\n        filename: '[name].bundle.js',\n        path: path.resolve(__dirname, 'dist')\n    }\n}"
  },
  {
    "path": "sample/counterApp/webpack.config.mf.js",
    "content": "const path = require('path');\nconst { CleanWebpackPlugin } = require('clean-webpack-plugin');\nconst webpack = require('webpack');\n\nmodule.exports = {\n    entry: './src/index.js',\n    mode: 'production',\n    devtool: 'cheap-module-source-map',\n    plugins: [\n        new CleanWebpackPlugin()\n    ],\n    module: {\n        rules:[{\n            test: /\\.(js|jsx)$/,\n            exclude: /node_modules/,\n            use: ['babel-loader']\n        }, {\n            test: /\\.css$/,\n            use: ['style-loader', 'css-loader']\n        }]\n    },\n    devServer: {\n        contentBase: './dist',\n        port: 4001\n    },\n    output: {\n        filename: '[name].bundle.js',\n        path: path.resolve(__dirname, 'dist')\n    }\n}"
  },
  {
    "path": "sample/shell/index.html",
    "content": "<html>\n    <head>\n        <script src=\"http://localhost:4001/main.bundle.js\"></script>\n        <script src=\"http://localhost:5001/main.bundle.js\"></script>\n    </head>\n\n    <body>\n        <h1>MF 1 - Counter App</h1>\n        <div id=\"counter\"></div>\n        <hr />\n        <h1>MF 2 - Todo App</h1>\n        <div id=\"todo\"></div>\n    </body>\n</html>"
  },
  {
    "path": "sample/shell/package.json",
    "content": "{\n    \"name\": \"redux-micro-frontend-sample-shell\",\n    \"version\": \"0.0.0\",\n    \"license\": \"MIT\",\n    \"description\": \"This is a sample app as the MF shell\",\n    \"author\": {\n        \"name\": \"Pratik Bhattacharya\",\n        \"email\": \"pratikb@microsoft.com\",\n        \"url\": \"https://www.devcompost.com/\"\n    },\n    \"homepage\": \"https://github.com/microsoft/redux-micro-frontend\",\n    \"keywords\": [\n        \"redux\",\n        \"micro frontend\",\n        \"microfrontend\",\n        \"microfrontends\",\n        \"micro frontends\",\n        \"state\",\n        \"statemanagement\"\n    ],\n    \"bugs\": {\n        \"url\": \"https://github.com/microsoft/redux-micro-frontend/issues\",\n        \"email\": \"pratikb@microsoft.com\"\n    },\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"https://github.com/microsoft/redux-micro-frontend\"\n    },\n    \"scripts\": {\n        \"build\": \"webpack\",\n        \"start\": \"webpack serve\"\n    },\n    \"private\": false,\n    \"dependencies\": {\n        \"react\": \"^16.14.0\",\n        \"react-dom\": \"^16.14.0\",\n        \"redux-micro-frontend\": \"^1.1.1\"\n    },\n    \"devDependencies\": {\n        \"clean-webpack-plugin\": \"^3.0.0\",\n        \"css-loader\": \"^5.0.0\",\n        \"html-webpack-plugin\": \"^4.5.0\",\n        \"style-loader\": \"^2.0.0\",\n        \"webpack\": \"^5.1.3\",\n        \"webpack-cli\": \"^4.1.0\",\n        \"webpack-dev-server\": \"^3.11.2\"\n    }\n}\n"
  },
  {
    "path": "sample/shell/src/index.js",
    "content": "(function() {\n\n    window[\"micro-front-end-context\"] = true;\n    window[\"mountCounter\"](\"counter\");\n    window[\"mountTodo\"](\"todo\");\n\n})();"
  },
  {
    "path": "sample/shell/webpack.config.js",
    "content": "const path = require('path');\nconst { CleanWebpackPlugin } = require('clean-webpack-plugin');\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\n\nmodule.exports = {\n    entry: './src/index.js',\n    mode: 'development',\n    devtool: 'cheap-module-source-map',\n    plugins: [\n        new CleanWebpackPlugin(),\n        new HtmlWebpackPlugin({\n            title: \"Micro-Front end Sample\",\n            template: 'index.html'\n        })\n    ],\n    module: {\n        rules:[{\n            test: /\\.css$/,\n            use: ['style-loader', 'css-loader']\n        }]\n    },\n    devServer: {\n        contentBase: './dist',\n        port: 6001\n    },\n    output: {\n        filename: '[name].bundle.js',\n        path: path.resolve(__dirname, 'dist')\n    }\n}"
  },
  {
    "path": "sample/todoApp/.babelrc",
    "content": "{\n    \"presets\": [\n        \"@babel/preset-react\"\n    ]\n}"
  },
  {
    "path": "sample/todoApp/index.html",
    "content": "<html>\n    <head></head>\n\n    <body>\n        <div id=\"app\"></div>\n    </body>\n</html>"
  },
  {
    "path": "sample/todoApp/package.json",
    "content": "{\n    \"name\": \"redux-micro-frontend-sample-todo\",\n    \"version\": \"0.0.0\",\n    \"license\": \"MIT\",\n    \"description\": \"This is a sample app with counter\",\n    \"author\": {\n        \"name\": \"Pratik Bhattacharya\",\n        \"email\": \"pratikb@microsoft.com\",\n        \"url\": \"https://www.devcompost.com/\"\n    },\n    \"homepage\": \"https://github.com/microsoft/redux-micro-frontend\",\n    \"keywords\": [\n        \"redux\",\n        \"micro frontend\",\n        \"microfrontend\",\n        \"microfrontends\",\n        \"micro frontends\",\n        \"state\",\n        \"statemanagement\"\n    ],\n    \"bugs\": {\n        \"url\": \"https://github.com/microsoft/redux-micro-frontend/issues\",\n        \"email\": \"pratikb@microsoft.com\"\n    },\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"https://github.com/microsoft/redux-micro-frontend\"\n    },\n    \"scripts\": {\n        \"build\": \"webpack\",\n        \"start\": \"webpack serve\",\n        \"start-component\": \"webpack serve --config ./webpack.config.mf.js\"\n    },\n    \"private\": false,\n    \"dependencies\": {},\n    \"devDependencies\": {\n        \"@babel/cli\": \"^7.12.1\",\n        \"@babel/core\": \"^7.12.3\",\n        \"@babel/preset-env\": \"^7.12.1\",\n        \"@babel/preset-react\": \"^7.12.1\",\n        \"babel-eslint\": \"^10.1.0\",\n        \"babel-loader\": \"^8.1.0\",\n        \"clean-webpack-plugin\": \"^3.0.0\",\n        \"css-loader\": \"^5.0.0\",\n        \"html-webpack-plugin\": \"^4.5.0\",\n        \"react\": \"^16.14.0\",\n        \"react-dom\": \"^16.14.0\",\n        \"redux-micro-frontend\": \"^1.1.1\",\n        \"style-loader\": \"^2.0.0\",\n        \"webpack\": \"^5.1.3\",\n        \"webpack-cli\": \"^4.1.0\",\n        \"webpack-dev-server\": \"^3.11.2\"\n    }\n}\n"
  },
  {
    "path": "sample/todoApp/src/addTodo.js",
    "content": "import React from 'react';\n\nexport class AddTodo extends React.Component {\n\n    constructor(props) {\n        super(props);\n        this.addTodo = this.addTodo.bind(this);\n    }\n\n    addTodo() {\n        this.props.addTodo(this.description.value);\n        this.description.value = '';\n    }\n\n    render() {\n        return (\n            <div>\n                <label>Add Todo Object</label>\n                <input type='Text' placeholder=\"Enter Todo\" ref={node => {\n                    this.description = node;\n                }}></input>\n                <button onClick={this.addTodo}>Submit</button>\n            </div>\n        )\n    }\n}"
  },
  {
    "path": "sample/todoApp/src/index.js",
    "content": "import React from 'react';\nimport { render } from 'react-dom';\nimport { TodoList } from './todoList';\n\n\nconst mountTodo = (elementId) => {\n    const renderElemement = document.getElementById(elementId);\n    render(<TodoList />, renderElemement);\n}\n\nwindow[\"mountTodo\"] = mountTodo;\n\nif (!(window[\"micro-front-end-context\"])) {\n    mountTodo(\"app\");\n}"
  },
  {
    "path": "sample/todoApp/src/store/todo.actions.js",
    "content": "export const AddTodo = (description) => {\n    return {\n        type: 'ADD_TODO',\n        payload: description\n    }\n}\n\nexport const RemoveTodo = (id) => {\n    return {\n        type: 'REMOVE_TODO',\n        payload: id\n    }\n}\n"
  },
  {
    "path": "sample/todoApp/src/store/todoReducer.js",
    "content": "export const TodoReducer = (state = [], action) => {\n    if (action.type === 'ADD_TODO') {\n        return [...state, { id: state.length + 1, description: action.payload }];\n    }\n    if (action.type === 'REMOVE_TODO') {\n        var todoId = action.payload;\n        var index = state.findIndex(todo => todo.id === todoId);\n        if (index === undefined || index === null || index < 0)\n            return state;\n        return [\n            ...state.slice(0, index),\n            ...state.slice(index + 1)\n        ];\n    }\n    return state;\n}"
  },
  {
    "path": "sample/todoApp/src/todo.js",
    "content": "import React from 'react';\n\nexport class Todo extends React.Component {\n    render() {\n        return(\n            <div onClick={() => this.props.removeTodo(this.props.id)}>\n                <span>{this.props.description}</span>\n            </div>\n        )\n    }\n}"
  },
  {
    "path": "sample/todoApp/src/todoList.js",
    "content": "import React from 'react';\nimport { Todo } from './todo';\nimport { createStore } from 'redux';\nimport { AddTodo as AddTodoComponent } from './addTodo';\nimport { TodoReducer } from './store/todoReducer';\nimport { GlobalStore } from 'redux-micro-frontend';\nimport { AddTodo, RemoveTodo } from './store/todo.actions';\n\nexport class TodoList extends React.Component {\n    constructor(props) {\n        super(props);\n\n        this.state = {\n            todos: [],\n            globalCounter: 0\n        };\n\n        this.addTodo = this.addTodo.bind(this);\n        this.removeTodo = this.removeTodo.bind(this);\n        this.counterChanged = this.counterChanged.bind(this);\n        this.stateChanged = this.stateChanged.bind(this);\n\n        this.globalStore = GlobalStore.Get();\n        this.store = createStore(TodoReducer);\n        this.globalStore.RegisterStore(\"TodoApp\", this.store, [GlobalStore.AllowAll]);\n        \n        try {\n            this.globalStore.SubscribeToPartnerState(\"TodoApp\", \"CounterApp\", this.counterChanged)\n        }\n        catch (error) { \n            //Since\n        }\n        this.globalStore.Subscribe(\"TodoApp\", this.stateChanged);\n    }\n\n    addTodo(description) {\n        this.globalStore.DispatchAction(\"TodoApp\", AddTodo(description));\n    }\n\n    removeTodo(todoId) {\n        this.globalStore.DispatchAction(\"TodoApp\", RemoveTodo(todoId));\n    }\n\n    counterChanged(counterState) {\n        this.setState({\n            globalCounter: counterState.global\n        });\n    }\n\n    stateChanged(todoState) {\n        this.setState({\n            todos: todoState\n        });\n    }\n\n    render() {\n        return (\n            <div>\n                <AddTodoComponent addTodo={this.addTodo}></AddTodoComponent>\n                <h2>Todos</h2>\n                <ul>\n                    {this.state.todos.map(todo => {\n                        return (\n                            <li>\n                                <Todo id={todo.id} description={todo.description} removeTodo={this.removeTodo}/>\n                            </li>\n                        )\n                    })}\n                </ul>\n                <div>\n                    Global Counter: {this.state.globalCounter}\n                </div>\n            </div>\n        )\n    }\n}"
  },
  {
    "path": "sample/todoApp/webpack.config.js",
    "content": "const path = require('path');\nconst { CleanWebpackPlugin } = require('clean-webpack-plugin');\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\n\nmodule.exports = {\n    entry: './src/index.js',\n    mode: 'development',\n    devtool: 'cheap-module-source-map',\n    plugins: [\n        new CleanWebpackPlugin(),\n        new HtmlWebpackPlugin({\n            title: \"React\",\n            template: 'index.html'\n        })\n    ],\n    module: {\n        rules:[{\n            test: /\\.(js|jsx)$/,\n            exclude: /node_modules/,\n            use: ['babel-loader']\n        }, {\n            test: /\\.css$/,\n            use: ['style-loader', 'css-loader']\n        }]\n    },\n    devServer: {\n        contentBase: './dist',\n        port: 5001\n    },\n    output: {\n        filename: '[name].bundle.js',\n        path: path.resolve(__dirname, 'dist')\n    }\n}"
  },
  {
    "path": "sample/todoApp/webpack.config.mf.js",
    "content": "const path = require('path');\nconst { CleanWebpackPlugin } = require('clean-webpack-plugin');\nconst webpack = require('webpack');\n\nmodule.exports = {\n    entry: './src/index.js',\n    mode: 'production',\n    devtool: 'cheap-module-source-map',\n    plugins: [\n        new CleanWebpackPlugin()\n    ],\n    module: {\n        rules:[{\n            test: /\\.(js|jsx)$/,\n            exclude: /node_modules/,\n            use: ['babel-loader']\n        }, {\n            test: /\\.css$/,\n            use: ['style-loader', 'css-loader']\n        }]\n    },\n    devServer: {\n        contentBase: './dist',\n        port: 5001\n    },\n    output: {\n        filename: '[name].bundle.js',\n        path: path.resolve(__dirname, 'dist')\n    }\n}"
  },
  {
    "path": "src/actions/action.interface.ts",
    "content": "export interface IAction<Payload = any> {\n    type: string,\n    payload: Payload,\n    logEnabled?: Boolean\n}"
  },
  {
    "path": "src/actions/index.ts",
    "content": "export * from './action.interface';"
  },
  {
    "path": "src/common/abstract.logger.ts",
    "content": "/**\n * Summary Logs data from application. Follows a Chain of Responsibility pattern where multiple loggers can be chained.\n */\nexport abstract class AbstractLogger {\n    LoggerIdentity: String;\n    NextLogger: AbstractLogger;\n\n    constructor(id: string) {\n        this.LoggerIdentity = id;\n    }\n\n    /**\n     * Summary Logs an event\n     * @param source Location from where the log is sent\n     * @param eventName Name of the event that has occurred\n     * @param properties Properties (KV pair) associated with the event\n     */\n    LogEvent(source: string, eventName: string, properties: any) {\n        try {\n            this.processEvent(source, eventName, properties);\n            if (this.NextLogger !== undefined && this.NextLogger !== null) {\n                this.NextLogger.LogEvent(source, eventName, properties);\n            }\n        }\n        catch {\n            // DO NOT THROW AN EXCEPTION WHEN LOGGING FAILS\n        }\n    }\n\n    /**\n     * Summary Logs an error in the system\n     * @param source Location where the error has occurred\n     * @param error Error\n     * @param properties Custom properties (KV pair)\n     */\n    LogException(source: string, error: Error, properties: any) {\n        try {\n            this.processException(source, error, properties);\n            if (this.NextLogger !== undefined && this.NextLogger !== null) {\n                this.NextLogger.LogException(source, error, properties);\n            }\n        }\n        catch {\n            // DO NOT THROW AN EXCEPTION WHEN LOGGING FAILS\n        }\n    }\n\n    /**\n     * Summary Sets the next logger in the chain. If the next logger is already filled then its chained to the last logger of the chain\n     * @param nextLogger Next Logger to be set in the chain\n     */\n    SetNextLogger(nextLogger: AbstractLogger) {\n        if (nextLogger === undefined || nextLogger === null)\n            return;\n        if (!this.isLoggerLoopCreated(nextLogger)) {\n            if (this.NextLogger === undefined || this.NextLogger === null) {\n                this.NextLogger = nextLogger;\n            } else {\n                this.NextLogger.SetNextLogger(nextLogger);\n            }\n        }\n    }\n\n    private isLoggerLoopCreated(nextLogger: AbstractLogger) {\n        let tmpLogger = {...nextLogger};\n        do {\n            if (tmpLogger.LoggerIdentity === this.LoggerIdentity)\n                return true;\n            tmpLogger = tmpLogger.NextLogger;\n        } while (tmpLogger !== null && tmpLogger !== undefined)\n        return false;\n    }\n\n    abstract processEvent(source: string, eventName: string, properties: any);\n    abstract processException(source, error: Error, properties: any);\n}\n"
  },
  {
    "path": "src/common/console.logger.ts",
    "content": "import { AbstractLogger } from \"./abstract.logger\";\n\nexport class ConsoleLogger extends AbstractLogger {\n    constructor(private _debugMode: boolean = false) {\n        super(\"DEFAULT_CONSOLE_LOGGER\");\n        this.NextLogger = null;\n    }\n\n    processEvent(source: string, eventName: string, properties: any) {\n        try {\n            if (!this._debugMode)\n                return;\n            console.log(`EVENT : ${eventName}. (${source})`);\n        } catch {\n            // DO NOT THROW AN EXCEPTION WHEN LOGGING FAILS\n        }\n    }\n\n    processException(source: string, error: Error, properties: any) {\n        try {\n            if (!this._debugMode)\n                return;\n            console.error(error);\n        } catch {\n            // DO NOT THROW AN EXCEPTION WHEN LOGGING FAILS\n        }\n    }\n}"
  },
  {
    "path": "src/common/interfaces/global.store.interface.ts",
    "content": "import { Store, Middleware, Reducer } from \"redux\";\nimport { IAction } from \"../../actions/action.interface\";\nimport { AbstractLogger as ILogger } from \"../abstract.logger\";\n\nexport interface IGlobalStore {\n    CreateStore(appName: string, appReducer: Reducer, middlewares?: Array<Middleware>, globalActions?: Array<string>, shouldReplaceStore?: boolean, shouldReplaceReducer?: boolean): Store<any, any>;\n    RegisterStore(appName: string, store: Store, globalActions?: Array<string>, shouldReplaceStore?: boolean): void\n    RegisterGlobalActions(appName: string, actions: Array<string>): void;\n\n    GetPlatformState(): any;\n    GetPartnerState(partnerName: string): any;\n    GetGlobalState(): any;\n\n    DispatchGlobalAction(source: string, action: IAction<any>): void;\n    DispatchLocalAction(source: string, action: IAction<any>): void;\n    DispatchAction(source: string, action: IAction<any>): void;\n\n    Subscribe(source: string, callback: (state: any) => void): () => void;\n    SubscribeToPlatformState(source: string, callback: (state: any) => void): () => void;\n    SubscribeToPartnerState(source: string, partner: string, callback: (state: any) => void): () => void;\n    SubscribeToPartnerState(source: string, partner: string, callback: (state: any) => void, eager: boolean): () => void;\n    SubscribeToGlobalState(source: string, callback: (state: any) => void): () => void;\n\n    AddSelectors(source: string, selectors: Record<string, any>, mergeSelectors?: boolean): void;\n    SelectPartnerState(partner: string, selector: string, defaultReturn?: any): any;\n\n    SetLogger(logger: ILogger): void;\n};"
  },
  {
    "path": "src/common/interfaces/index.ts",
    "content": "export * from './global.store.interface';\n"
  },
  {
    "path": "src/global.store.ts",
    "content": "import { IAction } from './actions/action.interface';\nimport { ConsoleLogger } from './common/console.logger';\nimport { ActionLogger } from './middlewares/action.logger';\nimport { composeWithDevTools } from 'redux-devtools-extension';\nimport { AbstractLogger as ILogger } from './common/abstract.logger';\nimport { IGlobalStore } from './common/interfaces/global.store.interface';\nimport { Store, Reducer, Middleware, createStore, applyMiddleware } from 'redux';\n\n/**\n * Summary Global store for all Apps and container shell (Platform) in Micro-Frontend application.\n * Description Singleton class to be used all all Apps for registering the isolated App States. The platform-level and global-level store can be accessed from this class.\n */\nexport class GlobalStore implements IGlobalStore {\n    public static readonly Platform: string = \"Platform\";\n    public static readonly AllowAll: string = \"*\";\n    public static readonly InstanceName: string = \"GlobalStoreInstance\";\n    public static DebugMode: boolean = false;\n\n    private _stores: { [key: string]: Store };\n    private _globalActions: { [key: string]: Array<string> };\n    private _globalListeners: Array<(state: any) => void>;\n    private _eagerPartnerStoreSubscribers: { [key: string]: { [key: string]: (state) => void } }\n    private _eagerUnsubscribers: { [key: string]: { [key: string]: () => void } }\n    private _actionLogger: ActionLogger = null;\n    private _selectors: { [key: string]: any };\n\n    private constructor(private _logger: ILogger = null) {\n        this._stores = {};\n        this._globalActions = {};\n        this._globalListeners = [];\n        this._eagerPartnerStoreSubscribers = {};\n        this._eagerUnsubscribers = {};\n        this._actionLogger = new ActionLogger(_logger);\n        this._selectors = {};\n    }\n\n    /**\n     * Summary Gets the singleton instance of the Global Store.\n     * \n     * @param {ILogger} logger Logger service.\n     */\n    public static Get(debugMode: boolean = false, logger: ILogger = null): IGlobalStore {\n        if (debugMode) {\n            this.DebugMode = debugMode;\n        }\n        if (debugMode && (logger === undefined || logger === null)) {\n            logger = new ConsoleLogger(debugMode);\n        }\n        let globalGlobalStoreInstance: IGlobalStore = window[GlobalStore.InstanceName] || null;\n        if (globalGlobalStoreInstance === undefined || globalGlobalStoreInstance === null) {\n            globalGlobalStoreInstance = new GlobalStore(logger);\n            window[GlobalStore.InstanceName] = globalGlobalStoreInstance;\n        }\n        return globalGlobalStoreInstance;\n    }\n\n    /**\n     * Summary: Creates and registers a new store\n     * \n     * @access public\n     * \n     * @param {string} appName Name of the App for whom the store is getting creating.\n     * @param {Reducer} appReducer The root reducer of the App. If partner app is using multiple reducers, then partner App must use combineReducer and pass the root reducer\n     * @param {Array<Middleware>} middlewares List of redux middlewares that the partner app needs.\n     * @param {boolean} shouldReplaceStore Flag to indicate if the Partner App wants to replace an already created/registered store with the new store.\n     * @param {boolean} shouldReplaceReducer Flag to indicate if the Partner App wants to replace the existing root Reducer with the given reducer. Note, that the previous root Reducer will be replaced and not updated. If the existing Reducer needs to be used, then partner app must do the append the new reducer and pass the combined root reducer.\n     * \n     * @returns {Store<any, any>} The new Store\n     */\n    CreateStore(appName: string, appReducer: Reducer, middlewares?: Array<Middleware>, globalActions?: Array<string>, shouldReplaceStore: boolean = false, shouldReplaceReducer: boolean = false): Store<any, any> {\n        let existingStore = this._stores[appName];\n        if (existingStore === null || existingStore === undefined || shouldReplaceStore) {\n            if (middlewares === undefined || middlewares === null)\n                middlewares = [];\n            let appStore = createStore(appReducer, GlobalStore.DebugMode ? composeWithDevTools(applyMiddleware(...middlewares)) : applyMiddleware(...middlewares));\n            this.RegisterStore(appName, appStore, globalActions, shouldReplaceStore);\n            return appStore;\n        }\n\n        if (shouldReplaceReducer) {\n            console.warn(`The reducer for ${appName} is getting replaced`);\n            existingStore.replaceReducer(appReducer);\n            this.RegisterStore(appName, existingStore, globalActions, true);\n        }\n        return existingStore;\n    }\n\n    /**\n     * Summary: Registers an isolated app store\n     * \n     * @access public\n     * \n     * @param {string} appName Name of the App.\n     * @param {Store} store Instance of the store.\n     * @param {boolean} shouldReplace Flag to indicate if the an already registered store needs to be replaced.\n     */\n    RegisterStore(appName: string, store: Store, globalActions?: Array<string>, shouldReplaceExistingStore: boolean = false): void {\n        let existingStore = this._stores[appName];\n        if (existingStore !== undefined && existingStore !== null && shouldReplaceExistingStore === false)\n            return;\n\n        this._stores[appName] = store;\n        store.subscribe(this.InvokeGlobalListeners.bind(this));\n        this.RegisterGlobalActions(appName, globalActions);\n        this.RegisterEagerSubscriptions(appName);\n        this.LogRegistration(appName, (existingStore !== undefined && existingStore !== null));\n    }\n\n    /**\n     * Summary: Registers a list of actions for an App that will be made Global.\n     * Description: Global actions can be dispatched on the App Store by any Partner Apps. If partner needs to make all actions as Global, then pass \"*\" in the list. If no global actions are registered then other partners won't be able to dispatch any action on the App Store.\n     * \n     * @access public\n     * \n     * @param {string} appName Name of the app.\n     * @param {Array<string>} actions List of global action names.\n     */\n    RegisterGlobalActions(appName: string, actions?: Array<string>): void {\n        if (actions === undefined || actions === null || actions.length === 0) {\n            return;\n        }\n\n        let registeredActions = this._globalActions[appName];\n        if (registeredActions === undefined || registeredActions === null) {\n            registeredActions = [];\n            this._globalActions[appName] = [];\n        }\n        let uniqueActions = actions.filter(action => registeredActions.find(registeredAction => action === registeredAction) === undefined);\n        uniqueActions = [...new Set(uniqueActions)]; // Removing any duplicates\n        this._globalActions[appName] = [...this._globalActions[appName], ...uniqueActions];\n    }\n\n    /**\n     * Summary: Gets the current state of the Platform\n     * \n     * @access public\n     * \n     * @returns Current Platform State (App with name Platform)\n     */\n    GetPlatformState(): any {\n        let platformStore = this.GetPlatformStore();\n        if (platformStore === undefined || platformStore === null)\n            return null;\n        return this.CopyState(platformStore.getState());\n    }\n\n    /**\n     * Summary: Gets the current state of the given Partner.\n     * Description: A read-only copy of the Partner state is returned. The state cannot be mutated using this method. For mutation dispatch actions. In case the partner hasn't been registered or the partner code hasn't loaded, the method will return null.\n     * \n     * @param partnerName Name of the partner whose state is needed\n     * \n     * @returns {any} Current partner state.\n     */\n    GetPartnerState(partnerName: string): any {\n        let partnerStore = this.GetPartnerStore(partnerName);\n        if (partnerStore === undefined || partnerStore === null)\n            return null;\n        let partnerState = partnerStore.getState();\n        return this.CopyState(partnerState);\n    }\n\n    /**\n     * Summary: Gets the global store.\n     * Description: The global store comprises of the states of all registered partner's state.\n     * Format\n     * {\n     *      Platform: { ...Platform_State },\n     *      Partner_Name_1: { ...Partner_1_State },\n     *      Partner_Name_2: { ...Partner_2_State }\n     * }\n     * \n     * @access public \n     * \n     * @returns {any} Global State.\n     */\n    GetGlobalState(): any {\n        let globalState = {};\n        for (let partner in this._stores) {\n            let state = this._stores[partner].getState();\n            globalState[partner] = state;\n        };\n        return this.CopyState(globalState);\n    }\n\n    /**\n     * Summary: Dispatches an action on all the Registered Store (including Platform level store).\n     * Description: The action will be dispatched only if the Partner App has declated the action to be global at it's store level.\n     * \n     * @access public\n     * \n     * @param {string} source Name of app dispatching the Actions\n     * @param {IAction<any>} action Action to be dispatched\n     */\n    DispatchGlobalAction(source: string, action: IAction<any>): void {\n        for (let partner in this._stores) {\n            let isActionRegisteredByPartner = this.IsActionRegisteredAsGlobal(partner, action);\n            if (isActionRegisteredByPartner) {\n                this._stores[partner].dispatch(action);\n            }\n        }\n    }\n\n    /**\n     * Summary: Dispatched an action of the local store\n     * \n     * @access public\n     * \n     * @param {string} source Name of app dispatching the Actions\n     * @param {IAction<any>} action Action to be dispatched\n     */\n    DispatchLocalAction(source: string, action: IAction<any>): void {\n        let localStore = this._stores[source];\n        if (localStore === undefined || localStore === null) {\n            let error = new Error(`Store is not registered`);\n            if (this._logger !== undefined && this._logger !== null)\n                this._logger.LogException(source, error, {});\n            throw error;\n        }\n        localStore.dispatch(action);\n    }\n\n    /**\n     * Summary: Dispatches an action at a local as well global level\n     * \n     * @access public\n     * \n     * @param {string} source Name of app dispatching the Actions\n     * @param {IAction<any>} action Action to be dispatched\n     */\n    DispatchAction(source: string, action: IAction<any>): void {\n        this.DispatchGlobalAction(source, action);\n\n        let isActionGlobal = this.IsActionRegisteredAsGlobal(source, action);\n        if (!isActionGlobal)\n            this.DispatchLocalAction(source, action);\n    }\n\n    /**\n     * Summary: Subscribe to current store's state changes\n     * \n     * @param {string} source Name of the application\n     * @param {(state: any) => void} callback Callback method to be invoked when state changes\n     */\n    Subscribe(source: string, callback: (state: any) => void): () => void {\n        let store = this.GetPartnerStore(source);\n        if (store === undefined || store === null) {\n            throw new Error(`ERROR: Store for ${source} hasn't been registered`);\n        }\n        return store.subscribe(() => callback(store.getState()));\n    }\n\n    /**\n     * Summary: Subscribe to any change in the Platform's state.\n     * \n     * @param {string} source Name of application subscribing to the state changes.\n     * @param {(state: any) => void} callback Callback method to be called for every platform's state change.\n     * \n     * @returns {() => void} Unsubscribe method. Call this method to unsubscribe to the changes.\n     */\n    SubscribeToPlatformState(source: string, callback: (state: any) => void): () => void {\n        let platformStore = this.GetPlatformStore();\n        return platformStore.subscribe(() => callback(platformStore.getState()));\n    }\n\n    /**\n     * Summary: Subscribe to any change in the Partner App's state.\n     * \n     * @access public\n     * \n     * \n     * @param {string} source Name of the application subscribing to the state changes.\n     * @param {string} partner Name of the Partner application to whose store is getting subscribed to.\n     * @param {(state: any) => void} callback Callback method to be called for every partner's state change.\n     * @param {boolean} eager Allows subscription to store that's yet to registered\n     * \n     * @throws Error when the partner is yet to be registered/loaded or partner doesn't exist.\n     * \n     * @returns {() => void} Unsubscribe method. Call this method to unsubscribe to the changes.\n     */\n    SubscribeToPartnerState(source: string, partner: string, callback: (state: any) => void, eager: boolean = true): () => void {\n        let partnerStore = this.GetPartnerStore(partner);\n        if (partnerStore === undefined || partnerStore === null) {\n            if (!eager) {\n                throw new Error(`ERROR: ${source} is trying to subscribe to partner ${partner}. Either ${partner} doesn't exist or hasn't been loaded yet`);\n            }\n            if (this._eagerPartnerStoreSubscribers[partner]) {\n                this._eagerPartnerStoreSubscribers[partner].source = callback;\n            } else {\n                this._eagerPartnerStoreSubscribers[partner] = {\n                    source: callback\n                }\n            }\n\n            return () => {\n                this.UnsubscribeEagerSubscription(source, partner);\n            }\n        }\n        return partnerStore.subscribe(() => callback(partnerStore.getState()));\n    }\n\n    /**\n     * Summary: Subscribe to any change in the Global State, including Platform-level and Partner-level changes.\n     * \n     * @access public\n     * \n     * @param {string} source Name of the application subscribing to the state change.\n     * @param {(state: any) => void} callback Callback method to be called for every any change in the global state.\n     * \n     * @returns {() => void} Unsubscribe method. Call this method to unsubscribe to the changes.\n     */\n    SubscribeToGlobalState(source: string, callback: (state: any) => void): () => void {\n        this._globalListeners.push(callback)\n        return () => {\n            this._globalListeners = this._globalListeners.filter(globalListener => globalListener !== callback);\n        }\n    }\n\n    UnsubscribeEagerSubscription(source: string, partnerName: string) {\n        if (!partnerName || !source)\n            return;\n\n        if (!this._eagerUnsubscribers[partnerName])\n            return;\n\n        let unsubscriber = this._eagerUnsubscribers[partnerName].source;\n        if (unsubscriber)\n            unsubscriber();\n    }\n\n    SetLogger(logger: ILogger) {\n        if (this._logger === undefined || this._logger === null)\n            this._logger = logger;\n        else\n            this._logger.SetNextLogger(logger);\n        this._actionLogger.SetLogger(logger);\n    }\n\n    /**\n     * Summary: Expose a collection of Selecotrs from a Partner-level that other partners can later consume. This allows partners to derive data without forcing partners to know the state structure.\n     * \n     * @access public\n     * \n     * @param {string} source Name of the application exposing an derived state API\n     * @param {Record<string, any>} selectors The collection of APIs of derived state selectors.\n     * @param {boolean} mergeSelectors If the source application already exposed an API set, merge the new API being passed in.\n     * \n     */\n     AddSelectors(source: string, selectors: Record<string, any>, mergeSelectors = false) {\n        if (this._selectors[source] == undefined) {\n            this._selectors[source] = selectors;\n        }\n\n        if (this._selectors[source] != undefined && mergeSelectors) {\n            this._selectors[source] = Object.assign({}, this._selectors[source], selectors);\n        }\n    }\n\n    /**\n     * Summary: Select derived state from a partner app using the selector name\n     * \n     * @access public\n     * \n     * @param {string} partner Name of the partner application to select derived data from\n     * @param {string} selector The name of the API to select\n     * @param {any} defaultReturn If the partner app does not have that API exposed, return this default value instead of undefined.\n     * \n     */\n     SelectPartnerState(partner: string, selector: string, defaultReturn?: any) {\n        if (this._selectors[partner] == undefined) {\n            throw new Error(`ERROR: ${partner} not exposed any selectors.`);\n        }\n        if (this._selectors[partner][selector] == undefined) {\n            console.warn(`${partner} has not exposed a selector with the name: ${selector}`);\n            return defaultReturn;\n        }\n\n        return this._selectors[partner][selector]();\n    }\n\n    private RegisterEagerSubscriptions(appName: string) {\n        let eagerCallbacksRegistrations = this._eagerPartnerStoreSubscribers[appName];\n        if (eagerCallbacksRegistrations === undefined || eagerCallbacksRegistrations === undefined)\n            return;\n        let registeredApps = Object.keys(eagerCallbacksRegistrations);\n        registeredApps.forEach(sourceApp => {\n            let callback = eagerCallbacksRegistrations[sourceApp];\n            if (callback) {\n                let unregistrationCallback = this.SubscribeToPartnerState(sourceApp, appName, callback, false);\n                if (this._eagerPartnerStoreSubscribers[appName]) {\n                    this._eagerPartnerStoreSubscribers[appName].sourceApp = unregistrationCallback;\n                } else {\n                    this._eagerPartnerStoreSubscribers[appName] = {\n                        sourceApp: unregistrationCallback\n                    };\n                }\n            }\n        });\n    }\n\n    private InvokeGlobalListeners(): void {\n        let globalState = this.GetGlobalState();\n        this._globalListeners.forEach(globalListener => {\n            globalListener(globalState);\n        });\n    }\n\n    private GetPlatformStore(): Store<any, any> {\n        return this.GetPartnerStore(GlobalStore.Platform);\n    }\n\n    private GetPartnerStore(partnerName: string): Store<any, any> {\n        return this._stores[partnerName];\n    }\n\n    private GetGlobalMiddlewares(): Array<Middleware> {\n        let actionLoggerMiddleware = this._actionLogger.CreateMiddleware();\n        return [actionLoggerMiddleware];\n    }\n\n    private IsActionRegisteredAsGlobal(appName: string, action: IAction<any>): boolean {\n        let registeredGlobalActions = this._globalActions[appName];\n        if (registeredGlobalActions === undefined || registeredGlobalActions === null) {\n            return false;\n        }\n        return registeredGlobalActions.some(registeredAction => registeredAction === action.type || registeredAction === GlobalStore.AllowAll);\n    }\n\n    private LogRegistration(appName: string, isReplaced: boolean) {\n        try {\n            let properties = {\n                \"AppName\": appName,\n                \"IsReplaced\": isReplaced.toString()\n            };\n            if (this._logger)\n                this._logger.LogEvent(\"Store.GlobalStore\", \"StoreRegistered\", properties);\n        }\n        catch (error) {\n            // Gulp the error\n            console.error(`ERROR: There was an error while logging registration for ${appName}`);\n            console.error(error);\n        }\n    }\n\n    private CopyState(state: any) {\n        if (state === undefined || state === null || typeof state !== 'object') {\n            return state;\n        } else {\n            return {\n                ...state\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/middlewares/action.logger.ts",
    "content": "import { Middleware } from 'redux';\nimport { stringify } from 'flatted';\nimport { IAction } from '../actions';\nimport { AbstractLogger as ILogger } from '../common/abstract.logger';\n\n/**\n * Summary Logs action and its impact on the state\n */\nexport class ActionLogger {\n\n    constructor(private _logger: ILogger) { }\n\n    /**\n     * Summary Creates as Redux middleware for logging the actions and its impact on the State\n     */\n    public CreateMiddleware(): Middleware {\n        return store => next => (action: IAction<any>) => {\n            if (!this.IsLoggingAllowed(action)) {\n                return next(action);\n            }\n\n            const dispatchedAt = new Date();\n            let state = store.getState();\n            this.LogActionDispatchStart(state, action);\n            let dispatchResult: IAction<any> = null;\n            try {\n                dispatchResult = next(action);\n            } catch (error) {\n                this.LogActionDispatchFailure(action, dispatchedAt, error);\n                throw error;\n            }\n            state = store.getState();\n            this.LogActionDispatchComplete(state, action, dispatchedAt);\n            return dispatchResult;\n        }\n    }\n\n    public SetLogger(logger: ILogger) {\n        if (this._logger === undefined || this._logger === null)\n            this._logger = logger;\n        else\n            this._logger.SetNextLogger(logger);\n    }\n\n    private IsLoggingAllowed(action: IAction) {\n        return action.logEnabled !== undefined\n            && action.logEnabled !== null\n            && action.logEnabled === true\n            && this._logger !== undefined\n            && this._logger !== null;\n    }\n\n    private LogActionDispatchStart(state: any, action: IAction<any>) {\n        try {\n            var properties = {\n                \"OldState\": stringify(state),\n                \"ActionName\": action.type,\n                \"DispatchStatus\": \"Dispatched\",\n                \"DispatchedOn\": new Date().toISOString(),\n                \"Payload\": stringify(action.payload)\n            };\n            this._logger.LogEvent(\"Fxp.Store.ActionLogger\", `${action.type} :: DISPATCHED`, properties);\n        }\n        catch (error) {\n            // Gulp the error\n            console.error(\"ERROR: There was an error while trying to log the Dispatch Complete event\");\n            console.error(error);\n        }\n    }\n\n    private LogActionDispatchComplete(state: any, action: any, dispatchedAt: Date) {\n        try {\n            let currentTime = new Date();\n            const timeTaken = currentTime.getTime() - dispatchedAt.getTime();\n            var properties = {\n                \"NewState\": stringify(state),\n                \"ActionName\": action.type,\n                \"DispatchStatus\": \"Completed\",\n                \"DispatchedOn\": new Date().toISOString(),\n                \"Payload\": stringify(action.payload),\n                \"TimeTaken\": timeTaken.toString()\n            };\n            this._logger.LogEvent(\"Fxp.Store.ActionLogger\", `${action.type} :: COMPLETED`, properties);\n        }\n        catch (error) {\n            // Gulp the error\n            console.error(\"ERROR: There was an error while trying to log the Dispatch Complete event\");\n            console.error(error);\n        }\n    }\n\n    private LogActionDispatchFailure(action: any, dispatchedAt: Date, exception: Error) {\n        try {\n            let currentTime = new Date();\n            const timeTaken = currentTime.getTime() - dispatchedAt.getTime();\n            var properties = {\n                \"ActionName\": action.type,\n                \"DispatchStatus\": \"Failed\",\n                \"DispatchedOn\": new Date().toISOString(),\n                \"Payload\": stringify(action.payload),\n                \"TimeTaken\": timeTaken.toString()\n            };\n            this._logger.LogEvent(\"Fxp.Store.ActionLogger\", `${action.type} :: FAILED`, properties);\n            this._logger.LogException(\"Fxp.Store.ActionLogger\", exception, properties);\n            console.error(exception);\n        }\n        catch (error) {\n            // Gulp the error\n            console.error(\"ERROR: There was an error while trying to log the Dispatch Failure event\");\n            console.error(error);\n        }\n    }\n}"
  },
  {
    "path": "test/global.store.tests.ts",
    "content": "import { createStore, Reducer } from 'redux';\nimport { GlobalStore } from '../src/global.store';\nimport { IAction } from '../src/actions/action.interface';\nimport { AbstractLogger as ILogger } from '../src/common/abstract.logger';\n\ndescribe(\"Global Store\", () => {\n    let mockLogger = {\n        LogEvent: function (source, event, properties) { },\n        LogException: function (source, error, properties) { }\n    } as ILogger;\n\n    beforeEach(() => {\n        spyOn(mockLogger, \"LogEvent\").and.callThrough();\n    });\n\n    let globalStore = GlobalStore.Get(true, mockLogger);\n\n    it(\"Should get created\", () => {\n        expect(globalStore).toBeDefined();\n    });\n\n    it(\"Should get created as a singleton\", () => {\n        let globalStoreDuplicate = GlobalStore.Get();\n        expect(globalStore).toBe(globalStoreDuplicate);\n    });\n\n    it(\"Should get created with Platform State\", () => {\n        expect((<any>globalStore)._stores).toBeDefined();\n    });\n\n    describe(\"CreateStore\", () => {\n        let dummyPartnerReducer: Reducer<any, any> = (state: any = {}, action) => {\n            return state;\n        };\n\n        it(\"Should create a new partner store without custom middlewares and no global actions\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner_1\";\n\n            // Act\n            let store = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, []);\n\n            // Assert\n            expect(store).toBeDefined();\n            expect((<any>globalStore)._stores).toBeDefined();\n            expect((<any>globalStore)._stores[partnerAppName]).toBeDefined();\n        });\n\n        it(\"Should re-register partner store when ShouldReplace is true\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner_1\";\n\n            // Act\n            let store = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], null, true);\n\n            // Assert\n            expect(store).toBeDefined();\n            expect((<any>globalStore)._stores).toBeDefined();\n            expect((<any>globalStore)._stores[partnerAppName]).toBeDefined();\n            expect(mockLogger.LogEvent).toHaveBeenCalled();\n        });\n\n\n        it(\"Should replace partner reducer when ShouldUpdate is true\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner_1\";\n\n            // Act\n            let store = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], null, false, true);\n\n            // Assert\n            expect(store).toBeDefined();\n            expect((<any>globalStore)._stores).toBeDefined();\n            expect((<any>globalStore)._stores[partnerAppName]).toBeDefined();\n\n            expect(mockLogger.LogEvent).toHaveBeenCalled();\n            expect(mockLogger.LogEvent).toHaveBeenCalledTimes(1);\n        });\n\n        it(\"Should not re-register partner store when ShouldReplace is false\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner_2\";\n            let store = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], null, true);\n\n            // Act\n            store = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], null, false);\n\n            // Assert\n            expect(store).toBeDefined();\n            expect((<any>globalStore)._stores).toBeDefined();\n            expect((<any>globalStore)._stores[partnerAppName]).toBeDefined();\n            expect(mockLogger.LogEvent).toHaveBeenCalled();\n            expect(mockLogger.LogEvent).toHaveBeenCalledTimes(1);\n        });\n    });\n\n    describe(\"RegisterGlobalActions\", () => {\n        it(\"Should register global actions for a new partner\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-1\";\n            let partnerGlobalActions = [\"ga-1\", \"ga-2\"];\n\n            // Act\n            globalStore.RegisterGlobalActions(partnerAppName, partnerGlobalActions);\n\n            // Assert\n            expect((<any>globalStore)._globalActions).toBeDefined();\n            expect((<any>globalStore)._globalActions[partnerAppName]).toBeDefined();\n            let registeredActions = ((<any>globalStore)._globalActions[partnerAppName] as string[]);\n            registeredActions.forEach(registeredAction => {\n                expect(partnerGlobalActions.some(action => action === registeredAction)).toBeTruthy();\n            })\n        });\n\n        it(\"Should not register global actions for a new partner when actions is null\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-2\";\n\n            // Act\n            globalStore.RegisterGlobalActions(partnerAppName, null);\n\n            // Assert\n            expect((<any>globalStore)._globalActions).toBeDefined();\n            expect((<any>globalStore)._globalActions[partnerAppName]).toBeUndefined();\n        });\n\n        it(\"Should not register global actions for a new partner when actions is empty\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-3\";\n\n            // Act\n            globalStore.RegisterGlobalActions(partnerAppName, []);\n\n            // Assert\n            expect((<any>globalStore)._globalActions).toBeDefined();\n            expect((<any>globalStore)._globalActions[partnerAppName]).toBeUndefined();\n        });\n\n        it(\"Should not register already registered global actions for a partner\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-4\";\n            let duplicateActionName = \"ga-2\";\n            let partnerGlobalActions_1 = [\"ga-1\", duplicateActionName];\n            let partnerGlobalActions_2 = [duplicateActionName, \"ga-3\"];\n\n            // Act\n            globalStore.RegisterGlobalActions(partnerAppName, partnerGlobalActions_1);\n            globalStore.RegisterGlobalActions(partnerAppName, partnerGlobalActions_2);\n\n            // Assert\n            expect((<any>globalStore)._globalActions).toBeDefined();\n            expect((<any>globalStore)._globalActions[partnerAppName]).toBeDefined();\n            let registeredActions = ((<any>globalStore)._globalActions[partnerAppName] as string[]);\n            expect(registeredActions.length).toBe(3);\n            expect(registeredActions.filter(a => a === duplicateActionName).length).toBe(1);\n        });\n\n        it(\"Should not register duplicate global actions for a partner\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-4\";\n            let duplicateActionName = \"ga-2\";\n            let partnerGlobalActions = [\"ga-1\", duplicateActionName, duplicateActionName, duplicateActionName, \"ga-3\"];\n\n            // Act\n            globalStore.RegisterGlobalActions(partnerAppName, partnerGlobalActions);\n\n            // Assert\n            expect((<any>globalStore)._globalActions).toBeDefined();\n            expect((<any>globalStore)._globalActions[partnerAppName]).toBeDefined();\n            let registeredActions = ((<any>globalStore)._globalActions[partnerAppName] as string[]);\n            expect(registeredActions.length).toBe(3);\n            expect(registeredActions.filter(a => a === duplicateActionName).length).toBe(1);\n        });\n    });\n\n    describe(\"GetPlatformState\", () => {\n        it(\"Should return Platform state\", () => {\n            // Act\n            let platformState = globalStore.GetPlatformState();\n\n            // Assert\n            expect(platformState).toBeDefined();\n        })\n    });\n\n    describe(\"GetPartnerState\", () => {\n        let dummyPartnerReducer: Reducer<any, any> = (state: string = null, action) => {\n            return action.payload;\n        };\n\n        it(\"Should return Partner state\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-10\";\n            globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [GlobalStore.AllowAll], false, false);\n            let actionText = \"ACTION!!!\";\n\n            // Act\n            globalStore.DispatchGlobalAction(\"TEST\",\n                {\n                    type: \"Sample\",\n                    payload: actionText\n                });\n            let partnerState = globalStore.GetPartnerState(partnerAppName);\n\n            // Assert\n            expect(partnerState).toBeDefined();\n            expect(partnerState).toBe(actionText);\n        });\n\n        it(\"Should not return Partner state when partner is not registered\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-11\";\n\n            // Act\n            let partnerState = globalStore.GetPartnerState(partnerAppName);\n\n            // Assert\n            expect(partnerState).toBeNull();\n        });\n    });\n\n    describe(\"GetGlobalState\", () => {\n        let dummyPartnerReducer: Reducer<any, any> = (state: string = null, action) => {\n            return action.payload;\n        };\n\n        it(\"Should formulate global state\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-20\";\n            globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [GlobalStore.AllowAll], false, false);\n            let actionText = \"ACTION!!!\";\n\n            // Act\n            globalStore.DispatchGlobalAction(\"TEST\",\n                {\n                    type: \"Sample\",\n                    payload: actionText\n                });\n            let partnerState = globalStore.GetGlobalState();\n\n            // Assert\n            expect(partnerState).toBeDefined();\n            expect(partnerState[partnerAppName]).toBeDefined();\n            expect(partnerState[partnerAppName]).toBe(actionText);\n        });\n    });\n\n    describe(\"DispatchGlobalAction\", () => {\n        let dummyPartnerReducer: Reducer<any, any> = (state: string = \"Default\", action: IAction<any>) => {\n            switch (action.type) {\n                case \"Local\": return \"Local\";\n                case \"Global\": return \"Global\";\n            }\n\n        };\n\n        it(\"Should dispatch globally registered action on a partner store\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-30\";\n            globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            // Act\n            globalStore.DispatchGlobalAction(\"Test\",\n                {\n                    type: \"Global\",\n                    payload: null\n                });\n            let partnerState = globalStore.GetGlobalState();\n\n            // Assert\n            expect(partnerState).toBeDefined();\n            expect(partnerState[partnerAppName]).toBeDefined();\n            expect(partnerState[partnerAppName]).toBe(\"Global\");\n        });\n\n        it(\"Should not dispatch non-globally registered action on a partner store\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-31\";\n            globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            // Act\n            globalStore.DispatchGlobalAction(\"Test\",\n                {\n                    type: \"Local\",\n                    payload: null\n                });\n            let partnerState = globalStore.GetGlobalState();\n\n            // Assert\n            expect(partnerState).toBeDefined();\n            expect(partnerState[partnerAppName]).toBeUndefined();\n        });\n    });\n\n    describe(\"SubscribeToPartnerState\", () => {\n        let dummyPartnerReducer: Reducer<any, any> = (state: string = \"Default\", action: IAction<any>) => {\n            switch (action.type) {\n                case \"Local\": return \"Local\";\n                case \"Global\": return \"Global\";\n            }\n\n        };\n\n        it(\"Should invoke callback when partner state changes\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-40\";\n            let isPartnerStateChanged = false;\n            globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            // Act\n            globalStore.SubscribeToPartnerState(\"Test\", partnerAppName, (state) => {\n                isPartnerStateChanged = true;\n            });\n            globalStore.DispatchGlobalAction(\"Test\",\n                {\n                    type: \"Global\",\n                    payload: null\n                });\n\n\n            // Assert\n            expect(isPartnerStateChanged).toBeTruthy();\n        });\n\n        it(\"Should allow registering to non-registered store in eager mode\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-100\";\n\n            // Act\n            let exceptionThrown = false;\n            try {\n                globalStore.SubscribeToPartnerState(\"Test\", partnerAppName, (state) => { }, true);\n            } catch {\n                exceptionThrown = true;\n            }\n\n            // Assert\n            expect(exceptionThrown).not.toBeTruthy();\n        });\n\n        it(\"Should throw exception when registering to non-registered store in non-eager mode\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-101\";\n\n            // Act\n            let exceptionThrown = false;\n            try {\n                globalStore.SubscribeToPartnerState(\"Test\", partnerAppName, (state) => { }, false);\n            } catch {\n                exceptionThrown = true;\n            }\n\n            // Assert\n            expect(exceptionThrown).toBeTruthy();\n        });\n\n        it(\"Should attach eager subscriber\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-102\";\n            let isPartnerStateChanged = false;\n            \n            // Act\n            globalStore.SubscribeToPartnerState(\"Test\", partnerAppName, (state) => {\n                isPartnerStateChanged = true;\n            }, true);\n\n            globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n            globalStore.DispatchGlobalAction(\"Test\",\n                {\n                    type: \"Global\",\n                    payload: null\n                });\n\n\n            // Assert\n            expect(isPartnerStateChanged).toBeTruthy();\n        });\n\n        it(\"Should attach eager multiple subscribers\", () => {\n            // Arrange\n            let partnerAppName_1 = \"SamplePartner-112\";\n            let isPartnerStateChanged_1 = false;\n\n            let partnerAppName_2 = \"SamplePartner-114\";\n            let isPartnerStateChanged_2 = false;\n            \n            // Act\n            globalStore.SubscribeToPartnerState(\"Test\", partnerAppName_1, (state) => {\n                isPartnerStateChanged_1 = true;\n            }, true);\n\n            globalStore.SubscribeToPartnerState(\"Test\", partnerAppName_2, (state) => {\n                isPartnerStateChanged_2 = true;\n            }, true);\n\n            globalStore.CreateStore(partnerAppName_1, dummyPartnerReducer, [], [\"Global\"], false, false);\n            globalStore.CreateStore(partnerAppName_2, dummyPartnerReducer, [], [\"Global\"], false, false);\n            globalStore.DispatchGlobalAction(\"Test\",\n                {\n                    type: \"Global\",\n                    payload: null\n                });\n\n\n            // Assert\n            expect(isPartnerStateChanged_1).toBeTruthy();\n            expect(isPartnerStateChanged_2).toBeTruthy();\n        });\n    });\n\n    describe(\"SubscribeToGlobalState\", () => {\n        let dummyPartnerReducer: Reducer<any, any> = (state: string = \"Default\", action: IAction<any>) => {\n            switch (action.type) {\n                case \"Local\": return \"Local\";\n                case \"Global\": return \"Global\";\n            }\n        };\n\n        it(\"Should invoke callback when global state changes due to partner change\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-40\";\n            let isGlobalStateChanged = false;\n            globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            // Act\n            globalStore.SubscribeToGlobalState(\"Test\", (state) => {\n                isGlobalStateChanged = true;\n            });\n            globalStore.DispatchGlobalAction(\"Test\",\n                {\n                    type: \"Global\",\n                    payload: null\n                });\n\n\n            // Assert\n            expect(isGlobalStateChanged).toBeTruthy();\n        });\n\n        it(\"Should invoke callback when global state changes due to partner change - CreateStore\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-40\";\n            let isGlobalStateChanged = false;\n            let store = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            // Act\n            globalStore.SubscribeToGlobalState(\"Test\", (state) => {\n                isGlobalStateChanged = true;\n            });\n            store.dispatch(\n                {\n                    type: \"Global\",\n                    payload: null\n                });\n\n\n            // Assert\n            expect(isGlobalStateChanged).toBe(true);\n        });\n\n        it(\"Should invoke callback when global state changes due to partner change - ResigerStore\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-41\";\n            let isGlobalStateChanged = false;\n            let store = createStore(dummyPartnerReducer);\n            globalStore.RegisterStore(partnerAppName, store, [\"Global\"], false);\n\n            // Act\n            globalStore.SubscribeToGlobalState(\"Test\", (state) => {\n                isGlobalStateChanged = true;\n            });\n            store.dispatch(\n                {\n                    type: \"Global\",\n                    payload: null\n                });\n\n\n            // Assert\n            expect(isGlobalStateChanged).toBe(true);\n        });\n    });\n\n    describe(\"AddSelectors\", () => {\n        let dummyPartnerReducer: Reducer<any, any> = (state: string = \"Default\", action: IAction<any>) => {\n            switch (action.type) {\n                case \"Local\": return \"Local\";\n                case \"Global\": return \"Global\";\n            }\n        };\n\n        it(\"Should successfully expose derived state API\", () => {\n            let partnerAppName = \"SamplePartner-2022\";\n            const partnerStore = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            // Arrange\n            globalStore.AddSelectors(partnerAppName, {\n                selectStateUpperCased: () => {\n                    const state = partnerStore.getState();\n                    return state.toUpperCase()\n                },\n                selectStateLowerCased: () => {\n                    const state = partnerStore.getState();\n                    return state.toLowerCase()\n                }\n            });\n\n            // Assert\n            const api = (<any>globalStore)._selectors[partnerAppName];\n            expect(api.selectStateUpperCased).toBeDefined();\n            expect(api.selectStateLowerCased).toBeDefined();\n        });\n\n        it(\"Should successfully merge derived state API\", () => {\n            let partnerAppName = \"SamplePartner-2023\";\n            const partnerStore = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            // Arrange\n            globalStore.AddSelectors(partnerAppName, {\n                selectStateUpperCased: () => {\n                    const state = partnerStore.getState();\n                    return state.toUpperCase()\n                },\n            });\n\n            globalStore.AddSelectors(partnerAppName, {\n                selectStateLowerCased: () => {\n                    const state = partnerStore.getState();\n                    return state.toLowerCase()\n                }\n            }, true);\n\n            // Assert\n            const api = (<any>globalStore)._selectors[partnerAppName];\n            expect(api.selectStateUpperCased).toBeDefined();\n            expect(api.selectStateLowerCased).toBeDefined();\n        });\n\n        it(\"Should not merge derived state API\", () => {\n            let partnerAppName = \"SamplePartner-2024\";\n            const partnerStore = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            // Arrange\n            globalStore.AddSelectors(partnerAppName, {\n                selectStateUpperCased: () => {\n                    const state = partnerStore.getState();\n                    return state.toUpperCase()\n                },\n            });\n\n            globalStore.AddSelectors(partnerAppName, {\n                selectStateLowerCased: () => {\n                    const state = partnerStore.getState();\n                    return state.toLowerCase()\n                }\n            });\n\n            // Assert\n            const api = (<any>globalStore)._selectors[partnerAppName];\n            expect(api.selectStateUpperCased).toBeDefined();\n            expect(api.selectStateLowerCased).toBeUndefined();\n        })\n    })\n\n    describe(\"SelectPartnerState\", () => {\n        let dummyPartnerReducer: Reducer<any, any> = (state: string = \"Default\", action: IAction<any>) => {\n            switch (action.type) {\n                case \"Local\": return \"Local\";\n                case \"Global\": return \"Global\";\n                default:\n                    return state;\n            }\n        };\n\n        it(\"Should be able to request a piece of derived state with valid key\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-2012\";\n            const partnerStore = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            globalStore.AddSelectors(partnerAppName, {\n                selectStateUpperCased: () => {\n                    const state = partnerStore.getState();\n                    return state.toUpperCase()\n                },\n                selectStateLowerCased: () => {\n                    const state = partnerStore.getState();\n                    return state.toLowerCase()\n                }\n            });\n\n            // Act\n            const partnerStateComputedUpperCase = globalStore.SelectPartnerState(partnerAppName, \"selectStateUpperCased\");\n            expect(partnerStateComputedUpperCase).toEqual(\"DEFAULT\");\n            const partnerStateComputedLowerCase = globalStore.SelectPartnerState(partnerAppName, \"selectStateLowerCased\");\n            expect(partnerStateComputedLowerCase).toEqual(\"default\");\n        });\n\n        it(\"Should be return undefined if derived state key is not defined\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-2013\";\n            const partnerStore = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            globalStore.AddSelectors(partnerAppName, {\n                selectStateUpperCased: () => {\n                    const state = partnerStore.getState();\n                    return state.toUpperCase()\n                },\n            });\n\n            // Act\n            const partnerStateComputedLowerCase = globalStore.SelectPartnerState(partnerAppName, \"selectStateLowerCased\");\n            expect(partnerStateComputedLowerCase).toEqual(undefined);\n        });\n\n        it(\"Should be return default value if derived state key is not defined\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-2013\";\n            const partnerStore = globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            globalStore.AddSelectors(partnerAppName, {\n                selectStateUpperCased: () => {\n                    const state = partnerStore.getState();\n                    return state.toUpperCase()\n                },\n            });\n\n            // Act\n            const partnerStateComputedLowerCase = globalStore.SelectPartnerState(partnerAppName, \"selectStateLowerCased\", \"I am a default value\");\n            expect(partnerStateComputedLowerCase).toEqual(\"I am a default value\");\n        });\n\n        it(\"Should throw error if partner has not exposed any derived\", () => {\n            // Arrange\n            let partnerAppName = \"SamplePartner-2014\";\n            let exceptionThrown = false;\n            globalStore.CreateStore(partnerAppName, dummyPartnerReducer, [], [\"Global\"], false, false);\n\n            try {\n                globalStore.SelectPartnerState(partnerAppName, \"selectStateLowerCased\");\n            } catch {\n                exceptionThrown = true;\n            }\n\n            // Assert\n            expect(exceptionThrown).toBeTruthy();\n        });\n    });\n});"
  },
  {
    "path": "test/middlewares/action.logger.tests.ts",
    "content": "import { IAction } from '../../src/actions/action.interface';\nimport { createStore, Reducer, applyMiddleware } from 'redux';\nimport { ActionLogger } from '../../src/middlewares/action.logger';\nimport { AbstractLogger as ILogger } from '../../src/common/abstract.logger';\n\ndescribe(\"ActionLoggerMiddleware\", () => {\n    let mockLogger = {\n        LogEvent: function (source, event, properties) { },\n        LogException: function (source, exception, properties) { }\n    } as ILogger;\n\n    it(\"Should get created\", () => {\n        expect(new ActionLogger(mockLogger)).toBeDefined();\n    });\n\n    it(\"Should log action start and action end\", () => {\n        // Arrange\n        spyOn(mockLogger, \"LogEvent\").and.callThrough();\n        let reducer: Reducer<any, any> = (state = null, action: IAction<any>): any => {\n            return action.payload;\n        };\n        let middleware = new ActionLogger(mockLogger).CreateMiddleware();\n        let store = createStore(reducer, applyMiddleware(middleware));\n\n        // Act\n        store.dispatch({\n            type: \"Action\",\n            payload: \"Dummy\",\n            logEnabled: true\n        } as IAction<any>);\n\n        // Assert\n        expect(mockLogger.LogEvent).toHaveBeenCalledTimes(2);\n    });\n\n    it(\"Should log action start and action failure\", () => {\n        // Arrange\n        spyOn(mockLogger, \"LogEvent\").and.callThrough();\n        spyOn(mockLogger, \"LogException\").and.callThrough();\n        let dummyError = new Error(\"Dummy Error\");\n        let reducer: Reducer<any, any> = (state = null, action: IAction<any>): any => {\n            if (action.type === \"FaultAction\") {\n                throw dummyError;\n            }\n            return state;\n        };\n        let middleware = new ActionLogger(mockLogger).CreateMiddleware();\n        let store = createStore(reducer, applyMiddleware(middleware));\n\n        // Act\n        try {\n            store.dispatch({\n                type: \"FaultAction\",\n                payload: \"Dummy\",\n                logEnabled: true\n            });\n            expect(true).toBeFalsy(); // Control should not come here\n        }\n        catch (error) {\n            // Assert\n            expect(error.message).toBe(dummyError.message);\n            expect(mockLogger.LogEvent).toHaveBeenCalledTimes(2);\n            expect(mockLogger.LogException).toHaveBeenCalledTimes(1);\n        }\n    });\n\n    it(\"Should not throw exception when logger fails\", () => {\n        // Arrange\n        spyOn(mockLogger, \"LogEvent\").and.throwError(\"Some dummy error\");\n        let reducer: Reducer<any, any> = (state = null, action: IAction<any>): any => {\n            return action.payload;\n        };\n        let middleware = new ActionLogger(mockLogger).CreateMiddleware();\n        let store = createStore(reducer, applyMiddleware(middleware));\n\n        // Act\n        store.dispatch({\n            type: \"Action\",\n            payload: \"Dummy\",\n            logEnabled: true\n        });\n\n        // Assert\n        expect(mockLogger.LogEvent).toHaveBeenCalledTimes(2);\n    });\n});"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"module\": \"ES6\",\n        \"sourceMap\": true,\n        \"target\": \"es5\",\n        \"allowJs\": false,\n        \"outDir\": \"lib\",\n        \"declaration\": true,\n        \"moduleResolution\": \"node\",\n        \"skipLibCheck\": true,\n        \"lib\": [\"es2017\", \"DOM\"],\n        \"downlevelIteration\": true\n    },\n    \"typeRoots\": [\n        \"node_modules/@types\"\n    ],\n    \"files\": [\n        \"index.ts\"\n    ],\n    \"exclude\": [\n        \"node_modules/**/*\",\n        \"**/*.spec.ts\"\n    ]\n}"
  }
]