[
  {
    "path": ".editorconfig",
    "content": "# http://editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.md]\ninsert_final_newline = false\ntrim_trailing_whitespace = false"
  },
  {
    "path": ".firebaserc",
    "content": "{\n  \"projects\": {\n    \"default\": \"fuse-angular-universal-s-67402\"\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.DS_Store\ndist\n.env\n.fusebox\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Typescript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\ndist/\n\n.DS_Store\n\ntest-report.xml\ntest-results.xml\n\nngc\n.ngc\n.aot\naot\n.e2e\n\ndocumentation\n\nsrc/config.json"
  },
  {
    "path": ".stylelintrc",
    "content": "{\n  \"rules\": {\n    \"block-no-empty\": null,\n    \"color-no-invalid-hex\": true,\n    \"comment-empty-line-before\": [\n        \"always\", {\n            \"ignore\": [\"stylelint-commands\", \"between-comments\"]\n        }\n    ],\n    \"declaration-colon-space-after\": \"always\",\n    \"indentation\": 2,\n    \"max-empty-lines\": 2,\n    \"unit-whitelist\": [\"em\", \"rem\", \"%\", \"px\", \"s\", \"ms\", \"vw\", \"vh\", \"deg\"]\n  }\n}\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\n    \"msjsdiag.debugger-for-chrome\",\n    \"michelemelluso.code-beautifier\",\n    \"eg2.tslint\",\n    \"eamodio.gitlens\"\n  ]\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "// Place your settings in this file to overwrite default and user settings.\n{\n  \"editor.tabSize\": 2,\n  \"beautify.tabSize\": 2,\n  \"editor.insertSpaces\": true,\n  \"autoimport.filesToScan\": \"src/**/*.{ts,tsx}\",\n  \"typescript.tsdk\": \"node_modules/typescript/lib\"\n}"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at patrickmichalina@mac.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nWe love pull requests from everyone. By participating in this project, you\nagree to abide by our [code of conduct].\n\n[code of conduct]: https://github.com/patrickmichalina/fusebox-angular-universal-starter/blob/master/CODE_OF_CONDUCT.md\n\nFork, then clone the repo:\n\n    git clone https://github.com/patrickmichalina/fusebox-angular-universal-starter\n\nMake sure the tests pass:\n\n    npm test\n\nMake your change. Add tests for your change. Make the tests pass:\n\n    npm test\n\nPush to your fork and [submit a pull request][pr].\n\n[pr]: https://github.com/patrickmichalina/fusebox-angular-universal-starter/compare\n\nAt this point you're waiting on us. We like to at least comment on pull requests\nwithin three business days (and, typically, one business day). We may suggest\nsome changes or improvements or alternatives.\n\nSome things that will increase the chance that your pull request is accepted:\n\n* Write tests.\n* Write a [good commit message][commit].\n\n[commit]: https://conventionalcommits.org\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Patrick Michalina\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Procfile",
    "content": "web: node dist/server.js --prod"
  },
  {
    "path": "README.md",
    "content": "## This project is archived and an improved version has been moved to [https://github.com/patrickmichalina/fusing-angular](https://github.com/patrickmichalina/fusing-angular)\n\n# Introduction\n\n[![CircleCI](https://circleci.com/gh/patrickmichalina/fusebox-angular-universal-starter.svg?style=shield)](https://circleci.com/gh/patrickmichalina/fusebox-angular-universal-starter)\n[![codecov](https://codecov.io/gh/patrickmichalina/fusebox-angular-universal-starter/branch/master/graph/badge.svg)](https://codecov.io/gh/patrickmichalina/fusebox-angular-universal-starter)\n[![dependencies Status](https://david-dm.org/patrickmichalina/fusebox-angular-universal-starter/status.svg)](https://david-dm.org/patrickmichalina/fusebox-angular-universal-starter)\n[![devDependencies Status](https://david-dm.org/patrickmichalina/fusebox-angular-universal-starter/dev-status.svg)](https://david-dm.org/patrickmichalina/fusebox-angular-universal-starter?type=dev)\n[![Greenkeeper badge](https://badges.greenkeeper.io/patrickmichalina/fusebox-angular-universal-starter.svg)](https://greenkeeper.io/)\n[![Angular Style Guide](https://mgechev.github.io/angular2-style-guide/images/badge.svg)](https://angular.io/styleguide)\n[![Fusebox-bundler](https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/fusebox-angular-universal-starter/Lobby)\n\nProvides an extremely fast seed project for the development of Angular Universal (isomorphic) projects. Check out the [live app](https://angular.patrickmichalina.com)\n\n# Project Goals\nThis starter project is designed to get a basic application up and running with basic implementations of core features most applications need. It uses [Firebase](https://firebase.google.com) for the authentication and the database layers.\n\nNote: Firebase doesn't have official support for Angular Universal at this time. However, we have implemented some of the basic features to render and transfer server state to the browser. Once an official support is released, we will use that.\n\n# Features\n- [x] [Angular](https://github.com/angular/angular/blob/master/CHANGELOG.md) as the application framework\n- [x] [Angular Material](https://material.angular.io) as the UI language and component library\n- [x] [Angular Flex Layout](https://github.com/angular/flex-layout) for dynamic responsive layouts\n- [x] [FuseBox](http://fuse-box.org) as the TypeScript/JavaScript bundler\n- [x] [Jest](https://facebook.github.io/jest) for unit and component testing\n- [x] [Nightmare](https://github.com/segmentio/nightmare) for UI testing\n- [x] [Sparky](http://fuse-box.org/page/sparky) as the task runner\n- [x] Fully typed build tools using [TypeScript](https://www.typescriptlang.org)\n- [x] Production and development builds\n- [x] Manage your type definitions using [@types](https://www.npmjs.com/~types)\n- [x] Simple [Heroku](https://www.heroku.com) Deployment\n- [x] HttpStateTransfer for caching server responses on client boostrap (no flickering)\n- [x] CDN asset configuration\n- [x] Automatic sitemap generation\n- [x] [SCSS](http://sass-lang.com) support for professional grade CSS management\n- [x] [Brotli compression](https://github.com/google/brotli) with [gzip](http://www.gzip.org) fallback\n- [x] [CircleCI](https://circleci.com) unit testing support \n- [x] Full favicon icon generation for multiple devices derived from a single seed image\n- [x] SEO support for Title and Meta tags\n- [x] OG (Open Graph) tags for social sharing\n- [x] Simple Ad-Blocker detection service\n- [x] Vendor-agnostic analytics using [angulartics2](https://github.com/angulartics/angulartics2)\n- [x] Generic token based Authentication service with [JWT](https://jwt.io) cookie support.\n- [x] Both Client and Server build tasks\n- [x] Hot Module Reloading for faster browser reloads during client development\n- [ ] [Ahead-of-Time](https://angular.io/guide/aot-compiler) (AOT) compilation support\n- [x] [angular-tslint-rules](https://github.com/fulls1z3/angular-tslint-rules) as configuration preset for [TSLint](https://github.com/palantir/tslint) and [codelyzer](https://github.com/mgechev/codelyzer).\n- [x] Automatic static file cache invalidation\n- [x] [Lazy Loaded](https://angular-2-training-book.rangle.io/handout/modules/lazy-loading-module.html) modules\n- [x] Analyze bundle sizes by using [source-map-explorer](https://github.com/danvk/source-map-explorer)\n- [x] Support for [Angular Mobile Toolkit](https://mobile.angular.io) (Service Worker)\n- [x] [Tree-Shaking](https://angular.io/guide/aot-compiler) for production builds\n\nBuilt by [AngularUniversal.com](https://www.angularuniversal.com). For additional help please checkout our [services](https://www.angularuniversal.com/services)\n\n# Quick Start\n\n**Note that we strongly recommend node >= v7.0.0 and npm >= 4.0.0.**\n\nTo start the seed use:\n\n\n```bash\n$ git clone --depth 1 https://github.com/patrickmichalina/fusebox-angular-universal-starter\n$ cd fusebox-angular-universal-starter\n\n# Add Firebase Admin values to your project\n# in a \".env\" file for local deveopment\n# in environment variables for other environments\nSee [Environment Variables](#environment-variables)\n\n# install the project's dependencies\n$ npm install\n\n# start the Angular Universal server\n$ npm start\n\n# start the server while watching tests and updating app documentation\n$ npm run start.deving\n\n# start the Angular Universal server w/ AOT build step\n$ npm run start.aot\n# can also be called passing the parameter --aot\n# npm start --aot\n\n# start the application in Client only mode (not server driven), with HMR enabled\n$ npm run start.spa\n\n# start the server in production mode\n$ npm run start.prod\n\n```\n\n# Table of Contents\n* [Testing](#testing)\n* [Configuration](#configuration)\n* [Environment Variables](#environment-variables)\n* [@Types](#types)\n* [File Structure](#file-structure)\n* [Change Log](#change-log)\n* [License](#license)\n\n# Bundling\nCheckout how blazing fast bundling can be using FuseBox!\n\n![fuse-box](https://thumbs.gfycat.com/WarmheartedUnfinishedHind-small.gif)\n\n# Testing\n```bash\n# single test run\n$ npm test \n\n# single test with coverage results\n$ npm run test.coverage\n\n# continuous testing\n$ npm run test.watch\n\n# e2e testing (primarilly for CI builds)\n$ npm run test.e2e.ci\n\n# continuous e2e testing\n$ npm run test.e2e.watch\n```\n\n![jest](https://thumbs.gfycat.com/CooperativeMammothEland-small.gif)\n\n# Configuration\n```shell\nComing Soon\n```\n\n# @Types\nWhen you include a module that doesn't include typings, you can include external type definitions using the npm @types repo.\n\ni.e, to have youtube api support, run this command in terminal: \n```shell\nnpm i -D @types/youtube @types/gapi @types/gapi.youtube\n``` \n\n# Environment Variables\n\n```bash\n# it is important to set the following environmental variables on your CI server (examples below)\nHOST : angular.patrickmichalina.com # the root origin of your application server\nCI : true \n\n# for Heroku Builds\nHEROKU : true # to build on heroku, ssl settings are setup using this flag\nNPM_CONFIG_PRODUCTION : false # to download all depenedencies on Heroku, including devDependencies\n\n# Firebase Admin SDK\nFB_SERVICE_ACCOUNT_PRIVATE_KEY_ID: Some_Secret\nFB_SERVICE_ACCOUNT_PRIVATE_KEY: Some_Secret\nFB_AUTH_KEY: Some_Secret\n\n```\n\n## File Structure\nWe use the component approach in our starter. This is the standard for developing Angular apps and a great way to ensure maintainable code\n```\nfusebox-angular-universal-starter/\n ├──.fusebox/                       * working folder for the js bundler\n ├──.vscode/                        * Visual Studio Code settings \n ├──coverage/                       * stores recent reporting of test coverage\n ├──dist/                           * output files that represent the bundled application and its dependencies\n ├──node_modules/                   * project depdendencies\n |\n ├──src/\n |   ├──client/                     * client Angular code. (most your work should be done here)\n |   └──server/                     * server code\n |\n ├──tools/\n |   ├──config/\n |   |   ├──app.config.ts          * configuration interface for the web applications\n |   |   ├──build.config.ts        * configuration values for the build system\n |   |   ├──build.interfaces.ts    * configuration interfaces for the build system\n |   |   └──build.transformer.ts   * build system config transform helper\n |   |\n |   ├──env/\n |   |   ├──base.ts                * base app configuration \n |   |   ├──dev.ts                 * dev app configuration\n |   |   ├──**.ts                  * arbitrary configuration called via the flag --env-config\n |   |   └──prod.ts                * production app configuration\n |   |\n |   ├──scripts/                   * misc. build helper scripts\n |   ├──tasks/                     * Sparky tasks\n |   ├──test/                      * testing system related configuration\n |   └──web/                       * static assets used for common web functions\n |\n ├──.gitignore                     * GIT settings\n ├──circl.yml                      * CirclCI configuration file\n ├──CODE_OF_CONDUCT.md             * standard code of conduct information\n ├──codecov.yml                    * codecov.io configuration file\n ├──CONTRIBUTING.md                * standard contributor information\n ├──fuse.ts                        * FuseBox entry point\n ├──LICENSE                        * software license\n ├──package-lock.json              * what npm uses to manage it's dependencies\n ├──package.json                   * what npm uses to manage it's dependencies\n ├──Procfile                       * Heroku deployment setting\n ├──README.md                      * project information\n ├──test-report.xml                * JUNIT test results\n ├──tsconfig-aot.json              * typescript config for AOT build using @angular-cli (ngc)\n └──tsconfig.json                  * typescript config\n```\n\n# Change Log\n\nYou can follow the [Angular change log here](https://github.com/angular/angular/blob/master/CHANGELOG.md).\n\n# License\n\n[MIT](https://github.com/patrickmichalina/fusebox-angular-universal-starter/blob/master/LICENSE)\n"
  },
  {
    "path": "app.json",
    "content": "{\n  \"name\": \"fusebox-angular-universal-star\",\n  \"env\": {\n    \"CI\": {\n      \"required\": true\n    },\n    \"HOST\": {\n      \"required\": true\n    },\n    \"HEROKU\": {\n      \"required\": true\n    },\n    \"NODE_MODULES_CACHE\": {\n      \"required\": true\n    },\n    \"NPM_CONFIG_PRODUCTION\": {\n      \"required\": true\n    },\n    \"HEROKU_APP_NAME\": {\n      \"required\": true\n    },\n    \"HEROKU_PARENT_APP_NAME\": {\n      \"required\": true\n    },\n    \"FB_SERVICE_ACCOUNT_PRIVATE_KEY\": {\n      \"required\": true\n    },\n    \"FB_SERVICE_ACCOUNT_PRIVATE_KEY_ID\": {\n      \"required\": true\n    },\n    \"FB_AUTH_KEY\": {\n      \"required\": true\n    }\n  }\n}"
  },
  {
    "path": "circle.yml",
    "content": "machine:\n  node:\n    version: 9.4.0\n  npm:\n    version: 5.6.0\n\ngeneral:\n  artifacts:\n    - ./coverage\n    - ./dist\n\ntest:\n  override:\n    - npm run lint\n    - npm run gen.config\n    - npm run test.ci.before\n    - node_modules/.bin/jest --ci --updateSnapshot --runInBand --coverage:\n        environment:\n          TEST_REPORT_PATH: $CIRCLE_TEST_REPORTS\n          TEST_REPORT_FILENAME: test-results.xml\n    - npm run test.ci.after\n\n  post:\n    - bash <(curl -s https://codecov.io/bash)\n    - npm run semantic-release || true\n\ndeployment:\n  production:\n    branch: master\n    heroku:\n      appname: fusebox-angular-universal-star"
  },
  {
    "path": "codecov.yml",
    "content": "codecov:\n  allow_coverage_offsets: false\n  ignore:\n    - \"tools\""
  },
  {
    "path": "data/security.rules.json",
    "content": "{\n  \"rules\": {\n    \"site-settings\": {\n      \".read\": \"true\",\n      \".write\": \"root.child('users').child(auth.uid).child('roles/superadmin').val() === true\"\n    },\n    \"posts\": {\n      \".read\": \"true\",\n      \".write\": \"true\"\n    },\n    \"pages\": {\n      \".read\": \"true\",\n      \".write\": \"true\",\n      \"blog\": {\n        \"$document\": {\n          \".read\": \"true\",\n          \".write\": \"true\"\n        }\n      }\n    },\n    \"users\": {\n      \".read\": \"auth != null && root.child('users').child(auth.uid).child('roles/superadmin').val() === true\",\n      \"$uid\": {\n        \".read\": \"auth != null && auth.uid == $uid || root.child('users').child(auth.uid).child('roles/superadmin').val() === true\",\n        \".write\": \"auth != null && auth.uid == $uid || root.child('users').child(auth.uid).child('roles/superadmin').val() === true\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "data/seed.settings.json",
    "content": "{\n  \"host\": \"http://localhost:8000\",\n  \"og\": {\n    \"title\": \"Fusebox Angular Universal Starter\",\n    \"description\": \"Seed project for Angular Universal apps featuring Server-Side Rendering (SSR), FuseBox, dev/prod builds, Brotli/Gzip, SCSS, favicon generation, @types, unit testing w/ Jest, and sitemap generator. Created by Patrick Michalina\",\n    \"image\": \"https://d3anl5a3ibkrdg.cloudfront.net/assets/favicons/android-chrome-512x512.png\",\n    \"type\": \"website\",\n    \"locale\": \"en_US\"\n  },\n  \"firebase\": {\n    \"appName\": \"fuse-angular-universal-starter\",\n    \"config\": {\n      \"apiKey\": \"AIzaSyDfE1owJZCbvasXieCKjMoGZddRhqcp7RM\",\n      \"authDomain\": \"fuse-angular-universal-s-67402.firebaseapp.com\",\n      \"databaseURL\": \"https://fuse-angular-universal-s-67402.firebaseio.com\",\n      \"projectId\": \"fuse-angular-universal-s-67402\",\n      \"storageBucket\": \"fuse-angular-universal-s-67402.appspot.com\",\n      \"messagingSenderId\": \"883416191164\"\n    }\n  },\n  \"assets\": {\n    \"userAvatarImage\": \"https://firebasestorage.googleapis.com/v0/b/fuse-angular-universal-s-67402.appspot.com/o/avatar.jpg?alt=media&token=9a153021-6e12-460b-9d87-40c2eed02c82\"\n  },\n  \"tokens\": {\n    \"facebookAppId\": \"117309532219749\"\n  },\n  \"injections\": [\n    {\n      \"inHead\": true,\n      \"element\": \"link\",\n      \"attributes\": {\n        \"href\": \"/manifest.json\",\n        \"rel\": \"manifest\"\n      }\n    },\n    {\n      \"inHead\": true,\n      \"element\": \"meta\",\n      \"attributes\": {\n        \"name\": \"theme-color\",\n        \"content\": \"#2196F3\"\n      }\n    },\n    {\n      \"inHead\": false,\n      \"element\": \"link\",\n      \"attributes\": {\n        \"href\": \"https://fonts.googleapis.com/css?family=Roboto\",\n        \"rel\": \"stylesheet\",\n        \"type\": \"text/css\",\n        \"media\": \"none\",\n        \"onload\": \"if(media!=='all')media='all'\"\n      }\n    },\n    {\n      \"inHead\": true,\n      \"element\": \"meta\",\n      \"attributes\": {\n        \"name\": \"google-site-verification\",\n        \"content\": \"RW-hcjXEgPMoy2NF8pTl8IEzP8gnj3cEZ6aF1HDUiOc\"\n      }\n    },\n    {\n      \"inHead\": true,\n      \"element\": \"script\",\n      \"value\": \"window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;ga('create', 'UA-107089312-2', 'auto');\",\n      \"attributes\": {\n        \"type\": \"text/javascript\"\n      }\n    },\n    {\n      \"inHead\": false,\n      \"element\": \"script\",\n      \"attributes\": {\n        \"async\": \"true\",\n        \"type\": \"text/javascript\",\n        \"src\": \"https://www.google-analytics.com/analytics.js\"\n      }\n    }\n  ],\n  \"i18n\": {\n    \"en\": {\n      \"about\": {\n        \"title\": \"About\",\n        \"description\": \"See contact information and details about the Angular Universal seed at angular.patrickmichalina.com\"\n      },\n      \"account\": {\n        \"title\": \"Account\",\n        \"description\": \"Update your profile and account settings\"\n      },\n      \"dashboard\": {\n        \"title\": \"Dashboard\",\n        \"description\": \"An example dashboard page\"\n      },\n      \"admin\": {\n        \"title\": \"Admin\",\n        \"description\": \"For application management\"\n      },\n      \"changelog\": {\n        \"title\": \"Changelog\",\n        \"description\": \"Version history of the application\"\n      },\n      \"home\": {\n        \"title\": \"Home\",\n        \"description\": \"A fullstack angular starter framework to quickly build mid-large scale web applications\"\n      },\n      \"login\": {\n        \"title\": \"Login\",\n        \"description\": \"Login to your account now.\"\n      },\n      \"logout\": {\n        \"title\": \"Logged Out\",\n        \"description\": \"Come back again!\"\n      },\n      \"search\": {\n        \"title\": \"Search\",\n        \"description\": \"Search for angular related projects on github, to showcase the flicker-free http state transfer of an Angular isomorphic application.\"\n      },\n      \"signup\": {\n        \"title\": \"Signup\",\n        \"description\": \"Sign up for an account with us. Create an account to start doing cool things with our application. It\\\"s easy to register\"\n      },\n      \"not-found\": {\n        \"title\": \"Not Found\",\n        \"description\": \"The page you requested can not be found.\"\n      }\n    },\n    \"jp\": {\n      \"home\": {\n        \"title\": \"こんにちは\",\n        \"description\": \"\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "data/seed.superusers.json",
    "content": "{\n  \"9BthHoxklbgCzP2vlFI3ZrNggrl1\": {\n    \"roles\": {\n      \"superadmin\": true,\n      \"admin\": true\n    }\n  }\n}"
  },
  {
    "path": "docs/angular-universal.md",
    "content": "# Universal \"Gotchas\"\n\n> When building Universal components in Angular 2 there are a few things to keep in mind.\n\n - **`window`**, **`document`**, **`navigator`**, and other browser types - _do not exist on the server_ - so using them, or any library that uses them (jQuery for example) will not work. You do have some options, if you truly need some of this functionality:\n    - If you need to use them, consider limiting them to only your client and wrapping them situationally. You can use the Object injected using the PLATFORM_ID token to check whether the current platform is browser or server. \n    \n    ```\n     import { PlatformService } from '../services';\n     \n     constructor(private platformService: PlatformService) { ... }\n     \n     ngOnInit() {\n       if (this.platformService.isBrowser) {\n          // Client only code.\n          ...\n       }\n       if (this.platformService.isServer) {\n         // Server only code.\n         ...\n       }\n     }\n    ```\n    \n     - Try to *limit or* **avoid** using **`setTimeout`**. It will slow down the server-side rendering process. Make sure to remove them in the [`ngOnDestroy`](https://angular.io/docs/ts/latest/api/core/index/OnDestroy-class.html) method of your Components.\n   - Also for RxJs timeouts, make sure to _cancel_ their stream on success, for they can slow down rendering as well.\n - **Don't manipulate the nativeElement directly**. Use the _Renderer2_. We do this to ensure that in any environment we're able to change our view.\n```\nconstructor(element: ElementRef, renderer: Renderer2) {\n  renderer.setStyle(element.nativeElement, 'font-size', 'x-large');\n}\n```\n - The application runs XHR requests on the server & once again on the Client-side (when the application bootstraps)\n    - Use a cache that's transferred from server to client (TODO: Point to the example)\n - Know the difference between attributes and properties in relation to the DOM.\n - Keep your directives stateless as much as possible. For stateful directives, you may need to provide an attribute that reflects the corresponding property with an initial string value such as url in img tag. For our native element the src attribute is reflected as the src property of the element type HTMLImageElement."
  },
  {
    "path": "docs/api-server.md",
    "content": "# API\nThis project includes an example API that can be split off into a standalone server. The goal of this API is to provide a set of commonly used endpoints that could apply to most modern web applications. This inclides things like:\n\n- Site Settings\n- Image Uploads\n- Tokens (login/logout)\n\n# Stack\n- [routing-controllers](https://github.com/pleerock/routing-controllers) Create structured, declarative and beautifully organized class-based controllers with heavy decorators usage in Express / Koa using TypeScript and Routing Controllers Framework.\n- [swagger-jsdoc]() Generates swagger doc based on JSDoc.\n- [swagger-ui-express]() Adds middleware to your express app to serve the Swagger UI bound to your Swagger document. This acts as living documentation for your API hosted from within your app.\n- [typedi]() Dependency Injection for TypeScript.\n\n# Structure\nThe API has an opinionted structure in order to better sperate the various layers of the applications:\n\n- Controllers: gateway to the outside world, where users actually interact with the server\n- Services: internal tools that can be used in controllers, other services, or anywhere in the API\n- Repositories: data stores that are normally accessed via services\n- Middlewares: express server plugins\n\n## Removing from the project\n... TODO"
  },
  {
    "path": "docs/openid-server.md",
    "content": "# Identity Server\nThis project includes an OpenID certifiec identity server that can be split off into a standalone OpenID server.\n\n## Removing from the project\n... TODO"
  },
  {
    "path": "e2e-spec.json",
    "content": "{\n  \"moduleFileExtensions\": [\n    \"ts\",\n    \"js\",\n    \"html\"\n  ],\n  \"testRegex\": \"(/__tests__/.*|\\\\.(test|e2e-spec))\\\\.(ts|js)$\",\n  \"transform\": {\n    \"^.+\\\\.tsx?$\": \"<rootDir>/node_modules/ts-jest/preprocessor.js\"\n  },\n  \"globals\": {\n    \"__TRANSFORM_HTML__\": true,\n    \"__process_env__\": {},\n    \"ts-jest\": {\n      \"tsConfigFile\": \"tsconfig.json\"\n    }\n  }\n}"
  },
  {
    "path": "firebase.json",
    "content": "{\n  \"database\": {\n    \"rules\": \"data/security.rules.json\"\n  }\n}\n"
  },
  {
    "path": "fuse.ts",
    "content": "import { Ng2TemplatePlugin } from 'ng2-fused';\nimport { argv } from 'yargs';\nimport { BUILD_CONFIG, ENV_CONFIG_INSTANCE, isProdBuild, typeHelper } from './tools/config/build.config';\nimport { WebIndexPlugin } from './tools/plugins/web-index';\nimport { init, reload, active } from 'browser-sync';\nimport {\n  EnvPlugin,\n  FuseBox,\n  HTMLPlugin,\n  JSONPlugin,\n  RawPlugin,\n  SassPlugin,\n  Sparky,\n  QuantumPlugin\n} from 'fuse-box';\nimport './tools/tasks';\n\nconst death = require('death')\nconst isReachable = require('is-reachable');\nconst isAot = argv.aot;\nconst isBuildServer = argv.ci;\nconst baseEntry = isAot ? 'main.aot' : 'main';\nconst appBundleName = `js/app`;\nconst vendorBundleName = `js/vendors`;\nconst mainEntryFileName = isProdBuild ? `${baseEntry}-prod` : `${baseEntry}`;\nconst vendorBundleInstructions = ` ~ client/${mainEntryFileName}.ts`;\nconst appBundleInstructions = ` !> [client/${mainEntryFileName}.ts]`;\nconst serverBundleInstructions = ' > [server/server.ts]';\n\nif (isProdBuild) typeHelper()\n\nconst baseOptions = {\n  homeDir: './src',\n  output: `${BUILD_CONFIG.outputDir}/$name.js`,\n  plugins: [\n    Ng2TemplatePlugin(),\n    ['*.component.html', RawPlugin()],\n    ['*.component.scss',\n      SassPlugin({ indentedSyntax: false, importer: true, sourceMap: false, outputStyle: 'compressed' } as any), RawPlugin()],\n    JSONPlugin(),\n    HTMLPlugin({ useDefault: false })\n  ]\n}\n\nconst appOptions = {\n  ...baseOptions,\n  hash: isProdBuild,\n  plugins: [\n    ...baseOptions.plugins,\n    WebIndexPlugin({\n      bundles: [vendorBundleName, appBundleName],\n      startingDocumentPath: 'dist/index.html',\n      appElement: {\n        name: 'pm-app',\n        innerHTML: 'Loading....'\n      },\n      additionalDeps: BUILD_CONFIG.dependencies as any[]\n    }),\n    isProdBuild && QuantumPlugin({\n      target: \"universal\",\n      warnings: false,\n      uglify: true,\n      treeshake: true,\n      bakeApiIntoBundle: vendorBundleName,\n      processPolyfill: true\n    })\n  ]\n}\n\nconst serverOptions = {\n  ...baseOptions,\n  target: 'server',\n  sourceMaps: false,\n  plugins: [\n    EnvPlugin(ENV_CONFIG_INSTANCE),\n    ...baseOptions.plugins\n  ]\n}\n\nSparky.task('build.universal', () => {\n  const fuseApp = FuseBox.init(appOptions as any);\n  const fuseServer = FuseBox.init(serverOptions as any);\n  const path = isAot ? 'client/.aot/src/client/app' : 'client/app';\n  const serverBundle = fuseServer.bundle('server').instructions(serverBundleInstructions);\n  const vendorBundle = fuseApp.bundle(`${vendorBundleName}`).instructions(vendorBundleInstructions)\n  const appBundle = fuseApp\n    .bundle(appBundleName)\n    .splitConfig({ dest: 'js/modules' })\n    .instructions(`${appBundleInstructions} + [${path}/**/!(*.spec|*.e2e-spec|*.ngsummary|*.snap).*]`)\n\n  if (!isBuildServer && !argv['build-only']) {\n    const proxy = `${BUILD_CONFIG.host}:${BUILD_CONFIG.port}`\n    vendorBundle.watch()\n    appBundle.watch()\n    return fuseApp.run().then(() => {\n      serverBundle.watch('src/**').completed(proc => {\n        typeHelper(false, false)\n        proc.start();\n        isReachable(proxy).then(() => {\n          active\n            ? reload()\n            : init({\n              port: BUILD_CONFIG.browserSyncPort,\n              proxy\n            })\n        })\n        death(function (signal: any, err: any) {\n          proc.kill()\n          process.exit()\n        })\n      })\n      return fuseServer.run()\n    });\n  } else {\n    return fuseApp.run().then(() => fuseServer.run())\n  }\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"fusebox-angular-universal-starter\",\n  \"version\": \"0.0.0-development\",\n  \"description\": \"Angular Universal starter build pipeline using FuseBox\",\n  \"license\": \"MIT\",\n  \"homepage\": \"https://angular.patrickmichalina.com\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/patrickmichalina/fusebox-angular-universal-starter\"\n  },\n  \"bugs\": {\n    \"email\": \"patrickmichalina@mac.com\",\n    \"url\": \"https://github.com/patrickmichalina/fusebox-angular-universal-starter/issues\"\n  },\n  \"author\": {\n    \"email\": \"patrickmichalina@mac.com\",\n    \"name\": \"Patrick Michalina\",\n    \"url\": \"https://patrickmichalina.com\"\n  },\n  \"readme\": \"https://github.com/patrickmichalina/fusebox-angular-universal-starter/blob/master/README.md\",\n  \"scripts\": {\n    \"test\": \"npm run gen.config && jest\",\n    \"test.ci\": \"npm run gen.config && jest --ci --updateSnapshot\",\n    \"test.update\": \"npm run gen.config && jest --updateSnapshot\",\n    \"test.coverage\": \"npm run gen.config && jest --coverage\",\n    \"test.watch\": \"npm run gen.config && jest --watch\",\n    \"test.ci.before\": \"greenkeeper-lockfile-update\",\n    \"test.ci.after\": \"greenkeeper-lockfile-upload\",\n    \"test.e2e\": \"npm run gen.config && jest -c e2e-spec.json\",\n    \"test.e2e.ci\": \"npm run gen.config && npm run start.e2e && concurrently --kill-others --success first \\\"node dist/server.js --e2e --port 8000\\\" \\\"npm run test.e2e\\\"\",\n    \"test.e2e.watch\": \"npm run gen.config && npm run start.e2e && concurrently --kill-others --success first --raw \\\"node dist/server.js --e2e\\\" \\\"jest -c e2e-spec.json --watch\\\" \",\n    \"start.mock.api\": \"json-server ./tools/test/db.json\",\n    \"start.e2e\": \"ts-node fuse.ts serve --build-type prod --env-config e2e --ci --port 8000\",\n    \"start\": \"ts-node fuse.ts serve --build-type dev --env-config dev\",\n    \"start.deving\": \"concurrently \\\"npm run start\\\" \\\"npm run test.watch\\\" \\\"npm run compodoc\\\"\",\n    \"start.spa\": \"ts-node fuse.ts serve --build-type dev --spa\",\n    \"start.prod\": \"ts-node fuse.ts serve --build-type prod --env-config=prod\",\n    \"start.prod.ci\": \"CI=true ts-node fuse.ts serve --build-type prod --env-config prod --ci --ci-vars\",\n    \"heroku-postbuild\": \"npm run start.prod.ci\",\n    \"source.tree.app\": \"ts-node fuse.ts build --build-only && source-map-explorer dist/js/app.js\",\n    \"source.tree.server\": \"ts-node fuse.ts build --build-only &&  source-map-explorer dist/server.js\",\n    \"compodoc\": \"./node_modules/.bin/compodoc -p tsconfig.json -t -s -w -o -r 8081 --theme readthedocs\",\n    \"prepush\": \"npm run lint && npm test\",\n    \"postmerge\": \"./tools/scripts/post-merge.sh || true\",\n    \"semantic-release\": \"semantic-release pre && npm publish && semantic-release post\",\n    \"lint\": \"ts-node fuse.ts lint --build-type prod\",\n    \"postinstall\": \"./node_modules/.bin/ts-node fuse.ts clean\",\n    \"firebase\": \"./node_modules/.bin/firebase\",\n    \"firebase.update\": \"./node_modules/.bin/firebase deploy && npm run firebase.seed.superusers\",\n    \"firebase.seed.site-settings\": \"./node_modules/.bin/firebase database:update /site-settings data/seed.settings.json -y\",\n    \"firebase.seed.superusers\": \"./node_modules/.bin/firebase database:update /users data/seed.superusers.json -y\",\n    \"gen.config\": \"ts-node fuse.ts config\"\n  },\n  \"devDependencies\": {\n    \"@compodoc/compodoc\": \"1.1.2\",\n    \"@pwa/manifest\": \"1.0.0\",\n    \"@types/body-parser\": \"1.16.8\",\n    \"@types/browser-sync\": \"0.0.39\",\n    \"@types/bunyan\": \"1.8.4\",\n    \"@types/clean-css\": \"3.4.30\",\n    \"@types/compression\": \"0.0.36\",\n    \"@types/cookie-parser\": \"1.4.1\",\n    \"@types/dotenv\": \"4.0.3\",\n    \"@types/express\": \"4.11.1\",\n    \"@types/getos\": \"3.0.0\",\n    \"@types/glob\": \"5.0.35\",\n    \"@types/html-minifier\": \"3.5.2\",\n    \"@types/jest\": \"22.2.3\",\n    \"@types/js-cookie\": \"2.1.0\",\n    \"@types/jsdom\": \"11.0.4\",\n    \"@types/memory-cache\": \"0.2.0\",\n    \"@types/mkdirp\": \"0.5.2\",\n    \"@types/ms\": \"0.7.30\",\n    \"@types/nightmare\": \"2.10.3\",\n    \"@types/node-sass\": \"3.10.32\",\n    \"@types/object-hash\": \"1.2.0\",\n    \"@types/quill\": \"1.3.6\",\n    \"@types/request\": \"2.47.0\",\n    \"@types/serve-favicon\": \"2.2.30\",\n    \"@types/stacktrace-js\": \"0.0.32\",\n    \"@types/string-hash\": \"1.1.1\",\n    \"@types/supertest\": \"2.0.4\",\n    \"@types/ws\": \"4.0.2\",\n    \"@types/yargs\": \"11.0.0\",\n    \"angular-tslint-rules\": \"1.2.1\",\n    \"browser-sync\": \"2.23.6\",\n    \"codelyzer\": \"4.2.1\",\n    \"concurrently\": \"3.5.1\",\n    \"condition-circle\": \"2.0.1\",\n    \"conventional-changelog\": \"1.1.24\",\n    \"death\": \"1.1.0\",\n    \"favicons\": \"4.8.6\",\n    \"firebase-tools\": \"3.17.6\",\n    \"font-awesome\": \"4.7.0\",\n    \"fuse-box\": \"3.0.2\",\n    \"fuse-box-typechecker\": \"2.7.1\",\n    \"glob\": \"7.1.2\",\n    \"greenkeeper-lockfile\": \"1.14.0\",\n    \"hash-files\": \"1.1.1\",\n    \"html-minifier\": \"3.5.15\",\n    \"husky\": \"0.14.3\",\n    \"is-reachable\": \"2.4.0\",\n    \"jest\": \"22.4.2\",\n    \"jest-junit-reporter\": \"1.1.0\",\n    \"jest-preset-angular\": \"5.2.1\",\n    \"jsdom\": \"11.6.2\",\n    \"last-release-git\": \"0.0.3\",\n    \"mkdirp\": \"0.5.1\",\n    \"ng2-fused\": \"0.5.1\",\n    \"node-run-cmd\": \"1.0.1\",\n    \"node-sass\": \"4.8.3\",\n    \"opn\": \"5.3.0\",\n    \"semantic-release\": \"15.1.7\",\n    \"source-map-explorer\": \"1.5.0\",\n    \"string-hash\": \"1.1.3\",\n    \"superagent\": \"3.8.2\",\n    \"svg2png\": \"4.1.1\",\n    \"ts-jest\": \"22.4.3\",\n    \"ts-node\": \"5.0.1\",\n    \"tslint\": \"5.9.1\",\n    \"tslint-immutable\": \"4.5.2\",\n    \"typescript\": \"2.7.2\",\n    \"uglify-es\": \"3.3.9\"\n  },\n  \"dependencies\": {\n    \"@angular/animations\": \"6.0.0-beta.7\",\n    \"@angular/cdk\": \"5.1.1\",\n    \"@angular/common\": \"6.0.0-beta.7\",\n    \"@angular/compiler\": \"6.0.0-beta.7\",\n    \"@angular/compiler-cli\": \"6.0.0-beta.7\",\n    \"@angular/core\": \"6.0.0-beta.7\",\n    \"@angular/flex-layout\": \"2.0.0-beta.12\",\n    \"@angular/forms\": \"6.0.0-beta.7\",\n    \"@angular/http\": \"6.0.0-beta.7\",\n    \"@angular/material\": \"5.1.1\",\n    \"@angular/platform-browser\": \"6.0.0-beta.7\",\n    \"@angular/platform-browser-dynamic\": \"6.0.0-beta.7\",\n    \"@angular/platform-server\": \"6.0.0-beta.7\",\n    \"@angular/router\": \"6.0.0-beta.7\",\n    \"@angular/service-worker\": \"6.0.0-beta.7\",\n    \"@expo/bunyan\": \"1.8.10\",\n    \"@nguniversal/common\": \"5.0.0\",\n    \"@nguniversal/express-engine\": \"5.0.0\",\n    \"@ngx-meta/core\": \"5.0.0\",\n    \"angular2-jwt\": \"0.2.3\",\n    \"angularfire2\": \"5.0.0-rc.6\",\n    \"angulartics2\": \"5.1.2\",\n    \"body-parser\": \"1.18.2\",\n    \"bunyan-middleware\": \"0.8.0\",\n    \"clean-css\": \"4.1.11\",\n    \"compressible\": \"2.0.13\",\n    \"compression\": \"1.7.2\",\n    \"cookie-parser\": \"1.4.3\",\n    \"core-js\": \"2.5.5\",\n    \"cors\": \"2.8.4\",\n    \"dotenv\": \"5.0.1\",\n    \"express\": \"4.16.3\",\n    \"express-minify-html\": \"0.12.0\",\n    \"express-urlrewrite\": \"1.2.0\",\n    \"firebase\": \"4.11.0\",\n    \"firebase-admin\": \"5.10.0\",\n    \"getos\": \"3.1.0\",\n    \"hammerjs\": \"2.0.8\",\n    \"js-cookie\": \"2.2.0\",\n    \"markdown-to-html-pipe\": \"1.2.4\",\n    \"ms\": \"2.1.1\",\n    \"nightmare\": \"3.0.0\",\n    \"object-hash\": \"1.3.0\",\n    \"quill\": \"1.3.6\",\n    \"reflect-metadata\": \"0.1.12\",\n    \"request\": \"2.85.0\",\n    \"routing-controllers\": \"0.7.7\",\n    \"rxjs\": \"5.5.10\",\n    \"serve-favicon\": \"2.4.5\",\n    \"shrink-ray\": \"0.1.3\",\n    \"sitemap-generator\": \"6.0.0\",\n    \"stacktrace-js\": \"2.0.0\",\n    \"supertest\": \"3.0.0\",\n    \"swagger-jsdoc\": \"1.9.7\",\n    \"swagger-ui-express\": \"3.0.8\",\n    \"systemjs\": \"0.21.3\",\n    \"ts-node\": \"5.0.1\",\n    \"tsickle\": \"0.27.5\",\n    \"typedi\": \"0.7.1\",\n    \"url-parse\": \"1.3.0\",\n    \"ws\": \"5.1.1\",\n    \"xhr2\": \"0.1.4\",\n    \"yargs\": \"11.0.0\",\n    \"zone.js\": \"0.8.20\"\n  },\n  \"engines\": {\n    \"node\": \"= 9.4.0\",\n    \"npm\": \"= 5.6.0\"\n  },\n  \"release\": {\n    \"branch\": \"master\",\n    \"verifyConditions\": \"condition-circle\",\n    \"getLastRelease\": \"last-release-git\"\n  },\n  \"jest\": {\n    \"preset\": \"jest-preset-angular\",\n    \"moduleNameMapper\": {\n      \"./client/operators\": \"<rootDir>/src/client/operators.ts\"\n    },\n    \"setupTestFrameworkScriptFile\": \"./tools/test/jest.setup.ts\",\n    \"testResultsProcessor\": \"./node_modules/jest-junit-reporter\",\n    \"collectCoverageFrom\": [\n      \"src/client/app/**/*(*.service|*.component|*.pipe|*.directive|*.module).{ts,html}\",\n      \"!src/client/app/shared/shared.module.ts\",\n      \"src/server/**/**.{ts}\"\n    ],\n    \"globals\": {\n      \"__TRANSFORM_HTML__\": true,\n      \"__process_env__\": {},\n      \"ts-jest\": {\n        \"tsConfigFile\": \"tsconfig.json\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/client/app/__snapshots__/app.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`App component should build without a problem 1`] = `\n<div\n  id=\"root0\"\n  ng-version=\"6.0.0-beta.7\"\n>\n  <pm-app>\n    <pm-navbar>\n      <nav\n        class=\"mat-elevation-z6\"\n      >\n        <button\n          angulartics2on=\"click\"\n          angularticsaction=\"ToggleSideMenu\"\n          class=\"mat-icon-button\"\n          id=\"toggle-menu-button\"\n          mat-icon-button=\"\"\n        >\n          <span\n            class=\"mat-button-wrapper\"\n          >\n            <mat-icon\n              aria-hidden=\"true\"\n              class=\"mat-icon\"\n              fonticon=\"fa-bars\"\n              fontset=\"fa\"\n              role=\"img\"\n            />\n          </span>\n          <div\n            class=\"mat-button-ripple mat-ripple\"\n            matripple=\"\"\n          />\n          <div\n            class=\"mat-button-focus-overlay\"\n          />\n        </button>\n        <a\n          class=\"nav-button mat-button\"\n          mat-button=\"\"\n          routerlink=\"\"\n        >\n          <span\n            class=\"mat-button-wrapper\"\n          >\n            FB-ANGULAR-UNIVERSAL-STARTER\n          </span>\n          <div\n            class=\"mat-button-ripple mat-ripple\"\n            matripple=\"\"\n          />\n          <div\n            class=\"mat-button-focus-overlay\"\n          />\n        </a>\n        <div\n          class=\"flex-spacer\"\n        />\n        \n        <a\n          class=\"nav-button mat-button\"\n          href=\"https://www.angularuniversal.com\"\n          mat-button=\"\"\n        >\n          <span\n            class=\"mat-button-wrapper\"\n          >\n            Consulting\n          </span>\n          <div\n            class=\"mat-button-ripple mat-ripple\"\n            matripple=\"\"\n          />\n          <div\n            class=\"mat-button-focus-overlay\"\n          />\n        </a>\n        <a\n          class=\"nav-button mat-button\"\n          href=\"/api-docs\"\n          mat-button=\"\"\n        >\n          <span\n            class=\"mat-button-wrapper\"\n          >\n            API\n          </span>\n          <div\n            class=\"mat-button-ripple mat-ripple\"\n            matripple=\"\"\n          />\n          <div\n            class=\"mat-button-focus-overlay\"\n          />\n        </a>\n        <button\n          aria-haspopup=\"true\"\n          class=\"mat-icon-button\"\n          id=\"user-menu-btn\"\n          mat-icon-button=\"\"\n        >\n          <span\n            class=\"mat-button-wrapper\"\n          >\n            <mat-icon\n              aria-hidden=\"true\"\n              class=\"mat-icon\"\n              fonticon=\"fa-user-circle-o\"\n              fontset=\"fa\"\n              role=\"img\"\n            />\n          </span>\n          <div\n            class=\"mat-button-ripple mat-ripple\"\n            matripple=\"\"\n          />\n          <div\n            class=\"mat-button-focus-overlay\"\n          />\n        </button>\n        <mat-menu\n          class=\"ng-tns-c8-1\"\n        >\n          \n        </mat-menu>\n      </nav>\n    </pm-navbar>\n    <mat-sidenav-container\n      class=\"sidenav-container mat-drawer-container mat-sidenav-container\"\n      exclude=\"#toggle-menu-button\"\n    >\n      <div\n        class=\"mat-drawer-backdrop\"\n      />\n      <mat-sidenav\n        class=\"sidenav mat-elevation-z6 mat-drawer mat-sidenav ng-tns-c1-0 ng-trigger ng-trigger-transform\"\n        tabindex=\"-1\"\n      >\n        <mat-list\n          class=\"mat-list\"\n        >\n          <div\n            id=\"menu-top\"\n          >\n            <h3\n              class=\"mat-subheader\"\n              matsubheader=\"\"\n            >\n              Pages\n            </h3>\n            <mat-slide-toggle\n              class=\"mat-slide-toggle mat-accent\"\n            >\n              <label\n                class=\"mat-slide-toggle-label\"\n              >\n                <div\n                  class=\"mat-slide-toggle-bar\"\n                >\n                  <input\n                    class=\"mat-slide-toggle-input cdk-visually-hidden\"\n                    type=\"checkbox\"\n                  />\n                  <div\n                    class=\"mat-slide-toggle-thumb-container\"\n                  >\n                    <div\n                      class=\"mat-slide-toggle-thumb\"\n                    />\n                    <div\n                      class=\"mat-slide-toggle-ripple mat-ripple\"\n                      mat-ripple=\"\"\n                    />\n                  </div>\n                </div>\n                <span\n                  class=\"mat-slide-toggle-content\"\n                />\n              </label>\n            </mat-slide-toggle>\n          </div>\n          <mat-list-item\n            class=\"mat-list-item\"\n          >\n            <div\n              class=\"mat-list-item-content\"\n            >\n              <div\n                class=\"mat-list-item-ripple mat-ripple\"\n                mat-ripple=\"\"\n              />\n              <div\n                class=\"mat-list-text\"\n              >\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                  routerlink=\"/\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Home\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                  routerlink=\"changelog\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Changelog\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  href=\"/sitemap.xml\"\n                  mat-button=\"\"\n                  matline=\"\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Sitemap\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  href=\"/robots.txt\"\n                  mat-button=\"\"\n                  matline=\"\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Robots\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  href=\"https://www.angularuniversal.com\"\n                  mat-button=\"\"\n                  matline=\"\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Consulting\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n              </div>\n            </div>\n          </mat-list-item>\n          <mat-divider\n            class=\"mat-divider\"\n            role=\"separator\"\n          />\n          <h3\n            class=\"mat-subheader\"\n            matsubheader=\"\"\n          >\n            Boss Area\n          </h3>\n          <mat-list-item\n            class=\"mat-list-item\"\n          >\n            <div\n              class=\"mat-list-item-content\"\n            >\n              <div\n                class=\"mat-list-item-ripple mat-ripple\"\n                mat-ripple=\"\"\n              />\n              <div\n                class=\"mat-list-text\"\n              >\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                  routerlink=\"pages\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Pages\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Assets\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                  routerlink=\"users\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Users\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                  routerlink=\"admin\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Admin\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n              </div>\n            </div>\n          </mat-list-item>\n        </mat-list>\n      </mat-sidenav>\n      \n    </mat-sidenav-container>\n  </pm-app>\n</div>\n`;\n"
  },
  {
    "path": "src/client/app/about/__snapshots__/about.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`AboutComponent should compile 1`] = `\n<test-component>\n  <pm-about>\n    <h1>\n      About\n    </h1>\n    <p>\n      An Angular starter with all the tools necessary to create server rendered Angular applications.\n    </p>\n    <form\n      class=\"ng-untouched ng-pristine ng-invalid\"\n      ng-reflect-form=\"[object Object]\"\n      novalidate=\"\"\n    >\n      <mat-form-field\n        class=\"mat-input-container mat-form-field ng-tns-c0-0 mat-form-field-type-mat-input mat-form-field-can-float mat-form-field-hide-placeholder mat-primary ng-untouched ng-pristine ng-invalid\"\n      >\n        <div\n          class=\"mat-input-wrapper mat-form-field-wrapper\"\n        >\n          <div\n            class=\"mat-input-flex mat-form-field-flex\"\n          >\n            \n            <div\n              class=\"mat-input-infix mat-form-field-infix\"\n            >\n              <input\n                aria-invalid=\"false\"\n                aria-required=\"false\"\n                class=\"mat-input-element mat-form-field-autofill-control ng-untouched ng-pristine ng-invalid\"\n                formcontrolname=\"title\"\n                id=\"mat-input-0\"\n                matinput=\"\"\n                ng-reflect-name=\"title\"\n                ng-reflect-placeholder=\"Title\"\n                placeholder=\"Title\"\n              />\n              <span\n                class=\"mat-form-field-label-wrapper mat-input-placeholder-wrapper mat-form-field-placeholder-wrapper\"\n              >\n                \n                <label\n                  aria-owns=\"mat-input-0\"\n                  class=\"mat-form-field-label mat-input-placeholder mat-form-field-placeholder ng-tns-c0-0 mat-empty mat-form-field-empty ng-star-inserted\"\n                  for=\"mat-input-0\"\n                  ng-reflect-ng-switch=\"false\"\n                >\n                  \n                  \n                  Title\n                  \n                  \n                </label>\n              </span>\n            </div>\n            \n          </div>\n          <div\n            class=\"mat-input-underline mat-form-field-underline\"\n          >\n            <span\n              class=\"mat-input-ripple mat-form-field-ripple\"\n            />\n          </div>\n          <div\n            class=\"mat-input-subscript-wrapper mat-form-field-subscript-wrapper\"\n            ng-reflect-ng-switch=\"hint\"\n          >\n            \n            \n            <div\n              class=\"mat-input-hint-wrapper mat-form-field-hint-wrapper ng-tns-c0-0 ng-trigger ng-trigger-transitionMessages ng-star-inserted\"\n              style=\"opacity: 1;\"\n            >\n              \n              <div\n                class=\"mat-input-hint-spacer mat-form-field-hint-spacer\"\n              />\n            </div>\n          </div>\n        </div>\n      </mat-form-field>\n      <mat-form-field\n        class=\"mat-input-container mat-form-field ng-tns-c0-1 mat-form-field-type-mat-input mat-form-field-can-float mat-form-field-hide-placeholder mat-primary ng-untouched ng-pristine ng-invalid\"\n      >\n        <div\n          class=\"mat-input-wrapper mat-form-field-wrapper\"\n        >\n          <div\n            class=\"mat-input-flex mat-form-field-flex\"\n          >\n            \n            <div\n              class=\"mat-input-infix mat-form-field-infix\"\n            >\n              <textarea\n                aria-invalid=\"false\"\n                aria-required=\"false\"\n                class=\"mat-input-element mat-form-field-autofill-control ng-untouched ng-pristine ng-invalid\"\n                formcontrolname=\"html\"\n                id=\"mat-input-1\"\n                matinput=\"\"\n                ng-reflect-name=\"html\"\n                ng-reflect-placeholder=\"html\"\n                placeholder=\"html\"\n              />\n              <span\n                class=\"mat-form-field-label-wrapper mat-input-placeholder-wrapper mat-form-field-placeholder-wrapper\"\n              >\n                \n                <label\n                  aria-owns=\"mat-input-1\"\n                  class=\"mat-form-field-label mat-input-placeholder mat-form-field-placeholder ng-tns-c0-1 mat-empty mat-form-field-empty ng-star-inserted\"\n                  for=\"mat-input-1\"\n                  ng-reflect-ng-switch=\"false\"\n                >\n                  \n                  \n                  html\n                  \n                  \n                </label>\n              </span>\n            </div>\n            \n          </div>\n          <div\n            class=\"mat-input-underline mat-form-field-underline\"\n          >\n            <span\n              class=\"mat-input-ripple mat-form-field-ripple\"\n            />\n          </div>\n          <div\n            class=\"mat-input-subscript-wrapper mat-form-field-subscript-wrapper\"\n            ng-reflect-ng-switch=\"hint\"\n          >\n            \n            \n            <div\n              class=\"mat-input-hint-wrapper mat-form-field-hint-wrapper ng-tns-c0-1 ng-trigger ng-trigger-transitionMessages ng-star-inserted\"\n              style=\"opacity: 1;\"\n            >\n              \n              <div\n                class=\"mat-input-hint-spacer mat-form-field-hint-spacer\"\n              />\n            </div>\n          </div>\n        </div>\n      </mat-form-field>\n      <button\n        class=\"mat-raised-button mat-primary\"\n        color=\"primary\"\n        disabled=\"\"\n        mat-raised-button=\"\"\n        ng-reflect-color=\"primary\"\n        ng-reflect-disabled=\"true\"\n      >\n        <span\n          class=\"mat-button-wrapper\"\n        >\n           Post \n        </span>\n        <div\n          class=\"mat-button-ripple mat-ripple\"\n          matripple=\"\"\n          ng-reflect-centered=\"false\"\n          ng-reflect-disabled=\"true\"\n          ng-reflect-trigger=\"[object HTMLButtonElement]\"\n        />\n        <div\n          class=\"mat-button-focus-overlay\"\n        />\n      </button>\n      <button\n        class=\"mat-raised-button mat-warn\"\n        color=\"warn\"\n        mat-raised-button=\"\"\n        ng-reflect-color=\"warn\"\n      >\n        <span\n          class=\"mat-button-wrapper\"\n        >\n           Delete All Posts \n        </span>\n        <div\n          class=\"mat-button-ripple mat-ripple\"\n          matripple=\"\"\n          ng-reflect-centered=\"false\"\n          ng-reflect-disabled=\"false\"\n          ng-reflect-trigger=\"[object HTMLButtonElement]\"\n        />\n        <div\n          class=\"mat-button-focus-overlay\"\n        />\n      </button>\n    </form>\n    \n  </pm-about>\n</test-component>\n`;\n"
  },
  {
    "path": "src/client/app/about/about-routing.module.ts",
    "content": "import { AboutComponent } from './about.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: AboutComponent,\n        canActivate: [MetaGuard],\n        data: {\n          meta: {\n            title: 'i18n.about.title',\n            description: 'i18n.about.description'\n          },\n          response: {\n            cache: {\n              directive: 'no-cache'\n            }\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class AboutRoutingModule { }\n"
  },
  {
    "path": "src/client/app/about/about.component.e2e-spec.ts",
    "content": "import { baseUrl, browser } from '../../../../tools/test/jest.e2e-setup'\n\ndescribe('About Page', () => {\n  it('should have title', async () => {\n    expect.assertions(1)\n    const page = browser.goto(`${baseUrl}/about`)\n\n    const text = await page.evaluate(() => document.title)\n\n    expect(text).toContain('About - Fusebox Angular Universal Starter')\n  })\n})\n"
  },
  {
    "path": "src/client/app/about/about.component.html",
    "content": "<h1>About</h1>\n<p>An Angular starter with all the tools necessary to create server rendered Angular applications.</p>\n\n<form [formGroup]=\"form\" novalidate>\n  <mat-form-field>\n    <input matInput placeholder=\"Title\" formControlName=\"title\">\n  </mat-form-field>\n  <mat-form-field>\n    <textarea matInput placeholder=\"html\" formControlName=\"html\"></textarea>\n  </mat-form-field>\n  <button mat-raised-button color=\"primary\" (click)=\"create()\" [disabled]=\"!form.valid\">\n    Post\n  </button>\n  <button mat-raised-button color=\"warn\" (click)=\"removeAll()\">\n    Delete All Posts\n  </button>\n</form>\n\n<mat-card *ngFor=\"let post of posts$ | async; trackBy: trackByPost\">\n  <h4>{{ post.title }}</h4>\n  <div [innerHTML]=\"post.html\"></div>\n</mat-card>"
  },
  {
    "path": "src/client/app/about/about.component.scss",
    "content": ""
  },
  {
    "path": "src/client/app/about/about.component.spec.ts",
    "content": "import { AboutComponent } from './about.component'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { AboutModule } from './about.module'\nimport { AppTestingModule } from '../../../testing/app-testing.module'\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-about></pm-about>'\n})\nclass TestComponent {}\n\ndescribe(AboutComponent.name, () => {\n  let fixture: ComponentFixture<TestComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [AppTestingModule.forRoot(), AboutModule],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(TestComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should compile', async(() => {\n    fixture.detectChanges()\n    expect(fixture.nativeElement).toBeDefined()\n    expect(fixture).toMatchSnapshot()\n  }))\n})\n"
  },
  {
    "path": "src/client/app/about/about.component.ts",
    "content": "import { PlatformService } from './../shared/services/platform.service'\nimport { ChangeDetectionStrategy, Component } from '@angular/core'\nimport { FirebaseDatabaseService } from '../shared/services/firebase-database.service'\nimport { FormControl, FormGroup, Validators } from '@angular/forms'\n\n@Component({\n  selector: 'pm-about',\n  templateUrl: './about.component.html',\n  styleUrls: ['./about.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AboutComponent {\n  readonly posts$ = this.db.getList<{ readonly title: string, readonly html: string }>('posts').map(a => {\n    return a.map((b: any) => {\n      return {\n        title: b.title || '',\n        html: b.html || ''\n      }\n    })\n  })\n\n  public readonly form = new FormGroup({\n    title: new FormControl('', [\n      Validators.required\n    ]),\n    html: new FormControl('', [\n      Validators.required\n    ])\n  })\n\n  constructor(private db: FirebaseDatabaseService, ps: PlatformService) { }\n\n  create() {\n    this.db.getListRef('posts').push(this.form.value)\n  }\n\n  removeAll() {\n    this.db.getObjectRef('posts').remove()\n  }\n\n  trackByPost(index: number, item: any) {\n    return index\n  }\n}\n"
  },
  {
    "path": "src/client/app/about/about.module.ts",
    "content": "import { AboutRoutingModule } from './about-routing.module'\nimport { AboutComponent } from './about.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [AboutRoutingModule, SharedModule],\n  declarations: [AboutComponent],\n  exports: [AboutComponent]\n})\nexport class AboutModule { }\n"
  },
  {
    "path": "src/client/app/account/account-routing.module.ts",
    "content": "import { AccountComponent } from './account.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\nimport { LoginGuard } from '../shared/services/guard-login.service'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: AccountComponent,\n        canActivate: [MetaGuard, LoginGuard],\n        data: {\n          meta: {\n            title: 'i18n.account.title',\n            description: 'i18n.account.description'\n          },\n          response: {\n            cache: {\n              directive: 'private'\n            }\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class AccountRoutingModule { }\n"
  },
  {
    "path": "src/client/app/account/account.component.html",
    "content": "<div *ngIf=\"user$ | async as user\" class=\"flex-center\">\n  <h2>{{ user.displayName }}</h2>\n  <a class=\"flex-center\">\n    <img *ngIf=\"user.photoURL\" [src]=\"user.photoURL\" class=\"avatar\" angulartics2On=\"mouseover\" angularticsAction=\"HoveredOverAccountProfileImage\">\n    <a mat-button>Update Photo</a>\n  </a>\n  <mat-accordion class=\"headers-align\">\n    <mat-expansion-panel #profilePanel>\n      <mat-expansion-panel-header>\n        <mat-panel-title>\n          Profile Info\n        </mat-panel-title>\n        <mat-panel-description>\n          name, email, and phone\n        </mat-panel-description>\n      </mat-expansion-panel-header>\n      <form [formGroup]=\"detailForm\" (ngSubmit)=\"updateDetail()\">\n        <mat-form-field>\n          <input matInput formControlName=\"displayName\" [value]=\"user.displayName\" type=\"text\" placeholder=\"Name\" angulartics2On=\"focus\"\n            angularticsAction=\"FocusedOnAccountDisplayNameInput\">\n        </mat-form-field>\n        <mat-form-field>\n          <input matInput formControlName=\"email\" type=\"email\" [value]=\"user.email\" placeholder=\"Email\" angulartics2On=\"focus\" angularticsAction=\"FocusedOnAccountEmailInput\">\n          <!-- <mat-icon matSuffix [matTooltip]=\"user.emailTooltip\" [color]=\"user.emailColor\" fontSet=\"fa\" [fontIcon]=\"user.emailIcon\" angulartics2On=\"mouseover\"\n                angularticsAction=\"HoveredOverAccountEmailCheckmarkIcon\"></mat-icon> -->\n        </mat-form-field>\n        <mat-form-field>\n          <input matInput formControlName=\"phoneNumber\" type=\"text\" [value]=\"user.phone || ''\" placeholder=\"Phone\" angulartics2On=\"focus\"\n            angularticsAction=\"FocusedOnAccountPhonenumberInput\">\n        </mat-form-field>\n        <button mat-raised-button type=\"submit\"\n        [disabled]=\"!detailForm.dirty\"\n        color=\"primary\" id=\"update-btn\" angulartics2On=\"input\" angulartics2On=\"click\" angularticsAction=\"ClickUpdateProfile\">Save Changes</button>\n      </form>\n    </mat-expansion-panel>\n    <mat-expansion-panel disabled=true #socialPanel>\n      <mat-expansion-panel-header>\n        <mat-panel-title>\n          Social Login\n        </mat-panel-title>\n        <mat-panel-description>\n          manage your accounts\n        </mat-panel-description>\n      </mat-expansion-panel-header>\n      <section class=\"demo-section\">\n        <button mat-button pmSocialButton=\"facebook\">\n          <div class=\"social-spacer\">\n            <mat-icon fontSet=\"fa\" fontIcon=\"fa-facebook-official\"></mat-icon>\n            <span>Link Facebook</span>\n          </div>\n        </button>\n        <button mat-button pmSocialButton=\"google\">\n          <div class=\"social-spacer\">\n            <mat-icon fontSet=\"fa\" fontIcon=\"fa-google\"></mat-icon>\n            <span>Link Google</span>\n          </div>\n        </button>\n        <button mat-button pmSocialButton=\"twitter\">\n          <div class=\"social-spacer\">\n            <mat-icon fontSet=\"fa\" fontIcon=\"fa-twitter\"></mat-icon>\n            <span>Link Twitter</span>\n          </div>\n        </button>\n        <button mat-button pmSocialButton=\"github\">\n          <div class=\"social-spacer\">\n            <mat-icon fontSet=\"fa\" fontIcon=\"fa-github\"></mat-icon>\n            <span>Link Github</span>\n          </div>\n        </button>\n      </section>\n    </mat-expansion-panel>\n    <mat-expansion-panel #passwordPanel>\n      <mat-error *ngIf=\"passwordError\">\n        {{ passwordError }}\n      </mat-error>\n      <mat-expansion-panel-header>\n        <mat-panel-title>\n          Password\n        </mat-panel-title>\n        <mat-panel-description>\n          update your password\n        </mat-panel-description>\n      </mat-expansion-panel-header>\n      <form [formGroup]=\"passForm\" (ngSubmit)=\"updatePassword()\">\n        <mat-form-field>\n          <input matInput formControlName=\"currentPassword\" type=\"password\" placeholder=\"Current Password\" angulartics2On=\"focus\" angularticsAction=\"FocusedOnAccountPasswordChangeInput\">\n          <mat-error *ngIf=\"passForm.controls.currentPassword.hasError('required')\">\n            <strong>required</strong>\n          </mat-error>\n        </mat-form-field>\n        <mat-form-field>\n          <input matInput formControlName=\"newPassword\" type=\"password\" placeholder=\"New Password\" angulartics2On=\"focus\" angularticsAction=\"FocusedOnAccountPasswordChangeInput\">\n          <mat-error *ngIf=\"passForm.controls.newPassword.hasError('required')\">\n            <strong>required</strong>\n          </mat-error>\n        </mat-form-field>\n        <button mat-raised-button [disabled]=\"passForm.invalid\" type=\"submit\" color=\"primary\" id=\"update-pass-btn\" angulartics2On=\"input\"\n          angulartics2On=\"click\" angularticsAction=\"ClickUpdateProfile\">Save Changes</button>\n      </form>\n    </mat-expansion-panel>\n  </mat-accordion>\n</div>\n"
  },
  {
    "path": "src/client/app/account/account.component.scss",
    "content": ":host {\n  .flex-center {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    img {\n      cursor: pointer;\n    }\n  }\n  mat-accordion {\n    margin-top: 1em;\n  }\n  form {\n    display: flex;\n    flex-direction: column;\n  }\n  #update-btn {\n    width: 100%;\n    &:hover {\n      cursor: pointer;\n    }\n  }\n  .headers-align .mat-expansion-panel-header-title,\n  .headers-align .mat-expansion-panel-header-description {\n    flex-basis: 0;\n  }\n  .headers-align .mat-expansion-panel-header-description {\n    justify-content: space-between;\n    align-items: center;\n  }\n  .avatar {\n    height: 95px;\n    width: 95px;\n  }\n  .social-spacer {\n    display: flex;\n    flex-direction: row;\n    align-items: baseline;\n  }\n}"
  },
  {
    "path": "src/client/app/account/account.component.ts",
    "content": "import { AuthService } from './../shared/services/auth.service'\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, ViewChild } from '@angular/core'\nimport { PlatformService } from './../shared/services/platform.service'\nimport { MatExpansionPanel, MatSnackBar } from '@angular/material'\nimport { FormControl, FormGroup, Validators } from '@angular/forms'\n\n@Component({\n  selector: 'pm-account',\n  templateUrl: './account.component.html',\n  styleUrls: ['./account.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AccountComponent {\n  @HostBinding('class.card-float-container') readonly containerClass = true\n  @ViewChild('passwordPanel') readonly passwordPanel: MatExpansionPanel\n  @ViewChild('profilePanel') readonly profilePanel: MatExpansionPanel\n  @ViewChild('socialPanel') readonly socialPanel: MatExpansionPanel\n  // private DEBOUNCE_TIME = 750\n\n  public readonly detailForm = new FormGroup({\n    displayName: new FormControl('', [\n      Validators.required\n    ]),\n    email: new FormControl('', [\n      Validators.required\n    ]),\n    phoneNumber: new FormControl('', [\n      Validators.required\n    ])\n  })\n\n  public readonly passForm = new FormGroup({\n    currentPassword: new FormControl('', [\n      Validators.required\n    ]),\n    newPassword: new FormControl('', [\n      Validators.required\n    ])\n  })\n  public readonly user$ = this.auth.user$\n  // private userSource = new Subject()\n  // private photoURL$ = new Subject<string>()\n  // public us$ = this.afAuth.idToken\n  // public user$ = this.userSource\n  //   .startWith(this.ts.get(AUTH_TS_KEY, {}))\n  //   .map(a => {\n  //     const emailVerified = (a as any).emailVerified\n  //     return {\n  //       ...a,\n  //       emailColor: emailVerified ? 'primary' : 'accent',\n  //       emailIcon: emailVerified ? 'fa-check-circle' : 'fa-question-circle',\n  //       emailTooltip: emailVerified ? 'confirmed email' : 'unconfirmed email'\n  //     }\n  //   })\n\n  constructor(private auth: AuthService, private snackBar: MatSnackBar, ps: PlatformService, private cd: ChangeDetectorRef) {\n    // if (ps.isBrowser) {\n    //   Observable.combineLatest(\n    //     this.us$,\n    //     this.detailForm.controls['displayName'].valueChanges.debounceTime(this.DEBOUNCE_TIME).distinctUntilChanged(),\n    //     this.photoURL$.startWith(undefined).debounceTime(this.DEBOUNCE_TIME).distinctUntilChanged(),\n    //     (user, displayName, photoURL) => {\n    //       return {\n    //         user,\n    //         update: {\n    //           displayName: displayName ? displayName : user.displayName,\n    //           photoURL: photoURL ? photoURL : user.photoURL\n    //         }\n    //       }\n    //     })\n    //     .flatMap(res => res.user.updateProfile(res.update))\n    //     .do(() => this.openSnackBar('name updated'))\n    //     .subscribe()\n\n    //   Observable.combineLatest(\n    //     this.us$,\n    //     this.detailForm.controls['email'].valueChanges.debounceTime(2500).distinctUntilChanged(),\n    //     (user, email) => {\n\n    //       return {\n    //         user,\n    //         email\n    //       }\n    //     })\n    //     .flatMap(res => res.user.updateEmail(res.email))\n    //     .do(() => this.openSnackBar('email updated'))\n    //     .subscribe(a => undefined, err => {\n    //       if (err.code === '\"auth/requires-recent-login\"') {\n    //         console.log('should re-auth')\n    //       }\n    //     })\n\n    //   // this.detailForm.controls['displayName'].valueChanges.debounceTime(500).subscribe(name => {\n    //   // })\n    //   // this.detailForm.controls['phoneNumber'].valueChanges.debounceTime(500).subscribe(console.log)\n    // }\n  }\n\n  openSnackBar(message: string, action = 'dismiss') {\n    this.snackBar.open(message, action, {\n      duration: 2000,\n      horizontalPosition: 'right',\n      verticalPosition: 'top'\n    })\n  }\n\n  updateDetail() {\n    this.auth\n      .updateProfile(this.detailForm.value.displayName)\n      .take(1)\n      .subscribe(() => {\n        this.openSnackBar('name updated')\n      }, err => {\n        console.log(err)\n      })\n  }\n\n  updatePassword() {\n    const permuteError = (err?: string) => {\n      this.passwordError = err\n      this.cd.markForCheck()\n    }\n\n    this.auth\n      .updateEmailPassword(this.passForm.value.currentPassword, this.passForm.value.newPassword)\n      .take(1)\n      .subscribe(res => {\n        permuteError()\n        this.passForm.reset()\n        // this.passwordPanel..close()\n        this.openSnackBar('password updated')\n      }, err => {\n        permuteError(err.message)\n      })\n  }\n\n  passwordError: string | undefined\n}\n"
  },
  {
    "path": "src/client/app/account/account.module.ts",
    "content": "import { AccountRoutingModule } from './account-routing.module'\nimport { AccountComponent } from './account.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [AccountRoutingModule, SharedModule],\n  declarations: [AccountComponent],\n  exports: [AccountComponent]\n})\nexport class AccountModule { }\n"
  },
  {
    "path": "src/client/app/admin/__snapshots__/admin.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`AdminComponent should match snapshot 1`] = `\n<pm-admin>\n  <h1>\n    Admin\n  </h1><p>\n    Your Admin module is routed here\n  </p>\n</pm-admin>\n`;\n"
  },
  {
    "path": "src/client/app/admin/admin-routing.module.ts",
    "content": "import { AdminComponent } from './admin.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: AdminComponent,\n        canActivate: [MetaGuard],\n        data: {\n          meta: {\n            title: 'i18n.admin.title',\n            description: 'i18n.admin.description'\n          },\n          response: {\n            cache: {\n              directive: 'private'\n            }\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class AdminRoutingModule { }\n"
  },
  {
    "path": "src/client/app/admin/admin.component.e2e-spec.ts",
    "content": "import { baseUrl, browser } from '../../../../tools/test/jest.e2e-setup'\n\ndescribe('Admin Page', () => {\n  it('should have title', async () => {\n    expect.assertions(1)\n    const page = browser.goto(`${baseUrl}/admin`)\n\n    const text = await page.evaluate(() => document.title)\n\n    expect(text).toEqual('Admin - Fusebox Angular Universal Starter')\n  })\n})\n"
  },
  {
    "path": "src/client/app/admin/admin.component.html",
    "content": "<h1>Admin</h1>\n<p>Your Admin module is routed here</p>"
  },
  {
    "path": "src/client/app/admin/admin.component.scss",
    "content": ""
  },
  {
    "path": "src/client/app/admin/admin.component.spec.ts",
    "content": "import { AdminComponent } from './admin.component'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { AdminModule } from './admin.module'\nimport { AppTestingModule } from '../../../testing/app-testing.module'\n\ndescribe(AdminComponent.name, () => {\n  let fixture: ComponentFixture<AdminComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [AppTestingModule.forRoot(), AdminModule],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(AdminComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should match snapshot', async(() => {\n    expect(fixture).toMatchSnapshot()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-admin></pm-admin>'\n})\nclass TestComponent {}\n"
  },
  {
    "path": "src/client/app/admin/admin.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-admin',\n  templateUrl: './admin.component.html',\n  styleUrls: ['./admin.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AdminComponent {\n}\n"
  },
  {
    "path": "src/client/app/admin/admin.module.ts",
    "content": "import { AdminRoutingModule } from './admin-routing.module'\nimport { AdminComponent } from './admin.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [AdminRoutingModule, SharedModule],\n  declarations: [AdminComponent],\n  exports: [AdminComponent]\n})\nexport class AdminModule { }\n"
  },
  {
    "path": "src/client/app/app-routing.module.ts",
    "content": "import { NgModule } from '@angular/core'\nimport { RouterModule, Routes } from '@angular/router'\n\nexport const routes: Routes = [\n  { path: '', loadChildren: async () => (await import('./home/home.module')).HomeModule },\n  { path: 'unauthorized', loadChildren: async () => (await import('./unauthorized/unauthorized.module')).UnauthorizedModule },\n  { path: 'about', loadChildren: async () => (await import('./about/about.module')).AboutModule },\n  { path: 'account', loadChildren: async () => (await import('./account/account.module')).AccountModule },\n  { path: 'login', loadChildren: async () => (await import('./login/login.module')).LoginModule },\n  { path: 'logout', loadChildren: async () => (await import('./logout/logout.module')).LogoutModule },\n  { path: 'signup', loadChildren: async () => (await import('./signup/signup.module')).SignupModule },\n  { path: 'admin', loadChildren: async () => (await import('./admin/admin.module')).AdminModule },\n  { path: 'changelog', loadChildren: async () => (await import('./changelog/changelog.module')).ChangelogModule },\n  { path: 'dashboard', loadChildren: async () => (await import('./dashboard/dashboard.module')).DashboardModule },\n  { path: 'pages', loadChildren: async () => (await import('./pages/pages.module')).PagesModule },\n  { path: 'users', loadChildren: async () => (await import('./users/users.module')).UsersModule }\n]\n\n@NgModule({\n  imports: [\n    RouterModule.forRoot(routes, { initialNavigation: 'enabled' })\n  ],\n  exports: [RouterModule]\n})\nexport class AppRoutingModule { }\n"
  },
  {
    "path": "src/client/app/app.browser.module.ts",
    "content": "import { BrowserAnimationsModule } from '@angular/platform-browser/animations'\nimport { BrowserModule, BrowserTransferStateModule, TransferState } from '@angular/platform-browser'\nimport { AppModule, REQ_KEY } from './app.module'\nimport { NgModule } from '@angular/core'\nimport { AppComponent } from './app.component'\nimport { REQUEST } from '@nguniversal/express-engine/tokens'\nimport 'hammerjs'\n\nexport function getRequest(transferState: TransferState): any {\n  return transferState.get<any>(REQ_KEY, {})\n}\n\n@NgModule({\n  bootstrap: [AppComponent],\n  imports: [\n    BrowserModule.withServerTransition({ appId: 'pm-app' }),\n    BrowserTransferStateModule,\n    BrowserAnimationsModule,\n    // ServiceWorkerModule.register('/ngsw-worker.js'),\n    AppModule\n  ],\n  providers: [\n    {\n      provide: REQUEST,\n      useFactory: getRequest,\n      deps: [TransferState]\n    }\n  ]\n})\nexport class AppBrowserModule { }\n"
  },
  {
    "path": "src/client/app/app.component.html",
    "content": "<pm-navbar (menuIconClick)=\"sidenav.toggle()\" [user]=\"user$ | async\"></pm-navbar>\n<mat-sidenav-container class=\"sidenav-container\" (pmClickOutside)=\"handleOutsideClicks()\" exclude=\"#toggle-menu-button\">\n  <mat-sidenav #sidenav class=\"sidenav mat-elevation-z6\" [mode]=\"menuMode\">\n    <mat-list>\n      <div id=\"menu-top\">\n        <h3 matSubheader>Pages</h3>\n        <mat-slide-toggle [checked]=\"menuMode === 'slide'\"></mat-slide-toggle>\n      </div>\n      <mat-list-item>\n        <a #out matLine mat-button routerLink=\"/\">Home</a>\n        <a #out matLine mat-button routerLink=\"changelog\">Changelog</a>\n        <a #out matLine mat-button href=\"/sitemap.xml\">Sitemap</a>\n        <a #out matLine mat-button href=\"/robots.txt\">Robots</a>\n        <a #out matLine mat-button href=\"https://www.angularuniversal.com\">Consulting</a>\n      </mat-list-item>\n      <mat-divider></mat-divider>\n      <h3 matSubheader>Boss Area</h3>\n      <mat-list-item>\n        <a #out matLine mat-button routerLink=\"pages\">Pages</a>\n        <a #out matLine mat-button>Assets</a>\n        <a #out matLine mat-button routerLink=\"users\">Users</a>\n        <a #out matLine mat-button routerLink=\"admin\">Admin</a>\n      </mat-list-item>\n    </mat-list>\n  </mat-sidenav>\n  <router-outlet></router-outlet>\n</mat-sidenav-container>"
  },
  {
    "path": "src/client/app/app.component.scss",
    "content": ":host {\n  display: flex;\n  flex: 1 0;\n  flex-direction: column;\n  .sidenav-container {\n    overflow-y: auto;\n    flex: 1 0;\n    flex-direction: column;\n  }\n  .sidenav {\n    width: 256px;\n  }\n  #menu-top {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    mat-slide-toggle {\n      margin-right: 1em;\n    }\n  }\n  ::ng-deep mat-sidenav-container {\n    mat-sidenav {\n      visibility: hidden;\n    }\n  }\n}"
  },
  {
    "path": "src/client/app/app.component.spec.ts",
    "content": "import { SharedModule } from './shared/shared.module'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { APP_BASE_HREF } from '@angular/common'\nimport { Route } from '@angular/router'\nimport { RouterTestingModule } from '@angular/router/testing'\nimport { Component } from '@angular/core'\nimport { HomeComponent } from './home/home.component'\nimport { AppModule } from './app.module'\nimport { AppBrowserModule } from './app.browser.module'\nimport { EnvConfig } from '../../../tools/config/app.config'\nimport { ENV_CONFIG } from './app.config'\nimport { EnvironmentService } from './shared/services/environment.service'\nimport { Angulartics2Module } from 'angulartics2'\nimport { Angulartics2GoogleAnalytics } from 'angulartics2/ga'\nimport { HttpClientTestingModule } from '@angular/common/http/testing'\nimport { NavbarService } from './shared/navbar/navbar.service'\nimport { MatCardModule } from '@angular/material'\nimport { AngularFireDatabase } from 'angularfire2/database'\nimport { Observable } from 'rxjs/Observable'\nimport { AuthService } from './shared/services/auth.service'\nimport '../operators'\n\nexport const TESTING_CONFIG: EnvConfig = {\n  name: 'Fusebox Angular Universal Starter',\n  // tslint:disable-next-line:max-line-length\n  description: 'Seed project for Angular Universal apps featuring Server-Side Rendering (SSR), FuseBox, dev/prod builds, Brotli/Gzip, SCSS, favicon generation, @types, unit testing w/ Jest, and sitemap generator. Created by Patrick Michalina',\n  firebase: {\n\n  } as any,\n  endpoints: {\n    api: 'http://localhost:8000/api',\n    websocketServer: 'ws://localhost:8001'\n  },\n  host: 'http://localhost:8083'\n}\n\n@Component({\n  selector: 'test-cmp',\n  template: '<pm-app></pm-app>'\n})\nclass TestComponent { }\n\ndescribe('App component', () => {\n  const config: Array<Route> = [\n    { path: '', component: HomeComponent }\n  ]\n\n  let fixture: ComponentFixture<TestComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [\n        AppModule,\n        AppBrowserModule,\n        SharedModule,\n        HttpClientTestingModule,\n        RouterTestingModule.withRoutes(config),\n        Angulartics2Module.forRoot([Angulartics2GoogleAnalytics]),\n        MatCardModule\n      ],\n      declarations: [TestComponent, HomeComponent],\n      providers: [\n        { provide: APP_BASE_HREF, useValue: '/' },\n        { provide: ENV_CONFIG, useValue: TESTING_CONFIG },\n        { provide: AuthService, useValue: { } },\n        {\n          provide: AngularFireDatabase, useValue: {\n            object: () => {\n              return {\n                snapshotChanges: () => Observable.of(),\n                valueChanges: () => Observable.of()\n              }\n            },\n            list: () => {\n              return {\n                snapshotChanges: () => Observable.of(),\n                valueChanges: () => Observable.of()\n              }\n            }\n          }\n        },\n        EnvironmentService,\n        NavbarService\n      ]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(TestComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should build without a problem', async(() => {\n    expect(fixture.nativeElement).toBeTruthy()\n    expect(fixture.nativeElement).toMatchSnapshot()\n  }))\n})\n"
  },
  {
    "path": "src/client/app/app.component.ts",
    "content": "import { ISetting } from './../../server/api/repositories/setting.repository'\nimport { REQUEST } from '@nguniversal/express-engine/tokens'\nimport { PlatformService } from './shared/services/platform.service'\nimport { HttpCacheDirective, ServerResponseService } from './shared/services/server-response.service'\nimport { CookieService } from './shared/services/cookie.service'\nimport { AuthService } from './shared/services/auth.service'\nimport { HttpClient } from '@angular/common/http'\nimport { WebSocketService } from './shared/services/web-socket.service'\nimport { DOCUMENT, TransferState } from '@angular/platform-browser'\nimport { SettingService } from './shared/services/setting.service'\nimport { Angulartics2GoogleAnalytics } from 'angulartics2/ga'\nimport { MatIconRegistry, MatSidenav, MatSlideToggle } from '@angular/material'\nimport { ActivatedRoute, NavigationEnd, Router } from '@angular/router'\nimport { InjectionService } from './shared/services/injection.service'\nimport { MinifierService } from './shared/services/minifier.service'\nimport { SEOService } from './shared/services/seo.service'\nimport {\n  AfterViewInit, ChangeDetectionStrategy, Component, ElementRef,\n  Inject, QueryList, Renderer2, ViewChild, ViewChildren\n} from '@angular/core'\n\n@Component({\n  selector: 'pm-app',\n  templateUrl: './app.component.html',\n  styleUrls: ['./app.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AppComponent implements AfterViewInit {\n  @ViewChild(MatSidenav) readonly sidenav: MatSidenav\n  @ViewChild(MatSlideToggle) readonly toggle: MatSlideToggle\n  @ViewChildren('out', { read: ElementRef }) readonly menuButtons: QueryList<ElementRef>\n\n  public readonly user$ = this.auth.user$\n  public menuMode = 'over'\n\n  constructor(ss: SettingService, analytics: Angulartics2GoogleAnalytics, wss: WebSocketService,\n    renderer: Renderer2, @Inject(DOCUMENT) doc: HTMLDocument, http: HttpClient, matIconRegistry: MatIconRegistry,\n    ps: PlatformService, router: Router, cs: CookieService, ts: TransferState, ar: ActivatedRoute, private auth: AuthService,\n    srs: ServerResponseService, domInjector: InjectionService, mini: MinifierService, seo: SEOService, @Inject(REQUEST) req: any\n    ) {\n      matIconRegistry.registerFontClassAlias('fontawesome', 'fa')\n    ss.settings$\n      .take(1)\n      .filter(Boolean)\n      .subscribe((settings: ISetting) => {\n        if (settings.tokens) seo.updateFbAppId(settings.tokens.facebookAppId)\n        settings.injections.filter(Boolean).forEach(link => domInjector.inject(renderer, link))\n      })\n\n    ss.settings$\n      .flatMap(settigns => router.events\n        .filter(event => event instanceof NavigationEnd)\n        .map(() => ar)\n        .map(route => {\n          seo.updateUrl(doc.location.href)\n          while (route.firstChild) route = route.firstChild\n          return route\n        })\n        .filter(route => route.outlet === 'primary')\n        .mergeMap(route => route.data)\n        .map(data => data['response']))\n      .subscribe((response: {\n        readonly cache: { readonly directive: HttpCacheDirective, readonly maxage?: string, readonly smaxage?: string },\n        readonly headers: { readonly [key: string]: string }\n      }) => {\n        if (response && response.cache) {\n          if (response.cache.directive === 'private') {\n            srs.setCachePrivate()\n          } else {\n            srs.setCache(response.cache.directive, response.cache.maxage, response.cache.smaxage)\n          }\n        } else {\n          srs.setCache('public', '7d', '7d')\n        }\n        if (response && response.headers) {\n          srs.setHeaders(response.headers)\n        }\n      })\n\n    // Uncomment to turn on direct web-socket connection with server\n    // if (ps.isBrowser) {\n    //   wss.messageBus$.subscribe()\n    //   wss.send({ message: 'ws test' })\n    // }\n  }\n\n  ngAfterViewInit() {\n    this.toggle.change\n      .map(a => a.checked)\n      .subscribe(checked => {\n        if (!checked) this.sidenav.close()\n        checked\n          ? this.menuMode = 'side'\n          : this.menuMode = 'over'\n      })\n    this.menuButtons\n      .map(a => a.nativeElement as HTMLAnchorElement)\n      .forEach(a => a.addEventListener('click', () => this.handleOutsideClicks()))\n  }\n\n  handleOutsideClicks() {\n    if (this.menuMode === 'side') return\n    this.sidenav.close()\n  }\n}\n"
  },
  {
    "path": "src/client/app/app.config.ts",
    "content": "import { InjectionToken } from '@angular/core'\nimport { EnvConfig } from '../../../tools/config/app.config'\n\nexport const ENV_CONFIG = new InjectionToken<EnvConfig>('app.config')\n"
  },
  {
    "path": "src/client/app/app.module.ts",
    "content": "import { ErrorHandler, NgModule } from '@angular/core'\nimport { HttpConfigInterceptor } from './shared/services/http-config-interceptor.service'\nimport { HttpCookieInterceptor } from './shared/services/http-cookie-interceptor.service'\nimport { AppComponent } from './app.component'\nimport { SharedModule } from './shared/shared.module'\nimport { AppRoutingModule } from './app-routing.module'\nimport { MetaLoader, MetaModule, MetaStaticLoader, PageTitlePositioning } from '@ngx-meta/core'\nimport { NotFoundModule } from './not-found/not-found.module'\nimport { BrowserModule, makeStateKey } from '@angular/platform-browser'\nimport { EnvironmentService } from './shared/services/environment.service'\nimport { ServerResponseService } from './shared/services/server-response.service'\nimport { Angulartics2Module } from 'angulartics2'\nimport { Angulartics2GoogleAnalytics } from 'angulartics2/ga'\nimport { HTTP_INTERCEPTORS, HttpClientModule, HttpResponse } from '@angular/common/http'\nimport { GlobalErrorHandler } from './shared/services/error-handler.service'\nimport { SettingService } from './shared/services/setting.service'\nimport { AngularFireModule, FirebaseAppConfigToken, FirebaseAppName } from 'angularfire2'\nimport { AngularFireAuthModule } from 'angularfire2/auth'\nimport { AngularFireDatabaseModule } from 'angularfire2/database'\nimport { TransferHttpCacheModule } from '@nguniversal/common'\nimport { AuthService, FB_COOKIE_KEY } from './shared/services/auth.service'\nimport { CACHE_TAG_CONFIG, CACHE_TAG_FACTORY, CacheTagConfig, HttpCacheTagModule } from './shared/http-cache-tag/http-cache-tag.module'\n\nexport const REQ_KEY = makeStateKey<string>('req')\n\nexport function metaFactory(env: EnvironmentService, ss: SettingService): MetaLoader {\n  const locale = 'en' // TODO: make this dynamic\n  const urlKey = 'host'\n  return new MetaStaticLoader({\n    callback: (key: string) => {\n      if (key && key.includes(urlKey)) {\n        return key.replace(urlKey, env.config.host)\n      }\n      return (key.includes('i18n')\n        ? ss.pluck(key.replace('i18n', `i18n.${locale}`))\n        : ss.pluck(key)).map(a => a ? a : '')\n    },\n    pageTitlePositioning: PageTitlePositioning.PrependPageTitle,\n    pageTitleSeparator: ' - ',\n    applicationName: 'og.title',\n    applicationUrl: 'host',\n    defaults: {\n      title: 'og.title',\n      description: 'og.description',\n      'og:image': 'og.image',\n      'og:type': 'og.type',\n      'og:locale': 'en_US',\n      'og:locale:alternate': 'en_US'\n    }\n  })\n}\n\nexport function firebaseConfigLoader(env: EnvironmentService) {\n  return env.config.firebase.config\n}\n\nexport function firebaseAppNameLoader(env: EnvironmentService) {\n  return env.config.firebase.appName\n}\n\nexport function cacheTagFactory(srs: ServerResponseService): any {\n  return (httpResponse: HttpResponse<any>, config: CacheTagConfig) => {\n    const cacheHeader = httpResponse.headers.get(config.headerKey)\n    if (cacheHeader) {\n      srs.appendHeader(config.headerKey, cacheHeader)\n    }\n  }\n}\n\n@NgModule({\n  imports: [\n    HttpClientModule,\n    AppRoutingModule,\n    NotFoundModule,\n    AngularFireModule,\n    AngularFireDatabaseModule,\n    AngularFireAuthModule,\n    TransferHttpCacheModule,\n    SharedModule.forRoot(),\n    Angulartics2Module.forRoot([Angulartics2GoogleAnalytics]),\n    BrowserModule.withServerTransition({ appId: 'pm-app' }),\n    MetaModule.forRoot({\n      provide: MetaLoader,\n      useFactory: (metaFactory),\n      deps: [EnvironmentService, SettingService]\n    }),\n    HttpCacheTagModule.forRoot(\n      {\n        provide: CACHE_TAG_CONFIG,\n        useValue: {\n          headerKey: 'Cache-Tag',\n          cacheableResponseCodes: [200]\n        }\n      },\n      {\n        provide: CACHE_TAG_FACTORY,\n        useFactory: cacheTagFactory,\n        deps: [ServerResponseService]\n      })\n  ],\n  providers: [\n    { provide: ErrorHandler, useClass: GlobalErrorHandler },\n    { provide: HTTP_INTERCEPTORS, useClass: HttpConfigInterceptor, multi: true },\n    { provide: HTTP_INTERCEPTORS, useClass: HttpCookieInterceptor, multi: true },\n    { provide: FB_COOKIE_KEY, useValue: 'fbAuth' },\n    {\n      provide: FirebaseAppName,\n      useFactory: firebaseAppNameLoader,\n      deps: [EnvironmentService]\n    },\n    {\n      provide: FirebaseAppConfigToken,\n      useFactory: firebaseConfigLoader,\n      deps: [EnvironmentService]\n    },\n    AuthService\n  ],\n  declarations: [AppComponent],\n  bootstrap: [AppComponent],\n  exports: [AppComponent]\n})\nexport class AppModule { }\n"
  },
  {
    "path": "src/client/app/changelog/__snapshots__/changelog.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`ChangelogComponent should compile 1`] = `\n<test-component>\n  <pm-changelog>\n    <mat-card\n      class=\"mat-card\"\n    >\n      <div />\n    </mat-card>\n  </pm-changelog>\n</test-component>\n`;\n"
  },
  {
    "path": "src/client/app/changelog/changelog-routing.module.ts",
    "content": "import { ChangelogComponent } from './changelog.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: ChangelogComponent,\n        canActivate: [MetaGuard],\n        data: {\n          meta: {\n            title: 'i18n.changelog.title',\n            description: 'i18n.changelog.description'\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class ChangelogRoutingModule { }\n"
  },
  {
    "path": "src/client/app/changelog/changelog.component.e2e-spec.ts",
    "content": "import { baseUrl, browser } from '../../../../tools/test/jest.e2e-setup'\n\ndescribe('Changelog Page', () => {\n  it('should have title', async () => {\n    expect.assertions(1)\n\n    const page = browser.goto(`${baseUrl}/changelog`)\n    const text = await page.evaluate(() => document.title)\n\n    expect(text).toContain('Changelog - Fusebox Angular Universal Starter')\n  })\n})\n"
  },
  {
    "path": "src/client/app/changelog/changelog.component.html",
    "content": "<mat-card>\n  <div [innerHTML]=\"changelog$ | async | MarkdownToHtml\"></div>\n</mat-card>"
  },
  {
    "path": "src/client/app/changelog/changelog.component.scss",
    "content": ""
  },
  {
    "path": "src/client/app/changelog/changelog.component.spec.ts",
    "content": "import { ChangelogComponent } from './changelog.component'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { ChangelogModule } from './changelog.module'\nimport { AppTestingModule } from '../../../testing/app-testing.module'\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-changelog></pm-changelog>'\n})\nclass TestComponent { }\n\ndescribe(ChangelogComponent.name, () => {\n  let fixture: ComponentFixture<TestComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [AppTestingModule.forRoot(), ChangelogModule],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(TestComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n    expect(fixture).toMatchSnapshot()\n  }))\n})\n"
  },
  {
    "path": "src/client/app/changelog/changelog.component.ts",
    "content": "import { Observable } from 'rxjs/Observable'\nimport { HttpClient } from '@angular/common/http'\nimport { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-changelog',\n  templateUrl: './changelog.component.html',\n  styleUrls: ['./changelog.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ChangelogComponent {\n  readonly changelog$ = this.http.get('./changelog.md', { responseType: 'text' })\n    .catch(a => Observable.of('Error loading changelog.md'))\n\n  constructor(private http: HttpClient) { }\n}\n"
  },
  {
    "path": "src/client/app/changelog/changelog.module.ts",
    "content": "import { ChangelogComponent } from './changelog.component'\nimport { ChangelogRoutingModule } from './changelog-routing.module'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [ChangelogRoutingModule, SharedModule],\n  declarations: [ChangelogComponent],\n  exports: [ChangelogComponent]\n})\nexport class ChangelogModule { }\n"
  },
  {
    "path": "src/client/app/dashboard/dashboard-menu.ts",
    "content": "export const DASHBOARD_MENU = [\n  {\n    heading: 'Heading 1',\n    children: [\n      {\n        path: 'test/test-child',\n        title: 'Test Child'\n      },\n      {\n        path: 'test/test-child-1',\n        title: 'Test Child 1'\n      }\n\n    ]\n  },\n  {\n    heading: 'Heading 2',\n    children: [\n      {\n        path: 'test1',\n        title: 'Test 1 Page'\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/client/app/dashboard/dashboard-routing.module.ts",
    "content": "import { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { DashboardComponent } from './dashboard.component'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: DashboardComponent,\n        children: [\n          { path: '', redirectTo: 'test', pathMatch: 'full' },\n          {\n            path: 'test',\n            loadChildren: async () => (await import('./test/test.module')).TestModule\n          },\n          {\n            path: 'test1',\n            loadChildren: async () => (await import('./test1/test1.module')).Test1Module\n          }\n        ]\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class DashboardRoutingModule { }\n"
  },
  {
    "path": "src/client/app/dashboard/dashboard.component.html",
    "content": "<mat-sidenav-container class=\"dashboard-container\">\n  <mat-sidenav #sidenav class=\"dashboard-sidenav\" mode=\"side\" opened=\"true\">\n    <nav class=\"nav-back-button\">\n      <button mat-button=\"\" (click)=\"toggleSidenav(true)\">\n                <span>\n                    <mat-icon role=\"img\" aria-hidden=\"true\">\n                            arrow_back\n                    </mat-icon>\n                </span>\n            </button>\n    </nav>\n    <nav *ngFor=\"let menu of dashboardMenu; trackBy: trackByMenu\">\n      <h3>{{ menu.heading }}</h3>\n      <ul>\n        <li *ngFor=\"let child of menu.children; trackBy: trackByChild\">\n          <a mat-button [routerLink]=\"[child.path]\" routerLinkActive=\"dashboard-sidenav-item-selected\" (click)=\"setPageTitle(child.title)\">\n                        {{ child.title }}\n                    </a>\n        </li>\n      </ul>\n    </nav>\n    <nav>\n      <h3>Forms Control</h3>\n      <ul>\n        <li>\n          <a routerlinkactive=\"dashboard-sidenav-item-selected\">\n                        Autocomplete\n                    </a>\n        </li>\n      </ul>\n    </nav>\n    <nav>\n      <h3>Forms Control</h3>\n      <ul>\n        <li>\n          <a routerlinkactive=\"dashboard-sidenav-item-selected\">\n                        Autocomplete\n                    </a>\n        </li>\n      </ul>\n    </nav>\n  </mat-sidenav>\n  <div class=\"dashboard-sidenav-content\">\n    <pm-dashboard-page-header (toggleSidenav)=\"toggleSidenav(event)\" [title]=\"pageTitle\"></pm-dashboard-page-header>\n    <router-outlet></router-outlet>\n    <pm-dashboard-page-footer></pm-dashboard-page-footer>\n  </div>\n</mat-sidenav-container>"
  },
  {
    "path": "src/client/app/dashboard/dashboard.component.scss",
    "content": ":host {\n  -webkit-box-flex: 1;\n  -ms-flex: 1 1 auto;\n  flex: 1 1 auto;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  .dashboard-container {\n    //background-color: #fafafa;\n    //color: rgba(0, 0, 0, 0.87);\n    width: 100%;\n    height: 700px;\n    .dashboard-sidenav {\n      box-shadow: 3px 0 6px rgba(0, 0, 0, .24);\n      width: 240px;\n      bottom: 0;\n      overflow: auto;\n      height: 100%;\n      z-index: 1;\n      .nav-back-button {\n        display: none;\n      }\n      .mat-icon {\n        padding: 10px 30px 0px 0px;\n      }\n      @media (max-width: 840px) {\n        .nav-back-button {\n          display: block;\n        }\n      }\n      h3 {\n        border: none;\n        font-size: 10px;\n        letter-spacing: 1px;\n        line-height: 24px;\n        text-transform: uppercase;\n        font-weight: 400;\n        margin: 0;\n        padding: 0 16px;\n        background: #9ba0a5;\n        color: rgba(255, 255, 255, 0.87);\n      }\n      ul {\n        list-style-type: none;\n        margin: 0;\n        padding: 0;\n      }\n      li {\n        border-bottom-width: 1px;\n        border-bottom-style: solid;\n        margin: 0;\n        padding: 0;\n        border-color: rgba(0, 0, 0, 0.06);\n        color: rgba(0, 0, 0, 0.54);\n        a {\n          box-sizing: border-box;\n          display: flex;\n          font-size: 14px;\n          font-weight: 400;\n          line-height: 47px;\n          text-decoration: none;\n          transition: all .3s;\n          padding: 0 16px;\n          position: relative;\n          color: rgba(0, 0, 0, 0.54);\n        }\n      }\n    }\n    /deep/ .mat-drawer-content {\n      margin-left: 240px !important;\n    }\n    @media (max-width: 840px) {\n      /deep/ .mat-drawer-content {\n        margin-left: 0px !important;\n      }\n    }\n    .dashboard-sidenav-content {\n      min-height: 100%;\n      display: -webkit-box;\n      display: -ms-flexbox;\n      display: flex;\n      -webkit-box-orient: vertical;\n      -webkit-box-direction: normal;\n      -ms-flex-direction: column;\n      flex-direction: column;\n    }\n    .dashboard-sidenav-item-selected {\n      background-color: #2196f3;\n      color: white !important;\n    }\n  }\n}"
  },
  {
    "path": "src/client/app/dashboard/dashboard.component.ts",
    "content": "import { Title } from '@angular/platform-browser'\nimport { ChangeDetectionStrategy, Component, HostListener, OnInit, ViewChild } from '@angular/core'\nimport { MatSidenav } from '@angular/material'\nimport { DASHBOARD_MENU } from './dashboard-menu'\n\n@Component({\n  selector: 'pm-dashboard',\n  templateUrl: './dashboard.component.html',\n  styleUrls: ['./dashboard.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class DashboardComponent implements OnInit {\n\n  pageTitle: string\n\n  readonly isMobile = false\n\n  readonly dashboardMenu = DASHBOARD_MENU\n\n  @ViewChild('sidenav') public readonly sidenav: MatSidenav\n\n  @HostListener('window:resize', ['$event'])\n  onResize(event: any) {\n    if (event.target.innerWidth < 840) {\n      this.sidenav.close()\n    }\n    if (event.target.innerWidth >= 840) {\n      this.sidenav.open()\n    }\n  }\n\n  constructor(private title: Title) { }\n\n  ngOnInit() {\n    const title = this.title.getTitle()\n    this.setPageTitle(title.substr(0, title.indexOf('-')))\n  }\n\n  toggleSidenav(event: any) {\n    this.sidenav.toggle()\n  }\n\n  setPageTitle(title: string) {\n    this.pageTitle = title\n  }\n\n  trackByMenu(index: number, item: any) {\n    return index\n  }\n\n  trackByChild(index: number, item: any) {\n    return index\n  }\n}\n"
  },
  {
    "path": "src/client/app/dashboard/dashboard.module.ts",
    "content": "import { DashboardComponent } from './dashboard.component'\nimport { DashboardRoutingModule } from './dashboard-routing.module'\nimport { DashboardPageHeaderComponent } from './header/dashboard-page-header.component'\nimport { DashboardPageFooterComponent } from './footer/dashboard-page-footer.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\nimport { MatIconModule, MatSidenavModule } from '@angular/material'\n\n@NgModule({\n  imports: [DashboardRoutingModule, SharedModule, MatSidenavModule, MatIconModule],\n  declarations: [DashboardComponent, DashboardPageHeaderComponent, DashboardPageFooterComponent],\n  exports: [DashboardComponent, DashboardPageHeaderComponent, DashboardPageFooterComponent]\n})\nexport class DashboardModule { }\n"
  },
  {
    "path": "src/client/app/dashboard/footer/__snapshots__/dashboard-page-footer.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`DashboardPageFooterComponent should match snapshot 1`] = `\n<pm-dashboard-page-footer>\n  <footer\n    class=\"dashboard-footer\"\n  >\n    <div\n      class=\"dashboard-footer-list\"\n    >\n      <div\n        class=\"logo\"\n      >\n        <img\n          alt=\"angular\"\n          class=\"logo\"\n          src=\"assets/angular-white-transparent.svg\"\n        />\n      </div>\n      <div\n        class=\"dashboard-footer-links\"\n      >\n        <ul>\n          <li>\n            <a\n              href=\"https://angular.io\"\n            >\n              Learn Angular\n            </a>\n          </li>\n        </ul>\n      </div>\n      <div\n        class=\"dashboard-footer-copyright\"\n      >\n        <p>\n          Powered by Google ©2010-2017. Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.\n        </p>\n      </div>\n    </div>\n  </footer>\n</pm-dashboard-page-footer>\n`;\n"
  },
  {
    "path": "src/client/app/dashboard/footer/dashboard-page-footer.component.html",
    "content": "<footer class=\"dashboard-footer\">\n  <div class=\"dashboard-footer-list\">\n    <div class=\"logo\">\n      <img alt=\"angular\" class=\"logo\" src=\"assets/angular-white-transparent.svg\">\n    </div>\n    <div class=\"dashboard-footer-links\">\n      <ul>\n        <li> <a href=\"https://angular.io\">Learn Angular</a> </li>\n      </ul>\n    </div>\n    <div class=\"dashboard-footer-copyright\">\n      <p>Powered by Google ©2010-2017. Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.</p>\n    </div>\n  </div>\n</footer>"
  },
  {
    "path": "src/client/app/dashboard/footer/dashboard-page-footer.component.scss",
    "content": ":host {\n  .dashboard-footer {\n    margin-top: 40px;\n    padding: 12px;\n    font-size: 12px;\n    background: #2196f3;\n    color: rgba(255, 255, 255, 0.87);\n    .dashboard-footer-list {\n      -webkit-box-align: center;\n      -ms-flex-align: center;\n      align-items: center;\n      display: -webkit-box;\n      display: -ms-flexbox;\n      display: flex;\n      -webkit-box-orient: horizontal;\n      -webkit-box-direction: normal;\n      -ms-flex-flow: row wrap;\n      flex-flow: row wrap;\n      padding: 8px;\n    }\n    .logo {\n      height: 50px;\n    }\n    .dashboard-footer-links {\n      ul {\n        list-style: none;\n        margin: 0 40px;\n        padding: 0;\n        a {\n          font-size: 16px;\n          padding: 0;\n          text-decoration: none;\n          color: rgba(255, 255, 255, 0.87);\n        }\n      }\n    }\n    .dashboard-footer-copyright {\n      display: -webkit-box;\n      display: -ms-flexbox;\n      display: flex;\n      -webkit-box-flex: 1;\n      -ms-flex: 1;\n      flex: 1;\n      -webkit-box-pack: end;\n      -ms-flex-pack: end;\n      justify-content: flex-end;\n    }\n  }\n}"
  },
  {
    "path": "src/client/app/dashboard/footer/dashboard-page-footer.component.spec.ts",
    "content": "import { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { DashboardPageFooterComponent } from './dashboard-page-footer.component'\nimport { DashboardModule } from '../dashboard.module'\n\ndescribe(DashboardPageFooterComponent.name, () => {\n  let fixture: ComponentFixture<DashboardPageFooterComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [DashboardModule],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(DashboardPageFooterComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should match snapshot', async(() => {\n    expect(fixture).toMatchSnapshot()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-dashboard-page-footer></pm-dashboard-page-footer>'\n})\nclass TestComponent {}\n"
  },
  {
    "path": "src/client/app/dashboard/footer/dashboard-page-footer.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-dashboard-page-footer',\n  templateUrl: './dashboard-page-footer.component.html',\n  styleUrls: ['./dashboard-page-footer.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class DashboardPageFooterComponent {\n\n}\n"
  },
  {
    "path": "src/client/app/dashboard/header/__snapshots__/dashboard-page-header.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`DashboardPageHeaderComponent should match snapshot 1`] = `\n<pm-dashboard-page-header\n  toggleSidenav={[Function EventEmitter]}\n>\n  <div\n    class=\"docs-primary-header\"\n  >\n    <button\n      class=\"side-nav-toggle mat-button\"\n      mat-button=\"\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        <span>\n          <mat-icon\n            aria-hidden=\"true\"\n            class=\"mat-icon\"\n            role=\"img\"\n          >\n             menu \n          </mat-icon>\n        </span>\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </button>\n    <h1>\n      \n    </h1>\n  </div>\n</pm-dashboard-page-header>\n`;\n"
  },
  {
    "path": "src/client/app/dashboard/header/dashboard-page-header.component.html",
    "content": "<div class=\"docs-primary-header\">\n  <button class=\"side-nav-toggle\" mat-button=\"\" (click)=\"toggle()\">\n    <span>\n      <mat-icon role=\"img\" aria-hidden=\"true\">\n        menu\n      </mat-icon>\n    </span>\n  </button>\n  <h1>{{ title }}</h1>\n</div>"
  },
  {
    "path": "src/client/app/dashboard/header/dashboard-page-header.component.scss",
    "content": ":host {\n  .docs-primary-header {\n    background: #9ba0a5;\n    padding-left: 20px;\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n    -webkit-box-align: center;\n    -ms-flex-align: center;\n    align-items: center;\n    h1 {\n      font-size: 30px;\n      font-weight: 300;\n      margin: 0;\n      padding: 20px 20px 20px 10px;\n      color: hsla(0, 0%, 100%, .87);\n    }\n  }\n  .side-nav-toggle {\n    padding-top: 14px;\n    margin: 8px;\n    min-width: 64px;\n    display: none;\n  }\n  .mat-icon {\n    color: white;\n  }\n  @media (max-width: 840px) {\n    .side-nav-toggle {\n      display: block;\n    }\n    .docs-primary-header {\n      padding-left: 0px;\n    }\n  }\n}"
  },
  {
    "path": "src/client/app/dashboard/header/dashboard-page-header.component.spec.ts",
    "content": "import { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { DashboardModule } from '../dashboard.module'\nimport { DashboardPageHeaderComponent } from './dashboard-page-header.component'\n\ndescribe(DashboardPageHeaderComponent.name, () => {\n  let fixture: ComponentFixture<DashboardPageHeaderComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [DashboardModule],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(DashboardPageHeaderComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should match snapshot', async(() => {\n    expect(fixture).toMatchSnapshot()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-dashboard-page-header></pm-dashboard-page-header>'\n})\nclass TestComponent {}\n"
  },
  {
    "path": "src/client/app/dashboard/header/dashboard-page-header.component.ts",
    "content": "import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'\n\n@Component({\n  selector: 'pm-dashboard-page-header',\n  templateUrl: './dashboard-page-header.component.html',\n  styleUrls: ['./dashboard-page-header.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class DashboardPageHeaderComponent {\n\n  @Input() readonly title: string\n\n  @Output() readonly toggleSidenav: EventEmitter<boolean> = new EventEmitter<boolean>()\n\n  toggle() {\n    this.toggleSidenav.emit(true)\n  }\n}\n"
  },
  {
    "path": "src/client/app/dashboard/test/__snapshots__/test.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TestComponent should match snapshot 1`] = `\n<pm-test>\n  <router-outlet />\n</pm-test>\n`;\n"
  },
  {
    "path": "src/client/app/dashboard/test/test-routing.module.ts",
    "content": "import { TestComponent } from './test.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\nimport { TestChild1Component } from './testChild1/testChild1.component'\nimport { TestChildComponent } from './testChild/testChild.component'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: TestComponent,\n        canActivateChild: [MetaGuard],\n        children: [\n          { path: '', redirectTo: 'test-child', pathMatch: 'full'  },\n          {\n            path: 'test-child',\n            component: TestChildComponent,\n            data: {\n              meta: {\n                title: 'Test Child',\n                // tslint:disable-next-line:max-line-length\n                description: 'Test for angular related projects on github, to showcase the flicker-free http state transfer of an Angular isomorphic application.'\n              }\n            }\n          },\n          {\n            path: 'test-child-1',\n            component: TestChild1Component,\n            data: {\n              meta: {\n                title: 'Test Child 1 Page',\n                // tslint:disable-next-line:max-line-length\n                description: 'Test for angular related projects on github, to showcase the flicker-free http state transfer of an Angular isomorphic application.'\n              }\n            }\n          }\n        ]\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class TestRoutingModule { }\n"
  },
  {
    "path": "src/client/app/dashboard/test/test.component.html",
    "content": "<router-outlet></router-outlet>"
  },
  {
    "path": "src/client/app/dashboard/test/test.component.scss",
    "content": ""
  },
  {
    "path": "src/client/app/dashboard/test/test.component.spec.ts",
    "content": "import { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { TestModule } from './test.module'\nimport { TestComponent } from './test.component'\nimport { RouterTestingModule } from '@angular/router/testing'\nimport { TestChildComponent } from './testChild/testChild.component'\nimport { TestChild1Component } from './testChild1/testChild1.component'\nimport { Route } from '@angular/router'\nimport { EnvironmentService } from '../../shared/services/environment.service'\nimport { APP_BASE_HREF } from '@angular/common'\n\ndescribe(TestComponent.name, () => {\n  const config: Array<Route> = [\n    {\n      path: '',\n      component: TestComponent,\n      pathMatch: 'full',\n      children: [\n        { path: '', redirectTo: 'test-child', pathMatch: 'full' },\n        {\n          path: 'test-child',\n          component: TestChildComponent\n        },\n        {\n          path: 'test-child-1',\n          component: TestChild1Component\n        }\n      ]\n    }\n  ]\n\n  let fixture: ComponentFixture<TestComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [TestModule, RouterTestingModule.withRoutes(config)],\n      declarations: [TestingComponent],\n      providers: [\n        { provide: APP_BASE_HREF, useValue: '/' },\n        EnvironmentService\n      ]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(TestComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should match snapshot', async(() => {\n    expect(fixture).toMatchSnapshot()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-test></pm-test>'\n})\nclass TestingComponent {}\n"
  },
  {
    "path": "src/client/app/dashboard/test/test.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-test',\n  templateUrl: './test.component.html',\n  styleUrls: ['./test.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class TestComponent {\n}\n"
  },
  {
    "path": "src/client/app/dashboard/test/test.module.ts",
    "content": "import { TestRoutingModule } from './test-routing.module'\nimport { TestComponent } from './test.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../../shared/shared.module'\nimport { TestChildComponent } from './testChild/testChild.component'\nimport { TestChild1Component } from './testChild1/testChild1.component'\n\n@NgModule({\n  imports: [TestRoutingModule, SharedModule],\n  declarations: [TestComponent, TestChild1Component, TestChildComponent],\n  exports: [TestComponent, TestChild1Component, TestChildComponent]\n})\nexport class TestModule { }\n"
  },
  {
    "path": "src/client/app/dashboard/test/test.service.ts",
    "content": "import { Injectable } from '@angular/core'\n\n@Injectable()\nexport class TestService {\n\n}\n"
  },
  {
    "path": "src/client/app/dashboard/test/testChild/__snapshots__/testChild.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TestChildComponent should match snapshot 1`] = `\n<pm-test-child-1>\n  <mat-card\n    class=\"mat-card\"\n  >\n     Test Child Project\n  \n  </mat-card>\n</pm-test-child-1>\n`;\n"
  },
  {
    "path": "src/client/app/dashboard/test/testChild/testChild.component.html",
    "content": "<mat-card>\n    Test Child Project\n</mat-card>"
  },
  {
    "path": "src/client/app/dashboard/test/testChild/testChild.component.scss",
    "content": ".mat-card {\n    margin: 10px;\n    height: 400px;\n}"
  },
  {
    "path": "src/client/app/dashboard/test/testChild/testChild.component.spec.ts",
    "content": "import { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { TestChildComponent } from './testChild.component'\nimport { TestModule } from '../test.module'\n\ndescribe(TestChildComponent.name, () => {\n  let fixture: ComponentFixture<TestChildComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [TestModule],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(TestChildComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should match snapshot', async(() => {\n    expect(fixture).toMatchSnapshot()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-test-child-1></pm-test-child-1>'\n})\nclass TestComponent {}\n"
  },
  {
    "path": "src/client/app/dashboard/test/testChild/testChild.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-test-child-1',\n  templateUrl: './testChild.component.html',\n  styleUrls: ['./testChild.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class TestChildComponent {\n}\n"
  },
  {
    "path": "src/client/app/dashboard/test/testChild1/__snapshots__/testChild1.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TestChild1Component should match snapshot 1`] = `\n<pm-test-child-2>\n  <mat-card\n    class=\"mat-card\"\n  >\n     Test Child 1 Project\n  \n  </mat-card>\n</pm-test-child-2>\n`;\n"
  },
  {
    "path": "src/client/app/dashboard/test/testChild1/testChild1.component.html",
    "content": "<mat-card>\n  Test Child 1 Project\n</mat-card>"
  },
  {
    "path": "src/client/app/dashboard/test/testChild1/testChild1.component.scss",
    "content": ".mat-card {\n  margin: 10px;\n  height: 400px;\n}"
  },
  {
    "path": "src/client/app/dashboard/test/testChild1/testChild1.component.spec.ts",
    "content": "import { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { TestModule } from '../test.module'\nimport { TestChild1Component } from './testChild1.component'\n\ndescribe(TestChild1Component.name, () => {\n  let fixture: ComponentFixture<TestChild1Component>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [TestModule],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(TestChild1Component)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should match snapshot', async(() => {\n    expect(fixture).toMatchSnapshot()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-test-child-2></pm-test-child-2>'\n})\nclass TestComponent {}\n"
  },
  {
    "path": "src/client/app/dashboard/test/testChild1/testChild1.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-test-child-2',\n  templateUrl: './testChild1.component.html',\n  styleUrls: ['./testChild1.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class TestChild1Component {\n}\n"
  },
  {
    "path": "src/client/app/dashboard/test1/__snapshots__/test1.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Test1Component should match snapshot 1`] = `\n<pm-test1>\n  <mat-card\n    class=\"mat-card\"\n  >\n     Test 1 Project\n  \n  </mat-card>\n</pm-test1>\n`;\n"
  },
  {
    "path": "src/client/app/dashboard/test1/test1-routing.module.ts",
    "content": "import { Test1Component } from './test1.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: Test1Component,\n        canActivate: [MetaGuard],\n        data: {\n          meta: {\n            title: 'Test 1 Page',\n            // tslint:disable-next-line:max-line-length\n            description: 'Test 1 for angular related projects on github, to showcase the flicker-free http state transfer of an Angular isomorphic application.'\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class Test1RoutingModule { }\n"
  },
  {
    "path": "src/client/app/dashboard/test1/test1.component.html",
    "content": "<mat-card>\n  Test 1 Project\n</mat-card>"
  },
  {
    "path": "src/client/app/dashboard/test1/test1.component.scss",
    "content": ".mat-card {\n  margin: 10px;\n  height: 400px;\n}"
  },
  {
    "path": "src/client/app/dashboard/test1/test1.component.spec.ts",
    "content": "import { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { Test1Component } from './test1.component'\nimport { Test1Module } from './test1.module'\n\ndescribe(Test1Component.name, () => {\n  let fixture: ComponentFixture<Test1Component>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [Test1Module],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(Test1Component)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should match snapshot', async(() => {\n    expect(fixture).toMatchSnapshot()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-test1></pm-test1>'\n})\nclass TestComponent {}\n"
  },
  {
    "path": "src/client/app/dashboard/test1/test1.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-test1',\n  templateUrl: './test1.component.html',\n  styleUrls: ['./test1.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class Test1Component {\n}\n"
  },
  {
    "path": "src/client/app/dashboard/test1/test1.module.ts",
    "content": "import { Test1RoutingModule } from './test1-routing.module'\nimport { Test1Component } from './test1.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../../shared/shared.module'\n\n@NgModule({\n  imports: [Test1RoutingModule, SharedModule],\n  declarations: [Test1Component],\n  exports: [Test1Component]\n})\nexport class Test1Module { }\n"
  },
  {
    "path": "src/client/app/home/__snapshots__/home.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`HomeComponent should compile 1`] = `\n<pm-home\n  containerClass={[Function Boolean]}\n>\n  <mat-card\n    class=\"mat-card\"\n  >\n    <div\n      id=\"hero\"\n    >\n      <h1\n        class=\"display-3\"\n      >\n        Hello, Angular Developer!\n      </h1>\n      <p\n        class=\"lead\"\n      >\n        Welcome to a blazing fast Angular Universal development starter\n      </p>\n      <a\n        angulartics2on=\"click\"\n        angularticsaction=\"ViewRepo\"\n        class=\"mat-raised-button\"\n        color=\"primary\"\n        href=\"https://github.com/patrickmichalina/fusebox-angular-universal-starter\"\n        mat-raised-button=\"\"\n        role=\"button\"\n      >\n        <span\n          class=\"mat-button-wrapper\"\n        >\n          <mat-icon\n            aria-hidden=\"true\"\n            class=\"fa fa-github mat-icon\"\n            role=\"img\"\n          />\n           Github\n        </span>\n        <div\n          class=\"mat-button-ripple mat-ripple\"\n          matripple=\"\"\n        />\n        <div\n          class=\"mat-button-focus-overlay\"\n        />\n      </a>\n    </div>\n    <mat-tab-group\n      class=\"mat-tab-group mat-primary\"\n    >\n      <mat-tab-header\n        class=\"mat-tab-header\"\n      >\n        <div\n          aria-hidden=\"true\"\n          class=\"mat-tab-header-pagination mat-tab-header-pagination-before mat-elevation-z4 mat-ripple\"\n          mat-ripple=\"\"\n        >\n          <div\n            class=\"mat-tab-header-pagination-chevron\"\n          />\n        </div>\n        <div\n          class=\"mat-tab-label-container\"\n        >\n          <div\n            class=\"mat-tab-list\"\n            role=\"tablist\"\n          >\n            <div\n              class=\"mat-tab-labels\"\n            >\n              \n            </div>\n            <mat-ink-bar\n              class=\"mat-ink-bar\"\n            />\n          </div>\n        </div>\n        <div\n          aria-hidden=\"true\"\n          class=\"mat-tab-header-pagination mat-tab-header-pagination-after mat-elevation-z4 mat-ripple\"\n          mat-ripple=\"\"\n        >\n          <div\n            class=\"mat-tab-header-pagination-chevron\"\n          />\n        </div>\n      </mat-tab-header>\n      <div\n        class=\"mat-tab-body-wrapper\"\n      >\n        \n      </div>\n    </mat-tab-group>\n  </mat-card>\n</pm-home>\n`;\n"
  },
  {
    "path": "src/client/app/home/home-routing.module.ts",
    "content": "import { HomeComponent } from './home.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: HomeComponent,\n        canActivate: [MetaGuard],\n        data: {\n          meta: {\n            title: 'i18n.home.title',\n            description: 'i18n.home.description'\n          },\n          response: {\n            cache: {\n              directive: 'public',\n              maxage: '7d',\n              smaxage: '7d'\n            },\n            headers: {\n              'X-Cool-Tag': 'some-value'\n            }\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class HomeRoutingModule { }\n"
  },
  {
    "path": "src/client/app/home/home.component.e2e-spec.ts",
    "content": "import { baseUrl, browser } from '../../../../tools/test/jest.e2e-setup'\n\ndescribe('Home Page', () => {\n  it('should have title', async () => {\n    expect.assertions(1)\n\n    const page = browser.goto(`${baseUrl}`)\n\n    const text = await page.evaluate(() => document.title)\n\n    expect(text).toContain('Home - Fusebox Angular Universal Starter')\n  })\n})\n"
  },
  {
    "path": "src/client/app/home/home.component.html",
    "content": "<mat-card>\n  <div id=\"hero\">\n    <h1 class=\"display-3\">Hello, Angular Developer!</h1>\n    <p class=\"lead\">Welcome to a blazing fast Angular Universal development starter</p>\n    <a mat-raised-button color=\"primary\" href=\"https://github.com/patrickmichalina/fusebox-angular-universal-starter\" role=\"button\"\n      angulartics2On=\"click\" angularticsAction=\"ViewRepo\">\n      <mat-icon class=\"fa fa-github\" aria-hidden=\"true\"></mat-icon> Github</a>\n  </div>\n  <mat-tab-group>\n    <mat-tab label=\"Developer Experience\">\n      <ul>\n        <li>\n          <a href=\"https://github.com/angular/angular/blob/master/CHANGELOG.md\" target=\"_blank\" rel=\"noopener\">Angular</a> as the application framework\n        </li>\n        <li>\n          <a href=\"https://material.angular.io\" target=\"_blank\" rel=\"noopener\">Angular Material</a> as the UI language and component library</li>\n        <li>\n          <a href=\"https://github.com/angular/angularfire2\" target=\"_blank\" rel=\"noopener\">Angular Firebase</a> for easy user authentication\n        </li>\n        <li>\n          <a href=\"https://github.com/angular/flex-layout\">Angular Flex Layout</a> for dynamic responsive layouts\n          <li>Fully typed build tools and application using\n            <a href=\"https://www.typescriptlang.org/\" target=\"_blank\" rel=\"noopener\">TypeScript</a>\n          </li>\n\n          <li>Manage your type definitions using @types (easier than using\n            <a href=\"https://github.com/typings/typings\">typings</a>)</li>\n          <li>Automatic sitemap generation</li>\n          <li>\n            <a href=\"https://mobile.angular.io\" target=\"_blank\" rel=\"noopener\">SCSS</a> support for professional grade CSS management</li>\n          <li>Full favicon icon generation for multiple devices derived from a single seed image</li>\n          <li>Analyze bundle sizes by using\n            <a href=\"https://github.com/danvk/source-map-explorer\">source-map-explorer</a>\n          </li>\n          <li>Cookies Support</li>\n          <li>Simple Ad-Blocker detection service</li>\n          <li>Built by <a href=\"https://www.angularuniversal.com\">Angular Universal Consulting</a></li>\n      </ul>\n    </mat-tab>\n    <mat-tab label=\"Testing\">\n      <ul>\n        <li>Angular testing with\n          <a href=\"https://facebook.github.io/jest\" target=\"_blank\" rel=\"noopener\">Jest</a> (faster than Karma)</li>\n        <li>End-to-end (e2e) testing with\n          <a href=\"https://github.com/segmentio/nightmare\" target=\"_blank\" rel=\"noopener\">Nightmare</a>\n        </li>\n        <li>\n          <a href=\"https://circleci.com\" target=\"_blank\" rel=\"noopener\">CircleCI</a> unit testing support</li>\n      </ul>\n    </mat-tab>\n    <mat-tab label=\"Performance\">\n      <ul>\n        <li>\n          <a href=\"http://fuse-box.org/\">FuseBox</a> bundling (faster than\n          <a href=\"https://webpack.github.io/\" target=\"_blank\" rel=\"noopener\">Webpack</a> and\n          <a href=\"http://jspm.io/\" target=\"_blank\" rel=\"noopener\">JSPM</a>)</li>\n        <li>\n          <a href=\"https://github.com/google/brotli\" target=\"_blank\" rel=\"noopener\">Brotli compression</a> with\n          <a href=\"http://www.gzip.org\" target=\"_blank\" rel=\"noopener\">gzip</a> fallback</li>\n        <li>\n          <a href=\"https://angular.io/guide/aot-compiler#tree-shaking-1\" target=\"_blank\" rel=\"noopener\">Lazy Loaded</a> modules</li>\n        <li>\n          <strong>Coming Soon</strong>:\n          <a href=\"https://angular-2-training-book.rangle.io/handout/modules/lazy-loading-module.html\" target=\"_blank\" rel=\"noopener\">Tree-Shaking</a> builds</li>\n        <li>\n          <strong>Coming Soon</strong>: Support for\n          <a href=\"https://mobile.angular.io\" target=\"_blank\" rel=\"noopener\">Angular Mobile Toolkit</a> (Service Worker)</li>\n      </ul>\n    </mat-tab>\n    <mat-tab label=\"Deployments\">\n      <ul>\n        <li>Production and development builds</li>\n        <li>Simple\n          <a href=\"https://heroku.com\" target=\"_blank\" rel=\"noopener\">Heroku</a> deployment</li>\n      </ul>\n    </mat-tab>\n    <mat-tab label=\"SEO\">\n      <ul>\n        <li>HttpStateTransfer for caching server responses on client boostrap (no flickering on load)</li>\n        <li>SEO support for Title and Meta tags</li>\n        <li>\n          <a href=\"http://ogp.me\" target=\"_blank\" rel=\"noopener\">OG (Open Graph)</a> tags for social sharing</li>\n        <li>Vendor-agnostic analytics using\n          <a href=\"https://angulartics.github.io\">angulartics2</a>\n        </li>\n      </ul>\n    </mat-tab>\n  </mat-tab-group>\n</mat-card>\n"
  },
  {
    "path": "src/client/app/home/home.component.scss",
    "content": ":host {\n  #hero {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    h1 {\n      margin-bottom: 0;\n    }\n  }\n}\n"
  },
  {
    "path": "src/client/app/home/home.component.spec.ts",
    "content": "import { By } from '@angular/platform-browser'\nimport { Angulartics2 } from 'angulartics2'\nimport { HomeComponent } from './home.component'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { HomeModule } from './home.module'\nimport { MatRaisedButtonCssMatStyler } from '@angular/material'\nimport { AppTestingModule } from '../../../testing/app-testing.module'\n\ndescribe(HomeComponent.name, () => {\n  let fixture: ComponentFixture<HomeComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [AppTestingModule.forRoot(), HomeModule],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(HomeComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n    expect(fixture).toMatchSnapshot()\n  }))\n\n  it('should track event when link clicked', async(() => {\n    const analytics = TestBed.get(Angulartics2) as Angulartics2\n    expect(analytics).toBeDefined()\n    const button = fixture.debugElement.query(By.directive(MatRaisedButtonCssMatStyler))\n    expect(button).toBeTruthy()\n    const link = button.nativeElement as HTMLAnchorElement\n    expect(link).toBeTruthy()\n    analytics.eventTrack.subscribe(eventTrack => {\n      expect(eventTrack).toBeDefined()\n      expect(eventTrack.action).toEqual('ViewRepo')\n      expect(eventTrack.properties.eventType).toEqual('click')\n    })\n    link.click()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-home></pm-home>'\n})\nclass TestComponent { }\n"
  },
  {
    "path": "src/client/app/home/home.component.ts",
    "content": "import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core'\n\n@Component({\n  selector: 'pm-home',\n  templateUrl: './home.component.html',\n  styleUrls: ['./home.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class HomeComponent {\n  @HostBinding('class.card-float-container') containerClass = true\n}\n"
  },
  {
    "path": "src/client/app/home/home.module.ts",
    "content": "import { HomeRoutingModule } from './home-routing.module'\nimport { HomeComponent } from './home.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [HomeRoutingModule, SharedModule],\n  declarations: [HomeComponent],\n  exports: [HomeComponent]\n})\nexport class HomeModule { }\n"
  },
  {
    "path": "src/client/app/login/__snapshots__/login.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`LoginComponent should compile 1`] = `\n<pm-login>\n  <div\n    fxlayout=\"column\"\n    fxlayoutalign=\"center center\"\n  >\n    <h1>\n      Login\n    </h1>\n    <pm-login-card>\n      \n      \n    </pm-login-card>\n  </div>\n</pm-login>\n`;\n"
  },
  {
    "path": "src/client/app/login/login-routing.module.ts",
    "content": "import { LoginComponent } from './login.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: LoginComponent,\n        canActivate: [MetaGuard],\n        data: {\n          meta: {\n            title: 'i18n.login.title',\n            description: 'i18n.login.description'\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class LoginRoutingModule { }\n"
  },
  {
    "path": "src/client/app/login/login.component.e2e-spec.ts",
    "content": "import { baseUrl, browser } from '../../../../tools/test/jest.e2e-setup'\n\ndescribe('Login Page', () => {\n  it('should have title', async () => {\n    expect.assertions(1)\n\n    const page = browser.goto(`${baseUrl}/login`)\n\n    const text = await page.evaluate(() => document.title)\n\n    expect(text).toContain('Login - Fusebox Angular Universal Starter')\n  })\n})\n"
  },
  {
    "path": "src/client/app/login/login.component.html",
    "content": "<div fxLayout=\"column\" fxLayoutAlign=\"center center\">\n  <h1>Login</h1>\n  <pm-login-card></pm-login-card>\n</div>"
  },
  {
    "path": "src/client/app/login/login.component.scss",
    "content": ""
  },
  {
    "path": "src/client/app/login/login.component.spec.ts",
    "content": "import { AuthService } from './../shared/services/auth.service'\nimport { AngularFireAuthModule } from 'angularfire2/auth'\nimport { LoginComponent } from './login.component'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { LoginModule } from './login.module'\nimport { AngularFireModule } from 'angularfire2'\nimport { AppTestingModule } from '../../../testing/app-testing.module'\n\ndescribe(LoginComponent.name, () => {\n  let fixture: ComponentFixture<LoginComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [AppTestingModule.forRoot(), LoginModule, AngularFireModule.initializeApp({\n        'apiKey': '1',\n        'authDomain': 'app.firebaseapp.com',\n        'databaseURL': 'https://app.firebaseio.com',\n        'projectId': 'firebase-app',\n        'messagingSenderId': '1'\n      }), AngularFireAuthModule],\n      declarations: [TestComponent],\n      providers: [\n        { provide: AuthService, userValue: { }}\n      ]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(LoginComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n    expect(fixture).toMatchSnapshot()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-login></pm-login>'\n})\nclass TestComponent {}\n"
  },
  {
    "path": "src/client/app/login/login.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-login',\n  templateUrl: './login.component.html',\n  styleUrls: ['./login.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class LoginComponent {\n}\n"
  },
  {
    "path": "src/client/app/login/login.module.ts",
    "content": "import { LoginComponent } from './login.component'\nimport { LoginRoutingModule } from './login-routing.module'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [LoginRoutingModule, SharedModule],\n  declarations: [LoginComponent],\n  exports: [LoginComponent]\n})\nexport class LoginModule { }\n"
  },
  {
    "path": "src/client/app/logout/__snapshots__/logout.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`LogoutComponent should compile 1`] = `\n<pm-logout>\n  You have been logged out. Come back soon!\n</pm-logout>\n`;\n"
  },
  {
    "path": "src/client/app/logout/logout-routing.module.ts",
    "content": "import { LogoutComponent } from './logout.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: LogoutComponent,\n        canActivate: [MetaGuard],\n        data: {\n          meta: {\n            title: 'i18n.logout.title',\n            description: 'i18n.logout.description'\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class LogoutRoutingModule { }\n"
  },
  {
    "path": "src/client/app/logout/logout.component.e2e-spec.ts",
    "content": "import { baseUrl, browser } from '../../../../tools/test/jest.e2e-setup'\n\ndescribe('Logout Page', () => {\n  it('should have title', async () => {\n    expect.assertions(1)\n\n    const page = browser.goto(`${baseUrl}/logout`)\n\n    const text = await page.evaluate(() => document.title)\n\n    expect(text).toContain('Logged Out - Fusebox Angular Universal Starter')\n  })\n})\n"
  },
  {
    "path": "src/client/app/logout/logout.component.html",
    "content": "You have been logged out. Come back soon!"
  },
  {
    "path": "src/client/app/logout/logout.component.scss",
    "content": ""
  },
  {
    "path": "src/client/app/logout/logout.component.spec.ts",
    "content": "import { AngularFireAuthModule } from 'angularfire2/auth'\nimport { LogoutComponent } from './logout.component'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { LogoutModule } from './logout.module'\nimport { AngularFireModule } from 'angularfire2'\nimport { AppTestingModule } from '../../../testing/app-testing.module'\n\ndescribe(LogoutComponent.name, () => {\n  let fixture: ComponentFixture<LogoutComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [AppTestingModule.forRoot(), LogoutModule, AngularFireAuthModule, AngularFireModule.initializeApp({\n        'apiKey': '1',\n        'authDomain': 'app.firebaseapp.com',\n        'databaseURL': 'https://app.firebaseio.com',\n        'projectId': 'firebase-app',\n        'messagingSenderId': '1'\n      })],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(LogoutComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n    expect(fixture).toMatchSnapshot()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-logout></pm-logout>'\n})\nclass TestComponent { }\n"
  },
  {
    "path": "src/client/app/logout/logout.component.ts",
    "content": "import { PlatformService } from './../shared/services/platform.service'\nimport { CookieService } from './../shared/services/cookie.service'\nimport { AngularFireAuth } from 'angularfire2/auth'\nimport { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-logout',\n  templateUrl: './logout.component.html',\n  styleUrls: ['./logout.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class LogoutComponent {\n  constructor(afAuth: AngularFireAuth, cs: CookieService, ps: PlatformService) {\n    cs.remove('fbJwt')\n    cs.remove('fbPhotoURL')\n    cs.remove('fbProviderId')\n    cs.remove('fbEmail')\n    cs.remove('fbDisplayName')\n    if (ps.isBrowser) {\n      afAuth.auth.signOut()\n    }\n  }\n}\n"
  },
  {
    "path": "src/client/app/logout/logout.module.ts",
    "content": "import { LogoutRoutingModule } from './logout-routing.module'\nimport { LogoutComponent } from './logout.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [LogoutRoutingModule, SharedModule],\n  declarations: [LogoutComponent],\n  exports: [LogoutComponent]\n})\nexport class LogoutModule { }\n"
  },
  {
    "path": "src/client/app/not-found/__snapshots__/not-found.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`NotFoundComponent should compile 1`] = `\n<test-component>\n  <pm-not-found\n    class=\"vert-flex-fill\"\n  >\n    \n    <div\n      class=\"vert-flex-fill\"\n      ng-reflect-ng-switch=\"false\"\n    >\n      \n      \n      <div\n        class=\"vert-flex-fill\"\n      >\n        <div\n          ng-reflect-html=\"SafeValue must use [property]=\"\n          pmhtmloutlet=\"\"\n        />\n        <footer\n          class=\"footer mat-elevation-z6\"\n        >\n          \n          <button\n            class=\"mat-raised-button mat-accent\"\n            color=\"accent\"\n            mat-raised-button=\"\"\n            ng-reflect-color=\"accent\"\n          >\n            <span\n              class=\"mat-button-wrapper\"\n            >\n              Edit\n            </span>\n            <div\n              class=\"mat-button-ripple mat-ripple\"\n              matripple=\"\"\n              ng-reflect-centered=\"false\"\n              ng-reflect-disabled=\"false\"\n              ng-reflect-trigger=\"[object HTMLButtonElement]\"\n            />\n            <div\n              class=\"mat-button-focus-overlay\"\n            />\n          </button>\n        </footer>\n      </div>\n    </div>\n  </pm-not-found>\n</test-component>\n`;\n"
  },
  {
    "path": "src/client/app/not-found/not-found-routing.module.ts",
    "content": "import { NotFoundComponent } from './not-found.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '**',\n        component: NotFoundComponent,\n        data: {\n          // meta: {\n          //   title: 'i18n.not-found.title',\n          //   description: 'i18n.not-found.description'\n          // }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class NotFoundRoutingModule { }\n"
  },
  {
    "path": "src/client/app/not-found/not-found.component.e2e-spec.ts",
    "content": "import { baseUrl, browser } from '../../../../tools/test/jest.e2e-setup'\n\ndescribe('NotFound Page', () => {\n  it('should have title', async () => {\n    const page = browser.goto(`${baseUrl}/not-found`)\n\n    const text = await page.evaluate(() => document.title)\n\n    expect(text).toContain('Not Found - Fusebox Angular Universal Starter')\n  })\n})\n"
  },
  {
    "path": "src/client/app/not-found/not-found.component.html",
    "content": "<div *ngIf=\"view$ | async as view\" [ngSwitch]=\"view.isEditing\" class=\"vert-flex-fill\">\n  <div *ngSwitchCase=\"true\" class=\"vert-flex-fill\">\n    <mat-tab-group [selectedIndex]=\"view.currentTab\" (selectedIndexChange)=\"updateTabParam($event)\">\n      <mat-tab label=\"Content\">\n        <pm-quill-editor [content]=\"view.page.content\"></pm-quill-editor>\n      </mat-tab>\n      <mat-tab label=\"Settings\">\n        <form [formGroup]=\"settingsForm\" id=\"settingsForm\" novalidate class=\"settings\" *ngIf=\"view.currentTab === 1\">\n          <mat-accordion class=\"example-headers-align\">\n            <mat-expansion-panel hideToggle=\"true\">\n              <mat-expansion-panel-header>\n                <mat-panel-title>\n                  Basic Info\n                </mat-panel-title>\n                <mat-panel-description>\n                  Title, description, and other page information\n                  <mat-icon fontSet=\"fa\" fontIcon=\"fa-info\" aria-hidden=\"true\"></mat-icon>\n                </mat-panel-description>\n              </mat-expansion-panel-header>\n              <div class=\"field-spacer\">\n                <mat-form-field>\n                  <input matInput placeholder=\"Title\" type=\"text\" formControlName=\"title\">\n                  <mat-hint align=\"end\">appears in\n                    <em>title</em> and\n                    <em>meta:og:title</em>\n                  </mat-hint>\n                </mat-form-field>\n                <mat-form-field>\n                  <input matInput placeholder=\"Description\" type=\"text\" formControlName=\"description\">\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:name:description</em> and\n                    <em>meta:og:description</em>\n                  </mat-hint>\n                </mat-form-field>\n                <mat-form-field>\n                  <input matInput placeholder=\"Type\" type=\"text\" formControlName=\"type\">\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:type</em>\n                  </mat-hint>\n                </mat-form-field>\n              </div>\n              <div class=\"field-spacer\">\n                <mat-form-field>\n                  <input matInput placeholder=\"Published\" [matDatepicker]=\"picker\" formControlName=\"articlePublishedTime\" (focus)=\"picker.open()\">\n                  <mat-datepicker touchUi=\"true\" #picker></mat-datepicker>\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:article:published_time</em>\n                  </mat-hint>\n                </mat-form-field>\n                <mat-form-field>\n                  <input matInput placeholder=\"Modified\" [matDatepicker]=\"pickerModified\" formControlName=\"articleModifiedTime\" (focus)=\"pickerModified.open()\">\n                  <mat-datepicker touchUi=\"true\" #pickerModified></mat-datepicker>\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:article:modified_time</em>\n                  </mat-hint>\n                </mat-form-field>\n                <mat-form-field>\n                  <input matInput placeholder=\"Expiration\" [matDatepicker]=\"pickerExpired\" formControlName=\"articleExpirationTime\" (focus)=\"pickerExpired.open()\">\n                  <mat-datepicker touchUi=\"true\" #pickerExpired></mat-datepicker>\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:article:expiration_time</em>\n                  </mat-hint>\n                </mat-form-field>\n              </div>\n              <div class=\"field-spacer\">\n                <mat-form-field>\n                  <input matInput placeholder=\"Author\" type=\"text\" formControlName=\"articleAuthor\">\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:article:author</em>\n                  </mat-hint>\n                </mat-form-field>\n                <mat-form-field>\n                  <input matInput placeholder=\"Section\" type=\"text\" formControlName=\"articleSection\">\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:article:section</em>\n                  </mat-hint>\n                </mat-form-field>\n              </div>\n              <div class=\"field-spacer\">\n                <mat-form-field>\n                  <!-- <input matInput placeholder=\"Tags\" type=\"text\" formControlName=\"articleTag\"> -->\n                  <mat-chip-list #chipList>\n                    <mat-chip *ngFor=\"let tag of tags; trackBy: trackByTag\" [selectable]=\"selectable\">\n                      {{ tag }}\n                      <mat-icon fontSet=\"fa\" fontIcon=\"fa-times\" aria-hidden=\"true\" (click)=\"remove(tag)\"></mat-icon>\n                    </mat-chip>\n                    <input placeholder=\"New tag...\" [matChipInputFor]=\"chipList\" [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\" [matChipInputAddOnBlur]=\"addOnBlur\"\n                      (matChipInputTokenEnd)=\"add($event)\" />\n                  </mat-chip-list>\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:article:tag</em>\n                  </mat-hint>\n                </mat-form-field>\n              </div>\n            </mat-expansion-panel>\n            <mat-expansion-panel hideToggle=\"true\">\n              <mat-expansion-panel-header>\n                <mat-panel-title>\n                  Caching\n                </mat-panel-title>\n                <mat-panel-description>\n                  HTTP cache settings\n                  <mat-icon fontSet=\"fa\" fontIcon=\"fa-globe\" aria-hidden=\"true\"></mat-icon>\n                </mat-panel-description>\n              </mat-expansion-panel-header>\n              <pm-cache-form (cacheChange)=\"updateCache($event)\" [cache]=\"cacheSettings\"></pm-cache-form>\n            </mat-expansion-panel>\n            <mat-expansion-panel hideToggle=\"true\">\n              <mat-expansion-panel-header>\n                <mat-panel-title>\n                  Images\n                </mat-panel-title>\n                <mat-panel-description>\n                  Image settings for the Open Graph protocol\n                  <mat-icon fontSet=\"fa\" fontIcon=\"fa-picture-o\" aria-hidden=\"true\"></mat-icon>\n                </mat-panel-description>\n              </mat-expansion-panel-header>\n              <div class=\"field-spacer\">\n                <mat-form-field>\n                  <input matInput placeholder=\"Image URL\" type=\"text\" formControlName=\"imgUrl\">\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:image</em>\n                  </mat-hint>\n                </mat-form-field>\n                <mat-form-field>\n                  <input matInput placeholder=\"Image Alternate Text\" type=\"text\" formControlName=\"imgAlt\">\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:image:alt</em>\n                  </mat-hint>\n                </mat-form-field>\n              </div>\n              <div class=\"field-spacer\">\n                <mat-form-field>\n                  <input matInput placeholder=\"Image Height\" type=\"number\" formControlName=\"imgHeight\">\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:image:height</em>\n                  </mat-hint>\n                </mat-form-field>\n                <mat-form-field>\n                  <input matInput placeholder=\"Image Width\" type=\"number\" formControlName=\"imgWidth\">\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:image:width</em>\n                  </mat-hint>\n                </mat-form-field>\n                <mat-form-field>\n                  <input matInput placeholder=\"Image Mime Type\" type=\"text\" formControlName=\"imgMime\">\n                  <mat-hint align=\"end\">appears in\n                    <em>meta:og:image:type</em>\n                  </mat-hint>\n                </mat-form-field>\n              </div>\n            </mat-expansion-panel>\n            <mat-expansion-panel hideToggle=\"true\" disabled>\n              <mat-expansion-panel-header>\n                <mat-panel-title>\n                  Music\n                </mat-panel-title>\n                <mat-panel-description>\n                  Music settings for the Open Graph protocol\n                  <mat-icon fontSet=\"fa\" fontIcon=\"fa-music\" aria-hidden=\"true\"></mat-icon>\n                </mat-panel-description>\n              </mat-expansion-panel-header>\n            </mat-expansion-panel>\n            <mat-expansion-panel hideToggle=\"true\" disabled>\n              <mat-expansion-panel-header>\n                <mat-panel-title>\n                  Videos\n                </mat-panel-title>\n                <mat-panel-description>\n                  Video settings for the Open Graph protocol\n                  <mat-icon fontSet=\"fa\" fontIcon=\"fa-video-camera\" aria-hidden=\"true\"></mat-icon>\n                </mat-panel-description>\n              </mat-expansion-panel-header>\n            </mat-expansion-panel>\n          </mat-accordion>\n          <br>\n          <mat-slide-toggle formControlName=\"userCommentsEnabled\">User Comments Enabled</mat-slide-toggle>\n          <mat-slide-toggle formControlName=\"isDraft\">Draft</mat-slide-toggle>\n        </form>\n      </mat-tab>\n      <mat-tab label=\"Dynamic Code & Styling\">\n        <div class=\"pad-tab\">\n          <h3 mat-subheader>Add custom css and javascript for this page</h3>\n          <div class=\"card-list\" *ngFor=\"let keyVal of injections$ | async | pmKeyVal; trackBy: trackByInjection\">\n            <button mat-icon-button>\n              <mat-icon fontSet=\"fa\" fontIcon=\"fa-minus-circle\" color=\"warn\" aria-hidden=\"true\" (click)=\"removeInjectable(keyVal.key)\"></mat-icon>\n            </button>\n            <mat-card>\n              <mat-form-field>\n                <input matInput placeholder=\"Name\" type=\"text\" [value]=\"keyVal.key\" #keyTest (keydown)=\"injectionFormChange($event, keyVal.value.type, $event)\">\n              </mat-form-field>\n              <div [ngSwitch]=\"keyVal.value.type\">\n                <pm-injection-form *ngSwitchCase=\"'style'\" [injectable]=\"keyVal.value\" showDomString=\"true\" (formChange)=\"injectionFormChange(keyTest.value, keyVal.value.type, $event)\"></pm-injection-form>\n                <pm-injection-form *ngSwitchCase=\"'script'\" [injectable]=\"keyVal.value\" showDomString=\"true\" (formChange)=\"injectionFormChange(keyTest.value, keyVal.value.type, $event)\"></pm-injection-form>\n              </div>\n            </mat-card>\n          </div>\n          <br>\n          <div>\n            <button mat-raised-button color=\"primary\" (click)=\"addInjectable('style')\">Add CSS</button>\n            <button mat-raised-button color=\"primary\" (click)=\"addInjectable('script')\">Add JavaScript</button>\n          </div>\n          <br>\n        </div>\n      </mat-tab>\n      <mat-tab label=\"Relationships\" disabled>\n      </mat-tab>\n      <mat-tab label=\"Privileges\" disabled>\n      </mat-tab>\n      <mat-tab label=\"Change History\" disabled>\n      </mat-tab>\n    </mat-tab-group>\n    <footer class=\"footer mat-elevation-z6\">\n      <button mat-button color=\"warn\" (click)=\"delete()\">Delete</button>\n      <button mat-button (click)=\"viewCurrent()\">View Current</button>\n      <button mat-raised-button color=\"primary\" (click)=\"publish()\" [disabled]=\"settingsForm.invalid\">Publish</button>\n    </footer>\n  </div>\n  <div *ngSwitchCase=\"false\" class=\"vert-flex-fill\">\n    <div pmHtmlOutlet [html]=\"view.page.content | pmSanitizeHtml\"></div>\n    <footer class=\"footer mat-elevation-z6\">\n      <button *ngIf=\"view.canEdit\" mat-raised-button color=\"accent\" (click)=\"edit()\">Edit</button>\n    </footer>\n  </div>\n</div>"
  },
  {
    "path": "src/client/app/not-found/not-found.component.scss",
    "content": ":host {\n  .footer {\n    text-align: right;\n    padding: .75em;\n  }\n   ::ng-deep mat-tab-group {\n    flex: 1;\n    height: 100%;\n    .mat-tab-body-wrapper {\n      height: 100%;\n    }\n    mat-tab-body {\n      .mat-tab-body-content {\n        overflow: overlay;\n      } // overflow-y: overlay;\n      // .mat-tab-body-content {\n      //   overflow-y: overlay;\n      // }\n    }\n  }\n  .settings {\n    display: flex;\n    flex-direction: column;\n    padding: 1em;\n    height: 100%;\n    overflow-y: auto;\n  }\n   ::ng-deep dynamic-html {\n    display: -webkit-inline-box;\n    overflow-y: auto;\n    height: 100%;\n  }\n   ::ng-deep mat-form-field {\n    padding-bottom: 1em;\n  }\n  .example-headers-align .mat-expansion-panel-header-title,\n  .example-headers-align .mat-expansion-panel-header-description {\n    flex-basis: 0;\n  }\n  .example-headers-align .mat-expansion-panel-header-description {\n    justify-content: space-between;\n    align-items: center;\n  }\n  .field-spacer {\n    display: flex;\n    flex: 1;\n    mat-form-field {\n      width: 100%;\n      margin-right: 1em;\n    }\n    @media only screen and (max-width: 959px) {\n      flex-direction: column;\n    }\n  }\n   ::ng-deep mat-chip {\n    mat-icon {\n      height: auto;\n      width: auto;\n      margin-left: 8px;\n      padding-bottom: 4px;\n      &:hover {\n        cursor: pointer;\n      }\n    }\n  }\n  .pad-tab {\n    padding: 0 1em;\n  }\n  .inj-list {\n    mat-list-item {\n      margin: 1em 0;\n    }\n    mat-card {\n      width: 100%;\n      padding: 1em;\n    }\n  }\n  .card-list {\n    margin-bottom: 1em;   \n    display: flex;\n    align-items: center;\n    mat-card {\n      width: 100%;\n      display: inherit;\n    }\n  }\n}"
  },
  {
    "path": "src/client/app/not-found/not-found.component.spec.ts",
    "content": "import { NotFoundComponent } from './not-found.component'\nimport { AuthService } from './../shared/services/auth.service'\nimport { of } from 'rxjs/observable/of'\nimport { TransferState } from '@angular/platform-browser'\nimport { ServerResponseService } from './../shared/services/server-response.service'\nimport { Observable } from 'rxjs/Observable'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { NotFoundModule } from './not-found.module'\nimport { FirebaseDatabaseService } from '../shared/services/firebase-database.service'\nimport { AppTestingModule } from '../../../testing/app-testing.module'\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-not-found></pm-not-found>'\n})\nclass TestComponent { }\n\ndescribe(NotFoundComponent.name, () => {\n  let fixture: ComponentFixture<TestComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [NotFoundModule, AppTestingModule.forRoot()],\n      declarations: [TestComponent],\n      providers: [\n        ServerResponseService,\n        TransferState,\n        {\n          provide: FirebaseDatabaseService,\n          useValue: {\n            get() {\n              return of({})\n            }\n          }\n        },\n        {\n          provide: AuthService,\n          useValue: {\n            user$: Observable.of({})\n          }\n        }\n      ]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(TestComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should compile', async(() => {\n    fixture.detectChanges()\n    expect(fixture.componentInstance).toBeDefined()\n    expect(fixture).toMatchSnapshot()\n  }))\n})\n"
  },
  {
    "path": "src/client/app/not-found/not-found.component.ts",
    "content": "import { BehaviorSubject } from 'rxjs/BehaviorSubject'\nimport { DOMInjectable } from './../shared/services/injection.service'\nimport { ChangeDetectionStrategy, Component, HostBinding, ViewChild } from '@angular/core'\nimport { QuillEditorComponent } from './../shared/quill-editor/quill-editor.component'\nimport { FirebaseDatabaseService } from './../shared/services/firebase-database.service'\nimport { AuthService } from './../shared/services/auth.service'\nimport { FormControl, FormGroup, Validators } from '@angular/forms'\nimport { ServerResponseService } from './../shared/services/server-response.service'\nimport { ActivatedRoute, Router } from '@angular/router'\nimport { Observable } from 'rxjs/Observable'\nimport { filter } from 'rxjs/operators'\nimport { SEONode, SEOService } from '../shared/services/seo.service'\nimport { MatChipInputEvent, MatDialog, MatSnackBar } from '@angular/material'\nimport { ModalConfirmationComponent } from '../shared/modal-confirmation/modal-confirmation.component'\nimport { ENTER } from '@angular/cdk/keycodes'\n\nconst COMMA = 188\n\nexport interface Page {\n  readonly content: string\n  readonly title: string\n  readonly isDraft: boolean\n  readonly userCommentsEnabled?: boolean\n  readonly cache?: { readonly [key: string]: boolean | string | number }\n  readonly imgWidth?: number,\n  readonly imgHeight?: number,\n  readonly imgAlt?: string,\n  readonly imgUrl?: string,\n  readonly imgMime?: string\n  readonly articleTag?: ReadonlyArray<string>\n}\n\ntype types = 'script' | 'style'\ninterface InjectionMap { readonly [key: string]: { readonly type: types, readonly injectable: DOMInjectable } }\n\n@Component({\n  selector: 'pm-not-found',\n  templateUrl: './not-found.component.html',\n  styleUrls: ['./not-found.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class NotFoundComponent {\n  @HostBinding('class.vert-flex-fill') readonly flexFill = true\n  @ViewChild(QuillEditorComponent) readonly editor: QuillEditorComponent\n\n  readonly addOnBlur = true\n  readonly separatorKeysCodes: ReadonlyArray<any> = [ENTER, COMMA]\n  tags: ReadonlyArray<string> = []\n  readonly injections$ = new BehaviorSubject<InjectionMap>({})\n  readonly injectionsToSave$ = new BehaviorSubject<InjectionMap>({})\n  readonly styleInjDefault = {\n    element: 'link',\n    attributes: {\n      href: 'https://',\n      type: 'text/css',\n      rel: 'stylesheet'\n    }\n  }\n\n  cacheSettings = {} as any\n\n  injectionFormChange(key: string, type: types, injectable: DOMInjectable) {\n    console.log(key)\n    // this.injectionsToSave$.next({\n    //   ...this.injections$.getValue(),\n    //   [key]: {\n    //     type,\n    //     injectable\n    //   }\n    // })\n  }\n\n  addInjectable(type: types) {\n    this.injections$.next({\n      ...this.injections$.getValue(),\n      [`${type.toString()}_${Math.random().toPrecision(4)}`]: {\n        type,\n        injectable: {} as any\n      }\n    })\n  }\n\n  insertInjectable(key: string, type: types, injectable: DOMInjectable) {\n    this.injections$.next({\n      ...this.injections$.getValue(),\n      [key]: {\n        type,\n        injectable\n      }\n    })\n  }\n\n  removeInjectable(key: string) {\n    const current = this.injections$.getValue()\n    this.injections$.next({\n      ...Object.keys(current)\n        .filter(k => k !== key)\n        .reduce((a, c) => ({ ...a, [c]: current[c] }), {})\n    })\n  }\n\n  add(event: MatChipInputEvent): void {\n    if (event.input) event.input.value = '' // clear input value\n    this.tags = [...this.tags, event.value.trim()]\n      .filter(a => a !== '')\n      .filter((elem, pos, arr) => arr.indexOf(elem) === pos)\n    this.updateTags()\n  }\n\n  remove(tag: any): void {\n    this.tags = [...this.tags].filter(a => a !== tag)\n    this.updateTags()\n  }\n\n  updateTags() {\n    this.settingsForm.controls['articleTag'].setValue(this.tags)\n  }\n\n  updateTabParam(tab: number) {\n    this.router.navigate([this.router.url.split('?')[0]], { queryParams: { tab }, queryParamsHandling: 'merge' })\n  }\n\n  private readonly params$ = this.ar.queryParams.shareReplay()\n\n  private readonly isEditMode$ = this.params$\n    .map(a => a.edit ? true : false)\n\n  private readonly currentTab$ = this.params$\n    .map(a => a.tab ? +a.tab : 0)\n\n  private readonly url$ = Observable.of(this.router.url.split('?')[0])\n    .pipe(filter(a => !a.includes('.')))\n    .shareReplay()\n\n  public readonly settingsForm = new FormGroup({\n    type: new FormControl('website', [Validators.required]),\n    title: new FormControl('', [Validators.required]),\n    description: new FormControl('', [Validators.required, Validators.max(158)]),\n    imgUrl: new FormControl('', []),\n    imgAlt: new FormControl('', []),\n    imgMime: new FormControl('', []),\n    imgHeight: new FormControl('', [Validators.min(1)]),\n    imgWidth: new FormControl('', [Validators.min(1)]),\n    articlePublishedTime: new FormControl(new Date(), []),\n    articleModifiedTime: new FormControl('', []),\n    articleExpirationTime: new FormControl('', []),\n    articleAuthor: new FormControl('', []),\n    articleSection: new FormControl('', []),\n    articleTag: new FormControl('', []),\n    userCommentsEnabled: new FormControl('', []),\n    isDraft: new FormControl('', [])\n  })\n\n  public readonly page$ = this.url$\n    .flatMap(url => this.db\n      .get<Page & SEONode>(`/pages/${url}`)\n      .flatMap(page => this.isEditMode$, (page, editMode) => ({ page, editMode }))\n      .map(res => {\n        if (res && res.page && (res.editMode || !res.page.isDraft)) {\n          if (!res.editMode) {\n            const pageCacheSettings = res.page.cache || {}\n            const cacheControl = Object.keys(pageCacheSettings)\n              .filter(key => pageCacheSettings[key])\n              .reduce((acc, curr) => {\n                const ret = typeof pageCacheSettings[curr] === 'boolean'\n                  ? curr\n                  : `${curr}=${pageCacheSettings[curr]}`\n\n                return acc.concat(', ').concat(ret)\n              }, '')\n              .replace(/(^,)|(,$)/g, '')\n              .trim()\n\n            if (cacheControl) {\n              this.rs.setHeader('Cache-Control', cacheControl)\n            } else {\n              this.rs.setCacheNone()\n            }\n          } else {\n            this.rs.setCacheNone()\n          }\n\n          return {\n            ...res.page,\n            content: res.page.content\n          }\n        }\n        this.rs.setNotFound()\n        this.rs.setCacheNone()\n        return {\n          ...res.page,\n          content: 'not found'\n        } as Page & SEONode\n      })\n      .do(page => {\n        this.cacheSettings = page.cache\n        this.seo.updateNode({\n          title: page.title,\n          description: page.description,\n          img: {\n            width: page.imgWidth,\n            height: page.imgHeight,\n            type: page.imgMime,\n            alt: page.imgAlt,\n            url: page.imgUrl\n          },\n          tags: page.articleTag\n        })\n\n        // tslint:disable:no-null-keyword\n        const formValues = Object.keys(this.settingsForm.controls)\n          .reduce((acc: any, controlKey) =>\n            ({ ...acc, [controlKey]: (page as any)[controlKey] || this.settingsForm.controls[controlKey].value || null }), {})\n        this.settingsForm.setValue(formValues)\n        this.tags = page.articleTag || []\n      })\n      .catch(err => {\n        if (err.code === 'PERMISSION_DENIED') {\n          this.rs.setStatus(401)\n          return Observable.of({\n            content: 'unauthorized'\n          })\n        } else {\n          this.rs.setError()\n          return Observable.of({\n            content: err || 'server error'\n          })\n        }\n      }))\n\n  readonly view$ = Observable.combineLatest(this.auth.user$, this.page$, this.currentTab$,\n    this.ar.queryParams.pluck('edit').map(a => a === 'true'),\n    (user, page, currentTab, isEditing) => {\n      return {\n        currentTab,\n        canEdit: true, // for demo only, user && user.roles && user.roles.admin,\n        isEditing,\n        page\n      }\n    })\n    .catch(err => {\n      this.rs.setError()\n      return Observable.of({\n        content: 'server error'\n      })\n    })\n\n  publish() {\n    const injections = this.injectionsToSave$.getValue()\n    const cache = this.cacheSettings\n\n    const settings = Object.keys(this.settingsForm.value)\n      .filter(key => typeof this.settingsForm.value[key] !== 'undefined')\n      .reduce((acc, curr) => ({ ...acc, [curr]: this.settingsForm.value[curr] }) as any, {})\n\n    this.url$.flatMap(url => this.db.getObjectRef(`/pages/${url}`)\n      .update({\n        ...settings,\n        injections,\n        cache,\n        content: this.editor.textValue.getValue()\n      }), (url, update) => ({ url, update }))\n      .take(1)\n      .subscribe(a => {\n        this.router.navigate([a.url])\n        this.showSnack('Published! Page is now live.')\n      })\n  }\n\n  showSnack(message: string) {\n    this.snackBar.open(message, 'dismiss', {\n      duration: 2000,\n      horizontalPosition: 'left',\n      verticalPosition: 'bottom'\n    })\n  }\n\n  confirmDelete() {\n    return this.dialog.open(ModalConfirmationComponent, {\n      width: '460px',\n      position: {\n        top: '30px'\n      },\n      data: {\n        message: 'Deleting this page will immediately remove it from the database and anyone reading it',\n        title: 'Are you sure?'\n      }\n    })\n  }\n\n  delete() {\n    this.confirmDelete()\n      .afterClosed()\n      .filter(Boolean)\n      .flatMap(() => this.url$)\n      .flatMap(url => this.db.getObjectRef(`/pages/${url}`).remove(), (url, update) => ({ url, update }))\n      .take(1)\n      .subscribe(a => {\n        this.router.navigate(['/pages'])\n        this.showSnack('Page removed!')\n      })\n  }\n\n  viewCurrent() {\n    this.url$\n      .take(1)\n      .subscribe(url => this.router.navigate([url]))\n  }\n\n  edit() {\n    this.url$\n      .take(1)\n      .subscribe(url => this.router.navigate([url], { queryParams: { edit: true } }))\n  }\n\n  constructor(private rs: ServerResponseService, private db: FirebaseDatabaseService, private seo: SEOService,\n    public auth: AuthService, private ar: ActivatedRoute, private router: Router, private dialog: MatDialog,\n    private snackBar: MatSnackBar) {\n  }\n\n  updateCache(cache: any) {\n    this.cacheSettings = cache\n  }\n\n  trackByInjection(index: number, item: any) {\n    return item.key\n  }\n\n  trackByTag(index: number, item: string) {\n    return item\n  }\n}\n"
  },
  {
    "path": "src/client/app/not-found/not-found.module.ts",
    "content": "import { NotFoundRoutingModule } from './not-found-routing.module'\nimport { NotFoundComponent } from './not-found.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [NotFoundRoutingModule, SharedModule],\n  declarations: [NotFoundComponent],\n  exports: [NotFoundComponent]\n})\nexport class NotFoundModule { }\n"
  },
  {
    "path": "src/client/app/pages/page-form/page-form.component.html",
    "content": "<h2>New Page</h2>\n<form [formGroup]=\"form\" id=\"pageForm\" novalidate>\n  <mat-form-field>\n    <input matInput placeholder=\"Slug\" formControlName=\"slug\">\n    <mat-error *ngIf=\"form.controls.slug.hasError('pattern')\">\n      must contain a forward slash and name\n    </mat-error>\n    <mat-error *ngIf=\"form.controls.slug.hasError('slugTaken')\">\n      slug already taken\n    </mat-error>\n    <mat-error *ngIf=\"form.controls.slug.hasError('required')\">\n      <span>slug is\n        <strong>required</strong>\n      </span>\n    </mat-error>\n  </mat-form-field>\n  <mat-form-field>\n    <input matInput placeholder=\"Title\" formControlName=\"title\">\n    <mat-error *ngIf=\"form.controls.title.hasError('required')\">\n      <span>title is\n        <strong>required</strong>\n      </span>\n    </mat-error>\n  </mat-form-field>\n  <mat-form-field>\n    <input matInput placeholder=\"Description\" formControlName=\"description\">\n    <mat-error *ngIf=\"form.controls.description.hasError('required')\">\n      <span>description is\n        <strong>required</strong>\n      </span>\n    </mat-error>\n  </mat-form-field>\n  <mat-slide-toggle formControlName=\"isDraft\">\n    Draft (not visible to public)\n  </mat-slide-toggle>\n  <br>\n  <button mat-raised-button color=\"primary\" type=\"text\" [disabled]=\"form.invalid\" (click)=\"create()\">Create</button>\n</form>"
  },
  {
    "path": "src/client/app/pages/page-form/page-form.component.scss",
    "content": ":host {\n  h2 {\n    margin: 0 0 1em 0;\n  }\n  form {\n    display: flex;\n    flex-direction: column;\n  }\n}"
  },
  {
    "path": "src/client/app/pages/page-form/page-form.component.ts",
    "content": "import { FirebaseDatabaseService } from '../../shared/services/firebase-database.service'\nimport { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core'\nimport { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms'\nimport { MatDialogRef } from '@angular/material'\nimport { ModalConfirmationComponent } from '../../shared/modal-confirmation/modal-confirmation.component'\n\n@Component({\n  selector: 'pm-page-form',\n  templateUrl: './page-form.component.html',\n  styleUrls: ['./page-form.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class PageFormComponent {\n  @Output() created = new EventEmitter()\n\n  public form = new FormGroup({\n    slug: new FormControl('blog/', [\n      Validators.required,\n      // Validators.pattern(/^[^/A-Za-z0-9]+(?:-[A-Za-z0-9]+)*$/),\n      Validators.pattern('^[^/]+/[^/]+$')\n    ], this.validateSlugNotTaken.bind(this)),\n    title: new FormControl('', [\n      Validators.required\n    ]),\n    description: new FormControl('', [\n      Validators.required\n    ]),\n    isDraft: new FormControl(true, [])\n  })\n\n  // tslint:disable:no-null-keyword\n  validateSlugNotTaken(control: AbstractControl) {\n    return this.db\n      .getObjectRef(`/pages/${control.value}`)\n      .valueChanges()\n      .debounceTime(200)\n      .take(1)\n      .map(a => a ? ({ slugTaken: true }) : null)\n  }\n\n  create() {\n    this.db.getObjectRef(`/pages/${this.form.value.slug}`).update({\n      title: this.form.value.title,\n      description: this.form.value.description,\n      isDraft: this.form.value.isDraft,\n      userCommentsEnabled: false,\n      cache: {\n        'no-cache': true,\n        'no-store': true,\n        'must-revalidate': true\n      }\n    })\n    .then(() => this.dialog.close(true))\n    .catch(() => this.dialog.close(false))\n  }\n\n  constructor(private db: FirebaseDatabaseService, public dialog: MatDialogRef<ModalConfirmationComponent>) { }\n}\n"
  },
  {
    "path": "src/client/app/pages/pages-routing.module.ts",
    "content": "import { PagesComponent } from './pages.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: PagesComponent,\n        canActivate: [MetaGuard],\n        data: {\n          // meta: {\n          //   title: 'i18n.admin.title',\n          //   description: 'i18n.admin.description'\n          // },\n          response: {\n            cache: {\n              directive: 'private'\n            }\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class PagesRoutingModule { }\n"
  },
  {
    "path": "src/client/app/pages/pages.component.html",
    "content": "<div *ngIf=\"view$ | async as view\">\n  <div class=\"flex\">\n    <h1>Pages</h1>\n    <button mat-fab color=\"accent\" (click)=\"openDialog()\">\n      <mat-icon fontSet=\"fa\" fontIcon=\"fa-plus\" aria-label=\"create a page\"></mat-icon>\n    </button>\n  </div>\n  <mat-tab-group [selectedIndex]=\"view.currentTab\" (selectedIndexChange)=\"updateTabParam($event)\">\n    <mat-tab *ngFor=\"let group of view.groups; trackBy: trackByGroup\" [label]=\"group.name\">\n      <mat-table #table [dataSource]=\"group.table\">\n        <ng-container matColumnDef=\"slug\">\n          <mat-header-cell *matHeaderCellDef>Slug</mat-header-cell>\n          <mat-cell *matCellDef=\"let element\">\n            <a [routerLink]=\"element.routerLink\">{{ element.slug }}</a>\n          </mat-cell>\n        </ng-container>\n\n        <ng-container matColumnDef=\"edit\">\n          <mat-header-cell *matHeaderCellDef></mat-header-cell>\n          <mat-cell *matCellDef=\"let element\">\n            <a mat-icon-button [routerLink]=\"element.routerLink\" [queryParams]=\"{ 'edit': true }\">\n              <mat-icon fontSet=\"fa\" fontIcon=\"fa-pencil-square-o\"></mat-icon>\n            </a>\n            <a mat-icon-button (click)=\"delete(group.name, element.slug)\">\n              <mat-icon fontSet=\"fa\" fontIcon=\"fa-trash\"></mat-icon>\n            </a>\n          </mat-cell>\n        </ng-container>\n\n        <mat-header-row *matHeaderRowDef=\"displayedColumns\"></mat-header-row>\n        <mat-row *matRowDef=\"let row; columns: displayedColumns;\"></mat-row>\n      </mat-table>\n    </mat-tab>\n  </mat-tab-group>\n</div>"
  },
  {
    "path": "src/client/app/pages/pages.component.scss",
    "content": ":host {\n  .flex {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    padding: 0 1em;\n  }\n}"
  },
  {
    "path": "src/client/app/pages/pages.component.ts",
    "content": "import { FirebaseDatabaseService } from './../shared/services/firebase-database.service'\nimport { ChangeDetectionStrategy, Component } from '@angular/core'\nimport { MatDialog, MatTableDataSource } from '@angular/material'\nimport { PageFormComponent } from './page-form/page-form.component'\nimport { ActivatedRoute, Router } from '@angular/router'\nimport { Observable } from 'rxjs/Observable'\nimport { ModalConfirmationComponent } from '../shared/modal-confirmation/modal-confirmation.component'\n\n@Component({\n  selector: 'pm-pages',\n  templateUrl: './pages.component.html',\n  styleUrls: ['./pages.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class PagesComponent {\n  readonly pages$ = this.db.get('/pages')\n  private readonly params$ = this.ar.queryParams.shareReplay()\n  private readonly currentTab$ = this.params$\n    .map(a => a.tab ? +a.tab : 0)\n\n  readonly view$ = Observable.combineLatest(this.currentTab$, this.pages$,\n    (currentTab, pages: { readonly [key: string]: string }) => {\n      return {\n        groups: Object.keys(pages || {}).map(group => {\n          return {\n            table: new MatTableDataSource(Object.keys(pages[group] || {}).map(slug => {\n              return {\n                ...(pages[group] as any)[slug],\n                slug,\n                routerLink: `/${group}/${slug}`\n              }\n            })),\n            name: group\n          }\n        }),\n        currentTab\n      }\n    })\n\n  readonly displayedColumns: ReadonlyArray<any> = ['slug', 'edit']\n\n  openDialog() {\n    this.dialog.open(PageFormComponent, {\n      width: '460px',\n      position: {\n        top: '30px'\n      }\n    })\n  }\n\n  confirmDelete() {\n    return this.dialog.open(ModalConfirmationComponent, {\n      width: '460px',\n      position: {\n        top: '30px'\n      },\n      data: {\n        message: 'Deleting this page will immediately remove it from the database',\n        title: 'Are you sure?'\n      }\n    })\n  }\n\n  delete(group: string, slug: string) {\n    this.confirmDelete()\n      .afterClosed()\n      .filter(Boolean)\n      .flatMap(() => this.db\n        .getObjectRef(`/pages/${group}/${slug}`)\n        .remove())\n      .take(1)\n      .subscribe(succ => {\n        // todo\n      }, err => {\n        // todo\n      })\n  }\n\n  updateTabParam(tab: number) {\n    this.router.navigate([this.router.url.split('?')[0]], { queryParams: { tab }, queryParamsHandling: 'merge' })\n  }\n\n  constructor(private db: FirebaseDatabaseService, private dialog: MatDialog, private ar: ActivatedRoute, private router: Router) { }\n\n  trackByGroup(index: number, item: any) {\n    return index\n  }\n}\n"
  },
  {
    "path": "src/client/app/pages/pages.module.ts",
    "content": "import { PagesRoutingModule } from './pages-routing.module'\nimport { PagesComponent } from './pages.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\nimport { PageFormComponent } from './page-form/page-form.component'\n\n@NgModule({\n  imports: [PagesRoutingModule, SharedModule],\n  declarations: [PagesComponent, PageFormComponent],\n  exports: [PagesComponent],\n  entryComponents: [PageFormComponent]\n})\nexport class PagesModule { }\n"
  },
  {
    "path": "src/client/app/shared/cache-form/cache-form.component.html",
    "content": "<form *ngIf=\"form\" [formGroup]=\"form\" fxLayout=\"column\">\n  <mat-checkbox *ngFor=\"let dir of dirsNoInput; trackBy: trackByDirsNoInput\" [formControlName]=\"dir.key\">{{ dir.key }}</mat-checkbox>\n  <div *ngFor=\"let dir of dirsWithInput; trackBy: trackByDirsWithInput\">\n    <mat-checkbox [formControlName]=\"dir.key\">{{ dir.key }}</mat-checkbox>\n    <mat-form-field floatPlaceholder=\"never\" *ngIf=\"form.get(dir.key).value\">\n      <input matInput [placeholder]=\"dir.inputKey\" type=\"text\" [formControlName]=\"dir.inputKey\">\n    </mat-form-field>\n  </div>\n</form>"
  },
  {
    "path": "src/client/app/shared/cache-form/cache-form.component.scss",
    "content": ":host {\n\n}"
  },
  {
    "path": "src/client/app/shared/cache-form/cache-form.component.ts",
    "content": "import { Subscription } from 'rxjs/Subscription'\nimport { FormControl, FormGroup } from '@angular/forms'\nimport { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'\n// tslint:disable-next-line:no-require-imports\nimport ms = require('ms')\n\n@Component({\n  selector: 'pm-cache-form',\n  templateUrl: './cache-form.component.html',\n  styleUrls: ['./cache-form.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class CacheFormComponent implements OnDestroy, OnInit {\n  private _cache: { [key: string]: boolean | number | string }\n  @Input() set cache(val: any) {\n    this._cache = val\n  }\n  get cache() {\n    return this._cache || {}\n  }\n  @Output() cacheChange = new EventEmitter<{ [key: string]: boolean | number | string }>()\n  @Output() cacheStringChange = this.cacheChange.asObservable()\n    .scan((acc: string, value) => Object.keys(value).map(a => {\n      return typeof value[a] === 'number' || typeof value[a] === 'string'\n        ? `${a}=${value[a]}`\n        : a\n    }).join(', '), '')\n\n  public form: FormGroup\n  private sub = new Subscription()\n  private readonly staticDirectives: ReadonlyArray<{ readonly key: string, readonly hasInput?: boolean }> = [\n    { key: 'no-cache' },\n    { key: 'no-store' },\n    { key: 'no-transform' },\n    { key: 'only-if-cached' },\n    { key: 'must-revalidate' },\n    { key: 'public' },\n    { key: 'private' },\n    { key: 'proxy-revalidate' },\n    { key: 'max-age', hasInput: true },\n    { key: 's-maxage', hasInput: true },\n    { key: 'max-stale', hasInput: true },\n    { key: 'min-fresh', hasInput: true }\n  ]\n\n  public readonly dirsNoInput: ReadonlyArray<{ readonly key: string, readonly hasInput?: boolean, readonly inputKey?: string }> =\n    this.staticDirectives\n      .filter(a => !a.hasInput)\n\n  public readonly dirsWithInput = this.staticDirectives\n    .filter(a => a.hasInput)\n    .map(a => ({ ...a, inputKey: this.getInputKey(a.key) }))\n\n  getInputKey(key: string) {\n    return `${key}-input`\n  }\n\n  removeInputKey(key: string) {\n    return key.replace('-input', '')\n  }\n\n  ngOnInit() {\n    this.form = new FormGroup(this.staticDirectives.reduce((a, c) => {\n      if (!c.hasInput) return ({ ...a, [c.key]: new FormControl(this.cache[c.key], []) })\n      return {\n        ...a,\n        [c.key]: new FormControl(this.cache[c.key], []),\n        [this.getInputKey(c.key)]: new FormControl(this.cache[c.key], [])\n      }\n    }, {}))\n\n    this.sub = this.form.valueChanges\n      .scan((acc, value) => Object.keys(value)\n        .filter(a => value[a])\n        .reduce((a, c) => {\n          if (typeof value[c] === 'string' && !value[this.removeInputKey(c)]) return ({ ...a })\n          return typeof value[c] === 'string'\n            ? { ...a, [this.removeInputKey(c)]: this.compute(value[c]).toString() }\n            : { ...a, [c]: value[c] }\n        }, {}), {})\n      .subscribe(res => this.cacheChange.next(res))\n  }\n\n  ngOnDestroy() {\n    this.sub.unsubscribe()\n  }\n\n  compute(entry: string): number {\n    if (!isNaN(+entry)) return +entry\n    try {\n      return ms(entry) / 1000\n    } catch (err) {\n      return 0\n    }\n  }\n\n  trackByDirsNoInput(index: number, item: any) {\n    return index\n  }\n\n  trackByDirsWithInput(index: number, item: any) {\n    return index\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/directives/avatar.directive.ts",
    "content": ""
  },
  {
    "path": "src/client/app/shared/directives/click-outside.directive.ts",
    "content": "import { Directive, ElementRef, EventEmitter, HostListener, Input, Output } from '@angular/core'\n\n@Directive({\n  selector: '[pmClickOutside]'\n})\nexport class ClickOutsideDirective {\n  @Input() exclude: string\n  @Output() pmClickOutside = new EventEmitter<boolean>()\n\n  constructor(private elementRef: ElementRef) { }\n\n  @HostListener('document:click', ['$event', '$event.target'])\n  public onClick(event: MouseEvent, targetElement: any): void {\n    if (!targetElement) return\n\n    const clickedInside = this.elementRef.nativeElement.contains(targetElement)\n    const nodes = Array.from(document.querySelectorAll(this.exclude)) as Array<HTMLElement>\n    const whitelisted = nodes.some(a => a.contains(targetElement))\n\n    if (!clickedInside && !whitelisted) {\n      this.pmClickOutside.emit(true)\n    }\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/directives/html-outlet.directive.ts",
    "content": "import {\n  Compiler,\n  Component,\n  ComponentFactory,\n  ComponentRef,\n  Directive,\n  Input,\n  ModuleWithComponentFactories,\n  NgModule,\n  OnChanges,\n  OnDestroy,\n  ReflectiveInjector,\n  ViewContainerRef,\n  ViewEncapsulation\n} from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { CommonModule } from '@angular/common'\nimport { SharedModule } from '../shared.module'\n\nexport function createComponentFactory(compiler: Compiler, metadata: Component): Promise<ComponentFactory<any>> {\n  const cmpClass = class DynamicComponent { }\n  const decoratedCmp = Component(metadata)(cmpClass)\n\n  @NgModule({\n    imports: [CommonModule, RouterModule, SharedModule],\n    declarations: [decoratedCmp]\n  })\n  class DynamicHtmlModule { }\n\n  return compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule)\n    .then((moduleWithComponentFactory: ModuleWithComponentFactories<any>) =>\n      moduleWithComponentFactory.componentFactories.find(x => x.componentType === decoratedCmp) as any)\n}\n\n@Directive({\n  selector: '[pmHtmlOutlet]'\n})\nexport class HtmlOutletDirective implements OnChanges, OnDestroy {\n  @Input() readonly html: any\n  cmpRef: ComponentRef<any>\n\n  constructor(private vcRef: ViewContainerRef, private compiler: Compiler) { }\n\n  ngOnChanges() {\n    // tslint:disable-next-line:max-line-length\n    const html = `<div class=\"vert-flex-fill ql-container ql-snow\" style=\"border:none;\"><div class=\"ql-editor\">${this.html.changingThisBreaksApplicationSecurity}</div></div>`\n    if (!html || typeof html !== 'string') return\n\n    if (this.cmpRef) {\n      this.cmpRef.destroy()\n    }\n\n    const compMetadata = new Component({\n      selector: 'dynamic-html',\n      template: html,\n      encapsulation: ViewEncapsulation.None\n    })\n\n    createComponentFactory(this.compiler, compMetadata)\n      .then(factory => {\n        const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector)\n        this.cmpRef = this.vcRef.createComponent(factory, 0, injector, [])\n      })\n  }\n\n  ngOnDestroy() {\n    if (this.cmpRef) {\n      this.cmpRef.destroy()\n    }\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/directives/social-button.directive.ts",
    "content": "import { Directive, ElementRef, HostListener, Input, OnInit } from '@angular/core'\n\n@Directive({\n  selector: '[pmSocialButton]'\n})\nexport class SocialButtonDirective implements OnInit {\n  @Input() readonly pmSocialButton: string\n\n  @HostListener('mouseenter') onMouseEnter() {\n    this.elementRef.nativeElement.style.backgroundColor = this.getHover(this.pmSocialButton)\n  }\n\n  @HostListener('mouseleave') onMouseLeave() {\n    this.setBaseSyles(this.pmSocialButton)\n  }\n\n  constructor(private elementRef: ElementRef) { }\n\n  private readonly map: { readonly [key: string]: { readonly bg: string, readonly hover: string, readonly border: string } } = {\n    facebook: { bg: '#4267b2', hover: '#2d4373', border: '#29487d' },\n    twitter: { bg: '#55acee', hover: '#2795e9', border: 'rgba(0, 0, 0, 0.2)' },\n    github: { bg: '#444', hover: '#2b2b2b', border: 'rgba(0, 0, 0, 0.2)' },\n    google: { bg: '#dd4b39', hover: '#c23321', border: 'rgba(0, 0, 0, 0.2)' }\n  }\n\n  ngOnInit() {\n    this.setBaseSyles(this.pmSocialButton)\n  }\n\n  setBaseSyles(provider: string) {\n    const val = this.map[provider] || {}\n    this.elementRef.nativeElement.style.backgroundColor = val.bg\n    this.elementRef.nativeElement.style.color = '#fff'\n    this.elementRef.nativeElement.style['border-bottom'] = `1px solid ${val.border}`\n  }\n\n  getHover(provider: string) {\n    const val = this.map[provider] || {}\n    return val.hover\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/http-cache-tag/http-cache-tag-interceptor.service.spec.ts",
    "content": "import { CACHE_TAG_CONFIG, CACHE_TAG_FACTORY, CacheTagConfig } from './http-cache-tag.module'\nimport { RESPONSE } from '@nguniversal/express-engine/tokens'\nimport { ENV_CONFIG } from './../../app.config'\nimport { HttpCacheTagInterceptor } from './http-cache-tag-interceptor.service'\nimport { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'\nimport { async, TestBed } from '@angular/core/testing'\nimport { Response } from 'express'\nimport { HTTP_INTERCEPTORS, HttpClient, HttpClientModule, HttpHeaders, HttpInterceptor, HttpResponse } from '@angular/common/http'\nimport { ServerResponseService } from '../services/server-response.service'\nimport '../../../operators'\n\nexport class ExpressResponse {\n  readonly store = new Map()\n  getHeader(key: string) {\n    return this.store.get(key)\n  }\n\n  header(key: string, value: string) {\n    this.store.set(key, value)\n  }\n}\n\nfunction testDeps() {\n  return {\n    interceptor: TestBed.get(HttpCacheTagInterceptor) as HttpInterceptor,\n    http: TestBed.get(HttpClient) as HttpClient,\n    httpMock: TestBed.get(HttpTestingController) as HttpTestingController,\n    serverResponse: TestBed.get(RESPONSE) as Response,\n    config: TestBed.get(CACHE_TAG_CONFIG) as CacheTagConfig\n  }\n}\n\ndescribe(HttpCacheTagInterceptor.name, () => {\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [HttpClientTestingModule, HttpClientModule],\n      providers: [\n        { provide: HttpCacheTagInterceptor, useClass: HttpCacheTagInterceptor, deps: [CACHE_TAG_CONFIG, CACHE_TAG_FACTORY] },\n        { provide: HTTP_INTERCEPTORS, useExisting: HttpCacheTagInterceptor, multi: true },\n        { provide: RESPONSE, useClass: ExpressResponse },\n        {\n          provide: ENV_CONFIG,\n          useValue: {\n            endpoints: {\n              discovery: 'http://some.endpoint/api'\n            }\n          }\n        },\n        {\n          provide: CACHE_TAG_CONFIG,\n          useValue: {\n            headerKey: 'Cache-Tag',\n            cacheableResponseCodes: [200, 201]\n          }\n        },\n        ServerResponseService,\n        {\n          provide: CACHE_TAG_FACTORY,\n          useFactory: (srs: ServerResponseService) => {\n            return (httpResponse: HttpResponse<any>, d: CacheTagConfig) => {\n              const cacheHeader = httpResponse.headers.get(testDeps().config.headerKey)\n              if (cacheHeader) {\n                srs.appendHeader(testDeps().config.headerKey, cacheHeader)\n              }\n            }\n          },\n          deps: [ServerResponseService]\n        }\n      ]\n    })\n  }))\n\n  afterEach(() => {\n    TestBed.resetTestingModule()\n  })\n\n  it('should construct', async(() => {\n    expect(testDeps().interceptor).toBeDefined()\n  }))\n\n  it('should append cache headers to server response', async(() => {\n    expect.assertions(4)\n    testDeps().http.get('http://www.google.com/api/some-thing/123', { observe: 'response' }).subscribe(response => {\n      expect(response).toBeTruthy()\n      expect(response.headers.get(testDeps().config.headerKey)).toEqual('Thing-123')\n      expect(testDeps().serverResponse.getHeader(testDeps().config.headerKey)).toEqual('Thing-123')\n    })\n\n    const req1 = testDeps().httpMock.expectOne(r => r.url === 'http://www.google.com/api/some-thing/123')\n    expect(req1.request.method).toEqual('GET')\n\n    req1.flush({ hello: 'world' }, {\n      headers: new HttpHeaders().set(testDeps().config.headerKey, 'Thing-123')\n    })\n\n    testDeps().httpMock.verify()\n  }))\n\n  it('should append cache headers to server response with other status code', async(() => {\n    expect.assertions(4)\n    testDeps().http.get('http://www.google.com/api/some-thing/123', { observe: 'response' }).subscribe(response => {\n      expect(response).toBeTruthy()\n      expect(response.headers.get(testDeps().config.headerKey)).toEqual('Thing-123')\n      expect(testDeps().serverResponse.getHeader(testDeps().config.headerKey)).toEqual('Thing-123')\n    })\n\n    const req1 = testDeps().httpMock.expectOne(r => r.url === 'http://www.google.com/api/some-thing/123')\n    expect(req1.request.method).toEqual('GET')\n\n    req1.flush({ hello: 'world' }, {\n      status: 201,\n      statusText: 'OK',\n      headers: new HttpHeaders().set(testDeps().config.headerKey, 'Thing-123')\n    })\n\n    testDeps().httpMock.verify()\n  }))\n\n  it('should not append cache headers when wrong header exists', async(() => {\n    expect.assertions(4)\n    testDeps().http.get('http://www.google.com/api/some-thing/123', { observe: 'response' }).subscribe(response => {\n      expect(response).toBeTruthy()\n      expect(response.headers.get(testDeps().config.headerKey)).toBeNull()\n      expect(testDeps().serverResponse.getHeader(testDeps().config.headerKey)).toBeUndefined()\n    })\n\n    const req = testDeps().httpMock.expectOne(r => r.url === 'http://www.google.com/api/some-thing/123')\n    expect(req.request.method).toEqual('GET')\n\n    req.flush({ hello: 'world' }, {\n      headers: new HttpHeaders().set('Some-Nothing-Tag', 'Thing-123')\n    })\n\n    testDeps().httpMock.verify()\n  }))\n\n  it('should not append cache headers when no headers exists', async(() => {\n    expect.assertions(4)\n    testDeps().http.get('http://www.google.com/api/some-thing/123', { observe: 'response' }).subscribe(response => {\n      expect(response).toBeTruthy()\n      expect(response.headers.get(testDeps().config.headerKey)).toBeNull()\n      expect(testDeps().serverResponse.getHeader(testDeps().config.headerKey)).toBeUndefined()\n    })\n\n    const req1 = testDeps().httpMock.expectOne(r => r.url === 'http://www.google.com/api/some-thing/123')\n    expect(req1.request.method).toEqual('GET')\n\n    req1.flush({ hello: 'world' })\n\n    testDeps().httpMock.verify()\n  }))\n\n  it('should only append headers with proper API response status codes', async(() => {\n    expect.assertions(4)\n    testDeps().http.get('http://www.google.com/api/some-thing/123', { observe: 'response' }).subscribe(response => {\n      expect(response).toBeTruthy()\n      expect(response.headers.get(testDeps().config.headerKey)).toBeDefined()\n      expect(testDeps().serverResponse.getHeader(testDeps().config.headerKey)).toBeUndefined()\n    })\n\n    const req = testDeps().httpMock.expectOne(r => r.url === 'http://www.google.com/api/some-thing/123')\n    expect(req.request.method).toEqual('GET')\n\n    req.flush({ hello: 'world' }, {\n      status: 206,\n      statusText: 'OK',\n      headers: new HttpHeaders().set(testDeps().config.headerKey, 'Thing-123')\n    })\n\n    testDeps().httpMock.verify()\n  }))\n})\n"
  },
  {
    "path": "src/client/app/shared/http-cache-tag/http-cache-tag-interceptor.service.ts",
    "content": "import { Inject, Injectable } from '@angular/core'\nimport { HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http'\nimport { CACHE_TAG_CONFIG, CACHE_TAG_FACTORY, CacheFactory, CacheTagConfig } from './http-cache-tag.module'\n\n@Injectable()\nexport class HttpCacheTagInterceptor implements HttpInterceptor {\n\n  constructor( @Inject(CACHE_TAG_CONFIG) private config: CacheTagConfig,\n    @Inject(CACHE_TAG_FACTORY) private factory: CacheFactory) {\n    if (!config.headerKey) throw new Error('missing config.headerKey')\n    if (!config.cacheableResponseCodes) throw new Error('missing config.cacheableResponseCodes')\n  }\n\n  isCacheableCode(code: number) {\n    return this.config.cacheableResponseCodes.find(a => a === code)\n  }\n\n  isCacheableUrl(url: string | null) {\n    if (!this.config.cacheableUrls || !url || url === null) return true\n    return this.config.cacheableUrls.test(url)\n  }\n\n  intercept(req: HttpRequest<any>, next: HttpHandler) {\n    return next.handle(req).map(event => {\n      if (event instanceof HttpResponse && this.isCacheableCode(event.status) && this.isCacheableUrl(event.url)) {\n        this.factory(event, this.config)\n      }\n      return event\n    })\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/http-cache-tag/http-cache-tag.module.ts",
    "content": "import { InjectionToken, ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'\nimport { HTTP_INTERCEPTORS, HttpResponse } from '@angular/common/http'\nimport { HttpCacheTagInterceptor } from './http-cache-tag-interceptor.service'\n\nexport const CACHE_TAG_CONFIG = new InjectionToken<CacheTagConfig>('app.config.http.cachetag')\nexport const CACHE_TAG_FACTORY = new InjectionToken<CacheFactory>('app.config.http.cachetag')\n\nexport interface CacheTagConfig {\n  readonly headerKey: string\n  readonly cacheableResponseCodes: ReadonlyArray<number>\n  readonly cacheableUrls?: RegExp,\n}\n\nexport type CacheFactory = (httpResponse: HttpResponse<any>, config: CacheTagConfig) => void\n\n@NgModule()\nexport class HttpCacheTagModule {\n  static forRoot(configProvider: any, factoryProvider: any): ModuleWithProviders {\n    return {\n      ngModule: HttpCacheTagModule,\n      providers: [\n        {\n          provide: HttpCacheTagInterceptor,\n          useClass: HttpCacheTagInterceptor,\n          deps: [CACHE_TAG_CONFIG, CACHE_TAG_FACTORY]\n        },\n        { provide: HTTP_INTERCEPTORS, useExisting: HttpCacheTagInterceptor, multi: true },\n        configProvider,\n        factoryProvider\n      ]\n    }\n  }\n\n  constructor( @Optional() @SkipSelf() parentModule: HttpCacheTagModule) {\n    if (parentModule)\n      throw new Error('HttpCachTageModule already loaded. Import in root module only.')\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/injection-form/injection-form.component.html",
    "content": "<form [formGroup]=\"form\" novalidate>\n  <mat-form-field>\n    <input matInput placeholder=\"Element\" type=\"text\" formControlName=\"element\">\n  </mat-form-field>\n  <div>\n    <label for=\"inHead\">In Head?</label>\n    <input id=\"inHead\" type=\"checkbox\" formControlName=\"inHead\">\n  </div>\n  <mat-form-field>\n    <textarea matInput placeholder=\"Inline Value\" formControlName=\"value\"></textarea>\n  </mat-form-field>\n  <div>\n    <label>Attributes</label>\n    <pm-key-value-form [keyVals]=\"injectable.attributes\" (attributesChanged)=\"attributesChanged($event)\"></pm-key-value-form>\n  </div>\n  <p *ngIf=\"showDomString\">{{ htmlString }}</p>\n  <!-- \n    TODO: replace above once material fixes bug\n    TODO: https://github.com/angular/material2/pull/7882\n    <mat-checkbox formControlName=\"inHead\">\n    In Head?\n  </mat-checkbox> -->\n</form>"
  },
  {
    "path": "src/client/app/shared/injection-form/injection-form.component.scss",
    "content": ":host {\n  display: block;\n}"
  },
  {
    "path": "src/client/app/shared/injection-form/injection-form.component.ts",
    "content": "import { BehaviorSubject } from 'rxjs/BehaviorSubject'\nimport { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output, Renderer2 } from '@angular/core'\nimport { FormControl, FormGroup, Validators } from '@angular/forms'\nimport { DOMInjectable, InjectionService } from '../services/injection.service'\nimport { Subscription } from 'rxjs/Subscription'\n\n@Component({\n  selector: 'pm-injection-form',\n  templateUrl: './injection-form.component.html',\n  styleUrls: ['./injection-form.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class InjectionFormComponent implements OnInit, OnDestroy {\n  @Input() showDomString: boolean\n  @Input() injectable: DOMInjectable = {} as any\n  @Output() formChange = new BehaviorSubject<DOMInjectable>(this.injectable)\n  @Output() changeHtmlString = new EventEmitter<string>()\n\n  public form: FormGroup\n  private sub = new Subscription()\n\n  get htmlString() {\n    return this.inj.getElementStringForm(this.renderer, this.formChange.getValue())\n  }\n\n  constructor(private renderer: Renderer2, private inj: InjectionService) { }\n\n  ngOnInit() {\n    this.form = new FormGroup({\n      element: new FormControl(this.injectable.element, [Validators.required]),\n      inHead: new FormControl(this.injectable.inHead || false, []),\n      value: new FormControl(this.injectable.value || undefined, []),\n      attributes: new FormControl(this.injectable.attributes || {}, [])\n    })\n    this.sub = this.form.valueChanges.skip(1).subscribe(form => {\n      this.formChange.next(form)\n      this.changeHtmlString.next(this.htmlString)\n    })\n  }\n\n  ngOnDestroy() {\n    this.sub.unsubscribe()\n  }\n\n  attributesChanged(attrs: any) {\n    if (this.form && this.form.controls) this.form.controls['attributes'].setValue(attrs || {})\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/key-value-form/key-value-form.component.html",
    "content": "<div *ngFor=\"let keyVal of change.asObservable() | async | pmKeyVal; trackBy: trackByFn\">\n  <button mat-icon-button color=\"warn\" (click)=\"remove(keyVal.key)\">\n    <mat-icon fontSet=\"fa\" fontIcon=\"fa-minus-circle\"></mat-icon>\n  </button>\n  <mat-form-field>\n    <input matInput placeholder=\"Key\" type=\"text\" [value]=\"keyVal.key\">\n  </mat-form-field>\n  <mat-form-field>\n    <input matInput placeholder=\"Value\" type=\"text\" [value]=\"keyVal.value\">\n  </mat-form-field>\n</div>\n\n<form [formGroup]=\"form\" novalidate (ngSubmit)=\"add(form.value)\">\n  <mat-form-field>\n    <input matInput placeholder=\"Key\" type=\"text\" formControlName=\"key\">\n  </mat-form-field>\n  <mat-form-field>\n    <input matInput placeholder=\"Value\" type=\"text\" formControlName=\"value\">\n  </mat-form-field>\n  <button mat-icon-button type=\"submit\" color=\"primary\" [disabled]=\"form.invalid\">\n    <mat-icon fontSet=\"fa\" fontIcon=\"fa-plus\"></mat-icon>\n  </button>\n</form>"
  },
  {
    "path": "src/client/app/shared/key-value-form/key-value-form.component.scss",
    "content": ":host {\n  display: block;\n}"
  },
  {
    "path": "src/client/app/shared/key-value-form/key-value-form.component.ts",
    "content": "import { BehaviorSubject } from 'rxjs/BehaviorSubject'\nimport { ChangeDetectionStrategy, Component, Input, OnChanges, Output, SimpleChanges } from '@angular/core'\nimport { FormControl, FormGroup, Validators } from '@angular/forms'\n\n@Component({\n  selector: 'pm-key-value-form',\n  templateUrl: './key-value-form.component.html',\n  styleUrls: ['./key-value-form.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class KeyValueFormComponent implements OnChanges {\n  @Input() keyVals: { [key: string]: string | boolean | number } = {}\n  @Output() attributesChanged = new BehaviorSubject(this.keyVals)\n\n  public form = new FormGroup({\n    key: new FormControl('', [Validators.required]),\n    value: new FormControl('', [Validators.required])\n  })\n\n  ngOnChanges(changes: SimpleChanges) {\n    if (changes.keyVals) {\n      this.attributesChanged.next(changes.keyVals.currentValue)\n    }\n  }\n\n  add(obj: { key: string, value: string }) {\n    this.attributesChanged.next({\n      ...this.attributesChanged.getValue(),\n      [obj.key]: obj.value\n    })\n  }\n\n  remove(key: string) {\n    this.attributesChanged.next({\n      ...Object.keys(this.attributesChanged.getValue())\n        .filter(k => k !== key)\n        .reduce((a, c) => ({ ...a, [c]: this.attributesChanged.getValue()[c] }), {})\n    })\n  }\n\n  trackByFn(index: number, item: any) {\n    console.log(item)\n    return index\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/login-card/login-card.component.html",
    "content": "<mat-spinner *ngIf=\"isLoading\"></mat-spinner>\n<mat-accordion class=\"headers-align\" *ngIf=\"!isLoading\">\n  <mat-expansion-panel hideToggle=\"true\" expanded=\"true\">\n    <mat-error id=\"socialNetworkErr\" *ngIf=\"socialNetworkErr\">\n      <p>{{ socialNetworkErrEmail }}</p>\n      <p>{{ socialNetworkErr }}</p>\n    </mat-error>\n    <mat-expansion-panel-header>\n      <mat-panel-title>\n        Sign in with social network\n      </mat-panel-title>\n    </mat-expansion-panel-header>\n    <button mat-raised-button (click)=\"login('facebook')\" pmSocialButton=\"facebook\">\n      <div class=\"social-spacer\">\n        <mat-icon fontSet=\"fa\" fontIcon=\"fa-facebook-official\"></mat-icon>\n        <span>Sign in with Facebook</span>\n      </div>\n    </button>\n    <button mat-raised-button (click)=\"login('google')\" pmSocialButton=\"google\">\n      <div class=\"social-spacer\">\n        <mat-icon fontSet=\"fa\" fontIcon=\"fa-google\"></mat-icon>\n        <span>Sign in with Google</span>\n      </div>\n    </button>\n    <button mat-raised-button (click)=\"login('github')\" pmSocialButton=\"github\">\n      <div class=\"social-spacer\">\n        <mat-icon fontSet=\"fa\" fontIcon=\"fa-github\"></mat-icon>\n        <span>Sign in with Github</span>\n      </div>\n    </button>\n    <button mat-raised-button (click)=\"login('twitter')\" pmSocialButton=\"twitter\">\n      <div class=\"social-spacer\">\n        <mat-icon fontSet=\"fa\" fontIcon=\"fa-twitter\"></mat-icon>\n        <span>Sign in with Twitter</span>\n      </div>\n    </button>\n  </mat-expansion-panel>\n  <mat-expansion-panel hideToggle=\"true\">\n    <mat-expansion-panel-header>\n      <mat-panel-title>\n        Sign in with email\n      </mat-panel-title>\n    </mat-expansion-panel-header>\n    <form [formGroup]=\"form\" id=\"emailForm\" novalidate>\n      <mat-error *ngIf=\"form.hasError('network')\">\n        {{ form.getError('network') }}\n      </mat-error>\n      <br *ngIf=\"form.hasError('network')\">\n      <mat-form-field>\n        <input matInput placeholder=\"Email\" formControlName=\"email\">\n        <mat-error *ngIf=\"form.controls.email.hasError('pattern')\">\n          Please enter a valid email address\n        </mat-error>\n        <mat-error *ngIf=\"form.controls.email.hasError('required')\">\n          Email is\n          <strong>required</strong>\n        </mat-error>\n      </mat-form-field>\n      <mat-form-field>\n        <input matInput placeholder=\"Password\" type=\"password\" formControlName=\"password\">\n        <mat-error *ngIf=\"form.controls.password.hasError('required')\">\n          Password is\n          <strong>required</strong>\n        </mat-error>\n      </mat-form-field>\n    </form>\n    <mat-action-row>\n      <button mat-button color=\"accent\" (click)=\"login('email_new')\" [disabled]=\"!form.valid\">Signup</button>\n      <button type=\"submit\" form=\"emailForm\" mat-raised-button color=\"primary\" (click)=\"login('email_login')\" [disabled]=\"!form.valid\">Sign In</button>\n    </mat-action-row>\n  </mat-expansion-panel>\n</mat-accordion>"
  },
  {
    "path": "src/client/app/shared/login-card/login-card.component.scss",
    "content": ":host {\n  div {\n    display: flex;\n    flex-direction: column;\n    button,\n    p {\n      align-self: center;\n      width: 190px;\n      margin-bottom: 1em;\n    }\n  }\n  mat-expansion-panel {\n    max-width: 300px;\n    align-self: center;\n  }\n  form {\n    mat-form-field {\n      width: 100%;\n    }\n  }\n  h1 {\n    margin-top: 0;\n    text-align: center;\n  }\n  #socialNetworkErr {\n    max-width: 300px;\n    margin-bottom: 2em;\n  }\n  .social-spacer {\n    display: flex;\n    flex-direction: row;\n    align-items: baseline;\n  }\n  ::ng-deep .mat-expansion-panel-body {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    button {\n      margin-bottom: 1em;\n      width: 200px;\n    }\n  }\n  ::ng-deep mat-action-row {\n    justify-content: space-between;\n    padding: 8px;\n    button {\n      margin: 0 !important;\n    }\n  }\n}\n\nmat-icon {\n  vertical-align: inherit !important;\n}\n\n"
  },
  {
    "path": "src/client/app/shared/login-card/login-card.component.ts",
    "content": "import { PlatformService } from './../services/platform.service'\nimport { Router } from '@angular/router'\nimport { AuthService } from './../services/auth.service'\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core'\nimport { FormControl, FormGroup, Validators } from '@angular/forms'\nimport { AngularFireAuth } from 'angularfire2/auth'\nimport { fromPromise } from 'rxjs/observable/fromPromise'\n\nconst EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$/\n\n@Component({\n  selector: 'pm-login-card',\n  templateUrl: './login-card.component.html',\n  styleUrls: ['./login-card.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class LoginCardComponent {\n  constructor(private cd: ChangeDetectorRef, private auth: AuthService, public afAuth: AngularFireAuth, router: Router,\n    ps: PlatformService) {\n    if (ps.isServer) return\n\n    fromPromise(afAuth.auth.getRedirectResult())\n      .take(1)\n      .subscribe(res => {\n        this.isLoading = false\n        this.cd.markForCheck()\n        // if (res && res.operationType === 'signIn') router.navigate(['/'])\n      }, err => this.socialNetworkError(err))\n  }\n\n  public socialNetworkErr: string\n  public socialNetworkErrEmail: string\n  public isLoading = true\n\n  login(provider: string) {\n    this.isLoading = true\n    switch (provider) {\n      case 'facebook': this.auth.signInWithFacebookRedirect().take(1).subscribe(res => undefined, err => this.socialNetworkError(err))\n        break\n      case 'google': this.auth.signInWithGoogleRedirect().take(1).subscribe(res => undefined, err => this.socialNetworkError(err))\n        break\n      case 'github': this.auth.signInWithGithubRedirect().take(1).subscribe(res => undefined, err => this.socialNetworkError(err))\n        break\n      case 'twitter': this.auth.signInWithTwitterRedirect().take(1).subscribe(res => undefined, err => this.socialNetworkError(err))\n        break\n      case 'email_new':\n        this.auth.createUserWithEmailAndPassword(this.form.value.email, this.form.value.password)\n          .take(1)\n          .subscribe(res => {\n            res.sendEmailVerification()\n          }, err => this.networkError(err))\n        break\n      case 'email_login':\n        this.auth.signInWithEmailAndPassword(this.form.value.email, this.form.value.password)\n          .take(1)\n          .subscribe(res => {\n            console.log(res)\n          }, err => this.networkError(err))\n        break\n      default:\n    }\n  }\n\n  networkError(err: any) {\n    this.form.setErrors({\n      ...this.form.errors,\n      network: err.message\n    })\n    this.cd.markForCheck()\n  }\n\n  socialNetworkError(err: any) {\n    this.isLoading = false\n    this.socialNetworkErrEmail = err.email\n    this.socialNetworkErr = err.message\n    this.cd.markForCheck()\n  }\n\n  public readonly form = new FormGroup({\n    email: new FormControl('', [\n      Validators.required,\n      Validators.pattern(EMAIL_REGEX)\n    ]),\n    password: new FormControl('', [\n      Validators.required\n    ])\n  })\n}\n"
  },
  {
    "path": "src/client/app/shared/material.module.ts",
    "content": "import { NgModule } from '@angular/core'\nimport {\n  MatButtonModule, MatCardModule, MatCheckboxModule, MatChipsModule,\n  MatDatepickerModule, MatDialogModule, MatExpansionModule, MatExpansionPanel, MatFormFieldModule,\n  MatIconModule, MatInputModule, MatListModule, MatMenuModule,\n  MatNativeDateModule, MatPaginatorModule, MatProgressSpinnerModule, MatRippleModule, MatSelectModule,\n  MatSidenavModule, MatSlideToggleModule, MatSnackBarModule, MatTableModule, MatTabsModule,\n  MatTooltipModule, NativeDateAdapter\n} from '@angular/material'\nimport { OverlayModule } from '@angular/cdk/overlay'\n\n@NgModule({\n  imports: [\n    MatButtonModule,\n    MatCardModule,\n    MatIconModule,\n    MatSidenavModule,\n    MatTooltipModule,\n    MatFormFieldModule,\n    MatInputModule,\n    MatExpansionModule,\n    MatMenuModule,\n    MatProgressSpinnerModule,\n    MatListModule,\n    MatSnackBarModule,\n    MatSlideToggleModule,\n    MatTabsModule,\n    MatTableModule,\n    MatDialogModule,\n    MatDatepickerModule,\n    MatNativeDateModule,\n    MatChipsModule,\n    MatSelectModule,\n    MatCheckboxModule,\n    OverlayModule,\n    MatRippleModule,\n    MatPaginatorModule\n  ],\n  providers: [NativeDateAdapter],\n  entryComponents: [MatExpansionPanel],\n  exports: [\n    MatButtonModule,\n    MatCardModule,\n    MatIconModule,\n    MatSidenavModule,\n    MatTooltipModule,\n    MatFormFieldModule,\n    MatInputModule,\n    MatExpansionModule,\n    MatMenuModule,\n    MatProgressSpinnerModule,\n    MatListModule,\n    MatSnackBarModule,\n    MatSlideToggleModule,\n    MatTabsModule,\n    MatTableModule,\n    MatDialogModule,\n    MatDatepickerModule,\n    MatNativeDateModule,\n    MatChipsModule,\n    MatSelectModule,\n    MatCheckboxModule,\n    OverlayModule,\n    MatRippleModule,\n    MatPaginatorModule\n  ]\n})\nexport class MaterialModule { }\n"
  },
  {
    "path": "src/client/app/shared/modal-confirmation/modal-confirmation.component.html",
    "content": "<h1>{{ title }}</h1>\n<p>{{ message }}</p>\n<div class=\"footer\">\n  <button mat-button (click)=\"dialog.close(false)\">Cancel</button>\n  <button mat-raised-button color=\"primary\" (click)=\"dialog.close(true)\">Ok</button>\n</div>"
  },
  {
    "path": "src/client/app/shared/modal-confirmation/modal-confirmation.component.scss",
    "content": ":host {\n  display: flex;\n  flex-direction: column;\n  h1,h2,h3,h4,h5 {\n    margin: 0;\n  }\n  .footer {\n    display: flex;\n    justify-content: space-between;\n  }\n}"
  },
  {
    "path": "src/client/app/shared/modal-confirmation/modal-confirmation.component.ts",
    "content": "import { ChangeDetectionStrategy, Component, Inject, Input } from '@angular/core'\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'\n\n@Component({\n  selector: 'pm-modal-confirmation',\n  templateUrl: './modal-confirmation.component.html',\n  styleUrls: ['./modal-confirmation.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ModalConfirmationComponent {\n  @Input() readonly message: string\n  @Input() readonly title: string\n\n  constructor(public dialog: MatDialogRef<ModalConfirmationComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { }\n\n  readonly view = {\n    message: this.data.message || this.message,\n    title: this.data.title || this.title\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/navbar/__snapshots__/navbar.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`NavbarComponent should compile 1`] = `\n<div\n  id=\"root0\"\n  ng-version=\"6.0.0-beta.7\"\n>\n  <nav\n    class=\"mat-elevation-z6\"\n  >\n    <button\n      angulartics2on=\"click\"\n      angularticsaction=\"ToggleSideMenu\"\n      class=\"mat-icon-button\"\n      id=\"toggle-menu-button\"\n      mat-icon-button=\"\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        <mat-icon\n          aria-hidden=\"true\"\n          class=\"mat-icon fa fa-bars\"\n          fonticon=\"fa-bars\"\n          fontset=\"fa\"\n          ng-reflect-font-icon=\"fa-bars\"\n          ng-reflect-font-set=\"fa\"\n          role=\"img\"\n        />\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple mat-button-ripple-round\"\n        matripple=\"\"\n        ng-reflect-centered=\"true\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"[object HTMLButtonElement]\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </button>\n    <a\n      aria-disabled=\"false\"\n      class=\"nav-button mat-button\"\n      href=\"/\"\n      mat-button=\"\"\n      ng-reflect-router-link=\"\"\n      routerlink=\"\"\n      tabindex=\"0\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        FB-ANGULAR-UNIVERSAL-STARTER\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n        ng-reflect-centered=\"false\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"/\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </a>\n    <div\n      class=\"flex-spacer\"\n    />\n    \n    <a\n      aria-disabled=\"false\"\n      class=\"nav-button mat-button\"\n      href=\"/dashboard\"\n      mat-button=\"\"\n      ng-reflect-router-link=\"dashboard\"\n      ng-reflect-router-link-active=\"active\"\n      ng-reflect-router-link-active-options=\"[object Object]\"\n      routerlinkactive=\"active\"\n      tabindex=\"0\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n         Dashboard \n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n        ng-reflect-centered=\"false\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"/dashboard\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </a>\n    <a\n      aria-disabled=\"false\"\n      class=\"nav-button mat-button\"\n      href=\"/about\"\n      mat-button=\"\"\n      ng-reflect-router-link=\"about\"\n      ng-reflect-router-link-active=\"active\"\n      ng-reflect-router-link-active-options=\"[object Object]\"\n      routerlinkactive=\"active\"\n      tabindex=\"0\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n         About \n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n        ng-reflect-centered=\"false\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"/about\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </a>\n    <a\n      aria-disabled=\"false\"\n      class=\"nav-button mat-button\"\n      href=\"https://www.angularuniversal.com\"\n      mat-button=\"\"\n      tabindex=\"0\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        Consulting\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n        ng-reflect-centered=\"false\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"https://www.angularuniversal.c\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </a>\n    <a\n      aria-disabled=\"false\"\n      class=\"nav-button mat-button\"\n      href=\"/api-docs\"\n      mat-button=\"\"\n      tabindex=\"0\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        API\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n        ng-reflect-centered=\"false\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"/api-docs\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </a>\n    <button\n      aria-haspopup=\"true\"\n      class=\"mat-icon-button\"\n      id=\"user-menu-btn\"\n      mat-icon-button=\"\"\n      ng-reflect-menu=\"[object Object]\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        <mat-icon\n          aria-hidden=\"true\"\n          class=\"mat-icon fa fa-user-circle-o\"\n          fonticon=\"fa-user-circle-o\"\n          fontset=\"fa\"\n          ng-reflect-font-icon=\"fa-user-circle-o\"\n          ng-reflect-font-set=\"fa\"\n          role=\"img\"\n        />\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple mat-button-ripple-round\"\n        matripple=\"\"\n        ng-reflect-centered=\"true\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"[object HTMLButtonElement]\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </button>\n    <mat-menu>\n      \n    </mat-menu>\n  </nav>\n</div>\n`;\n\nexports[`NavbarComponent should contain a list of links 1`] = `\n<div\n  id=\"root1\"\n  ng-version=\"6.0.0-beta.7\"\n>\n  <nav\n    class=\"mat-elevation-z6\"\n  >\n    <button\n      angulartics2on=\"click\"\n      angularticsaction=\"ToggleSideMenu\"\n      class=\"mat-icon-button\"\n      id=\"toggle-menu-button\"\n      mat-icon-button=\"\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        <mat-icon\n          aria-hidden=\"true\"\n          class=\"mat-icon fa fa-bars\"\n          fonticon=\"fa-bars\"\n          fontset=\"fa\"\n          ng-reflect-font-icon=\"fa-bars\"\n          ng-reflect-font-set=\"fa\"\n          role=\"img\"\n        />\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple mat-button-ripple-round\"\n        matripple=\"\"\n        ng-reflect-centered=\"true\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"[object HTMLButtonElement]\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </button>\n    <a\n      aria-disabled=\"false\"\n      class=\"nav-button mat-button\"\n      href=\"/\"\n      mat-button=\"\"\n      ng-reflect-router-link=\"\"\n      routerlink=\"\"\n      tabindex=\"0\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        FB-ANGULAR-UNIVERSAL-STARTER\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n        ng-reflect-centered=\"false\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"/\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </a>\n    <div\n      class=\"flex-spacer\"\n    />\n    \n    <a\n      aria-disabled=\"false\"\n      class=\"nav-button mat-button\"\n      href=\"/dashboard\"\n      mat-button=\"\"\n      ng-reflect-router-link=\"dashboard\"\n      ng-reflect-router-link-active=\"active\"\n      ng-reflect-router-link-active-options=\"[object Object]\"\n      routerlinkactive=\"active\"\n      tabindex=\"0\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n         Dashboard \n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n        ng-reflect-centered=\"false\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"/dashboard\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </a>\n    <a\n      aria-disabled=\"false\"\n      class=\"nav-button mat-button\"\n      href=\"/about\"\n      mat-button=\"\"\n      ng-reflect-router-link=\"about\"\n      ng-reflect-router-link-active=\"active\"\n      ng-reflect-router-link-active-options=\"[object Object]\"\n      routerlinkactive=\"active\"\n      tabindex=\"0\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n         About \n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n        ng-reflect-centered=\"false\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"/about\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </a>\n    <a\n      aria-disabled=\"false\"\n      class=\"nav-button mat-button\"\n      href=\"https://www.angularuniversal.com\"\n      mat-button=\"\"\n      tabindex=\"0\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        Consulting\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n        ng-reflect-centered=\"false\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"https://www.angularuniversal.c\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </a>\n    <a\n      aria-disabled=\"false\"\n      class=\"nav-button mat-button\"\n      href=\"/api-docs\"\n      mat-button=\"\"\n      tabindex=\"0\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        API\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple\"\n        matripple=\"\"\n        ng-reflect-centered=\"false\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"/api-docs\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </a>\n    <button\n      aria-haspopup=\"true\"\n      class=\"mat-icon-button\"\n      id=\"user-menu-btn\"\n      mat-icon-button=\"\"\n      ng-reflect-menu=\"[object Object]\"\n    >\n      <span\n        class=\"mat-button-wrapper\"\n      >\n        <mat-icon\n          aria-hidden=\"true\"\n          class=\"mat-icon fa fa-user-circle-o\"\n          fonticon=\"fa-user-circle-o\"\n          fontset=\"fa\"\n          ng-reflect-font-icon=\"fa-user-circle-o\"\n          ng-reflect-font-set=\"fa\"\n          role=\"img\"\n        />\n      </span>\n      <div\n        class=\"mat-button-ripple mat-ripple mat-button-ripple-round\"\n        matripple=\"\"\n        ng-reflect-centered=\"true\"\n        ng-reflect-disabled=\"false\"\n        ng-reflect-trigger=\"[object HTMLButtonElement]\"\n      />\n      <div\n        class=\"mat-button-focus-overlay\"\n      />\n    </button>\n    <mat-menu>\n      \n    </mat-menu>\n  </nav>\n</div>\n`;\n"
  },
  {
    "path": "src/client/app/shared/navbar/navbar.component.html",
    "content": "<nav class=\"mat-elevation-z6\">\n  <button id=\"toggle-menu-button\" mat-icon-button (click)=\"menuIconClick.next()\" angulartics2On=\"click\" angularticsAction=\"ToggleSideMenu\">\n    <mat-icon fontSet=\"fa\" fontIcon=\"fa-bars\"></mat-icon>\n  </button>\n  <a class=\"nav-button\" mat-button routerLink>FB-ANGULAR-UNIVERSAL-STARTER</a>\n  <div class=\"flex-spacer\"></div>\n  <a class=\"nav-button\" mat-button *ngFor=\"let item of navbarService.menu$ | async; trackBy: trackByFn\" [routerLink]=\"item.route\" routerLinkActive=\"active\"\n    [routerLinkActiveOptions]=\"{exact: true}\">\n    {{ item.name }}\n  </a>\n  <a class=\"nav-button\" mat-button href=\"https://www.angularuniversal.com\">Consulting</a>\n  <a class=\"nav-button\" mat-button href=\"/api-docs\">API</a>\n  <button id=\"user-menu-btn\" mat-icon-button [matMenuTriggerFor]=\"menu\">\n    <mat-icon fontSet=\"fa\" fontIcon=\"fa-user-circle-o\"></mat-icon>\n  </button>\n  <mat-menu #menu=\"matMenu\">\n    <div *ngIf=\"!user\">\n      <button mat-menu-item routerLink=\"/login\">Login</button>\n    </div>\n    <div *ngIf=\"user\">\n      <div id=\"user-box\">\n        <img [src]=\"user.photoURL\" class=\"avatar\">\n        <div>\n          <h3>{{ user.displayName }}</h3>\n          <h4>{{ user.email }}</h4>\n        </div>\n      </div>\n      <button mat-menu-item routerLink=\"/account\">\n        <span>Account</span>\n      </button>\n      <button mat-menu-item routerLink=\"/logout\">\n        <span>Logout</span>\n      </button>\n    </div>\n  </mat-menu>\n</nav>\n"
  },
  {
    "path": "src/client/app/shared/navbar/navbar.component.scss",
    "content": "@import '../../../../../node_modules/@angular/material/_theming.scss';\n\n:host {\n  z-index: 10;\n  nav {\n    background-color: mat-color($mat-blue, 700);\n    display: -webkit-box;\n    display: -ms-flexbox;\n    display: flex;\n    -ms-flex-wrap: wrap;\n    flex-wrap: wrap;\n    -webkit-box-align: center;\n    -ms-flex-align: center;\n    align-items: center;\n    padding: 8px 16px;\n    color: rgba(255, 255, 255, 0.87);\n    img {\n      width: 96px;\n    }\n    .active {\n      box-shadow: 2px 4px 8px 2px mat-color($mat-blue, 800);\n    }\n  }\n  .flex-spacer {\n    -webkit-box-flex: 1;\n    -ms-flex-positive: 1;\n    flex-grow: 1;\n  }\n  #user-menu-btn {\n    font-size: 22px;\n  }\n}\n\n#user-box {\n  padding: 1em;\n  display: flex;\n  align-items: center;\n  border-bottom: solid 1px mat-color($mat-grey, 200);\n  div {\n    flex-direction: column;\n    h3,\n    h4 {\n      margin: 0;\n    }\n    h3 {\n      font-size: 16px;\n      text-align: center;\n    }\n    h4 {\n      font-weight: 100;\n      font-size: 12px;\n    }\n  }\n  img {\n    margin-right: .8em;\n  }\n}\n\n"
  },
  {
    "path": "src/client/app/shared/navbar/navbar.component.spec.ts",
    "content": "import { MaterialModule } from '../material.module'\nimport { RouterTestingModule } from '@angular/router/testing'\nimport { NavbarComponent } from './navbar.component'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { INavbarService, NavbarService } from './navbar.service'\nimport { By } from '@angular/platform-browser'\nimport { Component } from '@angular/core'\nimport { MatRipple } from '@angular/material'\nimport '../../../operators'\n\ndescribe(NavbarComponent.name, () => {\n  let fixture: ComponentFixture<NavbarComponent>\n  let navbarService: INavbarService\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [RouterTestingModule, MaterialModule],\n      declarations: [NavbarComponent, TestComponent],\n      providers: [NavbarService]\n    }).compileComponents()\n  }))\n\n  beforeEach(() => {\n    fixture = TestBed.createComponent(NavbarComponent)\n    navbarService = TestBed.get(NavbarService)\n  })\n\n  afterEach(() => {\n    TestBed.resetTestingModule()\n  })\n\n  it('should compile', async(() => {\n    fixture.detectChanges()\n    expect(fixture.nativeElement).toBeDefined()\n    expect(fixture.nativeElement).toMatchSnapshot()\n  }))\n\n  it('should contain a list of links', async(() => {\n    fixture.detectChanges()\n    const buttonLinks = fixture.debugElement.queryAll(By.directive(MatRipple))\n    expect(buttonLinks).toBeDefined()\n\n    navbarService.menu$.subscribe(items => {\n      expect(buttonLinks.length).toEqual(items.length + 5)\n    })\n    expect(fixture.nativeElement).toMatchSnapshot()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-navbar></pm-navbar>'\n})\nclass TestComponent {}\n"
  },
  {
    "path": "src/client/app/shared/navbar/navbar.component.ts",
    "content": "import { ChangeDetectionStrategy, Component, EventEmitter, HostListener, Input, Output } from '@angular/core'\nimport { NavbarService } from './navbar.service'\n\nexport interface User {\n  readonly photoURL: string\n  readonly email: string\n  readonly name: string\n}\n\n@Component({\n  selector: 'pm-navbar',\n  templateUrl: './navbar.component.html',\n  styleUrls: ['./navbar.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class NavbarComponent {\n  @HostListener('click', ['$event.target']) click() {\n    this.clicked.next()\n  }\n  @Output() menuIconClick = new EventEmitter()\n  @Output() clicked = new EventEmitter()\n  @Input() user: User\n\n  constructor(public navbarService: NavbarService) { }\n\n  trackByFn(index: number, item: any) {\n    return item.route\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/navbar/navbar.service.spec.ts",
    "content": "import { async, TestBed } from '@angular/core/testing'\nimport { INavbarService, NavbarService } from './navbar.service'\nimport { Observable } from 'rxjs/Observable'\nimport '../../../operators'\n\ndescribe(NavbarService.name, () => {\n  let service: INavbarService\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      providers: [NavbarService]\n    })\n  }))\n\n  beforeEach(() => {\n    service = TestBed.get(NavbarService)\n  })\n\n  afterEach(() => {\n    TestBed.resetTestingModule()\n  })\n\n  it('should construct', async(() => {\n    expect(service).toBeDefined()\n  }))\n\n  it('should return an observable when called', async(() => {\n    expect(service.menu$).toEqual(expect.any(Observable))\n  }))\n})\n"
  },
  {
    "path": "src/client/app/shared/navbar/navbar.service.ts",
    "content": "import { Observable } from 'rxjs/Observable'\nimport { Injectable } from '@angular/core'\n\nexport interface INavbarService {\n  readonly menu$: Observable<ReadonlyArray<any>>\n}\n\n@Injectable()\nexport class NavbarService implements INavbarService {\n  readonly menu$ = Observable.of([\n    { route: 'dashboard', name: 'Dashboard' },\n    { route: 'about', name: 'About' }\n  ])\n}\n"
  },
  {
    "path": "src/client/app/shared/pipes/key-value.pipe.ts",
    "content": "import { Pipe, PipeTransform } from '@angular/core'\n\n@Pipe({ name: 'pmKeyVal' })\nexport class KeyValuePipe implements PipeTransform {\n  public transform(value: { readonly [key: string]: any }): ReadonlyArray<{ readonly key: string, readonly value: any }> {\n    return Object.keys(value || {}).map(key => ({ key, value: value[key] }))\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/pipes/keys.pipe.ts",
    "content": "import { Pipe, PipeTransform } from '@angular/core'\n\n@Pipe({ name: 'pmKeys' })\nexport class KeysPipe implements PipeTransform {\n  public transform(value: { readonly [key: string]: any }): ReadonlyArray<string> {\n    return Object.keys(value || {})\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/pipes/sanitize-html.pipe.ts",
    "content": "import { Pipe, PipeTransform } from '@angular/core'\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser'\n\n@Pipe({\n  name: 'pmSanitizeHtml'\n})\nexport class SanitizeHtmlPipe implements PipeTransform {\n  constructor(private sanitizer: DomSanitizer) { }\n\n  transform(v: string): SafeHtml {\n    return this.sanitizer.bypassSecurityTrustHtml(v)\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/quill-editor/quill-editor.component.html",
    "content": "<div #editor [innerHTML]=\"content | pmSanitizeHtml\" [class]=\"serverStyles\"> </div>"
  },
  {
    "path": "src/client/app/shared/quill-editor/quill-editor.component.scss",
    "content": ":host {\n  display: flex;\n  flex-direction: column;\n  flex: 1;\n  height: 100%;\n}\n"
  },
  {
    "path": "src/client/app/shared/quill-editor/quill-editor.component.ts",
    "content": "import { BehaviorSubject } from 'rxjs/BehaviorSubject'\nimport { PlatformService } from './../services/platform.service'\nimport { InjectionService } from './../services/injection.service'\nimport { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Input, Output, Renderer2, ViewChild } from '@angular/core'\nimport * as Quill from 'quill'\n\n@Component({\n  selector: 'pm-quill-editor',\n  templateUrl: './quill-editor.component.html',\n  styleUrls: ['./quill-editor.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class QuillEditorComponent implements AfterViewInit {\n  @ViewChild('editor') readonly editorContainer: any\n  @Input() readonly content: string\n  @Output() readonly textValue = new BehaviorSubject<string>(this.content)\n  @Output() readonly selectionChange = new EventEmitter()\n  @Output() readonly contentUpdated = new EventEmitter()\n\n  private quill: Quill.Quill\n\n  constructor(injector: InjectionService, renderer: Renderer2, private ps: PlatformService) {\n  }\n\n  get serverStyles() {\n    return this.ps.isServer\n      ? 'ql-container ql-snow ql-editor'\n      : ''\n  }\n\n  ngAfterViewInit() {\n    if (this.ps.isServer) return\n    // tslint:disable-next-line:no-require-imports\n    const _quill = require('quill')\n    const toolbarOptions: ReadonlyArray<any> = [\n      ['bold', 'italic', 'underline', 'strike'],        // toggled buttons\n      ['blockquote', 'code-block'],\n\n      [{ 'header': 1 }, { 'header': 2 }],               // custom button values\n      [{ 'list': 'ordered' }, { 'list': 'bullet' }],\n      [{ 'script': 'sub' }, { 'script': 'super' }],      // superscript/subscript\n      [{ 'indent': '-1' }, { 'indent': '+1' }],          // outdent/indent\n      [{ 'direction': 'rtl' }],                         // text direction\n\n      [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown\n      [{ 'header': [1, 2, 3, 4, 5, 6, false] }],\n\n      [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme\n      [{ 'font': [] }],\n      [{ 'align': [] }],\n      ['code-block'],\n      ['image'],\n      ['video'],\n      ['clean']                                         // remove formatting button\n    ]\n    this.quill = new _quill(this.editorContainer.nativeElement, {\n      modules: { toolbar: toolbarOptions },\n      theme: 'snow'\n    })\n    this.update()\n    this.quill.on('text-change', () => {\n      this.update()\n    })\n  }\n\n  private update() {\n    this.textValue.next(this.html)\n  }\n\n  get html() {\n    return (this.quill as any).container.firstChild.innerHTML\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/__snapshots__/error-handler.service.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`App component should build without a problem 1`] = `\n<div\n  id=\"root0\"\n  ng-version=\"6.0.0-beta.7\"\n>\n  <pm-app>\n    <pm-navbar>\n      <nav\n        class=\"mat-elevation-z6\"\n      >\n        <button\n          angulartics2on=\"click\"\n          angularticsaction=\"ToggleSideMenu\"\n          class=\"mat-icon-button\"\n          id=\"toggle-menu-button\"\n          mat-icon-button=\"\"\n        >\n          <span\n            class=\"mat-button-wrapper\"\n          >\n            <mat-icon\n              aria-hidden=\"true\"\n              class=\"mat-icon\"\n              fonticon=\"fa-bars\"\n              fontset=\"fa\"\n              role=\"img\"\n            />\n          </span>\n          <div\n            class=\"mat-button-ripple mat-ripple\"\n            matripple=\"\"\n          />\n          <div\n            class=\"mat-button-focus-overlay\"\n          />\n        </button>\n        <a\n          class=\"nav-button mat-button\"\n          mat-button=\"\"\n          routerlink=\"\"\n        >\n          <span\n            class=\"mat-button-wrapper\"\n          >\n            FB-ANGULAR-UNIVERSAL-STARTER\n          </span>\n          <div\n            class=\"mat-button-ripple mat-ripple\"\n            matripple=\"\"\n          />\n          <div\n            class=\"mat-button-focus-overlay\"\n          />\n        </a>\n        <div\n          class=\"flex-spacer\"\n        />\n        \n        <a\n          class=\"nav-button mat-button\"\n          href=\"https://www.angularuniversal.com\"\n          mat-button=\"\"\n        >\n          <span\n            class=\"mat-button-wrapper\"\n          >\n            Consulting\n          </span>\n          <div\n            class=\"mat-button-ripple mat-ripple\"\n            matripple=\"\"\n          />\n          <div\n            class=\"mat-button-focus-overlay\"\n          />\n        </a>\n        <a\n          class=\"nav-button mat-button\"\n          href=\"/api-docs\"\n          mat-button=\"\"\n        >\n          <span\n            class=\"mat-button-wrapper\"\n          >\n            API\n          </span>\n          <div\n            class=\"mat-button-ripple mat-ripple\"\n            matripple=\"\"\n          />\n          <div\n            class=\"mat-button-focus-overlay\"\n          />\n        </a>\n        <button\n          aria-haspopup=\"true\"\n          class=\"mat-icon-button\"\n          id=\"user-menu-btn\"\n          mat-icon-button=\"\"\n        >\n          <span\n            class=\"mat-button-wrapper\"\n          >\n            <mat-icon\n              aria-hidden=\"true\"\n              class=\"mat-icon\"\n              fonticon=\"fa-user-circle-o\"\n              fontset=\"fa\"\n              role=\"img\"\n            />\n          </span>\n          <div\n            class=\"mat-button-ripple mat-ripple\"\n            matripple=\"\"\n          />\n          <div\n            class=\"mat-button-focus-overlay\"\n          />\n        </button>\n        <mat-menu\n          class=\"ng-tns-c8-1\"\n        >\n          \n        </mat-menu>\n      </nav>\n    </pm-navbar>\n    <mat-sidenav-container\n      class=\"sidenav-container mat-drawer-container mat-sidenav-container\"\n      exclude=\"#toggle-menu-button\"\n    >\n      <div\n        class=\"mat-drawer-backdrop\"\n      />\n      <mat-sidenav\n        class=\"sidenav mat-elevation-z6 mat-drawer mat-sidenav ng-tns-c1-0 ng-trigger ng-trigger-transform\"\n        tabindex=\"-1\"\n      >\n        <mat-list\n          class=\"mat-list\"\n        >\n          <div\n            id=\"menu-top\"\n          >\n            <h3\n              class=\"mat-subheader\"\n              matsubheader=\"\"\n            >\n              Pages\n            </h3>\n            <mat-slide-toggle\n              class=\"mat-slide-toggle mat-accent\"\n            >\n              <label\n                class=\"mat-slide-toggle-label\"\n              >\n                <div\n                  class=\"mat-slide-toggle-bar\"\n                >\n                  <input\n                    class=\"mat-slide-toggle-input cdk-visually-hidden\"\n                    type=\"checkbox\"\n                  />\n                  <div\n                    class=\"mat-slide-toggle-thumb-container\"\n                  >\n                    <div\n                      class=\"mat-slide-toggle-thumb\"\n                    />\n                    <div\n                      class=\"mat-slide-toggle-ripple mat-ripple\"\n                      mat-ripple=\"\"\n                    />\n                  </div>\n                </div>\n                <span\n                  class=\"mat-slide-toggle-content\"\n                />\n              </label>\n            </mat-slide-toggle>\n          </div>\n          <mat-list-item\n            class=\"mat-list-item\"\n          >\n            <div\n              class=\"mat-list-item-content\"\n            >\n              <div\n                class=\"mat-list-item-ripple mat-ripple\"\n                mat-ripple=\"\"\n              />\n              <div\n                class=\"mat-list-text\"\n              >\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                  routerlink=\"/\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Home\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                  routerlink=\"changelog\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Changelog\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  href=\"/sitemap.xml\"\n                  mat-button=\"\"\n                  matline=\"\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Sitemap\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  href=\"/robots.txt\"\n                  mat-button=\"\"\n                  matline=\"\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Robots\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  href=\"https://www.angularuniversal.com\"\n                  mat-button=\"\"\n                  matline=\"\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Consulting\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n              </div>\n            </div>\n          </mat-list-item>\n          <mat-divider\n            class=\"mat-divider\"\n            role=\"separator\"\n          />\n          <h3\n            class=\"mat-subheader\"\n            matsubheader=\"\"\n          >\n            Boss Area\n          </h3>\n          <mat-list-item\n            class=\"mat-list-item\"\n          >\n            <div\n              class=\"mat-list-item-content\"\n            >\n              <div\n                class=\"mat-list-item-ripple mat-ripple\"\n                mat-ripple=\"\"\n              />\n              <div\n                class=\"mat-list-text\"\n              >\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                  routerlink=\"pages\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Pages\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Assets\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                  routerlink=\"users\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Users\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n                <a\n                  class=\"mat-button mat-line\"\n                  mat-button=\"\"\n                  matline=\"\"\n                  routerlink=\"admin\"\n                >\n                  <span\n                    class=\"mat-button-wrapper\"\n                  >\n                    Admin\n                  </span>\n                  <div\n                    class=\"mat-button-ripple mat-ripple\"\n                    matripple=\"\"\n                  />\n                  <div\n                    class=\"mat-button-focus-overlay\"\n                  />\n                </a>\n              </div>\n            </div>\n          </mat-list-item>\n        </mat-list>\n      </mat-sidenav>\n      \n    </mat-sidenav-container>\n  </pm-app>\n</div>\n`;\n"
  },
  {
    "path": "src/client/app/shared/services/adblock.service.spec.ts",
    "content": "import { IPlatformService, PlatformService } from './platform.service'\nimport { AdblockService, IAdblockService } from './adblock.service'\nimport { async, TestBed } from '@angular/core/testing'\nimport { HttpClientModule } from '@angular/common/http'\nimport { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'\nimport '../../../operators'\n\ndescribe(AdblockService.name, () => {\n  let service: IAdblockService\n  let httpMock: HttpTestingController\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [HttpClientTestingModule, HttpClientModule],\n      providers: [\n        AdblockService,\n        { provide: PlatformService, useValue: new MockPlatformService() }\n      ]\n    })\n  }))\n\n  beforeEach(() => {\n    service = TestBed.get(AdblockService)\n    httpMock = TestBed.get(HttpTestingController)\n  })\n\n  it('should detect adblock is present', async(() => {\n    service.adBlockerIsActive$.subscribe(isPresent => {\n      expect(isPresent).toBeTruthy()\n    })\n\n    const req = httpMock.expectOne(r => r.url === './ad-server.js')\n    expect(req.request.method).toEqual('GET')\n\n    req.flush('', { status: 404, statusText: 'not found'})\n    httpMock.verify()\n  }))\n\n  it('should detect adblock is not present', async(() => {\n    service.adBlockerIsActive$.subscribe(isPresent => {\n      expect(isPresent).toBeFalsy()\n    })\n\n    const req = httpMock.expectOne(r => r.url === './ad-server.js')\n    expect(req.request.method).toEqual('GET')\n\n    req.flush('', { status: 200, statusText: 'not found'})\n    httpMock.verify()\n  }))\n\n  it('should return false when not on platform browser', async(() => {\n    const ps = TestBed.get(PlatformService) as MockPlatformService\n    ps.isServer = true\n    service.adBlockerIsActive$.subscribe(result => expect(result).toBe(false))\n  }))\n})\n\nclass MockPlatformService implements IPlatformService {\n  public isBrowser = true\n  public isServer = false\n}\n"
  },
  {
    "path": "src/client/app/shared/services/adblock.service.ts",
    "content": "import { HttpClient } from '@angular/common/http'\nimport { Injectable } from '@angular/core'\nimport { PlatformService } from './platform.service'\nimport { Observable } from 'rxjs/Observable'\n\nexport interface IAdblockService {\n  adBlockerIsActive$: Observable<boolean>\n}\n\n@Injectable()\nexport class AdblockService implements IAdblockService {\n  public adBlockerIsActive$ = this.platformService.isBrowser\n    ? this.http.get('./ad-server.js')\n      .switchMap(a => Observable.of(false))\n      .catch(a => Observable.of(true))\n    : Observable.of(false)\n\n  constructor(private platformService: PlatformService, private http: HttpClient) { }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/auth.service.ts",
    "content": "import { PlatformService } from './platform.service'\nimport { BehaviorSubject } from 'rxjs/BehaviorSubject'\nimport { SettingService } from './setting.service'\nimport { CookieService } from './cookie.service'\nimport { Observable } from 'rxjs/Observable'\nimport { Inject, Injectable, InjectionToken } from '@angular/core'\nimport { AngularFireAuth } from 'angularfire2/auth'\nimport { JwtHelper } from 'angular2-jwt'\nimport { fromPromise } from 'rxjs/observable/fromPromise'\nimport { of } from 'rxjs/observable/of'\nimport { FirebaseDatabaseService } from './firebase-database.service'\nimport * as firebase from 'firebase/app'\nimport { Router } from '@angular/router'\nimport { User } from 'firebase'\n\nexport interface ExtendedUser {\n  id: string\n  jwt: Object\n  jwtDecoded: Object\n  emailVerified: boolean\n  displayName: string\n  email: string\n  phoneNumber: string\n  photoURL: string\n  providerId: string\n  roles: { [key: string]: boolean }\n}\n\nexport interface IAuthService {\n  user$: Observable<ExtendedUser>\n  logout(): void\n  redirectToSignInPage(): void\n}\n\nexport const FB_COOKIE_KEY = new InjectionToken<string>('auth.cookie.key')\n\n@Injectable()\nexport class AuthService implements IAuthService {\n  private jwtHelper = new JwtHelper()\n\n  private viaCookies$ = this.cs.cookies$\n    .map(cookies => {\n      return cookies\n        ? AuthService.cookieMapper(cookies[this.COOKIE_KEY], this.jwtHelper)\n        : undefined\n    })\n    .distinctUntilChanged()\n    .shareReplay()\n\n  static cookieMapper(data: any, jwtHelper: JwtHelper): any {\n    if (!data || !data.jwt && !jwtHelper.isTokenExpired(data.jwt)) return undefined\n    const jwtDecoded = jwtHelper.decodeToken(data.jwt)\n    return {\n      ...data,\n      jwtDecoded,\n      id: jwtDecoded.user_id,\n      emailVerified: jwtDecoded.email_verified\n    }\n  }\n\n  private userSource = new BehaviorSubject<ExtendedUser>(AuthService.cookieMapper(this.cs.get(this.COOKIE_KEY), this.jwtHelper))\n  public user$ = this.userSource.asObservable()\n  public isAdmin$ = this.user$.map(user => this.isAdmin(user))\n  public isAdmin(user: ExtendedUser) {\n    return user && user.roles && (user.roles.admin || user.roles.superadmin)\n  }\n\n  constructor(private cs: CookieService, private fbAuth: AngularFireAuth, ss: SettingService, private ps: PlatformService,\n    private db: FirebaseDatabaseService, @Inject(FB_COOKIE_KEY) private COOKIE_KEY: string, private router: Router) {\n    this.viaCookies$.subscribe(a => this.userSource.next(a))\n\n    if (ps.isServer) return\n\n    this.fbAuth.authState\n      .filter(a => a !== null)\n      .map(a => a as User)\n      .flatMap(a => this.db\n        .getObjectRef(`users/${a.uid}`)\n        .update({\n          displayName: a.displayName,\n          email: a.email,\n          photo: a.photoURL,\n          providers: (a.providerData || []).map((b: any) => b && b.providerId),\n          updated: new Date().toUTCString()\n        })\n        .catch(() => undefined), user => user)\n      .flatMap(a => a.getIdToken(), (user, token) => ({ user, token }))\n      .flatMap(a => this.db\n        .get(`users/${a.user.uid}`)\n        .map((b: any) => b && b.roles || {})\n        .catch(() => Observable.of(undefined)), (res, roles) => ({ roles, ...res }))\n      .flatMap(a => ss.settings$, (user, settings) => ({ ...user, settings }))\n      .subscribe(res => {\n        if (!res.roles) return\n        if (res.user.providerData) {\n          const expires = this.jwtHelper.getTokenExpirationDate(res.token as string)\n          const important = {\n            jwt: res.token,\n            roles: res.roles,\n            providerId: res.user.providerId,\n            displayName: res.user.displayName,\n            email: res.user.email,\n            photoURL: res.user.photoURL ? res.user.photoURL : res.settings.assets.userAvatarImage,\n            phoneNumber: res.user.phoneNumber,\n            providers: ((res && res.user.providerData) || []).map((a: any) => a && a.providerId)\n          }\n          cs.set(this.COOKIE_KEY, important, { expires })\n        }\n      })\n  }\n\n  signInWithFacebookRedirect() {\n    return fromPromise(this.fbAuth.auth.signInWithRedirect(new firebase.auth.FacebookAuthProvider()))\n  }\n\n  signInWithGoogleRedirect() {\n    return fromPromise(this.fbAuth.auth.signInWithRedirect(new firebase.auth.GoogleAuthProvider()))\n  }\n\n  signInWithGithubRedirect() {\n    return fromPromise(this.fbAuth.auth.signInWithRedirect(new firebase.auth.GithubAuthProvider()))\n  }\n\n  signInWithTwitterRedirect() {\n    return fromPromise(this.fbAuth.auth.signInWithRedirect(new firebase.auth.TwitterAuthProvider()))\n  }\n\n  createUserWithEmailAndPassword(email: string, password: string) {\n    return fromPromise(this.fbAuth.auth.createUserWithEmailAndPassword(email, password))\n  }\n\n  signInWithEmailAndPassword(email: string, password: string) {\n    return fromPromise(this.fbAuth.auth.signInWithEmailAndPassword(email, password))\n  }\n\n  redirectToSignInPage() {\n    this.router.navigate(['login'], { queryParams: { redirect: this.router.url }, skipLocationChange: true })\n  }\n\n  logout() {\n    if (this.ps.isBrowser) {\n      this.fbAuth.auth\n        .signOut()\n        .then(() => this.cs.remove(this.COOKIE_KEY))\n    }\n  }\n\n  refreshEmailCredentials(paswword: string) {\n    return this.fbAuth.authState\n      .map(user => {\n        return user && {\n          user,\n          credentials: firebase.auth.EmailAuthProvider.credential(user.email as string, paswword)\n        }\n      })\n  }\n\n  updateEmailPassword(currentPassword: string, newPassword: string) {\n    return this.refreshEmailCredentials(currentPassword)\n      .flatMap((userObj: {\n        user: User,\n        credentials: firebase.auth.AuthCredential\n      }) => userObj.user.reauthenticateWithCredential(userObj.credentials), (userObj: {\n        user: firebase.User,\n        credentials: firebase.auth.AuthCredential\n      }, res) => userObj.user)\n      .flatMap(user => user.updatePassword(newPassword))\n  }\n\n  updateProfile(displayName?: string, photoURL?: string) {\n    return this.fbAuth.authState\n      .flatMap(user => {\n        return user\n          ? user.updateProfile({\n            // tslint:disable:no-null-keyword\n            displayName: displayName || null,\n            photoURL: photoURL || null\n          })\n          : Observable.throw('missing user')\n      }, (user, e) => user)\n      .flatMap(user => user ? user.getIdToken(true) : of(undefined))\n      .flatMap(() => this.fbAuth.idToken)\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/cookie.service.spec.ts",
    "content": "import { PlatformService } from './platform.service'\nimport { CookieService, ICookieService } from './cookie.service'\nimport { async, TestBed } from '@angular/core/testing'\nimport { REQUEST } from '@nguniversal/express-engine/tokens'\n\ndescribe(CookieService.name, () => {\n  let service: ICookieService\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      providers: [\n        CookieService,\n        PlatformService,\n        { provide: REQUEST, useValue: {} }\n      ]\n    })\n  }))\n\n  beforeEach(() => {\n    service = TestBed.get(CookieService)\n  })\n\n  afterEach(() => {\n    TestBed.resetTestingModule()\n  })\n\n  it('should construct', async(() => {\n    expect(service).toBeDefined()\n  }))\n})\n"
  },
  {
    "path": "src/client/app/shared/services/cookie.service.ts",
    "content": "import { REQUEST } from '@nguniversal/express-engine/tokens'\nimport { PlatformService } from './platform.service'\nimport { Inject, Injectable } from '@angular/core'\nimport { Subject } from 'rxjs/Subject'\nimport { Observable } from 'rxjs/Observable'\nimport { CookieAttributes, getJSON, remove, set } from 'js-cookie'\n\nexport interface ICookieService {\n  readonly cookies$: Observable<{ readonly [key: string]: any }>\n  getAll(): any\n  get(name: string): any\n  set(name: string, value: any, options?: CookieAttributes): void\n  remove(name: string, options?: CookieAttributes): void\n}\n\n@Injectable()\nexport class CookieService implements ICookieService {\n  private readonly cookieSource = new Subject<{ readonly [key: string]: any }>()\n  public readonly cookies$ = this.cookieSource.asObservable()\n\n  constructor(private platformService: PlatformService, @Inject(REQUEST) private req: any) { }\n\n  public set(name: string, value: any, options?: CookieAttributes): void {\n    if (this.platformService.isBrowser) {\n      set(name, value, options)\n      this.updateSource()\n    }\n  }\n\n  public remove(name: string, options?: CookieAttributes): void {\n    if (this.platformService.isBrowser) {\n      remove(name, options)\n      this.updateSource()\n    }\n  }\n\n  public get(name: string): any {\n    if (this.platformService.isBrowser) {\n      return getJSON(name)\n    } else {\n      try {\n        return JSON.parse(this.req.cookies[name])\n      } catch (err) {\n        return this.req ? this.req.cookies[name] : undefined\n      }\n    }\n  }\n\n  public getAll(): any {\n    if (this.platformService.isBrowser) {\n      return getJSON()\n    } else {\n      if (this.req) return this.req.cookies\n    }\n  }\n\n  private updateSource() {\n    this.cookieSource.next(this.getAll())\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/environment.service.spec.ts",
    "content": "import { EnvironmentService, IEnvironmentService } from './environment.service'\nimport { async, TestBed } from '@angular/core/testing'\nimport { ENV_CONFIG } from '../../app.config'\n\ndescribe(EnvironmentService.name, () => {\n  let service: IEnvironmentService\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      providers: [\n        EnvironmentService,\n        { provide: ENV_CONFIG, useValue: { someValue: 1 } }\n      ]\n    })\n  }))\n\n  beforeEach(() => {\n    service = TestBed.get(EnvironmentService)\n  })\n\n  afterEach(() => {\n    TestBed.resetTestingModule()\n  })\n\n  it('should construct', async(() => {\n    expect(service).toBeDefined()\n  }))\n\n  it('should return config', async(() => {\n    expect(service.config).toEqual({\n      someValue: 1\n    })\n  }))\n})\n"
  },
  {
    "path": "src/client/app/shared/services/environment.service.ts",
    "content": "import { Inject, Injectable } from '@angular/core'\nimport { ENV_CONFIG } from '../../app.config'\nimport { EnvConfig } from '../../../../../tools/config/app.config'\n\nexport interface IEnvironmentService {\n  readonly config: EnvConfig\n}\n\n@Injectable()\nexport class EnvironmentService implements IEnvironmentService {\n  constructor(@Inject(ENV_CONFIG) private _config: any) { }\n  get config(): EnvConfig {\n    return this._config\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/error-handler.service.spec.ts",
    "content": "import { RouterTestingModule } from '@angular/router/testing'\nimport { PlatformService } from './platform.service'\nimport { GlobalErrorHandler } from './error-handler.service'\nimport { async, fakeAsync, TestBed, tick } from '@angular/core/testing'\nimport { ILoggingService, LOGGER_CONFIG, LoggingService } from './logging.service'\nimport { EnvironmentService, IEnvironmentService } from './environment.service'\nimport { TESTING_CONFIG } from '../../app.component.spec'\nimport * as StackTrace from 'stacktrace-js'\n\ndescribe(GlobalErrorHandler.name, () => {\n  let service: GlobalErrorHandler\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [RouterTestingModule],\n      providers: [\n        GlobalErrorHandler,\n        PlatformService,\n        { provide: LoggingService, useValue: new MockLoggingService() },\n        { provide: EnvironmentService, useValue: new MockEnvironmentService() },\n        {\n          provide: LOGGER_CONFIG,\n          useValue: {\n            name: 'Angular Universal App',\n            type: 'app'\n          }\n        }\n      ]\n    })\n  }))\n\n  beforeEach(async(() => {\n    service = TestBed.get(GlobalErrorHandler)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should construct', async(() => {\n    expect(service).toBeDefined()\n  }))\n\n  it('should log an error', fakeAsync(() => {\n    const spy = jest.spyOn(TestBed.get(LoggingService), 'error')\n    service.handleError(new Error('Testing this error'))\n    tick()\n    expect(spy).toHaveBeenCalledTimes(1)\n  }))\n\n  it('should log an error with message, url, and stack trace', fakeAsync(() => {\n    const spy = jest.spyOn(TestBed.get(LoggingService), 'error')\n    const error = new Error('Testing this error')\n    service.handleError(error)\n    tick()\n    StackTrace.fromError(error).then(stackframes => {\n      const stack = stackframes\n        .splice(0, 20)\n        .map(sf => sf.toString())\n        .join('\\n')\n      expect(spy).toBeCalledWith('Testing this error', { url: '', stack }, { config: TESTING_CONFIG })\n    })\n  }))\n\n  it('should catch bad stack trace parse', fakeAsync(() => {\n    const spy = jest.spyOn(TestBed.get(LoggingService), 'error')\n    service.handleError({ message: 'Testing this error' })\n    tick()\n    StackTrace.fromError({} as any).catch(error => {\n      expect(spy).toBeCalledWith('Testing this error', { url: '', stack: error }, { config: TESTING_CONFIG })\n    })\n  }))\n})\n\n// tslint:disable:no-empty\nclass MockLoggingService implements ILoggingService {\n  error(msg: string, errObj?: { [key: string]: any; } | undefined, extendedObj?: { [key: string]: any; } | undefined): void { }\n  trace(msg: string, errObj?: { [key: string]: any; } | undefined, extendedObj?: { [key: string]: any; } | undefined): void { }\n  debug(msg: string, errObj?: { [key: string]: any; } | undefined, extendedObj?: { [key: string]: any; } | undefined): void { }\n  info(msg: string, errObj?: { [key: string]: any; } | undefined, extendedObj?: { [key: string]: any; } | undefined): void { }\n  warn(msg: string, errObj?: { [key: string]: any; } | undefined, extendedObj?: { [key: string]: any; } | undefined): void { }\n}\n\nclass MockEnvironmentService implements IEnvironmentService {\n  config = TESTING_CONFIG\n}\n"
  },
  {
    "path": "src/client/app/shared/services/error-handler.service.ts",
    "content": "import { ErrorHandler, Injectable, Injector } from '@angular/core'\nimport { LocationStrategy, PathLocationStrategy } from '@angular/common'\nimport { ILoggingService, LoggingService } from './logging.service'\nimport { EnvironmentService, IEnvironmentService } from './environment.service'\nimport * as StackTrace from 'stacktrace-js'\n\n@Injectable()\nexport class GlobalErrorHandler implements ErrorHandler {\n  constructor(private injector: Injector) { }\n\n  handleError(error: any) {\n    const log = this.injector.get(LoggingService) as ILoggingService\n    const env = this.injector.get(EnvironmentService) as IEnvironmentService\n    const location = this.injector.get(LocationStrategy) as LocationStrategy\n    const message = error.message ? error.message : error.toString()\n    const url = location instanceof PathLocationStrategy ? location.path() : ''\n\n    // lets grab the last 20 stacks only\n    StackTrace.fromError(error).then(stackframes => {\n      const stack = stackframes\n        .splice(0, 20)\n        .map(sf => sf.toString())\n        .join('\\n')\n\n        log.error(message, { url, stack }, { config: env.config })\n    }).catch(err => log.error(message, { url, stack: err }, { config: env.config }))\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/firebase-database.service.ts",
    "content": "import { PlatformService } from './platform.service'\nimport { Observable } from 'rxjs/Observable'\nimport { Injectable } from '@angular/core'\nimport { AngularFireDatabase } from 'angularfire2/database'\nimport { makeStateKey, TransferState } from '@angular/platform-browser'\nimport { database } from 'firebase'\nimport { PathReference } from 'angularfire2/database/interfaces'\n\n@Injectable()\nexport class FirebaseDatabaseService {\n  constructor(private db: AngularFireDatabase, private ts: TransferState, private ps: PlatformService) { }\n\n  get<T>(path: string) {\n    const cached = this.ts.get<T | undefined>(this.cacheKey(path), undefined)\n    return cached\n      ? this.db.object<T>(path).valueChanges().startWith(cached).catch(err => Observable.of(undefined))\n      : this.db.object<T>(path).valueChanges().catch(err => Observable.of(undefined))\n  }\n\n  getList<T>(path: PathReference, queryFn?: (ref: database.Reference) => database.Query): Observable<T[]> {\n    const cached = this.ts.get<T[] | undefined>(this.cacheKey(path.toString()), undefined)\n    return cached\n      ? this.db.list<T>(path, queryFn).valueChanges().startWith(cached as any).catch(err => Observable.of([]))\n      : this.db.list<T>(path, queryFn).valueChanges().catch(err => Observable.of([]))\n  }\n\n  getListKeyed<T>(path: PathReference, queryFn?: (ref: database.Reference) => database.Query): Observable<T[]> {\n    const cached = this.ts.get<T[] | undefined>(this.cacheKey(path.toString()), undefined)\n    return cached\n      ? this.db.list(path, queryFn)\n        .snapshotChanges()\n        .map(a => this.keyMapper(a))\n        .startWith(cached as any).catch(err => Observable.of([]))\n      : this.db.list(path, queryFn)\n        .snapshotChanges()\n        .map(a => this.keyMapper(a))\n        .catch(err => Observable.of([]))\n  }\n\n  keyMapper(res: any) {\n    if (this.ps.isServer) return res\n    return res.map((obj: any) => {\n      return {\n        id: obj.key,\n        ...obj.payload.val()\n      }\n    })\n  }\n\n  getListRef<T>(path: PathReference, queryFn?: (ref: database.Reference) => database.Query) {\n    return this.db.list<T>(path, queryFn)\n  }\n\n  getObjectRef<T>(path: string) {\n    return this.db.object<T>(path)\n  }\n\n  cacheKey(path: string) {\n    return makeStateKey<string>(`FB.${path}`)\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/guard-admin.service.ts",
    "content": "import { AuthService } from './auth.service'\nimport { Injectable } from '@angular/core'\nimport { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'\nimport { Observable } from 'rxjs/Observable'\nimport { ServerResponseService } from './server-response.service'\nimport '../../../operators'\n\n@Injectable()\nexport class AdminGuard implements CanActivate {\n\n  constructor(private auth: AuthService, private router: Router, private srs: ServerResponseService) { }\n\n  canActivate(ars: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {\n    return this.auth.isAdmin$.do(a => {\n      if (!a) {\n        this.srs.setUnauthorized()\n        this.router.navigate(['unauthorized'], { queryParams: { redirect: state.url } })\n      }\n    })\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/guard-login.service.ts",
    "content": "import { AuthService } from './auth.service'\nimport { Injectable } from '@angular/core'\nimport { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'\nimport { Observable } from 'rxjs/Observable'\nimport '../../../operators'\n\n@Injectable()\nexport class LoginGuard implements CanActivate {\n\n  constructor(private auth: AuthService) { }\n\n  canActivate(ars: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {\n    return this.auth.user$.map(a => a ? true : false)\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/http-config-interceptor.service.spec.ts",
    "content": "import { EnvironmentService, IEnvironmentService } from './environment.service'\nimport { HttpConfigInterceptor } from './http-config-interceptor.service'\nimport {\n  HTTP_INTERCEPTORS,\n  HttpClient, HttpClientModule,\n  HttpInterceptor\n} from '@angular/common/http'\nimport { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'\nimport { async, TestBed } from '@angular/core/testing'\nimport '../../../operators'\n\ndescribe(HttpConfigInterceptor.name, () => {\n  let interceptor: HttpInterceptor\n  let http: HttpClient\n  let httpMock: HttpTestingController\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [HttpClientTestingModule, HttpClientModule],\n      providers: [\n        HttpConfigInterceptor,\n        { provide: HTTP_INTERCEPTORS, useClass: HttpConfigInterceptor, multi: true },\n        { provide: EnvironmentService, useValue: new MockEnvironmentService() }\n      ]\n    })\n  }))\n\n  beforeEach(() => {\n    interceptor = TestBed.get(HttpConfigInterceptor)\n    http = TestBed.get(HttpClient)\n    httpMock = TestBed.get(HttpTestingController)\n  })\n\n  afterEach(() => {\n    TestBed.resetTestingModule()\n  })\n\n  it('should construct', async(() => {\n    expect(interceptor).toBeDefined()\n  }))\n\n  it('should passthrough absolute url requests', async(() => {\n    expect.assertions(2)\n    http.get('http://www.google.com/api/some-thing').subscribe(response => expect(response).toBeTruthy())\n\n    const req = httpMock.expectOne(r => r.url === 'http://www.google.com/api/some-thing')\n    expect(req.request.method).toEqual('GET')\n\n    req.flush({ hello: 'world' })\n    httpMock.verify()\n  }))\n\n  it('should make relative urls universal friendly', async(() => {\n    expect.assertions(2)\n    http.get('./changelog.md').take(1).subscribe(response => expect(response).toBeTruthy())\n\n    const req = httpMock.expectOne(r => r.url === 'http://my-awesome-website.com/changelog.md')\n    expect(req.request.method).toEqual('GET')\n\n    req.flush({})\n\n    httpMock.verify()\n  }))\n\n  it('should handle entity reqests', async(() => {\n    expect.assertions(2)\n    http.get('note/123').take(1).subscribe(response => expect(response).toBeTruthy())\n\n    const req = httpMock.expectOne(r => r.url === 'http://my.api.com/note/123')\n    expect(req.request.method).toEqual('GET')\n\n    req.flush({})\n\n    httpMock.verify()\n  }))\n\n  it('should throw an exception when no config is present', async(() => {\n    const env = TestBed.get(EnvironmentService)\n    env.value = {}\n    expect.assertions(1)\n    expect(() => http.get('./changelog.md').take(1).subscribe(response => expect(response).toBeTruthy()))\n      .toThrow('missing endpoint configuration value')\n  }))\n\n  it('should throw an exception when no host value is present', async(() => {\n    const env = TestBed.get(EnvironmentService)\n    env.value = {\n      endpoints: {\n        api: 'http://my.api.com'\n      }\n    }\n    expect.assertions(1)\n    expect(() => http.get('./changelog.md').take(1).subscribe(response => expect(response).toBeTruthy()))\n      .toThrow('missing host configuration value')\n  }))\n\n})\n\nclass MockEnvironmentService implements IEnvironmentService {\n  get config() {\n    return this.value || {\n      endpoints: {\n        api: 'http://my.api.com'\n      },\n      host: 'http://my-awesome-website.com'\n    }\n  }\n  constructor(public value?: any) { }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/http-config-interceptor.service.ts",
    "content": "import { EnvironmentService } from './environment.service'\nimport { Observable } from 'rxjs/Observable'\nimport { Injectable } from '@angular/core'\nimport { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'\n\n@Injectable()\nexport class HttpConfigInterceptor implements HttpInterceptor {\n\n  constructor(private env: EnvironmentService) { }\n\n  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {\n    // handles absolute http requests\n    if (req.url.includes('http')) return next.handle(req)\n\n    if (!this.env.config.endpoints || !this.env.config.endpoints.api) throw new Error('missing endpoint configuration value')\n\n    // handles relative urls on node server\n    if (req.url.includes('./')) {\n      if (!this.env.config.host) throw new Error('missing host configuration value')\n      return next.handle(req.clone({\n        url: `${this.env.config.host}/${req.url.replace('./', '')}`\n      }))\n    }\n\n    // handles entity requests to remote API\n    return next.handle(req.clone({\n      url: `${this.env.config.endpoints.api}/${req.url}`\n    }))\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/http-cookie-interceptor.service.spec.ts",
    "content": "import { REQUEST } from '@nguniversal/express-engine/tokens'\nimport { IPlatformService, PlatformService } from './platform.service'\nimport { ENV_CONFIG } from './../../app.config'\nimport { COOKIE_HOST_WHITELIST, HttpCookieInterceptor } from './http-cookie-interceptor.service'\nimport {\n  HTTP_INTERCEPTORS, HttpClient, HttpClientModule, HttpInterceptor, HttpResponse\n} from '@angular/common/http'\nimport { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'\nimport { async, TestBed } from '@angular/core/testing'\nimport '../../../operators'\n\ndescribe(HttpCookieInterceptor.name, () => {\n  describe('when on platform server', () => {\n    let interceptor: HttpInterceptor\n    let http: HttpClient\n    let httpMock: HttpTestingController\n\n    beforeEach(async(() => {\n      TestBed.configureTestingModule({\n        imports: [HttpClientTestingModule, HttpClientModule],\n        providers: [\n          HttpCookieInterceptor,\n          { provide: HTTP_INTERCEPTORS, useClass: HttpCookieInterceptor, multi: true },\n          { provide: PlatformService, useValue: new MockPlatformService(false) },\n          {\n            provide: COOKIE_HOST_WHITELIST,\n            useValue: [\n              'some-domain.com'\n            ]\n          },\n          {\n            provide: REQUEST, useValue: {\n              cookies: {\n                'jwt_for_some_app': '123'\n              }\n            }\n          }\n        ]\n      })\n    }))\n\n    beforeEach(() => {\n      interceptor = TestBed.get(HttpCookieInterceptor)\n      http = TestBed.get(HttpClient)\n      httpMock = TestBed.get(HttpTestingController)\n    })\n\n    afterEach(() => {\n      TestBed.resetTestingModule()\n    })\n\n    it('should construct', async(() => {\n      expect(interceptor).toBeDefined()\n    }))\n\n    it('should send request with cookie headers when user is logged in', async(() => {\n      expect.assertions(4)\n\n      http.get('http://some-domain.com/article/123', {\n        observe: 'response'\n      }).subscribe(response => {\n        expect(response).toBeInstanceOf(HttpResponse)\n      })\n\n      const req = httpMock.expectOne(r => r.url === 'http://some-domain.com/article/123')\n      expect(req.request.method).toEqual('GET')\n      expect(req.request.withCredentials).toBeTruthy()\n      expect(req.request.headers.get('Cookie')).toEqual('jwt_for_some_app=123;')\n\n      req.flush({})\n      httpMock.verify()\n    }))\n\n    it('should not send request with cookie headers when request is from non-whitelisted domain', async(() => {\n      expect.assertions(4)\n\n      http.get('http://google.com/article/123', {\n        observe: 'response'\n      }).subscribe(response => {\n        expect(response).toBeInstanceOf(HttpResponse)\n      })\n\n      const req = httpMock.expectOne(r => r.url === 'http://google.com/article/123')\n      expect(req.request.method).toEqual('GET')\n      expect(req.request.withCredentials).toBeFalsy()\n      expect(req.request.headers.get('Cookie')).toBeNull()\n\n      req.flush({})\n      httpMock.verify()\n    }))\n\n    it('should return original request if no auth data is found', async(() => {\n      expect.assertions(3)\n\n      http.get('http://google.com/article/123', {\n        observe: 'response'\n      }).subscribe(response => {\n        expect(response).toBeInstanceOf(HttpResponse)\n      })\n\n      const req = httpMock.expectOne(r => r.url === 'http://google.com/article/123')\n      expect(req.request.method).toEqual('GET')\n      expect(req.request.headers.get('Authorization')).toBeFalsy()\n\n      req.flush({})\n      httpMock.verify()\n    }))\n\n  })\n  describe('when on platform browser', () => {\n    let interceptor: HttpInterceptor\n    let http: HttpClient\n    let httpMock: HttpTestingController\n\n    beforeEach(async(() => {\n      TestBed.configureTestingModule({\n        imports: [HttpClientTestingModule, HttpClientModule],\n        providers: [\n          HttpCookieInterceptor,\n          { provide: HTTP_INTERCEPTORS, useClass: HttpCookieInterceptor, multi: true },\n          { provide: PlatformService, useValue: new MockPlatformService(false) },\n          { provide: REQUEST, useValue: {} },\n          {\n            provide: COOKIE_HOST_WHITELIST,\n            useValue: [\n              'some-domain.com'\n            ]\n          },\n          {\n            provide: ENV_CONFIG,\n            useValue: {\n              endpoints: {\n                discovery: 'http://some.endpoint/api'\n              }\n            }\n          }\n        ]\n      }).compileComponents()\n    }))\n\n    beforeEach(() => {\n      interceptor = TestBed.get(HttpCookieInterceptor)\n      http = TestBed.get(HttpClient)\n      httpMock = TestBed.get(HttpTestingController)\n    })\n\n    afterEach(() => {\n      TestBed.resetTestingModule()\n    })\n\n    it('should construct', async(() => {\n      expect(interceptor).toBeDefined()\n    }))\n\n    it('should clone request with credentials set to true', async(() => {\n      expect.assertions(2)\n      http.get('http://google.com/article/123', {\n        observe: 'response'\n      }).subscribe(response => {\n        expect(response).toBeInstanceOf(HttpResponse)\n      })\n\n      const req = httpMock.expectOne(r => r.url === 'http://google.com/article/123')\n      expect(req.request.method).toEqual('GET')\n      // TOOD: expect(req.request.withCredentials).toBeTruthy()\n\n      req.flush({ data: {} })\n      httpMock.verify()\n    }))\n  })\n\n})\n\nclass MockPlatformService implements IPlatformService {\n  isServer: boolean = !this.isServer\n  constructor(public isBrowser = true) { }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/http-cookie-interceptor.service.ts",
    "content": "import { REQUEST } from '@nguniversal/express-engine/tokens'\nimport { PlatformService } from './platform.service'\nimport { Observable } from 'rxjs/Observable'\nimport { Injectable, InjectionToken, Injector } from '@angular/core'\nimport { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'\nimport * as express from 'express'\n\n// tslint:disable-next-line:no-require-imports\nconst URL = require('url-parse')\n\nexport const COOKIE_HOST_WHITELIST = new InjectionToken<string[]>('app.cookie.whitelist')\n\n@Injectable()\nexport class HttpCookieInterceptor implements HttpInterceptor {\n\n  constructor(private ps: PlatformService, private injector: Injector) { }\n\n  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {\n    const whitelistedDomains = this.injector.get(COOKIE_HOST_WHITELIST) as string[]\n    const url = new URL(req.url)\n\n    if (whitelistedDomains && !whitelistedDomains.some(a => a === url.hostname)) return next.handle(req)\n\n    if (this.ps.isServer) {\n      const serverRequest = this.injector.get(REQUEST) as express.Request\n\n      const cookieString = Object.keys(serverRequest.cookies || {}).reduce((accumulator, cookieName) => {\n        accumulator += `${cookieName}=${serverRequest.cookies[cookieName]};`\n        return accumulator\n      }, '')\n\n      return next.handle(req.clone({\n        withCredentials: true,\n        headers: req.headers.set('Cookie', cookieString)\n      }))\n    }\n    return next.handle(req.clone({\n      withCredentials: true\n    }))\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/injection.service.ts",
    "content": "import { DOCUMENT } from '@angular/platform-browser'\nimport { Inject, Injectable, Renderer2 } from '@angular/core'\nimport { sha1 } from 'object-hash'\n\nexport interface DOMInjectable {\n  readonly inHead: boolean\n  readonly element: string\n  readonly value?: string\n  readonly attributes?: { readonly [key: string]: string | boolean }\n}\n\n@Injectable()\nexport class InjectionService {\n  constructor(@Inject(DOCUMENT) private doc: any) { }\n\n  createElement<T extends HTMLElement>(renderer: Renderer2, injectable: DOMInjectable): T | undefined {\n    if (!injectable || !injectable.element) return undefined\n    const elm = renderer.createElement(injectable.element) as T\n    const id = sha1(JSON.stringify(injectable))\n\n    renderer.setProperty(elm, 'id', id)\n    if (injectable.value) renderer.setValue(elm, injectable.value)\n\n    Object.keys(injectable.attributes || {})\n      .forEach(key => renderer.setAttribute(elm, key, (injectable.attributes || {})[key] as any))\n\n    return elm\n  }\n\n  getElementStringForm(renderer: Renderer2, injectable: DOMInjectable | undefined) {\n    if (!injectable) return ''\n    const elm = this.createElement(renderer, injectable)\n    return ((elm && elm.outerHTML) || '').replace('><', `>${injectable.value}<`)\n  }\n\n  inject(renderer: Renderer2, injectable: DOMInjectable) {\n    const elm = this.createElement(renderer, injectable)\n\n    if (!elm || this.doc.getElementById(elm.id)) return\n\n    injectable.inHead\n      ? renderer.appendChild(this.doc.head, elm)\n      : renderer.appendChild(this.doc.body, elm)\n  }\n\n  injectCollection(renderer: Renderer2, injectables: ReadonlyArray<DOMInjectable>) {\n    injectables.forEach(injectable => this.inject(renderer, injectable))\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/logging.service.spec.ts",
    "content": "import { PlatformService } from './platform.service'\nimport { ILoggingService, LOGGER_CONFIG, LoggingService } from './logging.service'\nimport { async, TestBed } from '@angular/core/testing'\nimport { PLATFORM_ID } from '@angular/core'\n\ndescribe(LoggingService.name, () => {\n  describe('browser', () => {\n    let service: ILoggingService\n    beforeEach(async(() => {\n      TestBed.configureTestingModule({\n        providers: [\n          LoggingService,\n          PlatformService,\n          { provide: PLATFORM_ID, useValue: 'browser' },\n          {\n            provide: LOGGER_CONFIG,\n            useValue: {\n              name: 'Angular Universal App',\n              type: 'app'\n            }\n          }\n        ]\n      })\n    }))\n\n    beforeEach(async(() => {\n      service = TestBed.get(LoggingService)\n    }))\n\n    afterEach(async(() => {\n      TestBed.resetTestingModule()\n    }))\n\n    it('should construct', async(() => {\n      expect(service).toBeDefined()\n    }))\n\n    it('should call external logging library', async(() => {\n      const traceSpy = jest.spyOn((service as any).logger, 'trace')\n      const debugSpy = jest.spyOn((service as any).logger, 'debug')\n      const infoSpy = jest.spyOn((service as any).logger, 'info')\n      const warnSpy = jest.spyOn((service as any).logger, 'warn')\n      const errorSpy = jest.spyOn((service as any).logger, 'error')\n      service.trace('')\n      service.debug('')\n      service.info('')\n      service.warn('')\n      service.error('')\n      expect(traceSpy).toHaveBeenCalled()\n      expect(debugSpy).toHaveBeenCalled()\n      expect(infoSpy).toHaveBeenCalled()\n      expect(warnSpy).toHaveBeenCalled()\n      expect(errorSpy).toHaveBeenCalled()\n    }))\n  })\n})\n"
  },
  {
    "path": "src/client/app/shared/services/logging.service.ts",
    "content": "import { Inject, Injectable, InjectionToken } from '@angular/core'\nimport { createLogger, LoggerOptions } from '@expo/bunyan'\n\nexport interface ILoggingService {\n  trace(msg: string, errObj?: { readonly [key: string]: any }, extendedObj?: { readonly [key: string]: any }): void\n  debug(msg: string, errObj?: { readonly [key: string]: any }, extendedObj?: { readonly [key: string]: any }): void\n  info(msg: string, errObj?: { readonly [key: string]: any }, extendedObj?: { readonly [key: string]: any }): void\n  warn(msg: string, errObj?: { readonly [key: string]: any }, extendedObj?: { readonly [key: string]: any }): void\n  error(msg: string, errObj?: { readonly [key: string]: any }, extendedObj?: { readonly [key: string]: any }): void\n}\n\nexport const LOGGER_CONFIG = new InjectionToken<LoggerOptions>('app.logger.config')\n\n@Injectable()\nexport class LoggingService implements ILoggingService {\n  private readonly logger = createLogger(this.loggerConfig)\n\n  constructor(@Inject(LOGGER_CONFIG) private loggerConfig: LoggerOptions) { }\n\n  trace(msg: string, obj: { readonly [key: string]: any } = {}, extendedObj: { readonly [key: string]: any } = {}): void {\n    this.logger.trace({ trace: { msg, ...obj }, ...extendedObj }, msg)\n  }\n\n  debug(msg: string, obj: { readonly [key: string]: any } = {}, extendedObj: { readonly [key: string]: any } = {}): void {\n    this.logger.debug({ debug: { msg, ...obj }, ...extendedObj }, msg)\n  }\n\n  info(msg: string, obj: { readonly [key: string]: any } = {}, extendedObj: { readonly [key: string]: any } = {}): void {\n    this.logger.info({ info: { msg, ...obj }, ...extendedObj }, msg)\n  }\n\n  warn(msg: string, obj: { readonly [key: string]: any } = {}, extendedObj: { readonly [key: string]: any } = {}): void {\n    this.logger.warn({ warn: { msg, ...obj }, ...extendedObj }, msg)\n  }\n\n  error(msg: string, obj: { readonly [key: string]: any } = {}, extendedObj: { readonly [key: string]: any } = {}): void {\n    this.logger.error({ err: { msg, ...obj }, ...extendedObj }, msg)\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/minifier.service.ts",
    "content": "import { Injectable } from '@angular/core'\n\nexport interface IMinifierService {\n  css(css: string): string\n}\n\n@Injectable()\nexport class MinifierService implements IMinifierService {\n  css(css: string): string {\n    return css\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/platform.service.spec.ts",
    "content": "import { IPlatformService, PlatformService } from './platform.service'\nimport { async, TestBed } from '@angular/core/testing'\nimport { PLATFORM_ID } from '@angular/core'\n\ndescribe(PlatformService.name, () => {\n  let service: IPlatformService\n\n  describe(`${PlatformService.name}.browser`, () => {\n    beforeEach(async(() => {\n      TestBed.configureTestingModule({\n        providers: [\n          PlatformService,\n          { provide: PLATFORM_ID, useValue: 'browser' }\n        ]\n      })\n    }))\n\n    beforeEach(async(() => {\n      service = TestBed.get(PlatformService)\n    }))\n\n    afterEach(async(() => {\n      TestBed.resetTestingModule()\n    }))\n\n    it('should construct', async(() => {\n      expect(service).not.toBeNull()\n    }))\n\n    it('should be browser', async(() => {\n      expect(service.isBrowser).toEqual(true)\n      expect(service.isServer).toEqual(false)\n    }))\n  })\n\n  describe(`${PlatformService.name}.server`, () => {\n    beforeEach(async(() => {\n      TestBed.configureTestingModule({\n        providers: [\n          PlatformService,\n          { provide: PLATFORM_ID, useValue: 'server' }\n        ]\n      })\n    }))\n\n    beforeEach(async(() => {\n      service = TestBed.get(PlatformService)\n    }))\n\n    afterEach(async(() => {\n      TestBed.resetTestingModule()\n    }))\n\n    it('should be server', async(() => {\n      expect(service.isServer).toEqual(true)\n      expect(service.isBrowser).toEqual(false)\n    }))\n  })\n})\n"
  },
  {
    "path": "src/client/app/shared/services/platform.service.ts",
    "content": "import { Inject, Injectable, PLATFORM_ID } from '@angular/core'\nimport { isPlatformBrowser, isPlatformServer } from '@angular/common'\n\nexport interface IPlatformService {\n  readonly isBrowser: boolean\n  readonly isServer: boolean\n}\n\n@Injectable()\nexport class PlatformService implements IPlatformService {\n  constructor( @Inject(PLATFORM_ID) private platformId: any) { }\n\n  public get isBrowser(): boolean {\n    return isPlatformBrowser(this.platformId)\n  }\n\n  public get isServer(): boolean {\n    return isPlatformServer(this.platformId)\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/seo.service.ts",
    "content": "import { Injectable } from '@angular/core'\nimport { Meta, Title } from '@angular/platform-browser'\n\nexport interface SEONode {\n  readonly title?: string\n  readonly description?: string\n  readonly img?: SEOImage\n  readonly type?: string\n  readonly url?: string\n  readonly locale?: string\n  readonly facebookAppId?: string\n  readonly tags?: ReadonlyArray<string>\n}\n\nexport interface SEOImage {\n  readonly url?: string\n  readonly alt?: string\n  readonly type?: string\n  readonly height?: number\n  readonly width?: number\n}\n\n@Injectable()\nexport class SEOService {\n  constructor(private title: Title, private meta: Meta) { }\n\n  updateNode(node: SEONode) {\n    if (node.title) this.updateTitle(node.title)\n    if (node.description) this.updateDescription(node.description)\n    if (node.img) this.updateImg(node.img)\n    if (node.title) this.updateType(node.type)\n    if (node.url) this.updateUrl(node.url)\n    if (node.title) this.updateLocale(node.locale)\n    if (node.facebookAppId) this.updateFbAppId(node.facebookAppId)\n    if (node.tags) this.updateTags(node.tags)\n  }\n\n  updateTitle(title: string) {\n    this.title.setTitle(title)\n    this.meta.updateTag(this.createOgTag('title', title))\n  }\n\n  updateDescription(desc: string) {\n    this.meta.updateTag({ name: 'description', content: desc })\n    this.meta.updateTag(this.createOgTag('description', desc))\n  }\n\n  updateFbAppId(id: string) {\n    this.meta.updateTag({ property: 'fb:app_id', content: id })\n  }\n\n  updateImg(img: SEOImage) {\n    if (img.url) this.meta.updateTag(this.createOgTag('image', img.url, 'url'))\n    if (img.width) this.meta.updateTag(this.createOgTag('image', img.width.toString(), 'width'))\n    if (img.height) this.meta.updateTag(this.createOgTag('image', img.height.toString(), 'height'))\n    if (img.type) this.meta.updateTag(this.createOgTag('image', img.type, 'type'))\n    if (img.alt) this.meta.updateTag(this.createOgTag('image', img.alt, 'alt'))\n  }\n\n  updateType(type = 'website') {\n    this.meta.updateTag(this.createOgTag('type', type))\n  }\n\n  updateLocale(locale = 'en_US') {\n    this.meta.updateTag(this.createOgTag('locale', locale))\n  }\n\n  updateUrl(url: string) {\n    this.meta.updateTag(this.createOgTag('url', url))\n  }\n\n  updateTags(tags: ReadonlyArray<string>) {\n    tags.forEach(tag => this.meta.removeTag('property=\"og:article:tag\"'))\n    tags.forEach(tag => this.meta.addTag(this.createOgTag('article', tag, 'tag')))\n  }\n\n  createOgTag(property: string, content: string, property2?: string) {\n    return {\n      property: property2 ? `og:${property}:${property2}` : `og:${property}`,\n      content\n    }\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/server-response.service.spec.ts",
    "content": "import { IServerResponseService, ServerResponseService } from './server-response.service'\nimport { async, TestBed } from '@angular/core/testing'\nimport { RESPONSE } from '@nguniversal/express-engine/tokens'\n\ndescribe(ServerResponseService.name, () => {\n  let service: IServerResponseService\n  let _RESPONSE: MockResponse\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      providers: [\n        ServerResponseService,\n        { provide: RESPONSE, useValue: new MockResponse() }\n      ]\n    })\n  }))\n\n  beforeEach(() => {\n    service = TestBed.get(ServerResponseService)\n    _RESPONSE = TestBed.get(RESPONSE)\n  })\n\n  it('should construct', async(() => {\n    expect(service).toBeTruthy()\n  }))\n\n  it('should set header', async(() => {\n    service.setHeader('test', 'awesome value')\n    expect(service.getHeader('test')).toBe('awesome value')\n  }))\n\n  it('should set multiple headers', async(() => {\n    service.setHeaders({ thing: '1', thingy: '3'})\n    expect(service.getHeader('thing')).toBe('1')\n    expect(service.getHeader('thingy')).toBe('3')\n  }))\n\n  it('should append headers', async(() => {\n    expect.assertions(2)\n    service.setHeader('test', 'awesome value')\n    expect(service.getHeader('test')).toBe('awesome value')\n    service.appendHeader('test', 'another awesome value')\n    expect(service.getHeader('test')).toBe('awesome value,another awesome value')\n  }))\n\n  it('should append headers safely with undefined initial value', async(() => {\n    service.appendHeader('test', 'awesome value')\n    expect(service.getHeader('test')).toBe('awesome value')\n  }))\n\n  it('should append headers without duplicates values', async(() => {\n    expect.assertions(1)\n    service.appendHeader('test', 'awesome value')\n    service.appendHeader('test', 'awesome value')\n    service.appendHeader('test', 'awesome value')\n    expect(service.getHeader('test')).toBe('awesome value')\n  }))\n\n  it('should append headers using custom delimiter', async(() => {\n    expect.assertions(2)\n    service.setHeader('test', 'awesome value')\n    expect(service.getHeader('test')).toBe('awesome value')\n    service.appendHeader('test', 'another awesome value', '-')\n    expect(service.getHeader('test')).toBe('awesome value-another awesome value')\n  }))\n\n  it('should set standard not-found response', async(() => {\n    expect.assertions(1)\n    _RESPONSE.statusCode = 0\n    service.setNotFound()\n    expect(_RESPONSE.statusCode).toEqual(404)\n  }))\n\n  it('should set standard error response', async(() => {\n    expect.assertions(2)\n    _RESPONSE.statusCode = 0\n    service.setError()\n    expect(_RESPONSE.statusCode).toEqual(500)\n    expect(_RESPONSE.statusMessage).toEqual('internal server error')\n  }))\n\n  it('should set status', async(() => {\n    expect.assertions(2)\n    _RESPONSE.statusCode = 0\n    _RESPONSE.statusMessage = ''\n    service.setStatus(304, 'cool')\n    expect(_RESPONSE.statusCode).toEqual(304)\n    expect(_RESPONSE.statusMessage).toEqual('cool')\n  }))\n})\n\nclass MockResponse {\n  store: any = {}\n  statusCode: number\n  statusMessage: string\n\n  header(key: string, value: string) {\n    this.store[key] = value\n  }\n\n  getHeader(key: string) {\n    return this.store[key]\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/server-response.service.ts",
    "content": "import { RESPONSE } from '@nguniversal/express-engine/tokens'\nimport { Inject, Injectable, Optional } from '@angular/core'\nimport { Response } from 'express'\n// tslint:disable-next-line:no-require-imports\nimport ms = require('ms')\n\nexport interface IServerResponseService {\n  getHeader(key: string): string\n  setHeader(key: string, value: string): this\n  setHeaders(dictionary: { [key: string]: string }): this\n  appendHeader(key: string, value: string, delimiter?: string): this\n  setStatus(code: number, message?: string): this\n  setNotFound(message?: string): this\n  setError(message?: string): this\n}\n\n@Injectable()\nexport class ServerResponseService implements IServerResponseService {\n  private response: Response\n\n  constructor( @Optional() @Inject(RESPONSE) response: any) {\n    this.response = response\n  }\n\n  getHeader(key: string): string {\n    return this.response.getHeader(key) as string\n  }\n\n  setHeader(key: string, value: string): this {\n    if (this.response)\n      this.response.header(key, value)\n    return this\n  }\n\n  appendHeader(key: string, value: string, delimiter = ','): this {\n    if (this.response) {\n      const current = this.getHeader(key)\n      if (!current) return this.setHeader(key, value)\n\n      const newValue = [...current.split(delimiter), value]\n        .filter((el, i, a) => i === a.indexOf(el))\n        .join(delimiter)\n\n      this.response.header(key, newValue)\n    }\n    return this\n  }\n\n  setHeaders(dictionary: { [key: string]: string }): this {\n    if (this.response)\n      Object.keys(dictionary).forEach(key => this.setHeader(key, dictionary[key]))\n    return this\n  }\n\n  setStatus(code: number, message?: string): this {\n    if (this.response) {\n      this.response.statusCode = code\n      if (message)\n        this.response.statusMessage = message\n    }\n    return this\n  }\n\n  setNotFound(message = 'not found'): this {\n    if (this.response) {\n      this.response.statusCode = 404\n      this.response.statusMessage = message\n    }\n    return this\n  }\n\n  setUnauthorized(message = 'Unauthorized'): this {\n    if (this.response) {\n      this.response.statusCode = 401\n      this.response.statusMessage = message\n    }\n    return this\n  }\n\n  setCachePrivate(): this {\n    if (this.response) {\n      this.setCache('private')\n    }\n    return this\n  }\n\n  setCacheNone(): this {\n    if (this.response) {\n      this.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate')\n      this.setHeader('Pragma', 'no-cache')\n    }\n    return this\n  }\n\n  setCache(directive: HttpCacheDirective, maxAge?: string, smaxAge?: string): this {\n    if (this.response) {\n      // tslint:disable-next-line:max-line-length\n      if (smaxAge) {\n        this.setHeader('Cache-Control', `${directive}, max-age=${maxAge ? ms(maxAge) / 1000 : 0}, s-maxage=${ms(smaxAge) / 1000}`)\n      } else {\n        this.setHeader('Cache-Control', `${directive}, max-age=${maxAge ? ms(maxAge) / 1000 : 0}`)\n      }\n\n      this.setHeader('Expires', maxAge ? new Date(Date.now() + ms(maxAge)).toUTCString() : new Date(Date.now()).toUTCString())\n    }\n    return this\n  }\n\n  setError(message = 'internal server error'): this {\n    if (this.response) {\n      this.response.statusCode = 500\n      this.response.statusMessage = message\n    }\n    return this\n  }\n}\n\nexport type HttpCacheDirective = 'public' | 'private' | 'no-store' | 'no-cache' | 'must-revalidate' | 'no-transform' | 'proxy-revalidate'\n"
  },
  {
    "path": "src/client/app/shared/services/setting.service.spec.ts",
    "content": "import { HttpClientTestingModule } from '@angular/common/http/testing'\nimport { IPlatformService, PlatformService } from './platform.service'\nimport { SettingService } from './setting.service'\nimport { async, TestBed } from '@angular/core/testing'\nimport { FirebaseDatabaseService } from './firebase-database.service'\nimport { of } from 'rxjs/observable/of'\nimport '../../../operators'\n\ndescribe(SettingService.name, () => {\n  let service: SettingService\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [HttpClientTestingModule],\n      providers: [\n        SettingService,\n        { provide: PlatformService, useValue: new MockPlatformService() },\n        { provide: FirebaseDatabaseService, useValue: new MockDb() }\n      ]\n    })\n  }))\n\n  beforeEach(() => {\n    service = TestBed.get(SettingService)\n  })\n\n  it('should construct', async(() => {\n    expect(service).toBeTruthy()\n  }))\n})\n\nclass MockPlatformService implements IPlatformService {\n  public isBrowser = true\n  public isServer = false\n}\n\nclass MockDb {\n  get() {\n    return of('test')\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/setting.service.ts",
    "content": "import { Observable } from 'rxjs/Observable'\nimport { Injectable } from '@angular/core'\nimport { ISetting } from '../../../../server/api/services/setting.service'\nimport { FirebaseDatabaseService } from './firebase-database.service'\n\nexport interface ISettingService {\n  settings$: Observable<ISetting>\n  pluck(key: string): Observable<string>\n}\n\n@Injectable()\nexport class SettingService implements ISettingService {\n  public initialSettings: ISetting\n  public settings$ = this.db\n    .get<ISetting>('site-settings')\n    .map(settings => {\n      return {\n        injections: [],\n        ...settings\n      } as ISetting\n    })\n    .shareReplay()\n\n  public pluck(key: string) {\n    return this.settings$.map(dict => key.split('.')\n      .reduce((o, k) => (o || {})[k], dict as any))\n  }\n\n  constructor(private db: FirebaseDatabaseService) {\n    this.settings$.take(1).subscribe(set => this.initialSettings = set)\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/services/web-socket.service.ts",
    "content": "import { EnvironmentService } from './environment.service'\nimport { Subject } from 'rxjs/Subject'\nimport { PlatformService } from './platform.service'\nimport { Injectable } from '@angular/core'\nimport { WebSocketSubject, WebSocketSubjectConfig } from 'rxjs/observable/dom/WebSocketSubject'\n\n@Injectable()\nexport class WebSocketService {\n  private readonly source = this.ps.isBrowser && typeof window !== 'undefined' && (window as any).WebSocket &&\n    this.es.config.endpoints && this.es.config.endpoints.websocketServer\n    ? new WebSocketSubject(\n      {\n        url: this.es.config.endpoints.websocketServer\n      } as WebSocketSubjectConfig\n    )\n    : new Subject()\n\n  public readonly messageBus$ = this.source.asObservable()\n\n  constructor(private ps: PlatformService, private es: EnvironmentService) { }\n\n  send(obj: Object) {\n    this.source.next(JSON.stringify(obj))\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/shared.module.ts",
    "content": "import { LOGGER_CONFIG, LoggingService } from './services/logging.service'\nimport { SettingService } from './services/setting.service'\nimport { RouterModule } from '@angular/router'\nimport { NavbarComponent } from './navbar/navbar.component'\nimport { LoginCardComponent } from './login-card/login-card.component'\nimport { CookieService } from './services/cookie.service'\nimport { CommonModule } from '@angular/common'\nimport { PlatformService } from './services/platform.service'\nimport { ModuleWithProviders, NgModule } from '@angular/core'\nimport { WebSocketService } from './services/web-socket.service'\nimport { EnvironmentService } from './services/environment.service'\nimport { COOKIE_HOST_WHITELIST } from './services/http-cookie-interceptor.service'\nimport { ENV_CONFIG } from '../app.config'\nimport { NavbarService } from './navbar/navbar.service'\nimport { Angulartics2GoogleAnalytics } from 'angulartics2/ga'\nimport { MaterialModule } from './material.module'\nimport { ClickOutsideDirective } from './directives/click-outside.directive'\nimport { SocialButtonDirective } from './directives/social-button.directive'\nimport { MarkdownToHtmlModule } from 'markdown-to-html-pipe'\nimport { FormsModule, ReactiveFormsModule } from '@angular/forms'\nimport { FirebaseDatabaseService } from './services/firebase-database.service'\nimport { InjectionService } from './services/injection.service'\nimport { MinifierService } from './services/minifier.service'\nimport { QuillEditorComponent } from './quill-editor/quill-editor.component'\nimport { ServerResponseService } from './services/server-response.service'\nimport { SEOService } from './services/seo.service'\nimport { HtmlOutletDirective } from './directives/html-outlet.directive'\nimport { KeysPipe } from './pipes/keys.pipe'\nimport { KeyValuePipe } from './pipes/key-value.pipe'\nimport { SanitizeHtmlPipe } from './pipes/sanitize-html.pipe'\nimport { ModalConfirmationComponent } from './modal-confirmation/modal-confirmation.component'\nimport { InjectionFormComponent } from './injection-form/injection-form.component'\nimport { StyleInjectionFormComponent } from './style-injection-form/style-injection-form.component'\nimport { KeyValueFormComponent } from './key-value-form/key-value-form.component'\nimport { CacheFormComponent } from './cache-form/cache-form.component'\nimport { FlexLayoutModule } from '@angular/flex-layout'\nimport { LoginGuard } from './services/guard-login.service'\nimport { AdminGuard } from './services/guard-admin.service'\nimport * as config from '../../../config.json'\n\nexport function fuseBoxConfigFactory() {\n  return config\n}\n\nexport function loggerConfigFactory(ps: PlatformService, gooogleAnalytics: Angulartics2GoogleAnalytics) {\n  return {\n    name: 'Angular Universal App',\n    type: 'app',\n    streams: [{\n      level: 'error',\n      stream: {\n        write: (err: any) => {\n          if (ps.isBrowser) {\n            console.error('Application error', err)\n            if ((window as any).ga) {\n              gooogleAnalytics.exceptionTrack(err)\n            } else {\n              console.log('(Application error was not logged to analytics provider)')\n            }\n          } else {\n            console.error(err)\n          }\n        }\n      },\n      type: 'raw'\n    }]\n  }\n}\n\n@NgModule({\n  imports: [\n    CommonModule,\n    RouterModule,\n    MaterialModule,\n    FormsModule,\n    ReactiveFormsModule,\n    MarkdownToHtmlModule,\n    FlexLayoutModule\n  ],\n  exports: [\n    CommonModule,\n    RouterModule,\n    NavbarComponent,\n    MaterialModule,\n    ClickOutsideDirective,\n    SocialButtonDirective,\n    LoginCardComponent,\n    FormsModule,\n    ReactiveFormsModule,\n    MarkdownToHtmlModule,\n    QuillEditorComponent,\n    HtmlOutletDirective,\n    KeysPipe,\n    KeyValuePipe,\n    SanitizeHtmlPipe,\n    ModalConfirmationComponent,\n    InjectionFormComponent,\n    StyleInjectionFormComponent,\n    KeyValueFormComponent,\n    CacheFormComponent,\n    FlexLayoutModule\n  ],\n  entryComponents: [\n    ModalConfirmationComponent\n  ],\n  declarations: [\n    NavbarComponent,\n    ClickOutsideDirective,\n    SocialButtonDirective,\n    LoginCardComponent,\n    QuillEditorComponent,\n    HtmlOutletDirective,\n    KeysPipe,\n    KeyValuePipe,\n    SanitizeHtmlPipe,\n    ModalConfirmationComponent,\n    InjectionFormComponent,\n    StyleInjectionFormComponent,\n    KeyValueFormComponent,\n    CacheFormComponent\n  ],\n  providers: [\n    { provide: ENV_CONFIG, useFactory: fuseBoxConfigFactory },\n    { provide: COOKIE_HOST_WHITELIST, useValue: ['angular.patrickmichalina.com'] },\n    {\n      provide: LOGGER_CONFIG,\n      useFactory: loggerConfigFactory,\n      deps: [PlatformService, Angulartics2GoogleAnalytics]\n    },\n    PlatformService,\n    CookieService,\n    EnvironmentService,\n    NavbarService,\n    LoggingService,\n    SettingService,\n    WebSocketService,\n    FirebaseDatabaseService,\n    InjectionService,\n    MinifierService,\n    ServerResponseService,\n    SEOService,\n    LoginGuard,\n    AdminGuard\n  ]\n})\nexport class SharedModule {\n  static forRoot(): ModuleWithProviders {\n    return {\n      ngModule: SharedModule\n    }\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/style-injection-form/style-injection-form.component.html",
    "content": "<form [formGroup]=\"form\" novalidate>\n  <div>\n    <label for=\"inHead\">In Head?</label>\n    <input id=\"inHead\" type=\"checkbox\" formControlName=\"inHead\">\n  </div>\n  <div>\n    <label for=\"inline\">Inline Style?</label>\n    <input id=\"inline\" type=\"checkbox\" (change)=\"inline\" #inline [formControl]=\"isInline\">\n  </div>\n  <mat-form-field *ngIf=\"inline.checked\">\n    <textarea matInput placeholder=\"Inline CSS\" formControlName=\"value\"></textarea>\n  </mat-form-field>\n  <mat-form-field *ngIf=\"!inline.checked\">\n    <input matInput placeholder=\"CSS Href\" formControlName=\"linkHref\">\n  </mat-form-field>\n  <!-- <div *ngIf=\"!inline.checked\"> -->\n\n  <!-- <label for=\"attributes\">Attributes</label>\n    <pm-key-value-form id=\"attributes\" [keyVals]=\"form.value.attributes\"></pm-key-value-form> -->\n  <!-- </div> -->\n\n  <!-- <div>\n      <label for=\"hasAttributes\">Has Attributes?</label>\n      <input id=\"hasAttributes\" type=\"checkbox\" (change)=\"hasAttributes\" #hasAttributes>\n      <mat-form-field *ngIf=\"hasAttributes.checked\">\n        <input matInput placeholder=\"Inline Value\" type=\"text\" formControlName=\"value\">\n      </mat-form-field>\n    </div> -->\n\n  <!-- \n      TODO: replace above once material fixes bug\n      TODO: https://github.com/angular/material2/pull/7882\n      <mat-checkbox formControlName=\"inHead\">\n      In Head?\n    </mat-checkbox> -->\n</form>"
  },
  {
    "path": "src/client/app/shared/style-injection-form/style-injection-form.component.scss",
    "content": ":host {\n  display: block;\n}"
  },
  {
    "path": "src/client/app/shared/style-injection-form/style-injection-form.component.ts",
    "content": "import { FormControl } from '@angular/forms'\nimport { ChangeDetectionStrategy, Component } from '@angular/core'\n// import { InjectionFormComponent } from '../injection-form/injection-form.component'\n\n@Component({\n  selector: 'pm-style-injection-form',\n  templateUrl: './style-injection-form.component.html',\n  styleUrls: ['./style-injection-form.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class StyleInjectionFormComponent {\n  isInline = new FormControl(true)\n\n  constructor() {\n    // super()\n    // this.form.addControl('linkHref', new FormControl('', []))\n    // this.form.controls['element'].setValue('link')\n    // this.isInline.valueChanges.subscribe(inline => {\n    //   if (inline) {\n    //     this.form.controls['element'].setValue('style')\n    //     this.form.controls['linkHref'].setValidators([])\n    //     this.form.controls['linkHref'].setValue(undefined)\n    //     this.form.controls['attributes'].setValue(undefined)\n    //   } else {\n    //     this.form.controls['linkHref'].setValidators([Validators.required])\n    //     this.form.controls['element'].setValue('link')\n    //     this.form.controls['attributes'].setValue({\n    //       ...this.form.controls['attributes'].value,\n    //       rel: 'stylesheet',\n    //       type: 'text/css',\n    //       href: ''\n    //     })\n    //   }\n    // })\n  }\n}\n"
  },
  {
    "path": "src/client/app/shared/web-app-installer/web-app-installer.component.html",
    "content": "<div class=\"text-right browser-preview f12\"></div>\n<div class=\"logo-name-container\">\n</div>\n<div class=\"homescreen-text\">To install tap\n  <div class=\"icon-addToHome sprite-mobile\"></div> and choose\n  <br />\n</div>\n<div class=\"icon-homePointer sprite-mobile\"></div>"
  },
  {
    "path": "src/client/app/shared/web-app-installer/web-app-installer.component.scss",
    "content": ":host {\n  display: none;\n  width: 100vw;\n  height: 100vh;\n  position: fixed;\n  top: 0;\n  text-align: center;\n  color: #fff;\n  padding: 10vh 5vw;\n  box-sizing: border-box;\n  background-color: #000;\n  z-index: 9999;\n  background-color: rgba(0, 0, 0, 0.3);\n  .blur {\n    filter: blur(10px);\n    -webkit-filter: blur(10px);\n    transition: 0.2s filter linear;\n    -webkit-transition: 0.2s -webkit-filter linear;\n  }\n  .browser-preview {\n    margin: -45px 0 40px;\n    text-decoration: underline;\n    opacity: 0.8;\n    text-align: right;\n  }\n  .logo-name-container {\n    background-image: url('../assets/images/login/cab.svg');\n    background-repeat: no-repeat;\n    background-position: center 0;\n    padding-top: 110px;\n    margin: 0 45px;\n    background-size: 200px;\n    font-size: 24px;\n    margin-top: 15vh;\n  }\n  .homescreen-text {\n    padding-top: 15vh;\n    line-height: 1.5;\n    font-size: 18px;\n  }\n  .icon-addToHome {\n    vertical-align: text-bottom;\n    width: 35px;\n    height: 35px;\n    display: inline-block;\n    background: url('../assets/images/mobile-sprite.png') no-repeat top left;\n    background-size: cover;\n  }\n  .icon-homePointer {\n    margin-top: 5vh;\n    background: url('../assets/images/mobile-sprite.png') no-repeat top left;\n    background-position: center -40px;\n    width: 100%;\n    height: 50px;\n    background-size: 40px;\n    -webkit-animation-duration: 0.5s;\n    animation-duration: 0.5s;\n    -webkit-animation-name: topToBottom;\n    animation-name: topToBottom;\n    -webkit-animation-iteration-count: infinite;\n    animation-iteration-count: infinite;\n    -webkit-animation-direction: alternate;\n    animation-direction: alternate;\n  }\n  @keyframes topToBottom {\n    from {\n      transform: translate(0, 0);\n    }\n    to {\n      transform: translate(0, 20px);\n    }\n  }\n}"
  },
  {
    "path": "src/client/app/shared/web-app-installer/web-app-installer.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-web-app-installer',\n  templateUrl: './web-app-installer.component.html',\n  styleUrls: ['./web-app-installer.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class WebAppInstallerComponent {\n}\n"
  },
  {
    "path": "src/client/app/shared/web-app-installer/web-app-installer.module.ts",
    "content": "import { NgModule } from '@angular/core'\nimport { WebAppInstallerService } from './web-app-installer.service'\nimport { WebAppInstallerComponent } from './web-app-installer.component'\n\n@NgModule({\n  declarations: [WebAppInstallerComponent],\n  exports: [WebAppInstallerComponent],\n  providers: [WebAppInstallerService]\n})\nexport class WebAppInstallerlModule { }\n"
  },
  {
    "path": "src/client/app/shared/web-app-installer/web-app-installer.service.ts",
    "content": "import { Inject, Injectable, PLATFORM_ID } from '@angular/core'\nimport { isPlatformBrowser } from '@angular/common'\n\n@Injectable()\nexport class WebAppInstallerService {\n  get isIosWebApp() {\n    return isPlatformBrowser(this.platformId) &&\n      'standalone' in window.navigator &&\n      (window.navigator as any).standalone !== true\n  }\n\n  constructor(@Inject(PLATFORM_ID) private platformId: any) { }\n}\n"
  },
  {
    "path": "src/client/app/signup/__snapshots__/signup.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`SignupComponent should match snapshot 1`] = `\n<pm-signup>\n  Your <strong>\n    signup\n  </strong> form goes here!\n</pm-signup>\n`;\n"
  },
  {
    "path": "src/client/app/signup/signup-routing.module.ts",
    "content": "import { SignupComponent } from './signup.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: SignupComponent,\n        canActivate: [MetaGuard],\n        data: {\n          meta: {\n            title: 'i18n.signup.title',\n            description: 'i18n.signup.description'\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class SignupRoutingModule { }\n"
  },
  {
    "path": "src/client/app/signup/signup.component.e2e-spec.ts",
    "content": "import { baseUrl, browser } from '../../../../tools/test/jest.e2e-setup'\n\ndescribe('Signup Page', () => {\n  it('should have title', async () => {\n    expect.assertions(1)\n\n    const page = browser.goto(`${baseUrl}/signup`)\n\n    const text = await page.evaluate(() => document.title)\n\n    expect(text).toContain('Signup - Fusebox Angular Universal Starter')\n  })\n})\n"
  },
  {
    "path": "src/client/app/signup/signup.component.html",
    "content": "Your <strong>signup</strong> form goes here!"
  },
  {
    "path": "src/client/app/signup/signup.component.scss",
    "content": ""
  },
  {
    "path": "src/client/app/signup/signup.component.spec.ts",
    "content": "import { SignupComponent } from './signup.component'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { SignupModule } from './signup.module'\nimport { AppTestingModule } from '../../../testing/app-testing.module'\n\ndescribe(SignupComponent.name, () => {\n  let fixture: ComponentFixture<SignupComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [AppTestingModule.forRoot(), SignupModule],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(SignupComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should match snapshot', async(() => {\n    fixture.detectChanges()\n    expect(fixture).toMatchSnapshot()\n  }))\n\n  it('should compile', async(() => {\n    fixture.detectChanges()\n    expect(fixture.nativeElement).toBeDefined()\n  }))\n})\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-signup></pm-signup>'\n})\nclass TestComponent {}\n"
  },
  {
    "path": "src/client/app/signup/signup.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-signup',\n  templateUrl: './signup.component.html',\n  styleUrls: ['./signup.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SignupComponent { }\n"
  },
  {
    "path": "src/client/app/signup/signup.module.ts",
    "content": "import { SignupComponent } from './signup.component'\nimport { SignupRoutingModule } from './signup-routing.module'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [SignupRoutingModule, SharedModule],\n  declarations: [SignupComponent],\n  exports: [SignupComponent]\n})\nexport class SignupModule { }\n"
  },
  {
    "path": "src/client/app/unauthorized/__snapshots__/unauthorized.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`UnauthorizedComponent should compile 1`] = `\n<pm-unauthorized>\n  <div\n    fxlayout=\"column\"\n    fxlayoutalign=\"center center\"\n  >\n    <h1>\n      Unauthorized\n    </h1>\n    <pm-login-card>\n      \n      \n    </pm-login-card>\n  </div>\n</pm-unauthorized>\n`;\n"
  },
  {
    "path": "src/client/app/unauthorized/unauthorized-routing.module.ts",
    "content": "import { UnauthorizedComponent } from './unauthorized.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: UnauthorizedComponent,\n        canActivate: [MetaGuard],\n        data: {\n          meta: {\n            title: 'i18n.unauthorized.title',\n            description: 'i18n.unauthorized.description'\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class UnauthorizedRoutingModule { }\n"
  },
  {
    "path": "src/client/app/unauthorized/unauthorized.component.html",
    "content": "<div fxLayout=\"column\" fxLayoutAlign=\"center center\">\n  <h1>Unauthorized</h1>\n  <pm-login-card></pm-login-card>\n</div>"
  },
  {
    "path": "src/client/app/unauthorized/unauthorized.component.scss",
    "content": ""
  },
  {
    "path": "src/client/app/unauthorized/unauthorized.component.spec.ts",
    "content": "import { AuthService } from './../shared/services/auth.service'\nimport { AngularFireAuthModule } from 'angularfire2/auth'\nimport { UnauthorizedComponent } from './unauthorized.component'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { UnauthorizedModule } from './unauthorized.module'\nimport { AngularFireModule } from 'angularfire2'\nimport { AppTestingModule } from '../../../testing/app-testing.module'\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-unauthorized></pm-unauthorized>'\n})\nclass TestComponent { }\n\ndescribe(UnauthorizedComponent.name, () => {\n  let fixture: ComponentFixture<UnauthorizedComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [AppTestingModule.forRoot(), UnauthorizedModule, AngularFireAuthModule, AngularFireModule.initializeApp({\n        'apiKey': '1',\n        'authDomain': 'app.firebaseapp.com',\n        'databaseURL': 'https://app.firebaseio.com',\n        'projectId': 'firebase-app',\n        'messagingSenderId': '1'\n      })],\n      declarations: [TestComponent],\n      providers: [\n        { provide: AuthService, useValue: {} }\n      ]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(UnauthorizedComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n    expect(fixture).toMatchSnapshot()\n  }))\n})\n"
  },
  {
    "path": "src/client/app/unauthorized/unauthorized.component.ts",
    "content": "import { ChangeDetectionStrategy, Component } from '@angular/core'\n\n@Component({\n  selector: 'pm-unauthorized',\n  templateUrl: './unauthorized.component.html',\n  styleUrls: ['./unauthorized.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UnauthorizedComponent {\n}\n"
  },
  {
    "path": "src/client/app/unauthorized/unauthorized.module.ts",
    "content": "import { UnauthorizedRoutingModule } from './unauthorized-routing.module'\nimport { UnauthorizedComponent } from './unauthorized.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [UnauthorizedRoutingModule, SharedModule],\n  declarations: [UnauthorizedComponent],\n  exports: [UnauthorizedComponent]\n})\nexport class UnauthorizedModule { }\n"
  },
  {
    "path": "src/client/app/users/__snapshots__/users.component.spec.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`UsersComponent should compile 1`] = `\n<test-component>\n  <pm-users>\n    <div\n      fxflexfill=\"\"\n      fxlayout=\"column\"\n      style=\"height: 100%; margin: 0px; min-height: 100%; min-width: 100%; width: 100%;\"\n    >\n      <mat-nav-list\n        class=\"mat-nav-list\"\n        fxflex=\"\"\n        role=\"navigation\"\n      >\n        \n      </mat-nav-list>\n    </div>\n  </pm-users>\n</test-component>\n`;\n"
  },
  {
    "path": "src/client/app/users/users-routing.module.ts",
    "content": "import { UsersComponent } from './users.component'\nimport { NgModule } from '@angular/core'\nimport { RouterModule } from '@angular/router'\nimport { MetaGuard } from '@ngx-meta/core'\nimport { AdminGuard } from '../shared/services/guard-admin.service'\n\n@NgModule({\n  imports: [\n    RouterModule.forChild([\n      {\n        path: '',\n        component: UsersComponent,\n        canActivate: [MetaGuard, AdminGuard],\n        data: {\n          meta: {\n            title: 'i18n.users.title',\n            description: 'i18n.users.description'\n          },\n          response: {\n            cache: {\n              directive: 'private'\n            }\n          }\n        }\n      }\n    ])\n  ],\n  exports: [RouterModule]\n})\nexport class UsersRoutingModule { }\n"
  },
  {
    "path": "src/client/app/users/users.component.html",
    "content": "<div fxFlexFill fxLayout=\"column\">\n  <mat-nav-list fxFlex>\n    <mat-list-item *ngFor=\"let user of users$ | async; trackBy: trackByUsers\">\n      <img matListAvatar [src]=\"user.photo\">\n      <h4 mat-line>{{ user.displayName }}</h4>\n      <p mat-line> {{ user.email }} </p>\n    </mat-list-item>\n  </mat-nav-list>\n  <!-- <mat-paginator (page)=\"pageEvent($event)\" [length]=\"100\" [pageSize]=\"10\" [pageSizeOptions]=\"[5, 10, 25, 100]\"></mat-paginator> -->\n</div>"
  },
  {
    "path": "src/client/app/users/users.component.scss",
    "content": ":host {\n  mat-nav-list {\n    overflow-y: auto;\n  }\n}"
  },
  {
    "path": "src/client/app/users/users.component.spec.ts",
    "content": "import { UsersComponent } from './users.component'\nimport { async, ComponentFixture, TestBed } from '@angular/core/testing'\nimport { Component } from '@angular/core'\nimport { UsersModule } from './users.module'\nimport { AppTestingModule } from '../../../testing/app-testing.module'\n\n@Component({\n  selector: 'test-component',\n  template: '<pm-users></pm-users>'\n})\nclass TestComponent { }\n\ndescribe(UsersComponent.name, () => {\n  let fixture: ComponentFixture<TestComponent>\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [AppTestingModule.forRoot(), UsersModule],\n      declarations: [TestComponent]\n    }).compileComponents()\n  }))\n\n  beforeEach(async(() => {\n    fixture = TestBed.createComponent(TestComponent)\n  }))\n\n  afterEach(async(() => {\n    TestBed.resetTestingModule()\n  }))\n\n  it('should compile', async(() => {\n    expect(fixture.nativeElement).toBeDefined()\n    expect(fixture).toMatchSnapshot()\n  }))\n})\n"
  },
  {
    "path": "src/client/app/users/users.component.ts",
    "content": "import { Observable } from 'rxjs/Observable'\nimport { ChangeDetectionStrategy, Component } from '@angular/core'\nimport { FirebaseDatabaseService } from '../shared/services/firebase-database.service'\nimport { SettingService } from '../shared/services/setting.service'\n// import { PageEvent } from '@angular/material'\n\n@Component({\n  selector: 'pm-users',\n  templateUrl: './users.component.html',\n  styleUrls: ['./users.component.scss'],\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UsersComponent {\n\n  public usersFromDb$ = this.db\n    .getListKeyed('users', ref => ref.limitToFirst(5000)) // TODO: pagination\n\n  public users$ = Observable.combineLatest(this.usersFromDb$, this.ss.settings$,\n    (userInfo, settings) => {\n      return userInfo.map((user: any) => {\n        return {\n          ...user,\n          photo: user.photo || settings.assets.userAvatarImage\n        }\n      })\n    })\n\n  constructor(private db: FirebaseDatabaseService, private ss: SettingService) { }\n\n  // pageEvent(pageEvent: PageEvent) {\n  //   console.log(pageEvent)\n  // }\n\n  trackByUsers(index: number, item: any) {\n    return item.id\n  }\n}\n"
  },
  {
    "path": "src/client/app/users/users.module.ts",
    "content": "import { UsersRoutingModule } from './users-routing.module'\nimport { UsersComponent } from './users.component'\nimport { NgModule } from '@angular/core'\nimport { SharedModule } from '../shared/shared.module'\n\n@NgModule({\n  imports: [UsersRoutingModule, SharedModule],\n  declarations: [UsersComponent],\n  exports: [UsersComponent]\n})\nexport class UsersModule { }\n"
  },
  {
    "path": "src/client/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\"></html>"
  },
  {
    "path": "src/client/main-prod.ts",
    "content": "import './polyfills'\n\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic'\nimport { enableProdMode } from '@angular/core'\nimport { AppBrowserModule } from './app/app.browser.module'\n\nenableProdMode()\n\nplatformBrowserDynamic().bootstrapModule(AppBrowserModule)\n"
  },
  {
    "path": "src/client/main.aot-prod.ts",
    "content": "import './polyfills'\n\nimport { enableProdMode } from '@angular/core'\nimport { platformBrowser } from '@angular/platform-browser'\nimport { AppBrowserModuleNgFactory } from './.aot/src/client/app/app.browser.module.ngfactory'\n\nenableProdMode()\n\nplatformBrowser().bootstrapModuleFactory(AppBrowserModuleNgFactory)\n"
  },
  {
    "path": "src/client/main.aot.ts",
    "content": "import './polyfills'\n\nimport { platformBrowser } from '@angular/platform-browser'\nimport { AppBrowserModuleNgFactory } from './.aot/src/client/app/app.browser.module.ngfactory'\n\nplatformBrowser().bootstrapModuleFactory(AppBrowserModuleNgFactory)\n"
  },
  {
    "path": "src/client/main.ts",
    "content": "import './polyfills'\n\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic'\nimport { AppBrowserModule } from './app/app.browser.module'\n\nplatformBrowserDynamic().bootstrapModule(AppBrowserModule)\n"
  },
  {
    "path": "src/client/ngsw.json",
    "content": "{\n  \"index\": \"/\",\n  \"appData\": {\n    \"test\": true\n  },\n  \"assetGroups\": [\n    {\n      \"name\": \"appshell\",\n      \"resources\": {\n        \"files\": [\n          \"/**/*!(index).html\",\n          \"/**/*.js\",\n          \"/**/*.css\",\n          \"/assets/**/*\",\n          \"!/ngsw-worker.js\",\n          \"!/server.js\",\n          \"!/keystore.json\",\n          \"!/web/ping.html\"\n        ],\n        \"versionedFiles\": [],\n        \"urls\": [\n          \"/absolute/**\",\n          \"relative/*.txt\"\n        ]\n      }\n    }\n  ],\n  \"dataGroups\": [\n    {\n      \"name\": \"api\",\n      \"urls\": [\n        \"/api/**\"\n      ],\n      \"cacheConfig\": {\n        \"maxSize\": 100,\n        \"maxAge\": \"1d\",\n        \"timeout\": \"1m\",\n        \"strategy\": \"freshness\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/client/operators.ts",
    "content": "import 'rxjs/add/observable/of'\nimport 'rxjs/add/observable/throw'\nimport 'rxjs/add/observable/fromPromise'\nimport 'rxjs/add/observable/combineLatest'\nimport 'rxjs/add/operator/startWith'\nimport 'rxjs/add/operator/map'\nimport 'rxjs/add/operator/catch'\nimport 'rxjs/add/operator/switchMap'\nimport 'rxjs/add/operator/do'\nimport 'rxjs/add/operator/pluck'\nimport 'rxjs/add/operator/filter'\nimport 'rxjs/add/operator/share'\nimport 'rxjs/add/operator/shareReplay'\nimport 'rxjs/add/operator/toPromise'\nimport 'rxjs/add/operator/take'\nimport 'rxjs/add/operator/throttleTime'\nimport 'rxjs/add/operator/distinctUntilChanged'\nimport 'rxjs/add/operator/last'\nimport 'rxjs/add/operator/takeLast'\nimport 'rxjs/add/operator/debounceTime'\nimport 'rxjs/add/operator/finally'\nimport 'rxjs/add/operator/auditTime'\nimport 'rxjs/add/operator/takeUntil'\nimport 'rxjs/add/operator/skip'\n"
  },
  {
    "path": "src/client/polyfills.ts",
    "content": "// Fusebox\nif (process.env.NODE_ENV !== 'development') {\n  require = {} as any\n}\n\n// Required for Angular\nimport 'core-js/es7/reflect'\nimport 'zone.js/dist/zone'\n\n// IE9, IE10 and IE11 require all of the following polyfills.\n// import 'core-js/es6/symbol'\n// import 'core-js/es6/object'\n// import 'core-js/es6/function'\n// import 'core-js/es6/parse-int'\n// import 'core-js/es6/parse-float'\n// import 'core-js/es6/number'\n// import 'core-js/es6/math'\n// import 'core-js/es6/string'\n// import 'core-js/es6/date'\n// import 'core-js/es6/array'\n// import 'core-js/es6/regexp'\n// import 'core-js/es6/map'\n// import 'core-js/es6/weak-map'\n// import 'core-js/es6/set'\n\n// IE10 and IE11 requires the following to support `@angular/animation`.\n// import 'web-animations-js'\n\n// rxjs\nimport './operators'\n"
  },
  {
    "path": "src/client/styles/main.scss",
    "content": "$fa-font-path: '/assets/fonts/font-awesome';\n@import '../../../node_modules/font-awesome/scss/font-awesome';\n@import './node_modules/@angular/material/_theming.scss';\n@import './node_modules/quill/dist/quill.snow';\n@include mat-core();\n$primary: mat-palette($mat-blue, 700);\n$accent: mat-palette($mat-pink);\n$theme: mat-light-theme($primary, $accent);\n@include angular-material-theme($theme);\n.dark-theme {\n  $dark-p: mat-palette($mat-pink, 700);\n  $dark-a: mat-palette($mat-blue-grey);\n  $dark-t: mat-dark-theme($dark-p, $dark-a);\n  @include angular-material-theme($dark-t);\n}\n\nhtml {\n  font-family: Roboto, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif;\n}\n\nhtml,\nbody {\n  margin: 0;\n  display: flex;\n  flex: 1 0;\n  flex-direction: column;\n  height: 100%;\n}\n\n.avatar {\n  height: 40px;\n  width: 40px;\n  border-radius: 50%;\n  display: inline-block;\n}\n\n.card-float-container {\n  display: flex;\n  justify-content: center;\n  margin: 1em;\n  @media only screen and (max-width: 959px) {\n    display: block;\n    margin: 0;\n  }\n}\n\n// TODO remove in future\n.mat-card-header-text {\n  width: 100%;\n}\n\n// TODO remove in future\n.mat-form-field.mat-form-field {\n  width: auto;\n}\n\n.mat-raised-button .mat-button-wrapper>* {\n  vertical-align: inherit !important;\n}\n\n.mat-menu-content {\n  padding: 0 !important;\n}\n\n.vert-flex-fill {\n  display: flex;\n  flex-direction: column;\n  flex: 1;\n  height: 100%;\n}\n\n.ql-toolbar.ql-snow {\n  border: none !important;\n}\n\n.ql-container.ql-snow {\n  border: none;\n}"
  },
  {
    "path": "src/client/styles/material2-app-themes.scss",
    "content": ""
  },
  {
    "path": "src/server/api/api.spec.ts",
    "content": "import { testApi } from './test-helper'\n\ndescribe('API Server', () => {\n  it('should have swagger.json route', () => {\n    return testApi.get('/api-docs.json')\n      .expect(200)\n      .expect('Content-Type', /json/)\n  })\n\n  it('should have swagger docs route', () => {\n    return testApi.get('/api-docs/')\n      .expect(200)\n      .expect('Content-Type', /html/)\n  })\n})\n"
  },
  {
    "path": "src/server/api/controllers/index.ts",
    "content": "import { SettingsController } from './settings.controller'\n\nexport const controllers = [\n  SettingsController\n]\n"
  },
  {
    "path": "src/server/api/controllers/settings.controller.spec.ts",
    "content": "import { testApi } from '../test-helper'\nimport { SettingsController } from './settings.controller'\n\ndescribe(SettingsController.name, () => {\n  it('should get settings object', () => {\n    return testApi.get('/api/settings')\n      .expect(200)\n      .expect('Content-Type', 'application/json; charset=utf-8')\n  })\n})\n"
  },
  {
    "path": "src/server/api/controllers/settings.controller.ts",
    "content": "import { Get, JsonController } from 'routing-controllers'\nimport { SettingService } from '../services'\n\n@JsonController()\nexport class SettingsController {\n  constructor(private settingService: SettingService) { }\n\n  /**\n   * @swagger\n   * tags:\n   *   - name: settings\n   *     description: operations on the application's settings\n   */\n\n  /**\n   * @swagger\n   * /api/settings:\n   *   get:\n   *     tags: [settings]\n   *     description: get the site settings\n   *     produces:\n   *       - application/json\n   *     responses:\n   *       200:\n   *         description: success\n   */\n  @Get('/settings')\n  get() {\n    return this.settingService.settings$.take(1).toPromise()\n  }\n}\n"
  },
  {
    "path": "src/server/api/index.ts",
    "content": "// tslint:disable:no-require-imports\n\nimport { useContainer, useExpressServer as configApi } from 'routing-controllers'\nimport { Container } from 'typedi'\nimport { controllers } from './controllers'\nimport { middlewares } from './middlewares'\nimport * as express from 'express'\nimport * as bodyParser from 'body-parser'\nconst swaggerJSDoc = require('swagger-jsdoc')\nconst swaggerUi = require('swagger-ui-express')\n\nuseContainer(Container)\n\nexport const useApi = (app: express.Application) => {\n  const swaggerSpec = swaggerJSDoc({\n    swaggerDefinition: {\n      info: {\n        title: 'fusebox-angular-universal-starter',\n        description: '',\n        termsOfService: '',\n        contact: {\n          name: 'Patrick Michalina',\n          url: 'https://github.com/patrickmichalina/fusebox-angular-universal-starter/issues',\n          email: 'patrickmichalina@mac.com'\n        },\n        license: {\n          name: 'MIT',\n          url: 'https://github.com/patrickmichalina/fusebox-angular-universal-starter/blob/master/LICENSE'\n        },\n        version: '1.0.0'\n      }\n    },\n    apis: ['./src/server/api/controllers/**/*.ts']\n  })\n\n  app.use('/api/**', bodyParser.json())\n  app.use('/api/**', bodyParser.urlencoded({ extended: false }))\n  app.get('/api-docs.json', (req, res) => {\n    res.setHeader('Content-Type', 'application/json')\n    res.send(swaggerSpec)\n  })\n\n  app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec))\n\n  configApi(app, {\n    cors: true,\n    validation: true,\n    routePrefix: '/api',\n    controllers,\n    middlewares,\n    defaultErrorHandler: false,\n    defaults: {\n      nullResultCode: 404,\n      undefinedResultCode: 204\n    }\n  })\n}\n"
  },
  {
    "path": "src/server/api/middlewares/index.ts",
    "content": "import { ZoneErrorHandler } from './zone-error-handler'\n\nexport const middlewares = [\n  ZoneErrorHandler\n]\n"
  },
  {
    "path": "src/server/api/middlewares/zone-error-handler.ts",
    "content": "import { ExpressErrorMiddlewareInterface, Middleware } from 'routing-controllers'\nimport * as express from 'express'\n\n@Middleware({ type: 'after' })\nexport class ZoneErrorHandler implements ExpressErrorMiddlewareInterface {\n  // Simply supresses zonejs error\n  error(error: any, request: express.Request, response: express.Response) {\n    const err = {\n      httpCode: error.httpCode,\n      name: error.name,\n      message: error.message,\n      errors: error.errors\n    }\n    response.status(error.httpCode).send(err)\n  }\n}\n"
  },
  {
    "path": "src/server/api/repositories/index.ts",
    "content": "import { SettingRepository } from './setting.repository'\n\nexport {\n  SettingRepository\n}\n"
  },
  {
    "path": "src/server/api/repositories/setting.repository.ts",
    "content": "import { Observable } from 'rxjs/Observable'\nimport { Service } from 'typedi'\nimport { SETTINGS } from './settings'\nimport { FirebaseAppConfig } from 'angularfire2'\n\nexport interface ISetting {\n  readonly host: string\n  readonly og: {\n    readonly title: string\n    readonly description: string\n    readonly image: string\n    readonly type: string\n    readonly locale: string\n  },\n  readonly assets: {\n    readonly userAvatarImage: string\n  }\n  readonly tokens: {\n    readonly facebookAppId: string\n  },\n  readonly injections: ReadonlyArray<Injectable>,\n  readonly i18n: {\n    readonly [key: string]: any\n  },\n  readonly firebase: {\n    readonly appName: string\n    readonly config: FirebaseAppConfig\n  }\n}\n\nexport interface Injectable {\n  readonly inHead: boolean\n  readonly element: string\n  readonly value?: string\n  readonly attributes?: { readonly [key: string]: string | boolean }\n}\n\nexport interface ISettingRepository {\n  getDictionary(): Observable<ISetting>\n  // add(key: string, value: string, language?: string): void\n}\n\n@Service()\nexport class SettingRepository implements ISettingRepository {\n  private readonly db = SETTINGS\n\n  getDictionary(): Observable<ISetting> {\n    return Observable.of(this.db)\n  }\n\n  // add(key: string, value: string, language = 'EN') {\n  //   key.split('.').reduce((a, b) => {\n  //     return a\n  //   })\n  //   // this.db[language][key] = value\n  // }\n\n  // remove(key: string, language = 'EN') {\n  //   const languageContext = this.db[language]\n  //   const reducedDb = key.split('.').reduce((o, k, i, array) => {\n  //     if (i === array.length - 1) {\n  //       console.log(languageContext[k])\n  //     }\n  //     // console.log(o)\n  //     return o\n  //   }, languageContext)\n  //   // console.log(reducedDb)\n  //   // this.db = res\n  //   return 'd'\n  // }\n}\n"
  },
  {
    "path": "src/server/api/repositories/settings.ts",
    "content": "// tslint:disable:max-line-length\n\nimport { ISetting } from './setting.repository'\n\nexport const SETTINGS: ISetting = {\n  'host': 'localhost:8000',\n  'og': {\n    'title': 'Fusebox Angular Universal Starter',\n    'description': 'Seed project for Angular Universal apps featuring Server-Side Rendering (SSR), FuseBox, dev/prod builds, Brotli/Gzip, SCSS, favicon generation, @types, unit testing w/ Jest, and sitemap generator. Created by Patrick Michalina',\n    'image': 'https://d3anl5a3ibkrdg.cloudfront.net/assets/favicons/android-chrome-512x512.png',\n    'type': 'website',\n    'locale': 'en_US'\n  },\n  'firebase': {\n    'appName': 'fuse-angular-universal-starter',\n    'config': {\n      'apiKey': 'AIzaSyDfE1owJZCbvasXieCKjMoGZddRhqcp7RM',\n      'authDomain': 'fuse-angular-universal-s-67402.firebaseapp.com',\n      'databaseURL': 'https://fuse-angular-universal-s-67402.firebaseio.com',\n      'projectId': 'fuse-angular-universal-s-67402',\n      'storageBucket': 'fuse-angular-universal-s-67402.appspot.com',\n      'messagingSenderId': '883416191164'\n    }\n  },\n  'assets': {\n    'userAvatarImage': 'https://firebasestorage.googleapis.com/v0/b/fuse-angular-universal-s-67402.appspot.com/o/avatar.jpg?alt=media&token=9a153021-6e12-460b-9d87-40c2eed02c82'\n  },\n  'tokens': {\n    'facebookAppId': '117309532219749'\n  },\n  'injections': [\n    {\n      'inHead': true,\n      'element': 'link',\n      'attributes': {\n        'href': '/manifest.json',\n        'rel': 'manifest'\n      }\n    },\n    {\n      'inHead': true,\n      'element': 'meta',\n      'attributes': {\n        'name': 'theme-color',\n        'content': '#2196F3'\n      }\n    },\n    {\n      'inHead': false,\n      'element': 'link',\n      'attributes': {\n        'href': 'https://fonts.googleapis.com/css?family=Roboto',\n        'rel': 'stylesheet',\n        'type': 'text/css',\n        'media': 'none',\n        'onload': 'if(media!==\"all\")media=\"all\"'\n      }\n    },\n    {\n      'inHead': false,\n      'element': 'link',\n      'attributes': {\n        'href': 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',\n        'rel': 'stylesheet',\n        'type': 'text/css',\n        'media': 'none',\n        'onload': 'if(media!==\"all\")media=\"all\"'\n      }\n    },\n    {\n      'inHead': true,\n      'element': 'meta',\n      'attributes': {\n        'name': 'google-site-verification',\n        'content': 'RW-hcjXEgPMoy2NF8pTl8IEzP8gnj3cEZ6aF1HDUiOc'\n      }\n    },\n    {\n      'inHead': false,\n      'element': 'script',\n      'value': \"window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;ga('create', 'UA-107089312-2', 'auto');\",\n      'attributes': {\n        'type': 'text/javascript'\n      }\n    },\n    {\n      'inHead': false,\n      'element': 'script',\n      'attributes': {\n        'async': 'true',\n        'type': 'text/javascript',\n        'src': 'https://www.google-analytics.com/analytics.js'\n      }\n    }\n  ],\n  'i18n': {\n    'en': {\n      'about': {\n        'title': 'About',\n        'description': 'See contact information and details about the Angular Universal seed at angular.patrickmichalina.com'\n      },\n      'account': {\n        'title': 'Account',\n        'description': 'Update your profile and account settings'\n      },\n      'dashboard': {\n        'title': 'Dashboard',\n        'description': 'An example dashboard page'\n      },\n      'admin': {\n        'title': 'Admin',\n        'description': 'For application management'\n      },\n      'changelog': {\n        'title': 'Changelog',\n        'description': 'Version history of the application'\n      },\n      'home': {\n        'title': 'Home',\n        'description': 'A fullstack angular starter framework to quickly build mid-large scale web applications'\n      },\n      'login': {\n        'title': 'Login',\n        'description': 'Login to your account now.'\n      },\n      'logout': {\n        'title': 'Logged Out',\n        'description': 'Come back again!'\n      },\n      'search': {\n        'title': 'Search',\n        'description': 'Search for angular related projects on github, to showcase the flicker-free http state transfer of an Angular isomorphic application.'\n      },\n      'signup': {\n        'title': 'Signup',\n        'description': 'Sign up for an account with us. Create an account to start doing cool things with our application. It\\'s easy to register'\n      },\n      'not-found': {\n        'title': 'Not Found',\n        'description': 'The page you requested can not be found.'\n      }\n    },\n    'jp': {\n      'home': {\n        'title': 'こんにちは',\n        'description': ''\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/server/api/services/index.ts",
    "content": "import { SettingService } from './setting.service'\n\nexport {\n  SettingService\n}\n"
  },
  {
    "path": "src/server/api/services/setting.service.ts",
    "content": "import { Observable } from 'rxjs/Observable'\nimport { Service } from 'typedi'\nimport { ISetting, SettingRepository } from '../repositories/setting.repository'\nexport { ISetting }\n\nexport interface ISettingService {\n  readonly settings$: Observable<ISetting>\n  // getTranslation(lang: string): Observable<any>\n  // getDictionary(): Observable<any>\n  // getLanguageDictionary(lang: string): Observable<any>\n}\n\n@Service()\nexport class SettingService implements ISettingService {\n  readonly settings$ = this.repo.getDictionary()\n\n  constructor(private repo: SettingRepository) { }\n\n  // getDictionary(): Observable<any> {\n  //   const dictionary = this.repo.getDictionary()\n  //   return Observable.of(dictionary)\n  // }\n\n  // getLanguageDictionary(lang: string): Observable<any> {\n  //   throw new Error('Method not implemented.')\n  // }\n\n  // getTranslation(key: string, lang = 'EN') {\n  //   const dictionary = this.repo.getDictionary()\n  //   if (!dictionary) throw new Error('')\n\n  //   const languageContext = dictionary[lang]\n  //   const value = key.split('.').reduce((o, k) => (o || {})[k], languageContext)\n\n  //   return Observable.of(value)\n  // }\n}\n"
  },
  {
    "path": "src/server/api/test-helper.ts",
    "content": "// tslint:disable:no-require-imports\n// tslint:disable-next-line:import-blacklist\nimport 'rxjs'\nimport * as supertest from 'supertest'\nimport { useApi } from './index'\nconst express = require('express')\nconst app = express()\nuseApi(app)\n\nexport const testApi = supertest.agent(app)\n"
  },
  {
    "path": "src/server/server.angular-fire.service.ts",
    "content": "import { makeStateKey, TransferState } from '@angular/platform-browser'\nimport { Injectable, InjectionToken, NgZone } from '@angular/core'\nimport { fromPromise } from 'rxjs/observable/fromPromise'\nimport { fbAdminDb } from './server'\nimport { database } from 'firebase-admin'\n\nexport const FIREBASE_ADMIN_INSTANCE = new InjectionToken<string>('app.fb.admin')\n\n@Injectable()\nexport class FirebaseAdminService {\n  constructor(private zone: NgZone, private ts: TransferState) { }\n\n  public object(path: any, query?: any) {\n    return {\n      valueChanges: () => {\n        const timeout = setTimeout(() => undefined, 10000)\n        return fromPromise(new Promise<any>((resolve, reject) => {\n          this.zone.runOutsideAngular(() => { // Out of Angular's zone so it doesn't wait for the neverending socket connection to end.\n            const ref = this.applyQuery(fbAdminDb.ref(path), query)\n            ref.once('value').then((data: any) => {\n              this.zone.run(() => { // Back in Angular's zone\n                this.ts.set(makeStateKey<string>(`FB.${path}`), data.val())\n                resolve(data.val())\n                setTimeout(() => {\n                  // Maybe getting the data will result in more components to the view that need related data.\n                  // 20ms should be enough for those components to init and ask for more data.\n                  clearTimeout(timeout)\n                }, 20)\n              })\n            }, (err: any) => {\n              setTimeout(() => {\n                reject(err)\n                clearTimeout(timeout)\n              }, 20)\n            })\n          })\n        }))\n      }\n    }\n  }\n\n  public list(path: any, query?: any) {\n    return {\n      valueChanges: () => {\n        const timeout = setTimeout(() => undefined, 10000)\n        return fromPromise(new Promise<any>((resolve, reject) => {\n          this.zone.runOutsideAngular(() => { // Out of Angular's zone so it doesn't wait for the neverending socket connection to end.\n            query(fbAdminDb.ref(path)).once('value').then((snapshot: database.DataSnapshot) => {\n              this.zone.run(() => { // Back in Angular's zone\n                const res = snapshot.val()\n                const projected = Object.keys(res || {}).map(key => res[key])\n                this.ts.set(makeStateKey<string>(`FB.${path}`), projected)\n                resolve(projected)\n                setTimeout(() => {\n                  // Maybe getting the data will result in more components to the view that need related data.\n                  // 20ms should be enough for those components to init and ask for more data.\n                  clearTimeout(timeout)\n                }, 20)\n              })\n            }, (err: any) => {\n              setTimeout(() => {\n                reject(err)\n                clearTimeout(timeout)\n              }, 20)\n            })\n          })\n        }))\n      },\n      snapshotChanges: () => {\n        const timeout = setTimeout(() => undefined, 10000)\n        return fromPromise(new Promise<any>((resolve, reject) => {\n          this.zone.runOutsideAngular(() => { // Out of Angular's zone so it doesn't wait for the neverending socket connection to end.\n            query(fbAdminDb.ref(path)).once('value').then((snapshot: database.DataSnapshot) => {\n              this.zone.run(() => { // Back in Angular's zone\n                const res = snapshot.val()\n                const projected = Object.keys(res || {}).map(key => ({ ...res[key], id: key }))\n                this.ts.set(makeStateKey<string>(`FB.${path}`), projected)\n                resolve(projected)\n                setTimeout(() => {\n                  // Maybe getting the data will result in more components to the view that need related data.\n                  // 20ms should be enough for those components to init and ask for more data.\n                  clearTimeout(timeout)\n                }, 20)\n              })\n            }, (err: any) => {\n              setTimeout(() => {\n                reject(err)\n                clearTimeout(timeout)\n              }, 20)\n            })\n          })\n        }))\n      }\n    }\n  }\n\n  applyQuery(ref: any, query: any) {\n    // tslint:disable-next-line:forin\n    for (const n in query) {\n      const val = query[n].getValue\n        ? query[n].getValue() // BehaviorSubject\n        : query[n] // Primitive\n      switch (n) {\n        case 'orderByKey':\n          ref = ref.orderByKey()\n          break\n        default:\n          if (!(n in ref)) {\n            break\n          }\n          ref = ref[n](val)\n          break\n      }\n    }\n    return ref\n  }\n}\n"
  },
  {
    "path": "src/server/server.angular.module.ts",
    "content": "import { CookieService } from './../client/app/shared/services/cookie.service'\nimport { FB_COOKIE_KEY } from './../client/app/shared/services/auth.service'\nimport { TransferState } from '@angular/platform-browser'\nimport { Subject } from 'rxjs/Subject'\nimport { AngularFireAuth } from 'angularfire2/auth'\nimport { REQUEST } from '@nguniversal/express-engine/tokens'\nimport { APP_BOOTSTRAP_LISTENER, ApplicationRef, enableProdMode, Inject, NgModule, NgZone } from '@angular/core'\nimport { ServerModule, ServerTransferStateModule } from '@angular/platform-server'\nimport { AppComponent } from './../client/app/app.component'\nimport { EnvConfig } from '../../tools/config/app.config'\nimport { AppModule, REQ_KEY } from './../client/app/app.module'\nimport { AngularFireDatabase } from 'angularfire2/database'\nimport { FirebaseAdminService } from './server.angular-fire.service'\nimport { MinifierService } from '../client/app/shared/services/minifier.service'\nimport * as express from 'express'\nimport * as cleanCss from 'clean-css'\nimport 'rxjs/add/operator/filter'\nimport 'rxjs/add/operator/first'\nimport '../client/operators'\n\ndeclare var __process_env__: EnvConfig\n\nif (__process_env__.env !== 'dev') enableProdMode()\n\nexport function onBootstrap(appRef: ApplicationRef, transferState: TransferState, req: express.Request) {\n  return () => {\n    appRef.isStable\n      .filter(stable => stable)\n      .first()\n      .subscribe(() => {\n        transferState.set<any>(REQ_KEY, {\n          hostname: req.hostname,\n          originalUrl: req.originalUrl,\n          referer: req.get('referer')\n        })\n      })\n  }\n}\n\nexport function createAngularFireServer(req: express.Request, transferState: TransferState) {\n  return new AngularFireServer(req, transferState)\n}\n\nexport function getFirebaseServerModule(zone: NgZone, ts: TransferState) {\n  return new FirebaseAdminService(zone, ts)\n}\n\n@NgModule({\n  imports: [\n    ServerModule,\n    ServerTransferStateModule,\n    AppModule\n  ],\n  providers: [\n    {\n      provide: APP_BOOTSTRAP_LISTENER,\n      useFactory: onBootstrap,\n      multi: true,\n      deps: [\n        ApplicationRef,\n        TransferState,\n        REQUEST,\n        CookieService, FB_COOKIE_KEY\n      ]\n    },\n    {\n      provide: AngularFireAuth,\n      useFactory: createAngularFireServer,\n      deps: [\n        REQUEST,\n        TransferState\n      ]\n    },\n    {\n      provide: AngularFireDatabase,\n      useFactory: getFirebaseServerModule,\n      deps: [NgZone, TransferState]\n    },\n    {\n      provide: MinifierService,\n      useValue: {\n        css(css: string): string {\n          return new cleanCss({}).minify(css).styles || css\n        }\n      }\n    }\n  ],\n  bootstrap: [AppComponent]\n})\nexport class AppServerModule { }\n\nexport class AngularFireServer {\n  readonly authSource = new Subject<any | undefined>()\n  readonly idToken = this.authSource.asObservable()\n\n  constructor( @Inject(REQUEST) private req: any, ts: TransferState) {\n    const fbAuth = JSON.parse(this.req.cookies['fbAuth'] || '{}')\n    if (fbAuth.jwt) {\n      this.authSource.next({\n        getIdToken: () => {\n          return new Promise((resolve: any) => {\n            resolve(fbAuth.jwt)\n          })\n        },\n        authState: new Subject(),\n        ...fbAuth\n      })\n    } else {\n      this.authSource.next(undefined)\n    }\n  }\n}\n"
  },
  {
    "path": "src/server/server.config.ts",
    "content": "import { config as dotenv } from 'dotenv'\nimport { writeFileSync } from 'fs'\nimport { EnvConfig } from '../../tools/config/app.config'\ndeclare var __process_env__: any\n\nexport function fuseBoxConfigFactory() {\n  return __process_env__ as EnvConfig\n}\n\nexport interface ServerEnvironmentConfig {\n  FB_SERVICE_ACCOUNT_PRIVATE_KEY_ID: string\n  FB_SERVICE_ACCOUNT_PRIVATE_KEY: string\n}\n\n// this comes from fusebox\nexport const ANGULAR_APP_CONFIG = fuseBoxConfigFactory()\n\nconst env = dotenv()\nif (env.error && ANGULAR_APP_CONFIG.env === 'dev') {\n  console.error('.env development file created!\\nYOU MUST ADD YOUR CONFIGURATION VALUES TO IT')\n  writeFileSync('.env',\n`FB_SERVICE_ACCOUNT_PRIVATE_KEY_ID=\nFB_SERVICE_ACCOUNT_PRIVATE_KEY=`)\n}\n\nconst errors: string[] = []\n\nif (!process.env.FB_SERVICE_ACCOUNT_PRIVATE_KEY_ID) errors.push('Missing FB_SERVICE_ACCOUNT_PRIVATE_KEY_ID')\nif (!process.env.FB_SERVICE_ACCOUNT_PRIVATE_KEY) errors.push('Missing FB_SERVICE_ACCOUNT_PRIVATE_KEY')\n\nif (errors.length > 0) {\n  console.error('Invalid Configuration')\n  console.error(errors.join('\\n'))\n}\n\nexport const SERVER_CONFIG: ServerEnvironmentConfig = process.env as any\n\n// tslint:disable-next-line:no-require-imports\nconst base = require('./service-account.json')\n\nexport const FB_SERVICE_ACCOUNT_CONFIG = {\n  ...base,\n  private_key_id: SERVER_CONFIG.FB_SERVICE_ACCOUNT_PRIVATE_KEY_ID,\n  private_key: SERVER_CONFIG.FB_SERVICE_ACCOUNT_PRIVATE_KEY && SERVER_CONFIG.FB_SERVICE_ACCOUNT_PRIVATE_KEY.replace(/\\\\n/g, '\\n'),\n  project_id: ANGULAR_APP_CONFIG.firebase.config.projectId\n}\n"
  },
  {
    "path": "src/server/server.sitemap.ts",
    "content": "// tslint:disable:no-require-imports\n\nimport { writeFile } from 'fs'\nconst sitemapGenerator = require('sitemap-generator')\n\nexport const sitemap = (host: string) => new Promise<string>((resolve, reject) => {\n  const generator = new sitemapGenerator(host)\n\n  // Avoid infinite loop during initial creation\n  generator.crawler.addFetchCondition((parsedUrl: any) => {\n    return !parsedUrl.url.includes('sitemap.xml')\n  })\n\n  // TODO: this isn't working!\n  // generator.crawler.on('fetchconditionerror', (queueItem: any) => {\n  //   add back into sitemap stack\n  // });\n\n  generator.on('done', (sitemaps: string[]) => {\n    if (sitemaps && sitemaps[0]) {\n      writeFile('dist/sitemap.xml', sitemaps[0], () => {\n        console.log('Generated sitemap.xml')\n        return resolve(sitemaps[0])\n      })\n    } else {\n      return reject('Failed to generate sitemap.xml')\n    }\n  })\n\n  generator.on('clienterror', (err: any) => {\n    return reject(err)\n  })\n\n  console.log(`starting sitemap crawler on ${host}`)\n  generator.start()\n})\n"
  },
  {
    "path": "src/server/server.ts",
    "content": "// tslint:disable:no-require-imports\nimport 'reflect-metadata'\nimport 'zone.js/dist/zone-node'\nimport 'zone.js/dist/long-stack-trace-zone'\nimport * as express from 'express'\nimport * as favicon from 'serve-favicon'\nimport * as cookieParser from 'cookie-parser'\nimport * as admin from 'firebase-admin'\nimport { createLogger } from '@expo/bunyan'\nimport { ngExpressEngine } from '@nguniversal/express-engine'\nimport { AppServerModule } from './server.angular.module'\nimport { sitemap } from './server.sitemap'\nimport { exists, existsSync } from 'fs'\nimport { argv } from 'yargs'\nimport { useApi } from './api'\nimport { join, resolve } from 'path'\nimport { useWebSockets } from './server.web-socket'\nimport { ANGULAR_APP_CONFIG, FB_SERVICE_ACCOUNT_CONFIG } from './server.config'\nimport http = require('http')\nimport ms = require('ms')\n\nconst shrinkRay = require('shrink-ray')\nconst minifyHTML = require('express-minify-html')\nconst bunyanMiddleware = require('bunyan-middleware')\nconst xhr2 = require('xhr2')\n\nxhr2.prototype._restrictedHeaders.cookie = false\n\nrequire('ts-node/register')\n\nconst app = express()\nconst server = http.createServer(app)\nconst root = './dist'\nconst port = process.env.PORT || argv['port'] || 8001\nconst host = process.env.HOST || argv['host'] || 'http://localhost'\nconst isProd = argv['build-type'] === 'prod' || argv['prod']\nconst isTest = argv['e2e']\n\nconst staticOptions = {\n  index: false,\n  maxAge: isProd ? ms('1yr') : ms('0'),\n  setHeaders: (res: express.Response, path: any) => {\n    res.setHeader('Expires', isProd\n      ? new Date(Date.now() + ms('1yr')).toUTCString()\n      : new Date(Date.now() + ms('0')).toUTCString())\n  }\n}\nconst logger = createLogger({\n  name: 'Angular Universal App',\n  type: 'http',\n  streams: [{\n    level: 'error',\n    stream: { write: (err: any) => console.log },\n    type: 'raw'\n  }] as any\n})\n\nif (!isTest) app.use(bunyanMiddleware({ logger, excludeHeaders: ['authorization', 'cookie'] }))\n\nuseWebSockets(server) // uncomment to activate manual web-sockets\napp.engine('html', ngExpressEngine({ bootstrap: AppServerModule }))\napp.set('view engine', 'html')\napp.set('views', root)\napp.use(cookieParser())\napp.use(shrinkRay())\n\n// You can remove the API server by deleteing these two lines\nuseApi(app)\napp.set('ignore-routes', ['/api/'])\n\nif (isProd) {\n  app.use(minifyHTML({\n    override: true,\n    exception_url: false,\n    htmlMinifier: {\n      removeComments: true,\n      collapseWhitespace: true,\n      collapseBooleanAttributes: true,\n      removeAttributeQuotes: false,\n      minifyJS: true\n    }\n  }))\n}\n\nexistsSync(join(root, 'assets/favicons/favicon.ico'))\n  ? app.use(favicon(join(root, 'assets/favicons/favicon.ico')))\n  : app.get('/favicon.ico', (req, res) => res.status(204).send())\n\napp.use('/css', express.static('dist/css', staticOptions))\napp.use('/js', express.static('dist/js', staticOptions))\napp.use('/ngsw.json', express.static('dist/ngsw.json', staticOptions))\napp.use('/ngsw-worker.js', express.static('dist/ngsw-worker.js', staticOptions))\napp.use('/robots.txt', express.static('dist/web/robots.txt', staticOptions))\napp.use('/assets', express.static('dist/assets', { ...staticOptions, fallthrough: false }))\napp.use('/changelog.md', express.static('dist/web/changelog.md', { ...staticOptions, fallthrough: false }))\napp.use('/manifest.json', express.static('dist/manifest.json', staticOptions))\napp.get('/sitemap.xml', (req: express.Request, res: express.Response) => {\n  const fileLocation = resolve(root, 'sitemap.xml')\n  const url = isProd ? host : `${host}:${port}`\n\n  exists(fileLocation, doesExist => {\n    doesExist\n      ? res.sendFile(fileLocation)\n      : sitemap(url)\n        .then(a => res.header('Content-Type', 'text/xml').send(a))\n        .catch(err => res.sendStatus(500))\n  })\n})\n\napp.get('**', (req: express.Request, res: express.Response, next: express.NextFunction) => {\n  if ((req.app.get('ignore-routes') as string[]).some(a => req.url.includes(a))) return next()\n  return res.render('index', {\n    req,\n    res\n  })\n})\n\nexport const fbAdmin = admin.initializeApp({\n  credential: admin.credential.cert(FB_SERVICE_ACCOUNT_CONFIG),\n  databaseURL: ANGULAR_APP_CONFIG.firebase.config.databaseURL\n}, 'admin')\nexport const fbAdminDb = fbAdmin.database()\n\nserver.listen(port, () => {\n  console.log(`Angular Universal Server listening at ${host}:${port}`)\n})\n"
  },
  {
    "path": "src/server/server.web-socket.ts",
    "content": "import { Server } from 'http'\nimport { Server as wsServer } from 'ws'\n\nexport const useWebSockets = (server: Server): wsServer => {\n  const wss = new wsServer({ server })\n\n  // function heartbeat() {\n  //   // tslint:disable:no-invalid-this\n  //   console.log(heartbeat, this.isAlive)\n  //   this.isAlive = true\n  //   console.log(heartbeat, this.isAlive)\n  // }\n\n  wss.on('connection', (ws, req) => {\n    // ws.isAlive = true\n    // ws.on('pong', heartbeat)\n    ws.on('message', message => {\n      if (message && typeof message === 'string') {\n        try {\n          ws.send(JSON.stringify({ ...JSON.parse(message), server: 'Relayed from' }))\n        } catch (err) {\n          ws.send('an error occurred in the ws channel')\n        }\n      }\n    })\n  })\n\n  // setInterval(() => {\n  //   wss.clients.forEach(ws => {\n  //     // if (ws.isAlive === false) return ws.terminate()\n  //     ws.isAlive = false\n  //     ws.ping('', false, true)\n  //   })\n  // }, 1000)\n  return wss\n}\n"
  },
  {
    "path": "src/server/service-account.json",
    "content": "{\n  \"type\": \"service_account\",\n  \"project_id\": \"fuse-angular-universal-s-67402\",\n  \"private_key_id\": \"STORE_IN_ENVIRONMENT_VARIABLE_FOR_SECURITY\",\n  \"private_key\": \"STORE_IN_ENVIRONMENT_VARIABLE_FOR_SECURITY\",\n  \"client_email\": \"firebase-adminsdk-4wy6u@fuse-angular-universal-s-67402.iam.gserviceaccount.com\",\n  \"client_id\": \"116281772478555427333\",\n  \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n  \"token_uri\": \"https://accounts.google.com/o/oauth2/token\",\n  \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n  \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-4wy6u%40fuse-angular-universal-s-67402.iam.gserviceaccount.com\"\n}\n"
  },
  {
    "path": "src/testing/app-testing.module.ts",
    "content": "import { RouterTestingModule } from '@angular/router/testing'\nimport { HttpClientTestingModule } from '@angular/common/http/testing'\nimport { REQUEST } from '@nguniversal/express-engine/tokens'\nimport { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'\nimport { HttpClientModule } from '@angular/common/http'\nimport { PlatformService } from '../client/app/shared/services/platform.service'\nimport { CookieService } from '../client/app/shared/services/cookie.service'\nimport { EnvironmentService } from '../client/app/shared/services/environment.service'\nimport { FormsModule, ReactiveFormsModule } from '@angular/forms'\nimport { APP_BASE_HREF } from '@angular/common'\nimport { MockCookieService } from './mock-cookie.service'\nimport { MockEnvironmentService } from './mock-environment.service'\nimport { MaterialModule } from '../client/app/shared/material.module'\nimport { SharedModule } from '../client/app/shared/shared.module'\nimport { MockFirebaseDatabaseService } from './mock-firebase-database.service'\nimport { FirebaseDatabaseService } from '../client/app/shared/services/firebase-database.service'\nimport { BrowserAnimationsModule } from '@angular/platform-browser/animations'\nimport { Angulartics2Module } from 'angulartics2'\nimport { Angulartics2GoogleAnalytics } from 'angulartics2/ga'\nimport './client/operators'\n\n@NgModule({\n  imports: [\n    HttpClientModule,\n    HttpClientTestingModule,\n    RouterTestingModule,\n    FormsModule,\n    ReactiveFormsModule,\n    MaterialModule,\n    SharedModule,\n    BrowserAnimationsModule,\n    Angulartics2Module.forRoot([Angulartics2GoogleAnalytics])\n  ],\n  providers: [\n    PlatformService,\n    // WindowService,\n    { provide: CookieService, useClass: MockCookieService },\n    { provide: EnvironmentService, useClass: MockEnvironmentService },\n    { provide: FirebaseDatabaseService, useClass: MockFirebaseDatabaseService }\n  ]\n})\nexport class AppTestingModule {\n  static forRoot(requestProvider?: any, windowTokenProvider?: any, authConfigProvider?: any): ModuleWithProviders {\n    return {\n      ngModule: AppTestingModule,\n      providers: [\n        requestProvider || { provide: REQUEST, useValue: {} },\n        // windowTokenProvider || { provide: WINDOW, useValue: window },\n        { provide: APP_BASE_HREF, useValue: '/' }\n      ]\n    } as ModuleWithProviders\n  }\n  constructor(@Optional() @SkipSelf() parentModule: AppTestingModule) {\n    if (parentModule)\n      throw new Error('AppTestingModule already loaded.')\n  }\n}\n"
  },
  {
    "path": "src/testing/mock-cookie.service.ts",
    "content": "import { Injectable } from '@angular/core'\nimport { ICookieService } from '../client/app/shared/services/cookie.service'\nimport { BehaviorSubject } from 'rxjs/BehaviorSubject'\n\n@Injectable()\nexport class MockCookieService implements ICookieService {\n  private cookieSource = new BehaviorSubject<{ [key: string]: any }>(this.getAll())\n  public cookies$ = this.cookieSource.asObservable()\n  public mockCookieStore: any = { }\n\n  get(name: string): any {\n    return this.mockCookieStore[name]\n  }\n\n  getAll() {\n    return this.mockCookieStore || {}\n  }\n\n  set(name: string, value: any, options?: Cookies.CookieAttributes | undefined): void {\n    this.mockCookieStore[name] = value\n    this.cookieSource.next(this.getAll())\n  }\n\n  remove(name: string, options?: Cookies.CookieAttributes | undefined): void {\n    const { [name]: omit, ...newObject } = this.mockCookieStore\n    this.mockCookieStore = newObject\n    this.cookieSource.next(this.getAll())\n  }\n}\n"
  },
  {
    "path": "src/testing/mock-environment.service.ts",
    "content": "import { Injectable } from '@angular/core'\nimport { IEnvironmentService } from '../client/app/shared/services/environment.service'\n\n@Injectable()\nexport class MockEnvironmentService implements IEnvironmentService {\n  config: any = {}\n}\n"
  },
  {
    "path": "src/testing/mock-firebase-database.service.ts",
    "content": "import { Injectable } from '@angular/core'\nimport { of } from 'rxjs/observable/of'\n\n@Injectable()\nexport class MockFirebaseDatabaseService {\n  get() {\n    return of('test')\n  }\n  getList() {\n    return of([])\n  }\n  getListKeyed() {\n    return of([])\n  }\n}\n"
  },
  {
    "path": "src/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"isolatedModules\": false,\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true,\n    \"declaration\": false,\n    \"noImplicitAny\": true,\n    \"noImplicitUseStrict\": false,\n    \"strictNullChecks\": true,\n    \"noEmitHelpers\": false,\n    \"noLib\": false,\n    \"noUnusedLocals\": true,\n    \"outDir\": \"dist/\",\n    \"importHelpers\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"sourceMap\": true,\n    \"lib\": [\"es6\", \"dom\"],\n    \"skipLibCheck\": true\n  },\n  \"include\": [\n    \"./**/**.ts\",\n    \"../tools/manual-typings/**/**.ts\"\n  ],\n  \"exclude\": [\n    \"client/main.aot-prod.ts\",\n    \"client/main.aot.ts\"\n  ]\n}"
  },
  {
    "path": "src/tslint.json",
    "content": "{\n  \"rulesDirectory\": [\n    \"../node_modules/codelyzer\",\n    \"../tools/tslint-rules\"\n  ],\n  \"extends\": [\n    \"angular-tslint-rules\",\n    \"tslint-immutable\"\n  ],\n  \"rules\": {\n    \"i18n\": [false],\n    \"newline-per-chained-call\": [false],\n    \"no-unnecessary-class\": [false],\n    \"no-var-keyword\": true,\n    \"no-let\": false,\n    \"no-parameter-reassignment\": false,\n    \"readonly-keyword\": false,\n    \"readonly-array\": false,\n    \"no-object-mutation\": false,\n    \"no-delete\": true,\n    \"no-method-signature\": false,\n    \"typedef\": [\n      false\n    ],\n    \"angular-whitespace\": [\n      true,\n      \"check-interpolation\",\n      \"check-semicolon\"\n    ],\n    \"space-within-parens\": [\n      false\n    ],\n    \"object-literal-key-quotes\": [\n      false\n    ],\n    \"semicolon\": [\n      true,\n      \"never\"\n    ],\n    \"align\": [\n      false\n    ],\n    \"no-console\": [\n      false\n    ],\n    \"curly\": false,\n    \"member-ordering\": [\n      false\n    ],\n    \"member-access\": [\n      false,\n      \"no-public\"\n    ],\n    \"newline-before-return\": false,\n    \"directive-selector\": [\n      true,\n      \"attribute\",\n      [\n        \"app\",\n        \"pm\"\n      ],\n      \"camelCase\"\n    ],\n    \"component-selector\": [\n      true,\n      \"element\",\n      [\n        \"pm\",\n        \"app\",\n        \"test\",\n        \"i18n\"\n      ],\n      \"kebab-case\"\n    ],\n    \"use-view-encapsulation\": false,\n    \"pipe-naming\": [\n      true,\n      \"camelCase\",\n      \"pm\"\n    ],\n    \"array-type\": [\n      false\n    ],\n    \"validate-decorators\": [true, {\n      \"Component\": {\n        \"changeDetection\": \"\\\\.OnPush$\"\n      }\n    }, \"**/!(*.spec).ts\"]\n  }\n}"
  },
  {
    "path": "tools/config/app.config.ts",
    "content": "export interface EnvConfig {\n  name: string,\n  description: string,\n  firebase: {\n    appName: string\n    config: {\n      apiKey: string\n      authDomain: string\n      databaseURL: string\n      projectId: string\n      storageBucket: string\n      messagingSenderId: string\n    }\n  }\n  host: string\n  env?: string\n  endpoints?: {\n    api: string,\n    websocketServer: string\n  }\n}\n"
  },
  {
    "path": "tools/config/build.ci.replace.ts",
    "content": "import { argv } from 'yargs'\n\n// use this to replace values in your env/configs using environment variables from your CI/deployment system\nexport const OVERRIDES = argv['ci-vars'] ?\n  {\n    host: `https://${process.env.HEROKU_APP_NAME}.herokuapp.com`,\n    endpoints: {\n      api: `https://${process.env.HEROKU_APP_NAME}.herokuapp.com/api`,\n      websocketServer: `wss://${process.env.HEROKU_APP_NAME}.herokuapp.com`\n    }\n  } : {}"
  },
  {
    "path": "tools/config/build.config.ts",
    "content": "import { BuildConfiguration } from './build.interfaces'\nimport { argv } from 'yargs'\nimport { basename } from 'path'\nimport { OVERRIDES } from './build.ci.replace'\n\nexport const BUILD_CONFIG: BuildConfiguration = {\n  baseHref: '/',\n  faviconSource: './src/client/assets/favicon.png',\n  outputDir: 'dist',\n  sourceDir: 'src',\n  prodOutDir: './dist/prod',\n  assetParentDir: 'src/client',\n  toolsDir: './tools',\n  minifyIndex: true,\n  skipFaviconGenerationOnDev: true,\n  browserSyncPort: 8000,\n  host: 'http://localhost',\n  port: 8001,\n  dependencies: [\n    {\n      order: 1,\n      inHead: true,\n      element: 'meta',\n      attributes: {\n        name: 'viewport',\n        content: 'width=device-width, initial-scale=1'\n      }\n    }\n  ]\n}\n\nlet envConfig\nconst selectedEnv = argv['env-config'] || 'dev'\nconst selectedBuildType = argv['build-type'] || 'dev'\n\n// tslint:disable:no-require-imports\ntry {\n  envConfig = { ...require(`../env/${selectedEnv}`), ...OVERRIDES }\n} catch (err) {\n  throw new Error(`Unable to find environment configuration for '${selectedEnv}' `)\n}\n\nconst TypeHelper = require('fuse-box-typechecker').TypeHelper\n\nexport const taskName = (nodeFilename: string) => basename(nodeFilename).replace('.ts', '')\nexport const ENV_CONFIG_INSTANCE = envConfig\nexport const cdn = process.env.CDN_ORIGIN ? process.env.CDN_ORIGIN : undefined\nexport const isBuildServer: boolean = argv.ci\nexport const isAot: boolean = argv.aot\nexport const isProdBuild =\n  selectedBuildType === 'prod' ||\n  selectedBuildType === 'production' ||\n  process.env.NODE_ENV === 'prod' ||\n  process.env.NODE_ENV === 'production'\n\nexport const typeHelper = (sync = true, throwOnTsLint = true) => {\n  const _runner = TypeHelper({\n    basePath: './src',\n    tsConfig: './tsconfig.json',\n    tsLint: './tslint.json',\n    name: 'App typechecker',\n    throwOnTsLint\n  })\n  if (sync) {\n    _runner.runSync()\n  } else {\n    _runner.runAsync()\n  }\n}\n"
  },
  {
    "path": "tools/config/build.interfaces.ts",
    "content": "import { Dependency } from '../plugins/web-index'\n\nexport interface BuildConfiguration {\n  dependencies: Dependency[]\n  baseHref: string\n  faviconSource: string\n  outputDir: string\n  sourceDir: string\n  prodOutDir: string\n  assetParentDir: string\n  minifyIndex: boolean\n  skipFaviconGenerationOnDev: boolean\n  toolsDir: string\n  browserSyncPort: number\n  host: string\n  port: number\n}\n"
  },
  {
    "path": "tools/config/console.banner.256.txt",
    "content": "\n\n\u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[48;5;160m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;167m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;174m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m\n\u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;181m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m\n\u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;124m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m\n\u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;124m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;125m \u001b[0m\n\u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;124m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;161m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;161m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;125m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;255m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;224m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;231m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;124m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;124m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[48;5;161m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[48;5;131m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[48;5;167m \u001b[48;5;167m \u001b[48;5;131m \u001b[48;5;167m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m \u001b[0m\n\u001b[0m"
  },
  {
    "path": "tools/env/base.ts",
    "content": "import { EnvConfig } from '../config/app.config';\n\nconst BaseConfig: EnvConfig = {\n  name: 'Fusebox Angular Universal Starter',\n  description: 'Fusebox Angular Universal Starter',\n  endpoints: {\n    api: 'http://localhost:8000/api',\n    websocketServer: 'ws://localhost:8001'\n  },\n  firebase: {\n    appName: 'fuse-angular-universal-starter',\n    config: {\n      apiKey: 'AIzaSyDfE1owJZCbvasXieCKjMoGZddRhqcp7RM',\n      authDomain: 'fuse-angular-universal-s-67402.firebaseapp.com',\n      databaseURL: 'https://fuse-angular-universal-s-67402.firebaseio.com',\n      projectId: 'fuse-angular-universal-s-67402',\n      storageBucket: 'fuse-angular-universal-s-67402.appspot.com',\n      messagingSenderId: '883416191164'\n    }\n  },\n  host: 'http://localhost:8000'\n};\n\nexport = BaseConfig;"
  },
  {
    "path": "tools/env/dev.ts",
    "content": "import { EnvConfig } from '../config/app.config';\nimport * as base from './base';\n\nconst DevConfig: EnvConfig = {\n  ...base,\n  env: 'dev'\n};\n\nexport = DevConfig;"
  },
  {
    "path": "tools/env/e2e.ts",
    "content": "import { EnvConfig } from '../config/app.config';\nimport * as base from './base';\n\nconst DevConfig: EnvConfig = {\n  ...base,\n  env: 'e2e',\n  endpoints: {\n    api: 'http://localhost:8000/api',\n    websocketServer: 'ws://localhost:80001'\n  },\n};\n\nexport = DevConfig;"
  },
  {
    "path": "tools/env/prod.ts",
    "content": "import { EnvConfig } from '../config/app.config';\nimport * as base from './base';\n\nconst ProdConfig: EnvConfig = { \n  ...base,\n  env: 'prod',\n  host: \"https://angular.patrickmichalina.com\"\n};\n\nexport = ProdConfig;"
  },
  {
    "path": "tools/manual-typings/README.md",
    "content": "# Manual Typings\n\nWhen typings are not availabnle as part of the npm package or using @types repository, you can manual define the typed definitions for the javascript package."
  },
  {
    "path": "tools/manual-typings/project/sample.package.d.ts",
    "content": "// declare module \"moment/moment\" {\n//   export = moment;\n// }"
  },
  {
    "path": "tools/manual-typings/seed/bunyan.d.ts",
    "content": "declare module '@expo/bunyan' {\n  import * as bunyan from 'bunyan';\n  export = bunyan;\n}"
  },
  {
    "path": "tools/manual-typings/seed/hash-files.d.ts",
    "content": "declare module 'hash-files' {\n  export function sync(options?: any): string;\n}"
  },
  {
    "path": "tools/manual-typings/seed/json.d.ts",
    "content": "declare module \"*.json\" {\n  const value: any;\n  export default value;\n}"
  },
  {
    "path": "tools/manual-typings/seed/loglevel-std-streams.d.ts",
    "content": "declare module 'loglevel-std-streams' {\n  interface ILoglevelStdStreams {\n    (log: any): void;\n  }\n\n  const loglevelStdStreams: ILoglevelStdStreams;\n  export = loglevelStdStreams;\n}"
  },
  {
    "path": "tools/plugins/ng-aot.ts",
    "content": "import { WorkFlowContext } from 'fuse-box/src/core/WorkflowContext';\nimport { File } from 'fuse-box/src/core/File';\n\nimport { } from '@angular/angular-cli'\n\nexport interface NgAotPluginOptions {\n\n}\n\nexport class NgAotPluginClass {\n  public test: RegExp = /.ts/;\n\n  constructor(options: NgAotPluginOptions = {}) {\n    console.log(options);\n  }\n\n  public init(context: WorkFlowContext) {\n\n  }\n\n  public transform(file: File) {\n    file.loadContents();\n  }\n}\n\nexport const NgLazyPlugin = (options: NgAotPluginOptions = {}) => new NgAotPluginClass(options);\n"
  },
  {
    "path": "tools/plugins/pwa-fused.ts",
    "content": "import { WorkFlowContext } from 'fuse-box/src/core/WorkflowContext';\nconst pwaManifest = require('@pwa/manifest')\n\nexport interface PwaFusedPluginOptions {\n  distPath?: string,\n  manifest?: {\n    name?: string\n    short_name?: string\n    lang?: string\n    start_url?: string\n    display?: string,\n    theme_color?: string,\n    background_color?: string\n    orientation?: string\n  }\n}\n\nexport class PwaFusedPluginClass {\n  public dependencies = ['@pwa/manifest']\n\n  constructor(private opts: PwaFusedPluginOptions = {}) {\n    this.opts = {\n      distPath: 'dist',\n      manifest: {\n        name: 'Fusebox Angular Universal Starter',\n        short_name: 'Angular Universal',\n        lang: 'en',\n        start_url: '/',\n        display: 'standalone',\n        theme_color: '#FFFFFF',\n        background_color: '#3F51B5',\n        orientation: 'natural'\n      },\n      // ...opts,\n    } as PwaFusedPluginOptions\n  }\n\n  public preBundle(context: WorkFlowContext) {\n    if (context.bundle) {\n      pwaManifest(this.opts.manifest).then((manifest: any) => {\n        pwaManifest.write(this.opts.distPath || `${context.appRoot}/${this.opts.distPath}`, manifest);\n      });\n    }\n  }\n}\n\nexport const PwaFusedPlugin = (options?: PwaFusedPluginOptions) => new PwaFusedPluginClass(options);"
  },
  {
    "path": "tools/plugins/web-index.ts",
    "content": "import { BundleProducer } from 'fuse-box';\nimport { minify } from 'html-minifier';\nimport { readFileSync } from 'fs';\nimport { join } from 'path';\nimport hash = require('string-hash');\nimport jsdom = require('jsdom');\nconst { JSDOM } = jsdom\n\nexport interface WebIndexPluginOptions {\n  // Defines the title of a document\n  title?: string;\n\n  // Defines a default address or a default target for all links on a page\n  base?: string;\n\n  // Defines a app root element in the body, typical for JS frameworks like Angular JS\n  appElement?: { name: string, innerHTML: string };\n\n  // Output file\n  target?: string;\n\n  minify?: boolean | Object;\n\n  startingDocumentPath?: string;\n\n  // bundled javascript to inject as script elements\n  bundles: string[];\n\n  asyncBundles?: boolean | Object;\n\n  bundleOrdering?: { [key: string]: number };\n\n  additionalDeps?: Dependency[];\n\n  transformByQuery?: [{ query: string, transformer: (element: NodeListOf<Element>) => NodeListOf<Element>, execute: boolean }];\n\n  postProcess?(html: string): string;\n}\n\nexport class WebIndexPluginClass {\n  public dependencies: ['jsdom', 'fs', 'string-hash', 'html-minifier'];\n\n  constructor(public opts: WebIndexPluginOptions) {\n    if (!opts.additionalDeps) this.opts.additionalDeps = [];\n    if (!opts.bundles) this.opts.bundles = [];\n  }\n\n  producerEnd(producer: BundleProducer) {\n    const baseDeps: Dependency[] = [\n      {\n        attributes: {\n          href: this.opts.base || '/'\n        },\n        inHead: true,\n        element: 'base',\n        order: -100\n      },\n      {\n        attributes: {\n          charset: 'utf-8'\n        },\n        inHead: true,\n        element: 'meta',\n        order: -99\n      }\n    ]\n\n    const appElement: Dependency = this.opts.appElement\n      ? { element: this.opts.appElement.name, content: this.opts.appElement.innerHTML }\n      : undefined as any;\n\n    const deps = [appElement, ...producer.sortBundles()\n      .filter(bundle => this.opts.bundles.some(b => b === bundle.name))\n      .filter(bundle => bundle.context.output)\n      .filter(bundle => bundle.context.output.lastPrimaryOutput)\n      .map(bundle => {\n        return {\n          element: 'script',\n          attributes: {\n            src: bundle.context.output.folderFromBundleName\n              ? `/${bundle.context.output.folderFromBundleName}/${bundle.context.output.lastPrimaryOutput.filename}`\n              : `/${bundle.context.output.lastPrimaryOutput.filename}`,\n            defer: true\n          }\n        } as Dependency;\n      }), ...this.opts.additionalDeps as Dependency[], ...baseDeps]\n      .filter(a => a);\n\n    const depTransformer = new ConfigurationTransformer();\n\n    const startingHtml = this.opts.startingDocumentPath\n      ? readFileSync(join(producer.fuse.context.appRoot, this.opts.startingDocumentPath), 'utf-8')\n      : undefined;\n\n    let html = depTransformer.applyTransform(deps, startingHtml, this.opts.appElement && this.opts.appElement.name);\n\n    if (this.opts.transformByQuery) {\n      this.opts.transformByQuery.forEach(setting => {\n        if (setting.execute) {\n          const dom = new JSDOM(html);\n          let items = dom.window.document.querySelectorAll(setting.query);\n\n          items = setting.transformer(items);\n\n          html = dom.serialize();\n        }\n      });\n    }\n\n    const finalHtml = typeof (this.opts.postProcess) === 'function'\n      ? this.opts.postProcess(html)\n      : html;\n\n    producer.fuse.context.output.write(this.opts.target || 'index.html', finalHtml, true);\n  }\n}\n\nexport const WebIndexPlugin = (options: WebIndexPluginOptions = { bundles: [], additionalDeps: [] }) => new WebIndexPluginClass(options);\n\nexport interface Dependency {\n  order?: number;\n  inHead?: boolean;\n  attributes?: { [key: string]: string | boolean };\n  element: string;\n  content?: string;\n  shouldExecute?: (dep: Dependency) => boolean;\n}\n\nexport interface IConfigurationTransformer {\n  applyTransform(dependencies: Dependency[], document?: string): string;\n}\n\nexport class ConfigurationTransformer implements IConfigurationTransformer {\n  applyTransform(dependencies: Dependency[], document?: jsdom.JSDOM | string, appName = 'pm-app'): string {\n    if (!Array.isArray(dependencies)) throw new Error('dependencies must be an array');\n    if (!dependencies) {\n      return !document\n        ? new JSDOM().serialize()\n        : document instanceof jsdom.JSDOM\n          ? document.serialize()\n          : new JSDOM(document).serialize();\n    }\n\n    const doc = document\n      ? document instanceof jsdom.JSDOM\n        ? document\n        : new JSDOM(minify(document, { collapseWhitespace: true }))\n      : new JSDOM('<!DOCTYPE html><html lang=\"en\"></html>');\n\n    const existingElements = \n      Array.from(new Set(Array.from(doc.window.document.querySelectorAll(`script, link, meta, base, ${appName}`)).map(a => hash(a.outerHTML))));\n\n    const elements = dependencies.filter(dep =>\n      typeof (dep.shouldExecute) === 'function'\n        ? dep.shouldExecute(dep)\n        : true\n    ).map(dep => {\n      const el = doc.window.document.createElement(dep.element) as HTMLElement;\n\n      if (dep.content) el.innerHTML = dep.content;\n\n      Object.keys(dep.attributes || {}).forEach(key => el.setAttribute(key, (dep.attributes as any)[key]))\n\n      return {\n        ...dep,\n        el\n      };\n    }).filter(a => !existingElements.some(b => b === hash(a.el.outerHTML)))\n\n    const headElements = elements.filter(a => a.inHead).sort((a, b) => (a.order as number) - (b.order as number)).map(a => a.el);\n    const bodyElements = elements.filter(a => !a.inHead).sort((a, b) => (a.order as number) - (b.order as number)).map(a => a.el);\n\n    headElements.forEach(el => doc.window.document.head.appendChild(el));\n    bodyElements.forEach(el => doc.window.document.body.appendChild(el));\n\n    const resultingHtml = doc.serialize();\n\n    return resultingHtml;\n  }\n}"
  },
  {
    "path": "tools/scripts/post-merge.sh",
    "content": "#!/usr/bin/env bash\n# MIT © Sindre Sorhus - sindresorhus.com\n\n# git hook to run a command after `git pull` if a specified file was changed\nchanged_files=\"$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)\"\n\ncheck_run() {\n\techo \"$changed_files\" | grep --quiet \"$1\" && eval \"$2\"\n}\n\n# run `npm install` if package.json changed\ncheck_run package.json \"npm install\""
  },
  {
    "path": "tools/scripts/replace.ts",
    "content": "const jsdom = require(\"jsdom\");\nconst { JSDOM } = jsdom;\n\nexport const replace = function (file: any, key: string, replace: string) {\n  const dom = new JSDOM(file);\n\n  dom.window.document.querySelectorAll('[data-build-replace]').forEach((thing: any) => {\n    const split = thing.getAttribute('data-build-replace').split(':');\n    if (key === split[0]) {\n      thing[split[1]] = replace;\n      thing.removeAttribute('data-build-replace')\n    }\n  });\n\n  return dom.serialize();\n}\n\nexport const replaceByQuery = function (file: any, query: string, key: string, replace: string) {\n  const dom = new JSDOM(file);\n\n  dom.window.document.querySelectorAll(query).forEach((thing: any) => {\n    thing[key] = replace;\n  });\n\n  return dom.serialize();\n}\n\nexport const prefixByQuery = function (file: any, query: string, key: string, prefix: string) {\n  const dom = new JSDOM(file);\n\n  dom.window.document.querySelectorAll(query).forEach((thing: any) => {\n    if (!(thing[key] as string).includes('https') || !(thing[key] as string).includes('http')) {\n      thing[key] = `${prefix}${thing[key]}`;\n    }\n  });\n\n  return dom.serialize();\n}\n\n"
  },
  {
    "path": "tools/tasks/index.ts",
    "content": "import { readdirSync } from 'fs';\nimport { join } from 'path';\n\nreaddirSync(join(__dirname, 'seed')).forEach(file => require('./seed/' + file));\nreaddirSync(join(__dirname, 'project')).forEach(file => require('./project/' + file));\n"
  },
  {
    "path": "tools/tasks/project/sample.ts",
    "content": "import { Sparky } from 'fuse-box';\nimport { taskName } from '../../config/build.config';\n\n\n// An example task stub\nSparky.task(taskName(__filename), () => {\n  console.log('Sample Task!')\n});"
  },
  {
    "path": "tools/tasks/seed/assets.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { BUILD_CONFIG, taskName } from '../../config/build.config'\n\nSparky.task(taskName(__filename), () => Sparky.src('./assets/**/!(favicon).*', { base: `./${BUILD_CONFIG.assetParentDir}` })\n  .dest(`./${BUILD_CONFIG.outputDir}`))\n"
  },
  {
    "path": "tools/tasks/seed/banner.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { SparkyFile } from 'fuse-box/src/sparky/SparkyFile'\nimport { BUILD_CONFIG, taskName } from '../../config/build.config'\n\nSparky.task(taskName(__filename), () => Sparky.src(`${BUILD_CONFIG.toolsDir}/config/console.banner.256.txt`)\n  .file('console.banner.256.txt', (file: SparkyFile) => {\n    file.read()\n    console.log(file.contents.toString())\n  }))\n"
  },
  {
    "path": "tools/tasks/seed/changelog.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { BUILD_CONFIG, taskName } from '../../config/build.config'\nimport { createWriteStream } from 'fs'\n// tslint:disable-next-line:no-require-imports\nconst conventionalChangelog = require('conventional-changelog')\n\nSparky.task(taskName(__filename), () => {\n  // heroku does not clone the git repo, so no meta data can be found :(\n  if (process.env.HEROKU) return Promise.resolve()\n\n  return conventionalChangelog({\n    preset: 'angular',\n    releaseCount: 0\n  }).pipe(createWriteStream(`./${BUILD_CONFIG.outputDir}/CHANGELOG.md`))\n})\n"
  },
  {
    "path": "tools/tasks/seed/clean.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { BUILD_CONFIG, taskName } from '../../config/build.config'\n\nSparky.task(taskName(__filename), () =>\n  Sparky.src(`${BUILD_CONFIG.outputDir}`)\n    .clean(`${BUILD_CONFIG.outputDir}`)\n    .clean('.fusebox')\n    .clean('.ngc')\n    .clean('src/client/.aot'))\n"
  },
  {
    "path": "tools/tasks/seed/config.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { taskName, ENV_CONFIG_INSTANCE } from '../../config/build.config'\nimport { writeFileSync } from 'fs'\n\nSparky.task(taskName(__filename), () => {\n  writeFileSync('./src/config.json', JSON.stringify(ENV_CONFIG_INSTANCE, undefined, 2), { encoding: 'utf-8' })\n})\n"
  },
  {
    "path": "tools/tasks/seed/favicons.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { readFile, writeFile } from 'fs'\nimport { sync as mkdirp } from 'mkdirp'\nimport { BUILD_CONFIG, ENV_CONFIG_INSTANCE, taskName } from '../../config/build.config'\n\n// tslint:disable:no-require-imports\nconst favicons = require('favicons') // https://www.npmjs.com/package/favicons\nconst jsdom = require('jsdom')\nconst { JSDOM } = jsdom\n\nSparky.task(taskName(__filename), () => {\n  return new Promise((resolve, reject) => {\n    const config = JSON.parse(ENV_CONFIG_INSTANCE.angularAppConfig)\n    favicons(BUILD_CONFIG.faviconSource, {\n      path: '/assets/favicons',\n      appDescription: config.description,\n      appName: config.name,\n      background: '#1976d2',\n      theme_color: '#1976d2',\n      start_url: '',\n      short_name: 'Angular Universal',\n      lang: 'en'\n    }, (error: any, response: any) => {\n      if (error) {\n        // tslint:disable:no-console\n        console.log(error.status)\n        console.log(error.name)\n        console.log(error.message)\n        return\n      }\n\n      const htmlHeadBlock = (response.html as Array<string>).reduce((prev, curr) => {\n        return `${prev}\\n${curr}`\n      })\n\n      mkdirp(`./${BUILD_CONFIG.outputDir}/assets/favicons`)\n\n      const imagePromises = (response.images as Array<{ name: string, contents: Buffer }>).map(image =>\n        new Promise((resolve1, reject1) => {\n          return writeFile(`./${BUILD_CONFIG.outputDir}/assets/favicons/${image.name}`, image.contents, (err: any) => {\n            if (err) reject1(err)\n\n            return resolve1(image.contents)\n          })\n        }))\n\n      const filePromises = (response.files as Array<{ name: string, contents: string }>).map(file =>\n        new Promise((resolve2, reject2) => {\n          writeFile(`./${BUILD_CONFIG.outputDir}/assets/favicons/${file.name}`, file.contents, (err: any) => {\n            if (err) reject2(err)\n\n            return resolve2(file.contents)\n          })\n        }))\n\n      return Promise.all<any>([...imagePromises, ...filePromises]).then(() => {\n        const filePath = `./${BUILD_CONFIG.outputDir}/index.html`\n        readFile(filePath, 'utf-8', (err, data) => {\n          if (err) return reject(err)\n          const dom = new JSDOM(data);\n          (dom.window.document.head as HTMLHeadElement).innerHTML = `${dom.window.document.head.innerHTML}\\n${htmlHeadBlock}`\n          writeFile(filePath, dom.serialize(), { encoding: 'utf-8' }, writeErr => {\n            if (writeErr) return reject(writeErr)\n\n            return resolve()\n          })\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "tools/tasks/seed/fonts.ts",
    "content": "import { Sparky } from 'fuse-box';\nimport { BUILD_CONFIG, taskName } from '../../config/build.config';\n\n// copies font-awesome\nSparky.task(taskName(__filename), () =>\n  Sparky.src('./**', { base: `./node_modules/font-awesome/fonts` })\n    .dest(`./${BUILD_CONFIG.outputDir}/assets/fonts/font-awesome`));\n"
  },
  {
    "path": "tools/tasks/seed/index.copy.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { BUILD_CONFIG, taskName } from '../../config/build.config'\n\nSparky.task(taskName(__filename), () => Sparky.src('./index.html', { base: './src/client' }).dest(`./${BUILD_CONFIG.outputDir}`))\n"
  },
  {
    "path": "tools/tasks/seed/index.minify.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { SparkyFile } from 'fuse-box/src/sparky/SparkyFile'\nimport { minify } from 'html-minifier'\nimport { BUILD_CONFIG, taskName } from '../../config/build.config'\n\nSparky.task(taskName(__filename), () => {\n  new Promise((resolve, reject) => {\n    if (!BUILD_CONFIG.minifyIndex) resolve()\n\n    return Sparky.src('./dist/index.html').file('index.html', (file: SparkyFile) => {\n      file.read()\n      file.setContent(minify(file.contents.toString(), {\n        collapseWhitespace: true,\n        removeComments: true,\n        minifyJS: true\n      }))\n      file.save()\n      resolve()\n    })\n  })\n})\n"
  },
  {
    "path": "tools/tasks/seed/lint.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { taskName, typeHelper } from '../../config/build.config'\n\nSparky.task(taskName(__filename), () => typeHelper())\n"
  },
  {
    "path": "tools/tasks/seed/mk-dist.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { BUILD_CONFIG, taskName } from '../../config/build.config'\nimport { sync as mkdirp } from 'mkdirp'\n\nSparky.task(taskName(__filename), () => {\n  mkdirp(BUILD_CONFIG.outputDir)\n})\n"
  },
  {
    "path": "tools/tasks/seed/ngc.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { main as ngc } from '@angular/compiler-cli/src/main'\nimport { taskName } from '../../config/build.config'\n\nSparky.task(taskName(__filename), () => {\n  return ngc(['tsconfig-aot.json'])\n})\n"
  },
  {
    "path": "tools/tasks/seed/ngsw-json.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { BUILD_CONFIG, taskName } from '../../config/build.config'\n// tslint:disable-next-line:no-require-imports\nconst nrc = require('node-run-cmd')\n\nSparky.task(taskName(__filename), () => {\n  return nrc.run(`node_modules/.bin/ngsw-config ${BUILD_CONFIG.outputDir} ./src/client/ngsw.json`)\n})\n\n"
  },
  {
    "path": "tools/tasks/seed/ngsw-worker.min.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { SparkyFile } from 'fuse-box/src/sparky/SparkyFile'\nimport { taskName } from '../../config/build.config'\n// tslint:disable-next-line:no-require-imports\nconst uglifyJS = require('uglify-es')\n\nSparky.task(taskName(__filename), () => {\n  return Sparky.src(['./dist/ngsw-worker.js']).file('ngsw-worker.js', (file: SparkyFile) => {\n    file.read()\n    const result = uglifyJS.minify(file.contents.toString())\n    file.setContent(result.code)\n    file.save()\n  })\n})\n"
  },
  {
    "path": "tools/tasks/seed/ngsw-worker.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { BUILD_CONFIG, taskName } from '../../config/build.config'\n\nSparky.task(taskName(__filename), () => {\n  return Sparky.src(['ngsw-worker.js'], { base: './node_modules/@angular/service-worker' }).dest(`./${BUILD_CONFIG.outputDir}`)\n})\n"
  },
  {
    "path": "tools/tasks/seed/ngsw.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { taskName } from '../../config/build.config'\n\nSparky.task(taskName(__filename), ['ngsw-worker', 'ngsw-worker.min', 'ngsw-json'], () => undefined)\n"
  },
  {
    "path": "tools/tasks/seed/sass.files.ts",
    "content": "import { sync as glob } from 'glob'\nimport { Sparky } from 'fuse-box'\nimport { SparkyFile } from 'fuse-box/src/sparky/SparkyFile'\nimport { readFileSync } from 'fs'\nimport { taskName } from '../../config/build.config'\nimport { ConfigurationTransformer, Dependency } from '../../plugins/web-index'\n// tslint:disable-next-line:no-require-imports\nimport hash = require('string-hash')\n\nSparky.task(taskName(__filename), () => {\n  const css = glob('./dist/css/**/*.css').map(a => {\n    return {\n      hash: hash(readFileSync(a).toString()),\n      name: a.replace('./dist', '')\n    }\n  })\n\n  return Sparky.src('./dist/index.html').file('index.html', (file: SparkyFile) => {\n    file.read()\n\n    const transformer = new ConfigurationTransformer()\n    const deps: Dependency[] = css.map(c => {\n      return {\n        inHead: true,\n        order: 1,\n        element: 'link',\n        attributes: {\n          rel: 'stylesheet',\n          href: `${c.name}`\n        }\n      } as Dependency\n    })\n\n    const html = transformer.applyTransform(deps, file.contents.toString())\n    file.setContent(html)\n    file.save()\n  })\n})\n"
  },
  {
    "path": "tools/tasks/seed/sass.ts",
    "content": "// tslint:disable:no-require-imports\nimport { Sparky } from 'fuse-box'\nimport { isBuildServer, isProdBuild, taskName } from '../../config/build.config'\nimport { renderSync } from 'node-sass'\nimport { unlinkSync, writeFileSync, readFileSync } from 'fs'\nimport { sync as mkdirp } from 'mkdirp'\nimport { sync as glob } from 'glob'\nimport { ConfigurationTransformer } from '../../plugins/web-index'\nimport * as cleanCss from 'clean-css'\nimport hash = require('string-hash')\n\nSparky.task(taskName(__filename), () => {\n  let src\n  mkdirp('./dist/css')\n\n  const sass = () => {\n    const result = renderSync({\n      file: './src/client/styles/main.scss',\n    })\n    const hashed = hash(result.css.toString())\n\n    return {\n      css: result.css,\n      hashed\n    }\n  }\n\n  const process = () => {\n    const _sass = sass()\n    const name = 'main.css'\n\n    glob('./dist/css/main-*').forEach(file => unlinkSync(file))\n    writeFileSync(`./dist/css/${name}`, _sass.css)\n    \n    const indexPath = './dist/index.html'\n    const index = readFileSync(indexPath)\n    const transformer = new ConfigurationTransformer();\n    const html = transformer.applyTransform([{\n      inHead: true,\n      order: 1,\n      element: 'style',\n      content: new cleanCss({}).minify(_sass.css.toString()).styles,\n      attributes: {\n        id: 'primary-styles'\n      }\n    }], index.toString());\n    writeFileSync(indexPath, html);\n  }\n\n  src = isProdBuild || isBuildServer\n    ? Sparky.src('src/client/styles/**/**/*.scss').file('main.scss', process)\n    : Sparky.watch('src/client/styles/**/**/*.scss').file('main.scss', process)\n\n  return src\n})\n"
  },
  {
    "path": "tools/tasks/seed/serve.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { taskName } from '../../config/build.config'\n\nSparky.task(taskName(__filename), [\n  'clean',\n  'mk-dist',\n  'config',\n  'index.copy',\n  // 'favicons',\n  'fonts',\n  'changelog',\n  'web',\n  'assets',\n  'sass',\n  'build.universal',\n  'ngsw',\n  'banner'\n], () => undefined)\n"
  },
  {
    "path": "tools/tasks/seed/web.ts",
    "content": "import { Sparky } from 'fuse-box'\nimport { BUILD_CONFIG, taskName } from '../../config/build.config'\n\nSparky.task(taskName(__filename), () =>\n  Sparky.src('web/**/*.*', { base: `${BUILD_CONFIG.toolsDir}` })\n    .dest(`./${BUILD_CONFIG.outputDir}`))\n"
  },
  {
    "path": "tools/test/jest.e2e-setup.ts",
    "content": "import * as Nightmare from 'nightmare'\njasmine.DEFAULT_TIMEOUT_INTERVAL = 125000\n\n// tslint:disable:no-require-imports\nconst browser = require('nightmare')({\n  show: false\n}) as Nightmare\n\nconst baseUrl = 'http://localhost:8000'\n\nexport { browser, baseUrl }\n"
  },
  {
    "path": "tools/test/jest.mocks.ts",
    "content": "const mock = () => {\n  let storage = {};\n  return {\n    getItem: (key: string) => key in storage ? (<any>storage)[key] : null,\n    setItem: (key: string, value: any) => (<any>storage)[key] = value || '',\n    removeItem: (key: string) => delete (<any>storage)[key],\n    clear: () => storage = {},\n  };\n};\n// Object.defineProperty(window, 'Hammer', { value: {} });\nObject.defineProperty(window, 'CSS', { value: mock() });\nObject.defineProperty(window, 'matchMedia', { value: jest.fn(() => ({ matches: true })) });\nObject.defineProperty(window, 'localStorage', { value: mock() });\nObject.defineProperty(window, 'sessionStorage', { value: mock() });\nObject.defineProperty(window, 'getComputedStyle', {\n  value: () => {\n    return {\n      display: 'none',\n      appearance: ['-webkit-appearance']\n    };\n  }\n});\n\n// For Angular Material\nObject.defineProperty(document.body.style, 'transform', {\n  value: () => {\n    return {\n      enumerable: true,\n      configurable: true\n    };\n  },\n});\n\n// For Angular Material\n(window as any).Hammer = require('hammerjs')\n"
  },
  {
    "path": "tools/test/jest.setup.ts",
    "content": "import 'jest-preset-angular';\nimport './jest.mocks';\n"
  },
  {
    "path": "tools/tslint-rules/validateDecoratorsRule.ts",
    "content": "import * as path from 'path';\nimport * as ts from 'typescript';\nimport * as Lint from 'tslint';\nimport * as minimatch from 'minimatch';\n\n/**\n * Rule that enforces certain decorator properties to be defined and to match a pattern.\n * Properties can be forbidden by prefixing their name with a `!`.\n * Supports whitelisting files via the third argument. E.g.\n *\n * ```\n * \"validate-decorators\": [true, {\n *   \"Component\": {\n *     \"encapsulation\": \"\\\\.None$\",\n *     \"!styles\": \".*\"\n *   }\n * }, \"src/lib\"]\n * ```\n */\nexport class Rule extends Lint.Rules.AbstractRule {\n  apply(sourceFile: ts.SourceFile) {\n    return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));\n  }\n}\n\n/** Represents a set of required and forbidden decorator properties. */\ntype DecoratorRuleSet = {\n  required:  {[key: string]: RegExp},\n  forbidden:  {[key: string]: RegExp},\n};\n\n/** Represents a map between decorator names and rule sets. */\ntype DecoratorRules = {\n  [key: string]: DecoratorRuleSet\n};\n\nclass Walker extends Lint.RuleWalker {\n  // Whether the file should be checked at all.\n  private _enabled: boolean;\n\n  // Rules that will be used to validate the decorators.\n  private _rules: DecoratorRules;\n\n  constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) {\n    super(sourceFile, options);\n\n    // Globs that are used to determine which files to lint.\n    const fileGlobs = options.ruleArguments.slice(1) || [];\n\n    // Relative path for the current TypeScript source file.\n    const relativeFilePath = path.relative(process.cwd(), sourceFile.fileName);\n\n    this._rules = this._generateRules(options.ruleArguments[0]);\n    this._enabled = Object.keys(this._rules).length > 0 &&\n                    fileGlobs.some(p => minimatch(relativeFilePath, p));\n  }\n\n  visitClassDeclaration(node: ts.ClassDeclaration) {\n    if (this._enabled && node.decorators) {\n      node.decorators\n        .map(decorator => decorator.expression as any)\n        .filter(expression => expression.arguments.length && expression.arguments[0].properties)\n        .forEach(expression => this._validatedDecorator(expression));\n    }\n\n    super.visitClassDeclaration(node);\n  }\n\n  /**\n   * Validates that a decorator matches all of the defined rules.\n   * @param decorator Decorator to be checked.\n   */\n  private _validatedDecorator(decorator: any) {\n    // Get the rules that are relevant for the current decorator.\n    const rules = this._rules[decorator.expression.getText()];\n\n    // Don't do anything if there are no rules.\n    if (!rules) {\n      return;\n    }\n\n    // Extract the property names and values.\n    const props = decorator.arguments[0].properties.map((node: ts.PropertyAssignment) => ({\n      name: node.name.getText(),\n      value: node.initializer.getText(),\n      node\n    }));\n\n    // Find all of the required rule properties that are missing from the decorator.\n    const missing = Object.keys(rules.required)\n        .filter(key => !props.find((prop: any) => prop.name === key));\n\n    if (missing.length) {\n      // Exit early if any of the properties are missing.\n      this.addFailureAtNode(decorator.parent, 'Missing required properties: ' + missing.join(', '));\n    } else {\n      // If all the necessary properties are defined, ensure that\n      // they match the pattern and aren't in the forbidden list.\n      props\n        .filter((prop: any) => rules.required[prop.name] || rules.forbidden[prop.name])\n        .forEach((prop: any) => {\n          const {name, value, node} = prop;\n          const requiredPattern = rules.required[name];\n          const forbiddenPattern = rules.forbidden[name];\n\n          if (requiredPattern && !requiredPattern.test(value)) {\n            this.addFailureAtNode(node, `Invalid value for property. ` +\n                                        `Expected value to match \"${requiredPattern}\".`);\n          } else if (forbiddenPattern && forbiddenPattern.test(value)) {\n            this.addFailureAtNode(node, `Property value not allowed. ` +\n                                        `Value should not match \"${forbiddenPattern}\".`);\n          }\n        });\n    }\n  }\n\n  /**\n   * Cleans out the blank rules that are passed through the tslint.json\n   * and converts the string patterns into regular expressions.\n   * @param config Config object passed in via the tslint.json.\n   * @returns Sanitized rules.\n   */\n  private _generateRules(config: {[key: string]: {[key: string]: string}}): DecoratorRules {\n    const output: DecoratorRules = {};\n\n    if (config) {\n      Object.keys(config)\n        .filter(decoratorName => Object.keys(config[decoratorName]).length > 0)\n        .forEach(decoratorName => {\n          output[decoratorName] = Object.keys(config[decoratorName]).reduce((accumulator, prop) => {\n            const isForbidden = prop.startsWith('!');\n            const cleanName = isForbidden ? prop.slice(1) : prop;\n            const pattern = new RegExp(config[decoratorName][prop]);\n\n            if (isForbidden) {\n              accumulator.forbidden[cleanName] = pattern;\n            } else {\n              accumulator.required[cleanName] = pattern;\n            }\n\n            return accumulator;\n          }, {required: {}, forbidden: {}} as DecoratorRuleSet);\n        });\n    }\n\n    return output;\n  }\n}"
  },
  {
    "path": "tools/web/ping.html",
    "content": "Ok"
  },
  {
    "path": "tools/web/robots.txt",
    "content": "User-agent: *\nAllow: /"
  },
  {
    "path": "tsconfig-aot.json",
    "content": "{\n    \"compilerOptions\": {\n        \"target\": \"es5\",\n        \"module\": \"es2015\",\n        \"moduleResolution\": \"node\",\n        \"sourceMap\": true,\n        \"emitDecoratorMetadata\": true,\n        \"experimentalDecorators\": true,\n        \"lib\": [\n            \"es2015\",\n            \"dom\"\n        ],\n        \"noImplicitAny\": true,\n        \"suppressImplicitAnyIndexErrors\": true,\n        \"typeRoots\": [\n            \"./node_modules/@types/\",\n            \"./node_modules/@angular/material/\"\n        ],\n        \"outDir\": \".ngc\"\n    },\n    \"include\": [\n        \"./src/client/main.ts\",\n        \"./src/client/app/**/*.module.ts\"\n    ],\n    \"angularCompilerOptions\": {\n        \"genDir\": \"src/client/.aot\",\n        \"skipMetadataEmit\": true\n    }\n}"
  },
  {
    "path": "tsconfig-e2e.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2015\",\n    \"module\": \"commonjs\",\n    \"declaration\": false,\n    \"removeComments\": true,\n    \"noLib\": false,\n    \"emitDecoratorMetadata\": true,\n    \"experimentalDecorators\": true,\n    \"sourceMap\": true,\n    \"pretty\": true,\n    \"allowUnreachableCode\": false,\n    \"allowUnusedLabels\": false,\n    \"noImplicitAny\": false,\n    \"noImplicitReturns\": true,\n    \"noImplicitUseStrict\": false,\n    \"noFallthroughCasesInSwitch\": true,\n    \"typeRoots\": [\n      \"../../node_modules/@types\",\n      \"../../node_modules\"\n    ],\n    \"outDir\": \".e2e\",\n    \"types\": [\n      \"node\",\n      \"protractor\",\n      \"jest\"\n    ]\n  },\n  \"include\": [\n    \"./src/client/app/**/*e2e-spec.ts\"\n  ],\n  \"compileOnSave\": false\n}"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"isolatedModules\": false,\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true,\n    \"declaration\": false,\n    \"noImplicitAny\": true,\n    \"noImplicitUseStrict\": false,\n    \"strictNullChecks\": true,\n    \"noEmitHelpers\": false,\n    \"noLib\": false,\n    \"noUnusedLocals\": true,\n    \"outDir\": \"dist/\",\n    \"allowSyntheticDefaultImports\": false,\n    \"sourceMap\": true,\n    \"lib\": [\"es6\", \"dom\"],\n    \"skipLibCheck\": true,\n    \"skipDefaultLibCheck\": true\n  },\n  \"exclude\": [\n    \"./.vscode\",\n    \"./.fusebox\",\n    \"./.ngc\",\n    \"./dist\",\n    \"./node_modules\",\n    \"./coverage\"\n  ]\n}"
  }
]