diff --git a/.circleci/authReactLitestar.sh b/.circleci/authReactLitestar.sh new file mode 100755 index 000000000..472d74b2c --- /dev/null +++ b/.circleci/authReactLitestar.sh @@ -0,0 +1,103 @@ +echo "Starting tests for FDI $1"; + +if [ -z "$SUPERTOKENS_API_KEY" ]; then + echo "SUPERTOKENS_API_KEY not set" + exit 1 +fi +frontendDriverVersion=$1 +frontendDriverVersion=`echo $frontendDriverVersion | tr -d '"'` +if [[ $frontendDriverVersion == '1.0' ]]; then + exit 0 +fi + +coreDriverJson=`cat ../coreDriverInterfaceSupported.json` +coreDriverLength=`echo $coreDriverJson | jq ".versions | length"` +coreDriverArray=`echo $coreDriverJson | jq ".versions"` +coreDriverVersion=`echo $coreDriverArray | jq ". | last"` +coreDriverVersion=`echo $coreDriverVersion | tr -d '"'` +coreFree=`curl -s -X GET \ +"https://api.supertokens.io/0/core-driver-interface/dependency/core/latest?password=$SUPERTOKENS_API_KEY&planType=FREE&mode=DEV&version=$coreDriverVersion&driverName=python" \ +-H 'api-version: 1'` +if [[ `echo $coreFree | jq .core` == "null" ]] +then + echo "fetching latest X.Y version for core given core-driver-interface X.Y version: $coreDriverVersion, planType: FREE gave response: $coreFree. Please make sure all relevant cores have been pushed." + exit 1 +fi +coreFree=$(echo $coreFree | jq .core | tr -d '"') + + +frontendVersionXY=`curl -s -X GET \ +"https://api.supertokens.io/0/frontend-driver-interface/dependency/frontend/latest?password=$SUPERTOKENS_API_KEY&frontendName=website&mode=DEV&version=$frontendDriverVersion&driverName=python" \ +-H 'api-version: 1'` +if [[ `echo $frontendVersionXY | jq .frontend` == "null" ]] +then + echo "fetching latest X.Y version for frontend given frontend-driver-interface X.Y version: $frontendDriverVersion, name: webiste gave response: $frontend. Please make sure all relevant versions have been pushed." + exit 1 +fi +frontendVersionXY=$(echo $frontendVersionXY | jq .frontend | tr -d '"') + +frontendInfo=`curl -s -X GET \ +"https://api.supertokens.io/0/driver/latest?password=$SUPERTOKENS_API_KEY&mode=DEV&version=$frontendVersionXY&name=website" \ +-H 'api-version: 0'` +if [[ `echo $frontendInfo | jq .tag` == "null" ]] +then + echo "fetching latest X.Y.Z version for frontend, X.Y version: $frontendVersionXY gave response: $frontendInfo" + exit 1 +fi +frontendTag=$(echo $frontendInfo | jq .tag | tr -d '"') +frontendVersion=$(echo $frontendInfo | jq .version | tr -d '"') + +nodeVersionXY=`curl -s -X GET \ +"https://api.supertokens.io/0/frontend-driver-interface/dependency/driver/latest?password=$SUPERTOKENS_API_KEY&mode=DEV&version=$frontendDriverVersion&driverName=node&frontendName=auth-react" \ +-H 'api-version: 1'` +if [[ `echo $nodeVersionXY | jq .driver` == "null" ]] +then + echo "fetching latest X.Y version for driver given frontend-driver-interface X.Y version: $frontendDriverVersion gave response: $nodeVersionXY. Please make sure all relevant drivers have been pushed." + exit 1 +fi +nodeVersionXY=$(echo $nodeVersionXY | jq .driver | tr -d '"') + +nodeInfo=`curl -s -X GET \ +"https://api.supertokens.io/0/driver/latest?password=$SUPERTOKENS_API_KEY&mode=DEV&version=$nodeVersionXY&name=node" \ +-H 'api-version: 0'` +if [[ `echo $nodeInfo | jq .tag` == "null" ]] +then + echo "fetching latest X.Y.Z version for driver, X.Y version: $nodeVersionXY gave response: $nodeInfo" + exit 1 +fi +nodeTag=$(echo $nodeInfo | jq .tag | tr -d '"') + +frontendAuthReactVersionXY=`curl -s -X GET \ +"https://api.supertokens.io/0/frontend-driver-interface/dependency/frontend/latest?password=$SUPERTOKENS_API_KEY&frontendName=auth-react&mode=DEV&version=$frontendDriverVersion&driverName=python" \ +-H 'api-version: 1'` +if [[ `echo $frontendAuthReactVersionXY | jq .frontend` == "null" ]] +then + echo "fetching latest X.Y version for frontend given frontend-driver-interface X.Y version: $frontendDriverVersion, name: auth-react gave response: $frontend. Please make sure all relevant frontend libs have been pushed." + exit 1 +fi +frontendAuthReactVersionXY=$(echo $frontendAuthReactVersionXY | jq .frontend | tr -d '"') + +frontendAuthReactInfo=`curl -s -X GET \ +"https://api.supertokens.io/0/driver/latest?password=$SUPERTOKENS_API_KEY&mode=DEV&version=$frontendAuthReactVersionXY&name=auth-react" \ +-H 'api-version: 0'` +if [[ `echo $frontendAuthReactInfo | jq .tag` == "null" ]] +then + echo "fetching latest X.Y.Z version for frontend, X.Y version: $frontendAuthReactVersionXY gave response: $frontendAuthReactInfo" + exit 1 +fi +frontendAuthReactTag=$(echo $frontendAuthReactInfo | jq .tag | tr -d '"') +frontendAuthReactVersion=$(echo $frontendAuthReactInfo | jq .version | tr -d '"') + +if [[ $frontendDriverVersion == '1.3' || $frontendDriverVersion == '1.8' ]]; then + # we skip this since the tests for auth-react here are not reliable due to race conditions... + + # we skip 1.8 since the SDK with just 1.8 doesn't have the right scripts + exit 0 +else + ./setupAndTestWithAuthReactWithLitestar.sh $coreFree $frontendAuthReactTag $nodeTag + if [[ $? -ne 0 ]] + then + echo "test failed for auth-react tests... exiting!" + exit 1 + fi +fi diff --git a/.circleci/config_continue.yml b/.circleci/config_continue.yml index 66f414eaa..f70ac7348 100644 --- a/.circleci/config_continue.yml +++ b/.circleci/config_continue.yml @@ -76,6 +76,28 @@ jobs: - run: make with-fastapi - run: (cd .circleci/ && ./websiteFastApi.sh << parameters.fdi-version >>) - slack/status + test-website-litestar: + docker: + - image: python:<< parameters.python-version >> + - image: rishabhpoddar/oauth-server-cicd + resource_class: large + parameters: + fdi-version: + type: string + python-version: + type: string + parallelism: 4 + steps: + - checkout + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) + - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ + - run: echo "127.0.0.1 localhost.org" >> /etc/hosts + - run: python3 -m pip install pip setuptools --upgrade + - run: make with-litestar + - run: (cd .circleci/ && ./websiteLitestar.sh << parameters.fdi-version >>) + - slack/status test-website-flask: docker: - image: python:<< parameters.python-version >> @@ -309,6 +331,39 @@ jobs: - store_artifacts: path: ~/test_report/react-logs destination: react-logs + test-authreact-litestar: + docker: + - image: python:<< parameters.python-version >> + - image: rishabhpoddar/oauth-server-cicd + resource_class: large + environment: + MOCHA_FILE: /root/test_report/report_node-<< parameters.fdi-version >>.xml + parameters: + fdi-version: + type: string + python-version: + type: string + parallelism: 4 + steps: + - checkout + - attach_workspace: + at: / + - run: apt update && apt install -y bc jq lsof libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm1 libxss1 libasound2 libxshmfence1 xvfb + - run: (cd .circleci/ && ./installJava.sh) + - run: (cd .circleci/ && ./installNode.sh 20) + - run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ + - run: echo "127.0.0.1 localhost.org" >> /etc/hosts + - run: python3 -m pip install pip setuptools --upgrade + - run: make with-litestar + - run: (cd .circleci && ./authReactLitestar.sh << parameters.fdi-version >>) + - store_test_results: + path: /root/test_report/report_node-<< parameters.fdi-version >>.xml + - store_artifacts: + path: ~/test_report/screenshots + destination: screenshots + - store_artifacts: + path: ~/test_report/react-logs + destination: react-logs test-success: docker: - image: rishabhpoddar/supertokens_python_driver_testing @@ -370,6 +425,20 @@ workflows: only: /dev-v[0-9]+(\.[0-9]+)*/ branches: only: /test-cicd\/.*/ + - test-website-litestar: + requires: + - test-dev-tag-as-not-passed + context: + - slack-notification + matrix: + parameters: + fdi-version: placeholder + python-version: ['3.8', '3.13'] + filters: + tags: + only: /dev-v[0-9]+(\.[0-9]+)*/ + branches: + only: /test-cicd\/.*/ - test-website-flask: requires: - test-dev-tag-as-not-passed @@ -482,11 +551,26 @@ workflows: parameters: fdi-version: placeholder python-version: ['3.8', '3.13'] + - test-authreact-litestar: + requires: + - test-dev-tag-as-not-passed + context: + - slack-notification + filters: + tags: + only: /dev-v[0-9]+(\.[0-9]+)*/ + branches: + only: /test-cicd\/.*/ + matrix: + parameters: + fdi-version: placeholder + python-version: ['3.8', '3.13'] - test-success: requires: - test-unit - test-backend-sdk-testing - test-website-fastapi + - test-website-litestar - test-website-flask - test-website-django - test-website-drf-async @@ -495,6 +579,7 @@ workflows: - test-authreact-fastapi - test-authreact-flask - test-authreact-django + - test-authreact-litestar context: - slack-notification filters: diff --git a/.circleci/setupAndTestWithAuthReactWithLitestar.sh b/.circleci/setupAndTestWithAuthReactWithLitestar.sh new file mode 100755 index 000000000..e69de29bb diff --git a/.circleci/websiteLitestar.sh b/.circleci/websiteLitestar.sh new file mode 100755 index 000000000..f645fb95a --- /dev/null +++ b/.circleci/websiteLitestar.sh @@ -0,0 +1,99 @@ +coreDriverJson=`cat ../coreDriverInterfaceSupported.json` +coreDriverLength=`echo $coreDriverJson | jq ".versions | length"` +coreDriverArray=`echo $coreDriverJson | jq ".versions"` +echo "got core driver relations" + +# get driver version +version=`cat ../setup.py | grep -e 'version='` +while IFS='"' read -ra ADDR; do + counter=0 + for i in "${ADDR[@]}"; do + if [ $counter == 1 ] + then + version=$i + fi + counter=$(($counter+1)) + done +done <<< "$version" + +someFrontendTestsRan=false +i=0 +coreDriverVersion=`echo $coreDriverArray | jq ". | last"` +coreDriverVersion=`echo $coreDriverVersion | tr -d '"'` +coreFree=`curl -s -X GET \ +"https://api.supertokens.io/0/core-driver-interface/dependency/core/latest?password=$SUPERTOKENS_API_KEY&planType=FREE&mode=DEV&version=$coreDriverVersion&driverName=python" \ +-H 'api-version: 1'` +if [[ `echo $coreFree | jq .core` == "null" ]] +then + echo "fetching latest X.Y version for core given core-driver-interface X.Y version: $coreDriverVersion, planType: FREE gave response: $coreFree. Please make sure all relevant cores have been pushed." + exit 1 +fi +coreFree=$(echo $coreFree | jq .core | tr -d '"') + + +frontendDriverVersion=$1 +frontendDriverVersion=`echo $frontendDriverVersion | tr -d '"'` + +frontendVersionXY=`curl -s -X GET \ +"https://api.supertokens.io/0/frontend-driver-interface/dependency/frontend/latest?password=$SUPERTOKENS_API_KEY&frontendName=website&mode=DEV&version=$frontendDriverVersion&driverName=node" \ +-H 'api-version: 1'` +if [[ `echo $frontendVersionXY | jq .frontend` == "null" ]] +then + echo "fetching latest X.Y version for frontend given frontend-driver-interface X.Y version: $frontendDriverVersion, name: website gave response: $frontend. Please make sure all relevant versions have been pushed." + exit 1 +fi +frontendVersionXY=$(echo $frontendVersionXY | jq .frontend | tr -d '"') + +frontendInfo=`curl -s -X GET \ +"https://api.supertokens.io/0/frontend/latest?password=$SUPERTOKENS_API_KEY&mode=DEV&version=$frontendVersionXY&name=website" \ +-H 'api-version: 0'` +if [[ `echo $frontendInfo | jq .tag` == "null" ]] +then + echo "fetching latest X.Y.Z version for frontend, X.Y version: $frontendVersionXY gave response: $frontendInfo" + exit 1 +fi +frontendTag=$(echo $frontendInfo | jq .tag | tr -d '"') +frontendVersion=$(echo $frontendInfo | jq .version | tr -d '"') + +nodeVersionXY=`curl -s -X GET \ +"https://api.supertokens.io/0/frontend-driver-interface/dependency/driver/latest?password=$SUPERTOKENS_API_KEY&mode=DEV&version=$frontendDriverVersion&driverName=node&frontendName=website" \ +-H 'api-version: 1'` +if [[ `echo $nodeVersionXY | jq .driver` == "null" ]] +then + echo "fetching latest X.Y version for driver given frontend-driver-interface X.Y version: $frontendDriverVersion gave response: $nodeVersionXY. Please make sure all relevant drivers have been pushed." + exit 1 +fi +nodeVersionXY=$(echo $nodeVersionXY | jq .driver | tr -d '"') + +nodeInfo=`curl -s -X GET \ +"https://api.supertokens.io/0/driver/latest?password=$SUPERTOKENS_API_KEY&mode=DEV&version=$nodeVersionXY&name=node" \ +-H 'api-version: 0'` +if [[ `echo $nodeInfo | jq .tag` == "null" ]] +then + echo "fetching latest X.Y.Z version for driver, X.Y version: $nodeVersionXY gave response: $nodeInfo" + exit 1 +fi +nodeTag=$(echo $nodeInfo | jq .tag | tr -d '"') + +tries=1 +while [ $tries -le 3 ] +do + tries=$(( $tries + 1 )) + ./setupAndTestWithFrontend.sh $coreFree $frontendTag $nodeTag + if [[ $? -ne 0 ]] + then + if [[ $tries -le 3 ]] + then + rm -rf ../../supertokens-root + rm -rf ../../supertokens-website + echo "failed test.. retrying!" + else + echo "test failed for website tests... exiting!" + exit 1 + fi + else + rm -rf ../../supertokens-root + rm -rf ../../supertokens-website + break + fi +done diff --git a/.github/workflows/auth-react-test-1-litestar.yml b/.github/workflows/auth-react-test-1-litestar.yml new file mode 100644 index 000000000..1a08a4ec6 --- /dev/null +++ b/.github/workflows/auth-react-test-1-litestar.yml @@ -0,0 +1,113 @@ +name: Auth-React Tests - L1 - Litestar + +on: + pull_request: + types: + - opened + - reopened + - synchronize + push: + tags: + - dev-v[0-9]+.[0-9]+.[0-9]+ + +# Only one instance of this workflow will run on the same ref (PR/Branch/Tag) +# Previous runs will be cancelled. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + define-versions: + runs-on: ubuntu-latest + outputs: + fdiVersions: ${{ steps.versions.outputs.fdiVersions }} + cdiVersions: ${{ steps.versions.outputs.cdiVersions }} + pyVersions: '["3.8", "3.13"]' + nodeFdiVersionMap: ${{ steps.node-versions.outputs.fdiVersions }} + authReactFdiVersionMap: ${{ steps.auth-react-versions.outputs.fdiVersions }} + steps: + - uses: actions/checkout@v4 + + - uses: supertokens/get-supported-versions-action@main + id: versions + with: + has-fdi: true + has-cdi: true + + - uses: supertokens/actions/get-versions-from-repo@main + id: auth-react-versions + with: + repo: supertokens-auth-react + github-token: ${{ secrets.GITHUB_TOKEN }} + fdi-versions: ${{ steps.versions.outputs.fdiVersions }} + + - uses: supertokens/actions/get-versions-from-repo@main + id: node-versions + with: + repo: supertokens-node + github-token: ${{ secrets.GITHUB_TOKEN }} + fdi-versions: ${{ steps.versions.outputs.fdiVersions }} + + setup-auth-react: + runs-on: ubuntu-latest + needs: define-versions + strategy: + fail-fast: false + matrix: + fdi-version: ${{ fromJSON(needs.define-versions.outputs.fdiVersions) }} + + outputs: + AUTH_REACT__LOG_DIR: ${{ steps.envs.outputs.AUTH_REACT__LOG_DIR }} + AUTH_REACT__SCREENSHOT_DIR: ${{ steps.envs.outputs.AUTH_REACT__SCREENSHOT_DIR }} + AUTH_REACT__APP_SERVER: ${{ steps.envs.outputs.AUTH_REACT__APP_SERVER }} + AUTH_REACT__NODE_PORT: ${{ steps.envs.outputs.AUTH_REACT__NODE_PORT }} + AUTH_REACT__TEST_MODE: ${{ steps.envs.outputs.AUTH_REACT__TEST_MODE }} + AUTH_REACT__PORT: ${{ steps.envs.outputs.AUTH_REACT__PORT }} + specs: ${{ steps.envs.outputs.specs }} + matrix: ${{ steps.setup-matrix.outputs.matrix }} + + steps: + - name: Get node and auth-react versions for FDI + id: versions + run: | + nodeVersion=$( echo '${{ needs.define-versions.outputs.nodeFdiVersionMap }}' | jq -r '.["${{ matrix.fdi-version }}"]' ) + authReactVersion=$( echo '${{ needs.define-versions.outputs.authReactFdiVersionMap }}' | jq -r '.["${{ matrix.fdi-version }}"]' ) + + echo "nodeVersion=${nodeVersion}" >> $GITHUB_OUTPUT + echo "authReactVersion=${authReactVersion}" >> $GITHUB_OUTPUT + + - uses: supertokens/auth-react-testing-action/setup@main + id: envs + with: + auth-react-version: ${{ steps.versions.outputs.authReactVersion }} + node-sdk-version: ${{ steps.versions.outputs.nodeVersion }} + fdi-version: ${{ matrix.fdi-version }} + + - id: setup-matrix + uses: supertokens/extended-matrix-action@main + with: + artifact-id: ${{ matrix.fdi-version }} + matrix: | + py-version: ${{ needs.define-versions.outputs.pyVersions }} + framework: ["litestar"] + spec: ${{ steps.envs.outputs.specs }} + + launch-fdi-workflows: + uses: ./.github/workflows/auth-react-test-2.yml + needs: + - define-versions + - setup-auth-react + strategy: + max-parallel: 1 # This is important to avoid ddos GHA API + fail-fast: false # Don't fail fast to avoid locking TF State + matrix: + fdi-version: ${{ fromJSON(needs.define-versions.outputs.fdiVersions) }} + name: FDI ${{ matrix.fdi-version }} + with: + artifact-id: ${{ matrix.fdi-version }} + AUTH_REACT__LOG_DIR: ${{ needs.setup-auth-react.outputs.AUTH_REACT__LOG_DIR }} + AUTH_REACT__SCREENSHOT_DIR: ${{ needs.setup-auth-react.outputs.AUTH_REACT__SCREENSHOT_DIR }} + AUTH_REACT__APP_SERVER: ${{ needs.setup-auth-react.outputs.AUTH_REACT__APP_SERVER }} + AUTH_REACT__NODE_PORT: ${{ needs.setup-auth-react.outputs.AUTH_REACT__NODE_PORT }} + AUTH_REACT__TEST_MODE: ${{ needs.setup-auth-react.outputs.AUTH_REACT__TEST_MODE }} + AUTH_REACT__PORT: ${{ needs.setup-auth-react.outputs.AUTH_REACT__PORT }} diff --git a/.github/workflows/auth-react-test-3.yml b/.github/workflows/auth-react-test-3.yml index 0736f1d78..0d9b45634 100644 --- a/.github/workflows/auth-react-test-3.yml +++ b/.github/workflows/auth-react-test-3.yml @@ -144,6 +144,17 @@ jobs: mkdir -p $AUTH_REACT__LOG_DIR python3 app.py --port 8083 &> $AUTH_REACT__LOG_DIR/flask.log & + - name: Start Server (litestar) + if: matrix.framework == 'litestar' + working-directory: supertokens-python + run: | + source venv/bin/activate + export PYTHONPATH="${PYTHONPATH}:$(pwd)" + cd tests/auth-react/litestar-server + + mkdir -p $AUTH_REACT__LOG_DIR + litestar run --host 0.0.0.0 --port 8083 &> $AUTH_REACT__LOG_DIR/litestar.log & + - uses: supertokens/auth-react-testing-action@main with: fdi-version: ${{ inputs.fdi-version }} diff --git a/.github/workflows/website-test.yml b/.github/workflows/website-test.yml index dfe99e5ca..e0418f22a 100644 --- a/.github/workflows/website-test.yml +++ b/.github/workflows/website-test.yml @@ -72,6 +72,7 @@ jobs: - fastapi - flask - flask-nest-asyncio + - litestar steps: - name: Setup ENVs @@ -213,6 +214,19 @@ jobs: python3 app.py --port 8080 &> ${{ steps.envs.outputs.APP_SERVER_LOG_DIR }}/app-server.log & python3 app.py --port 8082 &> ${{ steps.envs.outputs.APP_SERVER_LOG_DIR }}/app-server-cross-domain.log & + - name: Install dependencies and start servers (litestar) + if: matrix.framework == 'litestar' + working-directory: supertokens-python + run: | + source venv/bin/activate + make with-litestar + + export PYTHONPATH="${PYTHONPATH}:$(pwd)" + cd tests/frontendIntegration/litestar-server + + litestar run --host 0.0.0.0 --port 8080 &> ${{ steps.envs.outputs.APP_SERVER_LOG_DIR }}/app-server.log & + litestar run --host 0.0.0.0 --port 8082 &> ${{ steps.envs.outputs.APP_SERVER_LOG_DIR }}/app-server-cross-domain.log & + - uses: supertokens/website-testing-action@main with: version: ${{ steps.versions.outputs.websiteVersion }} diff --git a/.gitignore b/.gitignore index dc3be0ba6..07dddc351 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ sqlite.db .mypy_cache/ test-results/ .ruff_cache/ +.venv/ diff --git a/Makefile b/Makefile index 92c1382b1..8d24efcbd 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,9 @@ freeze-dev-requirements: with-fastapi: pip3 install -e .[fastapi] +with-litestar: + pip3 install -e .[litestar] + with-django: pip3 install -e .[django] diff --git a/dev-requirements.txt b/dev-requirements.txt index e39220586..6ad4b3eb2 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -22,4 +22,5 @@ requests-mock==1.12.1 respx>=0.13.0, <1.0.0 uvicorn==0.32.0 wasmtime==25.0.0 +litestar==2.18.0 -e . diff --git a/examples/with-litestar/with-thirdpartyemailpassword/README.md b/examples/with-litestar/with-thirdpartyemailpassword/README.md new file mode 100644 index 000000000..326c6f9f6 --- /dev/null +++ b/examples/with-litestar/with-thirdpartyemailpassword/README.md @@ -0,0 +1,33 @@ +![SuperTokens banner](https://raw.githubusercontent.com/supertokens/supertokens-logo/master/images/Artboard%20%E2%80%93%2027%402x.png) + +# Litestar example + +This is an example to show how to use supertokens-python library with Litestar framework and ThirdpartyEmailpassword recipe. + +This server code can be used to implement social auth using: +- Google +- Google Workspaces +- Github +- Apple +- Discord + +## Installation + +Before installing, use the script to create a virtual environment and to install all the required packages. +```bash +source create_env.sh +``` + +## Start web app + +```bash +uvicorn main:app --reload --host=0.0.0.0 --port=3001 +``` + +## Author + +Created with :heart: by the folks at supertokens.com. + +## License + +This project is licensed under the Apache 2.0 license. diff --git a/examples/with-litestar/with-thirdpartyemailpassword/create_env.sh b/examples/with-litestar/with-thirdpartyemailpassword/create_env.sh new file mode 100755 index 000000000..9e24ce5fe --- /dev/null +++ b/examples/with-litestar/with-thirdpartyemailpassword/create_env.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +pip install virtualenv +#Create a virtualenv +virtualenv litestar_example +# shellcheck disable=SC2164 +source litestar_example/bin/activate + +pip install -r requirements.txt diff --git a/examples/with-litestar/with-thirdpartyemailpassword/main.py b/examples/with-litestar/with-thirdpartyemailpassword/main.py new file mode 100644 index 000000000..f85149405 --- /dev/null +++ b/examples/with-litestar/with-thirdpartyemailpassword/main.py @@ -0,0 +1,194 @@ +import os +from typing import Any + +import uvicorn # type: ignore +from dotenv import load_dotenv +from litestar import Litestar, MediaType, Response, get +from litestar.config.cors import CORSConfig +from litestar.di import Provide +from supertokens_python import ( + InputAppInfo, + SupertokensConfig, + get_all_cors_headers, + init, +) +from supertokens_python.framework.litestar import ( + create_supertokens_middleware, + get_supertokens_plugin, +) +from supertokens_python.recipe import ( + dashboard, + emailverification, + session, + thirdparty, + usermetadata, +) +from supertokens_python.recipe.session import SessionContainer +from supertokens_python.recipe.session.framework.litestar import verify_session + +load_dotenv() + + +def get_api_port(): + return 3001 + + +def get_website_port(): + return "3000" + + +def get_website_domain(): + return "http://localhost:" + get_website_port() + + +init( + supertokens_config=SupertokensConfig(connection_uri="https://try.supertokens.io"), + app_info=InputAppInfo( + app_name="Supertokens", + api_domain="http://localhost:" + str(get_api_port()), + website_domain=get_website_domain(), + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init(), + dashboard.init(), + emailverification.init("REQUIRED"), + usermetadata.init(), + thirdparty.init( + sign_in_and_up_feature=thirdparty.SignInAndUpFeature( + providers=[ + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="google", + clients=[ + thirdparty.ProviderClientConfig( + client_id=os.environ["GOOGLE_CLIENT_ID"], + client_secret=os.environ["GOOGLE_CLIENT_SECRET"], + client_type="web", + ), + thirdparty.ProviderClientConfig( + client_id=os.environ["GOOGLE_CLIENT_ID_MOBILE"], + client_secret=os.environ[ + "GOOGLE_CLIENT_SECRET_MOBILE" + ], + client_type="mobile", + ), + ], + ), + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="github", + clients=[ + thirdparty.ProviderClientConfig( + client_id=os.environ["GITHUB_CLIENT_ID"], + client_secret=os.environ["GITHUB_CLIENT_SECRET"], + client_type="web", + ), + thirdparty.ProviderClientConfig( + client_id=os.environ["GITHUB_CLIENT_ID_MOBILE"], + client_secret=os.environ[ + "GITHUB_CLIENT_SECRET_MOBILE" + ], + client_type="mobile", + ), + ], + ) + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="apple", + clients=[ + thirdparty.ProviderClientConfig( + client_id=os.environ["APPLE_CLIENT_ID"], + client_type="web", + additional_config={ + "keyId": os.environ["APPLE_KEY_ID"], + "teamId": os.environ["APPLE_TEAM_ID"], + "privateKey": os.environ["APPLE_PRIVATE_KEY"], + }, + ), + thirdparty.ProviderClientConfig( + client_id=os.environ["APPLE_CLIENT_ID_MOBILE"], + client_type="mobile", + additional_config={ + "keyId": os.environ["APPLE_KEY_ID"], + "teamId": os.environ["APPLE_TEAM_ID"], + "privateKey": os.environ["APPLE_PRIVATE_KEY"], + }, + ), + ], + ) + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="google-workspaces", + clients=[ + thirdparty.ProviderClientConfig( + client_id=os.environ["GOOGLE_WORKSPACES_CLIENT_ID"], + client_secret=os.environ[ + "GOOGLE_WORKSPACES_CLIENT_SECRET" + ], + ), + ], + ) + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="discord", + clients=[ + thirdparty.ProviderClientConfig( + client_id=os.environ["DISCORD_CLIENT_ID"], + client_secret=os.environ["DISCORD_CLIENT_SECRET"], + ), + ], + ) + ), + ] + ) + ), + ], + telemetry=False, +) + + +@get( + "/sessioninfo", + dependencies={"session": Provide(verify_session())}, +) +async def get_session_info(session: SessionContainer) -> Response[Any]: + return Response( + { + "sessionHandle": session.get_handle(), + "userId": session.get_user_id(), + "accessTokenPayload": session.get_access_token_payload(), + } + ) + + +def f_405(_, __: Exception): + return Response("", status_code=404, media_type=MediaType.TEXT) + + +cors = CORSConfig( + allow_origins=[get_website_domain()], + allow_credentials=True, + allow_methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"], + allow_headers=["Content-Type"] + get_all_cors_headers(), +) + +app = Litestar( + route_handlers=[ + get_session_info, + ], + cors_config=cors, + exception_handlers={ + Exception: f_405, + }, + middleware=[create_supertokens_middleware()], + plugins=[get_supertokens_plugin(mount_path="/auth")], +) + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=get_api_port()) # type: ignore diff --git a/examples/with-litestar/with-thirdpartyemailpassword/requirements.txt b/examples/with-litestar/with-thirdpartyemailpassword/requirements.txt new file mode 100644 index 000000000..2d718b9a7 --- /dev/null +++ b/examples/with-litestar/with-thirdpartyemailpassword/requirements.txt @@ -0,0 +1,4 @@ +litestar +uvicorn +python-dotenv +supertokens-python diff --git a/pyproject.toml b/pyproject.toml index 69efe4a1a..acd5168dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,9 @@ indent-style = "space" # Default typeCheckingMode = "strict" reportImportCycles = false include = ["supertokens_python/", "tests/", "examples/"] +exclude = [".venv"] +venvPath = "." +venv = ".venv" [tool.pytest.ini_options] addopts = " -v -p no:warnings" diff --git a/setup.py b/setup.py index d9e9dbfa2..afb33ed5d 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,13 @@ "python-dotenv==1.0.1", ] ), + "litestar": ( + [ + "litestar[all]", + "uvicorn", + "python-dotenv==1.0.1", + ] + ), "django": ( [ "django-cors-headers", diff --git a/supertokens_python/__init__.py b/supertokens_python/__init__.py index 43d3573b2..fa709d739 100644 --- a/supertokens_python/__init__.py +++ b/supertokens_python/__init__.py @@ -30,7 +30,7 @@ def init( app_info: InputAppInfo, - framework: Literal["fastapi", "flask", "django"], + framework: Literal["fastapi", "flask", "django", "litestar"], supertokens_config: SupertokensConfig, recipe_list: List[Callable[[supertokens.AppInfo], RecipeModule]], mode: Optional[Literal["asgi", "wsgi"]] = None, diff --git a/supertokens_python/framework/litestar/__init__.py b/supertokens_python/framework/litestar/__init__.py new file mode 100644 index 000000000..27421e974 --- /dev/null +++ b/supertokens_python/framework/litestar/__init__.py @@ -0,0 +1,28 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from .litestar_exception_handlers import ( + get_exception_handlers, + supertokens_exception_handler, +) +from .litestar_middleware import create_supertokens_middleware +from .litestar_plugin import SupertokensPlugin, get_supertokens_plugin + +__all__ = [ + "SupertokensPlugin", + "get_supertokens_plugin", + "create_supertokens_middleware", + "get_exception_handlers", + "supertokens_exception_handler", +] diff --git a/supertokens_python/framework/litestar/framework.py b/supertokens_python/framework/litestar/framework.py new file mode 100644 index 000000000..041ae1f1a --- /dev/null +++ b/supertokens_python/framework/litestar/framework.py @@ -0,0 +1,27 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from typing import Any + +from litestar import Request + +from supertokens_python.framework.types import Framework + + +class LitestarFramework(Framework): + def wrap_request(self, unwrapped: Request[Any, Any, Any]): + from supertokens_python.framework.litestar.litestar_request import ( + LitestarRequest, + ) + + return LitestarRequest(unwrapped) diff --git a/supertokens_python/framework/litestar/litestar_exception_handlers.py b/supertokens_python/framework/litestar/litestar_exception_handlers.py new file mode 100644 index 000000000..b1035c36c --- /dev/null +++ b/supertokens_python/framework/litestar/litestar_exception_handlers.py @@ -0,0 +1,132 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import asyncio +from typing import Any, Dict, Type, Union + +from litestar import Request, Response + +from supertokens_python.exceptions import SuperTokensError + + +def supertokens_exception_handler( + request: Request[Any, Any, Any], exc: SuperTokensError +) -> Response[Any]: + """ + Exception handler for SuperTokens errors in Litestar applications. + + This handler intercepts SuperTokens exceptions and converts them to proper + HTTP responses with session management. + + Note: This is a synchronous wrapper around async SuperTokens error handling. + Litestar requires exception handlers to be synchronous, so we run the async + logic in the event loop. + + Args: + request: The Litestar request object + exc: The SuperTokens exception + + Returns: + A Litestar Response object with proper status code and session cookies + """ + from supertokens_python import Supertokens + from supertokens_python.framework.litestar.litestar_request import LitestarRequest + from supertokens_python.framework.litestar.litestar_response import LitestarResponse + from supertokens_python.utils import default_user_context + + async def handle_async() -> Response[Any]: + """Async logic for handling the SuperTokens error""" + st = Supertokens.get_instance() + custom_request = LitestarRequest(request) + user_context = default_user_context(custom_request) + + # Create a response for SuperTokens to populate + response_obj = Response(content=None) + response = LitestarResponse(response_obj) + + # Handle the error through SuperTokens + # This will modify the response object with proper status code, + # clear tokens, and set appropriate headers + result = await st.handle_supertokens_error( + custom_request, + exc, + response, + user_context, + ) + + # Return the modified Litestar response + # The response object has been updated by SuperTokens error handlers + if isinstance(result, LitestarResponse): + litestar_response = result.response + + # Clear the session from request.state to prevent the middleware + # from re-applying session cookies after we've cleared them + if hasattr(request.state, "supertokens"): + delattr(request.state, "supertokens") + + # Litestar stores cookies separately from headers. When an exception + # handler returns a response, Litestar will automatically convert cookies + # to Set-Cookie headers. So we can just return the response as-is. + return litestar_response + + # Fallback to a generic error response + return Response( + content={"message": str(exc)}, + status_code=500, + ) + + # Run the async logic in the event loop + try: + loop = asyncio.get_running_loop() + except RuntimeError: + # No event loop running, create one + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + return loop.run_until_complete(handle_async()) + finally: + loop.close() + else: + # Event loop is already running (which is the case in Litestar) + # Create a task and run it + import nest_asyncio # type: ignore + + nest_asyncio.apply() # type: ignore + return loop.run_until_complete(handle_async()) + + +def get_exception_handlers() -> Dict[Union[int, Type[Exception]], Any]: + """ + Get exception handlers for SuperTokens errors. + + Returns: + A dictionary mapping exception types to handler functions. + + Example: + ```python + from litestar import Litestar + from supertokens_python.framework.litestar import ( + get_exception_handlers, + get_supertokens_plugin, + create_supertokens_middleware, + ) + + app = Litestar( + route_handlers=[...], + middleware=[create_supertokens_middleware()], + plugins=[get_supertokens_plugin(api_base_path="/auth")], + exception_handlers=get_exception_handlers(), + ) + ``` + """ + return {SuperTokensError: supertokens_exception_handler} # type: ignore diff --git a/supertokens_python/framework/litestar/litestar_middleware.py b/supertokens_python/framework/litestar/litestar_middleware.py new file mode 100644 index 000000000..aeca0f090 --- /dev/null +++ b/supertokens_python/framework/litestar/litestar_middleware.py @@ -0,0 +1,96 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from litestar.middleware import DefineMiddleware +from litestar.types import ASGIApp, Receive, Scope, Send + + +class SupertokensSessionMiddleware: + """ + Middleware to handle session management for non-auth routes. + + This middleware applies session-related response mutators (like setting cookies) + for routes that use SuperTokens sessions but aren't auth routes. + """ + + def __init__(self, app: ASGIApp) -> None: + self.app = app + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] != "http": + await self.app(scope, receive, send) + return + + from litestar import Request + from litestar import Response as LitestarResponseObj + from litestar.types import Message + + from supertokens_python.framework.litestar.litestar_request import ( + LitestarRequest, + ) + from supertokens_python.framework.litestar.litestar_response import ( + LitestarResponse, + ) + from supertokens_python.recipe.session import SessionContainer + from supertokens_python.supertokens import manage_session_post_response + from supertokens_python.utils import default_user_context + + request = Request(scope, receive=receive, send=send) # type: ignore + custom_request = LitestarRequest(request) + user_context = default_user_context(custom_request) + + async def send_wrapper(message: Message) -> None: + if message["type"] == "http.response.start": + # Apply session mutators to response headers + if hasattr(request.state, "supertokens") and isinstance( # type: ignore + getattr(request.state, "supertokens", None), # type: ignore + SessionContainer, + ): + # Create a temporary Litestar Response + temp_response = LitestarResponseObj(content=None) + + # Convert raw ASGI headers to dict for Litestar Response + for name, value in message.get("headers", []): + temp_response.headers[name.decode()] = value.decode() + + # Wrap it for SuperTokens + wrapped_response = LitestarResponse(temp_response) + + # Apply session mutators (this will modify temp_response) + manage_session_post_response( + getattr(request.state, "supertokens"), # type: ignore + wrapped_response, + user_context, + ) + + # Convert the Litestar Response to ASGI format to get cookies as Set-Cookie headers + asgi_response = temp_response.to_asgi_response( # type: ignore + app=None, request=Request(scope, receive=receive, send=send) + ) + + # Use the encoded headers which include Set-Cookie headers from cookies + message["headers"] = asgi_response.encoded_headers + + await send(message) + + await self.app(scope, receive, send_wrapper) + + +def create_supertokens_middleware() -> DefineMiddleware: + """ + Create a DefineMiddleware instance for SuperTokens session management. + + Returns: + A DefineMiddleware configured with SupertokensSessionMiddleware + """ + return DefineMiddleware(SupertokensSessionMiddleware) diff --git a/supertokens_python/framework/litestar/litestar_plugin.py b/supertokens_python/framework/litestar/litestar_plugin.py new file mode 100644 index 000000000..19244713b --- /dev/null +++ b/supertokens_python/framework/litestar/litestar_plugin.py @@ -0,0 +1,185 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from typing import Union, cast + +from litestar import asgi +from litestar.config.app import AppConfig +from litestar.plugins import InitPluginProtocol + + +class SupertokensPlugin(InitPluginProtocol): + """ + Litestar plugin for SuperTokens integration. + + This plugin handles authentication routes by mounting a custom ASGI app + that processes SuperTokens authentication requests. + """ + + def __init__(self, mount_path: str = "/auth"): + """ + Initialize the SuperTokens plugin. + + Args: + mount_path: The path where the SuperTokens ASGI app will be mounted. + This is the external path that will be accessible (e.g., "/auth" or "/api/v1/auth"). + """ + # Normalize the mount path + self.mount_path = mount_path.rstrip("/") or "/" + if not self.mount_path.startswith("/"): + self.mount_path = f"/{self.mount_path}" + + def on_app_init(self, app_config: AppConfig) -> AppConfig: + """ + Called during app initialization to register the SuperTokens ASGI app. + + Args: + app_config: The Litestar application configuration + + Returns: + The modified application configuration + """ + from litestar import Request, Response + from litestar.types import Receive, Scope, Send + + from supertokens_python import Supertokens + from supertokens_python.exceptions import SuperTokensError + from supertokens_python.framework.litestar.litestar_request import ( + LitestarRequest, + ) + from supertokens_python.framework.litestar.litestar_response import ( + LitestarResponse, + ) + from supertokens_python.recipe.session import SessionContainer + from supertokens_python.supertokens import manage_session_post_response + from supertokens_python.utils import default_user_context + + async def supertokens_asgi_app( + scope: Scope, receive: Receive, send: Send + ) -> None: + """ + ASGI app that handles SuperTokens authentication requests. + """ + if scope["type"] != "http": + not_found = Response(content=None, status_code=404) + await not_found.to_asgi_response( # type: ignore + app=None, request=Request(scope, receive, send) + )(scope, receive, send) + return + + st = Supertokens.get_instance() + + # Create Litestar request and wrap it for SuperTokens + litestar_request = Request(scope, receive=receive, send=send) # type: ignore + custom_request = LitestarRequest(litestar_request) # type: ignore + user_context = default_user_context(custom_request) + + try: + # Create a response object for SuperTokens to use + litestar_response = Response(content=None) + response = LitestarResponse(litestar_response) + + # Let SuperTokens handle the request + handled: Union[LitestarResponse, None] = cast( + LitestarResponse, + await st.middleware(custom_request, response, user_context), + ) + + if handled is None: # type: ignore + not_found = Response(content=None, status_code=404) + await not_found.to_asgi_response( + app=None, request=Request(scope, receive, send) + )(scope, receive, send) + return + + # Handle session management + if hasattr(litestar_request.state, "supertokens") and isinstance( # type: ignore + litestar_request.state.supertokens, # type: ignore + SessionContainer, + ): + manage_session_post_response( + litestar_request.state.supertokens, # type: ignore + handled, + user_context, + ) + + # Emit response + asgi_response = handled.response.to_asgi_response( # type: ignore + app=None, request=Request(scope, receive, send) + ) + await asgi_response(scope, receive, send) + return + + except SuperTokensError as e: + # SuperTokens error path + err_resp_obj = Response(content=None) + err_resp = LitestarResponse(err_resp_obj) + + handled = cast( + LitestarResponse, + await st.handle_supertokens_error( + custom_request, e, err_resp, user_context + ), + ) + + # Clear the session from request.state to prevent the middleware + # from re-applying session cookies after we've cleared them + if hasattr(litestar_request.state, "supertokens"): # type: ignore + delattr(litestar_request.state, "supertokens") # type: ignore + + asgi_response = handled.response.to_asgi_response( # type: ignore + app=None, request=Request(scope, receive, send) + ) + await asgi_response(scope, receive, send) + return + + # Fallback + fallback = Response(content=None, status_code=500) + await fallback.to_asgi_response( + app=None, request=Request(scope, receive, send) + )(scope, receive, send) # type: ignore + + # Mount the SuperTokens ASGI app to handle auth routes + app_mount = asgi(self.mount_path, is_mount=True)(supertokens_asgi_app) + app_config.route_handlers.append(app_mount) + + return app_config + + +def get_supertokens_plugin(mount_path: str = "/auth") -> SupertokensPlugin: + """ + Get a configured SuperTokens plugin for Litestar. + + Args: + mount_path: The path where the SuperTokens ASGI app will be mounted. + This is the external path that will be accessible (e.g., "/auth" or "/api/v1/auth"). + The mounted app will receive requests relative to this path. + + Returns: + A configured SupertokensPlugin instance + + Example: + # Mount at /auth + app = Litestar( + plugins=[get_supertokens_plugin(mount_path="/auth")] + ) + + # Mount at /api/v1/auth (when using Litestar app.path) + app = Litestar( + path="api/v1", + plugins=[get_supertokens_plugin(mount_path="/auth")] + ) + # This makes the auth routes available at /api/v1/auth externally + """ + return SupertokensPlugin(mount_path=mount_path) diff --git a/supertokens_python/framework/litestar/litestar_request.py b/supertokens_python/framework/litestar/litestar_request.py new file mode 100644 index 000000000..8207a9c70 --- /dev/null +++ b/supertokens_python/framework/litestar/litestar_request.py @@ -0,0 +1,75 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import json +from typing import Any, Dict, Union +from urllib.parse import parse_qsl + +from litestar import Request + +from supertokens_python.framework.request import BaseRequest +from supertokens_python.recipe.session.interfaces import SessionContainer + + +class LitestarRequest(BaseRequest): + def __init__(self, request: Request[Any, Any, Any]): + super().__init__() + self.request: Request[Any, Any, Any] = request + + def get_original_url(self) -> str: + return str(self.request.url) + + def get_query_param( + self, key: str, default: Union[str, None] = None + ) -> Union[str, None]: + return self.request.query_params.get(key, default) + + def get_query_params(self) -> Dict[str, Any]: + return dict(self.request.query_params.items()) # type: ignore + + async def json(self) -> Dict[str, Any]: + """ + Read the entire ASGI stream and JSON-decode it, + sidestepping Litestar’s internal max-body-size logic. + """ + body_bytes = b"".join([chunk async for chunk in self.request.stream()]) + if not body_bytes: + return {} + try: + return json.loads(body_bytes) + except json.JSONDecodeError: + return {} + + def method(self) -> str: + return self.request.method + + def get_cookie(self, key: str) -> Union[str, None]: + return self.request.cookies.get(key) + + def get_header(self, key: str) -> Union[str, None]: + return self.request.headers.get(key, None) + + def get_session(self) -> Union[SessionContainer, None]: + return self.request.state.supertokens + + def set_session(self, session: SessionContainer): + self.request.state.supertokens = session + + def set_session_as_none(self): + self.request.state.supertokens = None + + def get_path(self) -> str: + return self.request.scope["raw_path"].decode("utf-8") + + async def form_data(self): + return dict(parse_qsl((await self.request.body()).decode("utf-8"))) diff --git a/supertokens_python/framework/litestar/litestar_response.py b/supertokens_python/framework/litestar/litestar_response.py new file mode 100644 index 000000000..95e98963e --- /dev/null +++ b/supertokens_python/framework/litestar/litestar_response.py @@ -0,0 +1,93 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from math import ceil +from typing import Any, Dict, Literal, Optional + +from litestar import Response +from litestar.serialization import encode_json + +from supertokens_python.framework.response import BaseResponse +from supertokens_python.utils import get_timestamp_ms + + +class LitestarResponse(BaseResponse): + def __init__(self, response: Response[Any]): + super().__init__({}) + self.response: Response[Any] = response + self.original: Response[Any] = response + self.parser_checked: bool = False + self.response_sent: bool = False + self.status_set: bool = False + + def set_html_content(self, content: str): + if not self.response_sent: + body = bytes(content, "utf-8") + self.set_header("Content-Length", str(len(body))) + self.set_header("Content-Type", "text/html") + self.response.content = body + self.response_sent = True + + def set_cookie( + self, + key: str, + value: str, + expires: int, + path: str = "/", + domain: Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: Literal["lax", "strict", "none"] = "lax", + ): + self.response.set_cookie( + key=key, + value=value, + expires=ceil((expires - get_timestamp_ms()) / 1000), + path=path, + domain=domain, + secure=secure, + httponly=httponly, + samesite=samesite, + ) + + def set_header(self, key: str, value: str): + self.response.headers[key] = value + + def get_header(self, key: str) -> Optional[str]: + return self.response.headers.get(key, None) + + def remove_header(self, key: str): + del self.response.headers[key] + + def set_status_code(self, status_code: int): + if not self.status_set: + self.response.status_code = status_code + self.status_code = status_code + self.status_set = True + + def set_json_content(self, content: Dict[str, Any]): + if not self.response_sent: + body = encode_json( + content, + ) + self.set_header("Content-Type", "application/json; charset=utf-8") + self.set_header("Content-Length", str(len(body))) + self.response.content = body + self.response_sent = True + + def redirect(self, url: str) -> BaseResponse: + if not self.response_sent: + self.set_header("Location", url) + self.set_status_code(302) + self.response_sent = True + return self diff --git a/supertokens_python/framework/types.py b/supertokens_python/framework/types.py index 0e48bc490..eb7bd4e0d 100644 --- a/supertokens_python/framework/types.py +++ b/supertokens_python/framework/types.py @@ -18,13 +18,14 @@ from supertokens_python.framework.request import BaseRequest -frameworks = ["fastapi", "flask", "django"] +frameworks = ["fastapi", "flask", "django", "litestar"] class FrameworkEnum(Enum): FASTAPI = 1 FLASK = 2 DJANGO = 3 + LITESTAR = 4 class Framework(ABC): diff --git a/supertokens_python/recipe/session/framework/litestar/__init__.py b/supertokens_python/recipe/session/framework/litestar/__init__.py new file mode 100644 index 000000000..e4f35504d --- /dev/null +++ b/supertokens_python/recipe/session/framework/litestar/__init__.py @@ -0,0 +1,90 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import json +from typing import Any, Callable, Coroutine, Dict, List, Optional, Union + +from litestar import Request, Response + +from supertokens_python import Supertokens +from supertokens_python.exceptions import SuperTokensError +from supertokens_python.framework.litestar.litestar_request import LitestarRequest +from supertokens_python.framework.litestar.litestar_response import LitestarResponse +from supertokens_python.recipe.session import SessionContainer, SessionRecipe +from supertokens_python.recipe.session.interfaces import SessionClaimValidator +from supertokens_python.types import MaybeAwaitable +from supertokens_python.utils import ( + default_user_context, + set_request_in_user_context_if_not_defined, +) + + +def verify_session( + anti_csrf_check: Union[bool, None] = None, + session_required: bool = True, + check_database: bool = False, + override_global_claim_validators: Optional[ + Callable[ + [List[SessionClaimValidator], SessionContainer, Dict[str, Any]], + MaybeAwaitable[List[SessionClaimValidator]], + ] + ] = None, + user_context: Union[None, Dict[str, Any]] = None, +) -> Callable[..., Coroutine[Any, Any, Union[SessionContainer, None]]]: + _ = user_context + + async def func(request: Request[Any, Any, Any]) -> Union[SessionContainer, None]: + nonlocal user_context + base_req = LitestarRequest(request) + user_context = set_request_in_user_context_if_not_defined( + user_context, base_req + ) + + recipe = SessionRecipe.get_instance() + session = await recipe.verify_session( + base_req, + anti_csrf_check, + session_required, + check_database, + override_global_claim_validators, + user_context, + ) + if session is None: + if session_required: + raise Exception("Should never come here") + base_req.set_session_as_none() + else: + base_req.set_session(session) + return base_req.get_session() + + return func + + +async def session_exception_handler( + request: Request[Any, Any, Any], exc: SuperTokensError +) -> Response[Any]: + """Litestar exceptional handler for errors raised by Supertokens SDK when not using middleware + + Usage: `app.add_exception_handler(SuperTokensError, st_exception_handler)` + """ + base_req = LitestarRequest(request) + base_res = LitestarResponse(Response(content={})) + user_context = default_user_context(base_req) + result = await Supertokens.get_instance().handle_supertokens_error( + base_req, exc, base_res, user_context + ) + if isinstance(result, LitestarResponse): + body = json.loads(bytes(result.response.content)) + return Response(body, status_code=result.response.status_code) + + raise Exception("Should never come here") diff --git a/supertokens_python/supertokens.py b/supertokens_python/supertokens.py index abcf3aed0..38170523d 100644 --- a/supertokens_python/supertokens.py +++ b/supertokens_python/supertokens.py @@ -127,7 +127,7 @@ def __init__( app_name: str, api_domain: str, website_domain: Optional[str], - framework: Literal["fastapi", "flask", "django"], + framework: Literal["fastapi", "flask", "django", "litestar"], api_gateway_path: str, api_base_path: str, website_base_path: str, @@ -154,7 +154,7 @@ def __init__( self.website_base_path = NormalisedURLPath(website_base_path) if mode is not None: self.mode = mode - elif framework == "fastapi": + elif framework in ["fastapi", "litestar"]: mode = "asgi" else: mode = "wsgi" @@ -205,7 +205,7 @@ class Supertokens: def __init__( self, app_info: InputAppInfo, - framework: Literal["fastapi", "flask", "django"], + framework: Literal["fastapi", "flask", "django", "litestar"], supertokens_config: SupertokensConfig, recipe_list: List[Callable[[AppInfo], RecipeModule]], mode: Optional[Literal["asgi", "wsgi"]], @@ -330,7 +330,7 @@ def make_recipe(recipe: Callable[[AppInfo], RecipeModule]) -> RecipeModule: @staticmethod def init( app_info: InputAppInfo, - framework: Literal["fastapi", "flask", "django"], + framework: Literal["fastapi", "flask", "django", "litestar"], supertokens_config: SupertokensConfig, recipe_list: List[Callable[[AppInfo], RecipeModule]], mode: Optional[Literal["asgi", "wsgi"]], diff --git a/supertokens_python/utils.py b/supertokens_python/utils.py index c981f173a..0d0e78239 100644 --- a/supertokens_python/utils.py +++ b/supertokens_python/utils.py @@ -41,6 +41,7 @@ from supertokens_python.framework.django.framework import DjangoFramework from supertokens_python.framework.fastapi.framework import FastapiFramework from supertokens_python.framework.flask.framework import FlaskFramework +from supertokens_python.framework.litestar.framework import LitestarFramework from supertokens_python.framework.request import BaseRequest from supertokens_python.framework.response import BaseResponse from supertokens_python.logger import log_debug_message @@ -64,6 +65,7 @@ "fastapi": FastapiFramework(), "flask": FlaskFramework(), "django": DjangoFramework(), + "litestar": LitestarFramework(), } diff --git a/tests/auth-react/litestar-server/__init__.py b/tests/auth-react/litestar-server/__init__.py new file mode 100644 index 000000000..98bbe3f6d --- /dev/null +++ b/tests/auth-react/litestar-server/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/tests/auth-react/litestar-server/app.py b/tests/auth-react/litestar-server/app.py new file mode 100644 index 000000000..1fc8feabf --- /dev/null +++ b/tests/auth-react/litestar-server/app.py @@ -0,0 +1,1754 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import os +import time +from typing import Any, Awaitable, Callable, Dict, List, Optional, TypedDict, Union +from urllib.parse import parse_qs, urlparse + +import httpx +import requests +import uvicorn # type: ignore +from dotenv import load_dotenv +from litestar import Litestar, Request, Response, get, post +from litestar.config.cors import CORSConfig +from litestar.openapi import OpenAPIConfig +from litestar.openapi.plugins import ScalarRenderPlugin +from litestar.types import ASGIApp, Receive, Scope, Send +from supertokens_python import ( + InputAppInfo, + Supertokens, + SupertokensConfig, + convert_to_recipe_user_id, + get_all_cors_headers, + init, +) +from supertokens_python.asyncio import delete_user, get_user, list_users_by_account_info +from supertokens_python.framework.litestar import ( + create_supertokens_middleware, + get_exception_handlers, + get_supertokens_plugin, +) +from supertokens_python.framework.request import BaseRequest +from supertokens_python.ingredients.emaildelivery.types import ( + EmailDeliveryConfigWithService, + EmailDeliveryInterface, +) +from supertokens_python.recipe import ( + accountlinking, + emailpassword, + emailverification, + multifactorauth, + multitenancy, + oauth2provider, + passwordless, + session, + thirdparty, + totp, + userroles, + webauthn, +) +from supertokens_python.recipe.accountlinking import AccountInfoWithRecipeIdAndUserId +from supertokens_python.recipe.accountlinking.recipe import AccountLinkingRecipe +from supertokens_python.recipe.dashboard import DashboardRecipe +from supertokens_python.recipe.emailpassword import EmailPasswordRecipe +from supertokens_python.recipe.emailpassword.asyncio import update_email_or_password +from supertokens_python.recipe.emailpassword.interfaces import ( + APIInterface as EmailPasswordAPIInterface, +) +from supertokens_python.recipe.emailpassword.interfaces import ( + APIOptions as EPAPIOptions, +) +from supertokens_python.recipe.emailpassword.interfaces import ( + EmailAlreadyExistsError, + UnknownUserIdError, + UpdateEmailOrPasswordEmailChangeNotAllowedError, + UpdateEmailOrPasswordOkResult, +) +from supertokens_python.recipe.emailpassword.types import ( + FormField, + InputFormField, +) +from supertokens_python.recipe.emailverification import ( + EmailVerificationClaim, + EmailVerificationRecipe, +) +from supertokens_python.recipe.emailverification import ( + InputOverrideConfig as EVInputOverrideConfig, +) +from supertokens_python.recipe.emailverification.asyncio import unverify_email +from supertokens_python.recipe.emailverification.interfaces import ( + APIInterface as EmailVerificationAPIInterface, +) +from supertokens_python.recipe.emailverification.interfaces import ( + APIOptions as EVAPIOptions, +) +from supertokens_python.recipe.jwt import JWTRecipe +from supertokens_python.recipe.multifactorauth.asyncio import ( + add_to_required_secondary_factors_for_user, +) +from supertokens_python.recipe.multifactorauth.interfaces import ( + ResyncSessionAndFetchMFAInfoPUTOkResult, +) +from supertokens_python.recipe.multifactorauth.recipe import MultiFactorAuthRecipe +from supertokens_python.recipe.multifactorauth.types import MFARequirementList +from supertokens_python.recipe.multitenancy.asyncio import ( + associate_user_to_tenant, + create_or_update_tenant, + create_or_update_third_party_config, + delete_tenant, + disassociate_user_from_tenant, +) +from supertokens_python.recipe.multitenancy.interfaces import ( + AssociateUserToTenantEmailAlreadyExistsError, + AssociateUserToTenantOkResult, + AssociateUserToTenantPhoneNumberAlreadyExistsError, + AssociateUserToTenantThirdPartyUserAlreadyExistsError, + AssociateUserToTenantUnknownUserIdError, + TenantConfigCreateOrUpdate, +) +from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe +from supertokens_python.recipe.oauth2provider.asyncio import create_oauth2_client +from supertokens_python.recipe.oauth2provider.interfaces import CreateOAuth2ClientInput +from supertokens_python.recipe.oauth2provider.recipe import OAuth2ProviderRecipe +from supertokens_python.recipe.openid.recipe import OpenIdRecipe +from supertokens_python.recipe.passwordless import ( + ContactEmailOnlyConfig, + ContactEmailOrPhoneConfig, + ContactPhoneOnlyConfig, + PasswordlessRecipe, +) +from supertokens_python.recipe.passwordless.asyncio import update_user +from supertokens_python.recipe.passwordless.interfaces import ( + APIInterface as PasswordlessAPIInterface, +) +from supertokens_python.recipe.passwordless.interfaces import APIOptions as PAPIOptions +from supertokens_python.recipe.passwordless.interfaces import ( + PhoneNumberChangeNotAllowedError, + UpdateUserEmailAlreadyExistsError, + UpdateUserOkResult, + UpdateUserPhoneNumberAlreadyExistsError, + UpdateUserUnknownUserIdError, +) +from supertokens_python.recipe.session import SessionContainer, SessionRecipe +from supertokens_python.recipe.session.exceptions import ( + ClaimValidationError, + InvalidClaimsError, +) +from supertokens_python.recipe.session.framework.litestar import verify_session +from supertokens_python.recipe.session.interfaces import ( + APIInterface as SessionAPIInterface, +) +from supertokens_python.recipe.session.interfaces import APIOptions as SAPIOptions +from supertokens_python.recipe.session.interfaces import SessionClaimValidator +from supertokens_python.recipe.thirdparty import ( + ProviderConfig, + ThirdPartyRecipe, +) +from supertokens_python.recipe.thirdparty.asyncio import manually_create_or_update_user +from supertokens_python.recipe.thirdparty.interfaces import ( + APIInterface as ThirdpartyAPIInterface, +) +from supertokens_python.recipe.thirdparty.interfaces import APIOptions as TPAPIOptions +from supertokens_python.recipe.thirdparty.interfaces import ( + EmailChangeNotAllowedError, + ManuallyCreateOrUpdateUserOkResult, + SignInUpNotAllowed, +) +from supertokens_python.recipe.thirdparty.provider import Provider, RedirectUriInfo +from supertokens_python.recipe.thirdparty.types import UserInfo, UserInfoEmail +from supertokens_python.recipe.totp.recipe import TOTPRecipe +from supertokens_python.recipe.userroles import ( + PermissionClaim, + UserRoleClaim, + UserRolesRecipe, +) +from supertokens_python.recipe.userroles.asyncio import ( + add_role_to_user, + create_new_role_or_add_permissions, +) +from supertokens_python.recipe.webauthn.functions import update_user_email +from supertokens_python.recipe.webauthn.interfaces.api import ( + TypeWebauthnEmailDeliveryInput, +) +from supertokens_python.recipe.webauthn.recipe import WebauthnRecipe +from supertokens_python.recipe.webauthn.types.config import WebauthnConfig +from supertokens_python.types import RecipeUserId, User +from supertokens_python.types.auth_utils import LinkingToSessionUserFailedError +from supertokens_python.types.base import AccountInfoInput +from supertokens_python.types.response import GeneralErrorResponse +from typing_extensions import Literal + +load_dotenv("../auth-react.env") + +# App will be initialized at the end after all route handlers are defined +os.environ.setdefault("SUPERTOKENS_ENV", "testing") + + +# Uncomment the following for response logging +# Middleware - handled in Litestar app init +async def log_response(request: Request[Any, Any, Any], call_next): # type: ignore + response = await call_next(request) # type: ignore + + try: + body_bytes = b"" + async for chunk in response.body_iterator: # type: ignore + body_bytes += chunk # type: ignore + print(f"Response[Any]: {body_bytes.decode('utf-8')}") # type: ignore + response_with_body = Response[Any]( + content=body_bytes, + status_code=response.status_code, # type: ignore + headers=response.headers, # type: ignore + media_type=response.media_type, # type: ignore + ) + return response_with_body + except Exception: + pass + return response # type: ignore + + +class SaveWebauthnTokenUser(TypedDict): + email: str + recover_account_link: str + token: str + + +webauthn_store: Dict[str, SaveWebauthnTokenUser] = {} +code_store: Dict[str, List[Dict[str, Any]]] = {} +accountlinking_config: Dict[str, Any] = {} +enabled_providers: Optional[List[Any]] = None +enabled_recipes: Optional[List[Any]] = None +mfa_info: Dict[str, Any] = {} +contact_method: Union[None, Literal["PHONE", "EMAIL", "EMAIL_OR_PHONE"]] = None +flow_type: Union[ + None, Literal["USER_INPUT_CODE", "MAGIC_LINK", "USER_INPUT_CODE_AND_MAGIC_LINK"] +] = None + + +class CustomPlessEmailService( + passwordless.EmailDeliveryInterface[passwordless.EmailTemplateVars] +): + async def send_email( + self, + template_vars: passwordless.EmailTemplateVars, + user_context: Dict[str, Any], + ) -> None: + codes = code_store.get(template_vars.pre_auth_session_id) + if codes is None: + codes = [] + if template_vars.url_with_link_code: + template_vars.url_with_link_code = template_vars.url_with_link_code.replace( + "?preAuthSessionId", "?test=fix&preAuthSessionId" + ) + codes.append( + { + "urlWithLinkCode": template_vars.url_with_link_code, + "userInputCode": template_vars.user_input_code, + } + ) + code_store[template_vars.pre_auth_session_id] = codes + + +class CustomSMSService(passwordless.SMSDeliveryInterface[passwordless.SMSTemplateVars]): + async def send_sms( + self, template_vars: passwordless.SMSTemplateVars, user_context: Dict[str, Any] + ) -> None: + codes = code_store.get(template_vars.pre_auth_session_id) + if codes is None: + codes = [] + if template_vars.url_with_link_code: + template_vars.url_with_link_code = template_vars.url_with_link_code.replace( + "?preAuthSessionId", "?test=fix&preAuthSessionId" + ) + codes.append( + { + "urlWithLinkCode": template_vars.url_with_link_code, + "userInputCode": template_vars.user_input_code, + } + ) + code_store[template_vars.pre_auth_session_id] = codes + + +class CustomEVEmailService( + emailverification.EmailDeliveryInterface[emailverification.EmailTemplateVars] +): + async def send_email( + self, + template_vars: emailverification.EmailTemplateVars, + user_context: Dict[str, Any], + ) -> None: + global latest_url_with_token + latest_url_with_token = template_vars.email_verify_link + + +class CustomEPEmailService( + emailpassword.EmailDeliveryInterface[emailpassword.EmailTemplateVars] +): + async def send_email( + self, + template_vars: emailpassword.EmailTemplateVars, + user_context: Dict[str, Any], + ) -> None: + global latest_url_with_token + latest_url_with_token = template_vars.password_reset_link + + +def save_webauthn_token(user: SaveWebauthnTokenUser, recover_account_link: str): + global webauthn_store + webauthn = webauthn_store.get( + user["email"], + {"email": user["email"], "recover_account_link": "", "token": ""}, + ) + webauthn["recover_account_link"] = recover_account_link + + # Parse the token from the recoverAccountLink using URL and URLSearchParams + url = urlparse(recover_account_link) + token = parse_qs(url.query).get("token") + if token is not None and len(token) > 0: + webauthn["token"] = token[0] + + webauthn_store[user["email"]] = webauthn + + +class CustomWebwuthnEmailService( + EmailDeliveryInterface[TypeWebauthnEmailDeliveryInput] +): + async def send_email( + self, + template_vars: TypeWebauthnEmailDeliveryInput, + user_context: Dict[str, Any], + ): + save_webauthn_token( + user={ + "email": template_vars.user.email, + "recover_account_link": "", + "token": "", + }, + recover_account_link=template_vars.recover_account_link, + ) + + +def get_api_port(): + return "8083" + + +def get_website_port(): + return "3031" + + +async def check_request_body_for_general_error(req: BaseRequest) -> bool: + body = await req.json() + return body is not None and "generalError" in body and body["generalError"] + + +def check_request_query_for_general_error(req: BaseRequest) -> bool: + general_error = req.get_query_param("generalError") + return general_error is not None and general_error == "true" + + +async def check_for_general_error( + check_from: Literal["query", "body"], req: BaseRequest +): + is_general_error = False + if check_from == "query": + is_general_error = check_request_query_for_general_error(req) + else: + is_general_error = await check_request_body_for_general_error(req) + + return is_general_error + + +def get_website_domain(): + return "http://localhost:" + get_website_port() + + +latest_url_with_token = "" + + +async def validate_age(value: Any, tenant_id: str): + try: + if int(value) < 18: + return "You must be over 18 to register" + except Exception: + pass + + return None + + +form_fields = [ + InputFormField("name"), + InputFormField("age", validate=validate_age), + InputFormField("country", optional=True), +] + + +def auth0_provider_override(oi: Provider) -> Provider: + async def get_user_info( + oauth_tokens: Dict[str, Any], + user_context: Dict[str, Any], + ) -> UserInfo: + access_token = oauth_tokens.get("access_token") + if access_token is None: + raise Exception("access token is undefined") + + return UserInfo( + "someId", + UserInfoEmail("test@example.com", True), + ) + + oi.get_user_info = get_user_info + return oi + + +def mock_provider_override(oi: Provider) -> Provider: + async def get_user_info( + oauth_tokens: Dict[str, Any], + user_context: Dict[str, Any], + ) -> UserInfo: + user_id = oauth_tokens.get("userId", "user") + email = oauth_tokens.get("email", "email@test.com") + is_verified = oauth_tokens.get("isVerified", "true").lower() != "false" + + return UserInfo( + user_id, UserInfoEmail(email, is_verified), raw_user_info_from_provider=None + ) + + async def exchange_auth_code_for_oauth_tokens( + redirect_uri_info: RedirectUriInfo, + user_context: Dict[str, Any], + ) -> Dict[str, Any]: + return redirect_uri_info.redirect_uri_query_params + + oi.exchange_auth_code_for_oauth_tokens = exchange_auth_code_for_oauth_tokens + oi.get_user_info = get_user_info + return oi + + +def get_core_url(): + host = os.environ.get("SUPERTOKENS_CORE_HOST", "localhost") + port = os.environ.get("SUPERTOKENS_CORE_PORT", "3567") + + return f"http://{host}:{port}" + + +def setup_core_app( + *, appId: Optional[str] = None, coreConfig: Optional[Dict[str, Any]] = None +): + core_url = get_core_url() + + if appId is None: + appId = "" + + if coreConfig is None: + coreConfig = {} + + response = requests.put( + f"{core_url}/recipe/multitenancy/app/v2", + headers={ + "Content-Type": "application/json", + }, + data={ + "appId": appId, + "coreConfig": coreConfig, + }, + ) + + response_body = response.json() + assert response_body["status"] == "OK" + + return f"{core_url}/appid-{appId}" + + +def custom_init( + *, + coreUrl: str = get_core_url(), + accountLinkingConfig: Optional[Dict[str, Any]] = None, + enabledRecipes: Optional[List[str]] = None, + enabledProviders: Optional[List[str]] = None, + passwordlessFlowType: Optional[str] = "USER_INPUT_CODE_AND_MAGIC_LINK", + passwordlessContactMethod: Optional[str] = "EMAIL_OR_PHONE", + mfaInfo: Optional[Dict[str, Any]] = None, +): + if accountLinkingConfig is None: + accountLinkingConfig = {} + + if mfaInfo is None: + mfaInfo = {} + + AccountLinkingRecipe.reset() + UserRolesRecipe.reset() + PasswordlessRecipe.reset() + JWTRecipe.reset() + EmailVerificationRecipe.reset() + SessionRecipe.reset() + ThirdPartyRecipe.reset() + EmailPasswordRecipe.reset() + EmailVerificationRecipe.reset() + DashboardRecipe.reset() + MultitenancyRecipe.reset() + Supertokens.reset() + TOTPRecipe.reset() + MultiFactorAuthRecipe.reset() + OpenIdRecipe.reset() + OAuth2ProviderRecipe.reset() + WebauthnRecipe.reset() + + def override_email_verification_apis( + original_implementation_email_verification: EmailVerificationAPIInterface, + ): + original_email_verify_post = ( + original_implementation_email_verification.email_verify_post + ) + original_generate_email_verify_token_post = ( + original_implementation_email_verification.generate_email_verify_token_post + ) + + async def email_verify_post( + token: str, + session: Optional[SessionContainer], + tenant_id: str, + api_options: EVAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + return GeneralErrorResponse("general error from API email verify") + return await original_email_verify_post( + token, + session, + tenant_id, + api_options, + user_context, + ) + + async def generate_email_verify_token_post( + session: SessionContainer, + api_options: EVAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + return GeneralErrorResponse( + "general error from API email verification code" + ) + return await original_generate_email_verify_token_post( + session, api_options, user_context + ) + + original_implementation_email_verification.email_verify_post = email_verify_post + original_implementation_email_verification.generate_email_verify_token_post = ( + generate_email_verify_token_post + ) + return original_implementation_email_verification + + def override_email_password_apis( + original_implementation: EmailPasswordAPIInterface, + ): + original_email_exists_get = original_implementation.email_exists_get + original_generate_password_reset_token_post = ( + original_implementation.generate_password_reset_token_post + ) + original_password_reset_post = original_implementation.password_reset_post + original_sign_in_post = original_implementation.sign_in_post + original_sign_up_post = original_implementation.sign_up_post + + async def email_exists_get( + email: str, + tenant_id: str, + api_options: EPAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "query", api_options.request + ) + if is_general_error: + return GeneralErrorResponse("general error from API email exists") + return await original_email_exists_get( + email, tenant_id, api_options, user_context + ) + + async def generate_password_reset_token_post( + form_fields: List[FormField], + tenant_id: str, + api_options: EPAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + return GeneralErrorResponse("general error from API reset password") + return await original_generate_password_reset_token_post( + form_fields, tenant_id, api_options, user_context + ) + + async def password_reset_post( + form_fields: List[FormField], + token: str, + tenant_id: str, + api_options: EPAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + return GeneralErrorResponse( + "general error from API reset password consume" + ) + return await original_password_reset_post( + form_fields, token, tenant_id, api_options, user_context + ) + + async def sign_in_post( + form_fields: List[FormField], + tenant_id: str, + session: Optional[SessionContainer], + should_try_linking_with_session_user: Union[bool, None], + api_options: EPAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + msg = "general error from API sign in" + body = await api_options.request.json() + if body is not None and "generalErrorMessage" in body: + msg = body["generalErrorMessage"] + return GeneralErrorResponse(msg) + return await original_sign_in_post( + form_fields, + tenant_id, + session, + should_try_linking_with_session_user, + api_options, + user_context, + ) + + async def sign_up_post( + form_fields: List[FormField], + tenant_id: str, + session: Optional[SessionContainer], + should_try_linking_with_session_user: Union[bool, None], + api_options: EPAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + return GeneralErrorResponse("general error from API sign up") + return await original_sign_up_post( + form_fields, + tenant_id, + session, + should_try_linking_with_session_user, + api_options, + user_context, + ) + + original_implementation.email_exists_get = email_exists_get + original_implementation.generate_password_reset_token_post = ( + generate_password_reset_token_post + ) + original_implementation.password_reset_post = password_reset_post + original_implementation.sign_in_post = sign_in_post + original_implementation.sign_up_post = sign_up_post + return original_implementation + + def override_thirdparty_apis(original_implementation: ThirdpartyAPIInterface): + original_sign_in_up_post = original_implementation.sign_in_up_post + original_authorisation_url_get = original_implementation.authorisation_url_get + + async def sign_in_up_post( + provider: Provider, + redirect_uri_info: Union[RedirectUriInfo, None], + oauth_tokens: Union[Dict[str, Any], None], + session: Optional[SessionContainer], + should_try_linking_with_session_user: Union[bool, None], + tenant_id: str, + api_options: TPAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + return GeneralErrorResponse("general error from API sign in up") + return await original_sign_in_up_post( + provider, + redirect_uri_info, + oauth_tokens, + session, + should_try_linking_with_session_user, + tenant_id, + api_options, + user_context, + ) + + async def authorisation_url_get( + provider: Provider, + redirect_uri_on_provider_dashboard: str, + api_options: TPAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "query", api_options.request + ) + if is_general_error: + return GeneralErrorResponse( + "general error from API authorisation url get" + ) + return await original_authorisation_url_get( + provider, redirect_uri_on_provider_dashboard, api_options, user_context + ) + + original_implementation.sign_in_up_post = sign_in_up_post + original_implementation.authorisation_url_get = authorisation_url_get + return original_implementation + + def override_session_apis(original_implementation: SessionAPIInterface): + original_signout_post = original_implementation.signout_post + + async def signout_post( + session: SessionContainer, + api_options: SAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + return GeneralErrorResponse("general error from signout API") + return await original_signout_post(session, api_options, user_context) + + original_implementation.signout_post = signout_post + return original_implementation + + def override_passwordless_apis(original_implementation: PasswordlessAPIInterface): + original_consume_code_post = original_implementation.consume_code_post + original_create_code_post = original_implementation.create_code_post + original_resend_code_post = original_implementation.resend_code_post + + async def consume_code_post( + pre_auth_session_id: str, + user_input_code: Union[str, None], + device_id: Union[str, None], + link_code: Union[str, None], + session: Optional[SessionContainer], + should_try_linking_with_session_user: Union[bool, None], + tenant_id: str, + api_options: PAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + return GeneralErrorResponse("general error from API consume code") + return await original_consume_code_post( + pre_auth_session_id, + user_input_code, + device_id, + link_code, + session, + should_try_linking_with_session_user, + tenant_id, + api_options, + user_context, + ) + + async def create_code_post( + email: Union[str, None], + phone_number: Union[str, None], + session: Optional[SessionContainer], + should_try_linking_with_session_user: Union[bool, None], + tenant_id: str, + api_options: PAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + return GeneralErrorResponse("general error from API create code") + return await original_create_code_post( + email, + phone_number, + session, + should_try_linking_with_session_user, + tenant_id, + api_options, + user_context, + ) + + async def resend_code_post( + device_id: str, + pre_auth_session_id: str, + session: Optional[SessionContainer], + should_try_linking_with_session_user: Union[bool, None], + tenant_id: str, + api_options: PAPIOptions, + user_context: Dict[str, Any], + ): + is_general_error = await check_for_general_error( + "body", api_options.request + ) + if is_general_error: + return GeneralErrorResponse("general error from API resend code") + return await original_resend_code_post( + device_id, + pre_auth_session_id, + session, + should_try_linking_with_session_user, + tenant_id, + api_options, + user_context, + ) + + original_implementation.consume_code_post = consume_code_post + original_implementation.create_code_post = create_code_post + original_implementation.resend_code_post = resend_code_post + return original_implementation + + providers_list: List[thirdparty.ProviderInput] = [ + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="google", + clients=[ + thirdparty.ProviderClientConfig( + client_id=os.environ["GOOGLE_CLIENT_ID"], + client_secret=os.environ["GOOGLE_CLIENT_SECRET"], + ), + ], + ), + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="github", + clients=[ + thirdparty.ProviderClientConfig( + client_id=os.environ["GITHUB_CLIENT_ID"], + client_secret=os.environ["GITHUB_CLIENT_SECRET"], + ), + ], + ) + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="facebook", + clients=[ + thirdparty.ProviderClientConfig( + client_id=os.environ["FACEBOOK_CLIENT_ID"], + client_secret=os.environ["FACEBOOK_CLIENT_SECRET"], + ), + ], + ) + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="auth0", + name="Auth0", + authorization_endpoint=f"https://{os.environ['AUTH0_DOMAIN']}/authorize", + authorization_endpoint_query_params={"scope": "openid profile"}, + token_endpoint=f"https://{os.environ['AUTH0_DOMAIN']}/oauth/token", + clients=[ + thirdparty.ProviderClientConfig( + client_id=os.environ["AUTH0_CLIENT_ID"], + client_secret=os.environ["AUTH0_CLIENT_SECRET"], + ) + ], + ), + override=auth0_provider_override, + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="mock-provider", + name="Mock Provider", + authorization_endpoint=get_website_domain() + "/mockProvider/auth", + token_endpoint=get_website_domain() + "/mockProvider/token", + clients=[ + thirdparty.ProviderClientConfig( + client_id="supertokens", + client_secret="", + ) + ], + ), + override=mock_provider_override, + ), + ] + + if enabledProviders is not None: + providers_list = [ + provider + for provider in providers_list + if provider.config.third_party_id in enabledProviders + ] + + if passwordlessContactMethod is not None and passwordlessFlowType is not None: + if passwordlessContactMethod == "PHONE": + passwordless_init = passwordless.init( + contact_config=ContactPhoneOnlyConfig(), + flow_type=passwordlessFlowType, # type: ignore - type expects only certain literals + sms_delivery=passwordless.SMSDeliveryConfig(CustomSMSService()), + override=passwordless.InputOverrideConfig( + apis=override_passwordless_apis + ), + ) + elif passwordlessContactMethod == "EMAIL": + passwordless_init = passwordless.init( + contact_config=ContactEmailOnlyConfig(), + flow_type=passwordlessFlowType, # type: ignore - type expects only certain literals + email_delivery=passwordless.EmailDeliveryConfig( + CustomPlessEmailService() + ), + override=passwordless.InputOverrideConfig( + apis=override_passwordless_apis + ), + ) + else: + passwordless_init = passwordless.init( + contact_config=ContactEmailOrPhoneConfig(), + flow_type=passwordlessFlowType, # type: ignore - type expects only certain literals + email_delivery=passwordless.EmailDeliveryConfig( + CustomPlessEmailService() + ), + sms_delivery=passwordless.SMSDeliveryConfig(CustomSMSService()), + override=passwordless.InputOverrideConfig( + apis=override_passwordless_apis + ), + ) + else: + passwordless_init = passwordless.init( + contact_config=ContactEmailOrPhoneConfig(), + flow_type="USER_INPUT_CODE_AND_MAGIC_LINK", + email_delivery=passwordless.EmailDeliveryConfig(CustomPlessEmailService()), + sms_delivery=passwordless.SMSDeliveryConfig(CustomSMSService()), + override=passwordless.InputOverrideConfig(apis=override_passwordless_apis), + ) + + async def get_allowed_domains_for_tenant_id( + tenant_id: str, _: Dict[str, Any] + ) -> List[str]: + return [tenant_id + ".example.com", "localhost"] + + from supertokens_python.recipe.multifactorauth.interfaces import ( + APIInterface as MFAApiInterface, + ) + from supertokens_python.recipe.multifactorauth.interfaces import ( + APIOptions as MFAApiOptions, + ) + from supertokens_python.recipe.multifactorauth.interfaces import ( + RecipeInterface as MFARecipeInterface, + ) + + def override_mfa_functions(original_implementation: MFARecipeInterface): + og_get_factors_setup_for_user = ( + original_implementation.get_factors_setup_for_user + ) + + async def get_factors_setup_for_user( + user: User, + user_context: Dict[str, Any], + ): + res = await og_get_factors_setup_for_user(user, user_context) + if "alreadySetup" in mfaInfo: + return mfaInfo["alreadySetup"] + return res + + og_assert_allowed_to_setup_factor = original_implementation.assert_allowed_to_setup_factor_else_throw_invalid_claim_error + + async def assert_allowed_to_setup_factor_else_throw_invalid_claim_error( + session: SessionContainer, + factor_id: str, + mfa_requirements_for_auth: Callable[[], Awaitable[MFARequirementList]], + factors_set_up_for_user: Callable[[], Awaitable[List[str]]], + user_context: Dict[str, Any], + ): + if "allowedToSetup" in mfaInfo: + if factor_id not in mfaInfo["allowedToSetup"]: + raise InvalidClaimsError( + msg="INVALID_CLAIMS", + payload=[ + ClaimValidationError(id_="test", reason="test override") + ], + ) + else: + await og_assert_allowed_to_setup_factor( + session, + factor_id, + mfa_requirements_for_auth, + factors_set_up_for_user, + user_context, + ) + + og_get_mfa_requirements_for_auth = ( + original_implementation.get_mfa_requirements_for_auth + ) + + async def get_mfa_requirements_for_auth( + tenant_id: str, + access_token_payload: Dict[str, Any], + completed_factors: Dict[str, int], + user: Callable[[], Awaitable[User]], + factors_set_up_for_user: Callable[[], Awaitable[List[str]]], + required_secondary_factors_for_user: Callable[[], Awaitable[List[str]]], + required_secondary_factors_for_tenant: Callable[[], Awaitable[List[str]]], + user_context: Dict[str, Any], + ) -> MFARequirementList: + res = await og_get_mfa_requirements_for_auth( + tenant_id, + access_token_payload, + completed_factors, + user, + factors_set_up_for_user, + required_secondary_factors_for_user, + required_secondary_factors_for_tenant, + user_context, + ) + if "requirements" in mfaInfo: + return mfaInfo["requirements"] + return res + + original_implementation.get_mfa_requirements_for_auth = ( + get_mfa_requirements_for_auth + ) + + original_implementation.assert_allowed_to_setup_factor_else_throw_invalid_claim_error = assert_allowed_to_setup_factor_else_throw_invalid_claim_error + + original_implementation.get_factors_setup_for_user = get_factors_setup_for_user + return original_implementation + + def override_mfa_apis(original_implementation: MFAApiInterface): + og_resync_session_and_fetch_mfa_info_put = ( + original_implementation.resync_session_and_fetch_mfa_info_put + ) + + async def resync_session_and_fetch_mfa_info_put( + api_options: MFAApiOptions, + session: SessionContainer, + user_context: Dict[str, Any], + ) -> Union[ResyncSessionAndFetchMFAInfoPUTOkResult, GeneralErrorResponse]: + res = await og_resync_session_and_fetch_mfa_info_put( + api_options, session, user_context + ) + + if isinstance(res, ResyncSessionAndFetchMFAInfoPUTOkResult): + if "alreadySetup" in mfaInfo: + res.factors.already_setup = mfaInfo["alreadySetup"][:] + + if "noContacts" in mfaInfo: + res.emails = {} + res.phone_numbers = {} + + return res + + original_implementation.resync_session_and_fetch_mfa_info_put = ( + resync_session_and_fetch_mfa_info_put + ) + return original_implementation + + recipe_list: List[Any] = [ + {"id": "userroles", "init": userroles.init()}, + { + "id": "session", + "init": session.init( + override=session.InputOverrideConfig(apis=override_session_apis) + ), + }, + { + "id": "emailverification", + "init": emailverification.init( + mode="OPTIONAL", + email_delivery=emailverification.EmailDeliveryConfig( + CustomEVEmailService() + ), + override=EVInputOverrideConfig(apis=override_email_verification_apis), + ), + }, + { + "id": "emailpassword", + "init": emailpassword.init( + sign_up_feature=emailpassword.InputSignUpFeature(form_fields), + email_delivery=emailpassword.EmailDeliveryConfig( + CustomEPEmailService() + ), + override=emailpassword.InputOverrideConfig( + apis=override_email_password_apis, + ), + ), + }, + { + "id": "webauthn", + "init": webauthn.init( + config=WebauthnConfig( + email_delivery=EmailDeliveryConfigWithService[ + TypeWebauthnEmailDeliveryInput + ](service=CustomWebwuthnEmailService()) # type: ignore + ) + ), + }, + { + "id": "thirdparty", + "init": thirdparty.init( + sign_in_and_up_feature=thirdparty.SignInAndUpFeature(providers_list), + override=thirdparty.InputOverrideConfig(apis=override_thirdparty_apis), + ), + }, + { + "id": "passwordless", + "init": passwordless_init, + }, + { + "id": "multitenancy", + "init": multitenancy.init( + get_allowed_domains_for_tenant_id=get_allowed_domains_for_tenant_id + ), + }, + { + "id": "multifactorauth", + "init": multifactorauth.init( + first_factors=mfaInfo.get("firstFactors", None), + override=multifactorauth.OverrideConfig( + functions=override_mfa_functions, + apis=override_mfa_apis, + ), + ), + }, + { + "id": "totp", + "init": totp.init( + config=totp.TOTPConfig( + default_period=1, + default_skew=30, + ) + ), + }, + { + "id": "oauth2provider", + "init": oauth2provider.init(), + }, + ] + + accountlinking_config_input: Dict[str, Any] = { + "enabled": False, + "shouldAutoLink": { + "shouldAutomaticallyLink": True, + "shouldRequireVerification": True, + }, + **accountLinkingConfig, + } + + async def should_do_automatic_account_linking( + _: AccountInfoWithRecipeIdAndUserId, + __: Optional[User], + ___: Optional[SessionContainer], + ____: str, + _____: Dict[str, Any], + ) -> Union[ + accountlinking.ShouldNotAutomaticallyLink, + accountlinking.ShouldAutomaticallyLink, + ]: + should_auto_link = accountlinking_config_input["shouldAutoLink"] + assert isinstance(should_auto_link, dict) + should_automatically_link = should_auto_link["shouldAutomaticallyLink"] # type: ignore + assert isinstance(should_automatically_link, bool) + if should_automatically_link: + should_require_verification = should_auto_link["shouldRequireVerification"] # type: ignore + assert isinstance(should_require_verification, bool) + return accountlinking.ShouldAutomaticallyLink( + should_require_verification=should_require_verification + ) + return accountlinking.ShouldNotAutomaticallyLink() + + if accountlinking_config_input["enabled"]: + recipe_list.append( + { + "id": "accountlinking", + "init": accountlinking.init( + should_do_automatic_account_linking=should_do_automatic_account_linking + ), + } + ) + + if enabledRecipes is not None: + new_recipe_list = [] + for item in recipe_list: + for recipe_id in enabledRecipes: + if item["id"] in recipe_id: + new_recipe_list.append(item["init"]) # type: ignore + break + + recipe_list = new_recipe_list + + else: + recipe_list = [item["init"] for item in recipe_list] + + init( + supertokens_config=SupertokensConfig(coreUrl), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="localhost:" + get_api_port(), + website_domain=get_website_domain(), + ), + framework="django", + mode=os.environ.get("APP_MODE", "asgi"), # type: ignore + recipe_list=recipe_list, + telemetry=False, + ) + + +custom_init() + + +# Exception handler for Exception - registered in app # type: ignore +async def exception_handler(a, b): # type: ignore + print(a, b) # type: ignore + return Response[Any](content={}, status_code=500) + + +@post("/test/before", sync_to_thread=True) +def before() -> Response[Any]: + return Response[Any](content="") + + +@post("/test/beforeEach", sync_to_thread=True) +def before_each() -> Response[Any]: + global code_store + global latest_url_with_token + + latest_url_with_token = "" + code_store = dict() + + return Response[Any](content="") + + +@post("/test/afterEach", sync_to_thread=True) +def afterEach() -> Response[Any]: + return Response[Any](content="") + + +@post("/test/after", sync_to_thread=True) +def after() -> Response[Any]: + return Response[Any](content="") + + +@post("/test/setup/app") +async def setup_core_app_handler(request: Request[Any, Any, Any]) -> Response[Any]: + body = await request.json() + url = setup_core_app(**body) + + return Response[Any](content=url) + + +@post("/test/setup/st") +async def setup_st(request: Request[Any, Any, Any]) -> None: + print(await request.json()) + body = await request.json() + print("Setting up ST with body:", body) + custom_init(**body) + + +@post("/changeEmail") +async def change_email(request: Request[Any, Any, Any]) -> Response[Any]: + body: Union[dict[str, Any], None] = await request.json() + if body is None: + raise Exception("Should never come here") + + if body["rid"] == "emailpassword": + resp = await update_email_or_password( + recipe_user_id=convert_to_recipe_user_id(body["recipeUserId"]), + email=body["email"], + tenant_id_for_password_policy=body["tenantId"], + ) + if isinstance(resp, UpdateEmailOrPasswordOkResult): + return Response[Any](content={"status": "OK"}) + if isinstance(resp, EmailAlreadyExistsError): + return Response[Any](content={"status": "EMAIL_ALREADY_EXISTS_ERROR"}) + if isinstance(resp, UnknownUserIdError): + return Response[Any](content={"status": "UNKNOWN_USER_ID_ERROR"}) + if isinstance(resp, UpdateEmailOrPasswordEmailChangeNotAllowedError): + return Response[Any]( + content={ + "status": "EMAIL_CHANGE_NOT_ALLOWED_ERROR", + "reason": resp.reason, + } + ) + return Response[Any](content=resp.to_json()) + + if body["rid"] == "thirdparty": + user = await get_user(user_id=body["recipeUserId"]) + assert user is not None + login_method = next( + lm + for lm in user.login_methods + if lm.recipe_user_id.get_as_string() == body["recipeUserId"] + ) + assert login_method is not None + assert login_method.third_party is not None + resp = await manually_create_or_update_user( + tenant_id=body["tenantId"], + third_party_id=login_method.third_party.id, + third_party_user_id=login_method.third_party.user_id, + email=body["email"], + is_verified=False, + ) + if isinstance(resp, ManuallyCreateOrUpdateUserOkResult): + return Response[Any]( + content={ + "status": "OK", + "createdNewRecipeUser": resp.created_new_recipe_user, + } + ) + if isinstance(resp, LinkingToSessionUserFailedError): + raise Exception("Should not come here") + if isinstance(resp, SignInUpNotAllowed): + return Response[Any]( + content={"status": "SIGN_IN_UP_NOT_ALLOWED", "reason": resp.reason} + ) + return Response[Any]( + content={"status": "EMAIL_CHANGE_NOT_ALLOWED_ERROR", "reason": resp.reason} + ) + + if body["rid"] == "passwordless": + resp = await update_user( + recipe_user_id=convert_to_recipe_user_id(body["recipeUserId"]), + email=body.get("email"), + phone_number=body.get("phoneNumber"), + ) + + if isinstance(resp, UpdateUserOkResult): + return Response[Any](content={"status": "OK"}) + if isinstance(resp, UpdateUserUnknownUserIdError): + return Response[Any](content={"status": "UNKNOWN_USER_ID_ERROR"}) + if isinstance(resp, UpdateUserEmailAlreadyExistsError): + return Response[Any](content={"status": "EMAIL_ALREADY_EXISTS_ERROR"}) + if isinstance(resp, UpdateUserPhoneNumberAlreadyExistsError): + return Response[Any]( + content={"status": "PHONE_NUMBER_ALREADY_EXISTS_ERROR"} + ) + if isinstance(resp, EmailChangeNotAllowedError): + return Response[Any]( + content={ + "status": "EMAIL_CHANGE_NOT_ALLOWED_ERROR", + "reason": resp.reason, + } + ) + if isinstance(resp, PhoneNumberChangeNotAllowedError): + return Response[Any]( + content={ + "status": "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR", + "reason": resp.reason, + } + ) + + if body["rid"] == "webauthn": + resp = await update_user_email( + recipe_user_id=body["recipeUserId"], + email=body["email"], + ) + + return Response[Any](content=resp.to_json()) + + raise Exception("Should not come here") + + +@post("/setupTenant") +async def setup_tenant(request: Request[Any, Any, Any]) -> Response[Any]: + body = await request.json() + if body is None: + raise Exception("Should never come here") + tenant_id = body["tenantId"] + login_methods = body["loginMethods"] + core_config = body.get("coreConfig", {}) + + first_factors: List[str] = [] + if login_methods.get("emailPassword", {}).get("enabled") is True: + first_factors.append("emailpassword") + if login_methods.get("thirdParty", {}).get("enabled") is True: + first_factors.append("thirdparty") + if login_methods.get("passwordless", {}).get("enabled") is True: + first_factors.extend(["otp-phone", "otp-email", "link-phone", "link-email"]) + + core_resp = await create_or_update_tenant( + tenant_id, + config=TenantConfigCreateOrUpdate( + first_factors=first_factors, + core_config=core_config, + ), + ) + + if login_methods.get("thirdParty", {}).get("providers") is not None: + for provider in login_methods["thirdParty"]["providers"]: + await create_or_update_third_party_config( + tenant_id, + config=ProviderConfig.from_json(provider), + ) + + return Response[Any](content={"status": "OK", "createdNew": core_resp.created_new}) + + +@post("/addUserToTenant") +async def add_user_to_tenant(request: Request[Any, Any, Any]) -> Response[Any]: + body = await request.json() + if body is None: + raise Exception("Should never come here") + tenant_id = body["tenantId"] + recipe_user_id = body["recipeUserId"] + + core_resp = await associate_user_to_tenant(tenant_id, RecipeUserId(recipe_user_id)) + + if isinstance(core_resp, AssociateUserToTenantOkResult): + return Response( + content={ + "status": "OK", + "wasAlreadyAssociated": core_resp.was_already_associated, + } + ) + elif isinstance(core_resp, AssociateUserToTenantUnknownUserIdError): + return Response[Any](content={"status": "UNKNOWN_USER_ID_ERROR"}) + elif isinstance(core_resp, AssociateUserToTenantEmailAlreadyExistsError): + return Response[Any](content={"status": "EMAIL_ALREADY_EXISTS_ERROR"}) + elif isinstance(core_resp, AssociateUserToTenantPhoneNumberAlreadyExistsError): + return Response[Any](content={"status": "PHONE_NUMBER_ALREADY_EXISTS_ERROR"}) + elif isinstance(core_resp, AssociateUserToTenantThirdPartyUserAlreadyExistsError): + return Response[Any]( + content={"status": "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR"} + ) + return Response[Any]( + content={"status": "ASSOCIATION_NOT_ALLOWED_ERROR", "reason": core_resp.reason} + ) + + +@post("/removeUserFromTenant") +async def remove_user_from_tenant(request: Request[Any, Any, Any]) -> Response[Any]: + body = await request.json() + if body is None: + raise Exception("Should never come here") + tenant_id = body["tenantId"] + recipe_user_id = body["recipeUserId"] + + core_resp = await disassociate_user_from_tenant( + tenant_id, RecipeUserId(recipe_user_id) + ) + + return Response[Any]( + content={"status": "OK", "wasAssociated": core_resp.was_associated} + ) + + +@post("/removeTenant") +async def remove_tenant(request: Request[Any, Any, Any]) -> Response[Any]: + body = await request.json() + if body is None: + raise Exception("Should never come here") + tenant_id = body["tenantId"] + + core_resp = await delete_tenant(tenant_id) + + return Response[Any](content={"status": "OK", "didExist": core_resp.did_exist}) + + +@post("/addRequiredFactor") +async def add_required_factor( + request: Request[Any, Any, Any], session: SessionContainer +) -> Response[Any]: + body = await request.json() + if body is None or "factorId" not in body: + return Response[Any](content={"error": "Invalid request body"}, status_code=400) + + await add_to_required_secondary_factors_for_user( + session.get_user_id(), body["factorId"] + ) + + return Response[Any](content={"status": "OK"}) + + +@post("/test/getTOTPCode") +async def test_get_totp_code(request: Request[Any, Any, Any]) -> Response[Any]: + from pyotp import TOTP + + body = await request.json() + if body is None or "secret" not in body: + return Response[Any](content={"error": "Invalid request body"}, status_code=400) + + secret = body["secret"] + totp = TOTP(secret, digits=6, interval=1) + code = totp.now() + + return Response[Any](content={"totp": code}) + + +@post("/test/create-oauth2-client") +async def test_create_oauth2_client(request: Request[Any, Any, Any]) -> Response[Any]: + body = await request.json() + if body is None: + raise Exception("Invalid request body") + client = await create_oauth2_client(CreateOAuth2ClientInput.from_json(body)) + return Response[Any](content=client.to_json()) + + +@get("/test/getDevice", sync_to_thread=True) +def test_get_device(request: Request[Any, Any, Any]) -> Response[Any]: + global code_store + pre_auth_session_id = request.query_params.get("preAuthSessionId") + if pre_auth_session_id is None: + return Response[Any](content="") + codes = code_store.get(pre_auth_session_id) + return Response[Any]( + content={"preAuthSessionId": pre_auth_session_id, "codes": codes} + ) + + +@get("/test/featureFlags", sync_to_thread=True) +def test_feature_flags(request: Request[Any, Any, Any]) -> Response[Any]: + available = [ + "passwordless", + "generalerror", + "userroles", + "multitenancy", + "multitenancyManagementEndpoints", + "accountlinking", + "mfa", + "recipeConfig", + "accountlinking-fixes", + "oauth2", + "webauthn", + ] + return Response[Any](content={"available": available}) + + +@get("/ping", sync_to_thread=True) +def ping() -> Response[Any]: + return Response[Any](content="success") + + +@get("/sessionInfo") +async def get_session_info(session_: SessionContainer) -> Response[Any]: + return Response[Any]( + content={ + "sessionHandle": session_.get_handle(), + "userId": session_.get_user_id(), + "accessTokenPayload": session_.get_access_token_payload(), + "sessionDataFromDatabase": await session_.get_session_data_from_database(), + } + ) + + +@get("/token") +async def get_token() -> Response[Any]: + global latest_url_with_token + t = 0 + + while not latest_url_with_token: + time.sleep(0.5) + t += 1 + if t > 10: + break + + return Response[Any](content={"latestURLWithToken": latest_url_with_token}) + + +@get("/unverifyEmail") +async def unverify_email_api(session_: SessionContainer) -> Response[Any]: + await unverify_email(session_.get_recipe_user_id()) + await session_.fetch_and_set_claim(EmailVerificationClaim) + return Response[Any](content={"status": "OK"}) + + +@post("/setRole") +async def set_role_api( + request: Request[Any, Any, Any], session_: SessionContainer +) -> Response[Any]: + body = await request.json() + await create_new_role_or_add_permissions(body["role"], body["permissions"]) + await add_role_to_user("public", session_.get_user_id(), body["role"]) + await session_.fetch_and_set_claim(UserRoleClaim) + await session_.fetch_and_set_claim(PermissionClaim) + return Response[Any](content={"status": "OK"}) + + +@post("/deleteUser") +async def delete_user_api(request: Request[Any, Any, Any]) -> Response[Any]: + body = await request.json() + user = await list_users_by_account_info( + "public", AccountInfoInput(email=body["email"]) + ) + if len(user) == 0: + raise Exception("Should not come here") + await delete_user(user[0].id) + return Response[Any](content={"status": "OK"}) + + +@get("/test/webauthn/get-token") +async def webauth_get_token(request: Request[Any, Any, Any]) -> Response[Any]: + webauthn = webauthn_store.get(request.query_params.get("email", "")) + if webauthn is None: + return Response[Any](content={"error": "Webauthn not found"}, status_code=404) + + return Response[Any](content={"token": webauthn["token"]}) + + +@post("/test/webauthn/create-and-assert-credential") +async def webauthn_create_and_assert_credential( + request: Request[Any, Any, Any], +) -> Response[Any]: + body = await request.json() + test_server_port = os.environ.get("NODE_PORT", 8082) + response = httpx.post( + url=f"http://localhost:{test_server_port}/test/webauthn/create-and-assert-credential", + json=body, + ) + + return Response[Any](content=response.json()) + + +@post("/test/webauthn/create-credential") +async def webauthn_create_credential(request: Request[Any, Any, Any]) -> Response[Any]: + body = await request.json() + test_server_port = os.environ.get("NODE_PORT", 8082) + response = httpx.post( + url=f"http://localhost:{test_server_port}/test/webauthn/create-credential", + json=body, + ) + + return Response[Any](content=response.json()) + + +async def override_global_claim_validators( + gv: List[SessionClaimValidator], + _session: SessionContainer, + user_context: Dict[str, Any], +): + validators = gv.copy() + req = user_context["_default"]["request"] + body = await req.json() + + if body.get("role"): + info = body["role"] + validator = getattr(UserRoleClaim.validators, info["validator"]) + validators.append(validator(*info["args"])) + + if body.get("permission"): + info = body["permission"] + validator = getattr(PermissionClaim.validators, info["validator"]) + validators.append(validator(*info["args"])) + + return validators + + +@post("/checkRole") +async def check_role_api( + request: Request[Any, Any, Any], +) -> Response[Any]: + # Manually verify session with custom validators + await verify_session( + override_global_claim_validators=override_global_claim_validators + )(request) + return Response[Any](content={"status": "OK"}) + + +# Exception handler for 405 - registered in app # type: ignore +def f_405(_, e): # type: ignore + return Response[Any](content="", status_code=404) + + +# cors middleware added like this due to issue with add_middleware +# ref: https://github.com/tiangolo/fastapi/issues/1663 + + +# Create dependency injection for verify_session +async def provide_session( + request: Request[Any, Any, Any], +) -> Union[SessionContainer, None]: + """Dependency provider for session verification""" + return await verify_session()(request) + + +async def provide_optional_session( + request: Request[Any, Any, Any], +) -> Union[SessionContainer, None]: + """Dependency provider for optional session verification""" + return await verify_session(session_required=False)(request) + + +# Middleware for response logging (commented out - uncomment if needed) +async def log_response_middleware( + app: ASGIApp, scope: Scope, receive: Receive, send: Send +): + if scope["type"] != "http": + await app(scope, receive, send) + return + + # Implementation would go here + await app(scope, receive, send) + + +# Initialize Litestar app +app = Litestar( + route_handlers=[ + before, + before_each, + afterEach, + after, + setup_core_app_handler, + setup_st, + change_email, + setup_tenant, + add_user_to_tenant, + remove_user_from_tenant, + remove_tenant, + add_required_factor, + test_get_totp_code, + test_create_oauth2_client, + test_get_device, + test_feature_flags, + ping, + get_session_info, + get_token, + unverify_email_api, + set_role_api, + delete_user_api, + webauth_get_token, + webauthn_create_and_assert_credential, + webauthn_create_credential, + check_role_api, + ], + cors_config=CORSConfig( + allow_origins=[get_website_domain()], + allow_credentials=True, + allow_methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"], + allow_headers=["Content-Type"] + get_all_cors_headers(), + ), + openapi_config=OpenAPIConfig( + title="SuperTokens Litestar Demo", + version="1.0.0", + path="/docs", + render_plugins=[ScalarRenderPlugin()], + ), + plugins=[get_supertokens_plugin()], + middleware=[ + create_supertokens_middleware(), + # DefineMiddleware(log_response_middleware), # Uncomment for response logging + ], + exception_handlers=get_exception_handlers(), + debug=True, +) + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=int(get_api_port())) # type: ignore diff --git a/tests/frontendIntegration/litestar-server/__init__.py b/tests/frontendIntegration/litestar-server/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/frontendIntegration/litestar-server/app.py b/tests/frontendIntegration/litestar-server/app.py new file mode 100644 index 000000000..ee7082587 --- /dev/null +++ b/tests/frontendIntegration/litestar-server/app.py @@ -0,0 +1,812 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distribu/setAntiCsrfted on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governi/setAntiCsrfng permissions and limitations +# under the License./setAntiCsrf +import json +import os +import sys +import time +from base64 import b64encode +from typing import Any, Dict, Union + +import uvicorn +from litestar import ( + Litestar, + MediaType, + Request, + Response, + delete, + get, + post, + put, + route, +) +from litestar.config.cors import CORSConfig +from litestar.static_files import create_static_files_router # type: ignore +from supertokens_python import ( + InputAppInfo, + Supertokens, + SupertokensConfig, + get_all_cors_headers, + init, +) +from supertokens_python.constants import VERSION +from supertokens_python.framework import BaseRequest, BaseResponse +from supertokens_python.framework.litestar import ( + create_supertokens_middleware, + get_exception_handlers, + get_supertokens_plugin, +) +from supertokens_python.normalised_url_path import NormalisedURLPath +from supertokens_python.querier import Querier +from supertokens_python.recipe import session +from supertokens_python.recipe.jwt.recipe import JWTRecipe +from supertokens_python.recipe.multitenancy.recipe import MultitenancyRecipe +from supertokens_python.recipe.oauth2provider.recipe import OAuth2ProviderRecipe +from supertokens_python.recipe.openid.recipe import OpenIdRecipe +from supertokens_python.recipe.session import InputErrorHandlers +from supertokens_python.recipe.session.asyncio import ( + SessionRecipe, + create_new_session, + get_session_information, + merge_into_access_token_payload, + revoke_all_sessions_for_user, +) +from supertokens_python.recipe.session.framework.litestar import verify_session +from supertokens_python.recipe.session.interfaces import ( + APIInterface, + ClaimValidationResult, + JSONObject, + RecipeInterface, + SessionClaimValidator, +) +from supertokens_python.types import RecipeUserId +from supertokens_python.utils import is_version_gte + +protected_prop_name = { + "sub", + "iat", + "exp", + "sessionHandle", + "parentRefreshTokenHash1", + "refreshTokenHash1", + "antiCsrfToken", +} + +index_file = open("templates/index.html", "r") +file_contents = index_file.read() +index_file.close() +os.environ.setdefault("SUPERTOKENS_ENV", "testing") + +last_set_enable_anti_csrf = True +last_set_enable_jwt = False + + +class Test: + no_of_times_refresh_called_during_test = 0 + no_of_times_get_session_called_during_test = 0 + no_of_times_refresh_attempted_during_test = 0 + + @staticmethod + def reset(): + Test.no_of_times_refresh_called_during_test = 0 + Test.no_of_times_get_session_called_during_test = 0 + Test.no_of_times_refresh_attempted_during_test = 0 + + @staticmethod + def increment_refresh(): + Test.no_of_times_refresh_called_during_test = ( + Test.no_of_times_refresh_called_during_test + 1 + ) + + @staticmethod + def increment_attempted_refresh(): + Test.no_of_times_refresh_attempted_during_test = ( + Test.no_of_times_refresh_attempted_during_test + 1 + ) + + @staticmethod + def increment_get_session(): + Test.no_of_times_get_session_called_during_test = ( + Test.no_of_times_get_session_called_during_test + 1 + ) + + @staticmethod + def get_session_called_count(): + return Test.no_of_times_get_session_called_during_test + + @staticmethod + def get_refresh_called_count(): + return Test.no_of_times_refresh_called_during_test + + @staticmethod + def get_refresh_attempted_count(): + return Test.no_of_times_refresh_attempted_during_test + + +async def unauthorised_f(_: BaseRequest, __: str, res: BaseResponse): + res.set_status_code(401) + res.set_json_content({}) + return res + + +def apis_override_session(param: APIInterface): + param.disable_refresh_post = True + return param + + +def functions_override_session(param: RecipeInterface): + original_create_new_session = param.create_new_session + + async def create_new_session_custom( + user_id: str, + recipe_user_id: RecipeUserId, + access_token_payload: Union[Dict[str, Any], None], + session_data_in_database: Union[Dict[str, Any], None], + disable_anti_csrf: Union[bool, None], + tenant_id: str, + user_context: Dict[str, Any], + ): + if access_token_payload is None: + access_token_payload = {} + access_token_payload = {**access_token_payload, "customClaim": "customValue"} + return await original_create_new_session( + user_id, + recipe_user_id, + access_token_payload, + session_data_in_database, + disable_anti_csrf, + tenant_id, + user_context, + ) + + param.create_new_session = create_new_session_custom + + return param + + +def get_app_port(): + argvv = sys.argv + for i in range(0, len(argvv)): # pylint: disable=consider-using-enumerate + if argvv[i] == "--port": + return argvv[i + 1] + + return "8080" + + +def config( + core_url: str, + enable_anti_csrf: bool, + enable_jwt: bool, + jwt_property_name: Union[str, None], +): + anti_csrf: str = "VIA_TOKEN" if enable_anti_csrf else "NONE" + + if enable_jwt: + if is_version_gte(VERSION, "0.13.0"): + init( + supertokens_config=SupertokensConfig(core_url), + app_info=InputAppInfo( + app_name="SuperTokens Python SDK", + api_domain="0.0.0.0:" + get_app_port(), + website_domain="http://localhost:8080", + ), + framework="litestar", + recipe_list=[ + session.init( + error_handlers=InputErrorHandlers( + on_unauthorised=unauthorised_f + ), + anti_csrf=anti_csrf, # type: ignore + override=session.InputOverrideConfig( + apis=apis_override_session, + functions=functions_override_session, + ), + expose_access_token_to_frontend_in_cookie_based_auth=True, + ) + ], + telemetry=False, + ) + else: + init( + supertokens_config=SupertokensConfig(core_url), + app_info=InputAppInfo( + app_name="SuperTokens Python SDK", + api_domain="0.0.0.0:" + get_app_port(), + website_domain="http://localhost:8080", + ), + framework="litestar", + recipe_list=[ + session.init( + error_handlers=InputErrorHandlers( + on_unauthorised=unauthorised_f + ), + anti_csrf=anti_csrf, # type: ignore + override=session.InputOverrideConfig( + apis=apis_override_session, + functions=functions_override_session, + ), + ) + ], + telemetry=False, + ) + else: + init( + supertokens_config=SupertokensConfig(core_url), + app_info=InputAppInfo( + app_name="SuperTokens Python SDK", + api_domain="0.0.0.0:" + get_app_port(), + website_domain="http://localhost:8080", + ), + framework="litestar", + recipe_list=[ + session.init( + error_handlers=InputErrorHandlers(on_unauthorised=unauthorised_f), + anti_csrf=anti_csrf, # type: ignore + override=session.InputOverrideConfig(apis=apis_override_session), + ) + ], + telemetry=False, + ) + + +core_host = os.environ.get("SUPERTOKENS_CORE_HOST", "localhost") +core_port = os.environ.get("SUPERTOKENS_CORE_PORT", "3567") +config( + core_url=f"http://{core_host}:{core_port}", + enable_anti_csrf=True, + enable_jwt=False, + jwt_property_name=None, +) + + +@get("/index.html", media_type=MediaType.HTML, sync_to_thread=True) +def send_file() -> str: + return file_contents + + +def send_options_api_response() -> Response[Any]: + return Response(content="", status_code=200) + + +@route("/login", http_method=["OPTIONS"], sync_to_thread=True) +def login_options() -> Response[Any]: + return send_options_api_response() + + +@post("/login", media_type=MediaType.TEXT) +async def login(request: Request[Any, Any, Any]) -> str: + user_id = (await request.json())["userId"] + _session = await create_new_session(request, "public", RecipeUserId(user_id)) + return _session.get_user_id() + + +@post("/login-2.18", media_type=MediaType.TEXT) +async def login_218(request: Request[Any, Any, Any]) -> Response[Any]: + request_json = await request.json() + user_id = request_json["userId"] + payload = request_json["payload"] + + querier = Querier.get_instance() + Querier.api_version = "2.18" + legacy_session_resp = await querier.send_post_request( + NormalisedURLPath("/recipe/session"), + { + "userId": user_id, + "enableAntiCsrf": False, + "userDataInJWT": payload, + "userDataInDatabase": {}, + }, + {}, + ) + Querier.api_version = None + + front_token = b64encode( + json.dumps( + {"uid": user_id, "up": payload, "ate": time.time() * 1000 + 3600000} + ).encode("utf8") + ).decode("utf-8") + + return Response( + content="", + headers={ + "st-access-token": legacy_session_resp["accessToken"]["token"], + "st-refresh-token": legacy_session_resp["refreshToken"]["token"], + "front-token": front_token, + }, + status_code=200, + ) + + +@route("/beforeeach", http_method=["OPTIONS"], sync_to_thread=True) +def before_each_options() -> Response[Any]: + return send_options_api_response() + + +@post("/beforeeach", media_type=MediaType.TEXT, sync_to_thread=True) +def before_each() -> str: + Test.reset() + return "" + + +@route("/after", http_method=["OPTIONS"], sync_to_thread=True) +def afer_options() -> Response[Any]: + return send_options_api_response() + + +@post("/after", media_type=MediaType.TEXT, sync_to_thread=True) +def afer() -> str: + Test.reset() + return "" + + +@route("/testUserConfig", http_method=["OPTIONS"], sync_to_thread=True) +def test_user_config_options() -> Response[Any]: + return send_options_api_response() + + +@post("/testUserConfig", media_type=MediaType.TEXT, sync_to_thread=True) +def test_config() -> str: + return "" + + +@route("/multipleInterceptors", http_method=["OPTIONS"], sync_to_thread=True) +def multiple_interceptors_options() -> Response[Any]: + return send_options_api_response() + + +@post("/multipleInterceptors", media_type=MediaType.TEXT, sync_to_thread=True) +def multiple_interceptors(request: Request[Any, Any, Any]) -> str: + result_bool = ( + "success" + if "interceptorheader2" in request.headers + and "interceptorheader1" in request.headers + else "failure" + ) + return result_bool + + +@route("/", http_method=["OPTIONS"], sync_to_thread=True) +def options_root() -> Response[Any]: + return send_options_api_response() + + +@get("/", media_type=MediaType.TEXT) +async def get_info(request: Request[Any, Any, Any]) -> Response[Any]: + r_session = await verify_session()(request) + assert r_session is not None + Test.increment_get_session() + return Response( + content=r_session.get_user_id(), + headers={"Cache-Control": "no-cache, private"}, + status_code=200, + ) + + +@get("/check-rid-no-session", media_type=MediaType.TEXT, sync_to_thread=True) +def check_rid_no_session_api(request: Request[Any, Any, Any]) -> str: + rid = request.headers.get("rid") + return "failed" if rid is None else "success" + + +@route("/update-jwt", http_method=["OPTIONS"], sync_to_thread=True) +def update_options() -> Response[Any]: + return send_options_api_response() + + +@get("/update-jwt") +async def update_jwt(request: Request[Any, Any, Any]) -> Response[Any]: + sess = await verify_session()(request) + assert sess is not None + Test.increment_get_session() + return Response( + content=sess.get_access_token_payload(), + headers={"Cache-Control": "no-cache, private"}, + status_code=200, + ) + + +@post("/update-jwt") +async def update_jwt_post( + request: Request[Any, Any, Any], +) -> Response[Any]: + _session = await verify_session()(request) + assert _session is not None + clearing = {} + for k in _session.get_access_token_payload(): + if k not in protected_prop_name: + clearing[k] = None + + body = await request.json() + await _session.merge_into_access_token_payload({**clearing, **body}, {}) + + Test.increment_get_session() + return Response( + content=_session.get_access_token_payload(), + headers={"Cache-Control": "no-cache, private"}, + status_code=200, + ) + + +@post("/update-jwt-with-handle") +async def update_jwt_with_handle_post( + request: Request[Any, Any, Any], +) -> Response[Any]: + _session = await verify_session()(request) + assert _session is not None + info = await get_session_information(_session.get_handle()) + assert info is not None + clearing = {} + + for k in info.custom_claims_in_access_token_payload: + clearing[k] = None + + body = await request.json() + + await merge_into_access_token_payload(_session.get_handle(), {**clearing, **body}) + return Response( + content=_session.get_access_token_payload(), + headers={"Cache-Control": "no-cache, private"}, + status_code=200, + ) + + +def gcv_for_session_claim_err(*_): # type: ignore + class CustomValidator(SessionClaimValidator): + def should_refetch(self, payload: JSONObject, user_context: Dict[str, Any]): + return False + + async def validate(self, payload: JSONObject, user_context: Dict[str, Any]): + return ClaimValidationResult(False, {"message": "testReason"}) + + return [CustomValidator("test-claim-failing")] + + +@post("/session-claims-error") +async def session_claim_error_api(request: Request[Any, Any, Any]) -> Response[Any]: + await verify_session(override_global_claim_validators=gcv_for_session_claim_err)( # type: ignore + request + ) + return Response(content={}, status_code=200) + + +@post("/403-without-body", media_type=MediaType.TEXT, sync_to_thread=True) +def without_body_403() -> Response[Any]: + # send 403 without body + return Response(content=None, status_code=403) + + +@route("/testing", http_method=["OPTIONS"], sync_to_thread=True) +def testing_options() -> Response[Any]: + return send_options_api_response() + + +@get("/testing", media_type=MediaType.TEXT, sync_to_thread=True) +def testing(request: Request[Any, Any, Any]) -> Response[Any]: + if "testing" in request.headers: + return Response( + content="success", + headers={"testing": request.headers["testing"]}, + status_code=200, + ) + return Response(content="success", status_code=200) + + +@put("/testing", media_type=MediaType.TEXT, sync_to_thread=True) +def testing_put(request: Request[Any, Any, Any]) -> Response[Any]: + if "testing" in request.headers: + return Response( + content="success", + headers={"testing": request.headers["testing"]}, + status_code=200, + ) + return Response(content="success", status_code=200) + + +@post("/testing", media_type=MediaType.TEXT, sync_to_thread=True) +def testing_post(request: Request[Any, Any, Any]) -> Response[Any]: + if "testing" in request.headers: + return Response( + content="success", + headers={"testing": request.headers["testing"]}, + status_code=200, + ) + return Response(content="success", status_code=200) + + +@delete("/testing", media_type=MediaType.TEXT, sync_to_thread=True, status_code=200) +def testing_delete(request: Request[Any, Any, Any]) -> Response[Any]: + if "testing" in request.headers: + return Response( + content="success", + headers={"testing": request.headers["testing"]}, + status_code=200, + ) + return Response(content="success", status_code=200) + + +@route("/logout", http_method=["OPTIONS"], sync_to_thread=True) +def logout_options() -> Response[Any]: + return send_options_api_response() + + +@post("/logout", media_type=MediaType.TEXT) +async def logout(request: Request[Any, Any, Any]) -> str: + _session = await verify_session()(request) + assert _session is not None + await _session.revoke_session() + return "success" + + +@route("/revokeAll", http_method=["OPTIONS"], sync_to_thread=True) +def revoke_all_options() -> Response[Any]: + return send_options_api_response() + + +@post("/revokeAll", media_type=MediaType.TEXT) +async def revoke_all(request: Request[Any, Any, Any]) -> str: + _session = await verify_session()(request) + assert _session is not None + await revoke_all_sessions_for_user(_session.get_user_id()) + return "success" + + +@route("/refresh", http_method=["OPTIONS"], sync_to_thread=True) +def refresh_options() -> Response[Any]: + return send_options_api_response() + + +@get("/refreshAttemptedTime", media_type=MediaType.TEXT, sync_to_thread=True) +def refresh_attempted_time() -> Response[Any]: + return Response(content=str(Test.get_refresh_attempted_count()), status_code=200) + + +@post("/auth/session/refresh", media_type=MediaType.TEXT) +async def refresh(request: Request[Any, Any, Any]) -> str: + Test.increment_attempted_refresh() + try: + await verify_session()(request) + except Exception as e: + raise e + + if request.headers.get("rid") is None: + return "refresh failed" + Test.increment_refresh() + return "refresh success" + + +@route("/refreshCalledTime", http_method=["OPTIONS"], sync_to_thread=True) +def refresh_called_time_options() -> Response[Any]: + return send_options_api_response() + + +@get("/refreshCalledTime", media_type=MediaType.TEXT, sync_to_thread=True) +def refresh_called_time() -> Response[Any]: + return Response(content=str(Test.get_refresh_called_count()), status_code=200) + + +@route("/getSessionCalledTime", http_method=["OPTIONS"], sync_to_thread=True) +def get_session_called_time_options() -> Response[Any]: + return send_options_api_response() + + +@get("/getSessionCalledTime", media_type=MediaType.TEXT, sync_to_thread=True) +def get_session_called_time() -> Response[Any]: + return Response(content=str(Test.get_session_called_count()), status_code=200) + + +@route("/ping", http_method=["OPTIONS"], sync_to_thread=True) +def ping_options() -> Response[Any]: + return send_options_api_response() + + +@get("/ping", media_type=MediaType.TEXT, sync_to_thread=True) +def ping() -> str: + return "success" + + +@route("/testHeader", http_method=["OPTIONS"], sync_to_thread=True) +def test_header_options() -> Response[Any]: + return send_options_api_response() + + +@get("/testHeader", sync_to_thread=True) +def test_header(request: Request[Any, Any, Any]) -> Response[Any]: + success_info = request.headers.get("st-custom-header") + return Response({"success": success_info}, status_code=200) + + +@route("/checkDeviceInfo", http_method=["OPTIONS"], sync_to_thread=True) +def check_device_info_options() -> Response[Any]: + return send_options_api_response() + + +@get("/checkDeviceInfo", media_type=MediaType.TEXT, sync_to_thread=True) +def check_device_info(request: Request[Any, Any, Any]) -> str: + sdk_name = request.headers.get("supertokens-sdk-name") + sdk_version = request.headers.get("supertokens-sdk-version") + return "true" if sdk_name == "website" and isinstance(sdk_version, str) else "false" + + +@get("/check-rid", media_type=MediaType.TEXT, sync_to_thread=True) +def check_rid(request: Request[Any, Any, Any]) -> str: + rid = request.headers.get("rid") + + return "fail" if rid is None else "success" + + +@get("/featureFlags", sync_to_thread=True) +def feature_flags(_: Request[Any, Any, Any]) -> Response[Any]: + # print("Got into feature flags") + global last_set_enable_jwt # pylint: disable=global-variable-not-assigned + + return Response( + { + "sessionJwt": last_set_enable_jwt, + "sessionClaims": is_version_gte(VERSION, "0.11.0"), + "v3AccessToken": is_version_gte(VERSION, "0.13.0"), + "duplicateCookieHandling": is_version_gte(VERSION, "0.20.0"), + } + ) + + +@post("/reinitialiseBackendConfig", media_type=MediaType.TEXT) +async def reinitialize(request: Request[Any, Any, Any]) -> str: + global last_set_enable_jwt # pylint: disable=global-variable-not-assigned + global last_set_enable_anti_csrf # pylint: disable=global-variable-not-assigned + json = await request.json() + if "jwtPropertyName" not in json: + jwt_property_name = None + else: + jwt_property_name = json["jwtPropertyName"] + + Supertokens.reset() + SessionRecipe.reset() + MultitenancyRecipe.reset() + OpenIdRecipe.reset() + OAuth2ProviderRecipe.reset() + JWTRecipe.reset() + config( + json["coreUrl"], + last_set_enable_anti_csrf, + last_set_enable_jwt, + jwt_property_name, + ) + return "" + + +@post("/test/setup/st", media_type=MediaType.TEXT) +async def setup_st(request: Request[Any, Any, Any]) -> str: + global last_set_enable_jwt + global last_set_enable_anti_csrf + json = await request.json() + + Supertokens.reset() + SessionRecipe.reset() + MultitenancyRecipe.reset() + OpenIdRecipe.reset() + OAuth2ProviderRecipe.reset() + JWTRecipe.reset() + config( + core_url=json["coreUrl"], + enable_anti_csrf=json.get("enableAntiCsrf"), + enable_jwt=json.get("enableJWT"), + jwt_property_name=json.get("jwtPropertyName"), + ) + + last_set_enable_anti_csrf = json.get("enableAntiCsrf") + last_set_enable_jwt = json.get("enableJWT") + return "" + + +@route("/checkAllowCredentials", http_method=["OPTIONS"], sync_to_thread=True) +def check_allow_credentials_options() -> Response[Any]: + return send_options_api_response() + + +@get("/checkAllowCredentials", media_type=MediaType.TEXT, sync_to_thread=True) +def check_allow_credentials(request: Request[Any, Any, Any]) -> Response[Any]: + return Response( + content=json.dumps("allow-credentials" in request.headers), status_code=200 + ) + + +@route( + "/testError", + media_type=MediaType.TEXT, + http_method=["OPTIONS", "GET", "POST"], + sync_to_thread=True, +) +def test_error(request: Request[Any, Any, Any]) -> Response[Any]: + if request.method == "OPTIONS": + return send_options_api_response() + + status_code = int(request.query_params.get("code", "500")) + return Response(content="test error message", status_code=status_code) + + +# Create static files router for angular templates +static_files_router = create_static_files_router( + path="/angular", + directories=["templates/angular"], + name="angular", +) + +# Initialize Litestar app with all configurations +app = Litestar( + route_handlers=[ + send_file, + login_options, + login, + login_218, + before_each_options, + before_each, + afer_options, + afer, + test_user_config_options, + test_config, + multiple_interceptors_options, + multiple_interceptors, + options_root, + get_info, + check_rid_no_session_api, + update_options, + update_jwt, + update_jwt_post, + update_jwt_with_handle_post, + session_claim_error_api, + without_body_403, + testing_options, + testing, + testing_put, + testing_post, + testing_delete, + logout_options, + logout, + revoke_all_options, + revoke_all, + refresh_options, + refresh_attempted_time, + refresh, + refresh_called_time_options, + refresh_called_time, + get_session_called_time_options, + get_session_called_time, + ping_options, + ping, + test_header_options, + test_header, + check_device_info_options, + check_device_info, + check_rid, + feature_flags, + reinitialize, + setup_st, + check_allow_credentials_options, + check_allow_credentials, + test_error, + static_files_router, + ], + cors_config=CORSConfig( + allow_origins=["http://localhost:8080"], + allow_credentials=True, + allow_methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"], + allow_headers=["Content-Type"] + get_all_cors_headers(), + ), + plugins=[get_supertokens_plugin()], + middleware=[create_supertokens_middleware()], + exception_handlers=get_exception_handlers(), + debug=True, +) + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=8080) # type: ignore diff --git a/tests/frontendIntegration/litestar-server/templates/angular/main.js b/tests/frontendIntegration/litestar-server/templates/angular/main.js new file mode 100644 index 000000000..c9ab6e26d --- /dev/null +++ b/tests/frontendIntegration/litestar-server/templates/angular/main.js @@ -0,0 +1,214 @@ +"use strict"; +(self["webpackChunkwith_angular_thirdpartyemailpassword"] = + self["webpackChunkwith_angular_thirdpartyemailpassword"] || []).push([ + ["main"], + { + /***/ 5041: + /*!**********************************!*\ + !*** ./src/app/app.component.ts ***! + \**********************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ AppComponent: () => /* binding */ AppComponent + /* harmony export */ + }); + /* harmony import */ var _angular_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! @angular/core */ 3184 + ); + /* harmony import */ var _http_service__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./http.service */ 5876 + ); + + class AppComponent { + constructor(httpService) { + this.httpService = httpService; + this.title = "with-angular-thirdpartyemailpassword"; + window.angularHttpService = httpService; + } + } + AppComponent.ɵfac = function AppComponent_Factory(t) { + return new (t || AppComponent)( + _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdirectiveInject"]( + _http_service__WEBPACK_IMPORTED_MODULE_0__.HttpService + ) + ); + }; + AppComponent.ɵcmp = /*@__PURE__*/ _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({ + type: AppComponent, + selectors: [["app-root"]], + decls: 2, + vars: 0, + template: function AppComponent_Template(rf, ctx) { + if (rf & 1) { + _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div"); + _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "!!!"); + _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"](); + } + }, + encapsulation: 2 + }); + + /***/ + }, + + /***/ 6747: + /*!*******************************!*\ + !*** ./src/app/app.module.ts ***! + \*******************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ AppModule: () => /* binding */ AppModule + /* harmony export */ + }); + /* harmony import */ var _angular_platform_browser__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! @angular/platform-browser */ 318 + ); + /* harmony import */ var _app_component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./app.component */ 5041 + ); + /* harmony import */ var _angular_common_http__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! @angular/common/http */ 8784 + ); + /* harmony import */ var _angular_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! @angular/core */ 3184 + ); + + class AppModule {} + AppModule.ɵfac = function AppModule_Factory(t) { + return new (t || AppModule)(); + }; + AppModule.ɵmod = /*@__PURE__*/ _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineNgModule"]({ + type: AppModule, + bootstrap: [_app_component__WEBPACK_IMPORTED_MODULE_0__.AppComponent] + }); + AppModule.ɵinj = /*@__PURE__*/ _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjector"]({ + providers: [], + imports: [ + [ + _angular_platform_browser__WEBPACK_IMPORTED_MODULE_2__.BrowserModule, + _angular_common_http__WEBPACK_IMPORTED_MODULE_3__.HttpClientModule + ] + ] + }); + (function() { + (typeof ngJitMode === "undefined" || ngJitMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵsetNgModuleScope"](AppModule, { + declarations: [_app_component__WEBPACK_IMPORTED_MODULE_0__.AppComponent], + imports: [ + _angular_platform_browser__WEBPACK_IMPORTED_MODULE_2__.BrowserModule, + _angular_common_http__WEBPACK_IMPORTED_MODULE_3__.HttpClientModule + ] + }); + })(); + + /***/ + }, + + /***/ 5876: + /*!*********************************!*\ + !*** ./src/app/http.service.ts ***! + \*********************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ HttpService: () => /* binding */ HttpService + /* harmony export */ + }); + /* harmony import */ var _angular_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! @angular/core */ 3184 + ); + /* harmony import */ var _angular_common_http__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! @angular/common/http */ 8784 + ); + + class HttpService { + constructor(http) { + this.http = http; + window.angularHttpClient = http; + } + } + HttpService.ɵfac = function HttpService_Factory(t) { + return new (t || HttpService)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"]( + _angular_common_http__WEBPACK_IMPORTED_MODULE_1__.HttpClient + ) + ); + }; + HttpService.ɵprov = /*@__PURE__*/ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ + token: HttpService, + factory: HttpService.ɵfac, + providedIn: "root" + }); + + /***/ + }, + + /***/ 2340: + /*!*****************************************!*\ + !*** ./src/environments/environment.ts ***! + \*****************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ environment: () => /* binding */ environment + /* harmony export */ + }); + // This file can be replaced during build by using the `fileReplacements` array. + // `ng build` replaces `environment.ts` with `environment.prod.ts`. + // The list of file replacements can be found in `angular.json`. + const environment = { + production: false + }; + /* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ + // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. + + /***/ + }, + + /***/ 4431: + /*!*********************!*\ + !*** ./src/main.ts ***! + \*********************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony import */ var _angular_platform_browser__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! @angular/platform-browser */ 318 + ); + /* harmony import */ var _angular_core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! @angular/core */ 3184 + ); + /* harmony import */ var _app_app_module__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./app/app.module */ 6747 + ); + /* harmony import */ var _environments_environment__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./environments/environment */ 2340 + ); + + if (_environments_environment__WEBPACK_IMPORTED_MODULE_1__.environment.production) { + (0, _angular_core__WEBPACK_IMPORTED_MODULE_2__.enableProdMode)(); + } + _angular_platform_browser__WEBPACK_IMPORTED_MODULE_3__ + .platformBrowser() + .bootstrapModule(_app_app_module__WEBPACK_IMPORTED_MODULE_0__.AppModule) + .catch(err => console.error(err)); + + /***/ + } + }, + /******/ __webpack_require__ => { + // webpackRuntimeModules + /******/ var __webpack_exec__ = moduleId => __webpack_require__((__webpack_require__.s = moduleId)); + /******/ __webpack_require__.O(0, ["vendor"], () => __webpack_exec__(4431)); + /******/ var __webpack_exports__ = __webpack_require__.O(); + /******/ + } +]); +//# sourceMappingURL=main.js.map diff --git a/tests/frontendIntegration/litestar-server/templates/angular/polyfills.js b/tests/frontendIntegration/litestar-server/templates/angular/polyfills.js new file mode 100644 index 000000000..b8b46a74d --- /dev/null +++ b/tests/frontendIntegration/litestar-server/templates/angular/polyfills.js @@ -0,0 +1,3142 @@ +"use strict"; +(self["webpackChunkwith_angular_thirdpartyemailpassword"] = + self["webpackChunkwith_angular_thirdpartyemailpassword"] || []).push([ + ["polyfills"], + { + /***/ 7435: + /*!**************************!*\ + !*** ./src/polyfills.ts ***! + \**************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony import */ var zone_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! zone.js */ 4946 + ); + /* harmony import */ var zone_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/ __webpack_require__.n( + zone_js__WEBPACK_IMPORTED_MODULE_0__ + ); + /** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes recent versions of Safari, Chrome (including + * Opera), Edge on the desktop, and iOS and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + /*************************************************************************************************** + * BROWSER POLYFILLS + */ + /** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + /*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ + // Included with Angular CLI. + window.global = window; + /*************************************************************************************************** + * APPLICATION IMPORTS + */ + + /***/ + }, + + /***/ 4946: + /*!***********************************************!*\ + !*** ./node_modules/zone.js/fesm2015/zone.js ***! + \***********************************************/ + /***/ () => { + /** + * @license Angular v14.2.0-next.0 + * (c) 2010-2022 Google LLC. https://angular.io/ + * License: MIT + */ + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + (function(global) { + const performance = global["performance"]; + function mark(name) { + performance && performance["mark"] && performance["mark"](name); + } + function performanceMeasure(name, label) { + performance && performance["measure"] && performance["measure"](name, label); + } + mark("Zone"); + // Initialize before it's accessed below. + // __Zone_symbol_prefix global can be used to override the default zone + // symbol prefix with a custom one if needed. + const symbolPrefix = global["__Zone_symbol_prefix"] || "__zone_symbol__"; + function __symbol__(name) { + return symbolPrefix + name; + } + const checkDuplicate = global[__symbol__("forceDuplicateZoneCheck")] === true; + if (global["Zone"]) { + // if global['Zone'] already exists (maybe zone.js was already loaded or + // some other lib also registered a global object named Zone), we may need + // to throw an error, but sometimes user may not want this error. + // For example, + // we have two web pages, page1 includes zone.js, page2 doesn't. + // and the 1st time user load page1 and page2, everything work fine, + // but when user load page2 again, error occurs because global['Zone'] already exists. + // so we add a flag to let user choose whether to throw this error or not. + // By default, if existing Zone is from zone.js, we will not throw the error. + if (checkDuplicate || typeof global["Zone"].__symbol__ !== "function") { + throw new Error("Zone already loaded."); + } else { + return global["Zone"]; + } + } + class Zone { + constructor(parent, zoneSpec) { + this._parent = parent; + this._name = zoneSpec ? zoneSpec.name || "unnamed" : ""; + this._properties = (zoneSpec && zoneSpec.properties) || {}; + this._zoneDelegate = new _ZoneDelegate( + this, + this._parent && this._parent._zoneDelegate, + zoneSpec + ); + } + static assertZonePatched() { + if (global["Promise"] !== patches["ZoneAwarePromise"]) { + throw new Error( + "Zone.js has detected that ZoneAwarePromise `(window|global).Promise` " + + "has been overwritten.\n" + + "Most likely cause is that a Promise polyfill has been loaded " + + "after Zone.js (Polyfilling Promise api is not necessary when zone.js is loaded. " + + "If you must load one, do so before loading zone.js.)" + ); + } + } + static get root() { + let zone = Zone.current; + while (zone.parent) { + zone = zone.parent; + } + return zone; + } + static get current() { + return _currentZoneFrame.zone; + } + static get currentTask() { + return _currentTask; + } + // tslint:disable-next-line:require-internal-with-underscore + static __load_patch(name, fn, ignoreDuplicate = false) { + if (patches.hasOwnProperty(name)) { + // `checkDuplicate` option is defined from global variable + // so it works for all modules. + // `ignoreDuplicate` can work for the specified module + if (!ignoreDuplicate && checkDuplicate) { + throw Error("Already loaded patch: " + name); + } + } else if (!global["__Zone_disable_" + name]) { + const perfName = "Zone:" + name; + mark(perfName); + patches[name] = fn(global, Zone, _api); + performanceMeasure(perfName, perfName); + } + } + get parent() { + return this._parent; + } + get name() { + return this._name; + } + get(key) { + const zone = this.getZoneWith(key); + if (zone) return zone._properties[key]; + } + getZoneWith(key) { + let current = this; + while (current) { + if (current._properties.hasOwnProperty(key)) { + return current; + } + current = current._parent; + } + return null; + } + fork(zoneSpec) { + if (!zoneSpec) throw new Error("ZoneSpec required!"); + return this._zoneDelegate.fork(this, zoneSpec); + } + wrap(callback, source) { + if (typeof callback !== "function") { + throw new Error("Expecting function got: " + callback); + } + const _callback = this._zoneDelegate.intercept(this, callback, source); + const zone = this; + return function() { + return zone.runGuarded(_callback, this, arguments, source); + }; + } + run(callback, applyThis, applyArgs, source) { + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; + try { + return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); + } finally { + _currentZoneFrame = _currentZoneFrame.parent; + } + } + runGuarded(callback, applyThis = null, applyArgs, source) { + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; + try { + try { + return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); + } catch (error) { + if (this._zoneDelegate.handleError(this, error)) { + throw error; + } + } + } finally { + _currentZoneFrame = _currentZoneFrame.parent; + } + } + runTask(task, applyThis, applyArgs) { + if (task.zone != this) { + throw new Error( + "A task can only be run in the zone of creation! (Creation: " + + (task.zone || NO_ZONE).name + + "; Execution: " + + this.name + + ")" + ); + } + // https://github.com/angular/zone.js/issues/778, sometimes eventTask + // will run in notScheduled(canceled) state, we should not try to + // run such kind of task but just return + if (task.state === notScheduled && (task.type === eventTask || task.type === macroTask)) { + return; + } + const reEntryGuard = task.state != running; + reEntryGuard && task._transitionTo(running, scheduled); + task.runCount++; + const previousTask = _currentTask; + _currentTask = task; + _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; + try { + if (task.type == macroTask && task.data && !task.data.isPeriodic) { + task.cancelFn = undefined; + } + try { + return this._zoneDelegate.invokeTask(this, task, applyThis, applyArgs); + } catch (error) { + if (this._zoneDelegate.handleError(this, error)) { + throw error; + } + } + } finally { + // if the task's state is notScheduled or unknown, then it has already been cancelled + // we should not reset the state to scheduled + if (task.state !== notScheduled && task.state !== unknown) { + if (task.type == eventTask || (task.data && task.data.isPeriodic)) { + reEntryGuard && task._transitionTo(scheduled, running); + } else { + task.runCount = 0; + this._updateTaskCount(task, -1); + reEntryGuard && task._transitionTo(notScheduled, running, notScheduled); + } + } + _currentZoneFrame = _currentZoneFrame.parent; + _currentTask = previousTask; + } + } + scheduleTask(task) { + if (task.zone && task.zone !== this) { + // check if the task was rescheduled, the newZone + // should not be the children of the original zone + let newZone = this; + while (newZone) { + if (newZone === task.zone) { + throw Error( + `can not reschedule task to ${this.name} which is descendants of the original zone ${task.zone.name}` + ); + } + newZone = newZone.parent; + } + } + task._transitionTo(scheduling, notScheduled); + const zoneDelegates = []; + task._zoneDelegates = zoneDelegates; + task._zone = this; + try { + task = this._zoneDelegate.scheduleTask(this, task); + } catch (err) { + // should set task's state to unknown when scheduleTask throw error + // because the err may from reschedule, so the fromState maybe notScheduled + task._transitionTo(unknown, scheduling, notScheduled); + // TODO: @JiaLiPassion, should we check the result from handleError? + this._zoneDelegate.handleError(this, err); + throw err; + } + if (task._zoneDelegates === zoneDelegates) { + // we have to check because internally the delegate can reschedule the task. + this._updateTaskCount(task, 1); + } + if (task.state == scheduling) { + task._transitionTo(scheduled, scheduling); + } + return task; + } + scheduleMicroTask(source, callback, data, customSchedule) { + return this.scheduleTask( + new ZoneTask(microTask, source, callback, data, customSchedule, undefined) + ); + } + scheduleMacroTask(source, callback, data, customSchedule, customCancel) { + return this.scheduleTask( + new ZoneTask(macroTask, source, callback, data, customSchedule, customCancel) + ); + } + scheduleEventTask(source, callback, data, customSchedule, customCancel) { + return this.scheduleTask( + new ZoneTask(eventTask, source, callback, data, customSchedule, customCancel) + ); + } + cancelTask(task) { + if (task.zone != this) + throw new Error( + "A task can only be cancelled in the zone of creation! (Creation: " + + (task.zone || NO_ZONE).name + + "; Execution: " + + this.name + + ")" + ); + task._transitionTo(canceling, scheduled, running); + try { + this._zoneDelegate.cancelTask(this, task); + } catch (err) { + // if error occurs when cancelTask, transit the state to unknown + task._transitionTo(unknown, canceling); + this._zoneDelegate.handleError(this, err); + throw err; + } + this._updateTaskCount(task, -1); + task._transitionTo(notScheduled, canceling); + task.runCount = 0; + return task; + } + _updateTaskCount(task, count) { + const zoneDelegates = task._zoneDelegates; + if (count == -1) { + task._zoneDelegates = null; + } + for (let i = 0; i < zoneDelegates.length; i++) { + zoneDelegates[i]._updateTaskCount(task.type, count); + } + } + } + // tslint:disable-next-line:require-internal-with-underscore + Zone.__symbol__ = __symbol__; + const DELEGATE_ZS = { + name: "", + onHasTask: (delegate, _, target, hasTaskState) => delegate.hasTask(target, hasTaskState), + onScheduleTask: (delegate, _, target, task) => delegate.scheduleTask(target, task), + onInvokeTask: (delegate, _, target, task, applyThis, applyArgs) => + delegate.invokeTask(target, task, applyThis, applyArgs), + onCancelTask: (delegate, _, target, task) => delegate.cancelTask(target, task) + }; + class _ZoneDelegate { + constructor(zone, parentDelegate, zoneSpec) { + this._taskCounts = { microTask: 0, macroTask: 0, eventTask: 0 }; + this.zone = zone; + this._parentDelegate = parentDelegate; + this._forkZS = + zoneSpec && (zoneSpec && zoneSpec.onFork ? zoneSpec : parentDelegate._forkZS); + this._forkDlgt = zoneSpec && (zoneSpec.onFork ? parentDelegate : parentDelegate._forkDlgt); + this._forkCurrZone = + zoneSpec && (zoneSpec.onFork ? this.zone : parentDelegate._forkCurrZone); + this._interceptZS = + zoneSpec && (zoneSpec.onIntercept ? zoneSpec : parentDelegate._interceptZS); + this._interceptDlgt = + zoneSpec && (zoneSpec.onIntercept ? parentDelegate : parentDelegate._interceptDlgt); + this._interceptCurrZone = + zoneSpec && (zoneSpec.onIntercept ? this.zone : parentDelegate._interceptCurrZone); + this._invokeZS = zoneSpec && (zoneSpec.onInvoke ? zoneSpec : parentDelegate._invokeZS); + this._invokeDlgt = + zoneSpec && (zoneSpec.onInvoke ? parentDelegate : parentDelegate._invokeDlgt); + this._invokeCurrZone = + zoneSpec && (zoneSpec.onInvoke ? this.zone : parentDelegate._invokeCurrZone); + this._handleErrorZS = + zoneSpec && (zoneSpec.onHandleError ? zoneSpec : parentDelegate._handleErrorZS); + this._handleErrorDlgt = + zoneSpec && (zoneSpec.onHandleError ? parentDelegate : parentDelegate._handleErrorDlgt); + this._handleErrorCurrZone = + zoneSpec && (zoneSpec.onHandleError ? this.zone : parentDelegate._handleErrorCurrZone); + this._scheduleTaskZS = + zoneSpec && (zoneSpec.onScheduleTask ? zoneSpec : parentDelegate._scheduleTaskZS); + this._scheduleTaskDlgt = + zoneSpec && + (zoneSpec.onScheduleTask ? parentDelegate : parentDelegate._scheduleTaskDlgt); + this._scheduleTaskCurrZone = + zoneSpec && + (zoneSpec.onScheduleTask ? this.zone : parentDelegate._scheduleTaskCurrZone); + this._invokeTaskZS = + zoneSpec && (zoneSpec.onInvokeTask ? zoneSpec : parentDelegate._invokeTaskZS); + this._invokeTaskDlgt = + zoneSpec && (zoneSpec.onInvokeTask ? parentDelegate : parentDelegate._invokeTaskDlgt); + this._invokeTaskCurrZone = + zoneSpec && (zoneSpec.onInvokeTask ? this.zone : parentDelegate._invokeTaskCurrZone); + this._cancelTaskZS = + zoneSpec && (zoneSpec.onCancelTask ? zoneSpec : parentDelegate._cancelTaskZS); + this._cancelTaskDlgt = + zoneSpec && (zoneSpec.onCancelTask ? parentDelegate : parentDelegate._cancelTaskDlgt); + this._cancelTaskCurrZone = + zoneSpec && (zoneSpec.onCancelTask ? this.zone : parentDelegate._cancelTaskCurrZone); + this._hasTaskZS = null; + this._hasTaskDlgt = null; + this._hasTaskDlgtOwner = null; + this._hasTaskCurrZone = null; + const zoneSpecHasTask = zoneSpec && zoneSpec.onHasTask; + const parentHasTask = parentDelegate && parentDelegate._hasTaskZS; + if (zoneSpecHasTask || parentHasTask) { + // If we need to report hasTask, than this ZS needs to do ref counting on tasks. In such + // a case all task related interceptors must go through this ZD. We can't short circuit it. + this._hasTaskZS = zoneSpecHasTask ? zoneSpec : DELEGATE_ZS; + this._hasTaskDlgt = parentDelegate; + this._hasTaskDlgtOwner = this; + this._hasTaskCurrZone = zone; + if (!zoneSpec.onScheduleTask) { + this._scheduleTaskZS = DELEGATE_ZS; + this._scheduleTaskDlgt = parentDelegate; + this._scheduleTaskCurrZone = this.zone; + } + if (!zoneSpec.onInvokeTask) { + this._invokeTaskZS = DELEGATE_ZS; + this._invokeTaskDlgt = parentDelegate; + this._invokeTaskCurrZone = this.zone; + } + if (!zoneSpec.onCancelTask) { + this._cancelTaskZS = DELEGATE_ZS; + this._cancelTaskDlgt = parentDelegate; + this._cancelTaskCurrZone = this.zone; + } + } + } + fork(targetZone, zoneSpec) { + return this._forkZS + ? this._forkZS.onFork(this._forkDlgt, this.zone, targetZone, zoneSpec) + : new Zone(targetZone, zoneSpec); + } + intercept(targetZone, callback, source) { + return this._interceptZS + ? this._interceptZS.onIntercept( + this._interceptDlgt, + this._interceptCurrZone, + targetZone, + callback, + source + ) + : callback; + } + invoke(targetZone, callback, applyThis, applyArgs, source) { + return this._invokeZS + ? this._invokeZS.onInvoke( + this._invokeDlgt, + this._invokeCurrZone, + targetZone, + callback, + applyThis, + applyArgs, + source + ) + : callback.apply(applyThis, applyArgs); + } + handleError(targetZone, error) { + return this._handleErrorZS + ? this._handleErrorZS.onHandleError( + this._handleErrorDlgt, + this._handleErrorCurrZone, + targetZone, + error + ) + : true; + } + scheduleTask(targetZone, task) { + let returnTask = task; + if (this._scheduleTaskZS) { + if (this._hasTaskZS) { + returnTask._zoneDelegates.push(this._hasTaskDlgtOwner); + } + // clang-format off + returnTask = this._scheduleTaskZS.onScheduleTask( + this._scheduleTaskDlgt, + this._scheduleTaskCurrZone, + targetZone, + task + ); + // clang-format on + if (!returnTask) returnTask = task; + } else { + if (task.scheduleFn) { + task.scheduleFn(task); + } else if (task.type == microTask) { + scheduleMicroTask(task); + } else { + throw new Error("Task is missing scheduleFn."); + } + } + return returnTask; + } + invokeTask(targetZone, task, applyThis, applyArgs) { + return this._invokeTaskZS + ? this._invokeTaskZS.onInvokeTask( + this._invokeTaskDlgt, + this._invokeTaskCurrZone, + targetZone, + task, + applyThis, + applyArgs + ) + : task.callback.apply(applyThis, applyArgs); + } + cancelTask(targetZone, task) { + let value; + if (this._cancelTaskZS) { + value = this._cancelTaskZS.onCancelTask( + this._cancelTaskDlgt, + this._cancelTaskCurrZone, + targetZone, + task + ); + } else { + if (!task.cancelFn) { + throw Error("Task is not cancelable"); + } + value = task.cancelFn(task); + } + return value; + } + hasTask(targetZone, isEmpty) { + // hasTask should not throw error so other ZoneDelegate + // can still trigger hasTask callback + try { + this._hasTaskZS && + this._hasTaskZS.onHasTask( + this._hasTaskDlgt, + this._hasTaskCurrZone, + targetZone, + isEmpty + ); + } catch (err) { + this.handleError(targetZone, err); + } + } + // tslint:disable-next-line:require-internal-with-underscore + _updateTaskCount(type, count) { + const counts = this._taskCounts; + const prev = counts[type]; + const next = (counts[type] = prev + count); + if (next < 0) { + throw new Error("More tasks executed then were scheduled."); + } + if (prev == 0 || next == 0) { + const isEmpty = { + microTask: counts["microTask"] > 0, + macroTask: counts["macroTask"] > 0, + eventTask: counts["eventTask"] > 0, + change: type + }; + this.hasTask(this.zone, isEmpty); + } + } + } + class ZoneTask { + constructor(type, source, callback, options, scheduleFn, cancelFn) { + // tslint:disable-next-line:require-internal-with-underscore + this._zone = null; + this.runCount = 0; + // tslint:disable-next-line:require-internal-with-underscore + this._zoneDelegates = null; + // tslint:disable-next-line:require-internal-with-underscore + this._state = "notScheduled"; + this.type = type; + this.source = source; + this.data = options; + this.scheduleFn = scheduleFn; + this.cancelFn = cancelFn; + if (!callback) { + throw new Error("callback is not defined"); + } + this.callback = callback; + const self = this; + // TODO: @JiaLiPassion options should have interface + if (type === eventTask && options && options.useG) { + this.invoke = ZoneTask.invokeTask; + } else { + this.invoke = function() { + return ZoneTask.invokeTask.call(global, self, this, arguments); + }; + } + } + static invokeTask(task, target, args) { + if (!task) { + task = this; + } + _numberOfNestedTaskFrames++; + try { + task.runCount++; + return task.zone.runTask(task, target, args); + } finally { + if (_numberOfNestedTaskFrames == 1) { + drainMicroTaskQueue(); + } + _numberOfNestedTaskFrames--; + } + } + get zone() { + return this._zone; + } + get state() { + return this._state; + } + cancelScheduleRequest() { + this._transitionTo(notScheduled, scheduling); + } + // tslint:disable-next-line:require-internal-with-underscore + _transitionTo(toState, fromState1, fromState2) { + if (this._state === fromState1 || this._state === fromState2) { + this._state = toState; + if (toState == notScheduled) { + this._zoneDelegates = null; + } + } else { + throw new Error( + `${this.type} '${ + this.source + }': can not transition to '${toState}', expecting state '${fromState1}'${ + fromState2 ? " or '" + fromState2 + "'" : "" + }, was '${this._state}'.` + ); + } + } + toString() { + if (this.data && typeof this.data.handleId !== "undefined") { + return this.data.handleId.toString(); + } else { + return Object.prototype.toString.call(this); + } + } + // add toJSON method to prevent cyclic error when + // call JSON.stringify(zoneTask) + toJSON() { + return { + type: this.type, + state: this.state, + source: this.source, + zone: this.zone.name, + runCount: this.runCount + }; + } + } + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + /// MICROTASK QUEUE + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + const symbolSetTimeout = __symbol__("setTimeout"); + const symbolPromise = __symbol__("Promise"); + const symbolThen = __symbol__("then"); + let _microTaskQueue = []; + let _isDrainingMicrotaskQueue = false; + let nativeMicroTaskQueuePromise; + function nativeScheduleMicroTask(func) { + if (!nativeMicroTaskQueuePromise) { + if (global[symbolPromise]) { + nativeMicroTaskQueuePromise = global[symbolPromise].resolve(0); + } + } + if (nativeMicroTaskQueuePromise) { + let nativeThen = nativeMicroTaskQueuePromise[symbolThen]; + if (!nativeThen) { + // native Promise is not patchable, we need to use `then` directly + // issue 1078 + nativeThen = nativeMicroTaskQueuePromise["then"]; + } + nativeThen.call(nativeMicroTaskQueuePromise, func); + } else { + global[symbolSetTimeout](func, 0); + } + } + function scheduleMicroTask(task) { + // if we are not running in any task, and there has not been anything scheduled + // we must bootstrap the initial task creation by manually scheduling the drain + if (_numberOfNestedTaskFrames === 0 && _microTaskQueue.length === 0) { + // We are not running in Task, so we need to kickstart the microtask queue. + nativeScheduleMicroTask(drainMicroTaskQueue); + } + task && _microTaskQueue.push(task); + } + function drainMicroTaskQueue() { + if (!_isDrainingMicrotaskQueue) { + _isDrainingMicrotaskQueue = true; + while (_microTaskQueue.length) { + const queue = _microTaskQueue; + _microTaskQueue = []; + for (let i = 0; i < queue.length; i++) { + const task = queue[i]; + try { + task.zone.runTask(task, null, null); + } catch (error) { + _api.onUnhandledError(error); + } + } + } + _api.microtaskDrainDone(); + _isDrainingMicrotaskQueue = false; + } + } + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + /// BOOTSTRAP + ////////////////////////////////////////////////////// + ////////////////////////////////////////////////////// + const NO_ZONE = { name: "NO ZONE" }; + const notScheduled = "notScheduled", + scheduling = "scheduling", + scheduled = "scheduled", + running = "running", + canceling = "canceling", + unknown = "unknown"; + const microTask = "microTask", + macroTask = "macroTask", + eventTask = "eventTask"; + const patches = {}; + const _api = { + symbol: __symbol__, + currentZoneFrame: () => _currentZoneFrame, + onUnhandledError: noop, + microtaskDrainDone: noop, + scheduleMicroTask: scheduleMicroTask, + showUncaughtError: () => !Zone[__symbol__("ignoreConsoleErrorUncaughtError")], + patchEventTarget: () => [], + patchOnProperties: noop, + patchMethod: () => noop, + bindArguments: () => [], + patchThen: () => noop, + patchMacroTask: () => noop, + patchEventPrototype: () => noop, + isIEOrEdge: () => false, + getGlobalObjects: () => undefined, + ObjectDefineProperty: () => noop, + ObjectGetOwnPropertyDescriptor: () => undefined, + ObjectCreate: () => undefined, + ArraySlice: () => [], + patchClass: () => noop, + wrapWithCurrentZone: () => noop, + filterProperties: () => [], + attachOriginToPatched: () => noop, + _redefineProperty: () => noop, + patchCallbacks: () => noop, + nativeScheduleMicroTask: nativeScheduleMicroTask + }; + let _currentZoneFrame = { parent: null, zone: new Zone(null, null) }; + let _currentTask = null; + let _numberOfNestedTaskFrames = 0; + function noop() {} + performanceMeasure("Zone", "Zone"); + return (global["Zone"] = Zone); + })((typeof window !== "undefined" && window) || (typeof self !== "undefined" && self) || global); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + /** + * Suppress closure compiler errors about unknown 'Zone' variable + * @fileoverview + * @suppress {undefinedVars,globalThis,missingRequire} + */ + /// + // issue #989, to reduce bundle size, use short name + /** Object.getOwnPropertyDescriptor */ + const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + /** Object.defineProperty */ + const ObjectDefineProperty = Object.defineProperty; + /** Object.getPrototypeOf */ + const ObjectGetPrototypeOf = Object.getPrototypeOf; + /** Object.create */ + const ObjectCreate = Object.create; + /** Array.prototype.slice */ + const ArraySlice = Array.prototype.slice; + /** addEventListener string const */ + const ADD_EVENT_LISTENER_STR = "addEventListener"; + /** removeEventListener string const */ + const REMOVE_EVENT_LISTENER_STR = "removeEventListener"; + /** zoneSymbol addEventListener */ + const ZONE_SYMBOL_ADD_EVENT_LISTENER = Zone.__symbol__(ADD_EVENT_LISTENER_STR); + /** zoneSymbol removeEventListener */ + const ZONE_SYMBOL_REMOVE_EVENT_LISTENER = Zone.__symbol__(REMOVE_EVENT_LISTENER_STR); + /** true string const */ + const TRUE_STR = "true"; + /** false string const */ + const FALSE_STR = "false"; + /** Zone symbol prefix string const. */ + const ZONE_SYMBOL_PREFIX = Zone.__symbol__(""); + function wrapWithCurrentZone(callback, source) { + return Zone.current.wrap(callback, source); + } + function scheduleMacroTaskWithCurrentZone(source, callback, data, customSchedule, customCancel) { + return Zone.current.scheduleMacroTask(source, callback, data, customSchedule, customCancel); + } + const zoneSymbol = Zone.__symbol__; + const isWindowExists = typeof window !== "undefined"; + const internalWindow = isWindowExists ? window : undefined; + const _global = (isWindowExists && internalWindow) || (typeof self === "object" && self) || global; + const REMOVE_ATTRIBUTE = "removeAttribute"; + function bindArguments(args, source) { + for (let i = args.length - 1; i >= 0; i--) { + if (typeof args[i] === "function") { + args[i] = wrapWithCurrentZone(args[i], source + "_" + i); + } + } + return args; + } + function patchPrototype(prototype, fnNames) { + const source = prototype.constructor["name"]; + for (let i = 0; i < fnNames.length; i++) { + const name = fnNames[i]; + const delegate = prototype[name]; + if (delegate) { + const prototypeDesc = ObjectGetOwnPropertyDescriptor(prototype, name); + if (!isPropertyWritable(prototypeDesc)) { + continue; + } + prototype[name] = (delegate => { + const patched = function() { + return delegate.apply(this, bindArguments(arguments, source + "." + name)); + }; + attachOriginToPatched(patched, delegate); + return patched; + })(delegate); + } + } + } + function isPropertyWritable(propertyDesc) { + if (!propertyDesc) { + return true; + } + if (propertyDesc.writable === false) { + return false; + } + return !(typeof propertyDesc.get === "function" && typeof propertyDesc.set === "undefined"); + } + const isWebWorker = typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope; + // Make sure to access `process` through `_global` so that WebPack does not accidentally browserify + // this code. + const isNode = + !("nw" in _global) && + typeof _global.process !== "undefined" && + {}.toString.call(_global.process) === "[object process]"; + const isBrowser = !isNode && !isWebWorker && !!(isWindowExists && internalWindow["HTMLElement"]); + // we are in electron of nw, so we are both browser and nodejs + // Make sure to access `process` through `_global` so that WebPack does not accidentally browserify + // this code. + const isMix = + typeof _global.process !== "undefined" && + {}.toString.call(_global.process) === "[object process]" && + !isWebWorker && + !!(isWindowExists && internalWindow["HTMLElement"]); + const zoneSymbolEventNames$1 = {}; + const wrapFn = function(event) { + // https://github.com/angular/zone.js/issues/911, in IE, sometimes + // event will be undefined, so we need to use window.event + event = event || _global.event; + if (!event) { + return; + } + let eventNameSymbol = zoneSymbolEventNames$1[event.type]; + if (!eventNameSymbol) { + eventNameSymbol = zoneSymbolEventNames$1[event.type] = zoneSymbol("ON_PROPERTY" + event.type); + } + const target = this || event.target || _global; + const listener = target[eventNameSymbol]; + let result; + if (isBrowser && target === internalWindow && event.type === "error") { + // window.onerror have different signature + // https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror#window.onerror + // and onerror callback will prevent default when callback return true + const errorEvent = event; + result = + listener && + listener.call( + this, + errorEvent.message, + errorEvent.filename, + errorEvent.lineno, + errorEvent.colno, + errorEvent.error + ); + if (result === true) { + event.preventDefault(); + } + } else { + result = listener && listener.apply(this, arguments); + if (result != undefined && !result) { + event.preventDefault(); + } + } + return result; + }; + function patchProperty(obj, prop, prototype) { + let desc = ObjectGetOwnPropertyDescriptor(obj, prop); + if (!desc && prototype) { + // when patch window object, use prototype to check prop exist or not + const prototypeDesc = ObjectGetOwnPropertyDescriptor(prototype, prop); + if (prototypeDesc) { + desc = { enumerable: true, configurable: true }; + } + } + // if the descriptor not exists or is not configurable + // just return + if (!desc || !desc.configurable) { + return; + } + const onPropPatchedSymbol = zoneSymbol("on" + prop + "patched"); + if (obj.hasOwnProperty(onPropPatchedSymbol) && obj[onPropPatchedSymbol]) { + return; + } + // A property descriptor cannot have getter/setter and be writable + // deleting the writable and value properties avoids this error: + // + // TypeError: property descriptors must not specify a value or be writable when a + // getter or setter has been specified + delete desc.writable; + delete desc.value; + const originalDescGet = desc.get; + const originalDescSet = desc.set; + // slice(2) cuz 'onclick' -> 'click', etc + const eventName = prop.slice(2); + let eventNameSymbol = zoneSymbolEventNames$1[eventName]; + if (!eventNameSymbol) { + eventNameSymbol = zoneSymbolEventNames$1[eventName] = zoneSymbol("ON_PROPERTY" + eventName); + } + desc.set = function(newValue) { + // in some of windows's onproperty callback, this is undefined + // so we need to check it + let target = this; + if (!target && obj === _global) { + target = _global; + } + if (!target) { + return; + } + const previousValue = target[eventNameSymbol]; + if (typeof previousValue === "function") { + target.removeEventListener(eventName, wrapFn); + } + // issue #978, when onload handler was added before loading zone.js + // we should remove it with originalDescSet + originalDescSet && originalDescSet.call(target, null); + target[eventNameSymbol] = newValue; + if (typeof newValue === "function") { + target.addEventListener(eventName, wrapFn, false); + } + }; + // The getter would return undefined for unassigned properties but the default value of an + // unassigned property is null + desc.get = function() { + // in some of windows's onproperty callback, this is undefined + // so we need to check it + let target = this; + if (!target && obj === _global) { + target = _global; + } + if (!target) { + return null; + } + const listener = target[eventNameSymbol]; + if (listener) { + return listener; + } else if (originalDescGet) { + // result will be null when use inline event attribute, + // such as + // because the onclick function is internal raw uncompiled handler + // the onclick will be evaluated when first time event was triggered or + // the property is accessed, https://github.com/angular/zone.js/issues/525 + // so we should use original native get to retrieve the handler + let value = originalDescGet.call(this); + if (value) { + desc.set.call(this, value); + if (typeof target[REMOVE_ATTRIBUTE] === "function") { + target.removeAttribute(prop); + } + return value; + } + } + return null; + }; + ObjectDefineProperty(obj, prop, desc); + obj[onPropPatchedSymbol] = true; + } + function patchOnProperties(obj, properties, prototype) { + if (properties) { + for (let i = 0; i < properties.length; i++) { + patchProperty(obj, "on" + properties[i], prototype); + } + } else { + const onProperties = []; + for (const prop in obj) { + if (prop.slice(0, 2) == "on") { + onProperties.push(prop); + } + } + for (let j = 0; j < onProperties.length; j++) { + patchProperty(obj, onProperties[j], prototype); + } + } + } + const originalInstanceKey = zoneSymbol("originalInstance"); + // wrap some native API on `window` + function patchClass(className) { + const OriginalClass = _global[className]; + if (!OriginalClass) return; + // keep original class in global + _global[zoneSymbol(className)] = OriginalClass; + _global[className] = function() { + const a = bindArguments(arguments, className); + switch (a.length) { + case 0: + this[originalInstanceKey] = new OriginalClass(); + break; + case 1: + this[originalInstanceKey] = new OriginalClass(a[0]); + break; + case 2: + this[originalInstanceKey] = new OriginalClass(a[0], a[1]); + break; + case 3: + this[originalInstanceKey] = new OriginalClass(a[0], a[1], a[2]); + break; + case 4: + this[originalInstanceKey] = new OriginalClass(a[0], a[1], a[2], a[3]); + break; + default: + throw new Error("Arg list too long."); + } + }; + // attach original delegate to patched function + attachOriginToPatched(_global[className], OriginalClass); + const instance = new OriginalClass(function() {}); + let prop; + for (prop in instance) { + // https://bugs.webkit.org/show_bug.cgi?id=44721 + if (className === "XMLHttpRequest" && prop === "responseBlob") continue; + (function(prop) { + if (typeof instance[prop] === "function") { + _global[className].prototype[prop] = function() { + return this[originalInstanceKey][prop].apply(this[originalInstanceKey], arguments); + }; + } else { + ObjectDefineProperty(_global[className].prototype, prop, { + set: function(fn) { + if (typeof fn === "function") { + this[originalInstanceKey][prop] = wrapWithCurrentZone( + fn, + className + "." + prop + ); + // keep callback in wrapped function so we can + // use it in Function.prototype.toString to return + // the native one. + attachOriginToPatched(this[originalInstanceKey][prop], fn); + } else { + this[originalInstanceKey][prop] = fn; + } + }, + get: function() { + return this[originalInstanceKey][prop]; + } + }); + } + })(prop); + } + for (prop in OriginalClass) { + if (prop !== "prototype" && OriginalClass.hasOwnProperty(prop)) { + _global[className][prop] = OriginalClass[prop]; + } + } + } + function patchMethod(target, name, patchFn) { + let proto = target; + while (proto && !proto.hasOwnProperty(name)) { + proto = ObjectGetPrototypeOf(proto); + } + if (!proto && target[name]) { + // somehow we did not find it, but we can see it. This happens on IE for Window properties. + proto = target; + } + const delegateName = zoneSymbol(name); + let delegate = null; + if (proto && (!(delegate = proto[delegateName]) || !proto.hasOwnProperty(delegateName))) { + delegate = proto[delegateName] = proto[name]; + // check whether proto[name] is writable + // some property is readonly in safari, such as HtmlCanvasElement.prototype.toBlob + const desc = proto && ObjectGetOwnPropertyDescriptor(proto, name); + if (isPropertyWritable(desc)) { + const patchDelegate = patchFn(delegate, delegateName, name); + proto[name] = function() { + return patchDelegate(this, arguments); + }; + attachOriginToPatched(proto[name], delegate); + } + } + return delegate; + } + // TODO: @JiaLiPassion, support cancel task later if necessary + function patchMacroTask(obj, funcName, metaCreator) { + let setNative = null; + function scheduleTask(task) { + const data = task.data; + data.args[data.cbIdx] = function() { + task.invoke.apply(this, arguments); + }; + setNative.apply(data.target, data.args); + return task; + } + setNative = patchMethod( + obj, + funcName, + delegate => + function(self, args) { + const meta = metaCreator(self, args); + if (meta.cbIdx >= 0 && typeof args[meta.cbIdx] === "function") { + return scheduleMacroTaskWithCurrentZone( + meta.name, + args[meta.cbIdx], + meta, + scheduleTask + ); + } else { + // cause an error by calling it directly. + return delegate.apply(self, args); + } + } + ); + } + function attachOriginToPatched(patched, original) { + patched[zoneSymbol("OriginalDelegate")] = original; + } + let isDetectedIEOrEdge = false; + let ieOrEdge = false; + function isIE() { + try { + const ua = internalWindow.navigator.userAgent; + if (ua.indexOf("MSIE ") !== -1 || ua.indexOf("Trident/") !== -1) { + return true; + } + } catch (error) {} + return false; + } + function isIEOrEdge() { + if (isDetectedIEOrEdge) { + return ieOrEdge; + } + isDetectedIEOrEdge = true; + try { + const ua = internalWindow.navigator.userAgent; + if (ua.indexOf("MSIE ") !== -1 || ua.indexOf("Trident/") !== -1 || ua.indexOf("Edge/") !== -1) { + ieOrEdge = true; + } + } catch (error) {} + return ieOrEdge; + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + Zone.__load_patch("ZoneAwarePromise", (global, Zone, api) => { + const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + const ObjectDefineProperty = Object.defineProperty; + function readableObjectToString(obj) { + if (obj && obj.toString === Object.prototype.toString) { + const className = obj.constructor && obj.constructor.name; + return (className ? className : "") + ": " + JSON.stringify(obj); + } + return obj ? obj.toString() : Object.prototype.toString.call(obj); + } + const __symbol__ = api.symbol; + const _uncaughtPromiseErrors = []; + const isDisableWrappingUncaughtPromiseRejection = + global[__symbol__("DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION")] === true; + const symbolPromise = __symbol__("Promise"); + const symbolThen = __symbol__("then"); + const creationTrace = "__creationTrace__"; + api.onUnhandledError = e => { + if (api.showUncaughtError()) { + const rejection = e && e.rejection; + if (rejection) { + console.error( + "Unhandled Promise rejection:", + rejection instanceof Error ? rejection.message : rejection, + "; Zone:", + e.zone.name, + "; Task:", + e.task && e.task.source, + "; Value:", + rejection, + rejection instanceof Error ? rejection.stack : undefined + ); + } else { + console.error(e); + } + } + }; + api.microtaskDrainDone = () => { + while (_uncaughtPromiseErrors.length) { + const uncaughtPromiseError = _uncaughtPromiseErrors.shift(); + try { + uncaughtPromiseError.zone.runGuarded(() => { + if (uncaughtPromiseError.throwOriginal) { + throw uncaughtPromiseError.rejection; + } + throw uncaughtPromiseError; + }); + } catch (error) { + handleUnhandledRejection(error); + } + } + }; + const UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL = __symbol__("unhandledPromiseRejectionHandler"); + function handleUnhandledRejection(e) { + api.onUnhandledError(e); + try { + const handler = Zone[UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL]; + if (typeof handler === "function") { + handler.call(this, e); + } + } catch (err) {} + } + function isThenable(value) { + return value && value.then; + } + function forwardResolution(value) { + return value; + } + function forwardRejection(rejection) { + return ZoneAwarePromise.reject(rejection); + } + const symbolState = __symbol__("state"); + const symbolValue = __symbol__("value"); + const symbolFinally = __symbol__("finally"); + const symbolParentPromiseValue = __symbol__("parentPromiseValue"); + const symbolParentPromiseState = __symbol__("parentPromiseState"); + const source = "Promise.then"; + const UNRESOLVED = null; + const RESOLVED = true; + const REJECTED = false; + const REJECTED_NO_CATCH = 0; + function makeResolver(promise, state) { + return v => { + try { + resolvePromise(promise, state, v); + } catch (err) { + resolvePromise(promise, false, err); + } + // Do not return value or you will break the Promise spec. + }; + } + const once = function() { + let wasCalled = false; + return function wrapper(wrappedFunction) { + return function() { + if (wasCalled) { + return; + } + wasCalled = true; + wrappedFunction.apply(null, arguments); + }; + }; + }; + const TYPE_ERROR = "Promise resolved with itself"; + const CURRENT_TASK_TRACE_SYMBOL = __symbol__("currentTaskTrace"); + // Promise Resolution + function resolvePromise(promise, state, value) { + const onceWrapper = once(); + if (promise === value) { + throw new TypeError(TYPE_ERROR); + } + if (promise[symbolState] === UNRESOLVED) { + // should only get value.then once based on promise spec. + let then = null; + try { + if (typeof value === "object" || typeof value === "function") { + then = value && value.then; + } + } catch (err) { + onceWrapper(() => { + resolvePromise(promise, false, err); + })(); + return promise; + } + // if (value instanceof ZoneAwarePromise) { + if ( + state !== REJECTED && + value instanceof ZoneAwarePromise && + value.hasOwnProperty(symbolState) && + value.hasOwnProperty(symbolValue) && + value[symbolState] !== UNRESOLVED + ) { + clearRejectedNoCatch(value); + resolvePromise(promise, value[symbolState], value[symbolValue]); + } else if (state !== REJECTED && typeof then === "function") { + try { + then.call( + value, + onceWrapper(makeResolver(promise, state)), + onceWrapper(makeResolver(promise, false)) + ); + } catch (err) { + onceWrapper(() => { + resolvePromise(promise, false, err); + })(); + } + } else { + promise[symbolState] = state; + const queue = promise[symbolValue]; + promise[symbolValue] = value; + if (promise[symbolFinally] === symbolFinally) { + // the promise is generated by Promise.prototype.finally + if (state === RESOLVED) { + // the state is resolved, should ignore the value + // and use parent promise value + promise[symbolState] = promise[symbolParentPromiseState]; + promise[symbolValue] = promise[symbolParentPromiseValue]; + } + } + // record task information in value when error occurs, so we can + // do some additional work such as render longStackTrace + if (state === REJECTED && value instanceof Error) { + // check if longStackTraceZone is here + const trace = + Zone.currentTask && + Zone.currentTask.data && + Zone.currentTask.data[creationTrace]; + if (trace) { + // only keep the long stack trace into error when in longStackTraceZone + ObjectDefineProperty(value, CURRENT_TASK_TRACE_SYMBOL, { + configurable: true, + enumerable: false, + writable: true, + value: trace + }); + } + } + for (let i = 0; i < queue.length; ) { + scheduleResolveOrReject(promise, queue[i++], queue[i++], queue[i++], queue[i++]); + } + if (queue.length == 0 && state == REJECTED) { + promise[symbolState] = REJECTED_NO_CATCH; + let uncaughtPromiseError = value; + try { + // Here we throws a new Error to print more readable error log + // and if the value is not an error, zone.js builds an `Error` + // Object here to attach the stack information. + throw new Error( + "Uncaught (in promise): " + + readableObjectToString(value) + + (value && value.stack ? "\n" + value.stack : "") + ); + } catch (err) { + uncaughtPromiseError = err; + } + if (isDisableWrappingUncaughtPromiseRejection) { + // If disable wrapping uncaught promise reject + // use the value instead of wrapping it. + uncaughtPromiseError.throwOriginal = true; + } + uncaughtPromiseError.rejection = value; + uncaughtPromiseError.promise = promise; + uncaughtPromiseError.zone = Zone.current; + uncaughtPromiseError.task = Zone.currentTask; + _uncaughtPromiseErrors.push(uncaughtPromiseError); + api.scheduleMicroTask(); // to make sure that it is running + } + } + } + // Resolving an already resolved promise is a noop. + return promise; + } + const REJECTION_HANDLED_HANDLER = __symbol__("rejectionHandledHandler"); + function clearRejectedNoCatch(promise) { + if (promise[symbolState] === REJECTED_NO_CATCH) { + // if the promise is rejected no catch status + // and queue.length > 0, means there is a error handler + // here to handle the rejected promise, we should trigger + // windows.rejectionhandled eventHandler or nodejs rejectionHandled + // eventHandler + try { + const handler = Zone[REJECTION_HANDLED_HANDLER]; + if (handler && typeof handler === "function") { + handler.call(this, { rejection: promise[symbolValue], promise: promise }); + } + } catch (err) {} + promise[symbolState] = REJECTED; + for (let i = 0; i < _uncaughtPromiseErrors.length; i++) { + if (promise === _uncaughtPromiseErrors[i].promise) { + _uncaughtPromiseErrors.splice(i, 1); + } + } + } + } + function scheduleResolveOrReject(promise, zone, chainPromise, onFulfilled, onRejected) { + clearRejectedNoCatch(promise); + const promiseState = promise[symbolState]; + const delegate = promiseState + ? typeof onFulfilled === "function" + ? onFulfilled + : forwardResolution + : typeof onRejected === "function" + ? onRejected + : forwardRejection; + zone.scheduleMicroTask( + source, + () => { + try { + const parentPromiseValue = promise[symbolValue]; + const isFinallyPromise = + !!chainPromise && symbolFinally === chainPromise[symbolFinally]; + if (isFinallyPromise) { + // if the promise is generated from finally call, keep parent promise's state and value + chainPromise[symbolParentPromiseValue] = parentPromiseValue; + chainPromise[symbolParentPromiseState] = promiseState; + } + // should not pass value to finally callback + const value = zone.run( + delegate, + undefined, + isFinallyPromise && + delegate !== forwardRejection && + delegate !== forwardResolution + ? [] + : [parentPromiseValue] + ); + resolvePromise(chainPromise, true, value); + } catch (error) { + // if error occurs, should always return this error + resolvePromise(chainPromise, false, error); + } + }, + chainPromise + ); + } + const ZONE_AWARE_PROMISE_TO_STRING = "function ZoneAwarePromise() { [native code] }"; + const noop = function() {}; + const AggregateError = global.AggregateError; + class ZoneAwarePromise { + static toString() { + return ZONE_AWARE_PROMISE_TO_STRING; + } + static resolve(value) { + return resolvePromise(new this(null), RESOLVED, value); + } + static reject(error) { + return resolvePromise(new this(null), REJECTED, error); + } + static any(values) { + if (!values || typeof values[Symbol.iterator] !== "function") { + return Promise.reject(new AggregateError([], "All promises were rejected")); + } + const promises = []; + let count = 0; + try { + for (let v of values) { + count++; + promises.push(ZoneAwarePromise.resolve(v)); + } + } catch (err) { + return Promise.reject(new AggregateError([], "All promises were rejected")); + } + if (count === 0) { + return Promise.reject(new AggregateError([], "All promises were rejected")); + } + let finished = false; + const errors = []; + return new ZoneAwarePromise((resolve, reject) => { + for (let i = 0; i < promises.length; i++) { + promises[i].then( + v => { + if (finished) { + return; + } + finished = true; + resolve(v); + }, + err => { + errors.push(err); + count--; + if (count === 0) { + finished = true; + reject(new AggregateError(errors, "All promises were rejected")); + } + } + ); + } + }); + } + static race(values) { + let resolve; + let reject; + let promise = new this((res, rej) => { + resolve = res; + reject = rej; + }); + function onResolve(value) { + resolve(value); + } + function onReject(error) { + reject(error); + } + for (let value of values) { + if (!isThenable(value)) { + value = this.resolve(value); + } + value.then(onResolve, onReject); + } + return promise; + } + static all(values) { + return ZoneAwarePromise.allWithCallback(values); + } + static allSettled(values) { + const P = this && this.prototype instanceof ZoneAwarePromise ? this : ZoneAwarePromise; + return P.allWithCallback(values, { + thenCallback: value => ({ status: "fulfilled", value }), + errorCallback: err => ({ status: "rejected", reason: err }) + }); + } + static allWithCallback(values, callback) { + let resolve; + let reject; + let promise = new this((res, rej) => { + resolve = res; + reject = rej; + }); + // Start at 2 to prevent prematurely resolving if .then is called immediately. + let unresolvedCount = 2; + let valueIndex = 0; + const resolvedValues = []; + for (let value of values) { + if (!isThenable(value)) { + value = this.resolve(value); + } + const curValueIndex = valueIndex; + try { + value.then( + value => { + resolvedValues[curValueIndex] = callback + ? callback.thenCallback(value) + : value; + unresolvedCount--; + if (unresolvedCount === 0) { + resolve(resolvedValues); + } + }, + err => { + if (!callback) { + reject(err); + } else { + resolvedValues[curValueIndex] = callback.errorCallback(err); + unresolvedCount--; + if (unresolvedCount === 0) { + resolve(resolvedValues); + } + } + } + ); + } catch (thenErr) { + reject(thenErr); + } + unresolvedCount++; + valueIndex++; + } + // Make the unresolvedCount zero-based again. + unresolvedCount -= 2; + if (unresolvedCount === 0) { + resolve(resolvedValues); + } + return promise; + } + constructor(executor) { + const promise = this; + if (!(promise instanceof ZoneAwarePromise)) { + throw new Error("Must be an instanceof Promise."); + } + promise[symbolState] = UNRESOLVED; + promise[symbolValue] = []; // queue; + try { + const onceWrapper = once(); + executor && + executor( + onceWrapper(makeResolver(promise, RESOLVED)), + onceWrapper(makeResolver(promise, REJECTED)) + ); + } catch (error) { + resolvePromise(promise, false, error); + } + } + get [Symbol.toStringTag]() { + return "Promise"; + } + get [Symbol.species]() { + return ZoneAwarePromise; + } + then(onFulfilled, onRejected) { + var _a; + // We must read `Symbol.species` safely because `this` may be anything. For instance, `this` + // may be an object without a prototype (created through `Object.create(null)`); thus + // `this.constructor` will be undefined. One of the use cases is SystemJS creating + // prototype-less objects (modules) via `Object.create(null)`. The SystemJS creates an empty + // object and copies promise properties into that object (within the `getOrCreateLoad` + // function). The zone.js then checks if the resolved value has the `then` method and invokes + // it with the `value` context. Otherwise, this will throw an error: `TypeError: Cannot read + // properties of undefined (reading 'Symbol(Symbol.species)')`. + let C = (_a = this.constructor) === null || _a === void 0 ? void 0 : _a[Symbol.species]; + if (!C || typeof C !== "function") { + C = this.constructor || ZoneAwarePromise; + } + const chainPromise = new C(noop); + const zone = Zone.current; + if (this[symbolState] == UNRESOLVED) { + this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected); + } else { + scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected); + } + return chainPromise; + } + catch(onRejected) { + return this.then(null, onRejected); + } + finally(onFinally) { + var _a; + // See comment on the call to `then` about why thee `Symbol.species` is safely accessed. + let C = (_a = this.constructor) === null || _a === void 0 ? void 0 : _a[Symbol.species]; + if (!C || typeof C !== "function") { + C = ZoneAwarePromise; + } + const chainPromise = new C(noop); + chainPromise[symbolFinally] = symbolFinally; + const zone = Zone.current; + if (this[symbolState] == UNRESOLVED) { + this[symbolValue].push(zone, chainPromise, onFinally, onFinally); + } else { + scheduleResolveOrReject(this, zone, chainPromise, onFinally, onFinally); + } + return chainPromise; + } + } + // Protect against aggressive optimizers dropping seemingly unused properties. + // E.g. Closure Compiler in advanced mode. + ZoneAwarePromise["resolve"] = ZoneAwarePromise.resolve; + ZoneAwarePromise["reject"] = ZoneAwarePromise.reject; + ZoneAwarePromise["race"] = ZoneAwarePromise.race; + ZoneAwarePromise["all"] = ZoneAwarePromise.all; + const NativePromise = (global[symbolPromise] = global["Promise"]); + global["Promise"] = ZoneAwarePromise; + const symbolThenPatched = __symbol__("thenPatched"); + function patchThen(Ctor) { + const proto = Ctor.prototype; + const prop = ObjectGetOwnPropertyDescriptor(proto, "then"); + if (prop && (prop.writable === false || !prop.configurable)) { + // check Ctor.prototype.then propertyDescriptor is writable or not + // in meteor env, writable is false, we should ignore such case + return; + } + const originalThen = proto.then; + // Keep a reference to the original method. + proto[symbolThen] = originalThen; + Ctor.prototype.then = function(onResolve, onReject) { + const wrapped = new ZoneAwarePromise((resolve, reject) => { + originalThen.call(this, resolve, reject); + }); + return wrapped.then(onResolve, onReject); + }; + Ctor[symbolThenPatched] = true; + } + api.patchThen = patchThen; + function zoneify(fn) { + return function(self, args) { + let resultPromise = fn.apply(self, args); + if (resultPromise instanceof ZoneAwarePromise) { + return resultPromise; + } + let ctor = resultPromise.constructor; + if (!ctor[symbolThenPatched]) { + patchThen(ctor); + } + return resultPromise; + }; + } + if (NativePromise) { + patchThen(NativePromise); + patchMethod(global, "fetch", delegate => zoneify(delegate)); + } + // This is not part of public API, but it is useful for tests, so we expose it. + Promise[Zone.__symbol__("uncaughtPromiseErrors")] = _uncaughtPromiseErrors; + return ZoneAwarePromise; + }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + // override Function.prototype.toString to make zone.js patched function + // look like native function + Zone.__load_patch("toString", global => { + // patch Func.prototype.toString to let them look like native + const originalFunctionToString = Function.prototype.toString; + const ORIGINAL_DELEGATE_SYMBOL = zoneSymbol("OriginalDelegate"); + const PROMISE_SYMBOL = zoneSymbol("Promise"); + const ERROR_SYMBOL = zoneSymbol("Error"); + const newFunctionToString = function toString() { + if (typeof this === "function") { + const originalDelegate = this[ORIGINAL_DELEGATE_SYMBOL]; + if (originalDelegate) { + if (typeof originalDelegate === "function") { + return originalFunctionToString.call(originalDelegate); + } else { + return Object.prototype.toString.call(originalDelegate); + } + } + if (this === Promise) { + const nativePromise = global[PROMISE_SYMBOL]; + if (nativePromise) { + return originalFunctionToString.call(nativePromise); + } + } + if (this === Error) { + const nativeError = global[ERROR_SYMBOL]; + if (nativeError) { + return originalFunctionToString.call(nativeError); + } + } + } + return originalFunctionToString.call(this); + }; + newFunctionToString[ORIGINAL_DELEGATE_SYMBOL] = originalFunctionToString; + Function.prototype.toString = newFunctionToString; + // patch Object.prototype.toString to let them look like native + const originalObjectToString = Object.prototype.toString; + const PROMISE_OBJECT_TO_STRING = "[object Promise]"; + Object.prototype.toString = function() { + if (typeof Promise === "function" && this instanceof Promise) { + return PROMISE_OBJECT_TO_STRING; + } + return originalObjectToString.call(this); + }; + }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + let passiveSupported = false; + if (typeof window !== "undefined") { + try { + const options = Object.defineProperty({}, "passive", { + get: function() { + passiveSupported = true; + } + }); + // Note: We pass the `options` object as the event handler too. This is not compatible with the + // signature of `addEventListener` or `removeEventListener` but enables us to remove the handler + // without an actual handler. + window.addEventListener("test", options, options); + window.removeEventListener("test", options, options); + } catch (err) { + passiveSupported = false; + } + } + // an identifier to tell ZoneTask do not create a new invoke closure + const OPTIMIZED_ZONE_EVENT_TASK_DATA = { + useG: true + }; + const zoneSymbolEventNames = {}; + const globalSources = {}; + const EVENT_NAME_SYMBOL_REGX = new RegExp("^" + ZONE_SYMBOL_PREFIX + "(\\w+)(true|false)$"); + const IMMEDIATE_PROPAGATION_SYMBOL = zoneSymbol("propagationStopped"); + function prepareEventNames(eventName, eventNameToString) { + const falseEventName = (eventNameToString ? eventNameToString(eventName) : eventName) + FALSE_STR; + const trueEventName = (eventNameToString ? eventNameToString(eventName) : eventName) + TRUE_STR; + const symbol = ZONE_SYMBOL_PREFIX + falseEventName; + const symbolCapture = ZONE_SYMBOL_PREFIX + trueEventName; + zoneSymbolEventNames[eventName] = {}; + zoneSymbolEventNames[eventName][FALSE_STR] = symbol; + zoneSymbolEventNames[eventName][TRUE_STR] = symbolCapture; + } + function patchEventTarget(_global, api, apis, patchOptions) { + const ADD_EVENT_LISTENER = (patchOptions && patchOptions.add) || ADD_EVENT_LISTENER_STR; + const REMOVE_EVENT_LISTENER = (patchOptions && patchOptions.rm) || REMOVE_EVENT_LISTENER_STR; + const LISTENERS_EVENT_LISTENER = (patchOptions && patchOptions.listeners) || "eventListeners"; + const REMOVE_ALL_LISTENERS_EVENT_LISTENER = + (patchOptions && patchOptions.rmAll) || "removeAllListeners"; + const zoneSymbolAddEventListener = zoneSymbol(ADD_EVENT_LISTENER); + const ADD_EVENT_LISTENER_SOURCE = "." + ADD_EVENT_LISTENER + ":"; + const PREPEND_EVENT_LISTENER = "prependListener"; + const PREPEND_EVENT_LISTENER_SOURCE = "." + PREPEND_EVENT_LISTENER + ":"; + const invokeTask = function(task, target, event) { + // for better performance, check isRemoved which is set + // by removeEventListener + if (task.isRemoved) { + return; + } + const delegate = task.callback; + if (typeof delegate === "object" && delegate.handleEvent) { + // create the bind version of handleEvent when invoke + task.callback = event => delegate.handleEvent(event); + task.originalDelegate = delegate; + } + // invoke static task.invoke + // need to try/catch error here, otherwise, the error in one event listener + // will break the executions of the other event listeners. Also error will + // not remove the event listener when `once` options is true. + let error; + try { + task.invoke(task, target, [event]); + } catch (err) { + error = err; + } + const options = task.options; + if (options && typeof options === "object" && options.once) { + // if options.once is true, after invoke once remove listener here + // only browser need to do this, nodejs eventEmitter will cal removeListener + // inside EventEmitter.once + const delegate = task.originalDelegate ? task.originalDelegate : task.callback; + target[REMOVE_EVENT_LISTENER].call(target, event.type, delegate, options); + } + return error; + }; + function globalCallback(context, event, isCapture) { + // https://github.com/angular/zone.js/issues/911, in IE, sometimes + // event will be undefined, so we need to use window.event + event = event || _global.event; + if (!event) { + return; + } + // event.target is needed for Samsung TV and SourceBuffer + // || global is needed https://github.com/angular/zone.js/issues/190 + const target = context || event.target || _global; + const tasks = target[zoneSymbolEventNames[event.type][isCapture ? TRUE_STR : FALSE_STR]]; + if (tasks) { + const errors = []; + // invoke all tasks which attached to current target with given event.type and capture = false + // for performance concern, if task.length === 1, just invoke + if (tasks.length === 1) { + const err = invokeTask(tasks[0], target, event); + err && errors.push(err); + } else { + // https://github.com/angular/zone.js/issues/836 + // copy the tasks array before invoke, to avoid + // the callback will remove itself or other listener + const copyTasks = tasks.slice(); + for (let i = 0; i < copyTasks.length; i++) { + if (event && event[IMMEDIATE_PROPAGATION_SYMBOL] === true) { + break; + } + const err = invokeTask(copyTasks[i], target, event); + err && errors.push(err); + } + } + // Since there is only one error, we don't need to schedule microTask + // to throw the error. + if (errors.length === 1) { + throw errors[0]; + } else { + for (let i = 0; i < errors.length; i++) { + const err = errors[i]; + api.nativeScheduleMicroTask(() => { + throw err; + }); + } + } + } + } + // global shared zoneAwareCallback to handle all event callback with capture = false + const globalZoneAwareCallback = function(event) { + return globalCallback(this, event, false); + }; + // global shared zoneAwareCallback to handle all event callback with capture = true + const globalZoneAwareCaptureCallback = function(event) { + return globalCallback(this, event, true); + }; + function patchEventTargetMethods(obj, patchOptions) { + if (!obj) { + return false; + } + let useGlobalCallback = true; + if (patchOptions && patchOptions.useG !== undefined) { + useGlobalCallback = patchOptions.useG; + } + const validateHandler = patchOptions && patchOptions.vh; + let checkDuplicate = true; + if (patchOptions && patchOptions.chkDup !== undefined) { + checkDuplicate = patchOptions.chkDup; + } + let returnTarget = false; + if (patchOptions && patchOptions.rt !== undefined) { + returnTarget = patchOptions.rt; + } + let proto = obj; + while (proto && !proto.hasOwnProperty(ADD_EVENT_LISTENER)) { + proto = ObjectGetPrototypeOf(proto); + } + if (!proto && obj[ADD_EVENT_LISTENER]) { + // somehow we did not find it, but we can see it. This happens on IE for Window properties. + proto = obj; + } + if (!proto) { + return false; + } + if (proto[zoneSymbolAddEventListener]) { + return false; + } + const eventNameToString = patchOptions && patchOptions.eventNameToString; + // a shared global taskData to pass data for scheduleEventTask + // so we do not need to create a new object just for pass some data + const taskData = {}; + const nativeAddEventListener = (proto[zoneSymbolAddEventListener] = proto[ADD_EVENT_LISTENER]); + const nativeRemoveEventListener = (proto[zoneSymbol(REMOVE_EVENT_LISTENER)] = + proto[REMOVE_EVENT_LISTENER]); + const nativeListeners = (proto[zoneSymbol(LISTENERS_EVENT_LISTENER)] = + proto[LISTENERS_EVENT_LISTENER]); + const nativeRemoveAllListeners = (proto[zoneSymbol(REMOVE_ALL_LISTENERS_EVENT_LISTENER)] = + proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER]); + let nativePrependEventListener; + if (patchOptions && patchOptions.prepend) { + nativePrependEventListener = proto[zoneSymbol(patchOptions.prepend)] = + proto[patchOptions.prepend]; + } + /** + * This util function will build an option object with passive option + * to handle all possible input from the user. + */ + function buildEventListenerOptions(options, passive) { + if (!passiveSupported && typeof options === "object" && options) { + // doesn't support passive but user want to pass an object as options. + // this will not work on some old browser, so we just pass a boolean + // as useCapture parameter + return !!options.capture; + } + if (!passiveSupported || !passive) { + return options; + } + if (typeof options === "boolean") { + return { capture: options, passive: true }; + } + if (!options) { + return { passive: true }; + } + if (typeof options === "object" && options.passive !== false) { + return Object.assign(Object.assign({}, options), { passive: true }); + } + return options; + } + const customScheduleGlobal = function(task) { + // if there is already a task for the eventName + capture, + // just return, because we use the shared globalZoneAwareCallback here. + if (taskData.isExisting) { + return; + } + return nativeAddEventListener.call( + taskData.target, + taskData.eventName, + taskData.capture ? globalZoneAwareCaptureCallback : globalZoneAwareCallback, + taskData.options + ); + }; + const customCancelGlobal = function(task) { + // if task is not marked as isRemoved, this call is directly + // from Zone.prototype.cancelTask, we should remove the task + // from tasksList of target first + if (!task.isRemoved) { + const symbolEventNames = zoneSymbolEventNames[task.eventName]; + let symbolEventName; + if (symbolEventNames) { + symbolEventName = symbolEventNames[task.capture ? TRUE_STR : FALSE_STR]; + } + const existingTasks = symbolEventName && task.target[symbolEventName]; + if (existingTasks) { + for (let i = 0; i < existingTasks.length; i++) { + const existingTask = existingTasks[i]; + if (existingTask === task) { + existingTasks.splice(i, 1); + // set isRemoved to data for faster invokeTask check + task.isRemoved = true; + if (existingTasks.length === 0) { + // all tasks for the eventName + capture have gone, + // remove globalZoneAwareCallback and remove the task cache from target + task.allRemoved = true; + task.target[symbolEventName] = null; + } + break; + } + } + } + } + // if all tasks for the eventName + capture have gone, + // we will really remove the global event callback, + // if not, return + if (!task.allRemoved) { + return; + } + return nativeRemoveEventListener.call( + task.target, + task.eventName, + task.capture ? globalZoneAwareCaptureCallback : globalZoneAwareCallback, + task.options + ); + }; + const customScheduleNonGlobal = function(task) { + return nativeAddEventListener.call( + taskData.target, + taskData.eventName, + task.invoke, + taskData.options + ); + }; + const customSchedulePrepend = function(task) { + return nativePrependEventListener.call( + taskData.target, + taskData.eventName, + task.invoke, + taskData.options + ); + }; + const customCancelNonGlobal = function(task) { + return nativeRemoveEventListener.call( + task.target, + task.eventName, + task.invoke, + task.options + ); + }; + const customSchedule = useGlobalCallback ? customScheduleGlobal : customScheduleNonGlobal; + const customCancel = useGlobalCallback ? customCancelGlobal : customCancelNonGlobal; + const compareTaskCallbackVsDelegate = function(task, delegate) { + const typeOfDelegate = typeof delegate; + return ( + (typeOfDelegate === "function" && task.callback === delegate) || + (typeOfDelegate === "object" && task.originalDelegate === delegate) + ); + }; + const compare = + patchOptions && patchOptions.diff ? patchOptions.diff : compareTaskCallbackVsDelegate; + const unpatchedEvents = Zone[zoneSymbol("UNPATCHED_EVENTS")]; + const passiveEvents = _global[zoneSymbol("PASSIVE_EVENTS")]; + const makeAddListener = function( + nativeListener, + addSource, + customScheduleFn, + customCancelFn, + returnTarget = false, + prepend = false + ) { + return function() { + const target = this || _global; + let eventName = arguments[0]; + if (patchOptions && patchOptions.transferEventName) { + eventName = patchOptions.transferEventName(eventName); + } + let delegate = arguments[1]; + if (!delegate) { + return nativeListener.apply(this, arguments); + } + if (isNode && eventName === "uncaughtException") { + // don't patch uncaughtException of nodejs to prevent endless loop + return nativeListener.apply(this, arguments); + } + // don't create the bind delegate function for handleEvent + // case here to improve addEventListener performance + // we will create the bind delegate when invoke + let isHandleEvent = false; + if (typeof delegate !== "function") { + if (!delegate.handleEvent) { + return nativeListener.apply(this, arguments); + } + isHandleEvent = true; + } + if (validateHandler && !validateHandler(nativeListener, delegate, target, arguments)) { + return; + } + const passive = + passiveSupported && !!passiveEvents && passiveEvents.indexOf(eventName) !== -1; + const options = buildEventListenerOptions(arguments[2], passive); + if (unpatchedEvents) { + // check unpatched list + for (let i = 0; i < unpatchedEvents.length; i++) { + if (eventName === unpatchedEvents[i]) { + if (passive) { + return nativeListener.call(target, eventName, delegate, options); + } else { + return nativeListener.apply(this, arguments); + } + } + } + } + const capture = !options + ? false + : typeof options === "boolean" + ? true + : options.capture; + const once = options && typeof options === "object" ? options.once : false; + const zone = Zone.current; + let symbolEventNames = zoneSymbolEventNames[eventName]; + if (!symbolEventNames) { + prepareEventNames(eventName, eventNameToString); + symbolEventNames = zoneSymbolEventNames[eventName]; + } + const symbolEventName = symbolEventNames[capture ? TRUE_STR : FALSE_STR]; + let existingTasks = target[symbolEventName]; + let isExisting = false; + if (existingTasks) { + // already have task registered + isExisting = true; + if (checkDuplicate) { + for (let i = 0; i < existingTasks.length; i++) { + if (compare(existingTasks[i], delegate)) { + // same callback, same capture, same event name, just return + return; + } + } + } + } else { + existingTasks = target[symbolEventName] = []; + } + let source; + const constructorName = target.constructor["name"]; + const targetSource = globalSources[constructorName]; + if (targetSource) { + source = targetSource[eventName]; + } + if (!source) { + source = + constructorName + + addSource + + (eventNameToString ? eventNameToString(eventName) : eventName); + } + // do not create a new object as task.data to pass those things + // just use the global shared one + taskData.options = options; + if (once) { + // if addEventListener with once options, we don't pass it to + // native addEventListener, instead we keep the once setting + // and handle ourselves. + taskData.options.once = false; + } + taskData.target = target; + taskData.capture = capture; + taskData.eventName = eventName; + taskData.isExisting = isExisting; + const data = useGlobalCallback ? OPTIMIZED_ZONE_EVENT_TASK_DATA : undefined; + // keep taskData into data to allow onScheduleEventTask to access the task information + if (data) { + data.taskData = taskData; + } + const task = zone.scheduleEventTask( + source, + delegate, + data, + customScheduleFn, + customCancelFn + ); + // should clear taskData.target to avoid memory leak + // issue, https://github.com/angular/angular/issues/20442 + taskData.target = null; + // need to clear up taskData because it is a global object + if (data) { + data.taskData = null; + } + // have to save those information to task in case + // application may call task.zone.cancelTask() directly + if (once) { + options.once = true; + } + if (!(!passiveSupported && typeof task.options === "boolean")) { + // if not support passive, and we pass an option object + // to addEventListener, we should save the options to task + task.options = options; + } + task.target = target; + task.capture = capture; + task.eventName = eventName; + if (isHandleEvent) { + // save original delegate for compare to check duplicate + task.originalDelegate = delegate; + } + if (!prepend) { + existingTasks.push(task); + } else { + existingTasks.unshift(task); + } + if (returnTarget) { + return target; + } + }; + }; + proto[ADD_EVENT_LISTENER] = makeAddListener( + nativeAddEventListener, + ADD_EVENT_LISTENER_SOURCE, + customSchedule, + customCancel, + returnTarget + ); + if (nativePrependEventListener) { + proto[PREPEND_EVENT_LISTENER] = makeAddListener( + nativePrependEventListener, + PREPEND_EVENT_LISTENER_SOURCE, + customSchedulePrepend, + customCancel, + returnTarget, + true + ); + } + proto[REMOVE_EVENT_LISTENER] = function() { + const target = this || _global; + let eventName = arguments[0]; + if (patchOptions && patchOptions.transferEventName) { + eventName = patchOptions.transferEventName(eventName); + } + const options = arguments[2]; + const capture = !options ? false : typeof options === "boolean" ? true : options.capture; + const delegate = arguments[1]; + if (!delegate) { + return nativeRemoveEventListener.apply(this, arguments); + } + if ( + validateHandler && + !validateHandler(nativeRemoveEventListener, delegate, target, arguments) + ) { + return; + } + const symbolEventNames = zoneSymbolEventNames[eventName]; + let symbolEventName; + if (symbolEventNames) { + symbolEventName = symbolEventNames[capture ? TRUE_STR : FALSE_STR]; + } + const existingTasks = symbolEventName && target[symbolEventName]; + if (existingTasks) { + for (let i = 0; i < existingTasks.length; i++) { + const existingTask = existingTasks[i]; + if (compare(existingTask, delegate)) { + existingTasks.splice(i, 1); + // set isRemoved to data for faster invokeTask check + existingTask.isRemoved = true; + if (existingTasks.length === 0) { + // all tasks for the eventName + capture have gone, + // remove globalZoneAwareCallback and remove the task cache from target + existingTask.allRemoved = true; + target[symbolEventName] = null; + // in the target, we have an event listener which is added by on_property + // such as target.onclick = function() {}, so we need to clear this internal + // property too if all delegates all removed + if (typeof eventName === "string") { + const onPropertySymbol = ZONE_SYMBOL_PREFIX + "ON_PROPERTY" + eventName; + target[onPropertySymbol] = null; + } + } + existingTask.zone.cancelTask(existingTask); + if (returnTarget) { + return target; + } + return; + } + } + } + // issue 930, didn't find the event name or callback + // from zone kept existingTasks, the callback maybe + // added outside of zone, we need to call native removeEventListener + // to try to remove it. + return nativeRemoveEventListener.apply(this, arguments); + }; + proto[LISTENERS_EVENT_LISTENER] = function() { + const target = this || _global; + let eventName = arguments[0]; + if (patchOptions && patchOptions.transferEventName) { + eventName = patchOptions.transferEventName(eventName); + } + const listeners = []; + const tasks = findEventTasks( + target, + eventNameToString ? eventNameToString(eventName) : eventName + ); + for (let i = 0; i < tasks.length; i++) { + const task = tasks[i]; + let delegate = task.originalDelegate ? task.originalDelegate : task.callback; + listeners.push(delegate); + } + return listeners; + }; + proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER] = function() { + const target = this || _global; + let eventName = arguments[0]; + if (!eventName) { + const keys = Object.keys(target); + for (let i = 0; i < keys.length; i++) { + const prop = keys[i]; + const match = EVENT_NAME_SYMBOL_REGX.exec(prop); + let evtName = match && match[1]; + // in nodejs EventEmitter, removeListener event is + // used for monitoring the removeListener call, + // so just keep removeListener eventListener until + // all other eventListeners are removed + if (evtName && evtName !== "removeListener") { + this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, evtName); + } + } + // remove removeListener listener finally + this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, "removeListener"); + } else { + if (patchOptions && patchOptions.transferEventName) { + eventName = patchOptions.transferEventName(eventName); + } + const symbolEventNames = zoneSymbolEventNames[eventName]; + if (symbolEventNames) { + const symbolEventName = symbolEventNames[FALSE_STR]; + const symbolCaptureEventName = symbolEventNames[TRUE_STR]; + const tasks = target[symbolEventName]; + const captureTasks = target[symbolCaptureEventName]; + if (tasks) { + const removeTasks = tasks.slice(); + for (let i = 0; i < removeTasks.length; i++) { + const task = removeTasks[i]; + let delegate = task.originalDelegate + ? task.originalDelegate + : task.callback; + this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options); + } + } + if (captureTasks) { + const removeTasks = captureTasks.slice(); + for (let i = 0; i < removeTasks.length; i++) { + const task = removeTasks[i]; + let delegate = task.originalDelegate + ? task.originalDelegate + : task.callback; + this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options); + } + } + } + } + if (returnTarget) { + return this; + } + }; + // for native toString patch + attachOriginToPatched(proto[ADD_EVENT_LISTENER], nativeAddEventListener); + attachOriginToPatched(proto[REMOVE_EVENT_LISTENER], nativeRemoveEventListener); + if (nativeRemoveAllListeners) { + attachOriginToPatched(proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER], nativeRemoveAllListeners); + } + if (nativeListeners) { + attachOriginToPatched(proto[LISTENERS_EVENT_LISTENER], nativeListeners); + } + return true; + } + let results = []; + for (let i = 0; i < apis.length; i++) { + results[i] = patchEventTargetMethods(apis[i], patchOptions); + } + return results; + } + function findEventTasks(target, eventName) { + if (!eventName) { + const foundTasks = []; + for (let prop in target) { + const match = EVENT_NAME_SYMBOL_REGX.exec(prop); + let evtName = match && match[1]; + if (evtName && (!eventName || evtName === eventName)) { + const tasks = target[prop]; + if (tasks) { + for (let i = 0; i < tasks.length; i++) { + foundTasks.push(tasks[i]); + } + } + } + } + return foundTasks; + } + let symbolEventName = zoneSymbolEventNames[eventName]; + if (!symbolEventName) { + prepareEventNames(eventName); + symbolEventName = zoneSymbolEventNames[eventName]; + } + const captureFalseTasks = target[symbolEventName[FALSE_STR]]; + const captureTrueTasks = target[symbolEventName[TRUE_STR]]; + if (!captureFalseTasks) { + return captureTrueTasks ? captureTrueTasks.slice() : []; + } else { + return captureTrueTasks + ? captureFalseTasks.concat(captureTrueTasks) + : captureFalseTasks.slice(); + } + } + function patchEventPrototype(global, api) { + const Event = global["Event"]; + if (Event && Event.prototype) { + api.patchMethod( + Event.prototype, + "stopImmediatePropagation", + delegate => + function(self, args) { + self[IMMEDIATE_PROPAGATION_SYMBOL] = true; + // we need to call the native stopImmediatePropagation + // in case in some hybrid application, some part of + // application will be controlled by zone, some are not + delegate && delegate.apply(self, args); + } + ); + } + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + function patchCallbacks(api, target, targetName, method, callbacks) { + const symbol = Zone.__symbol__(method); + if (target[symbol]) { + return; + } + const nativeDelegate = (target[symbol] = target[method]); + target[method] = function(name, opts, options) { + if (opts && opts.prototype) { + callbacks.forEach(function(callback) { + const source = `${targetName}.${method}::` + callback; + const prototype = opts.prototype; + // Note: the `patchCallbacks` is used for patching the `document.registerElement` and + // `customElements.define`. We explicitly wrap the patching code into try-catch since + // callbacks may be already patched by other web components frameworks (e.g. LWC), and they + // make those properties non-writable. This means that patching callback will throw an error + // `cannot assign to read-only property`. See this code as an example: + // https://github.com/salesforce/lwc/blob/master/packages/@lwc/engine-core/src/framework/base-bridge-element.ts#L180-L186 + // We don't want to stop the application rendering if we couldn't patch some + // callback, e.g. `attributeChangedCallback`. + try { + if (prototype.hasOwnProperty(callback)) { + const descriptor = api.ObjectGetOwnPropertyDescriptor(prototype, callback); + if (descriptor && descriptor.value) { + descriptor.value = api.wrapWithCurrentZone(descriptor.value, source); + api._redefineProperty(opts.prototype, callback, descriptor); + } else if (prototype[callback]) { + prototype[callback] = api.wrapWithCurrentZone(prototype[callback], source); + } + } else if (prototype[callback]) { + prototype[callback] = api.wrapWithCurrentZone(prototype[callback], source); + } + } catch (_a) { + // Note: we leave the catch block empty since there's no way to handle the error related + // to non-writable property. + } + }); + } + return nativeDelegate.call(target, name, opts, options); + }; + api.attachOriginToPatched(target[method], nativeDelegate); + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + function filterProperties(target, onProperties, ignoreProperties) { + if (!ignoreProperties || ignoreProperties.length === 0) { + return onProperties; + } + const tip = ignoreProperties.filter(ip => ip.target === target); + if (!tip || tip.length === 0) { + return onProperties; + } + const targetIgnoreProperties = tip[0].ignoreProperties; + return onProperties.filter(op => targetIgnoreProperties.indexOf(op) === -1); + } + function patchFilteredProperties(target, onProperties, ignoreProperties, prototype) { + // check whether target is available, sometimes target will be undefined + // because different browser or some 3rd party plugin. + if (!target) { + return; + } + const filteredProperties = filterProperties(target, onProperties, ignoreProperties); + patchOnProperties(target, filteredProperties, prototype); + } + /** + * Get all event name properties which the event name startsWith `on` + * from the target object itself, inherited properties are not considered. + */ + function getOnEventNames(target) { + return Object.getOwnPropertyNames(target) + .filter(name => name.startsWith("on") && name.length > 2) + .map(name => name.substring(2)); + } + function propertyDescriptorPatch(api, _global) { + if (isNode && !isMix) { + return; + } + if (Zone[api.symbol("patchEvents")]) { + // events are already been patched by legacy patch. + return; + } + const ignoreProperties = _global["__Zone_ignore_on_properties"]; + // for browsers that we can patch the descriptor: Chrome & Firefox + let patchTargets = []; + if (isBrowser) { + const internalWindow = window; + patchTargets = patchTargets.concat([ + "Document", + "SVGElement", + "Element", + "HTMLElement", + "HTMLBodyElement", + "HTMLMediaElement", + "HTMLFrameSetElement", + "HTMLFrameElement", + "HTMLIFrameElement", + "HTMLMarqueeElement", + "Worker" + ]); + const ignoreErrorProperties = isIE() + ? [{ target: internalWindow, ignoreProperties: ["error"] }] + : []; + // in IE/Edge, onProp not exist in window object, but in WindowPrototype + // so we need to pass WindowPrototype to check onProp exist or not + patchFilteredProperties( + internalWindow, + getOnEventNames(internalWindow), + ignoreProperties ? ignoreProperties.concat(ignoreErrorProperties) : ignoreProperties, + ObjectGetPrototypeOf(internalWindow) + ); + } + patchTargets = patchTargets.concat([ + "XMLHttpRequest", + "XMLHttpRequestEventTarget", + "IDBIndex", + "IDBRequest", + "IDBOpenDBRequest", + "IDBDatabase", + "IDBTransaction", + "IDBCursor", + "WebSocket" + ]); + for (let i = 0; i < patchTargets.length; i++) { + const target = _global[patchTargets[i]]; + target && + target.prototype && + patchFilteredProperties( + target.prototype, + getOnEventNames(target.prototype), + ignoreProperties + ); + } + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + Zone.__load_patch("util", (global, Zone, api) => { + // Collect native event names by looking at properties + // on the global namespace, e.g. 'onclick'. + const eventNames = getOnEventNames(global); + api.patchOnProperties = patchOnProperties; + api.patchMethod = patchMethod; + api.bindArguments = bindArguments; + api.patchMacroTask = patchMacroTask; + // In earlier version of zone.js (<0.9.0), we use env name `__zone_symbol__BLACK_LISTED_EVENTS` to + // define which events will not be patched by `Zone.js`. + // In newer version (>=0.9.0), we change the env name to `__zone_symbol__UNPATCHED_EVENTS` to keep + // the name consistent with angular repo. + // The `__zone_symbol__BLACK_LISTED_EVENTS` is deprecated, but it is still be supported for + // backwards compatibility. + const SYMBOL_BLACK_LISTED_EVENTS = Zone.__symbol__("BLACK_LISTED_EVENTS"); + const SYMBOL_UNPATCHED_EVENTS = Zone.__symbol__("UNPATCHED_EVENTS"); + if (global[SYMBOL_UNPATCHED_EVENTS]) { + global[SYMBOL_BLACK_LISTED_EVENTS] = global[SYMBOL_UNPATCHED_EVENTS]; + } + if (global[SYMBOL_BLACK_LISTED_EVENTS]) { + Zone[SYMBOL_BLACK_LISTED_EVENTS] = Zone[SYMBOL_UNPATCHED_EVENTS] = + global[SYMBOL_BLACK_LISTED_EVENTS]; + } + api.patchEventPrototype = patchEventPrototype; + api.patchEventTarget = patchEventTarget; + api.isIEOrEdge = isIEOrEdge; + api.ObjectDefineProperty = ObjectDefineProperty; + api.ObjectGetOwnPropertyDescriptor = ObjectGetOwnPropertyDescriptor; + api.ObjectCreate = ObjectCreate; + api.ArraySlice = ArraySlice; + api.patchClass = patchClass; + api.wrapWithCurrentZone = wrapWithCurrentZone; + api.filterProperties = filterProperties; + api.attachOriginToPatched = attachOriginToPatched; + api._redefineProperty = Object.defineProperty; + api.patchCallbacks = patchCallbacks; + api.getGlobalObjects = () => ({ + globalSources, + zoneSymbolEventNames, + eventNames, + isBrowser, + isMix, + isNode, + TRUE_STR, + FALSE_STR, + ZONE_SYMBOL_PREFIX, + ADD_EVENT_LISTENER_STR, + REMOVE_EVENT_LISTENER_STR + }); + }); + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + const taskSymbol = zoneSymbol("zoneTask"); + function patchTimer(window, setName, cancelName, nameSuffix) { + let setNative = null; + let clearNative = null; + setName += nameSuffix; + cancelName += nameSuffix; + const tasksByHandleId = {}; + function scheduleTask(task) { + const data = task.data; + data.args[0] = function() { + return task.invoke.apply(this, arguments); + }; + data.handleId = setNative.apply(window, data.args); + return task; + } + function clearTask(task) { + return clearNative.call(window, task.data.handleId); + } + setNative = patchMethod( + window, + setName, + delegate => + function(self, args) { + if (typeof args[0] === "function") { + const options = { + isPeriodic: nameSuffix === "Interval", + delay: + nameSuffix === "Timeout" || nameSuffix === "Interval" + ? args[1] || 0 + : undefined, + args: args + }; + const callback = args[0]; + args[0] = function timer() { + try { + return callback.apply(this, arguments); + } finally { + // issue-934, task will be cancelled + // even it is a periodic task such as + // setInterval + // https://github.com/angular/angular/issues/40387 + // Cleanup tasksByHandleId should be handled before scheduleTask + // Since some zoneSpec may intercept and doesn't trigger + // scheduleFn(scheduleTask) provided here. + if (!options.isPeriodic) { + if (typeof options.handleId === "number") { + // in non-nodejs env, we remove timerId + // from local cache + delete tasksByHandleId[options.handleId]; + } else if (options.handleId) { + // Node returns complex objects as handleIds + // we remove task reference from timer object + options.handleId[taskSymbol] = null; + } + } + } + }; + const task = scheduleMacroTaskWithCurrentZone( + setName, + args[0], + options, + scheduleTask, + clearTask + ); + if (!task) { + return task; + } + // Node.js must additionally support the ref and unref functions. + const handle = task.data.handleId; + if (typeof handle === "number") { + // for non nodejs env, we save handleId: task + // mapping in local cache for clearTimeout + tasksByHandleId[handle] = task; + } else if (handle) { + // for nodejs env, we save task + // reference in timerId Object for clearTimeout + handle[taskSymbol] = task; + } + // check whether handle is null, because some polyfill or browser + // may return undefined from setTimeout/setInterval/setImmediate/requestAnimationFrame + if ( + handle && + handle.ref && + handle.unref && + typeof handle.ref === "function" && + typeof handle.unref === "function" + ) { + task.ref = handle.ref.bind(handle); + task.unref = handle.unref.bind(handle); + } + if (typeof handle === "number" || handle) { + return handle; + } + return task; + } else { + // cause an error by calling it directly. + return delegate.apply(window, args); + } + } + ); + clearNative = patchMethod( + window, + cancelName, + delegate => + function(self, args) { + const id = args[0]; + let task; + if (typeof id === "number") { + // non nodejs env. + task = tasksByHandleId[id]; + } else { + // nodejs env. + task = id && id[taskSymbol]; + // other environments. + if (!task) { + task = id; + } + } + if (task && typeof task.type === "string") { + if ( + task.state !== "notScheduled" && + ((task.cancelFn && task.data.isPeriodic) || task.runCount === 0) + ) { + if (typeof id === "number") { + delete tasksByHandleId[id]; + } else if (id) { + id[taskSymbol] = null; + } + // Do not cancel already canceled functions + task.zone.cancelTask(task); + } + } else { + // cause an error by calling it directly. + delegate.apply(window, args); + } + } + ); + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + function patchCustomElements(_global, api) { + const { isBrowser, isMix } = api.getGlobalObjects(); + if ((!isBrowser && !isMix) || !_global["customElements"] || !("customElements" in _global)) { + return; + } + const callbacks = [ + "connectedCallback", + "disconnectedCallback", + "adoptedCallback", + "attributeChangedCallback" + ]; + api.patchCallbacks(api, _global.customElements, "customElements", "define", callbacks); + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + function eventTargetPatch(_global, api) { + if (Zone[api.symbol("patchEventTarget")]) { + // EventTarget is already patched. + return; + } + const { + eventNames, + zoneSymbolEventNames, + TRUE_STR, + FALSE_STR, + ZONE_SYMBOL_PREFIX + } = api.getGlobalObjects(); + // predefine all __zone_symbol__ + eventName + true/false string + for (let i = 0; i < eventNames.length; i++) { + const eventName = eventNames[i]; + const falseEventName = eventName + FALSE_STR; + const trueEventName = eventName + TRUE_STR; + const symbol = ZONE_SYMBOL_PREFIX + falseEventName; + const symbolCapture = ZONE_SYMBOL_PREFIX + trueEventName; + zoneSymbolEventNames[eventName] = {}; + zoneSymbolEventNames[eventName][FALSE_STR] = symbol; + zoneSymbolEventNames[eventName][TRUE_STR] = symbolCapture; + } + const EVENT_TARGET = _global["EventTarget"]; + if (!EVENT_TARGET || !EVENT_TARGET.prototype) { + return; + } + api.patchEventTarget(_global, api, [EVENT_TARGET && EVENT_TARGET.prototype]); + return true; + } + function patchEvent(global, api) { + api.patchEventPrototype(global, api); + } + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + Zone.__load_patch("legacy", global => { + const legacyPatch = global[Zone.__symbol__("legacyPatch")]; + if (legacyPatch) { + legacyPatch(); + } + }); + Zone.__load_patch("queueMicrotask", (global, Zone, api) => { + api.patchMethod(global, "queueMicrotask", delegate => { + return function(self, args) { + Zone.current.scheduleMicroTask("queueMicrotask", args[0]); + }; + }); + }); + Zone.__load_patch("timers", global => { + const set = "set"; + const clear = "clear"; + patchTimer(global, set, clear, "Timeout"); + patchTimer(global, set, clear, "Interval"); + patchTimer(global, set, clear, "Immediate"); + }); + Zone.__load_patch("requestAnimationFrame", global => { + patchTimer(global, "request", "cancel", "AnimationFrame"); + patchTimer(global, "mozRequest", "mozCancel", "AnimationFrame"); + patchTimer(global, "webkitRequest", "webkitCancel", "AnimationFrame"); + }); + Zone.__load_patch("blocking", (global, Zone) => { + const blockingMethods = ["alert", "prompt", "confirm"]; + for (let i = 0; i < blockingMethods.length; i++) { + const name = blockingMethods[i]; + patchMethod(global, name, (delegate, symbol, name) => { + return function(s, args) { + return Zone.current.run(delegate, global, args, name); + }; + }); + } + }); + Zone.__load_patch("EventTarget", (global, Zone, api) => { + patchEvent(global, api); + eventTargetPatch(global, api); + // patch XMLHttpRequestEventTarget's addEventListener/removeEventListener + const XMLHttpRequestEventTarget = global["XMLHttpRequestEventTarget"]; + if (XMLHttpRequestEventTarget && XMLHttpRequestEventTarget.prototype) { + api.patchEventTarget(global, api, [XMLHttpRequestEventTarget.prototype]); + } + }); + Zone.__load_patch("MutationObserver", (global, Zone, api) => { + patchClass("MutationObserver"); + patchClass("WebKitMutationObserver"); + }); + Zone.__load_patch("IntersectionObserver", (global, Zone, api) => { + patchClass("IntersectionObserver"); + }); + Zone.__load_patch("FileReader", (global, Zone, api) => { + patchClass("FileReader"); + }); + Zone.__load_patch("on_property", (global, Zone, api) => { + propertyDescriptorPatch(api, global); + }); + Zone.__load_patch("customElements", (global, Zone, api) => { + patchCustomElements(global, api); + }); + Zone.__load_patch("XHR", (global, Zone) => { + // Treat XMLHttpRequest as a macrotask. + patchXHR(global); + const XHR_TASK = zoneSymbol("xhrTask"); + const XHR_SYNC = zoneSymbol("xhrSync"); + const XHR_LISTENER = zoneSymbol("xhrListener"); + const XHR_SCHEDULED = zoneSymbol("xhrScheduled"); + const XHR_URL = zoneSymbol("xhrURL"); + const XHR_ERROR_BEFORE_SCHEDULED = zoneSymbol("xhrErrorBeforeScheduled"); + function patchXHR(window) { + const XMLHttpRequest = window["XMLHttpRequest"]; + if (!XMLHttpRequest) { + // XMLHttpRequest is not available in service worker + return; + } + const XMLHttpRequestPrototype = XMLHttpRequest.prototype; + function findPendingTask(target) { + return target[XHR_TASK]; + } + let oriAddListener = XMLHttpRequestPrototype[ZONE_SYMBOL_ADD_EVENT_LISTENER]; + let oriRemoveListener = XMLHttpRequestPrototype[ZONE_SYMBOL_REMOVE_EVENT_LISTENER]; + if (!oriAddListener) { + const XMLHttpRequestEventTarget = window["XMLHttpRequestEventTarget"]; + if (XMLHttpRequestEventTarget) { + const XMLHttpRequestEventTargetPrototype = XMLHttpRequestEventTarget.prototype; + oriAddListener = XMLHttpRequestEventTargetPrototype[ZONE_SYMBOL_ADD_EVENT_LISTENER]; + oriRemoveListener = + XMLHttpRequestEventTargetPrototype[ZONE_SYMBOL_REMOVE_EVENT_LISTENER]; + } + } + const READY_STATE_CHANGE = "readystatechange"; + const SCHEDULED = "scheduled"; + function scheduleTask(task) { + const data = task.data; + const target = data.target; + target[XHR_SCHEDULED] = false; + target[XHR_ERROR_BEFORE_SCHEDULED] = false; + // remove existing event listener + const listener = target[XHR_LISTENER]; + if (!oriAddListener) { + oriAddListener = target[ZONE_SYMBOL_ADD_EVENT_LISTENER]; + oriRemoveListener = target[ZONE_SYMBOL_REMOVE_EVENT_LISTENER]; + } + if (listener) { + oriRemoveListener.call(target, READY_STATE_CHANGE, listener); + } + const newListener = (target[XHR_LISTENER] = () => { + if (target.readyState === target.DONE) { + // sometimes on some browsers XMLHttpRequest will fire onreadystatechange with + // readyState=4 multiple times, so we need to check task state here + if (!data.aborted && target[XHR_SCHEDULED] && task.state === SCHEDULED) { + // check whether the xhr has registered onload listener + // if that is the case, the task should invoke after all + // onload listeners finish. + // Also if the request failed without response (status = 0), the load event handler + // will not be triggered, in that case, we should also invoke the placeholder callback + // to close the XMLHttpRequest::send macroTask. + // https://github.com/angular/angular/issues/38795 + const loadTasks = target[Zone.__symbol__("loadfalse")]; + if (target.status !== 0 && loadTasks && loadTasks.length > 0) { + const oriInvoke = task.invoke; + task.invoke = function() { + // need to load the tasks again, because in other + // load listener, they may remove themselves + const loadTasks = target[Zone.__symbol__("loadfalse")]; + for (let i = 0; i < loadTasks.length; i++) { + if (loadTasks[i] === task) { + loadTasks.splice(i, 1); + } + } + if (!data.aborted && task.state === SCHEDULED) { + oriInvoke.call(task); + } + }; + loadTasks.push(task); + } else { + task.invoke(); + } + } else if (!data.aborted && target[XHR_SCHEDULED] === false) { + // error occurs when xhr.send() + target[XHR_ERROR_BEFORE_SCHEDULED] = true; + } + } + }); + oriAddListener.call(target, READY_STATE_CHANGE, newListener); + const storedTask = target[XHR_TASK]; + if (!storedTask) { + target[XHR_TASK] = task; + } + sendNative.apply(target, data.args); + target[XHR_SCHEDULED] = true; + return task; + } + function placeholderCallback() {} + function clearTask(task) { + const data = task.data; + // Note - ideally, we would call data.target.removeEventListener here, but it's too late + // to prevent it from firing. So instead, we store info for the event listener. + data.aborted = true; + return abortNative.apply(data.target, data.args); + } + const openNative = patchMethod( + XMLHttpRequestPrototype, + "open", + () => + function(self, args) { + self[XHR_SYNC] = args[2] == false; + self[XHR_URL] = args[1]; + return openNative.apply(self, args); + } + ); + const XMLHTTPREQUEST_SOURCE = "XMLHttpRequest.send"; + const fetchTaskAborting = zoneSymbol("fetchTaskAborting"); + const fetchTaskScheduling = zoneSymbol("fetchTaskScheduling"); + const sendNative = patchMethod( + XMLHttpRequestPrototype, + "send", + () => + function(self, args) { + if (Zone.current[fetchTaskScheduling] === true) { + // a fetch is scheduling, so we are using xhr to polyfill fetch + // and because we already schedule macroTask for fetch, we should + // not schedule a macroTask for xhr again + return sendNative.apply(self, args); + } + if (self[XHR_SYNC]) { + // if the XHR is sync there is no task to schedule, just execute the code. + return sendNative.apply(self, args); + } else { + const options = { + target: self, + url: self[XHR_URL], + isPeriodic: false, + args: args, + aborted: false + }; + const task = scheduleMacroTaskWithCurrentZone( + XMLHTTPREQUEST_SOURCE, + placeholderCallback, + options, + scheduleTask, + clearTask + ); + if ( + self && + self[XHR_ERROR_BEFORE_SCHEDULED] === true && + !options.aborted && + task.state === SCHEDULED + ) { + // xhr request throw error when send + // we should invoke task instead of leaving a scheduled + // pending macroTask + task.invoke(); + } + } + } + ); + const abortNative = patchMethod( + XMLHttpRequestPrototype, + "abort", + () => + function(self, args) { + const task = findPendingTask(self); + if (task && typeof task.type == "string") { + // If the XHR has already completed, do nothing. + // If the XHR has already been aborted, do nothing. + // Fix #569, call abort multiple times before done will cause + // macroTask task count be negative number + if (task.cancelFn == null || (task.data && task.data.aborted)) { + return; + } + task.zone.cancelTask(task); + } else if (Zone.current[fetchTaskAborting] === true) { + // the abort is called from fetch polyfill, we need to call native abort of XHR. + return abortNative.apply(self, args); + } + // Otherwise, we are trying to abort an XHR which has not yet been sent, so there is no + // task + // to cancel. Do nothing. + } + ); + } + }); + Zone.__load_patch("geolocation", global => { + /// GEO_LOCATION + if (global["navigator"] && global["navigator"].geolocation) { + patchPrototype(global["navigator"].geolocation, ["getCurrentPosition", "watchPosition"]); + } + }); + Zone.__load_patch("PromiseRejectionEvent", (global, Zone) => { + // handle unhandled promise rejection + function findPromiseRejectionHandler(evtName) { + return function(e) { + const eventTasks = findEventTasks(global, evtName); + eventTasks.forEach(eventTask => { + // windows has added unhandledrejection event listener + // trigger the event listener + const PromiseRejectionEvent = global["PromiseRejectionEvent"]; + if (PromiseRejectionEvent) { + const evt = new PromiseRejectionEvent(evtName, { + promise: e.promise, + reason: e.rejection + }); + eventTask.invoke(evt); + } + }); + }; + } + if (global["PromiseRejectionEvent"]) { + Zone[zoneSymbol("unhandledPromiseRejectionHandler")] = findPromiseRejectionHandler( + "unhandledrejection" + ); + Zone[zoneSymbol("rejectionHandledHandler")] = findPromiseRejectionHandler("rejectionhandled"); + } + }); + + /***/ + } + }, + /******/ __webpack_require__ => { + // webpackRuntimeModules + /******/ var __webpack_exec__ = moduleId => __webpack_require__((__webpack_require__.s = moduleId)); + /******/ var __webpack_exports__ = __webpack_exec__(7435); + /******/ + } +]); +//# sourceMappingURL=polyfills.js.map diff --git a/tests/frontendIntegration/litestar-server/templates/angular/runtime.js b/tests/frontendIntegration/litestar-server/templates/angular/runtime.js new file mode 100644 index 000000000..fd102e7a2 --- /dev/null +++ b/tests/frontendIntegration/litestar-server/templates/angular/runtime.js @@ -0,0 +1,192 @@ +/******/ (() => { + // webpackBootstrap + /******/ "use strict"; + /******/ var __webpack_modules__ = {}; // The module cache + /************************************************************************/ + /******/ /******/ var __webpack_module_cache__ = {}; // The require function + /******/ + + /******/ /******/ function __webpack_require__(moduleId) { + /******/ // Check if module is in cache + /******/ var cachedModule = __webpack_module_cache__[moduleId]; + /******/ if (cachedModule !== undefined) { + /******/ return cachedModule.exports; + /******/ + } // Create a new module (and put it into the cache) + /******/ /******/ var module = (__webpack_module_cache__[moduleId] = { + /******/ // no module.id needed + /******/ // no module.loaded needed + /******/ exports: {} + /******/ + }); // Execute the module function + /******/ + + /******/ /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); // Return the exports of the module + /******/ + + /******/ /******/ return module.exports; + /******/ + } // expose the modules object (__webpack_modules__) + /******/ + + /******/ /******/ __webpack_require__.m = __webpack_modules__; /* webpack/runtime/chunk loaded */ + /******/ + + /************************************************************************/ + /******/ /******/ (() => { + /******/ var deferred = []; + /******/ __webpack_require__.O = (result, chunkIds, fn, priority) => { + /******/ if (chunkIds) { + /******/ priority = priority || 0; + /******/ for (var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) + deferred[i] = deferred[i - 1]; + /******/ deferred[i] = [chunkIds, fn, priority]; + /******/ return; + /******/ + } + /******/ var notFulfilled = Infinity; + /******/ for (var i = 0; i < deferred.length; i++) { + /******/ var [chunkIds, fn, priority] = deferred[i]; + /******/ var fulfilled = true; + /******/ for (var j = 0; j < chunkIds.length; j++) { + /******/ if ( + (priority & (1 === 0) || notFulfilled >= priority) && + Object.keys(__webpack_require__.O).every(key => __webpack_require__.O[key](chunkIds[j])) + ) { + /******/ chunkIds.splice(j--, 1); + /******/ + } else { + /******/ fulfilled = false; + /******/ if (priority < notFulfilled) notFulfilled = priority; + /******/ + } + /******/ + } + /******/ if (fulfilled) { + /******/ deferred.splice(i--, 1); + /******/ var r = fn(); + /******/ if (r !== undefined) result = r; + /******/ + } + /******/ + } + /******/ return result; + /******/ + }; + /******/ + })(); /* webpack/runtime/compat get default export */ + /******/ + + /******/ /******/ (() => { + /******/ // getDefaultExport function for compatibility with non-harmony modules + /******/ __webpack_require__.n = module => { + /******/ var getter = + module && module.__esModule ? /******/ () => module["default"] : /******/ () => module; + /******/ __webpack_require__.d(getter, { a: getter }); + /******/ return getter; + /******/ + }; + /******/ + })(); /* webpack/runtime/define property getters */ + /******/ + + /******/ /******/ (() => { + /******/ // define getter functions for harmony exports + /******/ __webpack_require__.d = (exports, definition) => { + /******/ for (var key in definition) { + /******/ if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { + /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); + /******/ + } + /******/ + } + /******/ + }; + /******/ + })(); /* webpack/runtime/hasOwnProperty shorthand */ + /******/ + + /******/ /******/ (() => { + /******/ __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); + /******/ + })(); /* webpack/runtime/make namespace object */ + /******/ + + /******/ /******/ (() => { + /******/ // define __esModule on exports + /******/ __webpack_require__.r = exports => { + /******/ if (typeof Symbol !== "undefined" && Symbol.toStringTag) { + /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); + /******/ + } + /******/ Object.defineProperty(exports, "__esModule", { value: true }); + /******/ + }; + /******/ + })(); /* webpack/runtime/jsonp chunk loading */ + /******/ + + /******/ /******/ (() => { + /******/ // no baseURI + /******/ + + /******/ // object to store loaded and loading chunks + /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched + /******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded + /******/ var installedChunks = { + /******/ runtime: 0 + /******/ + }; /******/ /******/ /******/ /******/ /******/ // no chunk on demand loading // no prefetching // no preloaded // no HMR // no HMR manifest + /******/ + + /******/ /******/ /******/ /******/ /******/ /******/ __webpack_require__.O.j = chunkId => + installedChunks[chunkId] === 0; // install a JSONP callback for chunk loading + /******/ + + /******/ /******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => { + /******/ var [chunkIds, moreModules, runtime] = data; // add "moreModules" to the modules object, // then flag all "chunkIds" as loaded and fire callback + /******/ /******/ /******/ var moduleId, + chunkId, + i = 0; + /******/ if (chunkIds.some(id => installedChunks[id] !== 0)) { + /******/ for (moduleId in moreModules) { + /******/ if (__webpack_require__.o(moreModules, moduleId)) { + /******/ __webpack_require__.m[moduleId] = moreModules[moduleId]; + /******/ + } + /******/ + } + /******/ if (runtime) var result = runtime(__webpack_require__); + /******/ + } + /******/ if (parentChunkLoadingFunction) parentChunkLoadingFunction(data); + /******/ for (; i < chunkIds.length; i++) { + /******/ chunkId = chunkIds[i]; + /******/ if (__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { + /******/ installedChunks[chunkId][0](); + /******/ + } + /******/ installedChunks[chunkId] = 0; + /******/ + } + /******/ return __webpack_require__.O(result); + /******/ + }; + /******/ + + /******/ var chunkLoadingGlobal = (self["webpackChunkwith_angular_thirdpartyemailpassword"] = + self["webpackChunkwith_angular_thirdpartyemailpassword"] || []); + /******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); + /******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind( + null, + chunkLoadingGlobal.push.bind(chunkLoadingGlobal) + ); + /******/ + })(); + /******/ + /************************************************************************/ + /******/ + /******/ + /******/ +})(); +//# sourceMappingURL=runtime.js.map diff --git a/tests/frontendIntegration/litestar-server/templates/angular/vendor.js b/tests/frontendIntegration/litestar-server/templates/angular/vendor.js new file mode 100644 index 000000000..fc052f01a --- /dev/null +++ b/tests/frontendIntegration/litestar-server/templates/angular/vendor.js @@ -0,0 +1,48476 @@ +"use strict"; +(self["webpackChunkwith_angular_thirdpartyemailpassword"] = + self["webpackChunkwith_angular_thirdpartyemailpassword"] || []).push([ + ["vendor"], + { + /***/ 3279: + /*!**********************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/NotificationFactories.js ***! + \**********************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ COMPLETE_NOTIFICATION: () => /* binding */ COMPLETE_NOTIFICATION, + /* harmony export */ createNotification: () => /* binding */ createNotification, + /* harmony export */ errorNotification: () => /* binding */ errorNotification, + /* harmony export */ nextNotification: () => /* binding */ nextNotification + /* harmony export */ + }); + const COMPLETE_NOTIFICATION = (() => createNotification("C", undefined, undefined))(); + function errorNotification(error) { + return createNotification("E", undefined, error); + } + function nextNotification(value) { + return createNotification("N", value, undefined); + } + function createNotification(kind, value, error) { + return { + kind, + value, + error + }; + } + + /***/ + }, + + /***/ 833: + /*!***********************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/Observable.js ***! + \***********************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ Observable: () => /* binding */ Observable + /* harmony export */ + }); + /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./Subscriber */ 9904 + ); + /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__( + /*! ./Subscription */ 6078 + ); + /* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ./symbol/observable */ 4585 + ); + /* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! ./util/pipe */ 629 + ); + /* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__( + /*! ./config */ 9057 + ); + /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__( + /*! ./util/isFunction */ 2971 + ); + /* harmony import */ var _util_errorContext__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./util/errorContext */ 2309 + ); + + class Observable { + constructor(subscribe) { + if (subscribe) { + this._subscribe = subscribe; + } + } + lift(operator) { + const observable = new Observable(); + observable.source = this; + observable.operator = operator; + return observable; + } + subscribe(observerOrNext, error, complete) { + const subscriber = isSubscriber(observerOrNext) + ? observerOrNext + : new _Subscriber__WEBPACK_IMPORTED_MODULE_0__.SafeSubscriber( + observerOrNext, + error, + complete + ); + (0, _util_errorContext__WEBPACK_IMPORTED_MODULE_1__.errorContext)(() => { + const { operator, source } = this; + subscriber.add( + operator + ? operator.call(subscriber, source) + : source + ? this._subscribe(subscriber) + : this._trySubscribe(subscriber) + ); + }); + return subscriber; + } + _trySubscribe(sink) { + try { + return this._subscribe(sink); + } catch (err) { + sink.error(err); + } + } + forEach(next, promiseCtor) { + promiseCtor = getPromiseCtor(promiseCtor); + return new promiseCtor((resolve, reject) => { + const subscriber = new _Subscriber__WEBPACK_IMPORTED_MODULE_0__.SafeSubscriber({ + next: value => { + try { + next(value); + } catch (err) { + reject(err); + subscriber.unsubscribe(); + } + }, + error: reject, + complete: resolve + }); + this.subscribe(subscriber); + }); + } + _subscribe(subscriber) { + var _a; + return (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber); + } + [_symbol_observable__WEBPACK_IMPORTED_MODULE_2__.observable]() { + return this; + } + pipe(...operations) { + return (0, _util_pipe__WEBPACK_IMPORTED_MODULE_3__.pipeFromArray)(operations)(this); + } + toPromise(promiseCtor) { + promiseCtor = getPromiseCtor(promiseCtor); + return new promiseCtor((resolve, reject) => { + let value; + this.subscribe(x => (value = x), err => reject(err), () => resolve(value)); + }); + } + } + Observable.create = subscribe => { + return new Observable(subscribe); + }; + function getPromiseCtor(promiseCtor) { + var _a; + return (_a = + promiseCtor !== null && promiseCtor !== void 0 + ? promiseCtor + : _config__WEBPACK_IMPORTED_MODULE_4__.config.Promise) !== null && _a !== void 0 + ? _a + : Promise; + } + function isObserver(value) { + return ( + value && + (0, _util_isFunction__WEBPACK_IMPORTED_MODULE_5__.isFunction)(value.next) && + (0, _util_isFunction__WEBPACK_IMPORTED_MODULE_5__.isFunction)(value.error) && + (0, _util_isFunction__WEBPACK_IMPORTED_MODULE_5__.isFunction)(value.complete) + ); + } + function isSubscriber(value) { + return ( + (value && value instanceof _Subscriber__WEBPACK_IMPORTED_MODULE_0__.Subscriber) || + (isObserver(value) && (0, _Subscription__WEBPACK_IMPORTED_MODULE_6__.isSubscription)(value)) + ); + } + + /***/ + }, + + /***/ 228: + /*!********************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/Subject.js ***! + \********************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ AnonymousSubject: () => /* binding */ AnonymousSubject, + /* harmony export */ Subject: () => /* binding */ Subject + /* harmony export */ + }); + /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./Observable */ 833 + ); + /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! ./Subscription */ 6078 + ); + /* harmony import */ var _util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./util/ObjectUnsubscribedError */ 9872 + ); + /* harmony import */ var _util_arrRemove__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__( + /*! ./util/arrRemove */ 9663 + ); + /* harmony import */ var _util_errorContext__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ./util/errorContext */ 2309 + ); + + class Subject extends _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable { + constructor() { + super(); + this.closed = false; + this.currentObservers = null; + this.observers = []; + this.isStopped = false; + this.hasError = false; + this.thrownError = null; + } + lift(operator) { + const subject = new AnonymousSubject(this, this); + subject.operator = operator; + return subject; + } + _throwIfClosed() { + if (this.closed) { + throw new _util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_1__.ObjectUnsubscribedError(); + } + } + next(value) { + (0, _util_errorContext__WEBPACK_IMPORTED_MODULE_2__.errorContext)(() => { + this._throwIfClosed(); + if (!this.isStopped) { + if (!this.currentObservers) { + this.currentObservers = Array.from(this.observers); + } + for (const observer of this.currentObservers) { + observer.next(value); + } + } + }); + } + error(err) { + (0, _util_errorContext__WEBPACK_IMPORTED_MODULE_2__.errorContext)(() => { + this._throwIfClosed(); + if (!this.isStopped) { + this.hasError = this.isStopped = true; + this.thrownError = err; + const { observers } = this; + while (observers.length) { + observers.shift().error(err); + } + } + }); + } + complete() { + (0, _util_errorContext__WEBPACK_IMPORTED_MODULE_2__.errorContext)(() => { + this._throwIfClosed(); + if (!this.isStopped) { + this.isStopped = true; + const { observers } = this; + while (observers.length) { + observers.shift().complete(); + } + } + }); + } + unsubscribe() { + this.isStopped = this.closed = true; + this.observers = this.currentObservers = null; + } + get observed() { + var _a; + return ((_a = this.observers) === null || _a === void 0 ? void 0 : _a.length) > 0; + } + _trySubscribe(subscriber) { + this._throwIfClosed(); + return super._trySubscribe(subscriber); + } + _subscribe(subscriber) { + this._throwIfClosed(); + this._checkFinalizedStatuses(subscriber); + return this._innerSubscribe(subscriber); + } + _innerSubscribe(subscriber) { + const { hasError, isStopped, observers } = this; + if (hasError || isStopped) { + return _Subscription__WEBPACK_IMPORTED_MODULE_3__.EMPTY_SUBSCRIPTION; + } + this.currentObservers = null; + observers.push(subscriber); + return new _Subscription__WEBPACK_IMPORTED_MODULE_3__.Subscription(() => { + this.currentObservers = null; + (0, _util_arrRemove__WEBPACK_IMPORTED_MODULE_4__.arrRemove)(observers, subscriber); + }); + } + _checkFinalizedStatuses(subscriber) { + const { hasError, thrownError, isStopped } = this; + if (hasError) { + subscriber.error(thrownError); + } else if (isStopped) { + subscriber.complete(); + } + } + asObservable() { + const observable = new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(); + observable.source = this; + return observable; + } + } + Subject.create = (destination, source) => { + return new AnonymousSubject(destination, source); + }; + class AnonymousSubject extends Subject { + constructor(destination, source) { + super(); + this.destination = destination; + this.source = source; + } + next(value) { + var _a, _b; + (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.next) === null || + _b === void 0 + ? void 0 + : _b.call(_a, value); + } + error(err) { + var _a, _b; + (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.error) === null || + _b === void 0 + ? void 0 + : _b.call(_a, err); + } + complete() { + var _a, _b; + (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.complete) === null || + _b === void 0 + ? void 0 + : _b.call(_a); + } + _subscribe(subscriber) { + var _a, _b; + return (_b = + (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber)) !== + null && _b !== void 0 + ? _b + : _Subscription__WEBPACK_IMPORTED_MODULE_3__.EMPTY_SUBSCRIPTION; + } + } + + /***/ + }, + + /***/ 9904: + /*!***********************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/Subscriber.js ***! + \***********************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ EMPTY_OBSERVER: () => /* binding */ EMPTY_OBSERVER, + /* harmony export */ SafeSubscriber: () => /* binding */ SafeSubscriber, + /* harmony export */ Subscriber: () => /* binding */ Subscriber + /* harmony export */ + }); + /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ./util/isFunction */ 2971 + ); + /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./Subscription */ 6078 + ); + /* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! ./config */ 9057 + ); + /* harmony import */ var _util_reportUnhandledError__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__( + /*! ./util/reportUnhandledError */ 4709 + ); + /* harmony import */ var _util_noop__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__( + /*! ./util/noop */ 9635 + ); + /* harmony import */ var _NotificationFactories__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./NotificationFactories */ 3279 + ); + /* harmony import */ var _scheduler_timeoutProvider__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__( + /*! ./scheduler/timeoutProvider */ 3542 + ); + /* harmony import */ var _util_errorContext__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__( + /*! ./util/errorContext */ 2309 + ); + + class Subscriber extends _Subscription__WEBPACK_IMPORTED_MODULE_0__.Subscription { + constructor(destination) { + super(); + this.isStopped = false; + if (destination) { + this.destination = destination; + if ((0, _Subscription__WEBPACK_IMPORTED_MODULE_0__.isSubscription)(destination)) { + destination.add(this); + } + } else { + this.destination = EMPTY_OBSERVER; + } + } + static create(next, error, complete) { + return new SafeSubscriber(next, error, complete); + } + next(value) { + if (this.isStopped) { + handleStoppedNotification( + (0, _NotificationFactories__WEBPACK_IMPORTED_MODULE_1__.nextNotification)(value), + this + ); + } else { + this._next(value); + } + } + error(err) { + if (this.isStopped) { + handleStoppedNotification( + (0, _NotificationFactories__WEBPACK_IMPORTED_MODULE_1__.errorNotification)(err), + this + ); + } else { + this.isStopped = true; + this._error(err); + } + } + complete() { + if (this.isStopped) { + handleStoppedNotification( + _NotificationFactories__WEBPACK_IMPORTED_MODULE_1__.COMPLETE_NOTIFICATION, + this + ); + } else { + this.isStopped = true; + this._complete(); + } + } + unsubscribe() { + if (!this.closed) { + this.isStopped = true; + super.unsubscribe(); + this.destination = null; + } + } + _next(value) { + this.destination.next(value); + } + _error(err) { + try { + this.destination.error(err); + } finally { + this.unsubscribe(); + } + } + _complete() { + try { + this.destination.complete(); + } finally { + this.unsubscribe(); + } + } + } + const _bind = Function.prototype.bind; + function bind(fn, thisArg) { + return _bind.call(fn, thisArg); + } + class ConsumerObserver { + constructor(partialObserver) { + this.partialObserver = partialObserver; + } + next(value) { + const { partialObserver } = this; + if (partialObserver.next) { + try { + partialObserver.next(value); + } catch (error) { + handleUnhandledError(error); + } + } + } + error(err) { + const { partialObserver } = this; + if (partialObserver.error) { + try { + partialObserver.error(err); + } catch (error) { + handleUnhandledError(error); + } + } else { + handleUnhandledError(err); + } + } + complete() { + const { partialObserver } = this; + if (partialObserver.complete) { + try { + partialObserver.complete(); + } catch (error) { + handleUnhandledError(error); + } + } + } + } + class SafeSubscriber extends Subscriber { + constructor(observerOrNext, error, complete) { + super(); + let partialObserver; + if ( + (0, _util_isFunction__WEBPACK_IMPORTED_MODULE_2__.isFunction)(observerOrNext) || + !observerOrNext + ) { + partialObserver = { + next: observerOrNext !== null && observerOrNext !== void 0 ? observerOrNext : undefined, + error: error !== null && error !== void 0 ? error : undefined, + complete: complete !== null && complete !== void 0 ? complete : undefined + }; + } else { + let context; + if (this && _config__WEBPACK_IMPORTED_MODULE_3__.config.useDeprecatedNextContext) { + context = Object.create(observerOrNext); + context.unsubscribe = () => this.unsubscribe(); + partialObserver = { + next: observerOrNext.next && bind(observerOrNext.next, context), + error: observerOrNext.error && bind(observerOrNext.error, context), + complete: observerOrNext.complete && bind(observerOrNext.complete, context) + }; + } else { + partialObserver = observerOrNext; + } + } + this.destination = new ConsumerObserver(partialObserver); + } + } + function handleUnhandledError(error) { + if (_config__WEBPACK_IMPORTED_MODULE_3__.config.useDeprecatedSynchronousErrorHandling) { + (0, _util_errorContext__WEBPACK_IMPORTED_MODULE_4__.captureError)(error); + } else { + (0, _util_reportUnhandledError__WEBPACK_IMPORTED_MODULE_5__.reportUnhandledError)(error); + } + } + function defaultErrorHandler(err) { + throw err; + } + function handleStoppedNotification(notification, subscriber) { + const { onStoppedNotification } = _config__WEBPACK_IMPORTED_MODULE_3__.config; + onStoppedNotification && + _scheduler_timeoutProvider__WEBPACK_IMPORTED_MODULE_6__.timeoutProvider.setTimeout(() => + onStoppedNotification(notification, subscriber) + ); + } + const EMPTY_OBSERVER = { + closed: true, + next: _util_noop__WEBPACK_IMPORTED_MODULE_7__.noop, + error: defaultErrorHandler, + complete: _util_noop__WEBPACK_IMPORTED_MODULE_7__.noop + }; + + /***/ + }, + + /***/ 6078: + /*!*************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/Subscription.js ***! + \*************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ EMPTY_SUBSCRIPTION: () => /* binding */ EMPTY_SUBSCRIPTION, + /* harmony export */ Subscription: () => /* binding */ Subscription, + /* harmony export */ isSubscription: () => /* binding */ isSubscription + /* harmony export */ + }); + /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./util/isFunction */ 2971 + ); + /* harmony import */ var _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./util/UnsubscriptionError */ 2524 + ); + /* harmony import */ var _util_arrRemove__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ./util/arrRemove */ 9663 + ); + + class Subscription { + constructor(initialTeardown) { + this.initialTeardown = initialTeardown; + this.closed = false; + this._parentage = null; + this._finalizers = null; + } + unsubscribe() { + let errors; + if (!this.closed) { + this.closed = true; + const { _parentage } = this; + if (_parentage) { + this._parentage = null; + if (Array.isArray(_parentage)) { + for (const parent of _parentage) { + parent.remove(this); + } + } else { + _parentage.remove(this); + } + } + const { initialTeardown: initialFinalizer } = this; + if ((0, _util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(initialFinalizer)) { + try { + initialFinalizer(); + } catch (e) { + errors = + e instanceof + _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_1__.UnsubscriptionError + ? e.errors + : [e]; + } + } + const { _finalizers } = this; + if (_finalizers) { + this._finalizers = null; + for (const finalizer of _finalizers) { + try { + execFinalizer(finalizer); + } catch (err) { + errors = errors !== null && errors !== void 0 ? errors : []; + if ( + err instanceof + _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_1__.UnsubscriptionError + ) { + errors = [...errors, ...err.errors]; + } else { + errors.push(err); + } + } + } + } + if (errors) { + throw new _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_1__.UnsubscriptionError( + errors + ); + } + } + } + add(teardown) { + var _a; + if (teardown && teardown !== this) { + if (this.closed) { + execFinalizer(teardown); + } else { + if (teardown instanceof Subscription) { + if (teardown.closed || teardown._hasParent(this)) { + return; + } + teardown._addParent(this); + } + (this._finalizers = (_a = this._finalizers) !== null && _a !== void 0 ? _a : []).push( + teardown + ); + } + } + } + _hasParent(parent) { + const { _parentage } = this; + return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent)); + } + _addParent(parent) { + const { _parentage } = this; + this._parentage = Array.isArray(_parentage) + ? (_parentage.push(parent), _parentage) + : _parentage + ? [_parentage, parent] + : parent; + } + _removeParent(parent) { + const { _parentage } = this; + if (_parentage === parent) { + this._parentage = null; + } else if (Array.isArray(_parentage)) { + (0, _util_arrRemove__WEBPACK_IMPORTED_MODULE_2__.arrRemove)(_parentage, parent); + } + } + remove(teardown) { + const { _finalizers } = this; + _finalizers && + (0, _util_arrRemove__WEBPACK_IMPORTED_MODULE_2__.arrRemove)(_finalizers, teardown); + if (teardown instanceof Subscription) { + teardown._removeParent(this); + } + } + } + Subscription.EMPTY = (() => { + const empty = new Subscription(); + empty.closed = true; + return empty; + })(); + const EMPTY_SUBSCRIPTION = Subscription.EMPTY; + function isSubscription(value) { + return ( + value instanceof Subscription || + (value && + "closed" in value && + (0, _util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(value.remove) && + (0, _util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(value.add) && + (0, _util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(value.unsubscribe)) + ); + } + function execFinalizer(finalizer) { + if ((0, _util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(finalizer)) { + finalizer(); + } else { + finalizer.unsubscribe(); + } + } + + /***/ + }, + + /***/ 9057: + /*!*******************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/config.js ***! + \*******************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ config: () => /* binding */ config + /* harmony export */ + }); + const config = { + onUnhandledError: null, + onStoppedNotification: null, + Promise: undefined, + useDeprecatedSynchronousErrorHandling: false, + useDeprecatedNextContext: false + }; + + /***/ + }, + + /***/ 591: + /*!*****************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/observable/empty.js ***! + \*****************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ EMPTY: () => /* binding */ EMPTY, + /* harmony export */ empty: () => /* binding */ empty + /* harmony export */ + }); + /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../Observable */ 833 + ); + + const EMPTY = new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => + subscriber.complete() + ); + function empty(scheduler) { + return scheduler ? emptyScheduled(scheduler) : EMPTY; + } + function emptyScheduled(scheduler) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => + scheduler.schedule(() => subscriber.complete()) + ); + } + + /***/ + }, + + /***/ 9346: + /*!****************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/observable/from.js ***! + \****************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ from: () => /* binding */ from + /* harmony export */ + }); + /* harmony import */ var _scheduled_scheduled__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../scheduled/scheduled */ 9517 + ); + /* harmony import */ var _innerFrom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./innerFrom */ 4987 + ); + + function from(input, scheduler) { + return scheduler + ? (0, _scheduled_scheduled__WEBPACK_IMPORTED_MODULE_0__.scheduled)(input, scheduler) + : (0, _innerFrom__WEBPACK_IMPORTED_MODULE_1__.innerFrom)(input); + } + + /***/ + }, + + /***/ 4987: + /*!*********************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/observable/innerFrom.js ***! + \*********************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ fromArrayLike: () => /* binding */ fromArrayLike, + /* harmony export */ fromAsyncIterable: () => /* binding */ fromAsyncIterable, + /* harmony export */ fromInteropObservable: () => /* binding */ fromInteropObservable, + /* harmony export */ fromIterable: () => /* binding */ fromIterable, + /* harmony export */ fromPromise: () => /* binding */ fromPromise, + /* harmony export */ fromReadableStreamLike: () => /* binding */ fromReadableStreamLike, + /* harmony export */ innerFrom: () => /* binding */ innerFrom + /* harmony export */ + }); + /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! tslib */ 4929); + /* harmony import */ var _util_isArrayLike__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ../util/isArrayLike */ 9806 + ); + /* harmony import */ var _util_isPromise__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! ../util/isPromise */ 9548 + ); + /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../Observable */ 833 + ); + /* harmony import */ var _util_isInteropObservable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../util/isInteropObservable */ 1331 + ); + /* harmony import */ var _util_isAsyncIterable__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__( + /*! ../util/isAsyncIterable */ 470 + ); + /* harmony import */ var _util_throwUnobservableError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__( + /*! ../util/throwUnobservableError */ 7785 + ); + /* harmony import */ var _util_isIterable__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__( + /*! ../util/isIterable */ 3433 + ); + /* harmony import */ var _util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__( + /*! ../util/isReadableStreamLike */ 181 + ); + /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__( + /*! ../util/isFunction */ 2971 + ); + /* harmony import */ var _util_reportUnhandledError__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__( + /*! ../util/reportUnhandledError */ 4709 + ); + /* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__( + /*! ../symbol/observable */ 4585 + ); + + function innerFrom(input) { + if (input instanceof _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable) { + return input; + } + + if (input != null) { + if ((0, _util_isInteropObservable__WEBPACK_IMPORTED_MODULE_1__.isInteropObservable)(input)) { + return fromInteropObservable(input); + } + + if ((0, _util_isArrayLike__WEBPACK_IMPORTED_MODULE_2__.isArrayLike)(input)) { + return fromArrayLike(input); + } + + if ((0, _util_isPromise__WEBPACK_IMPORTED_MODULE_3__.isPromise)(input)) { + return fromPromise(input); + } + + if ((0, _util_isAsyncIterable__WEBPACK_IMPORTED_MODULE_4__.isAsyncIterable)(input)) { + return fromAsyncIterable(input); + } + + if ((0, _util_isIterable__WEBPACK_IMPORTED_MODULE_5__.isIterable)(input)) { + return fromIterable(input); + } + + if ((0, _util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_6__.isReadableStreamLike)(input)) { + return fromReadableStreamLike(input); + } + } + + throw (0, + _util_throwUnobservableError__WEBPACK_IMPORTED_MODULE_7__.createInvalidObservableTypeError)(input); + } + function fromInteropObservable(obj) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { + const obs = obj[_symbol_observable__WEBPACK_IMPORTED_MODULE_8__.observable](); + + if ((0, _util_isFunction__WEBPACK_IMPORTED_MODULE_9__.isFunction)(obs.subscribe)) { + return obs.subscribe(subscriber); + } + + throw new TypeError("Provided object does not correctly implement Symbol.observable"); + }); + } + function fromArrayLike(array) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { + for (let i = 0; i < array.length && !subscriber.closed; i++) { + subscriber.next(array[i]); + } + + subscriber.complete(); + }); + } + function fromPromise(promise) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { + promise + .then( + value => { + if (!subscriber.closed) { + subscriber.next(value); + subscriber.complete(); + } + }, + err => subscriber.error(err) + ) + .then(null, _util_reportUnhandledError__WEBPACK_IMPORTED_MODULE_10__.reportUnhandledError); + }); + } + function fromIterable(iterable) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { + for (const value of iterable) { + subscriber.next(value); + + if (subscriber.closed) { + return; + } + } + + subscriber.complete(); + }); + } + function fromAsyncIterable(asyncIterable) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { + process(asyncIterable, subscriber).catch(err => subscriber.error(err)); + }); + } + function fromReadableStreamLike(readableStream) { + return fromAsyncIterable( + (0, _util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_6__.readableStreamLikeToAsyncGenerator)( + readableStream + ) + ); + } + + function process(asyncIterable, subscriber) { + var asyncIterable_1, asyncIterable_1_1; + + var e_1, _a; + + return (0, tslib__WEBPACK_IMPORTED_MODULE_11__.__awaiter)(this, void 0, void 0, function*() { + try { + for ( + asyncIterable_1 = (0, tslib__WEBPACK_IMPORTED_MODULE_11__.__asyncValues)(asyncIterable); + (asyncIterable_1_1 = yield asyncIterable_1.next()), !asyncIterable_1_1.done; + + ) { + const value = asyncIterable_1_1.value; + subscriber.next(value); + + if (subscriber.closed) { + return; + } + } + } catch (e_1_1) { + e_1 = { + error: e_1_1 + }; + } finally { + try { + if (asyncIterable_1_1 && !asyncIterable_1_1.done && (_a = asyncIterable_1.return)) + yield _a.call(asyncIterable_1); + } finally { + if (e_1) throw e_1.error; + } + } + + subscriber.complete(); + }); + } + + /***/ + }, + + /***/ 6646: + /*!*****************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/observable/merge.js ***! + \*****************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ merge: () => /* binding */ merge + /* harmony export */ + }); + /* harmony import */ var _operators_mergeAll__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! ../operators/mergeAll */ 1308 + ); + /* harmony import */ var _innerFrom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ./innerFrom */ 4987 + ); + /* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./empty */ 591); + /* harmony import */ var _util_args__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../util/args */ 420 + ); + /* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./from */ 9346); + + function merge(...args) { + const scheduler = (0, _util_args__WEBPACK_IMPORTED_MODULE_0__.popScheduler)(args); + const concurrent = (0, _util_args__WEBPACK_IMPORTED_MODULE_0__.popNumber)(args, Infinity); + const sources = args; + return !sources.length + ? _empty__WEBPACK_IMPORTED_MODULE_1__.EMPTY + : sources.length === 1 + ? (0, _innerFrom__WEBPACK_IMPORTED_MODULE_2__.innerFrom)(sources[0]) + : (0, _operators_mergeAll__WEBPACK_IMPORTED_MODULE_3__.mergeAll)(concurrent)( + (0, _from__WEBPACK_IMPORTED_MODULE_4__.from)(sources, scheduler) + ); + } + + /***/ + }, + + /***/ 745: + /*!**************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/observable/of.js ***! + \**************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ of: () => /* binding */ of + /* harmony export */ + }); + /* harmony import */ var _util_args__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../util/args */ 420 + ); + /* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./from */ 9346); + + function of(...args) { + const scheduler = (0, _util_args__WEBPACK_IMPORTED_MODULE_0__.popScheduler)(args); + return (0, _from__WEBPACK_IMPORTED_MODULE_1__.from)(args, scheduler); + } + + /***/ + }, + + /***/ 3945: + /*!*****************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/operators/OperatorSubscriber.js ***! + \*****************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ OperatorSubscriber: () => /* binding */ OperatorSubscriber, + /* harmony export */ createOperatorSubscriber: () => /* binding */ createOperatorSubscriber + /* harmony export */ + }); + /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../Subscriber */ 9904 + ); + + function createOperatorSubscriber(destination, onNext, onComplete, onError, onFinalize) { + return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize); + } + class OperatorSubscriber extends _Subscriber__WEBPACK_IMPORTED_MODULE_0__.Subscriber { + constructor(destination, onNext, onComplete, onError, onFinalize, shouldUnsubscribe) { + super(destination); + this.onFinalize = onFinalize; + this.shouldUnsubscribe = shouldUnsubscribe; + this._next = onNext + ? function(value) { + try { + onNext(value); + } catch (err) { + destination.error(err); + } + } + : super._next; + this._error = onError + ? function(err) { + try { + onError(err); + } catch (err) { + destination.error(err); + } finally { + this.unsubscribe(); + } + } + : super._error; + this._complete = onComplete + ? function() { + try { + onComplete(); + } catch (err) { + destination.error(err); + } finally { + this.unsubscribe(); + } + } + : super._complete; + } + unsubscribe() { + var _a; + if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) { + const { closed } = this; + super.unsubscribe(); + !closed && ((_a = this.onFinalize) === null || _a === void 0 ? void 0 : _a.call(this)); + } + } + } + + /***/ + }, + + /***/ 3853: + /*!********************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/operators/concatMap.js ***! + \********************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ concatMap: () => /* binding */ concatMap + /* harmony export */ + }); + /* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./mergeMap */ 1353 + ); + /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../util/isFunction */ 2971 + ); + + function concatMap(project, resultSelector) { + return (0, _util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(resultSelector) + ? (0, _mergeMap__WEBPACK_IMPORTED_MODULE_1__.mergeMap)(project, resultSelector, 1) + : (0, _mergeMap__WEBPACK_IMPORTED_MODULE_1__.mergeMap)(project, 1); + } + + /***/ + }, + + /***/ 116: + /*!*****************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/operators/filter.js ***! + \*****************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ filter: () => /* binding */ filter + /* harmony export */ + }); + /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../util/lift */ 1944 + ); + /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./OperatorSubscriber */ 3945 + ); + + function filter(predicate, thisArg) { + return (0, _util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { + let index = 0; + source.subscribe( + (0, _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)( + subscriber, + value => predicate.call(thisArg, value, index++) && subscriber.next(value) + ) + ); + }); + } + + /***/ + }, + + /***/ 635: + /*!**************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/operators/map.js ***! + \**************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ map: () => /* binding */ map + /* harmony export */ + }); + /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../util/lift */ 1944 + ); + /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./OperatorSubscriber */ 3945 + ); + + function map(project, thisArg) { + return (0, _util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { + let index = 0; + source.subscribe( + (0, _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)( + subscriber, + value => { + subscriber.next(project.call(thisArg, value, index++)); + } + ) + ); + }); + } + + /***/ + }, + + /***/ 1308: + /*!*******************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/operators/mergeAll.js ***! + \*******************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ mergeAll: () => /* binding */ mergeAll + /* harmony export */ + }); + /* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./mergeMap */ 1353 + ); + /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../util/identity */ 9173 + ); + + function mergeAll(concurrent = Infinity) { + return (0, _mergeMap__WEBPACK_IMPORTED_MODULE_0__.mergeMap)( + _util_identity__WEBPACK_IMPORTED_MODULE_1__.identity, + concurrent + ); + } + + /***/ + }, + + /***/ 9280: + /*!*************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/operators/mergeInternals.js ***! + \*************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ mergeInternals: () => /* binding */ mergeInternals + /* harmony export */ + }); + /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../observable/innerFrom */ 4987 + ); + /* harmony import */ var _util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ../util/executeSchedule */ 1817 + ); + /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./OperatorSubscriber */ 3945 + ); + + function mergeInternals( + source, + subscriber, + project, + concurrent, + onBeforeNext, + expand, + innerSubScheduler, + additionalFinalizer + ) { + const buffer = []; + let active = 0; + let index = 0; + let isComplete = false; + const checkComplete = () => { + if (isComplete && !buffer.length && !active) { + subscriber.complete(); + } + }; + const outerNext = value => (active < concurrent ? doInnerSub(value) : buffer.push(value)); + const doInnerSub = value => { + expand && subscriber.next(value); + active++; + let innerComplete = false; + (0, _observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__.innerFrom)( + project(value, index++) + ).subscribe( + (0, _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)( + subscriber, + innerValue => { + onBeforeNext === null || onBeforeNext === void 0 + ? void 0 + : onBeforeNext(innerValue); + if (expand) { + outerNext(innerValue); + } else { + subscriber.next(innerValue); + } + }, + () => { + innerComplete = true; + }, + undefined, + () => { + if (innerComplete) { + try { + active--; + while (buffer.length && active < concurrent) { + const bufferedValue = buffer.shift(); + if (innerSubScheduler) { + (0, + _util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__.executeSchedule)( + subscriber, + innerSubScheduler, + () => doInnerSub(bufferedValue) + ); + } else { + doInnerSub(bufferedValue); + } + } + checkComplete(); + } catch (err) { + subscriber.error(err); + } + } + } + ) + ); + }; + source.subscribe( + (0, _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)( + subscriber, + outerNext, + () => { + isComplete = true; + checkComplete(); + } + ) + ); + return () => { + additionalFinalizer === null || additionalFinalizer === void 0 ? void 0 : additionalFinalizer(); + }; + } + + /***/ + }, + + /***/ 1353: + /*!*******************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/operators/mergeMap.js ***! + \*******************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ mergeMap: () => /* binding */ mergeMap + /* harmony export */ + }); + /* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./map */ 635); + /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ../observable/innerFrom */ 4987 + ); + /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! ../util/lift */ 1944 + ); + /* harmony import */ var _mergeInternals__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__( + /*! ./mergeInternals */ 9280 + ); + /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../util/isFunction */ 2971 + ); + + function mergeMap(project, resultSelector, concurrent = Infinity) { + if ((0, _util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(resultSelector)) { + return mergeMap( + (a, i) => + (0, _map__WEBPACK_IMPORTED_MODULE_1__.map)((b, ii) => resultSelector(a, b, i, ii))( + (0, _observable_innerFrom__WEBPACK_IMPORTED_MODULE_2__.innerFrom)(project(a, i)) + ), + concurrent + ); + } else if (typeof resultSelector === "number") { + concurrent = resultSelector; + } + return (0, _util_lift__WEBPACK_IMPORTED_MODULE_3__.operate)((source, subscriber) => + (0, _mergeInternals__WEBPACK_IMPORTED_MODULE_4__.mergeInternals)( + source, + subscriber, + project, + concurrent + ) + ); + } + + /***/ + }, + + /***/ 8728: + /*!********************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/operators/observeOn.js ***! + \********************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ observeOn: () => /* binding */ observeOn + /* harmony export */ + }); + /* harmony import */ var _util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ../util/executeSchedule */ 1817 + ); + /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../util/lift */ 1944 + ); + /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./OperatorSubscriber */ 3945 + ); + + function observeOn(scheduler, delay = 0) { + return (0, _util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { + source.subscribe( + (0, _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)( + subscriber, + value => + (0, _util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__.executeSchedule)( + subscriber, + scheduler, + () => subscriber.next(value), + delay + ), + () => + (0, _util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__.executeSchedule)( + subscriber, + scheduler, + () => subscriber.complete(), + delay + ), + err => + (0, _util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__.executeSchedule)( + subscriber, + scheduler, + () => subscriber.error(err), + delay + ) + ) + ); + }); + } + + /***/ + }, + + /***/ 1203: + /*!****************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/operators/share.js ***! + \****************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ share: () => /* binding */ share + /* harmony export */ + }); + /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! ../observable/innerFrom */ 4987 + ); + /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../Subject */ 228 + ); + /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ../Subscriber */ 9904 + ); + /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../util/lift */ 1944 + ); + + function share(options = {}) { + const { + connector = () => new _Subject__WEBPACK_IMPORTED_MODULE_0__.Subject(), + resetOnError = true, + resetOnComplete = true, + resetOnRefCountZero = true + } = options; + return wrapperSource => { + let connection; + let resetConnection; + let subject; + let refCount = 0; + let hasCompleted = false; + let hasErrored = false; + const cancelReset = () => { + resetConnection === null || resetConnection === void 0 + ? void 0 + : resetConnection.unsubscribe(); + resetConnection = undefined; + }; + const reset = () => { + cancelReset(); + connection = subject = undefined; + hasCompleted = hasErrored = false; + }; + const resetAndUnsubscribe = () => { + const conn = connection; + reset(); + conn === null || conn === void 0 ? void 0 : conn.unsubscribe(); + }; + return (0, _util_lift__WEBPACK_IMPORTED_MODULE_1__.operate)((source, subscriber) => { + refCount++; + if (!hasErrored && !hasCompleted) { + cancelReset(); + } + const dest = (subject = subject !== null && subject !== void 0 ? subject : connector()); + subscriber.add(() => { + refCount--; + if (refCount === 0 && !hasErrored && !hasCompleted) { + resetConnection = handleReset(resetAndUnsubscribe, resetOnRefCountZero); + } + }); + dest.subscribe(subscriber); + if (!connection && refCount > 0) { + connection = new _Subscriber__WEBPACK_IMPORTED_MODULE_2__.SafeSubscriber({ + next: value => dest.next(value), + error: err => { + hasErrored = true; + cancelReset(); + resetConnection = handleReset(reset, resetOnError, err); + dest.error(err); + }, + complete: () => { + hasCompleted = true; + cancelReset(); + resetConnection = handleReset(reset, resetOnComplete); + dest.complete(); + } + }); + (0, _observable_innerFrom__WEBPACK_IMPORTED_MODULE_3__.innerFrom)(source).subscribe( + connection + ); + } + })(wrapperSource); + }; + } + function handleReset(reset, on, ...args) { + if (on === true) { + reset(); + return; + } + if (on === false) { + return; + } + const onSubscriber = new _Subscriber__WEBPACK_IMPORTED_MODULE_2__.SafeSubscriber({ + next: () => { + onSubscriber.unsubscribe(); + reset(); + } + }); + return on(...args).subscribe(onSubscriber); + } + + /***/ + }, + + /***/ 4317: + /*!**********************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/operators/subscribeOn.js ***! + \**********************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ subscribeOn: () => /* binding */ subscribeOn + /* harmony export */ + }); + /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../util/lift */ 1944 + ); + + function subscribeOn(scheduler, delay = 0) { + return (0, _util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { + subscriber.add(scheduler.schedule(() => source.subscribe(subscriber), delay)); + }); + } + + /***/ + }, + + /***/ 3417: + /*!************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduleArray.js ***! + \************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ scheduleArray: () => /* binding */ scheduleArray + /* harmony export */ + }); + /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../Observable */ 833 + ); + + function scheduleArray(input, scheduler) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { + let i = 0; + return scheduler.schedule(function() { + if (i === input.length) { + subscriber.complete(); + } else { + subscriber.next(input[i++]); + if (!subscriber.closed) { + this.schedule(); + } + } + }); + }); + } + + /***/ + }, + + /***/ 5646: + /*!********************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduleAsyncIterable.js ***! + \********************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ scheduleAsyncIterable: () => /* binding */ scheduleAsyncIterable + /* harmony export */ + }); + /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../Observable */ 833 + ); + /* harmony import */ var _util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../util/executeSchedule */ 1817 + ); + + function scheduleAsyncIterable(input, scheduler) { + if (!input) { + throw new Error("Iterable cannot be null"); + } + + return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { + (0, _util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__.executeSchedule)( + subscriber, + scheduler, + () => { + const iterator = input[Symbol.asyncIterator](); + (0, _util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__.executeSchedule)( + subscriber, + scheduler, + () => { + iterator.next().then(result => { + if (result.done) { + subscriber.complete(); + } else { + subscriber.next(result.value); + } + }); + }, + 0, + true + ); + } + ); + }); + } + + /***/ + }, + + /***/ 4924: + /*!***************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduleIterable.js ***! + \***************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ scheduleIterable: () => /* binding */ scheduleIterable + /* harmony export */ + }); + /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../Observable */ 833 + ); + /* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ../symbol/iterator */ 7321 + ); + /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! ../util/isFunction */ 2971 + ); + /* harmony import */ var _util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../util/executeSchedule */ 1817 + ); + + function scheduleIterable(input, scheduler) { + return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { + let iterator; + (0, _util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__.executeSchedule)( + subscriber, + scheduler, + () => { + iterator = input[_symbol_iterator__WEBPACK_IMPORTED_MODULE_2__.iterator](); + (0, _util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__.executeSchedule)( + subscriber, + scheduler, + () => { + let value; + let done; + try { + ({ value, done } = iterator.next()); + } catch (err) { + subscriber.error(err); + return; + } + if (done) { + subscriber.complete(); + } else { + subscriber.next(value); + } + }, + 0, + true + ); + } + ); + return () => + (0, _util_isFunction__WEBPACK_IMPORTED_MODULE_3__.isFunction)( + iterator === null || iterator === void 0 ? void 0 : iterator.return + ) && iterator.return(); + }); + } + + /***/ + }, + + /***/ 4349: + /*!*****************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduleObservable.js ***! + \*****************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ scheduleObservable: () => /* binding */ scheduleObservable + /* harmony export */ + }); + /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../observable/innerFrom */ 4987 + ); + /* harmony import */ var _operators_observeOn__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ../operators/observeOn */ 8728 + ); + /* harmony import */ var _operators_subscribeOn__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../operators/subscribeOn */ 4317 + ); + + function scheduleObservable(input, scheduler) { + return (0, _observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__.innerFrom)(input).pipe( + (0, _operators_subscribeOn__WEBPACK_IMPORTED_MODULE_1__.subscribeOn)(scheduler), + (0, _operators_observeOn__WEBPACK_IMPORTED_MODULE_2__.observeOn)(scheduler) + ); + } + + /***/ + }, + + /***/ 6642: + /*!**************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/scheduled/schedulePromise.js ***! + \**************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ schedulePromise: () => /* binding */ schedulePromise + /* harmony export */ + }); + /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../observable/innerFrom */ 4987 + ); + /* harmony import */ var _operators_observeOn__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ../operators/observeOn */ 8728 + ); + /* harmony import */ var _operators_subscribeOn__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../operators/subscribeOn */ 4317 + ); + + function schedulePromise(input, scheduler) { + return (0, _observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__.innerFrom)(input).pipe( + (0, _operators_subscribeOn__WEBPACK_IMPORTED_MODULE_1__.subscribeOn)(scheduler), + (0, _operators_observeOn__WEBPACK_IMPORTED_MODULE_2__.observeOn)(scheduler) + ); + } + + /***/ + }, + + /***/ 316: + /*!*************************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduleReadableStreamLike.js ***! + \*************************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ scheduleReadableStreamLike: () => /* binding */ scheduleReadableStreamLike + /* harmony export */ + }); + /* harmony import */ var _scheduleAsyncIterable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./scheduleAsyncIterable */ 5646 + ); + /* harmony import */ var _util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../util/isReadableStreamLike */ 181 + ); + + function scheduleReadableStreamLike(input, scheduler) { + return (0, _scheduleAsyncIterable__WEBPACK_IMPORTED_MODULE_0__.scheduleAsyncIterable)( + (0, _util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_1__.readableStreamLikeToAsyncGenerator)( + input + ), + scheduler + ); + } + + /***/ + }, + + /***/ 9517: + /*!********************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduled.js ***! + \********************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ scheduled: () => /* binding */ scheduled + /* harmony export */ + }); + /* harmony import */ var _scheduleObservable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./scheduleObservable */ 4349 + ); + /* harmony import */ var _schedulePromise__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__( + /*! ./schedulePromise */ 6642 + ); + /* harmony import */ var _scheduleArray__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! ./scheduleArray */ 3417 + ); + /* harmony import */ var _scheduleIterable__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__( + /*! ./scheduleIterable */ 4924 + ); + /* harmony import */ var _scheduleAsyncIterable__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__( + /*! ./scheduleAsyncIterable */ 5646 + ); + /* harmony import */ var _util_isInteropObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../util/isInteropObservable */ 1331 + ); + /* harmony import */ var _util_isPromise__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__( + /*! ../util/isPromise */ 9548 + ); + /* harmony import */ var _util_isArrayLike__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! ../util/isArrayLike */ 9806 + ); + /* harmony import */ var _util_isIterable__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__( + /*! ../util/isIterable */ 3433 + ); + /* harmony import */ var _util_isAsyncIterable__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__( + /*! ../util/isAsyncIterable */ 470 + ); + /* harmony import */ var _util_throwUnobservableError__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__( + /*! ../util/throwUnobservableError */ 7785 + ); + /* harmony import */ var _util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__( + /*! ../util/isReadableStreamLike */ 181 + ); + /* harmony import */ var _scheduleReadableStreamLike__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__( + /*! ./scheduleReadableStreamLike */ 316 + ); + + function scheduled(input, scheduler) { + if (input != null) { + if ((0, _util_isInteropObservable__WEBPACK_IMPORTED_MODULE_0__.isInteropObservable)(input)) { + return (0, _scheduleObservable__WEBPACK_IMPORTED_MODULE_1__.scheduleObservable)( + input, + scheduler + ); + } + if ((0, _util_isArrayLike__WEBPACK_IMPORTED_MODULE_2__.isArrayLike)(input)) { + return (0, _scheduleArray__WEBPACK_IMPORTED_MODULE_3__.scheduleArray)(input, scheduler); + } + if ((0, _util_isPromise__WEBPACK_IMPORTED_MODULE_4__.isPromise)(input)) { + return (0, _schedulePromise__WEBPACK_IMPORTED_MODULE_5__.schedulePromise)(input, scheduler); + } + if ((0, _util_isAsyncIterable__WEBPACK_IMPORTED_MODULE_6__.isAsyncIterable)(input)) { + return (0, _scheduleAsyncIterable__WEBPACK_IMPORTED_MODULE_7__.scheduleAsyncIterable)( + input, + scheduler + ); + } + if ((0, _util_isIterable__WEBPACK_IMPORTED_MODULE_8__.isIterable)(input)) { + return (0, _scheduleIterable__WEBPACK_IMPORTED_MODULE_9__.scheduleIterable)( + input, + scheduler + ); + } + if ((0, _util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_10__.isReadableStreamLike)(input)) { + return (0, + _scheduleReadableStreamLike__WEBPACK_IMPORTED_MODULE_11__.scheduleReadableStreamLike)( + input, + scheduler + ); + } + } + throw (0, + _util_throwUnobservableError__WEBPACK_IMPORTED_MODULE_12__.createInvalidObservableTypeError)(input); + } + + /***/ + }, + + /***/ 3542: + /*!**************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/scheduler/timeoutProvider.js ***! + \**************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ timeoutProvider: () => /* binding */ timeoutProvider + /* harmony export */ + }); + const timeoutProvider = { + setTimeout(handler, timeout, ...args) { + const { delegate } = timeoutProvider; + if (delegate === null || delegate === void 0 ? void 0 : delegate.setTimeout) { + return delegate.setTimeout(handler, timeout, ...args); + } + return setTimeout(handler, timeout, ...args); + }, + clearTimeout(handle) { + const { delegate } = timeoutProvider; + return ((delegate === null || delegate === void 0 ? void 0 : delegate.clearTimeout) || + clearTimeout)(handle); + }, + delegate: undefined + }; + + /***/ + }, + + /***/ 7321: + /*!****************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/symbol/iterator.js ***! + \****************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ getSymbolIterator: () => /* binding */ getSymbolIterator, + /* harmony export */ iterator: () => /* binding */ iterator + /* harmony export */ + }); + function getSymbolIterator() { + if (typeof Symbol !== "function" || !Symbol.iterator) { + return "@@iterator"; + } + return Symbol.iterator; + } + const iterator = getSymbolIterator(); + + /***/ + }, + + /***/ 4585: + /*!******************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/symbol/observable.js ***! + \******************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ observable: () => /* binding */ observable + /* harmony export */ + }); + const observable = (() => (typeof Symbol === "function" && Symbol.observable) || "@@observable")(); + + /***/ + }, + + /***/ 9872: + /*!*****************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/ObjectUnsubscribedError.js ***! + \*****************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ ObjectUnsubscribedError: () => /* binding */ ObjectUnsubscribedError + /* harmony export */ + }); + /* harmony import */ var _createErrorClass__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./createErrorClass */ 7543 + ); + + const ObjectUnsubscribedError = (0, _createErrorClass__WEBPACK_IMPORTED_MODULE_0__.createErrorClass)( + _super => + function ObjectUnsubscribedErrorImpl() { + _super(this); + this.name = "ObjectUnsubscribedError"; + this.message = "object unsubscribed"; + } + ); + + /***/ + }, + + /***/ 2524: + /*!*************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/UnsubscriptionError.js ***! + \*************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ UnsubscriptionError: () => /* binding */ UnsubscriptionError + /* harmony export */ + }); + /* harmony import */ var _createErrorClass__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./createErrorClass */ 7543 + ); + + const UnsubscriptionError = (0, _createErrorClass__WEBPACK_IMPORTED_MODULE_0__.createErrorClass)( + _super => + function UnsubscriptionErrorImpl(errors) { + _super(this); + this.message = errors + ? `${errors.length} errors occurred during unsubscription: +${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join("\n ")}` + : ""; + this.name = "UnsubscriptionError"; + this.errors = errors; + } + ); + + /***/ + }, + + /***/ 420: + /*!**********************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/args.js ***! + \**********************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ popNumber: () => /* binding */ popNumber, + /* harmony export */ popResultSelector: () => /* binding */ popResultSelector, + /* harmony export */ popScheduler: () => /* binding */ popScheduler + /* harmony export */ + }); + /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./isFunction */ 2971 + ); + /* harmony import */ var _isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./isScheduler */ 9867 + ); + + function last(arr) { + return arr[arr.length - 1]; + } + function popResultSelector(args) { + return (0, _isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(last(args)) + ? args.pop() + : undefined; + } + function popScheduler(args) { + return (0, _isScheduler__WEBPACK_IMPORTED_MODULE_1__.isScheduler)(last(args)) + ? args.pop() + : undefined; + } + function popNumber(args, defaultValue) { + return typeof last(args) === "number" ? args.pop() : defaultValue; + } + + /***/ + }, + + /***/ 9663: + /*!***************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/arrRemove.js ***! + \***************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ arrRemove: () => /* binding */ arrRemove + /* harmony export */ + }); + function arrRemove(arr, item) { + if (arr) { + const index = arr.indexOf(item); + 0 <= index && arr.splice(index, 1); + } + } + + /***/ + }, + + /***/ 7543: + /*!**********************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/createErrorClass.js ***! + \**********************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ createErrorClass: () => /* binding */ createErrorClass + /* harmony export */ + }); + function createErrorClass(createImpl) { + const _super = instance => { + Error.call(instance); + instance.stack = new Error().stack; + }; + const ctorFunc = createImpl(_super); + ctorFunc.prototype = Object.create(Error.prototype); + ctorFunc.prototype.constructor = ctorFunc; + return ctorFunc; + } + + /***/ + }, + + /***/ 2309: + /*!******************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/errorContext.js ***! + \******************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ captureError: () => /* binding */ captureError, + /* harmony export */ errorContext: () => /* binding */ errorContext + /* harmony export */ + }); + /* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../config */ 9057 + ); + + let context = null; + function errorContext(cb) { + if (_config__WEBPACK_IMPORTED_MODULE_0__.config.useDeprecatedSynchronousErrorHandling) { + const isRoot = !context; + if (isRoot) { + context = { errorThrown: false, error: null }; + } + cb(); + if (isRoot) { + const { errorThrown, error } = context; + context = null; + if (errorThrown) { + throw error; + } + } + } else { + cb(); + } + } + function captureError(err) { + if (_config__WEBPACK_IMPORTED_MODULE_0__.config.useDeprecatedSynchronousErrorHandling && context) { + context.errorThrown = true; + context.error = err; + } + } + + /***/ + }, + + /***/ 1817: + /*!*********************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/executeSchedule.js ***! + \*********************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ executeSchedule: () => /* binding */ executeSchedule + /* harmony export */ + }); + function executeSchedule(parentSubscription, scheduler, work, delay = 0, repeat = false) { + const scheduleSubscription = scheduler.schedule(function() { + work(); + if (repeat) { + parentSubscription.add(this.schedule(null, delay)); + } else { + this.unsubscribe(); + } + }, delay); + parentSubscription.add(scheduleSubscription); + if (!repeat) { + return scheduleSubscription; + } + } + + /***/ + }, + + /***/ 9173: + /*!**************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/identity.js ***! + \**************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ identity: () => /* binding */ identity + /* harmony export */ + }); + function identity(x) { + return x; + } + + /***/ + }, + + /***/ 9806: + /*!*****************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/isArrayLike.js ***! + \*****************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ isArrayLike: () => /* binding */ isArrayLike + /* harmony export */ + }); + const isArrayLike = x => x && typeof x.length === "number" && typeof x !== "function"; + + /***/ + }, + + /***/ 470: + /*!*********************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/isAsyncIterable.js ***! + \*********************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ isAsyncIterable: () => /* binding */ isAsyncIterable + /* harmony export */ + }); + /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./isFunction */ 2971 + ); + + function isAsyncIterable(obj) { + return ( + Symbol.asyncIterator && + (0, _isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)( + obj === null || obj === void 0 ? void 0 : obj[Symbol.asyncIterator] + ) + ); + } + + /***/ + }, + + /***/ 2971: + /*!****************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/isFunction.js ***! + \****************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ isFunction: () => /* binding */ isFunction + /* harmony export */ + }); + function isFunction(value) { + return typeof value === "function"; + } + + /***/ + }, + + /***/ 1331: + /*!*************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/isInteropObservable.js ***! + \*************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ isInteropObservable: () => /* binding */ isInteropObservable + /* harmony export */ + }); + /* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../symbol/observable */ 4585 + ); + /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./isFunction */ 2971 + ); + + function isInteropObservable(input) { + return (0, _isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)( + input[_symbol_observable__WEBPACK_IMPORTED_MODULE_1__.observable] + ); + } + + /***/ + }, + + /***/ 3433: + /*!****************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/isIterable.js ***! + \****************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ isIterable: () => /* binding */ isIterable + /* harmony export */ + }); + /* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../symbol/iterator */ 7321 + ); + /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./isFunction */ 2971 + ); + + function isIterable(input) { + return (0, _isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)( + input === null || input === void 0 + ? void 0 + : input[_symbol_iterator__WEBPACK_IMPORTED_MODULE_1__.iterator] + ); + } + + /***/ + }, + + /***/ 9548: + /*!***************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/isPromise.js ***! + \***************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ isPromise: () => /* binding */ isPromise + /* harmony export */ + }); + /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./isFunction */ 2971 + ); + + function isPromise(value) { + return (0, _isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)( + value === null || value === void 0 ? void 0 : value.then + ); + } + + /***/ + }, + + /***/ 181: + /*!**************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/isReadableStreamLike.js ***! + \**************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ isReadableStreamLike: () => /* binding */ isReadableStreamLike, + /* harmony export */ readableStreamLikeToAsyncGenerator: () => + /* binding */ readableStreamLikeToAsyncGenerator + /* harmony export */ + }); + /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ 4929); + /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ./isFunction */ 2971 + ); + + function readableStreamLikeToAsyncGenerator(readableStream) { + return (0, tslib__WEBPACK_IMPORTED_MODULE_0__.__asyncGenerator)( + this, + arguments, + function* readableStreamLikeToAsyncGenerator_1() { + const reader = readableStream.getReader(); + + try { + while (true) { + const { value, done } = yield (0, tslib__WEBPACK_IMPORTED_MODULE_0__.__await)( + reader.read() + ); + + if (done) { + return yield (0, tslib__WEBPACK_IMPORTED_MODULE_0__.__await)(void 0); + } + + yield yield (0, tslib__WEBPACK_IMPORTED_MODULE_0__.__await)(value); + } + } finally { + reader.releaseLock(); + } + } + ); + } + function isReadableStreamLike(obj) { + return (0, _isFunction__WEBPACK_IMPORTED_MODULE_1__.isFunction)( + obj === null || obj === void 0 ? void 0 : obj.getReader + ); + } + + /***/ + }, + + /***/ 9867: + /*!*****************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/isScheduler.js ***! + \*****************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ isScheduler: () => /* binding */ isScheduler + /* harmony export */ + }); + /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./isFunction */ 2971 + ); + + function isScheduler(value) { + return value && (0, _isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(value.schedule); + } + + /***/ + }, + + /***/ 1944: + /*!**********************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/lift.js ***! + \**********************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ hasLift: () => /* binding */ hasLift, + /* harmony export */ operate: () => /* binding */ operate + /* harmony export */ + }); + /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./isFunction */ 2971 + ); + + function hasLift(source) { + return (0, _isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)( + source === null || source === void 0 ? void 0 : source.lift + ); + } + function operate(init) { + return source => { + if (hasLift(source)) { + return source.lift(function(liftedSource) { + try { + return init(liftedSource, this); + } catch (err) { + this.error(err); + } + }); + } + throw new TypeError("Unable to lift unknown Observable type"); + }; + } + + /***/ + }, + + /***/ 9635: + /*!**********************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/noop.js ***! + \**********************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ noop: () => /* binding */ noop + /* harmony export */ + }); + function noop() {} + + /***/ + }, + + /***/ 629: + /*!**********************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/pipe.js ***! + \**********************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ pipe: () => /* binding */ pipe, + /* harmony export */ pipeFromArray: () => /* binding */ pipeFromArray + /* harmony export */ + }); + /* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ./identity */ 9173 + ); + + function pipe(...fns) { + return pipeFromArray(fns); + } + function pipeFromArray(fns) { + if (fns.length === 0) { + return _identity__WEBPACK_IMPORTED_MODULE_0__.identity; + } + if (fns.length === 1) { + return fns[0]; + } + return function piped(input) { + return fns.reduce((prev, fn) => fn(prev), input); + }; + } + + /***/ + }, + + /***/ 4709: + /*!**************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/reportUnhandledError.js ***! + \**************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ reportUnhandledError: () => /* binding */ reportUnhandledError + /* harmony export */ + }); + /* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! ../config */ 9057 + ); + /* harmony import */ var _scheduler_timeoutProvider__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! ../scheduler/timeoutProvider */ 3542 + ); + + function reportUnhandledError(err) { + _scheduler_timeoutProvider__WEBPACK_IMPORTED_MODULE_0__.timeoutProvider.setTimeout(() => { + const { onUnhandledError } = _config__WEBPACK_IMPORTED_MODULE_1__.config; + if (onUnhandledError) { + onUnhandledError(err); + } else { + throw err; + } + }); + } + + /***/ + }, + + /***/ 7785: + /*!****************************************************************************!*\ + !*** ./node_modules/rxjs/dist/esm/internal/util/throwUnobservableError.js ***! + \****************************************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ createInvalidObservableTypeError: () => + /* binding */ createInvalidObservableTypeError + /* harmony export */ + }); + function createInvalidObservableTypeError(input) { + return new TypeError( + `You provided ${ + input !== null && typeof input === "object" ? "an invalid object" : `'${input}'` + } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.` + ); + } + + /***/ + }, + + /***/ 4929: + /*!*****************************************!*\ + !*** ./node_modules/tslib/tslib.es6.js ***! + \*****************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ __assign: () => /* binding */ __assign, + /* harmony export */ __asyncDelegator: () => /* binding */ __asyncDelegator, + /* harmony export */ __asyncGenerator: () => /* binding */ __asyncGenerator, + /* harmony export */ __asyncValues: () => /* binding */ __asyncValues, + /* harmony export */ __await: () => /* binding */ __await, + /* harmony export */ __awaiter: () => /* binding */ __awaiter, + /* harmony export */ __classPrivateFieldGet: () => /* binding */ __classPrivateFieldGet, + /* harmony export */ __classPrivateFieldIn: () => /* binding */ __classPrivateFieldIn, + /* harmony export */ __classPrivateFieldSet: () => /* binding */ __classPrivateFieldSet, + /* harmony export */ __createBinding: () => /* binding */ __createBinding, + /* harmony export */ __decorate: () => /* binding */ __decorate, + /* harmony export */ __exportStar: () => /* binding */ __exportStar, + /* harmony export */ __extends: () => /* binding */ __extends, + /* harmony export */ __generator: () => /* binding */ __generator, + /* harmony export */ __importDefault: () => /* binding */ __importDefault, + /* harmony export */ __importStar: () => /* binding */ __importStar, + /* harmony export */ __makeTemplateObject: () => /* binding */ __makeTemplateObject, + /* harmony export */ __metadata: () => /* binding */ __metadata, + /* harmony export */ __param: () => /* binding */ __param, + /* harmony export */ __read: () => /* binding */ __read, + /* harmony export */ __rest: () => /* binding */ __rest, + /* harmony export */ __spread: () => /* binding */ __spread, + /* harmony export */ __spreadArray: () => /* binding */ __spreadArray, + /* harmony export */ __spreadArrays: () => /* binding */ __spreadArrays, + /* harmony export */ __values: () => /* binding */ __values + /* harmony export */ + }); + /****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = + Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && + function(d, b) { + d.__proto__ = b; + }) || + function(d, b) { + for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __()); + } + + var __assign = function() { + __assign = + Object.assign || + function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + + function __rest(s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; + } + + function __decorate(decorators, target, key, desc) { + var c = arguments.length, + r = + c < 3 + ? target + : desc === null + ? (desc = Object.getOwnPropertyDescriptor(target, key)) + : desc, + d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") + r = Reflect.decorate(decorators, target, key, desc); + else + for (var i = decorators.length - 1; i >= 0; i--) + if ((d = decorators[i])) + r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + } + + function __param(paramIndex, decorator) { + return function(target, key) { + decorator(target, key, paramIndex); + }; + } + + function __metadata(metadataKey, metadataValue) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") + return Reflect.metadata(metadataKey, metadataValue); + } + + function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P + ? value + : new P(function(resolve) { + resolve(value); + }); + } + return new (P || (P = Promise))(function(resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + } + function rejected(value) { + try { + step(generator["throw"](value)); + } catch (e) { + reject(e); + } + } + function step(result) { + result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); + } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } + + function __generator(thisArg, body) { + var _ = { + label: 0, + sent: function() { + if (t[0] & 1) throw t[1]; + return t[1]; + }, + trys: [], + ops: [] + }, + f, + y, + t, + g; + return ( + (g = { next: verb(0), throw: verb(1), return: verb(2) }), + typeof Symbol === "function" && + (g[Symbol.iterator] = function() { + return this; + }), + g + ); + function verb(n) { + return function(v) { + return step([n, v]); + }; + } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) + try { + if ( + ((f = 1), + y && + (t = + op[0] & 2 + ? y["return"] + : op[0] + ? y["throw"] || ((t = y["return"]) && t.call(y), 0) + : y.next) && + !(t = t.call(y, op[1])).done) + ) + return t; + if (((y = 0), t)) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: + case 1: + t = op; + break; + case 4: + _.label++; + return { value: op[1], done: false }; + case 5: + _.label++; + y = op[1]; + op = [0]; + continue; + case 7: + op = _.ops.pop(); + _.trys.pop(); + continue; + default: + if ( + !((t = _.trys), (t = t.length > 0 && t[t.length - 1])) && + (op[0] === 6 || op[0] === 2) + ) { + _ = 0; + continue; + } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { + _.label = op[1]; + break; + } + if (op[0] === 6 && _.label < t[1]) { + _.label = t[1]; + t = op; + break; + } + if (t && _.label < t[2]) { + _.label = t[2]; + _.ops.push(op); + break; + } + if (t[2]) _.ops.pop(); + _.trys.pop(); + continue; + } + op = body.call(thisArg, _); + } catch (e) { + op = [6, e]; + y = 0; + } finally { + f = t = 0; + } + if (op[0] & 5) throw op[1]; + return { value: op[0] ? op[1] : void 0, done: true }; + } + } + + var __createBinding = Object.create + ? function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { + enumerable: true, + get: function() { + return m[k]; + } + }; + } + Object.defineProperty(o, k2, desc); + } + : function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; + }; + + function __exportStar(m, o) { + for (var p in m) + if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); + } + + function __values(o) { + var s = typeof Symbol === "function" && Symbol.iterator, + m = s && o[s], + i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") + return { + next: function() { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); + } + + function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), + r, + ar = [], + e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } catch (error) { + e = { error: error }; + } finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } finally { + if (e) throw e.error; + } + } + return ar; + } + + /** @deprecated */ + function __spread() { + for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); + return ar; + } + + /** @deprecated */ + function __spreadArrays() { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; + return r; + } + + function __spreadArray(to, from, pack) { + if (pack || arguments.length === 2) + for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); + } + + function __await(v) { + return this instanceof __await ? ((this.v = v), this) : new __await(v); + } + + function __asyncGenerator(thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), + i, + q = []; + return ( + (i = {}), + verb("next"), + verb("throw"), + verb("return"), + (i[Symbol.asyncIterator] = function() { + return this; + }), + i + ); + function verb(n) { + if (g[n]) + i[n] = function(v) { + return new Promise(function(a, b) { + q.push([n, v, a, b]) > 1 || resume(n, v); + }); + }; + } + function resume(n, v) { + try { + step(g[n](v)); + } catch (e) { + settle(q[0][3], e); + } + } + function step(r) { + r.value instanceof __await + ? Promise.resolve(r.value.v).then(fulfill, reject) + : settle(q[0][2], r); + } + function fulfill(value) { + resume("next", value); + } + function reject(value) { + resume("throw", value); + } + function settle(f, v) { + if ((f(v), q.shift(), q.length)) resume(q[0][0], q[0][1]); + } + } + + function __asyncDelegator(o) { + var i, p; + return ( + (i = {}), + verb("next"), + verb("throw", function(e) { + throw e; + }), + verb("return"), + (i[Symbol.iterator] = function() { + return this; + }), + i + ); + function verb(n, f) { + i[n] = o[n] + ? function(v) { + return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; + } + : f; + } + } + + function __asyncValues(o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], + i; + return m + ? m.call(o) + : ((o = typeof __values === "function" ? __values(o) : o[Symbol.iterator]()), + (i = {}), + verb("next"), + verb("throw"), + verb("return"), + (i[Symbol.asyncIterator] = function() { + return this; + }), + i); + function verb(n) { + i[n] = + o[n] && + function(v) { + return new Promise(function(resolve, reject) { + (v = o[n](v)), settle(resolve, reject, v.done, v.value); + }); + }; + } + function settle(resolve, reject, d, v) { + Promise.resolve(v).then(function(v) { + resolve({ value: v, done: d }); + }, reject); + } + } + + function __makeTemplateObject(cooked, raw) { + if (Object.defineProperty) { + Object.defineProperty(cooked, "raw", { value: raw }); + } else { + cooked.raw = raw; + } + return cooked; + } + + var __setModuleDefault = Object.create + ? function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + } + : function(o, v) { + o["default"] = v; + }; + + function __importStar(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) + for (var k in mod) + if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) + __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; + } + + function __importDefault(mod) { + return mod && mod.__esModule ? mod : { default: mod }; + } + + function __classPrivateFieldGet(receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) + throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); + } + + function __classPrivateFieldSet(receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) + throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return ( + kind === "a" ? f.call(receiver, value) : f ? (f.value = value) : state.set(receiver, value), + value + ); + } + + function __classPrivateFieldIn(state, receiver) { + if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) + throw new TypeError("Cannot use 'in' operator on non-object"); + return typeof state === "function" ? receiver === state : state.has(receiver); + } + + /***/ + }, + + /***/ 6362: + /*!**********************************************************!*\ + !*** ./node_modules/@angular/common/fesm2015/common.mjs ***! + \**********************************************************/ + /***/ (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ APP_BASE_HREF: () => /* binding */ APP_BASE_HREF, + /* harmony export */ AsyncPipe: () => /* binding */ AsyncPipe, + /* harmony export */ CommonModule: () => /* binding */ CommonModule, + /* harmony export */ CurrencyPipe: () => /* binding */ CurrencyPipe, + /* harmony export */ DATE_PIPE_DEFAULT_TIMEZONE: () => /* binding */ DATE_PIPE_DEFAULT_TIMEZONE, + /* harmony export */ DOCUMENT: () => /* binding */ DOCUMENT, + /* harmony export */ DatePipe: () => /* binding */ DatePipe, + /* harmony export */ DecimalPipe: () => /* binding */ DecimalPipe, + /* harmony export */ FormStyle: () => /* binding */ FormStyle, + /* harmony export */ FormatWidth: () => /* binding */ FormatWidth, + /* harmony export */ HashLocationStrategy: () => /* binding */ HashLocationStrategy, + /* harmony export */ I18nPluralPipe: () => /* binding */ I18nPluralPipe, + /* harmony export */ I18nSelectPipe: () => /* binding */ I18nSelectPipe, + /* harmony export */ JsonPipe: () => /* binding */ JsonPipe, + /* harmony export */ KeyValuePipe: () => /* binding */ KeyValuePipe, + /* harmony export */ LOCATION_INITIALIZED: () => /* binding */ LOCATION_INITIALIZED, + /* harmony export */ Location: () => /* binding */ Location, + /* harmony export */ LocationStrategy: () => /* binding */ LocationStrategy, + /* harmony export */ LowerCasePipe: () => /* binding */ LowerCasePipe, + /* harmony export */ NgClass: () => /* binding */ NgClass, + /* harmony export */ NgComponentOutlet: () => /* binding */ NgComponentOutlet, + /* harmony export */ NgForOf: () => /* binding */ NgForOf, + /* harmony export */ NgForOfContext: () => /* binding */ NgForOfContext, + /* harmony export */ NgIf: () => /* binding */ NgIf, + /* harmony export */ NgIfContext: () => /* binding */ NgIfContext, + /* harmony export */ NgLocaleLocalization: () => /* binding */ NgLocaleLocalization, + /* harmony export */ NgLocalization: () => /* binding */ NgLocalization, + /* harmony export */ NgPlural: () => /* binding */ NgPlural, + /* harmony export */ NgPluralCase: () => /* binding */ NgPluralCase, + /* harmony export */ NgStyle: () => /* binding */ NgStyle, + /* harmony export */ NgSwitch: () => /* binding */ NgSwitch, + /* harmony export */ NgSwitchCase: () => /* binding */ NgSwitchCase, + /* harmony export */ NgSwitchDefault: () => /* binding */ NgSwitchDefault, + /* harmony export */ NgTemplateOutlet: () => /* binding */ NgTemplateOutlet, + /* harmony export */ NumberFormatStyle: () => /* binding */ NumberFormatStyle, + /* harmony export */ NumberSymbol: () => /* binding */ NumberSymbol, + /* harmony export */ PathLocationStrategy: () => /* binding */ PathLocationStrategy, + /* harmony export */ PercentPipe: () => /* binding */ PercentPipe, + /* harmony export */ PlatformLocation: () => /* binding */ PlatformLocation, + /* harmony export */ Plural: () => /* binding */ Plural, + /* harmony export */ SlicePipe: () => /* binding */ SlicePipe, + /* harmony export */ TitleCasePipe: () => /* binding */ TitleCasePipe, + /* harmony export */ TranslationWidth: () => /* binding */ TranslationWidth, + /* harmony export */ UpperCasePipe: () => /* binding */ UpperCasePipe, + /* harmony export */ VERSION: () => /* binding */ VERSION, + /* harmony export */ ViewportScroller: () => /* binding */ ViewportScroller, + /* harmony export */ WeekDay: () => /* binding */ WeekDay, + /* harmony export */ XhrFactory: () => /* binding */ XhrFactory, + /* harmony export */ formatCurrency: () => /* binding */ formatCurrency, + /* harmony export */ formatDate: () => /* binding */ formatDate, + /* harmony export */ formatNumber: () => /* binding */ formatNumber, + /* harmony export */ formatPercent: () => /* binding */ formatPercent, + /* harmony export */ getCurrencySymbol: () => /* binding */ getCurrencySymbol, + /* harmony export */ getLocaleCurrencyCode: () => /* binding */ getLocaleCurrencyCode, + /* harmony export */ getLocaleCurrencyName: () => /* binding */ getLocaleCurrencyName, + /* harmony export */ getLocaleCurrencySymbol: () => /* binding */ getLocaleCurrencySymbol, + /* harmony export */ getLocaleDateFormat: () => /* binding */ getLocaleDateFormat, + /* harmony export */ getLocaleDateTimeFormat: () => /* binding */ getLocaleDateTimeFormat, + /* harmony export */ getLocaleDayNames: () => /* binding */ getLocaleDayNames, + /* harmony export */ getLocaleDayPeriods: () => /* binding */ getLocaleDayPeriods, + /* harmony export */ getLocaleDirection: () => /* binding */ getLocaleDirection, + /* harmony export */ getLocaleEraNames: () => /* binding */ getLocaleEraNames, + /* harmony export */ getLocaleExtraDayPeriodRules: () => /* binding */ getLocaleExtraDayPeriodRules, + /* harmony export */ getLocaleExtraDayPeriods: () => /* binding */ getLocaleExtraDayPeriods, + /* harmony export */ getLocaleFirstDayOfWeek: () => /* binding */ getLocaleFirstDayOfWeek, + /* harmony export */ getLocaleId: () => /* binding */ getLocaleId, + /* harmony export */ getLocaleMonthNames: () => /* binding */ getLocaleMonthNames, + /* harmony export */ getLocaleNumberFormat: () => /* binding */ getLocaleNumberFormat, + /* harmony export */ getLocaleNumberSymbol: () => /* binding */ getLocaleNumberSymbol, + /* harmony export */ getLocalePluralCase: () => /* binding */ getLocalePluralCase, + /* harmony export */ getLocaleTimeFormat: () => /* binding */ getLocaleTimeFormat, + /* harmony export */ getLocaleWeekEndRange: () => /* binding */ getLocaleWeekEndRange, + /* harmony export */ getNumberOfCurrencyDigits: () => /* binding */ getNumberOfCurrencyDigits, + /* harmony export */ isPlatformBrowser: () => /* binding */ isPlatformBrowser, + /* harmony export */ isPlatformServer: () => /* binding */ isPlatformServer, + /* harmony export */ isPlatformWorkerApp: () => /* binding */ isPlatformWorkerApp, + /* harmony export */ isPlatformWorkerUi: () => /* binding */ isPlatformWorkerUi, + /* harmony export */ registerLocaleData: () => /* binding */ registerLocaleData, + /* harmony export */ ɵBrowserPlatformLocation: () => /* binding */ BrowserPlatformLocation, + /* harmony export */ ɵDomAdapter: () => /* binding */ DomAdapter, + /* harmony export */ ɵNullViewportScroller: () => /* binding */ NullViewportScroller, + /* harmony export */ ɵPLATFORM_BROWSER_ID: () => /* binding */ PLATFORM_BROWSER_ID, + /* harmony export */ ɵPLATFORM_SERVER_ID: () => /* binding */ PLATFORM_SERVER_ID, + /* harmony export */ ɵPLATFORM_WORKER_APP_ID: () => /* binding */ PLATFORM_WORKER_APP_ID, + /* harmony export */ ɵPLATFORM_WORKER_UI_ID: () => /* binding */ PLATFORM_WORKER_UI_ID, + /* harmony export */ ɵgetDOM: () => /* binding */ getDOM, + /* harmony export */ ɵparseCookieValue: () => /* binding */ parseCookieValue, + /* harmony export */ ɵsetRootDomAdapter: () => /* binding */ setRootDomAdapter + /* harmony export */ + }); + /* harmony import */ var _angular_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! @angular/core */ 3184 + ); + /** + * @license Angular v13.3.11 + * (c) 2010-2022 Google LLC. https://angular.io/ + * License: MIT + */ + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + let _DOM = null; + + function getDOM() { + return _DOM; + } + + function setDOM(adapter) { + _DOM = adapter; + } + + function setRootDomAdapter(adapter) { + if (!_DOM) { + _DOM = adapter; + } + } + /* tslint:disable:requireParameterType */ + + /** + * Provides DOM operations in an environment-agnostic way. + * + * @security Tread carefully! Interacting with the DOM directly is dangerous and + * can introduce XSS risks. + */ + + class DomAdapter {} + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * A DI Token representing the main rendering context. In a browser this is the DOM Document. + * + * Note: Document might not be available in the Application Context when Application and Rendering + * Contexts are not the same (e.g. when running the application in a Web Worker). + * + * @publicApi + */ + + const DOCUMENT = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken("DocumentToken"); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * This class should not be used directly by an application developer. Instead, use + * {@link Location}. + * + * `PlatformLocation` encapsulates all calls to DOM APIs, which allows the Router to be + * platform-agnostic. + * This means that we can have different implementation of `PlatformLocation` for the different + * platforms that Angular supports. For example, `@angular/platform-browser` provides an + * implementation specific to the browser environment, while `@angular/platform-server` provides + * one suitable for use with server-side rendering. + * + * The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy} + * when they need to interact with the DOM APIs like pushState, popState, etc. + * + * {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly + * by the {@link Router} in order to navigate between routes. Since all interactions between {@link + * Router} / + * {@link Location} / {@link LocationStrategy} and DOM APIs flow through the `PlatformLocation` + * class, they are all platform-agnostic. + * + * @publicApi + */ + + class PlatformLocation { + historyGo(relativePosition) { + throw new Error("Not implemented"); + } + } + + PlatformLocation.ɵfac = function PlatformLocation_Factory(t) { + return new (t || PlatformLocation)(); + }; + + PlatformLocation.ɵprov = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__[ + "ɵɵdefineInjectable" + ]({ + token: PlatformLocation, + factory: function() { + return useBrowserPlatformLocation(); + }, + providedIn: "platform" + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + PlatformLocation, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, + args: [ + { + providedIn: "platform", + // See #23917 + useFactory: useBrowserPlatformLocation + } + ] + } + ], + null, + null + ); + })(); + + function useBrowserPlatformLocation() { + return (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(BrowserPlatformLocation); + } + /** + * @description + * Indicates when a location is initialized. + * + * @publicApi + */ + + const LOCATION_INITIALIZED = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken( + "Location Initialized" + ); + /** + * `PlatformLocation` encapsulates all of the direct calls to platform APIs. + * This class should not be used directly by an application developer. Instead, use + * {@link Location}. + */ + + class BrowserPlatformLocation extends PlatformLocation { + constructor(_doc) { + super(); + this._doc = _doc; + + this._init(); + } // This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it + + /** @internal */ + + _init() { + this.location = window.location; + this._history = window.history; + } + + getBaseHrefFromDOM() { + return getDOM().getBaseHref(this._doc); + } + + onPopState(fn) { + const window = getDOM().getGlobalEventTarget(this._doc, "window"); + window.addEventListener("popstate", fn, false); + return () => window.removeEventListener("popstate", fn); + } + + onHashChange(fn) { + const window = getDOM().getGlobalEventTarget(this._doc, "window"); + window.addEventListener("hashchange", fn, false); + return () => window.removeEventListener("hashchange", fn); + } + + get href() { + return this.location.href; + } + + get protocol() { + return this.location.protocol; + } + + get hostname() { + return this.location.hostname; + } + + get port() { + return this.location.port; + } + + get pathname() { + return this.location.pathname; + } + + get search() { + return this.location.search; + } + + get hash() { + return this.location.hash; + } + + set pathname(newPath) { + this.location.pathname = newPath; + } + + pushState(state, title, url) { + if (supportsState()) { + this._history.pushState(state, title, url); + } else { + this.location.hash = url; + } + } + + replaceState(state, title, url) { + if (supportsState()) { + this._history.replaceState(state, title, url); + } else { + this.location.hash = url; + } + } + + forward() { + this._history.forward(); + } + + back() { + this._history.back(); + } + + historyGo(relativePosition = 0) { + this._history.go(relativePosition); + } + + getState() { + return this._history.state; + } + } + + BrowserPlatformLocation.ɵfac = function BrowserPlatformLocation_Factory(t) { + return new (t || BrowserPlatformLocation)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](DOCUMENT) + ); + }; + + BrowserPlatformLocation.ɵprov = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__[ + "ɵɵdefineInjectable" + ]({ + token: BrowserPlatformLocation, + factory: function() { + return createBrowserPlatformLocation(); + }, + providedIn: "platform" + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + BrowserPlatformLocation, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, + args: [ + { + providedIn: "platform", + // See #23917 + useFactory: createBrowserPlatformLocation + } + ] + } + ], + function() { + return [ + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, + args: [DOCUMENT] + } + ] + } + ]; + }, + null + ); + })(); + + function supportsState() { + return !!window.history.pushState; + } + + function createBrowserPlatformLocation() { + return new BrowserPlatformLocation( + (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(DOCUMENT) + ); + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Joins two parts of a URL with a slash if needed. + * + * @param start URL string + * @param end URL string + * + * + * @returns The joined URL string. + */ + + function joinWithSlash(start, end) { + if (start.length == 0) { + return end; + } + + if (end.length == 0) { + return start; + } + + let slashes = 0; + + if (start.endsWith("/")) { + slashes++; + } + + if (end.startsWith("/")) { + slashes++; + } + + if (slashes == 2) { + return start + end.substring(1); + } + + if (slashes == 1) { + return start + end; + } + + return start + "/" + end; + } + /** + * Removes a trailing slash from a URL string if needed. + * Looks for the first occurrence of either `#`, `?`, or the end of the + * line as `/` characters and removes the trailing slash if one exists. + * + * @param url URL string. + * + * @returns The URL string, modified if needed. + */ + + function stripTrailingSlash(url) { + const match = url.match(/#|\?|$/); + const pathEndIdx = (match && match.index) || url.length; + const droppedSlashIdx = pathEndIdx - (url[pathEndIdx - 1] === "/" ? 1 : 0); + return url.slice(0, droppedSlashIdx) + url.slice(pathEndIdx); + } + /** + * Normalizes URL parameters by prepending with `?` if needed. + * + * @param params String of URL parameters. + * + * @returns The normalized URL parameters string. + */ + + function normalizeQueryParams(params) { + return params && params[0] !== "?" ? "?" + params : params; + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Enables the `Location` service to read route state from the browser's URL. + * Angular provides two strategies: + * `HashLocationStrategy` and `PathLocationStrategy`. + * + * Applications should use the `Router` or `Location` services to + * interact with application route state. + * + * For instance, `HashLocationStrategy` produces URLs like + * http://example.com#/foo, + * and `PathLocationStrategy` produces + * http://example.com/foo as an equivalent URL. + * + * See these two classes for more. + * + * @publicApi + */ + + class LocationStrategy { + historyGo(relativePosition) { + throw new Error("Not implemented"); + } + } + + LocationStrategy.ɵfac = function LocationStrategy_Factory(t) { + return new (t || LocationStrategy)(); + }; + + LocationStrategy.ɵprov = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__[ + "ɵɵdefineInjectable" + ]({ + token: LocationStrategy, + factory: function() { + return provideLocationStrategy(); + }, + providedIn: "root" + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + LocationStrategy, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, + args: [ + { + providedIn: "root", + useFactory: provideLocationStrategy + } + ] + } + ], + null, + null + ); + })(); + + function provideLocationStrategy(platformLocation) { + // See #23917 + const location = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(DOCUMENT).location; + return new PathLocationStrategy( + (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(PlatformLocation), + (location && location.origin) || "" + ); + } + /** + * A predefined [DI token](guide/glossary#di-token) for the base href + * to be used with the `PathLocationStrategy`. + * The base href is the URL prefix that should be preserved when generating + * and recognizing URLs. + * + * @usageNotes + * + * The following example shows how to use this token to configure the root app injector + * with a base href value, so that the DI framework can supply the dependency anywhere in the app. + * + * ```typescript + * import {Component, NgModule} from '@angular/core'; + * import {APP_BASE_HREF} from '@angular/common'; + * + * @NgModule({ + * providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}] + * }) + * class AppModule {} + * ``` + * + * @publicApi + */ + + const APP_BASE_HREF = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken("appBaseHref"); + /** + * @description + * A {@link LocationStrategy} used to configure the {@link Location} service to + * represent its state in the + * [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the + * browser's URL. + * + * If you're using `PathLocationStrategy`, you must provide a {@link APP_BASE_HREF} + * or add a `` element to the document. + * + * For instance, if you provide an `APP_BASE_HREF` of `'/my/app/'` and call + * `location.go('/foo')`, the browser's URL will become + * `example.com/my/app/foo`. To ensure all relative URIs resolve correctly, + * the `` and/or `APP_BASE_HREF` should end with a `/`. + * + * Similarly, if you add `` to the document and call + * `location.go('/foo')`, the browser's URL will become + * `example.com/my/app/foo`. + * + * Note that when using `PathLocationStrategy`, neither the query nor + * the fragment in the `` will be preserved, as outlined + * by the [RFC](https://tools.ietf.org/html/rfc3986#section-5.2.2). + * + * @usageNotes + * + * ### Example + * + * {@example common/location/ts/path_location_component.ts region='LocationComponent'} + * + * @publicApi + */ + + class PathLocationStrategy extends LocationStrategy { + constructor(_platformLocation, href) { + super(); + this._platformLocation = _platformLocation; + this._removeListenerFns = []; + + if (href == null) { + href = this._platformLocation.getBaseHrefFromDOM(); + } + + if (href == null) { + throw new Error( + `No base href set. Please provide a value for the APP_BASE_HREF token or add a base element to the document.` + ); + } + + this._baseHref = href; + } + /** @nodoc */ + + ngOnDestroy() { + while (this._removeListenerFns.length) { + this._removeListenerFns.pop()(); + } + } + + onPopState(fn) { + this._removeListenerFns.push( + this._platformLocation.onPopState(fn), + this._platformLocation.onHashChange(fn) + ); + } + + getBaseHref() { + return this._baseHref; + } + + prepareExternalUrl(internal) { + return joinWithSlash(this._baseHref, internal); + } + + path(includeHash = false) { + const pathname = + this._platformLocation.pathname + normalizeQueryParams(this._platformLocation.search); + const hash = this._platformLocation.hash; + return hash && includeHash ? `${pathname}${hash}` : pathname; + } + + pushState(state, title, url, queryParams) { + const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams)); + + this._platformLocation.pushState(state, title, externalUrl); + } + + replaceState(state, title, url, queryParams) { + const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams)); + + this._platformLocation.replaceState(state, title, externalUrl); + } + + forward() { + this._platformLocation.forward(); + } + + back() { + this._platformLocation.back(); + } + + historyGo(relativePosition = 0) { + var _a, _b; + + (_b = (_a = this._platformLocation).historyGo) === null || _b === void 0 + ? void 0 + : _b.call(_a, relativePosition); + } + } + + PathLocationStrategy.ɵfac = function PathLocationStrategy_Factory(t) { + return new (t || PathLocationStrategy)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](PlatformLocation), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](APP_BASE_HREF, 8) + ); + }; + + PathLocationStrategy.ɵprov = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__[ + "ɵɵdefineInjectable" + ]({ + token: PathLocationStrategy, + factory: PathLocationStrategy.ɵfac + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + PathLocationStrategy, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable + } + ], + function() { + return [ + { + type: PlatformLocation + }, + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, + args: [APP_BASE_HREF] + } + ] + } + ]; + }, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @description + * A {@link LocationStrategy} used to configure the {@link Location} service to + * represent its state in the + * [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) + * of the browser's URL. + * + * For instance, if you call `location.go('/foo')`, the browser's URL will become + * `example.com#/foo`. + * + * @usageNotes + * + * ### Example + * + * {@example common/location/ts/hash_location_component.ts region='LocationComponent'} + * + * @publicApi + */ + + class HashLocationStrategy extends LocationStrategy { + constructor(_platformLocation, _baseHref) { + super(); + this._platformLocation = _platformLocation; + this._baseHref = ""; + this._removeListenerFns = []; + + if (_baseHref != null) { + this._baseHref = _baseHref; + } + } + /** @nodoc */ + + ngOnDestroy() { + while (this._removeListenerFns.length) { + this._removeListenerFns.pop()(); + } + } + + onPopState(fn) { + this._removeListenerFns.push( + this._platformLocation.onPopState(fn), + this._platformLocation.onHashChange(fn) + ); + } + + getBaseHref() { + return this._baseHref; + } + + path(includeHash = false) { + // the hash value is always prefixed with a `#` + // and if it is empty then it will stay empty + let path = this._platformLocation.hash; + if (path == null) path = "#"; + return path.length > 0 ? path.substring(1) : path; + } + + prepareExternalUrl(internal) { + const url = joinWithSlash(this._baseHref, internal); + return url.length > 0 ? "#" + url : url; + } + + pushState(state, title, path, queryParams) { + let url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams)); + + if (url.length == 0) { + url = this._platformLocation.pathname; + } + + this._platformLocation.pushState(state, title, url); + } + + replaceState(state, title, path, queryParams) { + let url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams)); + + if (url.length == 0) { + url = this._platformLocation.pathname; + } + + this._platformLocation.replaceState(state, title, url); + } + + forward() { + this._platformLocation.forward(); + } + + back() { + this._platformLocation.back(); + } + + historyGo(relativePosition = 0) { + var _a, _b; + + (_b = (_a = this._platformLocation).historyGo) === null || _b === void 0 + ? void 0 + : _b.call(_a, relativePosition); + } + } + + HashLocationStrategy.ɵfac = function HashLocationStrategy_Factory(t) { + return new (t || HashLocationStrategy)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](PlatformLocation), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](APP_BASE_HREF, 8) + ); + }; + + HashLocationStrategy.ɵprov = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__[ + "ɵɵdefineInjectable" + ]({ + token: HashLocationStrategy, + factory: HashLocationStrategy.ɵfac + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + HashLocationStrategy, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable + } + ], + function() { + return [ + { + type: PlatformLocation + }, + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, + args: [APP_BASE_HREF] + } + ] + } + ]; + }, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @description + * + * A service that applications can use to interact with a browser's URL. + * + * Depending on the `LocationStrategy` used, `Location` persists + * to the URL's path or the URL's hash segment. + * + * @usageNotes + * + * It's better to use the `Router.navigate()` service to trigger route changes. Use + * `Location` only if you need to interact with or create normalized URLs outside of + * routing. + * + * `Location` is responsible for normalizing the URL against the application's base href. + * A normalized URL is absolute from the URL host, includes the application's base href, and has no + * trailing slash: + * - `/my/app/user/123` is normalized + * - `my/app/user/123` **is not** normalized + * - `/my/app/user/123/` **is not** normalized + * + * ### Example + * + * + * + * @publicApi + */ + + class Location { + constructor(platformStrategy, platformLocation) { + /** @internal */ + this._subject = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); + /** @internal */ + + this._urlChangeListeners = []; + this._platformStrategy = platformStrategy; + + const browserBaseHref = this._platformStrategy.getBaseHref(); + + this._platformLocation = platformLocation; + this._baseHref = stripTrailingSlash(_stripIndexHtml(browserBaseHref)); + + this._platformStrategy.onPopState(ev => { + this._subject.emit({ + url: this.path(true), + pop: true, + state: ev.state, + type: ev.type + }); + }); + } + /** + * Normalizes the URL path for this location. + * + * @param includeHash True to include an anchor fragment in the path. + * + * @returns The normalized URL path. + */ + // TODO: vsavkin. Remove the boolean flag and always include hash once the deprecated router is + // removed. + + path(includeHash = false) { + return this.normalize(this._platformStrategy.path(includeHash)); + } + /** + * Reports the current state of the location history. + * @returns The current value of the `history.state` object. + */ + + getState() { + return this._platformLocation.getState(); + } + /** + * Normalizes the given path and compares to the current normalized path. + * + * @param path The given URL path. + * @param query Query parameters. + * + * @returns True if the given URL path is equal to the current normalized path, false + * otherwise. + */ + + isCurrentPathEqualTo(path, query = "") { + return this.path() == this.normalize(path + normalizeQueryParams(query)); + } + /** + * Normalizes a URL path by stripping any trailing slashes. + * + * @param url String representing a URL. + * + * @returns The normalized URL string. + */ + + normalize(url) { + return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url))); + } + /** + * Normalizes an external URL path. + * If the given URL doesn't begin with a leading slash (`'/'`), adds one + * before normalizing. Adds a hash if `HashLocationStrategy` is + * in use, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use. + * + * @param url String representing a URL. + * + * @returns A normalized platform-specific URL. + */ + + prepareExternalUrl(url) { + if (url && url[0] !== "/") { + url = "/" + url; + } + + return this._platformStrategy.prepareExternalUrl(url); + } // TODO: rename this method to pushState + + /** + * Changes the browser's URL to a normalized version of a given URL, and pushes a + * new item onto the platform's history. + * + * @param path URL path to normalize. + * @param query Query parameters. + * @param state Location history state. + * + */ + + go(path, query = "", state = null) { + this._platformStrategy.pushState(state, "", path, query); + + this._notifyUrlChangeListeners( + this.prepareExternalUrl(path + normalizeQueryParams(query)), + state + ); + } + /** + * Changes the browser's URL to a normalized version of the given URL, and replaces + * the top item on the platform's history stack. + * + * @param path URL path to normalize. + * @param query Query parameters. + * @param state Location history state. + */ + + replaceState(path, query = "", state = null) { + this._platformStrategy.replaceState(state, "", path, query); + + this._notifyUrlChangeListeners( + this.prepareExternalUrl(path + normalizeQueryParams(query)), + state + ); + } + /** + * Navigates forward in the platform's history. + */ + + forward() { + this._platformStrategy.forward(); + } + /** + * Navigates back in the platform's history. + */ + + back() { + this._platformStrategy.back(); + } + /** + * Navigate to a specific page from session history, identified by its relative position to the + * current page. + * + * @param relativePosition Position of the target page in the history relative to the current + * page. + * A negative value moves backwards, a positive value moves forwards, e.g. `location.historyGo(2)` + * moves forward two pages and `location.historyGo(-2)` moves back two pages. When we try to go + * beyond what's stored in the history session, we stay in the current page. Same behaviour occurs + * when `relativePosition` equals 0. + * @see https://developer.mozilla.org/en-US/docs/Web/API/History_API#Moving_to_a_specific_point_in_history + */ + + historyGo(relativePosition = 0) { + var _a, _b; + + (_b = (_a = this._platformStrategy).historyGo) === null || _b === void 0 + ? void 0 + : _b.call(_a, relativePosition); + } + /** + * Registers a URL change listener. Use to catch updates performed by the Angular + * framework that are not detectible through "popstate" or "hashchange" events. + * + * @param fn The change handler function, which take a URL and a location history state. + */ + + onUrlChange(fn) { + this._urlChangeListeners.push(fn); + + if (!this._urlChangeSubscription) { + this._urlChangeSubscription = this.subscribe(v => { + this._notifyUrlChangeListeners(v.url, v.state); + }); + } + } + /** @internal */ + + _notifyUrlChangeListeners(url = "", state) { + this._urlChangeListeners.forEach(fn => fn(url, state)); + } + /** + * Subscribes to the platform's `popState` events. + * + * Note: `Location.go()` does not trigger the `popState` event in the browser. Use + * `Location.onUrlChange()` to subscribe to URL changes instead. + * + * @param value Event that is triggered when the state history changes. + * @param exception The exception to throw. + * + * @see [onpopstate](https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate) + * + * @returns Subscribed events. + */ + + subscribe(onNext, onThrow, onReturn) { + return this._subject.subscribe({ + next: onNext, + error: onThrow, + complete: onReturn + }); + } + } + /** + * Normalizes URL parameters by prepending with `?` if needed. + * + * @param params String of URL parameters. + * + * @returns The normalized URL parameters string. + */ + + Location.normalizeQueryParams = normalizeQueryParams; + /** + * Joins two parts of a URL with a slash if needed. + * + * @param start URL string + * @param end URL string + * + * + * @returns The joined URL string. + */ + + Location.joinWithSlash = joinWithSlash; + /** + * Removes a trailing slash from a URL string if needed. + * Looks for the first occurrence of either `#`, `?`, or the end of the + * line as `/` characters and removes the trailing slash if one exists. + * + * @param url URL string. + * + * @returns The URL string, modified if needed. + */ + + Location.stripTrailingSlash = stripTrailingSlash; + + Location.ɵfac = function Location_Factory(t) { + return new (t || Location)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](LocationStrategy), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](PlatformLocation) + ); + }; + + Location.ɵprov = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ + token: Location, + factory: function() { + return createLocation(); + }, + providedIn: "root" + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + Location, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, + args: [ + { + providedIn: "root", + // See #23917 + useFactory: createLocation + } + ] + } + ], + function() { + return [ + { + type: LocationStrategy + }, + { + type: PlatformLocation + } + ]; + }, + null + ); + })(); + + function createLocation() { + return new Location( + (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(LocationStrategy), + (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(PlatformLocation) + ); + } + + function _stripBaseHref(baseHref, url) { + return baseHref && url.startsWith(baseHref) ? url.substring(baseHref.length) : url; + } + + function _stripIndexHtml(url) { + return url.replace(/\/index.html$/, ""); + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** @internal */ + + const CURRENCIES_EN = { + ADP: [undefined, undefined, 0], + AFN: [undefined, "؋", 0], + ALL: [undefined, undefined, 0], + AMD: [undefined, "֏", 2], + AOA: [undefined, "Kz"], + ARS: [undefined, "$"], + AUD: ["A$", "$"], + AZN: [undefined, "₼"], + BAM: [undefined, "KM"], + BBD: [undefined, "$"], + BDT: [undefined, "৳"], + BHD: [undefined, undefined, 3], + BIF: [undefined, undefined, 0], + BMD: [undefined, "$"], + BND: [undefined, "$"], + BOB: [undefined, "Bs"], + BRL: ["R$"], + BSD: [undefined, "$"], + BWP: [undefined, "P"], + BYN: [undefined, "р.", 2], + BYR: [undefined, undefined, 0], + BZD: [undefined, "$"], + CAD: ["CA$", "$", 2], + CHF: [undefined, undefined, 2], + CLF: [undefined, undefined, 4], + CLP: [undefined, "$", 0], + CNY: ["CN¥", "¥"], + COP: [undefined, "$", 2], + CRC: [undefined, "₡", 2], + CUC: [undefined, "$"], + CUP: [undefined, "$"], + CZK: [undefined, "Kč", 2], + DJF: [undefined, undefined, 0], + DKK: [undefined, "kr", 2], + DOP: [undefined, "$"], + EGP: [undefined, "E£"], + ESP: [undefined, "₧", 0], + EUR: ["€"], + FJD: [undefined, "$"], + FKP: [undefined, "£"], + GBP: ["£"], + GEL: [undefined, "₾"], + GHS: [undefined, "GH₵"], + GIP: [undefined, "£"], + GNF: [undefined, "FG", 0], + GTQ: [undefined, "Q"], + GYD: [undefined, "$", 2], + HKD: ["HK$", "$"], + HNL: [undefined, "L"], + HRK: [undefined, "kn"], + HUF: [undefined, "Ft", 2], + IDR: [undefined, "Rp", 2], + ILS: ["₪"], + INR: ["₹"], + IQD: [undefined, undefined, 0], + IRR: [undefined, undefined, 0], + ISK: [undefined, "kr", 0], + ITL: [undefined, undefined, 0], + JMD: [undefined, "$"], + JOD: [undefined, undefined, 3], + JPY: ["¥", undefined, 0], + KHR: [undefined, "៛"], + KMF: [undefined, "CF", 0], + KPW: [undefined, "₩", 0], + KRW: ["₩", undefined, 0], + KWD: [undefined, undefined, 3], + KYD: [undefined, "$"], + KZT: [undefined, "₸"], + LAK: [undefined, "₭", 0], + LBP: [undefined, "L£", 0], + LKR: [undefined, "Rs"], + LRD: [undefined, "$"], + LTL: [undefined, "Lt"], + LUF: [undefined, undefined, 0], + LVL: [undefined, "Ls"], + LYD: [undefined, undefined, 3], + MGA: [undefined, "Ar", 0], + MGF: [undefined, undefined, 0], + MMK: [undefined, "K", 0], + MNT: [undefined, "₮", 2], + MRO: [undefined, undefined, 0], + MUR: [undefined, "Rs", 2], + MXN: ["MX$", "$"], + MYR: [undefined, "RM"], + NAD: [undefined, "$"], + NGN: [undefined, "₦"], + NIO: [undefined, "C$"], + NOK: [undefined, "kr", 2], + NPR: [undefined, "Rs"], + NZD: ["NZ$", "$"], + OMR: [undefined, undefined, 3], + PHP: ["₱"], + PKR: [undefined, "Rs", 2], + PLN: [undefined, "zł"], + PYG: [undefined, "₲", 0], + RON: [undefined, "lei"], + RSD: [undefined, undefined, 0], + RUB: [undefined, "₽"], + RUR: [undefined, "р."], + RWF: [undefined, "RF", 0], + SBD: [undefined, "$"], + SEK: [undefined, "kr", 2], + SGD: [undefined, "$"], + SHP: [undefined, "£"], + SLL: [undefined, undefined, 0], + SOS: [undefined, undefined, 0], + SRD: [undefined, "$"], + SSP: [undefined, "£"], + STD: [undefined, undefined, 0], + STN: [undefined, "Db"], + SYP: [undefined, "£", 0], + THB: [undefined, "฿"], + TMM: [undefined, undefined, 0], + TND: [undefined, undefined, 3], + TOP: [undefined, "T$"], + TRL: [undefined, undefined, 0], + TRY: [undefined, "₺"], + TTD: [undefined, "$"], + TWD: ["NT$", "$", 2], + TZS: [undefined, undefined, 2], + UAH: [undefined, "₴"], + UGX: [undefined, undefined, 0], + USD: ["$"], + UYI: [undefined, undefined, 0], + UYU: [undefined, "$"], + UYW: [undefined, undefined, 4], + UZS: [undefined, undefined, 2], + VEF: [undefined, "Bs", 2], + VND: ["₫", undefined, 0], + VUV: [undefined, undefined, 0], + XAF: ["FCFA", undefined, 0], + XCD: ["EC$", "$"], + XOF: ["F CFA", undefined, 0], + XPF: ["CFPF", undefined, 0], + XXX: ["¤"], + YER: [undefined, undefined, 0], + ZAR: [undefined, "R"], + ZMK: [undefined, undefined, 0], + ZMW: [undefined, "ZK"], + ZWD: [undefined, undefined, 0] + }; + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Format styles that can be used to represent numbers. + * @see `getLocaleNumberFormat()`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + var NumberFormatStyle; + + (function(NumberFormatStyle) { + NumberFormatStyle[(NumberFormatStyle["Decimal"] = 0)] = "Decimal"; + NumberFormatStyle[(NumberFormatStyle["Percent"] = 1)] = "Percent"; + NumberFormatStyle[(NumberFormatStyle["Currency"] = 2)] = "Currency"; + NumberFormatStyle[(NumberFormatStyle["Scientific"] = 3)] = "Scientific"; + })(NumberFormatStyle || (NumberFormatStyle = {})); + /** + * Plurality cases used for translating plurals to different languages. + * + * @see `NgPlural` + * @see `NgPluralCase` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + var Plural; + + (function(Plural) { + Plural[(Plural["Zero"] = 0)] = "Zero"; + Plural[(Plural["One"] = 1)] = "One"; + Plural[(Plural["Two"] = 2)] = "Two"; + Plural[(Plural["Few"] = 3)] = "Few"; + Plural[(Plural["Many"] = 4)] = "Many"; + Plural[(Plural["Other"] = 5)] = "Other"; + })(Plural || (Plural = {})); + /** + * Context-dependant translation forms for strings. + * Typically the standalone version is for the nominative form of the word, + * and the format version is used for the genitive case. + * @see [CLDR website](http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles) + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + var FormStyle; + + (function(FormStyle) { + FormStyle[(FormStyle["Format"] = 0)] = "Format"; + FormStyle[(FormStyle["Standalone"] = 1)] = "Standalone"; + })(FormStyle || (FormStyle = {})); + /** + * String widths available for translations. + * The specific character widths are locale-specific. + * Examples are given for the word "Sunday" in English. + * + * @publicApi + */ + + var TranslationWidth; + + (function(TranslationWidth) { + /** 1 character for `en-US`. For example: 'S' */ + TranslationWidth[(TranslationWidth["Narrow"] = 0)] = "Narrow"; + /** 3 characters for `en-US`. For example: 'Sun' */ + + TranslationWidth[(TranslationWidth["Abbreviated"] = 1)] = "Abbreviated"; + /** Full length for `en-US`. For example: "Sunday" */ + + TranslationWidth[(TranslationWidth["Wide"] = 2)] = "Wide"; + /** 2 characters for `en-US`, For example: "Su" */ + + TranslationWidth[(TranslationWidth["Short"] = 3)] = "Short"; + })(TranslationWidth || (TranslationWidth = {})); + /** + * String widths available for date-time formats. + * The specific character widths are locale-specific. + * Examples are given for `en-US`. + * + * @see `getLocaleDateFormat()` + * @see `getLocaleTimeFormat()` + * @see `getLocaleDateTimeFormat()` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * @publicApi + */ + + var FormatWidth; + + (function(FormatWidth) { + /** + * For `en-US`, 'M/d/yy, h:mm a'` + * (Example: `6/15/15, 9:03 AM`) + */ + FormatWidth[(FormatWidth["Short"] = 0)] = "Short"; + /** + * For `en-US`, `'MMM d, y, h:mm:ss a'` + * (Example: `Jun 15, 2015, 9:03:01 AM`) + */ + + FormatWidth[(FormatWidth["Medium"] = 1)] = "Medium"; + /** + * For `en-US`, `'MMMM d, y, h:mm:ss a z'` + * (Example: `June 15, 2015 at 9:03:01 AM GMT+1`) + */ + + FormatWidth[(FormatWidth["Long"] = 2)] = "Long"; + /** + * For `en-US`, `'EEEE, MMMM d, y, h:mm:ss a zzzz'` + * (Example: `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00`) + */ + + FormatWidth[(FormatWidth["Full"] = 3)] = "Full"; + })(FormatWidth || (FormatWidth = {})); + /** + * Symbols that can be used to replace placeholders in number patterns. + * Examples are based on `en-US` values. + * + * @see `getLocaleNumberSymbol()` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + var NumberSymbol; + + (function(NumberSymbol) { + /** + * Decimal separator. + * For `en-US`, the dot character. + * Example: 2,345`.`67 + */ + NumberSymbol[(NumberSymbol["Decimal"] = 0)] = "Decimal"; + /** + * Grouping separator, typically for thousands. + * For `en-US`, the comma character. + * Example: 2`,`345.67 + */ + + NumberSymbol[(NumberSymbol["Group"] = 1)] = "Group"; + /** + * List-item separator. + * Example: "one, two, and three" + */ + + NumberSymbol[(NumberSymbol["List"] = 2)] = "List"; + /** + * Sign for percentage (out of 100). + * Example: 23.4% + */ + + NumberSymbol[(NumberSymbol["PercentSign"] = 3)] = "PercentSign"; + /** + * Sign for positive numbers. + * Example: +23 + */ + + NumberSymbol[(NumberSymbol["PlusSign"] = 4)] = "PlusSign"; + /** + * Sign for negative numbers. + * Example: -23 + */ + + NumberSymbol[(NumberSymbol["MinusSign"] = 5)] = "MinusSign"; + /** + * Computer notation for exponential value (n times a power of 10). + * Example: 1.2E3 + */ + + NumberSymbol[(NumberSymbol["Exponential"] = 6)] = "Exponential"; + /** + * Human-readable format of exponential. + * Example: 1.2x103 + */ + + NumberSymbol[(NumberSymbol["SuperscriptingExponent"] = 7)] = "SuperscriptingExponent"; + /** + * Sign for permille (out of 1000). + * Example: 23.4‰ + */ + + NumberSymbol[(NumberSymbol["PerMille"] = 8)] = "PerMille"; + /** + * Infinity, can be used with plus and minus. + * Example: ∞, +∞, -∞ + */ + + NumberSymbol[(NumberSymbol["Infinity"] = 9)] = "Infinity"; + /** + * Not a number. + * Example: NaN + */ + + NumberSymbol[(NumberSymbol["NaN"] = 10)] = "NaN"; + /** + * Symbol used between time units. + * Example: 10:52 + */ + + NumberSymbol[(NumberSymbol["TimeSeparator"] = 11)] = "TimeSeparator"; + /** + * Decimal separator for currency values (fallback to `Decimal`). + * Example: $2,345.67 + */ + + NumberSymbol[(NumberSymbol["CurrencyDecimal"] = 12)] = "CurrencyDecimal"; + /** + * Group separator for currency values (fallback to `Group`). + * Example: $2,345.67 + */ + + NumberSymbol[(NumberSymbol["CurrencyGroup"] = 13)] = "CurrencyGroup"; + })(NumberSymbol || (NumberSymbol = {})); + /** + * The value for each day of the week, based on the `en-US` locale + * + * @publicApi + */ + + var WeekDay; + + (function(WeekDay) { + WeekDay[(WeekDay["Sunday"] = 0)] = "Sunday"; + WeekDay[(WeekDay["Monday"] = 1)] = "Monday"; + WeekDay[(WeekDay["Tuesday"] = 2)] = "Tuesday"; + WeekDay[(WeekDay["Wednesday"] = 3)] = "Wednesday"; + WeekDay[(WeekDay["Thursday"] = 4)] = "Thursday"; + WeekDay[(WeekDay["Friday"] = 5)] = "Friday"; + WeekDay[(WeekDay["Saturday"] = 6)] = "Saturday"; + })(WeekDay || (WeekDay = {})); + /** + * Retrieves the locale ID from the currently loaded locale. + * The loaded locale could be, for example, a global one rather than a regional one. + * @param locale A locale code, such as `fr-FR`. + * @returns The locale code. For example, `fr`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleId(locale) { + return (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale)[ + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].LocaleId + ]; + } + /** + * Retrieves day period strings for the given locale. + * + * @param locale A locale code for the locale format rules to use. + * @param formStyle The required grammatical form. + * @param width The required character width. + * @returns An array of localized period strings. For example, `[AM, PM]` for `en-US`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleDayPeriods(locale, formStyle, width) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + const amPmData = [ + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DayPeriodsFormat], + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DayPeriodsStandalone] + ]; + const amPm = getLastDefinedValue(amPmData, formStyle); + return getLastDefinedValue(amPm, width); + } + /** + * Retrieves days of the week for the given locale, using the Gregorian calendar. + * + * @param locale A locale code for the locale format rules to use. + * @param formStyle The required grammatical form. + * @param width The required character width. + * @returns An array of localized name strings. + * For example,`[Sunday, Monday, ... Saturday]` for `en-US`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleDayNames(locale, formStyle, width) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + const daysData = [ + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DaysFormat], + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DaysStandalone] + ]; + const days = getLastDefinedValue(daysData, formStyle); + return getLastDefinedValue(days, width); + } + /** + * Retrieves months of the year for the given locale, using the Gregorian calendar. + * + * @param locale A locale code for the locale format rules to use. + * @param formStyle The required grammatical form. + * @param width The required character width. + * @returns An array of localized name strings. + * For example, `[January, February, ...]` for `en-US`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleMonthNames(locale, formStyle, width) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + const monthsData = [ + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].MonthsFormat], + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].MonthsStandalone] + ]; + const months = getLastDefinedValue(monthsData, formStyle); + return getLastDefinedValue(months, width); + } + /** + * Retrieves Gregorian-calendar eras for the given locale. + * @param locale A locale code for the locale format rules to use. + * @param width The required character width. + + * @returns An array of localized era strings. + * For example, `[AD, BC]` for `en-US`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleEraNames(locale, width) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + const erasData = data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].Eras]; + return getLastDefinedValue(erasData, width); + } + /** + * Retrieves the first day of the week for the given locale. + * + * @param locale A locale code for the locale format rules to use. + * @returns A day index number, using the 0-based week-day index for `en-US` + * (Sunday = 0, Monday = 1, ...). + * For example, for `fr-FR`, returns 1 to indicate that the first day is Monday. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleFirstDayOfWeek(locale) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].FirstDayOfWeek]; + } + /** + * Range of week days that are considered the week-end for the given locale. + * + * @param locale A locale code for the locale format rules to use. + * @returns The range of day values, `[startDay, endDay]`. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleWeekEndRange(locale) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].WeekendRange]; + } + /** + * Retrieves a localized date-value formating string. + * + * @param locale A locale code for the locale format rules to use. + * @param width The format type. + * @returns The localized formating string. + * @see `FormatWidth` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleDateFormat(locale, width) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + return getLastDefinedValue( + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DateFormat], + width + ); + } + /** + * Retrieves a localized time-value formatting string. + * + * @param locale A locale code for the locale format rules to use. + * @param width The format type. + * @returns The localized formatting string. + * @see `FormatWidth` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + + * @publicApi + */ + + function getLocaleTimeFormat(locale, width) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + return getLastDefinedValue( + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].TimeFormat], + width + ); + } + /** + * Retrieves a localized date-time formatting string. + * + * @param locale A locale code for the locale format rules to use. + * @param width The format type. + * @returns The localized formatting string. + * @see `FormatWidth` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleDateTimeFormat(locale, width) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + const dateTimeFormatData = + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DateTimeFormat]; + return getLastDefinedValue(dateTimeFormatData, width); + } + /** + * Retrieves a localized number symbol that can be used to replace placeholders in number formats. + * @param locale The locale code. + * @param symbol The symbol to localize. + * @returns The character for the localized symbol. + * @see `NumberSymbol` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleNumberSymbol(locale, symbol) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + const res = + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].NumberSymbols][symbol]; + + if (typeof res === "undefined") { + if (symbol === NumberSymbol.CurrencyDecimal) { + return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].NumberSymbols][ + NumberSymbol.Decimal + ]; + } else if (symbol === NumberSymbol.CurrencyGroup) { + return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].NumberSymbols][ + NumberSymbol.Group + ]; + } + } + + return res; + } + /** + * Retrieves a number format for a given locale. + * + * Numbers are formatted using patterns, like `#,###.00`. For example, the pattern `#,###.00` + * when used to format the number 12345.678 could result in "12'345,678". That would happen if the + * grouping separator for your language is an apostrophe, and the decimal separator is a comma. + * + * Important: The characters `.` `,` `0` `#` (and others below) are special placeholders + * that stand for the decimal separator, and so on, and are NOT real characters. + * You must NOT "translate" the placeholders. For example, don't change `.` to `,` even though in + * your language the decimal point is written with a comma. The symbols should be replaced by the + * local equivalents, using the appropriate `NumberSymbol` for your language. + * + * Here are the special characters used in number patterns: + * + * | Symbol | Meaning | + * |--------|---------| + * | . | Replaced automatically by the character used for the decimal point. | + * | , | Replaced by the "grouping" (thousands) separator. | + * | 0 | Replaced by a digit (or zero if there aren't enough digits). | + * | # | Replaced by a digit (or nothing if there aren't enough). | + * | ¤ | Replaced by a currency symbol, such as $ or USD. | + * | % | Marks a percent format. The % symbol may change position, but must be retained. | + * | E | Marks a scientific format. The E symbol may change position, but must be retained. | + * | ' | Special characters used as literal characters are quoted with ASCII single quotes. | + * + * @param locale A locale code for the locale format rules to use. + * @param type The type of numeric value to be formatted (such as `Decimal` or `Currency`.) + * @returns The localized format string. + * @see `NumberFormatStyle` + * @see [CLDR website](http://cldr.unicode.org/translation/number-patterns) + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleNumberFormat(locale, type) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].NumberFormats][type]; + } + /** + * Retrieves the symbol used to represent the currency for the main country + * corresponding to a given locale. For example, '$' for `en-US`. + * + * @param locale A locale code for the locale format rules to use. + * @returns The localized symbol character, + * or `null` if the main country cannot be determined. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleCurrencySymbol(locale) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].CurrencySymbol] || null; + } + /** + * Retrieves the name of the currency for the main country corresponding + * to a given locale. For example, 'US Dollar' for `en-US`. + * @param locale A locale code for the locale format rules to use. + * @returns The currency name, + * or `null` if the main country cannot be determined. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleCurrencyName(locale) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].CurrencyName] || null; + } + /** + * Retrieves the default currency code for the given locale. + * + * The default is defined as the first currency which is still in use. + * + * @param locale The code of the locale whose currency code we want. + * @returns The code of the default currency for the given locale. + * + * @publicApi + */ + + function getLocaleCurrencyCode(locale) { + return (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵgetLocaleCurrencyCode"])(locale); + } + /** + * Retrieves the currency values for a given locale. + * @param locale A locale code for the locale format rules to use. + * @returns The currency values. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + */ + + function getLocaleCurrencies(locale) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].Currencies]; + } + /** + * @alias core/ɵgetLocalePluralCase + * @publicApi + */ + + const getLocalePluralCase = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵgetLocalePluralCase"]; + + function checkFullData(data) { + if (!data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].ExtraData]) { + throw new Error( + `Missing extra locale data for the locale "${ + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].LocaleId] + }". Use "registerLocaleData" to load new data. See the "I18n guide" on angular.io to know more.` + ); + } + } + /** + * Retrieves locale-specific rules used to determine which day period to use + * when more than one period is defined for a locale. + * + * There is a rule for each defined day period. The + * first rule is applied to the first day period and so on. + * Fall back to AM/PM when no rules are available. + * + * A rule can specify a period as time range, or as a single time value. + * + * This functionality is only available when you have loaded the full locale data. + * See the ["I18n guide"](guide/i18n-common-format-data-locale). + * + * @param locale A locale code for the locale format rules to use. + * @returns The rules for the locale, a single time value or array of *from-time, to-time*, + * or null if no periods are available. + * + * @see `getLocaleExtraDayPeriods()` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleExtraDayPeriodRules(locale) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + checkFullData(data); + const rules = + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].ExtraData][2] || []; + /* ExtraDayPeriodsRules */ + return rules.map(rule => { + if (typeof rule === "string") { + return extractTime(rule); + } + + return [extractTime(rule[0]), extractTime(rule[1])]; + }); + } + /** + * Retrieves locale-specific day periods, which indicate roughly how a day is broken up + * in different languages. + * For example, for `en-US`, periods are morning, noon, afternoon, evening, and midnight. + * + * This functionality is only available when you have loaded the full locale data. + * See the ["I18n guide"](guide/i18n-common-format-data-locale). + * + * @param locale A locale code for the locale format rules to use. + * @param formStyle The required grammatical form. + * @param width The required character width. + * @returns The translated day-period strings. + * @see `getLocaleExtraDayPeriodRules()` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLocaleExtraDayPeriods(locale, formStyle, width) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + checkFullData(data); + const dayPeriodsData = [ + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].ExtraData][0], + /* ExtraDayPeriodFormats */ + data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].ExtraData][1] + /* ExtraDayPeriodStandalone */ + ]; + const dayPeriods = getLastDefinedValue(dayPeriodsData, formStyle) || []; + return getLastDefinedValue(dayPeriods, width) || []; + } + /** + * Retrieves the writing direction of a specified locale + * @param locale A locale code for the locale format rules to use. + * @publicApi + * @returns 'rtl' or 'ltr' + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + */ + + function getLocaleDirection(locale) { + const data = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); + return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].Directionality]; + } + /** + * Retrieves the first value that is defined in an array, going backwards from an index position. + * + * To avoid repeating the same data (as when the "format" and "standalone" forms are the same) + * add the first value to the locale data arrays, and add other values only if they are different. + * + * @param data The data array to retrieve from. + * @param index A 0-based index into the array to start from. + * @returns The value immediately before the given index position. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getLastDefinedValue(data, index) { + for (let i = index; i > -1; i--) { + if (typeof data[i] !== "undefined") { + return data[i]; + } + } + + throw new Error("Locale data API: locale data undefined"); + } + /** + * Extracts the hours and minutes from a string like "15:45" + */ + + function extractTime(time) { + const [h, m] = time.split(":"); + return { + hours: +h, + minutes: +m + }; + } + /** + * Retrieves the currency symbol for a given currency code. + * + * For example, for the default `en-US` locale, the code `USD` can + * be represented by the narrow symbol `$` or the wide symbol `US$`. + * + * @param code The currency code. + * @param format The format, `wide` or `narrow`. + * @param locale A locale code for the locale format rules to use. + * + * @returns The symbol, or the currency code if no symbol is available. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getCurrencySymbol(code, format, locale = "en") { + const currency = getLocaleCurrencies(locale)[code] || CURRENCIES_EN[code] || []; + const symbolNarrow = currency[1]; + /* SymbolNarrow */ + + if (format === "narrow" && typeof symbolNarrow === "string") { + return symbolNarrow; + } + + return ( + currency[0] || code + /* Symbol */ + ); + } // Most currencies have cents, that's why the default is 2 + + const DEFAULT_NB_OF_CURRENCY_DIGITS = 2; + /** + * Reports the number of decimal digits for a given currency. + * The value depends upon the presence of cents in that particular currency. + * + * @param code The currency code. + * @returns The number of decimal digits, typically 0 or 2. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function getNumberOfCurrencyDigits(code) { + let digits; + const currency = CURRENCIES_EN[code]; + + if (currency) { + digits = currency[2]; + /* NbOfDigits */ + } + + return typeof digits === "number" ? digits : DEFAULT_NB_OF_CURRENCY_DIGITS; + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + const ISO8601_DATE_REGEX = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; // 1 2 3 4 5 6 7 8 9 10 11 + + const NAMED_FORMATS = {}; + const DATE_FORMATS_SPLIT = /((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; + var ZoneWidth; + + (function(ZoneWidth) { + ZoneWidth[(ZoneWidth["Short"] = 0)] = "Short"; + ZoneWidth[(ZoneWidth["ShortGMT"] = 1)] = "ShortGMT"; + ZoneWidth[(ZoneWidth["Long"] = 2)] = "Long"; + ZoneWidth[(ZoneWidth["Extended"] = 3)] = "Extended"; + })(ZoneWidth || (ZoneWidth = {})); + + var DateType; + + (function(DateType) { + DateType[(DateType["FullYear"] = 0)] = "FullYear"; + DateType[(DateType["Month"] = 1)] = "Month"; + DateType[(DateType["Date"] = 2)] = "Date"; + DateType[(DateType["Hours"] = 3)] = "Hours"; + DateType[(DateType["Minutes"] = 4)] = "Minutes"; + DateType[(DateType["Seconds"] = 5)] = "Seconds"; + DateType[(DateType["FractionalSeconds"] = 6)] = "FractionalSeconds"; + DateType[(DateType["Day"] = 7)] = "Day"; + })(DateType || (DateType = {})); + + var TranslationType; + + (function(TranslationType) { + TranslationType[(TranslationType["DayPeriods"] = 0)] = "DayPeriods"; + TranslationType[(TranslationType["Days"] = 1)] = "Days"; + TranslationType[(TranslationType["Months"] = 2)] = "Months"; + TranslationType[(TranslationType["Eras"] = 3)] = "Eras"; + })(TranslationType || (TranslationType = {})); + /** + * @ngModule CommonModule + * @description + * + * Formats a date according to locale rules. + * + * @param value The date to format, as a Date, or a number (milliseconds since UTC epoch) + * or an [ISO date-time string](https://www.w3.org/TR/NOTE-datetime). + * @param format The date-time components to include. See `DatePipe` for details. + * @param locale A locale code for the locale format rules to use. + * @param timezone The time zone. A time zone offset from GMT (such as `'+0430'`), + * or a standard UTC/GMT or continental US time zone abbreviation. + * If not specified, uses host system settings. + * + * @returns The formatted date string. + * + * @see `DatePipe` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function formatDate(value, format, locale, timezone) { + let date = toDate(value); + const namedFormat = getNamedFormat(locale, format); + format = namedFormat || format; + let parts = []; + let match; + + while (format) { + match = DATE_FORMATS_SPLIT.exec(format); + + if (match) { + parts = parts.concat(match.slice(1)); + const part = parts.pop(); + + if (!part) { + break; + } + + format = part; + } else { + parts.push(format); + break; + } + } + + let dateTimezoneOffset = date.getTimezoneOffset(); + + if (timezone) { + dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + date = convertTimezoneToLocal(date, timezone, true); + } + + let text = ""; + parts.forEach(value => { + const dateFormatter = getDateFormatter(value); + text += dateFormatter + ? dateFormatter(date, locale, dateTimezoneOffset) + : value === "''" + ? "'" + : value.replace(/(^'|'$)/g, "").replace(/''/g, "'"); + }); + return text; + } + /** + * Create a new Date object with the given date value, and the time set to midnight. + * + * We cannot use `new Date(year, month, date)` because it maps years between 0 and 99 to 1900-1999. + * See: https://github.com/angular/angular/issues/40377 + * + * Note that this function returns a Date object whose time is midnight in the current locale's + * timezone. In the future we might want to change this to be midnight in UTC, but this would be a + * considerable breaking change. + */ + + function createDate(year, month, date) { + // The `newDate` is set to midnight (UTC) on January 1st 1970. + // - In PST this will be December 31st 1969 at 4pm. + // - In GMT this will be January 1st 1970 at 1am. + // Note that they even have different years, dates and months! + const newDate = new Date(0); // `setFullYear()` allows years like 0001 to be set correctly. This function does not + // change the internal time of the date. + // Consider calling `setFullYear(2019, 8, 20)` (September 20, 2019). + // - In PST this will now be September 20, 2019 at 4pm + // - In GMT this will now be September 20, 2019 at 1am + + newDate.setFullYear(year, month, date); // We want the final date to be at local midnight, so we reset the time. + // - In PST this will now be September 20, 2019 at 12am + // - In GMT this will now be September 20, 2019 at 12am + + newDate.setHours(0, 0, 0); + return newDate; + } + + function getNamedFormat(locale, format) { + const localeId = getLocaleId(locale); + NAMED_FORMATS[localeId] = NAMED_FORMATS[localeId] || {}; + + if (NAMED_FORMATS[localeId][format]) { + return NAMED_FORMATS[localeId][format]; + } + + let formatValue = ""; + + switch (format) { + case "shortDate": + formatValue = getLocaleDateFormat(locale, FormatWidth.Short); + break; + + case "mediumDate": + formatValue = getLocaleDateFormat(locale, FormatWidth.Medium); + break; + + case "longDate": + formatValue = getLocaleDateFormat(locale, FormatWidth.Long); + break; + + case "fullDate": + formatValue = getLocaleDateFormat(locale, FormatWidth.Full); + break; + + case "shortTime": + formatValue = getLocaleTimeFormat(locale, FormatWidth.Short); + break; + + case "mediumTime": + formatValue = getLocaleTimeFormat(locale, FormatWidth.Medium); + break; + + case "longTime": + formatValue = getLocaleTimeFormat(locale, FormatWidth.Long); + break; + + case "fullTime": + formatValue = getLocaleTimeFormat(locale, FormatWidth.Full); + break; + + case "short": + const shortTime = getNamedFormat(locale, "shortTime"); + const shortDate = getNamedFormat(locale, "shortDate"); + formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Short), [ + shortTime, + shortDate + ]); + break; + + case "medium": + const mediumTime = getNamedFormat(locale, "mediumTime"); + const mediumDate = getNamedFormat(locale, "mediumDate"); + formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Medium), [ + mediumTime, + mediumDate + ]); + break; + + case "long": + const longTime = getNamedFormat(locale, "longTime"); + const longDate = getNamedFormat(locale, "longDate"); + formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Long), [ + longTime, + longDate + ]); + break; + + case "full": + const fullTime = getNamedFormat(locale, "fullTime"); + const fullDate = getNamedFormat(locale, "fullDate"); + formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Full), [ + fullTime, + fullDate + ]); + break; + } + + if (formatValue) { + NAMED_FORMATS[localeId][format] = formatValue; + } + + return formatValue; + } + + function formatDateTime(str, opt_values) { + if (opt_values) { + str = str.replace(/\{([^}]+)}/g, function(match, key) { + return opt_values != null && key in opt_values ? opt_values[key] : match; + }); + } + + return str; + } + + function padNumber(num, digits, minusSign = "-", trim, negWrap) { + let neg = ""; + + if (num < 0 || (negWrap && num <= 0)) { + if (negWrap) { + num = -num + 1; + } else { + num = -num; + neg = minusSign; + } + } + + let strNum = String(num); + + while (strNum.length < digits) { + strNum = "0" + strNum; + } + + if (trim) { + strNum = strNum.substr(strNum.length - digits); + } + + return neg + strNum; + } + + function formatFractionalSeconds(milliseconds, digits) { + const strMs = padNumber(milliseconds, 3); + return strMs.substr(0, digits); + } + /** + * Returns a date formatter that transforms a date into its locale digit representation + */ + + function dateGetter(name, size, offset = 0, trim = false, negWrap = false) { + return function(date, locale) { + let part = getDatePart(name, date); + + if (offset > 0 || part > -offset) { + part += offset; + } + + if (name === DateType.Hours) { + if (part === 0 && offset === -12) { + part = 12; + } + } else if (name === DateType.FractionalSeconds) { + return formatFractionalSeconds(part, size); + } + + const localeMinus = getLocaleNumberSymbol(locale, NumberSymbol.MinusSign); + return padNumber(part, size, localeMinus, trim, negWrap); + }; + } + + function getDatePart(part, date) { + switch (part) { + case DateType.FullYear: + return date.getFullYear(); + + case DateType.Month: + return date.getMonth(); + + case DateType.Date: + return date.getDate(); + + case DateType.Hours: + return date.getHours(); + + case DateType.Minutes: + return date.getMinutes(); + + case DateType.Seconds: + return date.getSeconds(); + + case DateType.FractionalSeconds: + return date.getMilliseconds(); + + case DateType.Day: + return date.getDay(); + + default: + throw new Error(`Unknown DateType value "${part}".`); + } + } + /** + * Returns a date formatter that transforms a date into its locale string representation + */ + + function dateStrGetter(name, width, form = FormStyle.Format, extended = false) { + return function(date, locale) { + return getDateTranslation(date, locale, name, width, form, extended); + }; + } + /** + * Returns the locale translation of a date for a given form, type and width + */ + + function getDateTranslation(date, locale, name, width, form, extended) { + switch (name) { + case TranslationType.Months: + return getLocaleMonthNames(locale, form, width)[date.getMonth()]; + + case TranslationType.Days: + return getLocaleDayNames(locale, form, width)[date.getDay()]; + + case TranslationType.DayPeriods: + const currentHours = date.getHours(); + const currentMinutes = date.getMinutes(); + + if (extended) { + const rules = getLocaleExtraDayPeriodRules(locale); + const dayPeriods = getLocaleExtraDayPeriods(locale, form, width); + const index = rules.findIndex(rule => { + if (Array.isArray(rule)) { + // morning, afternoon, evening, night + const [from, to] = rule; + const afterFrom = currentHours >= from.hours && currentMinutes >= from.minutes; + const beforeTo = + currentHours < to.hours || + (currentHours === to.hours && currentMinutes < to.minutes); // We must account for normal rules that span a period during the day (e.g. 6am-9am) + // where `from` is less (earlier) than `to`. But also rules that span midnight (e.g. + // 10pm - 5am) where `from` is greater (later!) than `to`. + // + // In the first case the current time must be BOTH after `from` AND before `to` + // (e.g. 8am is after 6am AND before 10am). + // + // In the second case the current time must be EITHER after `from` OR before `to` + // (e.g. 4am is before 5am but not after 10pm; and 11pm is not before 5am but it is + // after 10pm). + + if (from.hours < to.hours) { + if (afterFrom && beforeTo) { + return true; + } + } else if (afterFrom || beforeTo) { + return true; + } + } else { + // noon or midnight + if (rule.hours === currentHours && rule.minutes === currentMinutes) { + return true; + } + } + + return false; + }); + + if (index !== -1) { + return dayPeriods[index]; + } + } // if no rules for the day periods, we use am/pm by default + + return getLocaleDayPeriods(locale, form, width)[currentHours < 12 ? 0 : 1]; + + case TranslationType.Eras: + return getLocaleEraNames(locale, width)[date.getFullYear() <= 0 ? 0 : 1]; + + default: + // This default case is not needed by TypeScript compiler, as the switch is exhaustive. + // However Closure Compiler does not understand that and reports an error in typed mode. + // The `throw new Error` below works around the problem, and the unexpected: never variable + // makes sure tsc still checks this code is unreachable. + const unexpected = name; + throw new Error(`unexpected translation type ${unexpected}`); + } + } + /** + * Returns a date formatter that transforms a date and an offset into a timezone with ISO8601 or + * GMT format depending on the width (eg: short = +0430, short:GMT = GMT+4, long = GMT+04:30, + * extended = +04:30) + */ + + function timeZoneGetter(width) { + return function(date, locale, offset) { + const zone = -1 * offset; + const minusSign = getLocaleNumberSymbol(locale, NumberSymbol.MinusSign); + const hours = zone > 0 ? Math.floor(zone / 60) : Math.ceil(zone / 60); + + switch (width) { + case ZoneWidth.Short: + return ( + (zone >= 0 ? "+" : "") + + padNumber(hours, 2, minusSign) + + padNumber(Math.abs(zone % 60), 2, minusSign) + ); + + case ZoneWidth.ShortGMT: + return "GMT" + (zone >= 0 ? "+" : "") + padNumber(hours, 1, minusSign); + + case ZoneWidth.Long: + return ( + "GMT" + + (zone >= 0 ? "+" : "") + + padNumber(hours, 2, minusSign) + + ":" + + padNumber(Math.abs(zone % 60), 2, minusSign) + ); + + case ZoneWidth.Extended: + if (offset === 0) { + return "Z"; + } else { + return ( + (zone >= 0 ? "+" : "") + + padNumber(hours, 2, minusSign) + + ":" + + padNumber(Math.abs(zone % 60), 2, minusSign) + ); + } + + default: + throw new Error(`Unknown zone width "${width}"`); + } + }; + } + + const JANUARY = 0; + const THURSDAY = 4; + + function getFirstThursdayOfYear(year) { + const firstDayOfYear = createDate(year, JANUARY, 1).getDay(); + return createDate( + year, + 0, + 1 + (firstDayOfYear <= THURSDAY ? THURSDAY : THURSDAY + 7) - firstDayOfYear + ); + } + + function getThursdayThisWeek(datetime) { + return createDate( + datetime.getFullYear(), + datetime.getMonth(), + datetime.getDate() + (THURSDAY - datetime.getDay()) + ); + } + + function weekGetter(size, monthBased = false) { + return function(date, locale) { + let result; + + if (monthBased) { + const nbDaysBefore1stDayOfMonth = + new Date(date.getFullYear(), date.getMonth(), 1).getDay() - 1; + const today = date.getDate(); + result = 1 + Math.floor((today + nbDaysBefore1stDayOfMonth) / 7); + } else { + const thisThurs = getThursdayThisWeek(date); // Some days of a year are part of next year according to ISO 8601. + // Compute the firstThurs from the year of this week's Thursday + + const firstThurs = getFirstThursdayOfYear(thisThurs.getFullYear()); + const diff = thisThurs.getTime() - firstThurs.getTime(); + result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week + } + + return padNumber(result, size, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign)); + }; + } + /** + * Returns a date formatter that provides the week-numbering year for the input date. + */ + + function weekNumberingYearGetter(size, trim = false) { + return function(date, locale) { + const thisThurs = getThursdayThisWeek(date); + const weekNumberingYear = thisThurs.getFullYear(); + return padNumber( + weekNumberingYear, + size, + getLocaleNumberSymbol(locale, NumberSymbol.MinusSign), + trim + ); + }; + } + + const DATE_FORMATS = {}; // Based on CLDR formats: + // See complete list: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table + // See also explanations: http://cldr.unicode.org/translation/date-time + // TODO(ocombe): support all missing cldr formats: U, Q, D, F, e, j, J, C, A, v, V, X, x + + function getDateFormatter(format) { + if (DATE_FORMATS[format]) { + return DATE_FORMATS[format]; + } + + let formatter; + + switch (format) { + // Era name (AD/BC) + case "G": + case "GG": + case "GGG": + formatter = dateStrGetter(TranslationType.Eras, TranslationWidth.Abbreviated); + break; + + case "GGGG": + formatter = dateStrGetter(TranslationType.Eras, TranslationWidth.Wide); + break; + + case "GGGGG": + formatter = dateStrGetter(TranslationType.Eras, TranslationWidth.Narrow); + break; + // 1 digit representation of the year, e.g. (AD 1 => 1, AD 199 => 199) + + case "y": + formatter = dateGetter(DateType.FullYear, 1, 0, false, true); + break; + // 2 digit representation of the year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + + case "yy": + formatter = dateGetter(DateType.FullYear, 2, 0, true, true); + break; + // 3 digit representation of the year, padded (000-999). (e.g. AD 2001 => 01, AD 2010 => 10) + + case "yyy": + formatter = dateGetter(DateType.FullYear, 3, 0, false, true); + break; + // 4 digit representation of the year (e.g. AD 1 => 0001, AD 2010 => 2010) + + case "yyyy": + formatter = dateGetter(DateType.FullYear, 4, 0, false, true); + break; + // 1 digit representation of the week-numbering year, e.g. (AD 1 => 1, AD 199 => 199) + + case "Y": + formatter = weekNumberingYearGetter(1); + break; + // 2 digit representation of the week-numbering year, padded (00-99). (e.g. AD 2001 => 01, AD + // 2010 => 10) + + case "YY": + formatter = weekNumberingYearGetter(2, true); + break; + // 3 digit representation of the week-numbering year, padded (000-999). (e.g. AD 1 => 001, AD + // 2010 => 2010) + + case "YYY": + formatter = weekNumberingYearGetter(3); + break; + // 4 digit representation of the week-numbering year (e.g. AD 1 => 0001, AD 2010 => 2010) + + case "YYYY": + formatter = weekNumberingYearGetter(4); + break; + // Month of the year (1-12), numeric + + case "M": + case "L": + formatter = dateGetter(DateType.Month, 1, 1); + break; + + case "MM": + case "LL": + formatter = dateGetter(DateType.Month, 2, 1); + break; + // Month of the year (January, ...), string, format + + case "MMM": + formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Abbreviated); + break; + + case "MMMM": + formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Wide); + break; + + case "MMMMM": + formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Narrow); + break; + // Month of the year (January, ...), string, standalone + + case "LLL": + formatter = dateStrGetter( + TranslationType.Months, + TranslationWidth.Abbreviated, + FormStyle.Standalone + ); + break; + + case "LLLL": + formatter = dateStrGetter( + TranslationType.Months, + TranslationWidth.Wide, + FormStyle.Standalone + ); + break; + + case "LLLLL": + formatter = dateStrGetter( + TranslationType.Months, + TranslationWidth.Narrow, + FormStyle.Standalone + ); + break; + // Week of the year (1, ... 52) + + case "w": + formatter = weekGetter(1); + break; + + case "ww": + formatter = weekGetter(2); + break; + // Week of the month (1, ...) + + case "W": + formatter = weekGetter(1, true); + break; + // Day of the month (1-31) + + case "d": + formatter = dateGetter(DateType.Date, 1); + break; + + case "dd": + formatter = dateGetter(DateType.Date, 2); + break; + // Day of the Week StandAlone (1, 1, Mon, Monday, M, Mo) + + case "c": + case "cc": + formatter = dateGetter(DateType.Day, 1); + break; + + case "ccc": + formatter = dateStrGetter( + TranslationType.Days, + TranslationWidth.Abbreviated, + FormStyle.Standalone + ); + break; + + case "cccc": + formatter = dateStrGetter( + TranslationType.Days, + TranslationWidth.Wide, + FormStyle.Standalone + ); + break; + + case "ccccc": + formatter = dateStrGetter( + TranslationType.Days, + TranslationWidth.Narrow, + FormStyle.Standalone + ); + break; + + case "cccccc": + formatter = dateStrGetter( + TranslationType.Days, + TranslationWidth.Short, + FormStyle.Standalone + ); + break; + // Day of the Week + + case "E": + case "EE": + case "EEE": + formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Abbreviated); + break; + + case "EEEE": + formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Wide); + break; + + case "EEEEE": + formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Narrow); + break; + + case "EEEEEE": + formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Short); + break; + // Generic period of the day (am-pm) + + case "a": + case "aa": + case "aaa": + formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Abbreviated); + break; + + case "aaaa": + formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Wide); + break; + + case "aaaaa": + formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Narrow); + break; + // Extended period of the day (midnight, at night, ...), standalone + + case "b": + case "bb": + case "bbb": + formatter = dateStrGetter( + TranslationType.DayPeriods, + TranslationWidth.Abbreviated, + FormStyle.Standalone, + true + ); + break; + + case "bbbb": + formatter = dateStrGetter( + TranslationType.DayPeriods, + TranslationWidth.Wide, + FormStyle.Standalone, + true + ); + break; + + case "bbbbb": + formatter = dateStrGetter( + TranslationType.DayPeriods, + TranslationWidth.Narrow, + FormStyle.Standalone, + true + ); + break; + // Extended period of the day (midnight, night, ...), standalone + + case "B": + case "BB": + case "BBB": + formatter = dateStrGetter( + TranslationType.DayPeriods, + TranslationWidth.Abbreviated, + FormStyle.Format, + true + ); + break; + + case "BBBB": + formatter = dateStrGetter( + TranslationType.DayPeriods, + TranslationWidth.Wide, + FormStyle.Format, + true + ); + break; + + case "BBBBB": + formatter = dateStrGetter( + TranslationType.DayPeriods, + TranslationWidth.Narrow, + FormStyle.Format, + true + ); + break; + // Hour in AM/PM, (1-12) + + case "h": + formatter = dateGetter(DateType.Hours, 1, -12); + break; + + case "hh": + formatter = dateGetter(DateType.Hours, 2, -12); + break; + // Hour of the day (0-23) + + case "H": + formatter = dateGetter(DateType.Hours, 1); + break; + // Hour in day, padded (00-23) + + case "HH": + formatter = dateGetter(DateType.Hours, 2); + break; + // Minute of the hour (0-59) + + case "m": + formatter = dateGetter(DateType.Minutes, 1); + break; + + case "mm": + formatter = dateGetter(DateType.Minutes, 2); + break; + // Second of the minute (0-59) + + case "s": + formatter = dateGetter(DateType.Seconds, 1); + break; + + case "ss": + formatter = dateGetter(DateType.Seconds, 2); + break; + // Fractional second + + case "S": + formatter = dateGetter(DateType.FractionalSeconds, 1); + break; + + case "SS": + formatter = dateGetter(DateType.FractionalSeconds, 2); + break; + + case "SSS": + formatter = dateGetter(DateType.FractionalSeconds, 3); + break; + // Timezone ISO8601 short format (-0430) + + case "Z": + case "ZZ": + case "ZZZ": + formatter = timeZoneGetter(ZoneWidth.Short); + break; + // Timezone ISO8601 extended format (-04:30) + + case "ZZZZZ": + formatter = timeZoneGetter(ZoneWidth.Extended); + break; + // Timezone GMT short format (GMT+4) + + case "O": + case "OO": + case "OOO": // Should be location, but fallback to format O instead because we don't have the data yet + + case "z": + case "zz": + case "zzz": + formatter = timeZoneGetter(ZoneWidth.ShortGMT); + break; + // Timezone GMT long format (GMT+0430) + + case "OOOO": + case "ZZZZ": // Should be location, but fallback to format O instead because we don't have the data yet + + case "zzzz": + formatter = timeZoneGetter(ZoneWidth.Long); + break; + + default: + return null; + } + + DATE_FORMATS[format] = formatter; + return formatter; + } + + function timezoneToOffset(timezone, fallback) { + // Support: IE 11 only, Edge 13-15+ + // IE/Edge do not "understand" colon (`:`) in timezone + timezone = timezone.replace(/:/g, ""); + const requestedTimezoneOffset = Date.parse("Jan 01, 1970 00:00:00 " + timezone) / 60000; + return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; + } + + function addDateMinutes(date, minutes) { + date = new Date(date.getTime()); + date.setMinutes(date.getMinutes() + minutes); + return date; + } + + function convertTimezoneToLocal(date, timezone, reverse) { + const reverseValue = reverse ? -1 : 1; + const dateTimezoneOffset = date.getTimezoneOffset(); + const timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + return addDateMinutes(date, reverseValue * (timezoneOffset - dateTimezoneOffset)); + } + /** + * Converts a value to date. + * + * Supported input formats: + * - `Date` + * - number: timestamp + * - string: numeric (e.g. "1234"), ISO and date strings in a format supported by + * [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). + * Note: ISO strings without time return a date without timeoffset. + * + * Throws if unable to convert to a date. + */ + + function toDate(value) { + if (isDate(value)) { + return value; + } + + if (typeof value === "number" && !isNaN(value)) { + return new Date(value); + } + + if (typeof value === "string") { + value = value.trim(); + + if (/^(\d{4}(-\d{1,2}(-\d{1,2})?)?)$/.test(value)) { + /* For ISO Strings without time the day, month and year must be extracted from the ISO String + before Date creation to avoid time offset and errors in the new Date. + If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new + date, some browsers (e.g. IE 9) will throw an invalid Date error. + If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset + is applied. + Note: ISO months are 0 for January, 1 for February, ... */ + const [y, m = 1, d = 1] = value.split("-").map(val => +val); + return createDate(y, m - 1, d); + } + + const parsedNb = parseFloat(value); // any string that only contains numbers, like "1234" but not like "1234hello" + + if (!isNaN(value - parsedNb)) { + return new Date(parsedNb); + } + + let match; + + if ((match = value.match(ISO8601_DATE_REGEX))) { + return isoStringToDate(match); + } + } + + const date = new Date(value); + + if (!isDate(date)) { + throw new Error(`Unable to convert "${value}" into a date`); + } + + return date; + } + /** + * Converts a date in ISO8601 to a Date. + * Used instead of `Date.parse` because of browser discrepancies. + */ + + function isoStringToDate(match) { + const date = new Date(0); + let tzHour = 0; + let tzMin = 0; // match[8] means that the string contains "Z" (UTC) or a timezone like "+01:00" or "+0100" + + const dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear; + const timeSetter = match[8] ? date.setUTCHours : date.setHours; // if there is a timezone defined like "+01:00" or "+0100" + + if (match[9]) { + tzHour = Number(match[9] + match[10]); + tzMin = Number(match[9] + match[11]); + } + + dateSetter.call(date, Number(match[1]), Number(match[2]) - 1, Number(match[3])); + const h = Number(match[4] || 0) - tzHour; + const m = Number(match[5] || 0) - tzMin; + const s = Number(match[6] || 0); // The ECMAScript specification (https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.11) + // defines that `DateTime` milliseconds should always be rounded down, so that `999.9ms` + // becomes `999ms`. + + const ms = Math.floor(parseFloat("0." + (match[7] || 0)) * 1000); + timeSetter.call(date, h, m, s, ms); + return date; + } + + function isDate(value) { + return value instanceof Date && !isNaN(value.valueOf()); + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + const NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/; + const MAX_DIGITS = 22; + const DECIMAL_SEP = "."; + const ZERO_CHAR = "0"; + const PATTERN_SEP = ";"; + const GROUP_SEP = ","; + const DIGIT_CHAR = "#"; + const CURRENCY_CHAR = "¤"; + const PERCENT_CHAR = "%"; + /** + * Transforms a number to a locale string based on a style and a format. + */ + + function formatNumberToLocaleString( + value, + pattern, + locale, + groupSymbol, + decimalSymbol, + digitsInfo, + isPercent = false + ) { + let formattedText = ""; + let isZero = false; + + if (!isFinite(value)) { + formattedText = getLocaleNumberSymbol(locale, NumberSymbol.Infinity); + } else { + let parsedNumber = parseNumber(value); + + if (isPercent) { + parsedNumber = toPercent(parsedNumber); + } + + let minInt = pattern.minInt; + let minFraction = pattern.minFrac; + let maxFraction = pattern.maxFrac; + + if (digitsInfo) { + const parts = digitsInfo.match(NUMBER_FORMAT_REGEXP); + + if (parts === null) { + throw new Error(`${digitsInfo} is not a valid digit info`); + } + + const minIntPart = parts[1]; + const minFractionPart = parts[3]; + const maxFractionPart = parts[5]; + + if (minIntPart != null) { + minInt = parseIntAutoRadix(minIntPart); + } + + if (minFractionPart != null) { + minFraction = parseIntAutoRadix(minFractionPart); + } + + if (maxFractionPart != null) { + maxFraction = parseIntAutoRadix(maxFractionPart); + } else if (minFractionPart != null && minFraction > maxFraction) { + maxFraction = minFraction; + } + } + + roundNumber(parsedNumber, minFraction, maxFraction); + let digits = parsedNumber.digits; + let integerLen = parsedNumber.integerLen; + const exponent = parsedNumber.exponent; + let decimals = []; + isZero = digits.every(d => !d); // pad zeros for small numbers + + for (; integerLen < minInt; integerLen++) { + digits.unshift(0); + } // pad zeros for small numbers + + for (; integerLen < 0; integerLen++) { + digits.unshift(0); + } // extract decimals digits + + if (integerLen > 0) { + decimals = digits.splice(integerLen, digits.length); + } else { + decimals = digits; + digits = [0]; + } // format the integer digits with grouping separators + + const groups = []; + + if (digits.length >= pattern.lgSize) { + groups.unshift(digits.splice(-pattern.lgSize, digits.length).join("")); + } + + while (digits.length > pattern.gSize) { + groups.unshift(digits.splice(-pattern.gSize, digits.length).join("")); + } + + if (digits.length) { + groups.unshift(digits.join("")); + } + + formattedText = groups.join(getLocaleNumberSymbol(locale, groupSymbol)); // append the decimal digits + + if (decimals.length) { + formattedText += getLocaleNumberSymbol(locale, decimalSymbol) + decimals.join(""); + } + + if (exponent) { + formattedText += getLocaleNumberSymbol(locale, NumberSymbol.Exponential) + "+" + exponent; + } + } + + if (value < 0 && !isZero) { + formattedText = pattern.negPre + formattedText + pattern.negSuf; + } else { + formattedText = pattern.posPre + formattedText + pattern.posSuf; + } + + return formattedText; + } + /** + * @ngModule CommonModule + * @description + * + * Formats a number as currency using locale rules. + * + * @param value The number to format. + * @param locale A locale code for the locale format rules to use. + * @param currency A string containing the currency symbol or its name, + * such as "$" or "Canadian Dollar". Used in output string, but does not affect the operation + * of the function. + * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) + * currency code, such as `USD` for the US dollar and `EUR` for the euro. + * Used to determine the number of digits in the decimal part. + * @param digitsInfo Decimal representation options, specified by a string in the following format: + * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. + * + * @returns The formatted currency value. + * + * @see `formatNumber()` + * @see `DecimalPipe` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function formatCurrency(value, locale, currency, currencyCode, digitsInfo) { + const format = getLocaleNumberFormat(locale, NumberFormatStyle.Currency); + const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign)); + pattern.minFrac = getNumberOfCurrencyDigits(currencyCode); + pattern.maxFrac = pattern.minFrac; + const res = formatNumberToLocaleString( + value, + pattern, + locale, + NumberSymbol.CurrencyGroup, + NumberSymbol.CurrencyDecimal, + digitsInfo + ); + return ( + res + .replace(CURRENCY_CHAR, currency) // if we have 2 time the currency character, the second one is ignored + .replace(CURRENCY_CHAR, "") // If there is a spacing between currency character and the value and + // the currency character is supressed by passing an empty string, the + // spacing character would remain as part of the string. Then we + // should remove it. + .trim() + ); + } + /** + * @ngModule CommonModule + * @description + * + * Formats a number as a percentage according to locale rules. + * + * @param value The number to format. + * @param locale A locale code for the locale format rules to use. + * @param digitsInfo Decimal representation options, specified by a string in the following format: + * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. + * + * @returns The formatted percentage value. + * + * @see `formatNumber()` + * @see `DecimalPipe` + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * @publicApi + * + */ + + function formatPercent(value, locale, digitsInfo) { + const format = getLocaleNumberFormat(locale, NumberFormatStyle.Percent); + const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign)); + const res = formatNumberToLocaleString( + value, + pattern, + locale, + NumberSymbol.Group, + NumberSymbol.Decimal, + digitsInfo, + true + ); + return res.replace( + new RegExp(PERCENT_CHAR, "g"), + getLocaleNumberSymbol(locale, NumberSymbol.PercentSign) + ); + } + /** + * @ngModule CommonModule + * @description + * + * Formats a number as text, with group sizing, separator, and other + * parameters based on the locale. + * + * @param value The number to format. + * @param locale A locale code for the locale format rules to use. + * @param digitsInfo Decimal representation options, specified by a string in the following format: + * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. + * + * @returns The formatted text string. + * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) + * + * @publicApi + */ + + function formatNumber(value, locale, digitsInfo) { + const format = getLocaleNumberFormat(locale, NumberFormatStyle.Decimal); + const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign)); + return formatNumberToLocaleString( + value, + pattern, + locale, + NumberSymbol.Group, + NumberSymbol.Decimal, + digitsInfo + ); + } + + function parseNumberFormat(format, minusSign = "-") { + const p = { + minInt: 1, + minFrac: 0, + maxFrac: 0, + posPre: "", + posSuf: "", + negPre: "", + negSuf: "", + gSize: 0, + lgSize: 0 + }; + const patternParts = format.split(PATTERN_SEP); + const positive = patternParts[0]; + const negative = patternParts[1]; + const positiveParts = + positive.indexOf(DECIMAL_SEP) !== -1 + ? positive.split(DECIMAL_SEP) + : [ + positive.substring(0, positive.lastIndexOf(ZERO_CHAR) + 1), + positive.substring(positive.lastIndexOf(ZERO_CHAR) + 1) + ], + integer = positiveParts[0], + fraction = positiveParts[1] || ""; + p.posPre = integer.substr(0, integer.indexOf(DIGIT_CHAR)); + + for (let i = 0; i < fraction.length; i++) { + const ch = fraction.charAt(i); + + if (ch === ZERO_CHAR) { + p.minFrac = p.maxFrac = i + 1; + } else if (ch === DIGIT_CHAR) { + p.maxFrac = i + 1; + } else { + p.posSuf += ch; + } + } + + const groups = integer.split(GROUP_SEP); + p.gSize = groups[1] ? groups[1].length : 0; + p.lgSize = groups[2] || groups[1] ? (groups[2] || groups[1]).length : 0; + + if (negative) { + const trunkLen = positive.length - p.posPre.length - p.posSuf.length, + pos = negative.indexOf(DIGIT_CHAR); + p.negPre = negative.substr(0, pos).replace(/'/g, ""); + p.negSuf = negative.substr(pos + trunkLen).replace(/'/g, ""); + } else { + p.negPre = minusSign + p.posPre; + p.negSuf = p.posSuf; + } + + return p; + } // Transforms a parsed number into a percentage by multiplying it by 100 + + function toPercent(parsedNumber) { + // if the number is 0, don't do anything + if (parsedNumber.digits[0] === 0) { + return parsedNumber; + } // Getting the current number of decimals + + const fractionLen = parsedNumber.digits.length - parsedNumber.integerLen; + + if (parsedNumber.exponent) { + parsedNumber.exponent += 2; + } else { + if (fractionLen === 0) { + parsedNumber.digits.push(0, 0); + } else if (fractionLen === 1) { + parsedNumber.digits.push(0); + } + + parsedNumber.integerLen += 2; + } + + return parsedNumber; + } + /** + * Parses a number. + * Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/ + */ + + function parseNumber(num) { + let numStr = Math.abs(num) + ""; + let exponent = 0, + digits, + integerLen; + let i, j, zeros; // Decimal point? + + if ((integerLen = numStr.indexOf(DECIMAL_SEP)) > -1) { + numStr = numStr.replace(DECIMAL_SEP, ""); + } // Exponential form? + + if ((i = numStr.search(/e/i)) > 0) { + // Work out the exponent. + if (integerLen < 0) integerLen = i; + integerLen += +numStr.slice(i + 1); + numStr = numStr.substring(0, i); + } else if (integerLen < 0) { + // There was no decimal point or exponent so it is an integer. + integerLen = numStr.length; + } // Count the number of leading zeros. + + for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) { + /* empty */ + } + + if (i === (zeros = numStr.length)) { + // The digits are all zero. + digits = [0]; + integerLen = 1; + } else { + // Count the number of trailing zeros + zeros--; + + while (numStr.charAt(zeros) === ZERO_CHAR) zeros--; // Trailing zeros are insignificant so ignore them + + integerLen -= i; + digits = []; // Convert string to array of digits without leading/trailing zeros. + + for (j = 0; i <= zeros; i++, j++) { + digits[j] = Number(numStr.charAt(i)); + } + } // If the number overflows the maximum allowed digits then use an exponent. + + if (integerLen > MAX_DIGITS) { + digits = digits.splice(0, MAX_DIGITS - 1); + exponent = integerLen - 1; + integerLen = 1; + } + + return { + digits, + exponent, + integerLen + }; + } + /** + * Round the parsed number to the specified number of decimal places + * This function changes the parsedNumber in-place + */ + + function roundNumber(parsedNumber, minFrac, maxFrac) { + if (minFrac > maxFrac) { + throw new Error( + `The minimum number of digits after fraction (${minFrac}) is higher than the maximum (${maxFrac}).` + ); + } + + let digits = parsedNumber.digits; + let fractionLen = digits.length - parsedNumber.integerLen; + const fractionSize = Math.min(Math.max(minFrac, fractionLen), maxFrac); // The index of the digit to where rounding is to occur + + let roundAt = fractionSize + parsedNumber.integerLen; + let digit = digits[roundAt]; + + if (roundAt > 0) { + // Drop fractional digits beyond `roundAt` + digits.splice(Math.max(parsedNumber.integerLen, roundAt)); // Set non-fractional digits beyond `roundAt` to 0 + + for (let j = roundAt; j < digits.length; j++) { + digits[j] = 0; + } + } else { + // We rounded to zero so reset the parsedNumber + fractionLen = Math.max(0, fractionLen); + parsedNumber.integerLen = 1; + digits.length = Math.max(1, (roundAt = fractionSize + 1)); + digits[0] = 0; + + for (let i = 1; i < roundAt; i++) digits[i] = 0; + } + + if (digit >= 5) { + if (roundAt - 1 < 0) { + for (let k = 0; k > roundAt; k--) { + digits.unshift(0); + parsedNumber.integerLen++; + } + + digits.unshift(1); + parsedNumber.integerLen++; + } else { + digits[roundAt - 1]++; + } + } // Pad out with zeros to get the required fraction length + + for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0); + + let dropTrailingZeros = fractionSize !== 0; // Minimal length = nb of decimals required + current nb of integers + // Any number besides that is optional and can be removed if it's a trailing 0 + + const minLen = minFrac + parsedNumber.integerLen; // Do any carrying, e.g. a digit was rounded up to 10 + + const carry = digits.reduceRight(function(carry, d, i, digits) { + d = d + carry; + digits[i] = d < 10 ? d : d - 10; // d % 10 + + if (dropTrailingZeros) { + // Do not keep meaningless fractional trailing zeros (e.g. 15.52000 --> 15.52) + if (digits[i] === 0 && i >= minLen) { + digits.pop(); + } else { + dropTrailingZeros = false; + } + } + + return d >= 10 ? 1 : 0; // Math.floor(d / 10); + }, 0); + + if (carry) { + digits.unshift(carry); + parsedNumber.integerLen++; + } + } + + function parseIntAutoRadix(text) { + const result = parseInt(text); + + if (isNaN(result)) { + throw new Error("Invalid integer literal when parsing " + text); + } + + return result; + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @publicApi + */ + + class NgLocalization {} + + NgLocalization.ɵfac = function NgLocalization_Factory(t) { + return new (t || NgLocalization)(); + }; + + NgLocalization.ɵprov = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]( + { + token: NgLocalization, + factory: function NgLocalization_Factory(t) { + let r = null; + + if (t) { + r = new t(); + } else { + r = (locale => new NgLocaleLocalization(locale))( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID + ) + ); + } + + return r; + }, + providedIn: "root" + } + ); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgLocalization, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, + args: [ + { + providedIn: "root", + useFactory: locale => new NgLocaleLocalization(locale), + deps: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] + } + ] + } + ], + null, + null + ); + })(); + /** + * Returns the plural category for a given value. + * - "=value" when the case exists, + * - the plural category otherwise + */ + + function getPluralCategory(value, cases, ngLocalization, locale) { + let key = `=${value}`; + + if (cases.indexOf(key) > -1) { + return key; + } + + key = ngLocalization.getPluralCategory(value, locale); + + if (cases.indexOf(key) > -1) { + return key; + } + + if (cases.indexOf("other") > -1) { + return "other"; + } + + throw new Error(`No plural message found for value "${value}"`); + } + /** + * Returns the plural case based on the locale + * + * @publicApi + */ + + class NgLocaleLocalization extends NgLocalization { + constructor(locale) { + super(); + this.locale = locale; + } + + getPluralCategory(value, locale) { + const plural = getLocalePluralCase(locale || this.locale)(value); + + switch (plural) { + case Plural.Zero: + return "zero"; + + case Plural.One: + return "one"; + + case Plural.Two: + return "two"; + + case Plural.Few: + return "few"; + + case Plural.Many: + return "many"; + + default: + return "other"; + } + } + } + + NgLocaleLocalization.ɵfac = function NgLocaleLocalization_Factory(t) { + return new (t || NgLocaleLocalization)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID + ) + ); + }; + + NgLocaleLocalization.ɵprov = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__[ + "ɵɵdefineInjectable" + ]({ + token: NgLocaleLocalization, + factory: NgLocaleLocalization.ɵfac + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgLocaleLocalization, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable + } + ], + function() { + return [ + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, + args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] + } + ] + } + ]; + }, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Register global data to be used internally by Angular. See the + * ["I18n guide"](guide/i18n-common-format-data-locale) to know how to import additional locale + * data. + * + * The signature registerLocaleData(data: any, extraData?: any) is deprecated since v5.1 + * + * @publicApi + */ + + function registerLocaleData(data, localeId, extraData) { + return (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵregisterLocaleData"])( + data, + localeId, + extraData + ); + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + function parseCookieValue(cookieStr, name) { + name = encodeURIComponent(name); + + for (const cookie of cookieStr.split(";")) { + const eqIndex = cookie.indexOf("="); + const [cookieName, cookieValue] = + eqIndex == -1 ? [cookie, ""] : [cookie.slice(0, eqIndex), cookie.slice(eqIndex + 1)]; + + if (cookieName.trim() === name) { + return decodeURIComponent(cookieValue); + } + } + + return null; + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @ngModule CommonModule + * + * @usageNotes + * ``` + * ... + * + * ... + * + * ... + * + * ... + * + * ... + * ``` + * + * @description + * + * Adds and removes CSS classes on an HTML element. + * + * The CSS classes are updated as follows, depending on the type of the expression evaluation: + * - `string` - the CSS classes listed in the string (space delimited) are added, + * - `Array` - the CSS classes declared as Array elements are added, + * - `Object` - keys are CSS classes that get added when the expression given in the value + * evaluates to a truthy value, otherwise they are removed. + * + * @publicApi + */ + + class NgClass { + constructor(_iterableDiffers, _keyValueDiffers, _ngEl, _renderer) { + this._iterableDiffers = _iterableDiffers; + this._keyValueDiffers = _keyValueDiffers; + this._ngEl = _ngEl; + this._renderer = _renderer; + this._iterableDiffer = null; + this._keyValueDiffer = null; + this._initialClasses = []; + this._rawClass = null; + } + + set klass(value) { + this._removeClasses(this._initialClasses); + + this._initialClasses = typeof value === "string" ? value.split(/\s+/) : []; + + this._applyClasses(this._initialClasses); + + this._applyClasses(this._rawClass); + } + + set ngClass(value) { + this._removeClasses(this._rawClass); + + this._applyClasses(this._initialClasses); + + this._iterableDiffer = null; + this._keyValueDiffer = null; + this._rawClass = typeof value === "string" ? value.split(/\s+/) : value; + + if (this._rawClass) { + if ( + (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisListLikeIterable"])(this._rawClass) + ) { + this._iterableDiffer = this._iterableDiffers.find(this._rawClass).create(); + } else { + this._keyValueDiffer = this._keyValueDiffers.find(this._rawClass).create(); + } + } + } + + ngDoCheck() { + if (this._iterableDiffer) { + const iterableChanges = this._iterableDiffer.diff(this._rawClass); + + if (iterableChanges) { + this._applyIterableChanges(iterableChanges); + } + } else if (this._keyValueDiffer) { + const keyValueChanges = this._keyValueDiffer.diff(this._rawClass); + + if (keyValueChanges) { + this._applyKeyValueChanges(keyValueChanges); + } + } + } + + _applyKeyValueChanges(changes) { + changes.forEachAddedItem(record => this._toggleClass(record.key, record.currentValue)); + changes.forEachChangedItem(record => this._toggleClass(record.key, record.currentValue)); + changes.forEachRemovedItem(record => { + if (record.previousValue) { + this._toggleClass(record.key, false); + } + }); + } + + _applyIterableChanges(changes) { + changes.forEachAddedItem(record => { + if (typeof record.item === "string") { + this._toggleClass(record.item, true); + } else { + throw new Error( + `NgClass can only toggle CSS classes expressed as strings, got ${(0, + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵstringify"])(record.item)}` + ); + } + }); + changes.forEachRemovedItem(record => this._toggleClass(record.item, false)); + } + /** + * Applies a collection of CSS classes to the DOM element. + * + * For argument of type Set and Array CSS class names contained in those collections are always + * added. + * For argument of type Map CSS class name in the map's key is toggled based on the value (added + * for truthy and removed for falsy). + */ + + _applyClasses(rawClassVal) { + if (rawClassVal) { + if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { + rawClassVal.forEach(klass => this._toggleClass(klass, true)); + } else { + Object.keys(rawClassVal).forEach(klass => + this._toggleClass(klass, !!rawClassVal[klass]) + ); + } + } + } + /** + * Removes a collection of CSS classes from the DOM element. This is mostly useful for cleanup + * purposes. + */ + + _removeClasses(rawClassVal) { + if (rawClassVal) { + if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { + rawClassVal.forEach(klass => this._toggleClass(klass, false)); + } else { + Object.keys(rawClassVal).forEach(klass => this._toggleClass(klass, false)); + } + } + } + + _toggleClass(klass, enabled) { + klass = klass.trim(); + + if (klass) { + klass.split(/\s+/g).forEach(klass => { + if (enabled) { + this._renderer.addClass(this._ngEl.nativeElement, klass); + } else { + this._renderer.removeClass(this._ngEl.nativeElement, klass); + } + }); + } + } + } + + NgClass.ɵfac = function NgClass_Factory(t) { + return new (t || NgClass)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.IterableDiffers + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 + ) + ); + }; + + NgClass.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ + type: NgClass, + selectors: [["", "ngClass", ""]], + inputs: { + klass: ["class", "klass"], + ngClass: "ngClass" + } + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgClass, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngClass]" + } + ] + } + ], + function() { + return [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.IterableDiffers + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 + } + ]; + }, + { + klass: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, + args: ["class"] + } + ], + ngClass: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, + args: ["ngClass"] + } + ] + } + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Instantiates a {@link Component} type and inserts its Host View into the current View. + * `NgComponentOutlet` provides a declarative approach for dynamic component creation. + * + * `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and + * any existing component will be destroyed. + * + * @usageNotes + * + * ### Fine tune control + * + * You can control the component creation process by using the following optional attributes: + * + * * `ngComponentOutletInjector`: Optional custom {@link Injector} that will be used as parent for + * the Component. Defaults to the injector of the current view container. + * + * * `ngComponentOutletContent`: Optional list of projectable nodes to insert into the content + * section of the component, if it exists. + * + * * `ngComponentOutletNgModuleFactory`: Optional module factory to allow loading another + * module dynamically, then loading a component from that module. + * + * ### Syntax + * + * Simple + * ``` + * + * ``` + * + * Customized injector/content + * ``` + * + * + * ``` + * + * Customized ngModuleFactory + * ``` + * + * + * ``` + * + * ### A simple example + * + * {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'} + * + * A more complete example with additional options: + * + * {@example common/ngComponentOutlet/ts/module.ts region='CompleteExample'} + * + * @publicApi + * @ngModule CommonModule + */ + + class NgComponentOutlet { + constructor(_viewContainerRef) { + this._viewContainerRef = _viewContainerRef; + this._componentRef = null; + this._moduleRef = null; + } + /** @nodoc */ + + ngOnChanges(changes) { + this._viewContainerRef.clear(); + + this._componentRef = null; + + if (this.ngComponentOutlet) { + const elInjector = this.ngComponentOutletInjector || this._viewContainerRef.parentInjector; + + if (changes["ngComponentOutletNgModuleFactory"]) { + if (this._moduleRef) this._moduleRef.destroy(); + + if (this.ngComponentOutletNgModuleFactory) { + const parentModule = elInjector.get( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgModuleRef + ); + this._moduleRef = this.ngComponentOutletNgModuleFactory.create( + parentModule.injector + ); + } else { + this._moduleRef = null; + } + } + + const componentFactoryResolver = this._moduleRef + ? this._moduleRef.componentFactoryResolver + : elInjector.get(_angular_core__WEBPACK_IMPORTED_MODULE_0__.ComponentFactoryResolver); + const componentFactory = componentFactoryResolver.resolveComponentFactory( + this.ngComponentOutlet + ); + this._componentRef = this._viewContainerRef.createComponent( + componentFactory, + this._viewContainerRef.length, + elInjector, + this.ngComponentOutletContent + ); + } + } + /** @nodoc */ + + ngOnDestroy() { + if (this._moduleRef) this._moduleRef.destroy(); + } + } + + NgComponentOutlet.ɵfac = function NgComponentOutlet_Factory(t) { + return new (t || NgComponentOutlet)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + ) + ); + }; + + NgComponentOutlet.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__[ + "ɵɵdefineDirective" + ]({ + type: NgComponentOutlet, + selectors: [["", "ngComponentOutlet", ""]], + inputs: { + ngComponentOutlet: "ngComponentOutlet", + ngComponentOutletInjector: "ngComponentOutletInjector", + ngComponentOutletContent: "ngComponentOutletContent", + ngComponentOutletNgModuleFactory: "ngComponentOutletNgModuleFactory" + }, + features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgComponentOutlet, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngComponentOutlet]" + } + ] + } + ], + function() { + return [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + } + ]; + }, + { + ngComponentOutlet: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ], + ngComponentOutletInjector: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ], + ngComponentOutletContent: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ], + ngComponentOutletNgModuleFactory: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ] + } + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @publicApi + */ + + class NgForOfContext { + constructor($implicit, ngForOf, index, count) { + this.$implicit = $implicit; + this.ngForOf = ngForOf; + this.index = index; + this.count = count; + } + + get first() { + return this.index === 0; + } + + get last() { + return this.index === this.count - 1; + } + + get even() { + return this.index % 2 === 0; + } + + get odd() { + return !this.even; + } + } + /** + * A [structural directive](guide/structural-directives) that renders + * a template for each item in a collection. + * The directive is placed on an element, which becomes the parent + * of the cloned templates. + * + * The `ngForOf` directive is generally used in the + * [shorthand form](guide/structural-directives#asterisk) `*ngFor`. + * In this form, the template to be rendered for each iteration is the content + * of an anchor element containing the directive. + * + * The following example shows the shorthand syntax with some options, + * contained in an `
  • ` element. + * + * ``` + *
  • ...
  • + * ``` + * + * The shorthand form expands into a long form that uses the `ngForOf` selector + * on an `` element. + * The content of the `` element is the `
  • ` element that held the + * short-form directive. + * + * Here is the expanded version of the short-form example. + * + * ``` + * + *
  • ...
  • + *
    + * ``` + * + * Angular automatically expands the shorthand syntax as it compiles the template. + * The context for each embedded view is logically merged to the current component + * context according to its lexical position. + * + * When using the shorthand syntax, Angular allows only [one structural directive + * on an element](guide/structural-directives#one-per-element). + * If you want to iterate conditionally, for example, + * put the `*ngIf` on a container element that wraps the `*ngFor` element. + * For futher discussion, see + * [Structural Directives](guide/structural-directives#one-per-element). + * + * @usageNotes + * + * ### Local variables + * + * `NgForOf` provides exported values that can be aliased to local variables. + * For example: + * + * ``` + *
  • + * {{i}}/{{users.length}}. {{user}} default + *
  • + * ``` + * + * The following exported values can be aliased to local variables: + * + * - `$implicit: T`: The value of the individual items in the iterable (`ngForOf`). + * - `ngForOf: NgIterable`: The value of the iterable expression. Useful when the expression is + * more complex then a property access, for example when using the async pipe (`userStreams | + * async`). + * - `index: number`: The index of the current item in the iterable. + * - `count: number`: The length of the iterable. + * - `first: boolean`: True when the item is the first item in the iterable. + * - `last: boolean`: True when the item is the last item in the iterable. + * - `even: boolean`: True when the item has an even index in the iterable. + * - `odd: boolean`: True when the item has an odd index in the iterable. + * + * ### Change propagation + * + * When the contents of the iterator changes, `NgForOf` makes the corresponding changes to the DOM: + * + * * When an item is added, a new instance of the template is added to the DOM. + * * When an item is removed, its template instance is removed from the DOM. + * * When items are reordered, their respective templates are reordered in the DOM. + * + * Angular uses object identity to track insertions and deletions within the iterator and reproduce + * those changes in the DOM. This has important implications for animations and any stateful + * controls that are present, such as `` elements that accept user input. Inserted rows can + * be animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state + * such as user input. + * For more on animations, see [Transitions and Triggers](guide/transition-and-triggers). + * + * The identities of elements in the iterator can change while the data does not. + * This can happen, for example, if the iterator is produced from an RPC to the server, and that + * RPC is re-run. Even if the data hasn't changed, the second response produces objects with + * different identities, and Angular must tear down the entire DOM and rebuild it (as if all old + * elements were deleted and all new elements inserted). + * + * To avoid this expensive operation, you can customize the default tracking algorithm. + * by supplying the `trackBy` option to `NgForOf`. + * `trackBy` takes a function that has two arguments: `index` and `item`. + * If `trackBy` is given, Angular tracks changes by the return value of the function. + * + * @see [Structural Directives](guide/structural-directives) + * @ngModule CommonModule + * @publicApi + */ + + class NgForOf { + constructor(_viewContainer, _template, _differs) { + this._viewContainer = _viewContainer; + this._template = _template; + this._differs = _differs; + this._ngForOf = null; + this._ngForOfDirty = true; + this._differ = null; + } + /** + * The value of the iterable expression, which can be used as a + * [template input variable](guide/structural-directives#shorthand). + */ + + set ngForOf(ngForOf) { + this._ngForOf = ngForOf; + this._ngForOfDirty = true; + } + /** + * Specifies a custom `TrackByFunction` to compute the identity of items in an iterable. + * + * If a custom `TrackByFunction` is not provided, `NgForOf` will use the item's [object + * identity](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) + * as the key. + * + * `NgForOf` uses the computed key to associate items in an iterable with DOM elements + * it produces for these items. + * + * A custom `TrackByFunction` is useful to provide good user experience in cases when items in an + * iterable rendered using `NgForOf` have a natural identifier (for example, custom ID or a + * primary key), and this iterable could be updated with new object instances that still + * represent the same underlying entity (for example, when data is re-fetched from the server, + * and the iterable is recreated and re-rendered, but most of the data is still the same). + * + * @see `TrackByFunction` + */ + + set ngForTrackBy(fn) { + if ((typeof ngDevMode === "undefined" || ngDevMode) && fn != null && typeof fn !== "function") { + // TODO(vicb): use a log service once there is a public one available + if (console && console.warn) { + console.warn( + `trackBy must be a function, but received ${JSON.stringify(fn)}. ` + + `See https://angular.io/api/common/NgForOf#change-propagation for more information.` + ); + } + } + + this._trackByFn = fn; + } + + get ngForTrackBy() { + return this._trackByFn; + } + /** + * A reference to the template that is stamped out for each item in the iterable. + * @see [template reference variable](guide/template-reference-variables) + */ + + set ngForTemplate(value) { + // TODO(TS2.1): make TemplateRef>> once we move to TS v2.1 + // The current type is too restrictive; a template that just uses index, for example, + // should be acceptable. + if (value) { + this._template = value; + } + } + /** + * Applies the changes when needed. + * @nodoc + */ + + ngDoCheck() { + if (this._ngForOfDirty) { + this._ngForOfDirty = false; // React on ngForOf changes only once all inputs have been initialized + + const value = this._ngForOf; + + if (!this._differ && value) { + if (typeof ngDevMode === "undefined" || ngDevMode) { + try { + // CAUTION: this logic is duplicated for production mode below, as the try-catch + // is only present in development builds. + this._differ = this._differs.find(value).create(this.ngForTrackBy); + } catch (_a) { + throw new Error( + `Cannot find a differ supporting object '${value}' of type '${getTypeName( + value + )}'. NgFor only supports binding to Iterables such as Arrays.` + ); + } + } else { + // CAUTION: this logic is duplicated for development mode above, as the try-catch + // is only present in development builds. + this._differ = this._differs.find(value).create(this.ngForTrackBy); + } + } + } + + if (this._differ) { + const changes = this._differ.diff(this._ngForOf); + + if (changes) this._applyChanges(changes); + } + } + + _applyChanges(changes) { + const viewContainer = this._viewContainer; + changes.forEachOperation((item, adjustedPreviousIndex, currentIndex) => { + if (item.previousIndex == null) { + // NgForOf is never "null" or "undefined" here because the differ detected + // that a new item needs to be inserted from the iterable. This implies that + // there is an iterable value for "_ngForOf". + viewContainer.createEmbeddedView( + this._template, + new NgForOfContext(item.item, this._ngForOf, -1, -1), + currentIndex === null ? undefined : currentIndex + ); + } else if (currentIndex == null) { + viewContainer.remove( + adjustedPreviousIndex === null ? undefined : adjustedPreviousIndex + ); + } else if (adjustedPreviousIndex !== null) { + const view = viewContainer.get(adjustedPreviousIndex); + viewContainer.move(view, currentIndex); + applyViewChange(view, item); + } + }); + + for (let i = 0, ilen = viewContainer.length; i < ilen; i++) { + const viewRef = viewContainer.get(i); + const context = viewRef.context; + context.index = i; + context.count = ilen; + context.ngForOf = this._ngForOf; + } + + changes.forEachIdentityChange(record => { + const viewRef = viewContainer.get(record.currentIndex); + applyViewChange(viewRef, record); + }); + } + /** + * Asserts the correct type of the context for the template that `NgForOf` will render. + * + * The presence of this method is a signal to the Ivy template type-check compiler that the + * `NgForOf` structural directive renders its template with a specific context type. + */ + + static ngTemplateContextGuard(dir, ctx) { + return true; + } + } + + NgForOf.ɵfac = function NgForOf_Factory(t) { + return new (t || NgForOf)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.IterableDiffers + ) + ); + }; + + NgForOf.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ + type: NgForOf, + selectors: [["", "ngFor", "", "ngForOf", ""]], + inputs: { + ngForOf: "ngForOf", + ngForTrackBy: "ngForTrackBy", + ngForTemplate: "ngForTemplate" + } + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgForOf, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngFor][ngForOf]" + } + ] + } + ], + function() { + return [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.IterableDiffers + } + ]; + }, + { + ngForOf: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ], + ngForTrackBy: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ], + ngForTemplate: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ] + } + ); + })(); + + function applyViewChange(view, record) { + view.context.$implicit = record.item; + } + + function getTypeName(type) { + return type["name"] || typeof type; + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * A structural directive that conditionally includes a template based on the value of + * an expression coerced to Boolean. + * When the expression evaluates to true, Angular renders the template + * provided in a `then` clause, and when false or null, + * Angular renders the template provided in an optional `else` clause. The default + * template for the `else` clause is blank. + * + * A [shorthand form](guide/structural-directives#asterisk) of the directive, + * `*ngIf="condition"`, is generally used, provided + * as an attribute of the anchor element for the inserted template. + * Angular expands this into a more explicit version, in which the anchor element + * is contained in an `` element. + * + * Simple form with shorthand syntax: + * + * ``` + *
    Content to render when condition is true.
    + * ``` + * + * Simple form with expanded syntax: + * + * ``` + *
    Content to render when condition is + * true.
    + * ``` + * + * Form with an "else" block: + * + * ``` + *
    Content to render when condition is true.
    + * Content to render when condition is false. + * ``` + * + * Shorthand form with "then" and "else" blocks: + * + * ``` + *
    + * Content to render when condition is true. + * Content to render when condition is false. + * ``` + * + * Form with storing the value locally: + * + * ``` + *
    {{value}}
    + * Content to render when value is null. + * ``` + * + * @usageNotes + * + * The `*ngIf` directive is most commonly used to conditionally show an inline template, + * as seen in the following example. + * The default `else` template is blank. + * + * {@example common/ngIf/ts/module.ts region='NgIfSimple'} + * + * ### Showing an alternative template using `else` + * + * To display a template when `expression` evaluates to false, use an `else` template + * binding as shown in the following example. + * The `else` binding points to an `` element labeled `#elseBlock`. + * The template can be defined anywhere in the component view, but is typically placed right after + * `ngIf` for readability. + * + * {@example common/ngIf/ts/module.ts region='NgIfElse'} + * + * ### Using an external `then` template + * + * In the previous example, the then-clause template is specified inline, as the content of the + * tag that contains the `ngIf` directive. You can also specify a template that is defined + * externally, by referencing a labeled `` element. When you do this, you can + * change which template to use at runtime, as shown in the following example. + * + * {@example common/ngIf/ts/module.ts region='NgIfThenElse'} + * + * ### Storing a conditional result in a variable + * + * You might want to show a set of properties from the same object. If you are waiting + * for asynchronous data, the object can be undefined. + * In this case, you can use `ngIf` and store the result of the condition in a local + * variable as shown in the following example. + * + * {@example common/ngIf/ts/module.ts region='NgIfAs'} + * + * This code uses only one `AsyncPipe`, so only one subscription is created. + * The conditional statement stores the result of `userStream|async` in the local variable `user`. + * You can then bind the local `user` repeatedly. + * + * The conditional displays the data only if `userStream` returns a value, + * so you don't need to use the + * safe-navigation-operator (`?.`) + * to guard against null values when accessing properties. + * You can display an alternative template while waiting for the data. + * + * ### Shorthand syntax + * + * The shorthand syntax `*ngIf` expands into two separate template specifications + * for the "then" and "else" clauses. For example, consider the following shorthand statement, + * that is meant to show a loading page while waiting for data to be loaded. + * + * ``` + *
    + * ... + *
    + * + * + *
    Loading...
    + *
    + * ``` + * + * You can see that the "else" clause references the `` + * with the `#loading` label, and the template for the "then" clause + * is provided as the content of the anchor element. + * + * However, when Angular expands the shorthand syntax, it creates + * another `` tag, with `ngIf` and `ngIfElse` directives. + * The anchor element containing the template for the "then" clause becomes + * the content of this unlabeled `` tag. + * + * ``` + * + *
    + * ... + *
    + *
    + * + * + *
    Loading...
    + *
    + * ``` + * + * The presence of the implicit template object has implications for the nesting of + * structural directives. For more on this subject, see + * [Structural Directives](guide/structural-directives#one-per-element). + * + * @ngModule CommonModule + * @publicApi + */ + + class NgIf { + constructor(_viewContainer, templateRef) { + this._viewContainer = _viewContainer; + this._context = new NgIfContext(); + this._thenTemplateRef = null; + this._elseTemplateRef = null; + this._thenViewRef = null; + this._elseViewRef = null; + this._thenTemplateRef = templateRef; + } + /** + * The Boolean expression to evaluate as the condition for showing a template. + */ + + set ngIf(condition) { + this._context.$implicit = this._context.ngIf = condition; + + this._updateView(); + } + /** + * A template to show if the condition expression evaluates to true. + */ + + set ngIfThen(templateRef) { + assertTemplate("ngIfThen", templateRef); + this._thenTemplateRef = templateRef; + this._thenViewRef = null; // clear previous view if any. + + this._updateView(); + } + /** + * A template to show if the condition expression evaluates to false. + */ + + set ngIfElse(templateRef) { + assertTemplate("ngIfElse", templateRef); + this._elseTemplateRef = templateRef; + this._elseViewRef = null; // clear previous view if any. + + this._updateView(); + } + + _updateView() { + if (this._context.$implicit) { + if (!this._thenViewRef) { + this._viewContainer.clear(); + + this._elseViewRef = null; + + if (this._thenTemplateRef) { + this._thenViewRef = this._viewContainer.createEmbeddedView( + this._thenTemplateRef, + this._context + ); + } + } + } else { + if (!this._elseViewRef) { + this._viewContainer.clear(); + + this._thenViewRef = null; + + if (this._elseTemplateRef) { + this._elseViewRef = this._viewContainer.createEmbeddedView( + this._elseTemplateRef, + this._context + ); + } + } + } + } + /** + * Asserts the correct type of the context for the template that `NgIf` will render. + * + * The presence of this method is a signal to the Ivy template type-check compiler that the + * `NgIf` structural directive renders its template with a specific context type. + */ + + static ngTemplateContextGuard(dir, ctx) { + return true; + } + } + + NgIf.ɵfac = function NgIf_Factory(t) { + return new (t || NgIf)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef + ) + ); + }; + + NgIf.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ + type: NgIf, + selectors: [["", "ngIf", ""]], + inputs: { + ngIf: "ngIf", + ngIfThen: "ngIfThen", + ngIfElse: "ngIfElse" + } + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgIf, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngIf]" + } + ] + } + ], + function() { + return [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef + } + ]; + }, + { + ngIf: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ], + ngIfThen: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ], + ngIfElse: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ] + } + ); + })(); + /** + * @publicApi + */ + + class NgIfContext { + constructor() { + this.$implicit = null; + this.ngIf = null; + } + } + + function assertTemplate(property, templateRef) { + const isTemplateRefOrNull = !!(!templateRef || templateRef.createEmbeddedView); + + if (!isTemplateRefOrNull) { + throw new Error( + `${property} must be a TemplateRef, but received '${(0, + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵstringify"])(templateRef)}'.` + ); + } + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + class SwitchView { + constructor(_viewContainerRef, _templateRef) { + this._viewContainerRef = _viewContainerRef; + this._templateRef = _templateRef; + this._created = false; + } + + create() { + this._created = true; + + this._viewContainerRef.createEmbeddedView(this._templateRef); + } + + destroy() { + this._created = false; + + this._viewContainerRef.clear(); + } + + enforceState(created) { + if (created && !this._created) { + this.create(); + } else if (!created && this._created) { + this.destroy(); + } + } + } + /** + * @ngModule CommonModule + * + * @description + * The `[ngSwitch]` directive on a container specifies an expression to match against. + * The expressions to match are provided by `ngSwitchCase` directives on views within the container. + * - Every view that matches is rendered. + * - If there are no matches, a view with the `ngSwitchDefault` directive is rendered. + * - Elements within the `[NgSwitch]` statement but outside of any `NgSwitchCase` + * or `ngSwitchDefault` directive are preserved at the location. + * + * @usageNotes + * Define a container element for the directive, and specify the switch expression + * to match against as an attribute: + * + * ``` + * + * ``` + * + * Within the container, `*ngSwitchCase` statements specify the match expressions + * as attributes. Include `*ngSwitchDefault` as the final case. + * + * ``` + * + * ... + * ... + * ... + * + * ``` + * + * ### Usage Examples + * + * The following example shows how to use more than one case to display the same view: + * + * ``` + * + * + * ... + * ... + * ... + * + * ... + * + * ``` + * + * The following example shows how cases can be nested: + * ``` + * + * ... + * ... + * ... + * + * + * + * + * + * ... + * + * ``` + * + * @publicApi + * @see `NgSwitchCase` + * @see `NgSwitchDefault` + * @see [Structural Directives](guide/structural-directives) + * + */ + + class NgSwitch { + constructor() { + this._defaultUsed = false; + this._caseCount = 0; + this._lastCaseCheckIndex = 0; + this._lastCasesMatched = false; + } + + set ngSwitch(newValue) { + this._ngSwitch = newValue; + + if (this._caseCount === 0) { + this._updateDefaultCases(true); + } + } + /** @internal */ + + _addCase() { + return this._caseCount++; + } + /** @internal */ + + _addDefault(view) { + if (!this._defaultViews) { + this._defaultViews = []; + } + + this._defaultViews.push(view); + } + /** @internal */ + + _matchCase(value) { + const matched = value == this._ngSwitch; + this._lastCasesMatched = this._lastCasesMatched || matched; + this._lastCaseCheckIndex++; + + if (this._lastCaseCheckIndex === this._caseCount) { + this._updateDefaultCases(!this._lastCasesMatched); + + this._lastCaseCheckIndex = 0; + this._lastCasesMatched = false; + } + + return matched; + } + + _updateDefaultCases(useDefault) { + if (this._defaultViews && useDefault !== this._defaultUsed) { + this._defaultUsed = useDefault; + + for (let i = 0; i < this._defaultViews.length; i++) { + const defaultView = this._defaultViews[i]; + defaultView.enforceState(useDefault); + } + } + } + } + + NgSwitch.ɵfac = function NgSwitch_Factory(t) { + return new (t || NgSwitch)(); + }; + + NgSwitch.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ + type: NgSwitch, + selectors: [["", "ngSwitch", ""]], + inputs: { + ngSwitch: "ngSwitch" + } + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgSwitch, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngSwitch]" + } + ] + } + ], + null, + { + ngSwitch: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ] + } + ); + })(); + /** + * @ngModule CommonModule + * + * @description + * Provides a switch case expression to match against an enclosing `ngSwitch` expression. + * When the expressions match, the given `NgSwitchCase` template is rendered. + * If multiple match expressions match the switch expression value, all of them are displayed. + * + * @usageNotes + * + * Within a switch container, `*ngSwitchCase` statements specify the match expressions + * as attributes. Include `*ngSwitchDefault` as the final case. + * + * ``` + * + * ... + * ... + * ... + * + * ``` + * + * Each switch-case statement contains an in-line HTML template or template reference + * that defines the subtree to be selected if the value of the match expression + * matches the value of the switch expression. + * + * Unlike JavaScript, which uses strict equality, Angular uses loose equality. + * This means that the empty string, `""` matches 0. + * + * @publicApi + * @see `NgSwitch` + * @see `NgSwitchDefault` + * + */ + + class NgSwitchCase { + constructor(viewContainer, templateRef, ngSwitch) { + this.ngSwitch = ngSwitch; + + if ((typeof ngDevMode === "undefined" || ngDevMode) && !ngSwitch) { + throwNgSwitchProviderNotFoundError("ngSwitchCase", "NgSwitchCase"); + } + + ngSwitch._addCase(); + + this._view = new SwitchView(viewContainer, templateRef); + } + /** + * Performs case matching. For internal use only. + * @nodoc + */ + + ngDoCheck() { + this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); + } + } + + NgSwitchCase.ɵfac = function NgSwitchCase_Factory(t) { + return new (t || NgSwitchCase)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgSwitch, 9) + ); + }; + + NgSwitchCase.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ + type: NgSwitchCase, + selectors: [["", "ngSwitchCase", ""]], + inputs: { + ngSwitchCase: "ngSwitchCase" + } + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgSwitchCase, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngSwitchCase]" + } + ] + } + ], + function() { + return [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef + }, + { + type: NgSwitch, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host + } + ] + } + ]; + }, + { + ngSwitchCase: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ] + } + ); + })(); + /** + * @ngModule CommonModule + * + * @description + * + * Creates a view that is rendered when no `NgSwitchCase` expressions + * match the `NgSwitch` expression. + * This statement should be the final case in an `NgSwitch`. + * + * @publicApi + * @see `NgSwitch` + * @see `NgSwitchCase` + * + */ + + class NgSwitchDefault { + constructor(viewContainer, templateRef, ngSwitch) { + if ((typeof ngDevMode === "undefined" || ngDevMode) && !ngSwitch) { + throwNgSwitchProviderNotFoundError("ngSwitchDefault", "NgSwitchDefault"); + } + + ngSwitch._addDefault(new SwitchView(viewContainer, templateRef)); + } + } + + NgSwitchDefault.ɵfac = function NgSwitchDefault_Factory(t) { + return new (t || NgSwitchDefault)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgSwitch, 9) + ); + }; + + NgSwitchDefault.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ + type: NgSwitchDefault, + selectors: [["", "ngSwitchDefault", ""]] + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgSwitchDefault, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngSwitchDefault]" + } + ] + } + ], + function() { + return [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef + }, + { + type: NgSwitch, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host + } + ] + } + ]; + }, + null + ); + })(); + + function throwNgSwitchProviderNotFoundError(attrName, directiveName) { + throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"]( + 2000, + /* PARENT_NG_SWITCH_NOT_FOUND */ + `An element with the "${attrName}" attribute ` + + `(matching the "${directiveName}" directive) must be located inside an element with the "ngSwitch" attribute ` + + `(matching "NgSwitch" directive)` + ); + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @ngModule CommonModule + * + * @usageNotes + * ``` + * + * there is nothing + * there is one + * there are a few + * + * ``` + * + * @description + * + * Adds / removes DOM sub-trees based on a numeric value. Tailored for pluralization. + * + * Displays DOM sub-trees that match the switch expression value, or failing that, DOM sub-trees + * that match the switch expression's pluralization category. + * + * To use this directive you must provide a container element that sets the `[ngPlural]` attribute + * to a switch expression. Inner elements with a `[ngPluralCase]` will display based on their + * expression: + * - if `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value + * matches the switch expression exactly, + * - otherwise, the view will be treated as a "category match", and will only display if exact + * value matches aren't found and the value maps to its category for the defined locale. + * + * See http://cldr.unicode.org/index/cldr-spec/plural-rules + * + * @publicApi + */ + + class NgPlural { + constructor(_localization) { + this._localization = _localization; + this._caseViews = {}; + } + + set ngPlural(value) { + this._switchValue = value; + + this._updateView(); + } + + addCase(value, switchView) { + this._caseViews[value] = switchView; + } + + _updateView() { + this._clearViews(); + + const cases = Object.keys(this._caseViews); + const key = getPluralCategory(this._switchValue, cases, this._localization); + + this._activateView(this._caseViews[key]); + } + + _clearViews() { + if (this._activeView) this._activeView.destroy(); + } + + _activateView(view) { + if (view) { + this._activeView = view; + + this._activeView.create(); + } + } + } + + NgPlural.ɵfac = function NgPlural_Factory(t) { + return new (t || NgPlural)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgLocalization) + ); + }; + + NgPlural.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ + type: NgPlural, + selectors: [["", "ngPlural", ""]], + inputs: { + ngPlural: "ngPlural" + } + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgPlural, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngPlural]" + } + ] + } + ], + function() { + return [ + { + type: NgLocalization + } + ]; + }, + { + ngPlural: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ] + } + ); + })(); + /** + * @ngModule CommonModule + * + * @description + * + * Creates a view that will be added/removed from the parent {@link NgPlural} when the + * given expression matches the plural expression according to CLDR rules. + * + * @usageNotes + * ``` + * + * ... + * ... + * + *``` + * + * See {@link NgPlural} for more details and example. + * + * @publicApi + */ + + class NgPluralCase { + constructor(value, template, viewContainer, ngPlural) { + this.value = value; + const isANumber = !isNaN(Number(value)); + ngPlural.addCase(isANumber ? `=${value}` : value, new SwitchView(viewContainer, template)); + } + } + + NgPluralCase.ɵfac = function NgPluralCase_Factory(t) { + return new (t || NgPluralCase)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinjectAttribute"]("ngPluralCase"), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgPlural, 1) + ); + }; + + NgPluralCase.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ + type: NgPluralCase, + selectors: [["", "ngPluralCase", ""]] + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgPluralCase, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngPluralCase]" + } + ] + } + ], + function() { + return [ + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Attribute, + args: ["ngPluralCase"] + } + ] + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + }, + { + type: NgPlural, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host + } + ] + } + ]; + }, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @ngModule CommonModule + * + * @usageNotes + * + * Set the font of the containing element to the result of an expression. + * + * ``` + * ... + * ``` + * + * Set the width of the containing element to a pixel value returned by an expression. + * + * ``` + * ... + * ``` + * + * Set a collection of style values using an expression that returns key-value pairs. + * + * ``` + * ... + * ``` + * + * @description + * + * An attribute directive that updates styles for the containing HTML element. + * Sets one or more style properties, specified as colon-separated key-value pairs. + * The key is a style name, with an optional `.` suffix + * (such as 'top.px', 'font-style.em'). + * The value is an expression to be evaluated. + * The resulting non-null value, expressed in the given unit, + * is assigned to the given style property. + * If the result of evaluation is null, the corresponding style is removed. + * + * @publicApi + */ + + class NgStyle { + constructor(_ngEl, _differs, _renderer) { + this._ngEl = _ngEl; + this._differs = _differs; + this._renderer = _renderer; + this._ngStyle = null; + this._differ = null; + } + + set ngStyle(values) { + this._ngStyle = values; + + if (!this._differ && values) { + this._differ = this._differs.find(values).create(); + } + } + + ngDoCheck() { + if (this._differ) { + const changes = this._differ.diff(this._ngStyle); + + if (changes) { + this._applyChanges(changes); + } + } + } + + _setStyle(nameAndUnit, value) { + const [name, unit] = nameAndUnit.split("."); + value = value != null && unit ? `${value}${unit}` : value; + + if (value != null) { + this._renderer.setStyle(this._ngEl.nativeElement, name, value); + } else { + this._renderer.removeStyle(this._ngEl.nativeElement, name); + } + } + + _applyChanges(changes) { + changes.forEachRemovedItem(record => this._setStyle(record.key, null)); + changes.forEachAddedItem(record => this._setStyle(record.key, record.currentValue)); + changes.forEachChangedItem(record => this._setStyle(record.key, record.currentValue)); + } + } + + NgStyle.ɵfac = function NgStyle_Factory(t) { + return new (t || NgStyle)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 + ) + ); + }; + + NgStyle.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ + type: NgStyle, + selectors: [["", "ngStyle", ""]], + inputs: { + ngStyle: "ngStyle" + } + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgStyle, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngStyle]" + } + ] + } + ], + function() { + return [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 + } + ]; + }, + { + ngStyle: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, + args: ["ngStyle"] + } + ] + } + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @ngModule CommonModule + * + * @description + * + * Inserts an embedded view from a prepared `TemplateRef`. + * + * You can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`. + * `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding + * by the local template `let` declarations. + * + * @usageNotes + * ``` + * + * ``` + * + * Using the key `$implicit` in the context object will set its value as default. + * + * ### Example + * + * {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'} + * + * @publicApi + */ + + class NgTemplateOutlet { + constructor(_viewContainerRef) { + this._viewContainerRef = _viewContainerRef; + this._viewRef = null; + /** + * A context object to attach to the {@link EmbeddedViewRef}. This should be an + * object, the object's keys will be available for binding by the local template `let` + * declarations. + * Using the key `$implicit` in the context object will set its value as default. + */ + + this.ngTemplateOutletContext = null; + /** + * A string defining the template reference and optionally the context object for the template. + */ + + this.ngTemplateOutlet = null; + } + /** @nodoc */ + + ngOnChanges(changes) { + if (changes["ngTemplateOutlet"]) { + const viewContainerRef = this._viewContainerRef; + + if (this._viewRef) { + viewContainerRef.remove(viewContainerRef.indexOf(this._viewRef)); + } + + this._viewRef = this.ngTemplateOutlet + ? viewContainerRef.createEmbeddedView( + this.ngTemplateOutlet, + this.ngTemplateOutletContext + ) + : null; + } else if ( + this._viewRef && + changes["ngTemplateOutletContext"] && + this.ngTemplateOutletContext + ) { + this._viewRef.context = this.ngTemplateOutletContext; + } + } + } + + NgTemplateOutlet.ɵfac = function NgTemplateOutlet_Factory(t) { + return new (t || NgTemplateOutlet)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + ) + ); + }; + + NgTemplateOutlet.ɵdir = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]( + { + type: NgTemplateOutlet, + selectors: [["", "ngTemplateOutlet", ""]], + inputs: { + ngTemplateOutletContext: "ngTemplateOutletContext", + ngTemplateOutlet: "ngTemplateOutlet" + }, + features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] + } + ); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + NgTemplateOutlet, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, + args: [ + { + selector: "[ngTemplateOutlet]" + } + ] + } + ], + function() { + return [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef + } + ]; + }, + { + ngTemplateOutletContext: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ], + ngTemplateOutlet: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input + } + ] + } + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * A collection of Angular directives that are likely to be used in each and every Angular + * application. + */ + + const COMMON_DIRECTIVES = [ + NgClass, + NgComponentOutlet, + NgForOf, + NgIf, + NgTemplateOutlet, + NgStyle, + NgSwitch, + NgSwitchCase, + NgSwitchDefault, + NgPlural, + NgPluralCase + ]; + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + function invalidPipeArgumentError(type, value) { + const errorMessage = + typeof ngDevMode === "undefined" || ngDevMode + ? `InvalidPipeArgument: '${value}' for pipe '${(0, + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵstringify"])(type)}'` + : ""; + return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"]( + 2100, + /* INVALID_PIPE_ARGUMENT */ + errorMessage + ); + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + class SubscribableStrategy { + createSubscription(async, updateLatestValue) { + return async.subscribe({ + next: updateLatestValue, + error: e => { + throw e; + } + }); + } + + dispose(subscription) { + subscription.unsubscribe(); + } + + onDestroy(subscription) { + subscription.unsubscribe(); + } + } + + class PromiseStrategy { + createSubscription(async, updateLatestValue) { + return async.then(updateLatestValue, e => { + throw e; + }); + } + + dispose(subscription) {} + + onDestroy(subscription) {} + } + + const _promiseStrategy = new PromiseStrategy(); + + const _subscribableStrategy = new SubscribableStrategy(); + /** + * @ngModule CommonModule + * @description + * + * Unwraps a value from an asynchronous primitive. + * + * The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has + * emitted. When a new value is emitted, the `async` pipe marks the component to be checked for + * changes. When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid + * potential memory leaks. When the reference of the expression changes, the `async` pipe + * automatically unsubscribes from the old `Observable` or `Promise` and subscribes to the new one. + * + * @usageNotes + * + * ### Examples + * + * This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the + * promise. + * + * {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'} + * + * It's also possible to use `async` with Observables. The example below binds the `time` Observable + * to the view. The Observable continuously updates the view with the current time. + * + * {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'} + * + * @publicApi + */ + + class AsyncPipe { + constructor(_ref) { + this._ref = _ref; + this._latestValue = null; + this._subscription = null; + this._obj = null; + this._strategy = null; + } + + ngOnDestroy() { + if (this._subscription) { + this._dispose(); + } + } + + transform(obj) { + if (!this._obj) { + if (obj) { + this._subscribe(obj); + } + + return this._latestValue; + } + + if (obj !== this._obj) { + this._dispose(); + + return this.transform(obj); + } + + return this._latestValue; + } + + _subscribe(obj) { + this._obj = obj; + this._strategy = this._selectStrategy(obj); + this._subscription = this._strategy.createSubscription(obj, value => + this._updateLatestValue(obj, value) + ); + } + + _selectStrategy(obj) { + if ((0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisPromise"])(obj)) { + return _promiseStrategy; + } + + if ((0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisSubscribable"])(obj)) { + return _subscribableStrategy; + } + + throw invalidPipeArgumentError(AsyncPipe, obj); + } + + _dispose() { + this._strategy.dispose(this._subscription); + + this._latestValue = null; + this._subscription = null; + this._obj = null; + } + + _updateLatestValue(async, value) { + if (async === this._obj) { + this._latestValue = value; + + this._ref.markForCheck(); + } + } + } + + AsyncPipe.ɵfac = function AsyncPipe_Factory(t) { + return new (t || AsyncPipe)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.ChangeDetectorRef, + 16 + ) + ); + }; + + AsyncPipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "async", + type: AsyncPipe, + pure: false + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + AsyncPipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "async", + pure: false + } + ] + } + ], + function() { + return [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ChangeDetectorRef + } + ]; + }, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Transforms text to all lower case. + * + * @see `UpperCasePipe` + * @see `TitleCasePipe` + * @usageNotes + * + * The following example defines a view that allows the user to enter + * text, and then uses the pipe to convert the input text to all lower case. + * + * + * + * @ngModule CommonModule + * @publicApi + */ + + class LowerCasePipe { + transform(value) { + if (value == null) return null; + + if (typeof value !== "string") { + throw invalidPipeArgumentError(LowerCasePipe, value); + } + + return value.toLowerCase(); + } + } + + LowerCasePipe.ɵfac = function LowerCasePipe_Factory(t) { + return new (t || LowerCasePipe)(); + }; + + LowerCasePipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "lowercase", + type: LowerCasePipe, + pure: true + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + LowerCasePipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "lowercase" + } + ] + } + ], + null, + null + ); + })(); // + // Regex below matches any Unicode word and number compatible with ES5. In ES2018 the same result + // can be achieved by using /[0-9\p{L}]\S*/gu and also known as Unicode Property Escapes + // (https://2ality.com/2017/07/regexp-unicode-property-escapes.html). Since there is no + // transpilation of this functionality down to ES5 without external tool, the only solution is + // to use already transpiled form. Example can be found here - + // https://mothereff.in/regexpu#input=var+regex+%3D+%2F%5B0-9%5Cp%7BL%7D%5D%5CS*%2Fgu%3B%0A%0A&unicodePropertyEscape=1 + // + + const unicodeWordMatch = /(?:[0-9A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088E\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7CA\uA7D0\uA7D1\uA7D3\uA7D5-\uA7D9\uA7F2-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDD00-\uDD23\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF1C\uDF27\uDF30-\uDF45\uDF70-\uDF81\uDFB0-\uDFC4\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDEB8\uDF00-\uDF1A\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCDF\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDEE0-\uDEF2\uDFB0]|\uD808[\uDC00-\uDF99]|\uD809[\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE70-\uDEBE\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE7F\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD837[\uDF00-\uDF1E]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB]|\uD839[\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43\uDD4B]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF38\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])\S*/g; + /** + * Transforms text to title case. + * Capitalizes the first letter of each word and transforms the + * rest of the word to lower case. + * Words are delimited by any whitespace character, such as a space, tab, or line-feed character. + * + * @see `LowerCasePipe` + * @see `UpperCasePipe` + * + * @usageNotes + * The following example shows the result of transforming various strings into title case. + * + * + * + * @ngModule CommonModule + * @publicApi + */ + + class TitleCasePipe { + transform(value) { + if (value == null) return null; + + if (typeof value !== "string") { + throw invalidPipeArgumentError(TitleCasePipe, value); + } + + return value.replace( + unicodeWordMatch, + txt => txt[0].toUpperCase() + txt.substr(1).toLowerCase() + ); + } + } + + TitleCasePipe.ɵfac = function TitleCasePipe_Factory(t) { + return new (t || TitleCasePipe)(); + }; + + TitleCasePipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "titlecase", + type: TitleCasePipe, + pure: true + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + TitleCasePipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "titlecase" + } + ] + } + ], + null, + null + ); + })(); + /** + * Transforms text to all upper case. + * @see `LowerCasePipe` + * @see `TitleCasePipe` + * + * @ngModule CommonModule + * @publicApi + */ + + class UpperCasePipe { + transform(value) { + if (value == null) return null; + + if (typeof value !== "string") { + throw invalidPipeArgumentError(UpperCasePipe, value); + } + + return value.toUpperCase(); + } + } + + UpperCasePipe.ɵfac = function UpperCasePipe_Factory(t) { + return new (t || UpperCasePipe)(); + }; + + UpperCasePipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "uppercase", + type: UpperCasePipe, + pure: true + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + UpperCasePipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "uppercase" + } + ] + } + ], + null, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Optionally-provided default timezone to use for all instances of `DatePipe` (such as `'+0430'`). + * If the value isn't provided, the `DatePipe` will use the end-user's local system timezone. + */ + + const DATE_PIPE_DEFAULT_TIMEZONE = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken( + "DATE_PIPE_DEFAULT_TIMEZONE" + ); // clang-format off + + /** + * @ngModule CommonModule + * @description + * + * Formats a date value according to locale rules. + * + * `DatePipe` is executed only when it detects a pure change to the input value. + * A pure change is either a change to a primitive input value + * (such as `String`, `Number`, `Boolean`, or `Symbol`), + * or a changed object reference (such as `Date`, `Array`, `Function`, or `Object`). + * + * Note that mutating a `Date` object does not cause the pipe to be rendered again. + * To ensure that the pipe is executed, you must create a new `Date` object. + * + * Only the `en-US` locale data comes with Angular. To localize dates + * in another language, you must import the corresponding locale data. + * See the [I18n guide](guide/i18n-common-format-data-locale) for more information. + * + * The time zone of the formatted value can be specified either by passing it in as the second + * parameter of the pipe, or by setting the default through the `DATE_PIPE_DEFAULT_TIMEZONE` + * injection token. The value that is passed in as the second parameter takes precedence over + * the one defined using the injection token. + * + * @see `formatDate()` + * + * + * @usageNotes + * + * The result of this pipe is not reevaluated when the input is mutated. To avoid the need to + * reformat the date on every change-detection cycle, treat the date as an immutable object + * and change the reference when the pipe needs to run again. + * + * ### Pre-defined format options + * + * | Option | Equivalent to | Examples (given in `en-US` locale) | + * |---------------|-------------------------------------|-------------------------------------------------| + * | `'short'` | `'M/d/yy, h:mm a'` | `6/15/15, 9:03 AM` | + * | `'medium'` | `'MMM d, y, h:mm:ss a'` | `Jun 15, 2015, 9:03:01 AM` | + * | `'long'` | `'MMMM d, y, h:mm:ss a z'` | `June 15, 2015 at 9:03:01 AM GMT+1` | + * | `'full'` | `'EEEE, MMMM d, y, h:mm:ss a zzzz'` | `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00` | + * | `'shortDate'` | `'M/d/yy'` | `6/15/15` | + * | `'mediumDate'`| `'MMM d, y'` | `Jun 15, 2015` | + * | `'longDate'` | `'MMMM d, y'` | `June 15, 2015` | + * | `'fullDate'` | `'EEEE, MMMM d, y'` | `Monday, June 15, 2015` | + * | `'shortTime'` | `'h:mm a'` | `9:03 AM` | + * | `'mediumTime'`| `'h:mm:ss a'` | `9:03:01 AM` | + * | `'longTime'` | `'h:mm:ss a z'` | `9:03:01 AM GMT+1` | + * | `'fullTime'` | `'h:mm:ss a zzzz'` | `9:03:01 AM GMT+01:00` | + * + * ### Custom format options + * + * You can construct a format string using symbols to specify the components + * of a date-time value, as described in the following table. + * Format details depend on the locale. + * Fields marked with (*) are only available in the extra data set for the given locale. + * + * | Field type | Format | Description | Example Value | + * |-------------------- |-------------|---------------------------------------------------------------|------------------------------------------------------------| + * | Era | G, GG & GGG | Abbreviated | AD | + * | | GGGG | Wide | Anno Domini | + * | | GGGGG | Narrow | A | + * | Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | + * | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | + * | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | + * | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | + * | Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | + * | | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | + * | | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | + * | | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | + * | Month | M | Numeric: 1 digit | 9, 12 | + * | | MM | Numeric: 2 digits + zero padded | 09, 12 | + * | | MMM | Abbreviated | Sep | + * | | MMMM | Wide | September | + * | | MMMMM | Narrow | S | + * | Month standalone | L | Numeric: 1 digit | 9, 12 | + * | | LL | Numeric: 2 digits + zero padded | 09, 12 | + * | | LLL | Abbreviated | Sep | + * | | LLLL | Wide | September | + * | | LLLLL | Narrow | S | + * | Week of year | w | Numeric: minimum digits | 1... 53 | + * | | ww | Numeric: 2 digits + zero padded | 01... 53 | + * | Week of month | W | Numeric: 1 digit | 1... 5 | + * | Day of month | d | Numeric: minimum digits | 1 | + * | | dd | Numeric: 2 digits + zero padded | 01 | + * | Week day | E, EE & EEE | Abbreviated | Tue | + * | | EEEE | Wide | Tuesday | + * | | EEEEE | Narrow | T | + * | | EEEEEE | Short | Tu | + * | Week day standalone | c, cc | Numeric: 1 digit | 2 | + * | | ccc | Abbreviated | Tue | + * | | cccc | Wide | Tuesday | + * | | ccccc | Narrow | T | + * | | cccccc | Short | Tu | + * | Period | a, aa & aaa | Abbreviated | am/pm or AM/PM | + * | | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem | + * | | aaaaa | Narrow | a/p | + * | Period* | B, BB & BBB | Abbreviated | mid. | + * | | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | + * | | BBBBB | Narrow | md | + * | Period standalone* | b, bb & bbb | Abbreviated | mid. | + * | | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | + * | | bbbbb | Narrow | md | + * | Hour 1-12 | h | Numeric: minimum digits | 1, 12 | + * | | hh | Numeric: 2 digits + zero padded | 01, 12 | + * | Hour 0-23 | H | Numeric: minimum digits | 0, 23 | + * | | HH | Numeric: 2 digits + zero padded | 00, 23 | + * | Minute | m | Numeric: minimum digits | 8, 59 | + * | | mm | Numeric: 2 digits + zero padded | 08, 59 | + * | Second | s | Numeric: minimum digits | 0... 59 | + * | | ss | Numeric: 2 digits + zero padded | 00... 59 | + * | Fractional seconds | S | Numeric: 1 digit | 0... 9 | + * | | SS | Numeric: 2 digits + zero padded | 00... 99 | + * | | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 | + * | Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 | + * | | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 | + * | | Z, ZZ & ZZZ | ISO8601 basic format | -0800 | + * | | ZZZZ | Long localized GMT format | GMT-8:00 | + * | | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 | + * | | O, OO & OOO | Short localized GMT format | GMT-8 | + * | | OOOO | Long localized GMT format | GMT-08:00 | + * + * + * ### Format examples + * + * These examples transform a date into various formats, + * assuming that `dateObj` is a JavaScript `Date` object for + * year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11, + * given in the local time for the `en-US` locale. + * + * ``` + * {{ dateObj | date }} // output is 'Jun 15, 2015' + * {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM' + * {{ dateObj | date:'shortTime' }} // output is '9:43 PM' + * {{ dateObj | date:'mm:ss' }} // output is '43:11' + * ``` + * + * ### Usage example + * + * The following component uses a date pipe to display the current date in different formats. + * + * ``` + * @Component({ + * selector: 'date-pipe', + * template: `
    + *

    Today is {{today | date}}

    + *

    Or if you prefer, {{today | date:'fullDate'}}

    + *

    The time is {{today | date:'h:mm a z'}}

    + *
    ` + * }) + * // Get the current date and time as a date-time value. + * export class DatePipeComponent { + * today: number = Date.now(); + * } + * ``` + * + * @publicApi + */ + // clang-format on + + class DatePipe { + constructor(locale, defaultTimezone) { + this.locale = locale; + this.defaultTimezone = defaultTimezone; + } + + transform(value, format = "mediumDate", timezone, locale) { + var _a; + + if (value == null || value === "" || value !== value) return null; + + try { + return formatDate( + value, + format, + locale || this.locale, + (_a = timezone !== null && timezone !== void 0 ? timezone : this.defaultTimezone) !== + null && _a !== void 0 + ? _a + : undefined + ); + } catch (error) { + throw invalidPipeArgumentError(DatePipe, error.message); + } + } + } + + DatePipe.ɵfac = function DatePipe_Factory(t) { + return new (t || DatePipe)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID, + 16 + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](DATE_PIPE_DEFAULT_TIMEZONE, 24) + ); + }; + + DatePipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "date", + type: DatePipe, + pure: true + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + DatePipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "date", + pure: true + } + ] + } + ], + function() { + return [ + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, + args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] + } + ] + }, + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, + args: [DATE_PIPE_DEFAULT_TIMEZONE] + }, + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional + } + ] + } + ]; + }, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + const _INTERPOLATION_REGEXP = /#/g; + /** + * @ngModule CommonModule + * @description + * + * Maps a value to a string that pluralizes the value according to locale rules. + * + * @usageNotes + * + * ### Example + * + * {@example common/pipes/ts/i18n_pipe.ts region='I18nPluralPipeComponent'} + * + * @publicApi + */ + + class I18nPluralPipe { + constructor(_localization) { + this._localization = _localization; + } + /** + * @param value the number to be formatted + * @param pluralMap an object that mimics the ICU format, see + * http://userguide.icu-project.org/formatparse/messages. + * @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by + * default). + */ + + transform(value, pluralMap, locale) { + if (value == null) return ""; + + if (typeof pluralMap !== "object" || pluralMap === null) { + throw invalidPipeArgumentError(I18nPluralPipe, pluralMap); + } + + const key = getPluralCategory(value, Object.keys(pluralMap), this._localization, locale); + return pluralMap[key].replace(_INTERPOLATION_REGEXP, value.toString()); + } + } + + I18nPluralPipe.ɵfac = function I18nPluralPipe_Factory(t) { + return new (t || I18nPluralPipe)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgLocalization, 16) + ); + }; + + I18nPluralPipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "i18nPlural", + type: I18nPluralPipe, + pure: true + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + I18nPluralPipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "i18nPlural", + pure: true + } + ] + } + ], + function() { + return [ + { + type: NgLocalization + } + ]; + }, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @ngModule CommonModule + * @description + * + * Generic selector that displays the string that matches the current value. + * + * If none of the keys of the `mapping` match the `value`, then the content + * of the `other` key is returned when present, otherwise an empty string is returned. + * + * @usageNotes + * + * ### Example + * + * {@example common/pipes/ts/i18n_pipe.ts region='I18nSelectPipeComponent'} + * + * @publicApi + */ + + class I18nSelectPipe { + /** + * @param value a string to be internationalized. + * @param mapping an object that indicates the text that should be displayed + * for different values of the provided `value`. + */ + transform(value, mapping) { + if (value == null) return ""; + + if (typeof mapping !== "object" || typeof value !== "string") { + throw invalidPipeArgumentError(I18nSelectPipe, mapping); + } + + if (mapping.hasOwnProperty(value)) { + return mapping[value]; + } + + if (mapping.hasOwnProperty("other")) { + return mapping["other"]; + } + + return ""; + } + } + + I18nSelectPipe.ɵfac = function I18nSelectPipe_Factory(t) { + return new (t || I18nSelectPipe)(); + }; + + I18nSelectPipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "i18nSelect", + type: I18nSelectPipe, + pure: true + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + I18nSelectPipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "i18nSelect", + pure: true + } + ] + } + ], + null, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @ngModule CommonModule + * @description + * + * Converts a value into its JSON-format representation. Useful for debugging. + * + * @usageNotes + * + * The following component uses a JSON pipe to convert an object + * to JSON format, and displays the string in both formats for comparison. + * + * {@example common/pipes/ts/json_pipe.ts region='JsonPipe'} + * + * @publicApi + */ + + class JsonPipe { + /** + * @param value A value of any type to convert into a JSON-format string. + */ + transform(value) { + return JSON.stringify(value, null, 2); + } + } + + JsonPipe.ɵfac = function JsonPipe_Factory(t) { + return new (t || JsonPipe)(); + }; + + JsonPipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "json", + type: JsonPipe, + pure: false + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + JsonPipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "json", + pure: false + } + ] + } + ], + null, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + function makeKeyValuePair(key, value) { + return { + key: key, + value: value + }; + } + /** + * @ngModule CommonModule + * @description + * + * Transforms Object or Map into an array of key value pairs. + * + * The output array will be ordered by keys. + * By default the comparator will be by Unicode point value. + * You can optionally pass a compareFn if your keys are complex types. + * + * @usageNotes + * ### Examples + * + * This examples show how an Object or a Map can be iterated by ngFor with the use of this + * keyvalue pipe. + * + * {@example common/pipes/ts/keyvalue_pipe.ts region='KeyValuePipe'} + * + * @publicApi + */ + + class KeyValuePipe { + constructor(differs) { + this.differs = differs; + this.keyValues = []; + this.compareFn = defaultComparator; + } + + transform(input, compareFn = defaultComparator) { + if (!input || (!(input instanceof Map) && typeof input !== "object")) { + return null; + } + + if (!this.differ) { + // make a differ for whatever type we've been passed in + this.differ = this.differs.find(input).create(); + } + + const differChanges = this.differ.diff(input); + const compareFnChanged = compareFn !== this.compareFn; + + if (differChanges) { + this.keyValues = []; + differChanges.forEachItem(r => { + this.keyValues.push(makeKeyValuePair(r.key, r.currentValue)); + }); + } + + if (differChanges || compareFnChanged) { + this.keyValues.sort(compareFn); + this.compareFn = compareFn; + } + + return this.keyValues; + } + } + + KeyValuePipe.ɵfac = function KeyValuePipe_Factory(t) { + return new (t || KeyValuePipe)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers, + 16 + ) + ); + }; + + KeyValuePipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "keyvalue", + type: KeyValuePipe, + pure: false + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + KeyValuePipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "keyvalue", + pure: false + } + ] + } + ], + function() { + return [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers + } + ]; + }, + null + ); + })(); + + function defaultComparator(keyValueA, keyValueB) { + const a = keyValueA.key; + const b = keyValueB.key; // if same exit with 0; + + if (a === b) return 0; // make sure that undefined are at the end of the sort. + + if (a === undefined) return 1; + if (b === undefined) return -1; // make sure that nulls are at the end of the sort. + + if (a === null) return 1; + if (b === null) return -1; + + if (typeof a == "string" && typeof b == "string") { + return a < b ? -1 : 1; + } + + if (typeof a == "number" && typeof b == "number") { + return a - b; + } + + if (typeof a == "boolean" && typeof b == "boolean") { + return a < b ? -1 : 1; + } // `a` and `b` are of different types. Compare their string values. + + const aString = String(a); + const bString = String(b); + return aString == bString ? 0 : aString < bString ? -1 : 1; + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @ngModule CommonModule + * @description + * + * Formats a value according to digit options and locale rules. + * Locale determines group sizing and separator, + * decimal point character, and other locale-specific configurations. + * + * @see `formatNumber()` + * + * @usageNotes + * + * ### digitsInfo + * + * The value's decimal representation is specified by the `digitsInfo` + * parameter, written in the following format:
    + * + * ``` + * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits} + * ``` + * + * - `minIntegerDigits`: + * The minimum number of integer digits before the decimal point. + * Default is 1. + * + * - `minFractionDigits`: + * The minimum number of digits after the decimal point. + * Default is 0. + * + * - `maxFractionDigits`: + * The maximum number of digits after the decimal point. + * Default is 3. + * + * If the formatted value is truncated it will be rounded using the "to-nearest" method: + * + * ``` + * {{3.6 | number: '1.0-0'}} + * + * + * {{-3.6 | number:'1.0-0'}} + * + * ``` + * + * ### locale + * + * `locale` will format a value according to locale rules. + * Locale determines group sizing and separator, + * decimal point character, and other locale-specific configurations. + * + * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. + * + * See [Setting your app locale](guide/i18n-common-locale-id). + * + * ### Example + * + * The following code shows how the pipe transforms values + * according to various format specifications, + * where the caller's default locale is `en-US`. + * + * + * + * @publicApi + */ + + class DecimalPipe { + constructor(_locale) { + this._locale = _locale; + } + /** + * @param value The value to be formatted. + * @param digitsInfo Sets digit and decimal representation. + * [See more](#digitsinfo). + * @param locale Specifies what locale format rules to use. + * [See more](#locale). + */ + + transform(value, digitsInfo, locale) { + if (!isValue(value)) return null; + locale = locale || this._locale; + + try { + const num = strToNumber(value); + return formatNumber(num, locale, digitsInfo); + } catch (error) { + throw invalidPipeArgumentError(DecimalPipe, error.message); + } + } + } + + DecimalPipe.ɵfac = function DecimalPipe_Factory(t) { + return new (t || DecimalPipe)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID, + 16 + ) + ); + }; + + DecimalPipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "number", + type: DecimalPipe, + pure: true + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + DecimalPipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "number" + } + ] + } + ], + function() { + return [ + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, + args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] + } + ] + } + ]; + }, + null + ); + })(); + /** + * @ngModule CommonModule + * @description + * + * Transforms a number to a percentage + * string, formatted according to locale rules that determine group sizing and + * separator, decimal-point character, and other locale-specific + * configurations. + * + * @see `formatPercent()` + * + * @usageNotes + * The following code shows how the pipe transforms numbers + * into text strings, according to various format specifications, + * where the caller's default locale is `en-US`. + * + * + * + * @publicApi + */ + + class PercentPipe { + constructor(_locale) { + this._locale = _locale; + } + /** + * + * @param value The number to be formatted as a percentage. + * @param digitsInfo Decimal representation options, specified by a string + * in the following format:
    + * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}. + * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. + * Default is `1`. + * - `minFractionDigits`: The minimum number of digits after the decimal point. + * Default is `0`. + * - `maxFractionDigits`: The maximum number of digits after the decimal point. + * Default is `0`. + * @param locale A locale code for the locale format rules to use. + * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. + * See [Setting your app locale](guide/i18n-common-locale-id). + */ + + transform(value, digitsInfo, locale) { + if (!isValue(value)) return null; + locale = locale || this._locale; + + try { + const num = strToNumber(value); + return formatPercent(num, locale, digitsInfo); + } catch (error) { + throw invalidPipeArgumentError(PercentPipe, error.message); + } + } + } + + PercentPipe.ɵfac = function PercentPipe_Factory(t) { + return new (t || PercentPipe)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID, + 16 + ) + ); + }; + + PercentPipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "percent", + type: PercentPipe, + pure: true + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + PercentPipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "percent" + } + ] + } + ], + function() { + return [ + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, + args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] + } + ] + } + ]; + }, + null + ); + })(); + /** + * @ngModule CommonModule + * @description + * + * Transforms a number to a currency string, formatted according to locale rules + * that determine group sizing and separator, decimal-point character, + * and other locale-specific configurations. + * + * {@a currency-code-deprecation} + *
    + * + * **Deprecation notice:** + * + * The default currency code is currently always `USD` but this is deprecated from v9. + * + * **In v11 the default currency code will be taken from the current locale identified by + * the `LOCALE_ID` token. See the [i18n guide](guide/i18n-common-locale-id) for + * more information.** + * + * If you need the previous behavior then set it by creating a `DEFAULT_CURRENCY_CODE` provider in + * your application `NgModule`: + * + * ```ts + * {provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'} + * ``` + * + *
    + * + * @see `getCurrencySymbol()` + * @see `formatCurrency()` + * + * @usageNotes + * The following code shows how the pipe transforms numbers + * into text strings, according to various format specifications, + * where the caller's default locale is `en-US`. + * + * + * + * @publicApi + */ + + class CurrencyPipe { + constructor(_locale, _defaultCurrencyCode = "USD") { + this._locale = _locale; + this._defaultCurrencyCode = _defaultCurrencyCode; + } + /** + * + * @param value The number to be formatted as currency. + * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, + * such as `USD` for the US dollar and `EUR` for the euro. The default currency code can be + * configured using the `DEFAULT_CURRENCY_CODE` injection token. + * @param display The format for the currency indicator. One of the following: + * - `code`: Show the code (such as `USD`). + * - `symbol`(default): Show the symbol (such as `$`). + * - `symbol-narrow`: Use the narrow symbol for locales that have two symbols for their + * currency. + * For example, the Canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`. If the + * locale has no narrow symbol, uses the standard symbol for the locale. + * - String: Use the given string value instead of a code or a symbol. + * For example, an empty string will suppress the currency & symbol. + * - Boolean (marked deprecated in v5): `true` for symbol and false for `code`. + * + * @param digitsInfo Decimal representation options, specified by a string + * in the following format:
    + * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}. + * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. + * Default is `1`. + * - `minFractionDigits`: The minimum number of digits after the decimal point. + * Default is `2`. + * - `maxFractionDigits`: The maximum number of digits after the decimal point. + * Default is `2`. + * If not provided, the number will be formatted with the proper amount of digits, + * depending on what the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) specifies. + * For example, the Canadian dollar has 2 digits, whereas the Chilean peso has none. + * @param locale A locale code for the locale format rules to use. + * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. + * See [Setting your app locale](guide/i18n-common-locale-id). + */ + + transform(value, currencyCode = this._defaultCurrencyCode, display = "symbol", digitsInfo, locale) { + if (!isValue(value)) return null; + locale = locale || this._locale; + + if (typeof display === "boolean") { + if ((typeof ngDevMode === "undefined" || ngDevMode) && console && console.warn) { + console.warn( + `Warning: the currency pipe has been changed in Angular v5. The symbolDisplay option (third parameter) is now a string instead of a boolean. The accepted values are "code", "symbol" or "symbol-narrow".` + ); + } + + display = display ? "symbol" : "code"; + } + + let currency = currencyCode || this._defaultCurrencyCode; + + if (display !== "code") { + if (display === "symbol" || display === "symbol-narrow") { + currency = getCurrencySymbol( + currency, + display === "symbol" ? "wide" : "narrow", + locale + ); + } else { + currency = display; + } + } + + try { + const num = strToNumber(value); + return formatCurrency(num, locale, currency, currencyCode, digitsInfo); + } catch (error) { + throw invalidPipeArgumentError(CurrencyPipe, error.message); + } + } + } + + CurrencyPipe.ɵfac = function CurrencyPipe_Factory(t) { + return new (t || CurrencyPipe)( + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID, + 16 + ), + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"]( + _angular_core__WEBPACK_IMPORTED_MODULE_0__.DEFAULT_CURRENCY_CODE, + 16 + ) + ); + }; + + CurrencyPipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "currency", + type: CurrencyPipe, + pure: true + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + CurrencyPipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "currency" + } + ] + } + ], + function() { + return [ + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, + args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] + } + ] + }, + { + type: undefined, + decorators: [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, + args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.DEFAULT_CURRENCY_CODE] + } + ] + } + ]; + }, + null + ); + })(); + + function isValue(value) { + return !(value == null || value === "" || value !== value); + } + /** + * Transforms a string into a number (if needed). + */ + + function strToNumber(value) { + // Convert strings to numbers + if (typeof value === "string" && !isNaN(Number(value) - parseFloat(value))) { + return Number(value); + } + + if (typeof value !== "number") { + throw new Error(`${value} is not a number`); + } + + return value; + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @ngModule CommonModule + * @description + * + * Creates a new `Array` or `String` containing a subset (slice) of the elements. + * + * @usageNotes + * + * All behavior is based on the expected behavior of the JavaScript API `Array.prototype.slice()` + * and `String.prototype.slice()`. + * + * When operating on an `Array`, the returned `Array` is always a copy even when all + * the elements are being returned. + * + * When operating on a blank value, the pipe returns the blank value. + * + * ### List Example + * + * This `ngFor` example: + * + * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_list'} + * + * produces the following: + * + * ```html + *
  • b
  • + *
  • c
  • + * ``` + * + * ### String Examples + * + * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_string'} + * + * @publicApi + */ + + class SlicePipe { + transform(value, start, end) { + if (value == null) return null; + + if (!this.supports(value)) { + throw invalidPipeArgumentError(SlicePipe, value); + } + + return value.slice(start, end); + } + + supports(obj) { + return typeof obj === "string" || Array.isArray(obj); + } + } + + SlicePipe.ɵfac = function SlicePipe_Factory(t) { + return new (t || SlicePipe)(); + }; + + SlicePipe.ɵpipe = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ + name: "slice", + type: SlicePipe, + pure: false + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + SlicePipe, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, + args: [ + { + name: "slice", + pure: false + } + ] + } + ], + null, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * A collection of Angular pipes that are likely to be used in each and every application. + */ + + const COMMON_PIPES = [ + AsyncPipe, + UpperCasePipe, + LowerCasePipe, + JsonPipe, + SlicePipe, + DecimalPipe, + PercentPipe, + TitleCasePipe, + CurrencyPipe, + DatePipe, + I18nPluralPipe, + I18nSelectPipe, + KeyValuePipe + ]; + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + // Note: This does not contain the location providers, + // as they need some platform specific implementations to work. + + /** + * Exports all the basic Angular directives and pipes, + * such as `NgIf`, `NgForOf`, `DecimalPipe`, and so on. + * Re-exported by `BrowserModule`, which is included automatically in the root + * `AppModule` when you create a new app with the CLI `new` command. + * + * * The `providers` options configure the NgModule's injector to provide + * localization dependencies to members. + * * The `exports` options make the declared directives and pipes available for import + * by other NgModules. + * + * @publicApi + */ + + class CommonModule {} + + CommonModule.ɵfac = function CommonModule_Factory(t) { + return new (t || CommonModule)(); + }; + + CommonModule.ɵmod = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineNgModule"]({ + type: CommonModule + }); + CommonModule.ɵinj = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjector"]({}); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"]( + CommonModule, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgModule, + args: [ + { + declarations: [COMMON_DIRECTIVES, COMMON_PIPES], + exports: [COMMON_DIRECTIVES, COMMON_PIPES] + } + ] + } + ], + null, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + const PLATFORM_BROWSER_ID = "browser"; + const PLATFORM_SERVER_ID = "server"; + const PLATFORM_WORKER_APP_ID = "browserWorkerApp"; + const PLATFORM_WORKER_UI_ID = "browserWorkerUi"; + /** + * Returns whether a platform id represents a browser platform. + * @publicApi + */ + + function isPlatformBrowser(platformId) { + return platformId === PLATFORM_BROWSER_ID; + } + /** + * Returns whether a platform id represents a server platform. + * @publicApi + */ + + function isPlatformServer(platformId) { + return platformId === PLATFORM_SERVER_ID; + } + /** + * Returns whether a platform id represents a web worker app platform. + * @publicApi + */ + + function isPlatformWorkerApp(platformId) { + return platformId === PLATFORM_WORKER_APP_ID; + } + /** + * Returns whether a platform id represents a web worker UI platform. + * @publicApi + */ + + function isPlatformWorkerUi(platformId) { + return platformId === PLATFORM_WORKER_UI_ID; + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @publicApi + */ + + const VERSION = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.Version("13.3.11"); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Defines a scroll position manager. Implemented by `BrowserViewportScroller`. + * + * @publicApi + */ + + class ViewportScroller {} // De-sugared tree-shakable injection + // See #23917 + + /** @nocollapse */ + + ViewportScroller.ɵprov = (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"])({ + token: ViewportScroller, + providedIn: "root", + factory: () => + new BrowserViewportScroller( + (0, _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(DOCUMENT), + window + ) + }); + /** + * Manages the scroll position for a browser window. + */ + + class BrowserViewportScroller { + constructor(document, window) { + this.document = document; + this.window = window; + + this.offset = () => [0, 0]; + } + /** + * Configures the top offset used when scrolling to an anchor. + * @param offset A position in screen coordinates (a tuple with x and y values) + * or a function that returns the top offset position. + * + */ + + setOffset(offset) { + if (Array.isArray(offset)) { + this.offset = () => offset; + } else { + this.offset = offset; + } + } + /** + * Retrieves the current scroll position. + * @returns The position in screen coordinates. + */ + + getScrollPosition() { + if (this.supportsScrolling()) { + return [this.window.pageXOffset, this.window.pageYOffset]; + } else { + return [0, 0]; + } + } + /** + * Sets the scroll position. + * @param position The new position in screen coordinates. + */ + + scrollToPosition(position) { + if (this.supportsScrolling()) { + this.window.scrollTo(position[0], position[1]); + } + } + /** + * Scrolls to an element and attempts to focus the element. + * + * Note that the function name here is misleading in that the target string may be an ID for a + * non-anchor element. + * + * @param target The ID of an element or name of the anchor. + * + * @see https://html.spec.whatwg.org/#the-indicated-part-of-the-document + * @see https://html.spec.whatwg.org/#scroll-to-fragid + */ + + scrollToAnchor(target) { + if (!this.supportsScrolling()) { + return; + } + + const elSelected = findAnchorFromDocument(this.document, target); + + if (elSelected) { + this.scrollToElement(elSelected); // After scrolling to the element, the spec dictates that we follow the focus steps for the + // target. Rather than following the robust steps, simply attempt focus. + // + // @see https://html.spec.whatwg.org/#get-the-focusable-area + // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus + // @see https://html.spec.whatwg.org/#focusable-area + + elSelected.focus(); + } + } + /** + * Disables automatic scroll restoration provided by the browser. + */ + + setHistoryScrollRestoration(scrollRestoration) { + if (this.supportScrollRestoration()) { + const history = this.window.history; + + if (history && history.scrollRestoration) { + history.scrollRestoration = scrollRestoration; + } + } + } + /** + * Scrolls to an element using the native offset and the specified offset set on this scroller. + * + * The offset can be used when we know that there is a floating header and scrolling naively to an + * element (ex: `scrollIntoView`) leaves the element hidden behind the floating header. + */ + + scrollToElement(el) { + const rect = el.getBoundingClientRect(); + const left = rect.left + this.window.pageXOffset; + const top = rect.top + this.window.pageYOffset; + const offset = this.offset(); + this.window.scrollTo(left - offset[0], top - offset[1]); + } + /** + * We only support scroll restoration when we can get a hold of window. + * This means that we do not support this behavior when running in a web worker. + * + * Lifting this restriction right now would require more changes in the dom adapter. + * Since webworkers aren't widely used, we will lift it once RouterScroller is + * battle-tested. + */ + + supportScrollRestoration() { + try { + if (!this.supportsScrolling()) { + return false; + } // The `scrollRestoration` property could be on the `history` instance or its prototype. + + const scrollRestorationDescriptor = + getScrollRestorationProperty(this.window.history) || + getScrollRestorationProperty(Object.getPrototypeOf(this.window.history)); // We can write to the `scrollRestoration` property if it is a writable data field or it has a + // setter function. + + return ( + !!scrollRestorationDescriptor && + !!(scrollRestorationDescriptor.writable || scrollRestorationDescriptor.set) + ); + } catch (_a) { + return false; + } + } + + supportsScrolling() { + try { + return !!this.window && !!this.window.scrollTo && "pageXOffset" in this.window; + } catch (_a) { + return false; + } + } + } + + function getScrollRestorationProperty(obj) { + return Object.getOwnPropertyDescriptor(obj, "scrollRestoration"); + } + + function findAnchorFromDocument(document, target) { + const documentResult = document.getElementById(target) || document.getElementsByName(target)[0]; + + if (documentResult) { + return documentResult; + } // `getElementById` and `getElementsByName` won't pierce through the shadow DOM so we + // have to traverse the DOM manually and do the lookup through the shadow roots. + + if ( + typeof document.createTreeWalker === "function" && + document.body && + (document.body.createShadowRoot || document.body.attachShadow) + ) { + const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT); + let currentNode = treeWalker.currentNode; + + while (currentNode) { + const shadowRoot = currentNode.shadowRoot; + + if (shadowRoot) { + // Note that `ShadowRoot` doesn't support `getElementsByName` + // so we have to fall back to `querySelector`. + const result = + shadowRoot.getElementById(target) || shadowRoot.querySelector(`[name="${target}"]`); + + if (result) { + return result; + } + } + + currentNode = treeWalker.nextNode(); + } + } + + return null; + } + /** + * Provides an empty implementation of the viewport scroller. + */ + + class NullViewportScroller { + /** + * Empty implementation + */ + setOffset(offset) {} + /** + * Empty implementation + */ + + getScrollPosition() { + return [0, 0]; + } + /** + * Empty implementation + */ + + scrollToPosition(position) {} + /** + * Empty implementation + */ + + scrollToAnchor(anchor) {} + /** + * Empty implementation + */ + + setHistoryScrollRestoration(scrollRestoration) {} + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * A wrapper around the `XMLHttpRequest` constructor. + * + * @publicApi + */ + + class XhrFactory {} + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + // This file only reexports content of the `src` folder. Keep it that way. + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Generated bundle index. Do not edit. + */ + + /***/ + }, + + /***/ 8784: + /*!********************************************************!*\ + !*** ./node_modules/@angular/common/fesm2015/http.mjs ***! + \********************************************************/ + /***/ (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ HTTP_INTERCEPTORS: () => /* binding */ HTTP_INTERCEPTORS, + /* harmony export */ HttpBackend: () => /* binding */ HttpBackend, + /* harmony export */ HttpClient: () => /* binding */ HttpClient, + /* harmony export */ HttpClientJsonpModule: () => /* binding */ HttpClientJsonpModule, + /* harmony export */ HttpClientModule: () => /* binding */ HttpClientModule, + /* harmony export */ HttpClientXsrfModule: () => /* binding */ HttpClientXsrfModule, + /* harmony export */ HttpContext: () => /* binding */ HttpContext, + /* harmony export */ HttpContextToken: () => /* binding */ HttpContextToken, + /* harmony export */ HttpErrorResponse: () => /* binding */ HttpErrorResponse, + /* harmony export */ HttpEventType: () => /* binding */ HttpEventType, + /* harmony export */ HttpHandler: () => /* binding */ HttpHandler, + /* harmony export */ HttpHeaderResponse: () => /* binding */ HttpHeaderResponse, + /* harmony export */ HttpHeaders: () => /* binding */ HttpHeaders, + /* harmony export */ HttpParams: () => /* binding */ HttpParams, + /* harmony export */ HttpRequest: () => /* binding */ HttpRequest, + /* harmony export */ HttpResponse: () => /* binding */ HttpResponse, + /* harmony export */ HttpResponseBase: () => /* binding */ HttpResponseBase, + /* harmony export */ HttpUrlEncodingCodec: () => /* binding */ HttpUrlEncodingCodec, + /* harmony export */ HttpXhrBackend: () => /* binding */ HttpXhrBackend, + /* harmony export */ HttpXsrfTokenExtractor: () => /* binding */ HttpXsrfTokenExtractor, + /* harmony export */ JsonpClientBackend: () => /* binding */ JsonpClientBackend, + /* harmony export */ JsonpInterceptor: () => /* binding */ JsonpInterceptor, + /* harmony export */ XhrFactory: () => /* binding */ XhrFactory, + /* harmony export */ ɵHttpInterceptingHandler: () => /* binding */ HttpInterceptingHandler + /* harmony export */ + }); + /* harmony import */ var _angular_common__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__( + /*! @angular/common */ 6362 + ); + /* harmony import */ var _angular_core__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__( + /*! @angular/core */ 3184 + ); + /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! rxjs */ 745); + /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! rxjs */ 833); + /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( + /*! rxjs/operators */ 3853 + ); + /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( + /*! rxjs/operators */ 116 + ); + /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__( + /*! rxjs/operators */ 635 + ); + /** + * @license Angular v13.3.11 + * (c) 2010-2022 Google LLC. https://angular.io/ + * License: MIT + */ + + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Transforms an `HttpRequest` into a stream of `HttpEvent`s, one of which will likely be a + * `HttpResponse`. + * + * `HttpHandler` is injectable. When injected, the handler instance dispatches requests to the + * first interceptor in the chain, which dispatches to the second, etc, eventually reaching the + * `HttpBackend`. + * + * In an `HttpInterceptor`, the `HttpHandler` parameter is the next interceptor in the chain. + * + * @publicApi + */ + + class HttpHandler {} + /** + * A final `HttpHandler` which will dispatch the request via browser HTTP APIs to a backend. + * + * Interceptors sit between the `HttpClient` interface and the `HttpBackend`. + * + * When injected, `HttpBackend` dispatches requests directly to the backend, without going + * through the interceptor chain. + * + * @publicApi + */ + + class HttpBackend {} + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Represents the header configuration options for an HTTP request. + * Instances are immutable. Modifying methods return a cloned + * instance with the change. The original object is never changed. + * + * @publicApi + */ + + class HttpHeaders { + /** Constructs a new HTTP header object with the given values.*/ + constructor(headers) { + /** + * Internal map of lowercased header names to the normalized + * form of the name (the form seen first). + */ + this.normalizedNames = new Map(); + /** + * Queued updates to be materialized the next initialization. + */ + + this.lazyUpdate = null; + + if (!headers) { + this.headers = new Map(); + } else if (typeof headers === "string") { + this.lazyInit = () => { + this.headers = new Map(); + headers.split("\n").forEach(line => { + const index = line.indexOf(":"); + + if (index > 0) { + const name = line.slice(0, index); + const key = name.toLowerCase(); + const value = line.slice(index + 1).trim(); + this.maybeSetNormalizedName(name, key); + + if (this.headers.has(key)) { + this.headers.get(key).push(value); + } else { + this.headers.set(key, [value]); + } + } + }); + }; + } else { + this.lazyInit = () => { + this.headers = new Map(); + Object.keys(headers).forEach(name => { + let values = headers[name]; + const key = name.toLowerCase(); + + if (typeof values === "string") { + values = [values]; + } + + if (values.length > 0) { + this.headers.set(key, values); + this.maybeSetNormalizedName(name, key); + } + }); + }; + } + } + /** + * Checks for existence of a given header. + * + * @param name The header name to check for existence. + * + * @returns True if the header exists, false otherwise. + */ + + has(name) { + this.init(); + return this.headers.has(name.toLowerCase()); + } + /** + * Retrieves the first value of a given header. + * + * @param name The header name. + * + * @returns The value string if the header exists, null otherwise + */ + + get(name) { + this.init(); + const values = this.headers.get(name.toLowerCase()); + return values && values.length > 0 ? values[0] : null; + } + /** + * Retrieves the names of the headers. + * + * @returns A list of header names. + */ + + keys() { + this.init(); + return Array.from(this.normalizedNames.values()); + } + /** + * Retrieves a list of values for a given header. + * + * @param name The header name from which to retrieve values. + * + * @returns A string of values if the header exists, null otherwise. + */ + + getAll(name) { + this.init(); + return this.headers.get(name.toLowerCase()) || null; + } + /** + * Appends a new value to the existing set of values for a header + * and returns them in a clone of the original instance. + * + * @param name The header name for which to append the values. + * @param value The value to append. + * + * @returns A clone of the HTTP headers object with the value appended to the given header. + */ + + append(name, value) { + return this.clone({ + name, + value, + op: "a" + }); + } + /** + * Sets or modifies a value for a given header in a clone of the original instance. + * If the header already exists, its value is replaced with the given value + * in the returned object. + * + * @param name The header name. + * @param value The value or values to set or overide for the given header. + * + * @returns A clone of the HTTP headers object with the newly set header value. + */ + + set(name, value) { + return this.clone({ + name, + value, + op: "s" + }); + } + /** + * Deletes values for a given header in a clone of the original instance. + * + * @param name The header name. + * @param value The value or values to delete for the given header. + * + * @returns A clone of the HTTP headers object with the given value deleted. + */ + + delete(name, value) { + return this.clone({ + name, + value, + op: "d" + }); + } + + maybeSetNormalizedName(name, lcName) { + if (!this.normalizedNames.has(lcName)) { + this.normalizedNames.set(lcName, name); + } + } + + init() { + if (!!this.lazyInit) { + if (this.lazyInit instanceof HttpHeaders) { + this.copyFrom(this.lazyInit); + } else { + this.lazyInit(); + } + + this.lazyInit = null; + + if (!!this.lazyUpdate) { + this.lazyUpdate.forEach(update => this.applyUpdate(update)); + this.lazyUpdate = null; + } + } + } + + copyFrom(other) { + other.init(); + Array.from(other.headers.keys()).forEach(key => { + this.headers.set(key, other.headers.get(key)); + this.normalizedNames.set(key, other.normalizedNames.get(key)); + }); + } + + clone(update) { + const clone = new HttpHeaders(); + clone.lazyInit = !!this.lazyInit && this.lazyInit instanceof HttpHeaders ? this.lazyInit : this; + clone.lazyUpdate = (this.lazyUpdate || []).concat([update]); + return clone; + } + + applyUpdate(update) { + const key = update.name.toLowerCase(); + + switch (update.op) { + case "a": + case "s": + let value = update.value; + + if (typeof value === "string") { + value = [value]; + } + + if (value.length === 0) { + return; + } + + this.maybeSetNormalizedName(update.name, key); + const base = (update.op === "a" ? this.headers.get(key) : undefined) || []; + base.push(...value); + this.headers.set(key, base); + break; + + case "d": + const toDelete = update.value; + + if (!toDelete) { + this.headers.delete(key); + this.normalizedNames.delete(key); + } else { + let existing = this.headers.get(key); + + if (!existing) { + return; + } + + existing = existing.filter(value => toDelete.indexOf(value) === -1); + + if (existing.length === 0) { + this.headers.delete(key); + this.normalizedNames.delete(key); + } else { + this.headers.set(key, existing); + } + } + + break; + } + } + /** + * @internal + */ + + forEach(fn) { + this.init(); + Array.from(this.normalizedNames.keys()).forEach(key => + fn(this.normalizedNames.get(key), this.headers.get(key)) + ); + } + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Provides encoding and decoding of URL parameter and query-string values. + * + * Serializes and parses URL parameter keys and values to encode and decode them. + * If you pass URL query parameters without encoding, + * the query parameters can be misinterpreted at the receiving end. + * + * + * @publicApi + */ + + class HttpUrlEncodingCodec { + /** + * Encodes a key name for a URL parameter or query-string. + * @param key The key name. + * @returns The encoded key name. + */ + encodeKey(key) { + return standardEncoding(key); + } + /** + * Encodes the value of a URL parameter or query-string. + * @param value The value. + * @returns The encoded value. + */ + + encodeValue(value) { + return standardEncoding(value); + } + /** + * Decodes an encoded URL parameter or query-string key. + * @param key The encoded key name. + * @returns The decoded key name. + */ + + decodeKey(key) { + return decodeURIComponent(key); + } + /** + * Decodes an encoded URL parameter or query-string value. + * @param value The encoded value. + * @returns The decoded value. + */ + + decodeValue(value) { + return decodeURIComponent(value); + } + } + + function paramParser(rawParams, codec) { + const map = new Map(); + + if (rawParams.length > 0) { + // The `window.location.search` can be used while creating an instance of the `HttpParams` class + // (e.g. `new HttpParams({ fromString: window.location.search })`). The `window.location.search` + // may start with the `?` char, so we strip it if it's present. + const params = rawParams.replace(/^\?/, "").split("&"); + params.forEach(param => { + const eqIdx = param.indexOf("="); + const [key, val] = + eqIdx == -1 + ? [codec.decodeKey(param), ""] + : [ + codec.decodeKey(param.slice(0, eqIdx)), + codec.decodeValue(param.slice(eqIdx + 1)) + ]; + const list = map.get(key) || []; + list.push(val); + map.set(key, list); + }); + } + + return map; + } + /** + * Encode input string with standard encodeURIComponent and then un-encode specific characters. + */ + + const STANDARD_ENCODING_REGEX = /%(\d[a-f0-9])/gi; + const STANDARD_ENCODING_REPLACEMENTS = { + "40": "@", + "3A": ":", + "24": "$", + "2C": ",", + "3B": ";", + "2B": "+", + "3D": "=", + "3F": "?", + "2F": "/" + }; + + function standardEncoding(v) { + return encodeURIComponent(v).replace(STANDARD_ENCODING_REGEX, (s, t) => { + var _a; + + return (_a = STANDARD_ENCODING_REPLACEMENTS[t]) !== null && _a !== void 0 ? _a : s; + }); + } + + function valueToString(value) { + return `${value}`; + } + /** + * An HTTP request/response body that represents serialized parameters, + * per the MIME type `application/x-www-form-urlencoded`. + * + * This class is immutable; all mutation operations return a new instance. + * + * @publicApi + */ + + class HttpParams { + constructor(options = {}) { + this.updates = null; + this.cloneFrom = null; + this.encoder = options.encoder || new HttpUrlEncodingCodec(); + + if (!!options.fromString) { + if (!!options.fromObject) { + throw new Error(`Cannot specify both fromString and fromObject.`); + } + + this.map = paramParser(options.fromString, this.encoder); + } else if (!!options.fromObject) { + this.map = new Map(); + Object.keys(options.fromObject).forEach(key => { + const value = options.fromObject[key]; + this.map.set(key, Array.isArray(value) ? value : [value]); + }); + } else { + this.map = null; + } + } + /** + * Reports whether the body includes one or more values for a given parameter. + * @param param The parameter name. + * @returns True if the parameter has one or more values, + * false if it has no value or is not present. + */ + + has(param) { + this.init(); + return this.map.has(param); + } + /** + * Retrieves the first value for a parameter. + * @param param The parameter name. + * @returns The first value of the given parameter, + * or `null` if the parameter is not present. + */ + + get(param) { + this.init(); + const res = this.map.get(param); + return !!res ? res[0] : null; + } + /** + * Retrieves all values for a parameter. + * @param param The parameter name. + * @returns All values in a string array, + * or `null` if the parameter not present. + */ + + getAll(param) { + this.init(); + return this.map.get(param) || null; + } + /** + * Retrieves all the parameters for this body. + * @returns The parameter names in a string array. + */ + + keys() { + this.init(); + return Array.from(this.map.keys()); + } + /** + * Appends a new value to existing values for a parameter. + * @param param The parameter name. + * @param value The new value to add. + * @return A new body with the appended value. + */ + + append(param, value) { + return this.clone({ + param, + value, + op: "a" + }); + } + /** + * Constructs a new body with appended values for the given parameter name. + * @param params parameters and values + * @return A new body with the new value. + */ + + appendAll(params) { + const updates = []; + Object.keys(params).forEach(param => { + const value = params[param]; + + if (Array.isArray(value)) { + value.forEach(_value => { + updates.push({ + param, + value: _value, + op: "a" + }); + }); + } else { + updates.push({ + param, + value: value, + op: "a" + }); + } + }); + return this.clone(updates); + } + /** + * Replaces the value for a parameter. + * @param param The parameter name. + * @param value The new value. + * @return A new body with the new value. + */ + + set(param, value) { + return this.clone({ + param, + value, + op: "s" + }); + } + /** + * Removes a given value or all values from a parameter. + * @param param The parameter name. + * @param value The value to remove, if provided. + * @return A new body with the given value removed, or with all values + * removed if no value is specified. + */ + + delete(param, value) { + return this.clone({ + param, + value, + op: "d" + }); + } + /** + * Serializes the body to an encoded string, where key-value pairs (separated by `=`) are + * separated by `&`s. + */ + + toString() { + this.init(); + return ( + this.keys() + .map(key => { + const eKey = this.encoder.encodeKey(key); // `a: ['1']` produces `'a=1'` + // `b: []` produces `''` + // `c: ['1', '2']` produces `'c=1&c=2'` + + return this.map + .get(key) + .map(value => eKey + "=" + this.encoder.encodeValue(value)) + .join("&"); + }) // filter out empty values because `b: []` produces `''` + // which results in `a=1&&c=1&c=2` instead of `a=1&c=1&c=2` if we don't + .filter(param => param !== "") + .join("&") + ); + } + + clone(update) { + const clone = new HttpParams({ + encoder: this.encoder + }); + clone.cloneFrom = this.cloneFrom || this; + clone.updates = (this.updates || []).concat(update); + return clone; + } + + init() { + if (this.map === null) { + this.map = new Map(); + } + + if (this.cloneFrom !== null) { + this.cloneFrom.init(); + this.cloneFrom.keys().forEach(key => this.map.set(key, this.cloneFrom.map.get(key))); + this.updates.forEach(update => { + switch (update.op) { + case "a": + case "s": + const base = (update.op === "a" ? this.map.get(update.param) : undefined) || []; + base.push(valueToString(update.value)); + this.map.set(update.param, base); + break; + + case "d": + if (update.value !== undefined) { + let base = this.map.get(update.param) || []; + const idx = base.indexOf(valueToString(update.value)); + + if (idx !== -1) { + base.splice(idx, 1); + } + + if (base.length > 0) { + this.map.set(update.param, base); + } else { + this.map.delete(update.param); + } + } else { + this.map.delete(update.param); + break; + } + } + }); + this.cloneFrom = this.updates = null; + } + } + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * A token used to manipulate and access values stored in `HttpContext`. + * + * @publicApi + */ + + class HttpContextToken { + constructor(defaultValue) { + this.defaultValue = defaultValue; + } + } + /** + * Http context stores arbitrary user defined values and ensures type safety without + * actually knowing the types. It is backed by a `Map` and guarantees that keys do not clash. + * + * This context is mutable and is shared between cloned requests unless explicitly specified. + * + * @usageNotes + * + * ### Usage Example + * + * ```typescript + * // inside cache.interceptors.ts + * export const IS_CACHE_ENABLED = new HttpContextToken(() => false); + * + * export class CacheInterceptor implements HttpInterceptor { + * + * intercept(req: HttpRequest, delegate: HttpHandler): Observable> { + * if (req.context.get(IS_CACHE_ENABLED) === true) { + * return ...; + * } + * return delegate.handle(req); + * } + * } + * + * // inside a service + * + * this.httpClient.get('/api/weather', { + * context: new HttpContext().set(IS_CACHE_ENABLED, true) + * }).subscribe(...); + * ``` + * + * @publicApi + */ + + class HttpContext { + constructor() { + this.map = new Map(); + } + /** + * Store a value in the context. If a value is already present it will be overwritten. + * + * @param token The reference to an instance of `HttpContextToken`. + * @param value The value to store. + * + * @returns A reference to itself for easy chaining. + */ + + set(token, value) { + this.map.set(token, value); + return this; + } + /** + * Retrieve the value associated with the given token. + * + * @param token The reference to an instance of `HttpContextToken`. + * + * @returns The stored value or default if one is defined. + */ + + get(token) { + if (!this.map.has(token)) { + this.map.set(token, token.defaultValue()); + } + + return this.map.get(token); + } + /** + * Delete the value associated with the given token. + * + * @param token The reference to an instance of `HttpContextToken`. + * + * @returns A reference to itself for easy chaining. + */ + + delete(token) { + this.map.delete(token); + return this; + } + /** + * Checks for existence of a given token. + * + * @param token The reference to an instance of `HttpContextToken`. + * + * @returns True if the token exists, false otherwise. + */ + + has(token) { + return this.map.has(token); + } + /** + * @returns a list of tokens currently stored in the context. + */ + + keys() { + return this.map.keys(); + } + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Determine whether the given HTTP method may include a body. + */ + + function mightHaveBody(method) { + switch (method) { + case "DELETE": + case "GET": + case "HEAD": + case "OPTIONS": + case "JSONP": + return false; + + default: + return true; + } + } + /** + * Safely assert whether the given value is an ArrayBuffer. + * + * In some execution environments ArrayBuffer is not defined. + */ + + function isArrayBuffer(value) { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; + } + /** + * Safely assert whether the given value is a Blob. + * + * In some execution environments Blob is not defined. + */ + + function isBlob(value) { + return typeof Blob !== "undefined" && value instanceof Blob; + } + /** + * Safely assert whether the given value is a FormData instance. + * + * In some execution environments FormData is not defined. + */ + + function isFormData(value) { + return typeof FormData !== "undefined" && value instanceof FormData; + } + /** + * Safely assert whether the given value is a URLSearchParams instance. + * + * In some execution environments URLSearchParams is not defined. + */ + + function isUrlSearchParams(value) { + return typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams; + } + /** + * An outgoing HTTP request with an optional typed body. + * + * `HttpRequest` represents an outgoing request, including URL, method, + * headers, body, and other request configuration options. Instances should be + * assumed to be immutable. To modify a `HttpRequest`, the `clone` + * method should be used. + * + * @publicApi + */ + + class HttpRequest { + constructor(method, url, third, fourth) { + this.url = url; + /** + * The request body, or `null` if one isn't set. + * + * Bodies are not enforced to be immutable, as they can include a reference to any + * user-defined data type. However, interceptors should take care to preserve + * idempotence by treating them as such. + */ + + this.body = null; + /** + * Whether this request should be made in a way that exposes progress events. + * + * Progress events are expensive (change detection runs on each event) and so + * they should only be requested if the consumer intends to monitor them. + */ + + this.reportProgress = false; + /** + * Whether this request should be sent with outgoing credentials (cookies). + */ + + this.withCredentials = false; + /** + * The expected response type of the server. + * + * This is used to parse the response appropriately before returning it to + * the requestee. + */ + + this.responseType = "json"; + this.method = method.toUpperCase(); // Next, need to figure out which argument holds the HttpRequestInit + // options, if any. + + let options; // Check whether a body argument is expected. The only valid way to omit + // the body argument is to use a known no-body method like GET. + + if (mightHaveBody(this.method) || !!fourth) { + // Body is the third argument, options are the fourth. + this.body = third !== undefined ? third : null; + options = fourth; + } else { + // No body required, options are the third argument. The body stays null. + options = third; + } // If options have been passed, interpret them. + + if (options) { + // Normalize reportProgress and withCredentials. + this.reportProgress = !!options.reportProgress; + this.withCredentials = !!options.withCredentials; // Override default response type of 'json' if one is provided. + + if (!!options.responseType) { + this.responseType = options.responseType; + } // Override headers if they're provided. + + if (!!options.headers) { + this.headers = options.headers; + } + + if (!!options.context) { + this.context = options.context; + } + + if (!!options.params) { + this.params = options.params; + } + } // If no headers have been passed in, construct a new HttpHeaders instance. + + if (!this.headers) { + this.headers = new HttpHeaders(); + } // If no context have been passed in, construct a new HttpContext instance. + + if (!this.context) { + this.context = new HttpContext(); + } // If no parameters have been passed in, construct a new HttpUrlEncodedParams instance. + + if (!this.params) { + this.params = new HttpParams(); + this.urlWithParams = url; + } else { + // Encode the parameters to a string in preparation for inclusion in the URL. + const params = this.params.toString(); + + if (params.length === 0) { + // No parameters, the visible URL is just the URL given at creation time. + this.urlWithParams = url; + } else { + // Does the URL already have query parameters? Look for '?'. + const qIdx = url.indexOf("?"); // There are 3 cases to handle: + // 1) No existing parameters -> append '?' followed by params. + // 2) '?' exists and is followed by existing query string -> + // append '&' followed by params. + // 3) '?' exists at the end of the url -> append params directly. + // This basically amounts to determining the character, if any, with + // which to join the URL and parameters. + + const sep = qIdx === -1 ? "?" : qIdx < url.length - 1 ? "&" : ""; + this.urlWithParams = url + sep + params; + } + } + } + /** + * Transform the free-form body into a serialized format suitable for + * transmission to the server. + */ + + serializeBody() { + // If no body is present, no need to serialize it. + if (this.body === null) { + return null; + } // Check whether the body is already in a serialized form. If so, + // it can just be returned directly. + + if ( + isArrayBuffer(this.body) || + isBlob(this.body) || + isFormData(this.body) || + isUrlSearchParams(this.body) || + typeof this.body === "string" + ) { + return this.body; + } // Check whether the body is an instance of HttpUrlEncodedParams. + + if (this.body instanceof HttpParams) { + return this.body.toString(); + } // Check whether the body is an object or array, and serialize with JSON if so. + + if ( + typeof this.body === "object" || + typeof this.body === "boolean" || + Array.isArray(this.body) + ) { + return JSON.stringify(this.body); + } // Fall back on toString() for everything else. + + return this.body.toString(); + } + /** + * Examine the body and attempt to infer an appropriate MIME type + * for it. + * + * If no such type can be inferred, this method will return `null`. + */ + + detectContentTypeHeader() { + // An empty body has no content type. + if (this.body === null) { + return null; + } // FormData bodies rely on the browser's content type assignment. + + if (isFormData(this.body)) { + return null; + } // Blobs usually have their own content type. If it doesn't, then + // no type can be inferred. + + if (isBlob(this.body)) { + return this.body.type || null; + } // Array buffers have unknown contents and thus no type can be inferred. + + if (isArrayBuffer(this.body)) { + return null; + } // Technically, strings could be a form of JSON data, but it's safe enough + // to assume they're plain strings. + + if (typeof this.body === "string") { + return "text/plain"; + } // `HttpUrlEncodedParams` has its own content-type. + + if (this.body instanceof HttpParams) { + return "application/x-www-form-urlencoded;charset=UTF-8"; + } // Arrays, objects, boolean and numbers will be encoded as JSON. + + if ( + typeof this.body === "object" || + typeof this.body === "number" || + typeof this.body === "boolean" + ) { + return "application/json"; + } // No type could be inferred. + + return null; + } + + clone(update = {}) { + var _a; // For method, url, and responseType, take the current value unless + // it is overridden in the update hash. + + const method = update.method || this.method; + const url = update.url || this.url; + const responseType = update.responseType || this.responseType; // The body is somewhat special - a `null` value in update.body means + // whatever current body is present is being overridden with an empty + // body, whereas an `undefined` value in update.body implies no + // override. + + const body = update.body !== undefined ? update.body : this.body; // Carefully handle the boolean options to differentiate between + // `false` and `undefined` in the update args. + + const withCredentials = + update.withCredentials !== undefined ? update.withCredentials : this.withCredentials; + const reportProgress = + update.reportProgress !== undefined ? update.reportProgress : this.reportProgress; // Headers and params may be appended to if `setHeaders` or + // `setParams` are used. + + let headers = update.headers || this.headers; + let params = update.params || this.params; // Pass on context if needed + + const context = (_a = update.context) !== null && _a !== void 0 ? _a : this.context; // Check whether the caller has asked to add headers. + + if (update.setHeaders !== undefined) { + // Set every requested header. + headers = Object.keys(update.setHeaders).reduce( + (headers, name) => headers.set(name, update.setHeaders[name]), + headers + ); + } // Check whether the caller has asked to set params. + + if (update.setParams) { + // Set every requested param. + params = Object.keys(update.setParams).reduce( + (params, param) => params.set(param, update.setParams[param]), + params + ); + } // Finally, construct the new HttpRequest using the pieces from above. + + return new HttpRequest(method, url, body, { + params, + headers, + context, + reportProgress, + responseType, + withCredentials + }); + } + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Type enumeration for the different kinds of `HttpEvent`. + * + * @publicApi + */ + + var HttpEventType; + + (function(HttpEventType) { + /** + * The request was sent out over the wire. + */ + HttpEventType[(HttpEventType["Sent"] = 0)] = "Sent"; + /** + * An upload progress event was received. + */ + + HttpEventType[(HttpEventType["UploadProgress"] = 1)] = "UploadProgress"; + /** + * The response status code and headers were received. + */ + + HttpEventType[(HttpEventType["ResponseHeader"] = 2)] = "ResponseHeader"; + /** + * A download progress event was received. + */ + + HttpEventType[(HttpEventType["DownloadProgress"] = 3)] = "DownloadProgress"; + /** + * The full response including the body was received. + */ + + HttpEventType[(HttpEventType["Response"] = 4)] = "Response"; + /** + * A custom event from an interceptor or a backend. + */ + + HttpEventType[(HttpEventType["User"] = 5)] = "User"; + })(HttpEventType || (HttpEventType = {})); + /** + * Base class for both `HttpResponse` and `HttpHeaderResponse`. + * + * @publicApi + */ + + class HttpResponseBase { + /** + * Super-constructor for all responses. + * + * The single parameter accepted is an initialization hash. Any properties + * of the response passed there will override the default values. + */ + constructor( + init, + defaultStatus = 200, + /* Ok */ + defaultStatusText = "OK" + ) { + // If the hash has values passed, use them to initialize the response. + // Otherwise use the default values. + this.headers = init.headers || new HttpHeaders(); + this.status = init.status !== undefined ? init.status : defaultStatus; + this.statusText = init.statusText || defaultStatusText; + this.url = init.url || null; // Cache the ok value to avoid defining a getter. + + this.ok = this.status >= 200 && this.status < 300; + } + } + /** + * A partial HTTP response which only includes the status and header data, + * but no response body. + * + * `HttpHeaderResponse` is a `HttpEvent` available on the response + * event stream, only when progress events are requested. + * + * @publicApi + */ + + class HttpHeaderResponse extends HttpResponseBase { + /** + * Create a new `HttpHeaderResponse` with the given parameters. + */ + constructor(init = {}) { + super(init); + this.type = HttpEventType.ResponseHeader; + } + /** + * Copy this `HttpHeaderResponse`, overriding its contents with the + * given parameter hash. + */ + + clone(update = {}) { + // Perform a straightforward initialization of the new HttpHeaderResponse, + // overriding the current parameters with new ones if given. + return new HttpHeaderResponse({ + headers: update.headers || this.headers, + status: update.status !== undefined ? update.status : this.status, + statusText: update.statusText || this.statusText, + url: update.url || this.url || undefined + }); + } + } + /** + * A full HTTP response, including a typed response body (which may be `null` + * if one was not returned). + * + * `HttpResponse` is a `HttpEvent` available on the response event + * stream. + * + * @publicApi + */ + + class HttpResponse extends HttpResponseBase { + /** + * Construct a new `HttpResponse`. + */ + constructor(init = {}) { + super(init); + this.type = HttpEventType.Response; + this.body = init.body !== undefined ? init.body : null; + } + + clone(update = {}) { + return new HttpResponse({ + body: update.body !== undefined ? update.body : this.body, + headers: update.headers || this.headers, + status: update.status !== undefined ? update.status : this.status, + statusText: update.statusText || this.statusText, + url: update.url || this.url || undefined + }); + } + } + /** + * A response that represents an error or failure, either from a + * non-successful HTTP status, an error while executing the request, + * or some other failure which occurred during the parsing of the response. + * + * Any error returned on the `Observable` response stream will be + * wrapped in an `HttpErrorResponse` to provide additional context about + * the state of the HTTP layer when the error occurred. The error property + * will contain either a wrapped Error object or the error response returned + * from the server. + * + * @publicApi + */ + + class HttpErrorResponse extends HttpResponseBase { + constructor(init) { + // Initialize with a default status of 0 / Unknown Error. + super(init, 0, "Unknown Error"); + this.name = "HttpErrorResponse"; + /** + * Errors are never okay, even when the status code is in the 2xx success range. + */ + + this.ok = false; // If the response was successful, then this was a parse error. Otherwise, it was + // a protocol-level failure of some sort. Either the request failed in transit + // or the server returned an unsuccessful status code. + + if (this.status >= 200 && this.status < 300) { + this.message = `Http failure during parsing for ${init.url || "(unknown url)"}`; + } else { + this.message = `Http failure response for ${init.url || "(unknown url)"}: ${init.status} ${ + init.statusText + }`; + } + + this.error = init.error || null; + } + } + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * Constructs an instance of `HttpRequestOptions` from a source `HttpMethodOptions` and + * the given `body`. This function clones the object and adds the body. + * + * Note that the `responseType` *options* value is a String that identifies the + * single data type of the response. + * A single overload version of the method handles each response type. + * The value of `responseType` cannot be a union, as the combined signature could imply. + * + */ + + function addBody(options, body) { + return { + body, + headers: options.headers, + context: options.context, + observe: options.observe, + params: options.params, + reportProgress: options.reportProgress, + responseType: options.responseType, + withCredentials: options.withCredentials + }; + } + /** + * Performs HTTP requests. + * This service is available as an injectable class, with methods to perform HTTP requests. + * Each request method has multiple signatures, and the return type varies based on + * the signature that is called (mainly the values of `observe` and `responseType`). + * + * Note that the `responseType` *options* value is a String that identifies the + * single data type of the response. + * A single overload version of the method handles each response type. + * The value of `responseType` cannot be a union, as the combined signature could imply. + + * + * @usageNotes + * Sample HTTP requests for the [Tour of Heroes](/tutorial/toh-pt0) application. + * + * ### HTTP Request Example + * + * ``` + * // GET heroes whose name contains search term + * searchHeroes(term: string): observable{ + * + * const params = new HttpParams({fromString: 'name=term'}); + * return this.httpClient.request('GET', this.heroesUrl, {responseType:'json', params}); + * } + * ``` + * + * Alternatively, the parameter string can be used without invoking HttpParams + * by directly joining to the URL. + * ``` + * this.httpClient.request('GET', this.heroesUrl + '?' + 'name=term', {responseType:'json'}); + * ``` + * + * + * ### JSONP Example + * ``` + * requestJsonp(url, callback = 'callback') { + * return this.httpClient.jsonp(this.heroesURL, callback); + * } + * ``` + * + * ### PATCH Example + * ``` + * // PATCH one of the heroes' name + * patchHero (id: number, heroName: string): Observable<{}> { + * const url = `${this.heroesUrl}/${id}`; // PATCH api/heroes/42 + * return this.httpClient.patch(url, {name: heroName}, httpOptions) + * .pipe(catchError(this.handleError('patchHero'))); + * } + * ``` + * + * @see [HTTP Guide](guide/http) + * @see [HTTP Request](api/common/http/HttpRequest) + * + * @publicApi + */ + + class HttpClient { + constructor(handler) { + this.handler = handler; + } + /** + * Constructs an observable for a generic HTTP request that, when subscribed, + * fires the request through the chain of registered interceptors and on to the + * server. + * + * You can pass an `HttpRequest` directly as the only parameter. In this case, + * the call returns an observable of the raw `HttpEvent` stream. + * + * Alternatively you can pass an HTTP method as the first parameter, + * a URL string as the second, and an options hash containing the request body as the third. + * See `addBody()`. In this case, the specified `responseType` and `observe` options determine the + * type of returned observable. + * * The `responseType` value determines how a successful response body is parsed. + * * If `responseType` is the default `json`, you can pass a type interface for the resulting + * object as a type parameter to the call. + * + * The `observe` value determines the return type, according to what you are interested in + * observing. + * * An `observe` value of events returns an observable of the raw `HttpEvent` stream, including + * progress events by default. + * * An `observe` value of response returns an observable of `HttpResponse`, + * where the `T` parameter depends on the `responseType` and any optionally provided type + * parameter. + * * An `observe` value of body returns an observable of `` with the same `T` body type. + * + */ + + request(first, url, options = {}) { + let req; // First, check whether the primary argument is an instance of `HttpRequest`. + + if (first instanceof HttpRequest) { + // It is. The other arguments must be undefined (per the signatures) and can be + // ignored. + req = first; + } else { + // It's a string, so it represents a URL. Construct a request based on it, + // and incorporate the remaining arguments (assuming `GET` unless a method is + // provided. + // Figure out the headers. + let headers = undefined; + + if (options.headers instanceof HttpHeaders) { + headers = options.headers; + } else { + headers = new HttpHeaders(options.headers); + } // Sort out parameters. + + let params = undefined; + + if (!!options.params) { + if (options.params instanceof HttpParams) { + params = options.params; + } else { + params = new HttpParams({ + fromObject: options.params + }); + } + } // Construct the request. + + req = new HttpRequest(first, url, options.body !== undefined ? options.body : null, { + headers, + context: options.context, + params, + reportProgress: options.reportProgress, + // By default, JSON is assumed to be returned for all calls. + responseType: options.responseType || "json", + withCredentials: options.withCredentials + }); + } // Start with an Observable.of() the initial request, and run the handler (which + // includes all interceptors) inside a concatMap(). This way, the handler runs + // inside an Observable chain, which causes interceptors to be re-run on every + // subscription (this also makes retries re-run the handler, including interceptors). + + const events$ = (0, rxjs__WEBPACK_IMPORTED_MODULE_0__.of)(req).pipe( + (0, rxjs_operators__WEBPACK_IMPORTED_MODULE_1__.concatMap)(req => this.handler.handle(req)) + ); // If coming via the API signature which accepts a previously constructed HttpRequest, + // the only option is to get the event stream. Otherwise, return the event stream if + // that is what was requested. + + if (first instanceof HttpRequest || options.observe === "events") { + return events$; + } // The requested stream contains either the full response or the body. In either + // case, the first step is to filter the event stream to extract a stream of + // responses(s). + + const res$ = events$.pipe( + (0, rxjs_operators__WEBPACK_IMPORTED_MODULE_2__.filter)( + event => event instanceof HttpResponse + ) + ); // Decide which stream to return. + + switch (options.observe || "body") { + case "body": + // The requested stream is the body. Map the response stream to the response + // body. This could be done more simply, but a misbehaving interceptor might + // transform the response body into a different format and ignore the requested + // responseType. Guard against this by validating that the response is of the + // requested type. + switch (req.responseType) { + case "arraybuffer": + return res$.pipe( + (0, rxjs_operators__WEBPACK_IMPORTED_MODULE_3__.map)(res => { + // Validate that the body is an ArrayBuffer. + if (res.body !== null && !(res.body instanceof ArrayBuffer)) { + throw new Error("Response is not an ArrayBuffer."); + } + + return res.body; + }) + ); + + case "blob": + return res$.pipe( + (0, rxjs_operators__WEBPACK_IMPORTED_MODULE_3__.map)(res => { + // Validate that the body is a Blob. + if (res.body !== null && !(res.body instanceof Blob)) { + throw new Error("Response is not a Blob."); + } + + return res.body; + }) + ); + + case "text": + return res$.pipe( + (0, rxjs_operators__WEBPACK_IMPORTED_MODULE_3__.map)(res => { + // Validate that the body is a string. + if (res.body !== null && typeof res.body !== "string") { + throw new Error("Response is not a string."); + } + + return res.body; + }) + ); + + case "json": + default: + // No validation needed for JSON responses, as they can be of any type. + return res$.pipe( + (0, rxjs_operators__WEBPACK_IMPORTED_MODULE_3__.map)(res => res.body) + ); + } + + case "response": + // The response stream was requested directly, so return it. + return res$; + + default: + // Guard against new future observe types being added. + throw new Error(`Unreachable: unhandled observe type ${options.observe}}`); + } + } + /** + * Constructs an observable that, when subscribed, causes the configured + * `DELETE` request to execute on the server. See the individual overloads for + * details on the return type. + * + * @param url The endpoint URL. + * @param options The HTTP options to send with the request. + * + */ + + delete(url, options = {}) { + return this.request("DELETE", url, options); + } + /** + * Constructs an observable that, when subscribed, causes the configured + * `GET` request to execute on the server. See the individual overloads for + * details on the return type. + */ + + get(url, options = {}) { + return this.request("GET", url, options); + } + /** + * Constructs an observable that, when subscribed, causes the configured + * `HEAD` request to execute on the server. The `HEAD` method returns + * meta information about the resource without transferring the + * resource itself. See the individual overloads for + * details on the return type. + */ + + head(url, options = {}) { + return this.request("HEAD", url, options); + } + /** + * Constructs an `Observable` that, when subscribed, causes a request with the special method + * `JSONP` to be dispatched via the interceptor pipeline. + * The [JSONP pattern](https://en.wikipedia.org/wiki/JSONP) works around limitations of certain + * API endpoints that don't support newer, + * and preferable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) protocol. + * JSONP treats the endpoint API as a JavaScript file and tricks the browser to process the + * requests even if the API endpoint is not located on the same domain (origin) as the client-side + * application making the request. + * The endpoint API must support JSONP callback for JSONP requests to work. + * The resource API returns the JSON response wrapped in a callback function. + * You can pass the callback function name as one of the query parameters. + * Note that JSONP requests can only be used with `GET` requests. + * + * @param url The resource URL. + * @param callbackParam The callback function name. + * + */ + + jsonp(url, callbackParam) { + return this.request("JSONP", url, { + params: new HttpParams().append(callbackParam, "JSONP_CALLBACK"), + observe: "body", + responseType: "json" + }); + } + /** + * Constructs an `Observable` that, when subscribed, causes the configured + * `OPTIONS` request to execute on the server. This method allows the client + * to determine the supported HTTP methods and other capabilities of an endpoint, + * without implying a resource action. See the individual overloads for + * details on the return type. + */ + + options(url, options = {}) { + return this.request("OPTIONS", url, options); + } + /** + * Constructs an observable that, when subscribed, causes the configured + * `PATCH` request to execute on the server. See the individual overloads for + * details on the return type. + */ + + patch(url, body, options = {}) { + return this.request("PATCH", url, addBody(options, body)); + } + /** + * Constructs an observable that, when subscribed, causes the configured + * `POST` request to execute on the server. The server responds with the location of + * the replaced resource. See the individual overloads for + * details on the return type. + */ + + post(url, body, options = {}) { + return this.request("POST", url, addBody(options, body)); + } + /** + * Constructs an observable that, when subscribed, causes the configured + * `PUT` request to execute on the server. The `PUT` method replaces an existing resource + * with a new set of values. + * See the individual overloads for details on the return type. + */ + + put(url, body, options = {}) { + return this.request("PUT", url, addBody(options, body)); + } + } + + HttpClient.ɵfac = function HttpClient_Factory(t) { + return new (t || HttpClient)(_angular_core__WEBPACK_IMPORTED_MODULE_4__["ɵɵinject"](HttpHandler)); + }; + + HttpClient.ɵprov = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_4__["ɵɵdefineInjectable"]({ + token: HttpClient, + factory: HttpClient.ɵfac + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_4__["ɵsetClassMetadata"]( + HttpClient, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_4__.Injectable + } + ], + function() { + return [ + { + type: HttpHandler + } + ]; + }, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + /** + * `HttpHandler` which applies an `HttpInterceptor` to an `HttpRequest`. + * + * + */ + + class HttpInterceptorHandler { + constructor(next, interceptor) { + this.next = next; + this.interceptor = interceptor; + } + + handle(req) { + return this.interceptor.intercept(req, this.next); + } + } + /** + * A multi-provider token that represents the array of registered + * `HttpInterceptor` objects. + * + * @publicApi + */ + + const HTTP_INTERCEPTORS = new _angular_core__WEBPACK_IMPORTED_MODULE_4__.InjectionToken( + "HTTP_INTERCEPTORS" + ); + + class NoopInterceptor { + intercept(req, next) { + return next.handle(req); + } + } + + NoopInterceptor.ɵfac = function NoopInterceptor_Factory(t) { + return new (t || NoopInterceptor)(); + }; + + NoopInterceptor.ɵprov = /* @__PURE__ */ _angular_core__WEBPACK_IMPORTED_MODULE_4__[ + "ɵɵdefineInjectable" + ]({ + token: NoopInterceptor, + factory: NoopInterceptor.ɵfac + }); + + (function() { + (typeof ngDevMode === "undefined" || ngDevMode) && + _angular_core__WEBPACK_IMPORTED_MODULE_4__["ɵsetClassMetadata"]( + NoopInterceptor, + [ + { + type: _angular_core__WEBPACK_IMPORTED_MODULE_4__.Injectable + } + ], + null, + null + ); + })(); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + // Every request made through JSONP needs a callback name that's unique across the + // whole page. Each request is assigned an id and the callback name is constructed + // from that. The next id to be assigned is tracked in a global variable here that + // is shared among all applications on the page. + + let nextRequestId = 0; + /** + * When a pending + + + + + + + + diff --git a/tests/litestar/__init__.py b/tests/litestar/__init__.py new file mode 100644 index 000000000..e0e2433cc --- /dev/null +++ b/tests/litestar/__init__.py @@ -0,0 +1,3 @@ +import nest_asyncio # type: ignore + +nest_asyncio.apply() # type: ignore diff --git a/tests/litestar/test_litestar.py b/tests/litestar/test_litestar.py new file mode 100644 index 000000000..439c10fbd --- /dev/null +++ b/tests/litestar/test_litestar.py @@ -0,0 +1,1136 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import json +from typing import Any, Dict, Optional, Union +from unittest import skip + +from litestar import Litestar, Request, Response, get, post +from litestar.di import Provide +from litestar.testing import TestClient +from pytest import fixture, mark +from supertokens_python import InputAppInfo, SupertokensConfig, init +from supertokens_python.framework import BaseRequest +from supertokens_python.framework.litestar import ( + create_supertokens_middleware, + get_exception_handlers, + get_supertokens_plugin, +) +from supertokens_python.querier import Querier +from supertokens_python.recipe import emailpassword, session, thirdparty +from supertokens_python.recipe.dashboard import DashboardRecipe, InputOverrideConfig +from supertokens_python.recipe.dashboard.interfaces import RecipeInterface +from supertokens_python.recipe.dashboard.utils import DashboardConfig +from supertokens_python.recipe.emailpassword.interfaces import ( + APIInterface as EPAPIInterface, +) +from supertokens_python.recipe.emailpassword.interfaces import APIOptions +from supertokens_python.recipe.passwordless import ContactConfig, PasswordlessRecipe +from supertokens_python.recipe.session import SessionContainer +from supertokens_python.recipe.session.asyncio import ( + create_new_session, + get_session, + refresh_session, +) +from supertokens_python.recipe.session.exceptions import UnauthorisedError +from supertokens_python.recipe.session.framework.litestar import verify_session +from supertokens_python.recipe.session.interfaces import APIInterface +from supertokens_python.recipe.session.interfaces import APIOptions as SessionAPIOptions +from supertokens_python.types import RecipeUserId +from supertokens_python.utils import is_version_gte + +from tests.utils import ( + TEST_DRIVER_CONFIG_ACCESS_TOKEN_PATH, + TEST_DRIVER_CONFIG_COOKIE_DOMAIN, + TEST_DRIVER_CONFIG_COOKIE_SAME_SITE, + TEST_DRIVER_CONFIG_REFRESH_TOKEN_PATH, + assert_info_clears_tokens, + create_users, + extract_all_cookies, + extract_info, + get_new_core_app_url, + get_st_init_args, +) + + +def override_dashboard_functions(original_implementation: RecipeInterface): + async def should_allow_access( + request: BaseRequest, config: DashboardConfig, user_context: Dict[str, Any] + ): + auth_header = request.get_header("authorization") + return auth_header == "Bearer testapikey" + + original_implementation.should_allow_access = should_allow_access + return original_implementation + + +@fixture(scope="function") +def driver_config_client() -> TestClient[Litestar]: + @get("/login") + async def login(request: Request[Any, Any, Any]) -> Dict[str, Any]: + user_id = "userId" + await create_new_session( + request=request, + tenant_id="public", + recipe_user_id=RecipeUserId(user_id), + access_token_payload={}, + session_data_in_database={}, + ) + return {"userId": user_id} + + @post("/refresh") + async def custom_refresh( + request: Request[Any, Any, Any], + ) -> Union[Dict[str, Any], Response[Any]]: + try: + await refresh_session(request) + return Response(content=None) + except UnauthorisedError: + return Response(content={"message": "Unauthorized"}, status_code=401) + + @get("/info") + async def info_get(request: Request[Any, Any, Any]) -> Dict[str, Any]: + await get_session(request, True) + return {} + + @get("/custom/info", sync_to_thread=True) + def custom_info() -> Dict[str, Any]: + return {} + + @get("/handle") + async def handle_get( + request: Request[Any, Any, Any], + ) -> Dict[str, Any]: + session: Union[None, SessionContainer] = await get_session(request, True) + if session is None: + raise Exception("Should never come here") + return {"s": session.get_handle()} + + @get( + "/handle-session-optional", + dependencies={ + "session": Provide( + verify_session(session_required=False), + ), + }, + ) + async def handle_get_optional( + session: Optional[SessionContainer], + ) -> Dict[str, Any]: + if session is None: + return {"s": "empty session"} + return {"s": session.get_handle()} + + @post("/logout") + async def custom_logout(request: Request[Any, Any, Any]) -> Dict[str, Any]: + session: Union[None, SessionContainer] = await get_session(request, True) + if session is None: + raise Exception("Should never come here") + await session.revoke_session() + return {} + + @post("/create") + async def _create(request: Request[Any, Any, Any]) -> Dict[str, Any]: + await create_new_session( + request=request, + tenant_id="public", + recipe_user_id=RecipeUserId("userId"), + access_token_payload={}, + session_data_in_database={}, + ) + return {} + + @post("/create-throw") + async def _create_throw(request: Request[Any, Any, Any]) -> None: + await create_new_session( + request=request, + tenant_id="public", + recipe_user_id=RecipeUserId("userId"), + access_token_payload={}, + session_data_in_database={}, + ) + raise UnauthorisedError("unauthorised") + + app = Litestar( + route_handlers=[ + login, + custom_refresh, + info_get, + custom_info, + handle_get, + handle_get_optional, + custom_logout, + _create, + _create_throw, + ], + middleware=[create_supertokens_middleware()], + plugins=[get_supertokens_plugin(mount_path="/auth")], + exception_handlers=get_exception_handlers(), + ) + return TestClient(app) + + +def apis_override_session(param: APIInterface): + param.disable_refresh_post = True + return param + + +@mark.asyncio +async def test_login_refresh(driver_config_client: TestClient[Litestar]): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + override=session.InputOverrideConfig(apis=apis_override_session), + ) + ], + ) + + response_1 = driver_config_client.get("/login") + cookies_1 = extract_all_cookies(response_1) + + assert response_1.headers.get("anti-csrf") is not None + assert cookies_1["sAccessToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_1["sRefreshToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_1["sAccessToken"]["path"] == TEST_DRIVER_CONFIG_ACCESS_TOKEN_PATH + assert cookies_1["sRefreshToken"]["path"] == TEST_DRIVER_CONFIG_REFRESH_TOKEN_PATH + assert cookies_1["sAccessToken"]["httponly"] + assert cookies_1["sRefreshToken"]["httponly"] + assert ( + cookies_1["sAccessToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert ( + cookies_1["sRefreshToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + + response_3 = driver_config_client.post( + url="/refresh", + headers={"anti-csrf": response_1.headers.get("anti-csrf")}, + cookies={ + "sRefreshToken": cookies_1["sRefreshToken"]["value"], + }, + ) + cookies_3 = extract_all_cookies(response_3) + + assert cookies_3["sAccessToken"]["value"] != cookies_1["sAccessToken"]["value"] + assert cookies_3["sRefreshToken"]["value"] != cookies_1["sRefreshToken"]["value"] + assert response_3.headers.get("anti-csrf") is not None + assert cookies_3["sAccessToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_3["sRefreshToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_3["sRefreshToken"]["path"] == TEST_DRIVER_CONFIG_REFRESH_TOKEN_PATH + assert cookies_3["sAccessToken"]["httponly"] + assert cookies_3["sRefreshToken"]["httponly"] + assert ( + cookies_3["sAccessToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert ( + cookies_3["sRefreshToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + + +@mark.asyncio +async def test_login_logout(driver_config_client: TestClient[Litestar]): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ) + ], + ) + + response_1 = driver_config_client.get("/login") + cookies_1 = extract_all_cookies(response_1) + + assert response_1.headers.get("anti-csrf") is not None + assert cookies_1["sAccessToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_1["sRefreshToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_1["sAccessToken"]["path"] == TEST_DRIVER_CONFIG_ACCESS_TOKEN_PATH + assert cookies_1["sRefreshToken"]["path"] == TEST_DRIVER_CONFIG_REFRESH_TOKEN_PATH + assert cookies_1["sAccessToken"]["httponly"] + assert cookies_1["sRefreshToken"]["httponly"] + assert ( + cookies_1["sAccessToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert ( + cookies_1["sRefreshToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert cookies_1["sAccessToken"]["secure"] is None + assert cookies_1["sRefreshToken"]["secure"] is None + + response_2 = driver_config_client.post( + url="/logout", + headers={"anti-csrf": response_1.headers.get("anti-csrf")}, + cookies={ + "sAccessToken": cookies_1["sAccessToken"]["value"], + }, + ) + cookies_2 = extract_all_cookies(response_2) + assert response_2.headers.get("anti-csrf") is None + assert cookies_2["sAccessToken"]["value"] == "" + assert cookies_2["sRefreshToken"]["value"] == "" + assert cookies_2["sAccessToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_2["sRefreshToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_2["sAccessToken"]["path"] == TEST_DRIVER_CONFIG_ACCESS_TOKEN_PATH + assert cookies_2["sAccessToken"]["httponly"] + assert cookies_2["sRefreshToken"]["httponly"] + assert ( + cookies_2["sAccessToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert ( + cookies_2["sRefreshToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert cookies_2["sAccessToken"]["secure"] is None + assert cookies_2["sRefreshToken"]["secure"] is None + + +@mark.asyncio +async def test_login_info(driver_config_client: TestClient[Litestar]): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ) + ], + ) + + response_1 = driver_config_client.get("/login") + cookies_1 = extract_all_cookies(response_1) + + assert response_1.headers.get("anti-csrf") is not None + assert cookies_1["sAccessToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_1["sRefreshToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_1["sAccessToken"]["path"] == TEST_DRIVER_CONFIG_ACCESS_TOKEN_PATH + assert cookies_1["sRefreshToken"]["path"] == TEST_DRIVER_CONFIG_REFRESH_TOKEN_PATH + assert cookies_1["sAccessToken"]["httponly"] + assert cookies_1["sRefreshToken"]["httponly"] + assert ( + cookies_1["sAccessToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert ( + cookies_1["sRefreshToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert cookies_1["sAccessToken"]["secure"] is None + assert cookies_1["sRefreshToken"]["secure"] is None + + response_2 = driver_config_client.get( + url="/info", + headers={"anti-csrf": response_1.headers.get("anti-csrf")}, + cookies={ + "sAccessToken": cookies_1["sAccessToken"]["value"], + }, + ) + cookies_2 = extract_all_cookies(response_2) + assert not cookies_2 + + +@mark.asyncio +async def test_login_handle(driver_config_client: TestClient[Litestar]): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ) + ], + ) + + response_1 = driver_config_client.get("/login") + cookies_1 = extract_all_cookies(response_1) + + assert response_1.headers.get("anti-csrf") is not None + assert cookies_1["sAccessToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_1["sRefreshToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_1["sAccessToken"]["path"] == TEST_DRIVER_CONFIG_ACCESS_TOKEN_PATH + assert cookies_1["sRefreshToken"]["path"] == TEST_DRIVER_CONFIG_REFRESH_TOKEN_PATH + assert cookies_1["sAccessToken"]["httponly"] + assert cookies_1["sRefreshToken"]["httponly"] + assert ( + cookies_1["sAccessToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert ( + cookies_1["sRefreshToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert cookies_1["sAccessToken"]["secure"] is None + assert cookies_1["sRefreshToken"]["secure"] is None + + response_2 = driver_config_client.get( + url="/handle", + headers={"anti-csrf": response_1.headers.get("anti-csrf")}, + cookies={ + "sAccessToken": cookies_1["sAccessToken"]["value"], + }, + ) + result_Dict = json.loads(response_2.content) + assert "s" in result_Dict + + +@mark.asyncio +async def test_login_refresh_error_handler(driver_config_client: TestClient[Litestar]): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ) + ], + ) + + response_1 = driver_config_client.get("/login") + cookies_1 = extract_all_cookies(response_1) + + assert response_1.headers.get("anti-csrf") is not None + assert cookies_1["sAccessToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_1["sRefreshToken"]["domain"] == TEST_DRIVER_CONFIG_COOKIE_DOMAIN + assert cookies_1["sAccessToken"]["path"] == TEST_DRIVER_CONFIG_ACCESS_TOKEN_PATH + assert cookies_1["sRefreshToken"]["path"] == TEST_DRIVER_CONFIG_REFRESH_TOKEN_PATH + assert cookies_1["sAccessToken"]["httponly"] + assert cookies_1["sRefreshToken"]["httponly"] + assert ( + cookies_1["sAccessToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert ( + cookies_1["sRefreshToken"]["samesite"].lower() + == TEST_DRIVER_CONFIG_COOKIE_SAME_SITE + ) + assert cookies_1["sAccessToken"]["secure"] is None + assert cookies_1["sRefreshToken"]["secure"] is None + + response_3 = driver_config_client.post( + url="/refresh", + headers={"anti-csrf": response_1.headers.get("anti-csrf")}, + cookies={ + # no cookies + }, + ) + assert response_3.status_code == 401 # not authorized because no refresh tokens + + +@mark.asyncio +async def test_custom_response(driver_config_client: TestClient[Litestar]): + def override_email_password_apis(original_implementation: EPAPIInterface): + original_func = original_implementation.email_exists_get + + async def email_exists_get( + email: str, + tenant_id: str, + api_options: APIOptions, + user_context: Dict[str, Any], + ): + response_dict = {"custom": True} + api_options.response.set_status_code(203) + api_options.response.set_json_content(response_dict) + return await original_func(email, tenant_id, api_options, user_context) + + original_implementation.email_exists_get = email_exists_get + return original_implementation + + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + emailpassword.init( + override=emailpassword.InputOverrideConfig( + apis=override_email_password_apis + ) + ) + ], + ) + + response = driver_config_client.get( + url="/auth/signup/email/exists?email=test@example.com", + ) + + dict_response = json.loads(response.text) + assert response.status_code == 203 + assert dict_response["custom"] + + +@mark.asyncio +async def test_optional_session(driver_config_client: TestClient[Litestar]): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init(get_token_transfer_method=lambda _, __, ___: "cookie") + ], + ) + + response = driver_config_client.get( + url="handle-session-optional", + ) + + dict_response = json.loads(response.text) + assert response.status_code == 200 + assert dict_response["s"] == "empty session" + + +@mark.parametrize( + "litestar_root_path", + [ + "/api/v1", + "/api", + "api", + # Don't pass "/" as litestar_root_path. You'll get unexpected behaviour + # because api_base_path will be "//auth" + ], +) +def test_litestar_root_path(litestar_root_path: str): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path=f"{litestar_root_path}/auth", # Full external path + ), + framework="litestar", + recipe_list=[ + session.init(get_token_transfer_method=lambda _, __, ___: "cookie"), + emailpassword.init(), + ], + ) + + # Test with root_path + if litestar_root_path.startswith("/"): + litestar_root_path = litestar_root_path[1:] + app = Litestar( + path=litestar_root_path, + middleware=[create_supertokens_middleware()], + plugins=[ + get_supertokens_plugin( + mount_path="/auth", # Mount at /auth relative to app.path + ) + ], + ) + + test_client = TestClient(app) + + response = test_client.get( + f"{litestar_root_path}/auth/signup/email/exists?email=test@example.com" + ) + assert response.status_code == 200 + assert response.json() == {"status": "OK", "exists": False} + + # The API should not be available without the root path (returns 404) + response = test_client.get("/auth/signup/email/exists?email=test@example.com") + assert response.status_code == 404 + + +@mark.asyncio +@mark.parametrize("token_transfer_method", ["cookie", "header"]) +async def test_should_clear_all_response_during_refresh_if_unauthorized( + driver_config_client: TestClient[Litestar], token_transfer_method: str +): + def override_session_apis(oi: APIInterface): + oi_refresh_post = oi.refresh_post + + async def refresh_post( + api_options: SessionAPIOptions, user_context: Dict[str, Any] + ): + await oi_refresh_post(api_options, user_context) + raise UnauthorisedError("unauthorized", clear_tokens=True) + + oi.refresh_post = refresh_post + return oi + + init( + **get_st_init_args( + url=get_new_core_app_url(), + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + override=session.InputOverrideConfig(apis=override_session_apis), + ) + ], + ) + ) + + res = driver_config_client.post( + "/create", headers={"st-auth-mode": token_transfer_method} + ) + info = extract_info(res) + + assert info["accessTokenFromAny"] is not None + assert info["refreshTokenFromAny"] is not None + + headers: Dict[str, Any] = {} + cookies: Dict[str, Any] = {} + + if token_transfer_method == "header": + headers.update({"authorization": f"Bearer {info['refreshTokenFromAny']}"}) + else: + # Clear existing cookies from the client to avoid duplicate cookie issues + driver_config_client.cookies.clear() + cookies.update( + {"sRefreshToken": info["refreshTokenFromAny"], "sIdRefreshToken": "asdf"} + ) + + if info["antiCsrf"] is not None: + headers.update({"anti-csrf": info["antiCsrf"]}) + + res = driver_config_client.post( + "/auth/session/refresh", headers=headers, cookies=cookies + ) + info = extract_info(res) + + assert res.status_code == 401 + assert_info_clears_tokens(info, token_transfer_method) + + +@mark.asyncio +@mark.parametrize("token_transfer_method", ["cookie", "header"]) +async def test_revoking_session_after_create_new_session_with_throwing_unauthorized_error( + driver_config_client: TestClient[Litestar], token_transfer_method: str +): + init( + **get_st_init_args( + url=get_new_core_app_url(), + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + ) + ], + ) + ) + + res = driver_config_client.post( + "/create-throw", headers={"st-auth-mode": token_transfer_method} + ) + info = extract_info(res) + + assert res.status_code == 401 + assert_info_clears_tokens(info, token_transfer_method) + + +@mark.asyncio +async def test_session_with_legacy_refresh_token_and_unauthorized_should_clear_legacy_token( + driver_config_client: TestClient[Litestar], +): + init( + **get_st_init_args( + url=get_new_core_app_url(), + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + ) + ], + ) + ) + + headers: Dict[str, Any] = {} + cookies: Dict[str, Any] = {} + + cookies.update( + # Invalid refresh token so that core throws an unauthorized error + {"sRefreshToken": "non-existing-token", "sIdRefreshToken": "irrelevant-value"} + ) + + res = driver_config_client.post( + "/auth/session/refresh", headers=headers, cookies=cookies + ) + info = extract_info(res) + + assert res.status_code == 401 + assert res.json() == {"message": "unauthorised"} + assert_info_clears_tokens(info, "cookie") + + assert info["sIdRefreshToken"]["value"] == "" + assert info["sIdRefreshToken"]["expires"] == "Thu, 01 Jan 1970 00:00:00 GMT" + + +@mark.asyncio +async def test_search_with_email_t(driver_config_client: TestClient[Litestar]): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ), + DashboardRecipe.init( + api_key="testapikey", + override=InputOverrideConfig(functions=override_dashboard_functions), + ), + emailpassword.init(), + ], + ) + querier = Querier.get_instance(DashboardRecipe.recipe_id) + cdi_version = await querier.get_api_version() + if not cdi_version: + skip("CDI version not available") + if not is_version_gte(cdi_version, "2.20"): + skip("CDI version too old") + await create_users(emailpassword=True) + query = {"limit": "10", "email": "t"} + res = driver_config_client.get( + "/auth/dashboard/api/users", + headers={ + "Authorization": "Bearer testapikey", + "Content-Type": "application/json", + }, + params=query, + ) + info = extract_info(res) + assert res.status_code == 200 + assert len(info["body"]["users"]) == 5 + + +@mark.asyncio +async def test_search_with_email_multiple_email_entry( + driver_config_client: TestClient[Litestar], +): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ), + DashboardRecipe.init( + api_key="testapikey", + override=InputOverrideConfig(functions=override_dashboard_functions), + ), + emailpassword.init(), + ], + ) + querier = Querier.get_instance(DashboardRecipe.recipe_id) + cdi_version = await querier.get_api_version() + if not cdi_version: + skip("CDI version not available") + if not is_version_gte(cdi_version, "2.20"): + skip("CDI version too old") + await create_users(emailpassword=True) + query = {"limit": "10", "email": "iresh;john"} + res = driver_config_client.get( + "/auth/dashboard/api/users", + headers={ + "Authorization": "Bearer testapikey", + "Content-Type": "application/json", + }, + params=query, + ) + info = extract_info(res) + assert res.status_code == 200 + assert len(info["body"]["users"]) == 1 + + +@mark.asyncio +async def test_search_with_email_iresh(driver_config_client: TestClient[Litestar]): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ), + DashboardRecipe.init( + api_key="testapikey", + override=InputOverrideConfig(functions=override_dashboard_functions), + ), + emailpassword.init(), + ], + ) + querier = Querier.get_instance(DashboardRecipe.recipe_id) + cdi_version = await querier.get_api_version() + if not cdi_version: + skip("CDI version not available") + if not is_version_gte(cdi_version, "2.20"): + skip("CDI version too old") + await create_users(emailpassword=True) + query = {"limit": "10", "email": "iresh"} + res = driver_config_client.get( + "/auth/dashboard/api/users", + headers={ + "Authorization": "Bearer testapikey", + "Content-Type": "application/json", + }, + params=query, + ) + info = extract_info(res) + assert res.status_code == 200 + assert len(info["body"]["users"]) == 0 + + +@mark.asyncio +async def test_search_with_phone_plus_one(driver_config_client: TestClient[Litestar]): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ), + DashboardRecipe.init( + api_key="testapikey", + override=InputOverrideConfig(functions=override_dashboard_functions), + ), + PasswordlessRecipe.init( + contact_config=ContactConfig(contact_method="EMAIL"), + flow_type="USER_INPUT_CODE", + ), + ], + ) + querier = Querier.get_instance(DashboardRecipe.recipe_id) + cdi_version = await querier.get_api_version() + if not cdi_version: + skip("CDI version not available") + if not is_version_gte(cdi_version, "2.20"): + skip("CDI version too old") + await create_users(passwordless=True) + query = {"limit": "10", "phone": "+1"} + res = driver_config_client.get( + "/auth/dashboard/api/users", + headers={ + "Authorization": "Bearer testapikey", + "Content-Type": "application/json", + }, + params=query, + ) + info = extract_info(res) + assert res.status_code == 200 + assert len(info["body"]["users"]) == 3 + + +@mark.asyncio +async def test_search_with_phone_one_bracket( + driver_config_client: TestClient[Litestar], +): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ), + DashboardRecipe.init( + api_key="testapikey", + override=InputOverrideConfig(functions=override_dashboard_functions), + ), + PasswordlessRecipe.init( + contact_config=ContactConfig(contact_method="EMAIL"), + flow_type="USER_INPUT_CODE", + ), + ], + ) + querier = Querier.get_instance(DashboardRecipe.recipe_id) + cdi_version = await querier.get_api_version() + if not cdi_version: + skip("CDI version not available") + if not is_version_gte(cdi_version, "2.20"): + skip("CDI version too old") + await create_users(passwordless=True) + query = {"limit": "10", "phone": "1("} + res = driver_config_client.get( + "/auth/dashboard/api/users", + headers={ + "Authorization": "Bearer testapikey", + "Content-Type": "application/json", + }, + params=query, + ) + info = extract_info(res) + assert res.status_code == 200 + assert len(info["body"]["users"]) == 0 + + +@mark.asyncio +async def test_search_with_provider_google(driver_config_client: TestClient[Litestar]): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ), + DashboardRecipe.init( + api_key="testapikey", + override=InputOverrideConfig(functions=override_dashboard_functions), + ), + thirdparty.init( + sign_in_and_up_feature=thirdparty.SignInAndUpFeature( + providers=[ + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="apple", + clients=[ + thirdparty.ProviderClientConfig( + client_id="4398792-io.supertokens.example.service", + additional_config={ + "keyId": "7M48Y4RYDL", + "teamId": "YWQCXGJRJL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + }, + ), + ], + ) + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="google", + clients=[ + thirdparty.ProviderClientConfig( + client_id="467101b197249757c71f", + client_secret="e97051221f4b6426e8fe8d51486396703012f5bd", + ), + ], + ) + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="github", + clients=[ + thirdparty.ProviderClientConfig( + client_id="1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + client_secret="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", + ), + ], + ) + ), + ], + ) + ), + ], + ) + querier = Querier.get_instance(DashboardRecipe.recipe_id) + cdi_version = await querier.get_api_version() + if not cdi_version: + skip("CDI version not available") + if not is_version_gte(cdi_version, "2.20"): + skip("CDI version too old") + await create_users(thirdparty=True) + query = {"limit": "10", "provider": "google"} + res = driver_config_client.get( + "/auth/dashboard/api/users", + headers={ + "Authorization": "Bearer testapikey", + "Content-Type": "application/json", + }, + params=query, + ) + info = extract_info(res) + assert res.status_code == 200 + assert len(info["body"]["users"]) == 3 + + +@mark.asyncio +async def test_search_with_provider_google_and_phone_1( + driver_config_client: TestClient[Litestar], +): + init( + supertokens_config=SupertokensConfig(get_new_core_app_url()), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="litestar", + recipe_list=[ + session.init( + anti_csrf="VIA_TOKEN", + cookie_domain="supertokens.io", + get_token_transfer_method=lambda _, __, ___: "cookie", + ), + DashboardRecipe.init( + api_key="testapikey", + override=InputOverrideConfig(functions=override_dashboard_functions), + ), + PasswordlessRecipe.init( + contact_config=ContactConfig(contact_method="EMAIL"), + flow_type="USER_INPUT_CODE", + ), + thirdparty.init( + sign_in_and_up_feature=thirdparty.SignInAndUpFeature( + providers=[ + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="apple", + clients=[ + thirdparty.ProviderClientConfig( + client_id="4398792-io.supertokens.example.service", + additional_config={ + "keyId": "7M48Y4RYDL", + "teamId": "YWQCXGJRJL", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----", + }, + ), + ], + ) + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="google", + clients=[ + thirdparty.ProviderClientConfig( + client_id="467101b197249757c71f", + client_secret="e97051221f4b6426e8fe8d51486396703012f5bd", + ), + ], + ) + ), + thirdparty.ProviderInput( + config=thirdparty.ProviderConfig( + third_party_id="github", + clients=[ + thirdparty.ProviderClientConfig( + client_id="1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com", + client_secret="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", + ), + ], + ) + ), + ], + ) + ), + ], + ) + querier = Querier.get_instance(DashboardRecipe.recipe_id) + cdi_version = await querier.get_api_version() + if not cdi_version: + skip("CDI version not available") + if not is_version_gte(cdi_version, "2.20"): + skip("CDI version too old") + await create_users(thirdparty=True, passwordless=True) + query = {"limit": "10", "provider": "google", "phone": "1"} + res = driver_config_client.get( + "/auth/dashboard/api/users", + headers={ + "Authorization": "Bearer testapikey", + "Content-Type": "application/json", + }, + params=query, + ) + info = extract_info(res) + assert res.status_code == 200 + assert len(info["body"]["users"]) == 0 diff --git a/tests/test_user_context.py b/tests/test_user_context.py index 4ef289756..ef54bad42 100644 --- a/tests/test_user_context.py +++ b/tests/test_user_context.py @@ -513,4 +513,4 @@ async def test_default_user_context_func_calls(): [str(path)] * sum("default_user_context(" in line for line in lines) ) - assert len(file_occurences) == 18 + assert len(file_occurences) == 23