diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 20f28f1d47a7888b6d5bd43ca4fed20b2f6aa8a1..0000000000000000000000000000000000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,31 +0,0 @@ -pages: - stage: deploy - image: python:3.9.18-slim-bullseye - variables: - PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" - PAGES_BRANCH: gl-pages - HTTPS_REMOTE: https://gitlab-ci-token:${ACCESS_TOKEN}@${CI_SERVER_HOST}/rep/${CI_PROJECT_PATH}.git - before_script: - - pip install -q mkdocs-material mike - - apt-get update -qq && apt-get -qq install -y git > /dev/null - - git config --global --replace-all user.name $GITLAB_USER_NAME - - git config --global --replace-all user.email $GITLAB_USER_EMAIL - - git fetch origin $PAGES_BRANCH && git checkout $PAGES_BRANCH || git checkout -b $PAGES_BRANCH origin/$PAGES_BRANCH || echo "Pages branch not deployed yet." - - git checkout $CI_COMMIT_SHA - script: - - | - if [ -n "$CI_COMMIT_TAG" ]; then - mike deploy --deploy-prefix public -r $HTTPS_REMOTE -p -b $PAGES_BRANCH -u $CI_COMMIT_TAG latest - mike set-default --deploy-prefix public -r $HTTPS_REMOTE -p -b $PAGES_BRANCH latest - elif [ "$CI_COMMIT_REF_NAME" == "develop" ]; then - mike deploy --deploy-prefix public -r $HTTPS_REMOTE -p -b $PAGES_BRANCH -u $CI_COMMIT_BRANCH - fi - - git checkout $PAGES_BRANCH -- public/ - artifacts: - paths: - - public/ - rules: - - if: '$CI_COMMIT_TAG' - - if: '$CI_COMMIT_REF_NAME == "develop"' - when: always - - when: never \ No newline at end of file diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index e5488d5adb34fd00f66cdfa83d2313ad2397e420..0000000000000000000000000000000000000000 --- a/LICENSE +++ /dev/null @@ -1,332 +0,0 @@ -OCF documentation (c) by ETSI - -OCF documentation is licensed under a -Creative Commons Attribution 4.0 International License. - -You can fine the license below and at . - -======================================================================= - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution 4.0 International Public License ("Public License"). To the -extent this Public License may be interpreted as a contract, You are -granted the Licensed Rights in consideration of Your acceptance of -these terms and conditions, and the Licensor grants You such rights in -consideration of benefits the Licensor receives from making the -Licensed Material available under these terms and conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - d. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - e. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - f. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - g. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - h. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - i. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - j. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - k. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's - License You apply must not prevent recipients of the Adapted - Material from complying with this Public License. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material; and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - - - diff --git a/README.md b/README.md deleted file mode 100644 index c44afa14a5cb6ce8710b4dd44168b69443727037..0000000000000000000000000000000000000000 --- a/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# documentation - - - -## Getting started - -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin https://labs.etsi.org/rep/ocf/documentation.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://labs.etsi.org/rep/ocf/documentation/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/doc/alarms_actions.md b/doc/alarms_actions.md deleted file mode 100644 index b7715494e2c504668c69d5a690197346fdb7be4b..0000000000000000000000000000000000000000 --- a/doc/alarms_actions.md +++ /dev/null @@ -1,113 +0,0 @@ -# Alarms - -In Openslice parts of TMF642 Alarm Management API are currently implemented. Alarms can be managed through the TMF API endpoint as well as the UI. - - - -## Alarms and Actions - -Note: Actions is an experimental feature. We expect to have a more mature solution in future. The component in the architecture is the Openslcie Assurance Services - -Alarms can be automatically resolved by specific actions. Today only the following actions are offered. - -* execDay2 -* scaleServiceEqually - - -## execDay2 - -Usually used to perform a Day2 configuration (towards OSM). To use it, Create a New Action Specification Name=execDay2 as following - -[![Openslice usage](./images/alarms_actions/day2actionspec.png)](./images/alarms_actions/day2actionspec.png) - - -Now make a Service Order for your service. In this example ςε used a cirros NSD - -Create a New Action Rule for the running services as the following example: - - -[![Openslice usage](./images/alarms_actions/action_rule_exampleday2.png)](./images/alarms_actions/action_rule_exampleday2.png) - -The scope is the running cirros service. - -Params should be paramname=value;paramname2=value2;paramname3=value3 (must exist in the VNF otherwise OSM will raise an error). - -In this case should be filename=test.txt - -Primitive=touch - -ServiceId = select the service which will accept the Day2. In this case is the same - -To test it: - -Go to the Service Inventory and select the active Service. - -Note the UUID of the service (e.g. c4e7990a-e174-4cd2-9133-b10e56721e08 copy from address bar), DeploymentRequestID and NSDID from characteristics - -You can either use the UUID of the service or the DeploymentRequestID and POST to the Alarms endpoint ( /tmf-api/alarmManagement/v4/alarm) - - -If the DeploymentRequestID is used then POST: - -``` - -{ - "alarmRaisedTime": "2021-06-29T12:30:24.675Z", - "alarmReportingTime": "2021-06-29T12:30:54.675Z", - "state": "raised", - "alarmType": "qualityOfServiceAlarm", - "probableCause": "thresholdCrossed", - "ackState": "unacknowledged", - "perceivedSeverity": "major", - "sourceSystemId": "mano-client-service", - "alarmDetails": "NSID=3;DeploymentRequestID=1", - "specificProblem": "myalram raised" -} - -``` - - -If the UUID is used then POST: - -``` - -{ - "alarmRaisedTime": "2021-06-29T12:30:24.675Z", - "alarmReportingTime": "2021-06-29T12:30:54.675Z", - "state": "raised", - "alarmType": "qualityOfServiceAlarm", - "probableCause": "thresholdCrossed", - "ackState": "unacknowledged", - "perceivedSeverity": "major", - "sourceSystemId": "mano-client-service", - "alarmDetails": "analarm", - "specificProblem": "myalram raised", - "affectedService": [ - { - "id": "c4e7990a-e174-4cd2-9133-b10e56721e08" - } - ] - -} - -``` - -The Alarm to be created must have the affected Service ID equal to the running service from the scope (the cirros_ns) - -Go to service inventory you will see the notes and also the service characteristics for any EXEC_ACTION updates - -You can also adjust the alarm conditions. They must match true so the alarm to be acknowledged -So if another external service raises an Alarm (with POST) for the running service, a Day2 will be performed on another Service - - -## scaleServiceEqually - - -This action is used from getting a scaling event from OSM. Please see the next demo for details on how it works - - -### Prototype demo - -You can watch how we used the prototype on the following ETSI ZMS PoC #2 - -* ETSI ZMS PoC #2: diff --git a/doc/architecture/architecture.md b/doc/architecture/architecture.md deleted file mode 100644 index 9181348600e7706d51ec95802c023ccc68a3982b..0000000000000000000000000000000000000000 --- a/doc/architecture/architecture.md +++ /dev/null @@ -1,49 +0,0 @@ -# Architecture - - -Openslice offers the following main functionalities: - -* Service Catalog Management: A CSP will have the ability to manage the Service Catalog Items, their attributes , organize in categories and decide what to make available to Customers -* Services Specifications: A CSP will be able to manage Service Specifications -* Service Catalog Exposure: A CSP will be able to expose catalog to customers and related parties -* Service Catalog to Service Catalog: Openslice able to consume and provide Service Catalog items to other catalogs -* Service Order: The Customer will be able to place a Service Order -* Service Inventory: The Customer and Provider will be able to view deployed Services status - - -The following figure displays the overall architecture of Openslice. - -[![Openslice architecture](../images/architecture.png)](../images/architecture.png) - - -Openslice allows Vertical Customers browsing the available offered service specifications. It consists of: - -* Web frontend UIs that consist of mainly two portals: i) a NFV portal allowing users self-service management and onboarding VNFDs/NSDs to facility’s NFVO ii) a Services Portal, which allows users to browse the Service Catalog, Service Blueprints specifications and the Service Inventory -* An API gateway that proxies the internal APIs and used by the web front end as well as any other 3rd party service -* A Message Bus where all microservices use it to exchange messages either via message queues or via publish/subscribe topics -* An authentication server implementing Oauth2 authentication scheme -* A microservice offering TMF compliant API services (eg Service Catalog API, Service Ordering APIetc) -* A microservice offering NFV API services (eg VNF/NSD onboarding etc) and allows to store VNFDs and NSDs in a catalog -* A microservice that is capable to interface to an issue management system. For example it raises an issue to all related stakeholders (CSP, NOP, CSC) that a new Service Order is requested -* Central logging microservice that is capable to log all distributed actions in to an Elasticsearch cluster -* A Service Orchestrator solution that will propagate Service Ordering requests to the equivalent SOs and NFVOs - - -The following figure depicts how Openslice microservices are deployed - -[![Openslice microservices network deployment](../images/microservices_network_deployment.png)](../images/microservices_network_deployment.png) - - - -## Deploying Openslice in multi domain scenarios - -A typical deployment across domains, involves today some typical components: i) an OSS/BSS to allow customers access the service catalog and perform service orders, ii) a Service Orchestrator (SO) component for executing the service order workflow, as well as iii) a Network Functions Virtualization Orchestrator (NFVO) for configuring the iv) network resources. - -TMF Open APIs are introduced not only for exposing catalogues and accepting service orders, but also implementing the East-West interfaces between the domains, fulfilling also the LSO requirements as introduced by MEF. - -The following figure shows how openslice could be used in such scenarios: - -[![Openslice multi-domain-architecture](../images/multi-domain-architecture.png)](../images/multi-domain-architecture.png) - - -See more [Consuming Services From External Partner Organizations](./consumingServicesFromExternalPartners.md) diff --git a/doc/architecture/centrallog.md b/doc/architecture/centrallog.md deleted file mode 100644 index a6cbc2b8f2572e1dabf61324306b4c7326998e13..0000000000000000000000000000000000000000 --- a/doc/architecture/centrallog.md +++ /dev/null @@ -1,5 +0,0 @@ -# Central Logging - -Openslice follows the centralized log management concept, i.e. a type of logging solution system that consolidates the log data from different services and pushes it to a central, accessible and easy-to-use interface. - -For that reason, Elasticsearch is elected as an open-source centralized logging solution for collecting, parsing and storing logs towards a real-time data analytics tool that provides insights from any type of structured and unstructured data source. \ No newline at end of file diff --git a/doc/architecture/consumingServicesFromExternalPartners.md b/doc/architecture/consumingServicesFromExternalPartners.md deleted file mode 100644 index 090a9f754b059ee4dd5251f9bfc7ccfcca15063f..0000000000000000000000000000000000000000 --- a/doc/architecture/consumingServicesFromExternalPartners.md +++ /dev/null @@ -1,185 +0,0 @@ -# Consuming Services From External Partner Organizations - -A typical deployment across domains, involves today some typical components: i) an OSS/BSS to allow customers access the service catalog and perform service orders, ii) a Service Orchestrator (SO) component for executing the service order workflow, as well as iii) a Network Functions Virtualization Orchestrator (NFVO) for configuring the iv) network resources. - -TMF Open APIs are introduced not only for exposing catalogues and accepting service orders, but also implementing the East-West interfaces between the domains, fulfilling also the LSO requirements as introduced by MEF. - -The following figure shows how openslice could be used in such scenarios: - -[![Openslice multi-domain-architecture](../images/multi-domain-architecture.png)](../images/multi-domain-architecture.png) - - -In Openslice we can consume services from 3rd parties via Open APIs. - -We use the TMF 632 Party Management model to specify Organizations that we can exchange items and other information such as: - -- Import Service Specifications -- Create a Service Order -- Use the Service Inventory to query the status of the service ordered to the external partner organization - -## Define an Organization as 3rd party to consume services East-West - -An organization must have the following characteristics in openslice catalog, like for example: - -"EXTERNAL_TMFAPI_BASEURL", "http://portal.openslice.io" - -"EXTERNAL_TMFAPI_CLIENTREGISTRATIONID", "authOpensliceProvider" - -"EXTERNAL_TMFAPI_OAUTH2CLIENTID", "osapiWebClientId" - -"EXTERNAL_TMFAPI_OAUTH2CLIENTSECRET", "secret" - -"EXTERNAL_TMFAPI_OAUTH2SCOPES", scopes - -"EXTERNAL_TMFAPI_OAUTH2TOKENURI", "http://portal.openslice.io/osapi-oauth-server/oauth/token" - -"EXTERNAL_TMFAPI_USERNAME", "admin" - -"EXTERNAL_TMFAPI_PASSWORD", "openslice" - -"EXTERNAL_TMFAPI_SERVICE_CATALOG_URLS" = "/tmf-api/serviceCatalogManagement/v4/serviceSpecification?type=CustomerFacingServiceSpecification" (this is optional, fetch a list of service specs it will be relative with the BASEURL. If the url is empty then no specs will be fetched, the EXTERNAL_TMFAPI_SERVICE_CATEGORY_URLS might be used) - -"EXTERNAL_TMFAPI_SERVICE_CATEGORY_URLS" = "/tmf-api/serviceCatalogManagement/v4/serviceCategory/{categoryid}" (this example will fetch all specs in a category. You may define comma separated URLs of categories API URL . This will fetch specifications of every defined category. If you want only one specific category put for example the uuid only of one category: "/tmf-api/serviceCatalogManagement/v4/serviceCategory/bda02821-bc4d-4bd6-b64b-d9c2aa5f8e6d". multiple urls should be "/tmf-api/serviceCatalogManagement/v4/serviceCategory/bda02821-bc4d-4bd6-b64b-d9c2aa5f8e6d,/tmf-api/serviceCatalogManagement/v4/serviceCategory/9b6d8bf3-abd2-43c4-8154-c8c6fe5545b2") - -"EXTERNAL_TMFAPI_SERVICE_SPEC" = "/tmf-api/serviceCatalogManagement/v4/serviceSpecification" - -"EXTERNAL_TMFAPI_SERVICE_ORDER_URLS"= "/test/v1/serviceorder" (this is optional) - - -An example Organization defined example in json: -``` - -{ - "uuid": "1a09a8b5-6bd5-444b-b0b9-a73c69eb42ae", - "@baseType": "BaseEntity", - "@schemaLocation": null, - "@type": null, - "href": null, - "name": "Openslice.io", - "id": "1a09a8b5-6bd5-444b-b0b9-a73c69eb42ae", - "isHeadOffice": null, - "isLegalEntity": null, - "nameType": null, - "organizationType": null, - "tradingName": null, - "contactMedium": [], - "creditRating": [], - "existsDuring": null, - "externalReference": [], - "organizationChildRelationship": [], - "organizationIdentification": [], - "organizationParentRelationship": null, - "otherName": [], - "partyCharacteristic": [ - { - "uuid": "3a2f7221-e0a2-4a6b-88d1-534c8e1963f6", - "@baseType": "BaseEntity", - "@schemaLocation": null, - "@type": null, - "href": null, - "name": "EXTERNAL_TMFAPI_CLIENTREGISTRATIONID", - "valueType": null, - "value": { - "value": "authOpensliceProvider", - "alias": null - } - }, - { - "uuid": "c24bb527-f178-4d38-9b93-2027c1732876", - "@baseType": "BaseEntity", - "@schemaLocation": null, - "@type": null, - "href": null, - "name": "EXTERNAL_TMFAPI_USERNAME", - "valueType": null, - "value": { - "value": "admin", - "alias": null - } - }, - { - "uuid": "27e45df8-414b-44c6-a5d5-3f064e2cfd3b", - "@baseType": "BaseEntity", - "@schemaLocation": null, - "@type": null, - "href": null, - "name": "EXTERNAL_TMFAPI_PASSWORD", - "valueType": null, - "value": { - "value": "openslice", - "alias": null - } - }, - { - "uuid": "e0e470b8-6024-4014-8a18-2333e5465ce1", - "@baseType": "BaseEntity", - "@schemaLocation": null, - "@type": null, - "href": null, - "name": "EXTERNAL_TMFAPI_OAUTH2CLIENTSECRET", - "valueType": null, - "value": { - "value": "secret", - "alias": null - } - }, - { - "uuid": "3e0de762-ac80-4c1e-a0a1-f265ff0899b4", - "@baseType": "BaseEntity", - "@schemaLocation": null, - "@type": null, - "href": null, - "name": "EXTERNAL_TMFAPI_OAUTH2SCOPES", - "valueType": null, - "value": { - "value": "admin;read", - "alias": null - } - }, - { - "uuid": "0bbb8314-f7f2-420d-9fed-ba054b15f886", - "@baseType": "BaseEntity", - "@schemaLocation": null, - "@type": null, - "href": null, - "name": "EXTERNAL_TMFAPI_OAUTH2TOKENURI", - "valueType": null, - "value": { - "value": "http://portal.openslice.io/osapi-oauth-server/oauth/token", - "alias": null - } - }, - { - "uuid": "3a567de4-79eb-4006-a500-3e5229b44175", - "@baseType": "BaseEntity", - "@schemaLocation": null, - "@type": null, - "href": null, - "name": "EXTERNAL_TMFAPI_OAUTH2CLIENTID", - "valueType": null, - "value": { - "value": "osapiWebClientId", - "alias": null - } - }, - { - "uuid": "6dca729f-dbe1-46b7-89f1-5c4f9fe89d4e", - "@baseType": "BaseEntity", - "@schemaLocation": null, - "@type": null, - "href": null, - "name": "EXTERNAL_TMFAPI_BASEURL", - "valueType": null, - "value": { - "value": "http://portal.openslice.io", - "alias": null - } - } - ], - "relatedParty": [], - "status": null, - "taxExemptionCertificate": [] -} - -``` - \ No newline at end of file diff --git a/doc/architecture/issuemgt.md b/doc/architecture/issuemgt.md deleted file mode 100644 index b50f5459a186f1eff82bebcb5e805c0752d6dde8..0000000000000000000000000000000000000000 --- a/doc/architecture/issuemgt.md +++ /dev/null @@ -1,7 +0,0 @@ -# Issue Management - -For issue management support, Openslice relies on Bugzilla. Bugzilla is a ticketing tool that allows issue reporting and tracking via tickets to all relevant stakeholders. - -The figure below displays the overall issue management service architecture integrating Bugzilla as its core and how this tool interacts with other Openslice services presenting some distinctive scenarios. It should be noted that Bugzilla tickets will not only be used for bugs/errors, but also for general requests, e.g. Service Order procedure. - -[![Issue management system](../images/issue_management.png)](../images/issue_management.png) \ No newline at end of file diff --git a/doc/architecture/messagebus.md b/doc/architecture/messagebus.md deleted file mode 100644 index a228afb52670b03e7036a2199cbfecd1454a2d50..0000000000000000000000000000000000000000 --- a/doc/architecture/messagebus.md +++ /dev/null @@ -1,816 +0,0 @@ -# Message Bus and exchanged Messages - -Openslice has a Message bus which allows Openslice services to exchange messages via queues and topics. - -It is based on ActiveMQ. - -3rd party services can be attached to bus and subscribe to message topics or request resources via queues. - - -## QUEUE MESSAGES - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_SERVICEORDERS | -|**Name** | jms:queue:CATALOG.GET.SERVICEORDERS | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | | -|**Description** | Return a List as String Json| - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_SERVICEORDER_BY_ID | -|**Name** | jms:queue:CATALOG.GET.SERVICEORDER_BY_ID | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | String orderid | -|**Description** | Return a ServiceOrder as String Json | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_UPD_SERVICEORDER_BY_ID | -|**Name** | jms:queue:CATALOG.UPD.SERVICEORDER_BY_ID | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | ServiceOrderUpdate serviceOrder | -|**Headers** | "orderid"= orderid | -|**Description** | Returns a ServiceOrder as String | - - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_SERVICESPEC_BY_ID | -|**Name** | jms:queue:CATALOG.GET.SERVICESPEC_BY_ID | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | specid | -|**Description** | Return a ServiceSpecification | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_ADD_SERVICESPEC | -|**Name** | jms:queue:CATALOG.ADD.SERVICESPEC | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | CRIDGE | -|**Body** | ServiceSpecCreate | -|**Description** | Creates a ServiceSpecification and returns a ServiceSpecification as String | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_UPD_SERVICESPEC | -|**Name** | jms:queue:CATALOG.UPD.SERVICESPEC | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | CRIDGE | -|**Body** | ServiceSpecUpdate | -|**Headers** | "serviceSpecid" = serviceSpecId| -|**Description** | Updates a ServiceSpecification and returns a ServiceSpecification as String. | ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_UPDADD_SERVICESPEC | -|**Name** | jms:queue:CATALOG.UPDADD.SERVICESPEC | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | CRIDGE | -|**Body** | ServiceSpecUpdate | -|**Headers** | "serviceSpecid" = serviceSpecId, "forceId"=forceId | -|**Description** | Updates a ServiceSpecification and returns a ServiceSpecification as String. If forceId is true then tries to assign the requested ID to the spec | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_ADD_SERVICEORDER | -|**Name** | jms:queue:CATALOG.ADD.SERVICEORDER | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | ServiceOrderCreate serviceOrder | -|**Headers** | | -|**Description** | Creates a ServiceOrder and returns a ServiceOrder as String | - ---- - - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_INITIAL_SERVICEORDERS_IDS | -|**Name** | jms:queue:CATALOG.GET.INITIAL_SERVICEORDERS | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | | -|**Body** | | -|**Description** | Return a List as String Json| - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_SERVICEORDER_IDS_BY_STATE | -|**Name** | jms:queue:CATALOG.GET.ACKNOWLEDGED_SERVICEORDERS | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | | -|**Headers** | "orderstate"= orderState | -|**Description** | String Json ArrayList of ServiceOrders | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_ADD_SERVICE | -|**Name** | jms:queue:CATALOG.ADD.SERVICE | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | ServiceCreate String json | -|**Headers** | "orderid"=orderid, "serviceSpecid"= specid | -|**Description** | Creates Service based an a Service Spec, Returns a Service object | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_UPD_SERVICE | -|**Name** | jms:queue:CATALOG.UPD.SERVICE | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | | -|**Body** | ServiceUpdate | -|**Headers** | "serviceid" = serviceId, "propagateToSO" = true/false | -|**Description** | will update a service by id and return the service instance. If propagateToSO=true then any service change will be handled by OSOM. This is needed to be controlled in order to avoid update loops| - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_SERVICE_BY_ID | -|**Name** | jms:queue:CATALOG.GET.SERVICE | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | String serviceID | -|**Description** | returns a Service instance | - ----| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_SERVICE_BY_ORDERID | -|**Name** | jms:queue:CATALOG.GET.SERVICE_BY_ORDERID | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | | -|**Body** | String serviceID | -|**Description** | returns Service IDs of a specific order given then order id | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_SERVICE_QUEUE_ITEMS_GET | -|**Name** | jms:queue:CATALOG.SERVICEQUEUEITEMS.GET | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | | -|**Description** | returns a LIST OF Service Queue Items | ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_SERVICE_QUEUE_ITEM_UPD | -|**Name** | jms:queue:CATALOG.SERVICEQUEUEITEM.UPDATE | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | String SERVICEQUEUEITEM | -|**Headers** | "itemid" = SERVICEQUEUEITEM id | -|**Description** | ill update a service queue item by id and return the instance | ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_SERVICE_QUEUE_ITEM_DELETE | -|**Name** | jms:queue:CATALOG.SERVICEQUEUEITEM.DELETE | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | | -|**Headers** | "itemid" = SERVICEQUEUEITEM id | -|**Description** | ill delete a service queue item by id | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_SERVICES_TO_TERMINATE | -|**Name** | jms:queue:CATALOG.GET.SERVICETOTERMINATE | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | | -|**Headers** | | -|**Description** | Get a list of ACTIVE services with END DAte in the past | ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_SERVICES_OF_PARTNERS | -|**Name** | jms:queue:CATALOG.GET.SERVICESOFPARTNERS | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | | -|**Headers** | | -|**Description** | Get a list of ACTIVE services from the inventory of partners | - - - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | NFV_CATALOG_GET_NSD_BY_ID | -|**Name** | jms:queue:NFVCATALOG.GET.NSD_BY_ID | -|**Type** | queue | -|**Destination** | NFV Catalog service | -|**Producers** | TMF API, OSOM | -|**Body** | NSDid | -|**Description** | Returns a NetworkServiceDescriptor object | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | NFV_CATALOG_DEPLOY_NSD_REQ | -|**Name** | jms:queue:NFVCATALOG.DEPLOY.NSD_REQ | -|**Type** | queue | -|**Destination** | NFV Catalog service | -|**Producers** | OSOM | -|**Body** | DeploymentDescriptor as Json String | -|**Headers** | NSD id | -|**Description** | Returns a DeploymentDescriptor object as json string containing deployment info| - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | NFV_CATALOG_UPD_DEPLOYMENT_BY_ID | -|**Name** | jms:queue:NFVCATALOG.UPD.DEPLOYMENT_BY_ID | -|**Type** | queue | -|**Destination** | NFV Catalog service | -|**Producers** | OSOM | -|**Body** | DeploymentDescriptor as Json String | -|**Headers** | DeploymentDescriptor id | -|**Description** | Updates and Returns a DeploymentDescriptor object as json string containing deployment info| - ---- - - - -| Message | | -| ------------- |----------------| -|**Alias** | GET_USER_BY_USERNAME | -|**Name** | jms:queue:GET.USER_BY_USERNAME | -|**Type** | queue | -|**Destination** | NFV Catalog service (this is temproary for now) | -|**Producers** | TMF API | -|**Body** | username | -|**Headers** | | -|**Description** | Returns a PortalUser object as json string containing user info| - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | NFV_CATALOG_GET_DEPLOYMENT_BY_ID | -|**Name** | jms:queue:NFVCATALOG.GET.DEPLOYMENT_BY_ID | -|**Type** | queue | -|**Destination** | NFV Catalog service | -|**Producers** | OSOM | -|**Body** | Deployment ID | -|**Description** | Returns a DeploymentDescriptor object | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_EXTERNAL_SERVICE_PARTNERS | -|**Name** | jms:queue:CATALOG.GET.EXTERNALSERVICEPARTNERS | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | | -|**Headers** | | -|**Description** | As a String Json ArrayList of Organizaton objects containing the characteristic name **EXTERNAL_TMFAPI** | - ---- - - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_UPD_EXTERNAL_SERVICESPEC | -|**Name** | jms:queue:CATALOG.UPD.EXTERNAL_SERVICESPEC | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM or maybe used by others that would like to update a Service Spec | -|**Body** | A serviceSpecification as json string | -|**Headers** | servicespecification id, orgid id | -|**Description** | Updates (or inserts if does not exist in catalog) an external service specification) | - ---- - - - - -| Message | | -| ------------- |----------------| -|**Alias** | NFV_CATALOG_NSACTIONS_SCALE | -|**Name** | jms:queue:NSACTIONS.SCALE | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM or maybe used by others that would like scale a NS | -|**Body** | A ScaleDescriptor as json string | -|**Headers** | none | -|**Description** | performs a scale | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | NFV_CATALOG_NS_LCMCHANGED | -|**Name** | NFV_CATALOG_NS_LCMCHANGED | -|**Type** | topic | -|**Destination** | any | -|**Producers** | MANO client | -|**Body** | A json string | -|**Headers** | none | -|**Description** | A NFV_CATALOG_NS_LCMCHANGED message is published when LCM of a running NS is changed | - ---- - -#ALARMS - - -| Message | | -| ------------- |----------------| -|**Alias** | ALARMS_ADD_ALARM | -|**Name** | jms:queue:ALARMS.ADD.ALARM | -|**Type** | queue | -|**Publishers** | | -|**Consumers** | TMF API | -|**Body** | AlarmCreate | -|**Headers** | | -|**Description** | Add an alarm | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | ALARMS_UPDATE_ALARM | -|**Name** | jms:queue:ALARMS.UPDATE.ALARM | -|**Type** | queue | -|**Publishers** | | -|**Consumers** | TMF API | -|**Body** | AlarmUpdate | -|**Headers** | alarmid = alarm id, body (AlarmUpdate object) | -|**Description** | Update an alarm | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | ALARMS_GET_ALARM | -|**Name** | jms:queue:ALARMS.GET.ALARM | -|**Type** | queue | -|**Publishers** | | -|**Consumers** | TMF API | -|**Body** | | -|**Headers** | alarmid = alarm id | -|**Description** | get an alarm | - ---- - -## EVENT TOPICS IN Message Bus -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_SERVICE_CREATE | -|**Name** | jms:topic:EVENT.SERVICE.CREATE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | - | -|**Body** | Notification object | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | xx | - ---- -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_SERVICE_STATE_CHANGED | -|**Name** | jms:topic:EVENT.SERVICE.STATECHANGED | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | - | -|**Body** | Notification object | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | xx | - ---- -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_SERVICE_DELETE | -|**Name** | jms:topic:EVENT.SERVICE.DELETE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | - | -|**Body** | Notification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | xx | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_SERVICE_ATTRIBUTE_VALUE_CHANGED | -|**Name** | jms:topic:EVENT.SERVICE.ATTRCHANGED | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | - | -|**Body** | Notification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | xx | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_SERVICE_ORDER_CREATE | -|**Name** | jms:topic:EVENT.SERVICEORDER.CREATE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | BUGZILLA Service, CentralLog Service | -|**Body** | Notification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | The Event of the Notification object contains the ServiceOrder object. Bugzilla service for example uses this to create a new issue | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_SERVICE_ORDER_STATE_CHANGED | -|**Name** | jms:topic:EVENT.SERVICEORDER.STATECHANGED | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | BUGZILLA Service, CentralLog Service | -|**Body** | Notification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | The Event of the Notification object contains the ServiceOrder object. Bugzilla service for example uses this to update an issue | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_SERVICE_ORDER_DELETE | -|**Name** | jms:topic:EVENT.SERVICEORDER.DELETE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | BUGZILLA Service, CentralLog Service | -|**Body** | Notification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | The Event of the Notification object contains the ServiceOrder object | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_SERVICE_ORDER_ATTRIBUTE_VALUE_CHANGED | -|**Name** | jms:topic:EVENT.SERVICEORDER.ATTRCHANGED | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | BUGZILLA Service, CentralLog Service | -|**Body** | Notification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | The Event of the Notification object contains the ServiceOrder object | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_ALARM_CREATE | -|**Name** | jms:topic:EVENT.ALARM.CREATE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | OAS, BUGZILLA Service, CentralLog Service | -|**Body** | AlarmCreateEvent | -|**Headers** | | -|**Description** | The Event contains the Alarm object in payload | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_ADD_RESOURCE | -|**Name** | jms:queue:CATALOG.ADD.RESOURCE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | any | -|**Body** | ResourceCreate | -|**Headers** | | -|**Description** | The Body contains the ResourceCreate object to add | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_UPD_RESOURCE | -|**Name** | jms:queue:CATALOG.UPD.RESOURCE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | any | -|**Body** | ResourceUpdate | -|**Headers** | resourceid , propagateToSO | -|**Description** | The Body contains the ResourceCreate object to update | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_RESOURCE_BY_ID | -|**Name** | jms:queue:CATALOG.GET.RESOURCE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | any | -|**Body** | resourceid | -|**Headers** | | -|**Description** | The Body contains the ResourceCreate object to update | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_RESOURCES_OF_PARTNERS | -|**Name** | jms:queue:CATALOG.GET.SERVICESOFPARTNERS | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | any | -|**Body** | none | -|**Headers** | none | -|**Description** | retrieve all active services of partners | - ---- -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_ADD_RESOURCESPEC | -|**Name** | jms:queue:CATALOG.ADD.RESOURCESPEC | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | any | -|**Body** | ResourceSpecificationCreate | -|**Headers** | | -|**Description** | The Body contains the ResourceSpecificationCreate object to add | - ---- -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_UPD_RESOURCESPEC | -|**Name** | jms:queue:CATALOG.UPD.RESOURCESPEC | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | any | -|**Body** | ResourceSpecificationUpdate | -|**Headers** | resourceSpecId | -|**Description** | The Body contains the ResourceSpecificationCreate object to update | - - ---- -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_RESOURCESPEC_BY_ID | -|**Name** | jms:queue:CATALOG.GET.RESOURCESPEC_BY_ID | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | any | -|**Body** | resourceSpecid | -|**Headers** | | -|**Description** | The Body contains the object id to find | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_UPDADD_RESOURCESPEC | -|**Name** | jms:queue:CATALOG.UPDADD.RESOURCESPEC | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | any | -|**Body** | resourceid | -|**Headers** | | -|**Description** | The Body contains the ResourceSpecificationCreate object to update or create if not exist | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_RESOURCE_CREATE | -|**Name** | jms:topic:EVENT.RESOURCE.CREATE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | BUGZILLA Service, CentralLog Service, other | -|**Body** | Notification object. | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | The Event of the Notification object contains the Resource object | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_RESOURCE_STATE_CHANGED | -|**Name** | jms:topic:EVENT.RESOURCE.STATECHANGED | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | BUGZILLA Service, CentralLog Service, other | -|**Body** | Notification object. | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | The Event of the Notification object contains the Resource object | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_RESOURCE_DELETE | -|**Name** | jms:topic:EVENT.SERVICE.RESOURCE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | BUGZILLA Service, CentralLog Service, other | -|**Body** | Notification object. | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | The Event of the Notification object contains the Resource object | - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | EVENT_RESOURCE_ATTRIBUTE_VALUE_CHANGED | -|**Name** | jms:topic:EVENT.RESOURCE.ATTRCHANGED | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | BUGZILLA Service, CentralLog Service, other | -|**Body** | Notification object. | -|**Headers** | "eventid"=eventid, "objId"= objId | -|**Description** | The Event of the Notification object contains the Resource object | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_LCMRULE_BY_ID | -|**Name** | jms:queue:CATALOG.GET.LCMRULE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | any | -|**Body** | lcmid | -|**Headers** | | -|**Description** | The Body contains the LCMRuleSpec object | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_LCMRULES_BY_SPECID_PHASE | -|**Name** | jms:queue:CATALOG.GET.LCMRULES_BY_SPECID_PHASE | -|**Type** | topic | -|**Publishers** | TMF API | -|**Consumers** | any | -|**Body** | | -|**Headers** | header.servicespecid, header.phasename | -|**Description** | The Body contains the LCMRuleSpec objects of the specific Service Spec and the specific phase | - - - - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_SERVICETESTSPEC_BY_ID | -|**Name** | jms:queue:CATALOG.GET.SERVICETESTSPEC_BY_ID | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | specid | -|**Description** | Return a ServiceTestSpecification | - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_ADD_SERVICETEST | -|**Name** | jms:queue:CATALOG.ADD.SERVICETEST | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | ServiceTestCreate String json | -|**Headers** | "orderid"=orderid, "serviceTestSpecid"= specid | -|**Description** | Creates Service Test based an a Service Test Spec, Returns a ServiceTest object | - ---- - - - - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_UPD_SERVICETEST | -|**Name** | jms:queue:CATALOG.UPD.SERVICETEST | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | | -|**Body** | ServiceTestUpdate | -|**Headers** | "serviceid" = serviceId, "propagateToSO" = true/false | -|**Description** | will update a service test by id and return the service instance. If propagateToSO=true then any service change will be handled by OSOM. This is needed to be controlled in order to avoid update loops| - ---- - - -| Message | | -| ------------- |----------------| -|**Alias** | CATALOG_GET_SERVICETEST_BY_ID | -|**Name** | jms:queue:CATALOG.GET.SERVICETEST | -|**Type** | queue | -|**Destination** | TMF API service | -|**Producers** | OSOM | -|**Body** | String serviceID | -|**Description** | returns a Service TEST instance | - - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CRD_DEPLOY_CR_REQ | -|**Name** | jms:queue:CRD.DEPLOY.CR_REQ | -|**Type** | queue | -|**Destination** | CRD service | -|**Producers** | OSOM | -|**Body** | CR spec as String | -|**Headers** | related service id | -|**Description** | Returns a String object containing deployment info| - ---- - -| Message | | -| ------------- |----------------| -|**Alias** | CRD_PATCH_CR_REQ | -|**Name** | jms:queue:CRD.PATCH.CR_REQ | -|**Type** | queue | -|**Destination** | CRD service | -|**Producers** | OSOM | -|**Body** | CR as String | -|**Headers** | related service id | -|**Description** | Returns a String object containing PATCH info| - - -| Message | | -| ------------- |----------------| -|**Alias** | CRD_DELETE_CR_REQ | -|**Name** | jms:queue:CRD.DELETE.CR_REQ | -|**Type** | queue | -|**Destination** | CRD service | -|**Producers** | OSOM | -|**Body** | CR as String | -|**Headers** | related service id | -|**Description** | Returns a String object containing deletion info| \ No newline at end of file diff --git a/doc/architecture/nfvapi.md b/doc/architecture/nfvapi.md deleted file mode 100644 index f31f195812fda0c41cf8165c5448c32a4931884c..0000000000000000000000000000000000000000 --- a/doc/architecture/nfvapi.md +++ /dev/null @@ -1,207 +0,0 @@ -# API interaction - -## OAuth token - -See [oauth](./oauth.md) - -## Request a protected API resource - -Example: Get all vxfs (check the `Authorization:Bearer` to be correct) - -``` -curl -H "Authorization:Bearer eybGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5hcGkiLCJhZG1pbiIsInJlYWQiLCJ3cml0ZSJdLCJvcmdhbml6YXRpb24iOiJteW9yZ2FuaXp0aW9uIiwiZXhwIjoxNTcxOTI0MjU2LCJhdXRob3JpdGllcyI6WyJST0xFX01FTlRPUiIsIlJPTEVfQURNSU4iXSwianRpIjoiNzNkZmIxODEtNTMwOS00MmExLThkOWUtOGM3YmQ0YTE1YmU0IiwiY2xpZW50X2lkIjoib3NhcGlXZWJDbGllbnRJZE91dCJ9.Pj_hxnyMGhFhN8avU_DiAw1-LlcaIz5Hp9HNqalw-X4" http://localhost:13000/osapi/admin/vxfs -``` -Example response: - -``` -[ - { - "id": 1, - "owner": { - "id": 1, - "organization": "ee", - "name": "Portal Administrator", - "email": "", - "username": "admin", - "createdAt": null - }, - "uuid": "a954daf2-16da-4b7e-ae42-4825936d453c", - "name": "cirros_vnfd", - "iconsrc": "/osapi/images/a954daf2-16da-4b7e-ae42-4825936d453c/cirros-64.png", - "shortDescription": "cirros_vnfd", - "longDescription": "Simple VNF example with a cirros", - "version": "1.0", - "packageLocation": "/osapi/packages/a954daf2-16da-4b7e-ae42-4825936d453c/cirros_vnf.tar.gz", - "dateCreated": 1568971426000, - "dateUpdated": 1568981107000, - "categories": [ - { - "id": 3, - "name": "Service", - "productsCount": 1, - "appscount": 0, - "vxFscount": 1 - }, - { - "id": 2, - "name": "Networking", - "productsCount": 1, - "appscount": 0, - "vxFscount": 1 - } - ], - "extensions": [], - "validationJobs": [], - "screenshots": "", - "vendor": "OSM", - "published": false, - "termsOfUse": null, - "descriptor": "vnfd-catalog:\n vnfd:\n - connection-point:\n - name: eth0\n type: VPORT\n description: Simple VNF example with a cirros\n id: cirros_vnfd\n logo: cirros-64.png\n mgmt-interface:\n cp: eth0\n name: cirros_vnfd\n short-name: cirros_vnfd\n vdu:\n - count: 1\n description: cirros_vnfd-VM\n id: cirros_vnfd-VM\n image: cirros034\n interface:\n - external-connection-point-ref: eth0\n name: eth0\n position: '1'\n type: EXTERNAL\n virtual-interface:\n bandwidth: '0'\n type: VIRTIO\n vpci: 0000:00:0a.0\n name: cirros_vnfd-VM\n vm-flavor:\n memory-mb: 512\n storage-gb: 1\n vcpu-count: 1\n vendor: OSM\n version: '1.0'\n", - "descriptorHTML": "

cirros_vnfd


Vendor: OSM
Version: 1.0
Description: Simple VNF example with a cirros
VM Count: 1
vCPU Count: 1
Memory: 512 MB
Storage: 1 GB
", - "certified": false, - "certifiedBy": null, - "validationStatus": "UNDER_REVIEW", - "packagingFormat": "OSMvFIVE", - "supportedMANOPlatforms": [ - { - "id": 1, - "name": "osm fivee", - "version": "osm fivee", - "description": "osm five" - } - ], - "vxfOnBoardedDescriptors": [], - "vfimagesVDU": [ - { - "id": 1, - "name": "cirros034", - "uuid": "d4549610-8abd-42ad-97f4-0a64e1c93977", - "shortDescription": "Automatically created during vxf cirros_vnfd submission. Owner must update.", - "packageLocation": null, - "publicURL": null, - "dateCreated": 1568971426000, - "dateUpdated": null, - "refVxFs": [ - { - "id": 1, - "name": "cirros_vnfd" - } - ], - "owner": { - "id": 1, - "organization": "ee", - "name": "Portal Administrator", - "email": "", - "username": "admin", - "active": true, - "currentSessionID": null, - "apikey": "e41c1cc4-aa56-4b7e-9f4d-64589549d768", - "createdAt": 1568711859000, - "roles": [ - "ADMIN", - "MENTOR" - ] - }, - "published": false, - "termsOfUse": null, - "deployedInfrastructures": [] - } - ] - }, - { - "id": 2, - "owner": { - "id": 1, - "organization": "ee", - "name": "Portal Administrator", - "email": "", - "username": "admin", - "createdAt": null - }, - "uuid": "4ab80095-a63e-4fe7-8598-e1c7e880706e", - "name": "cirros_sriov_vnfd", - "iconsrc": null, - "shortDescription": "cirros_sriov_vnf", - "longDescription": "Simple VNF example with a cirros SRIOV interface", - "version": "1.0", - "packageLocation": "/osapi/packages/4ab80095-a63e-4fe7-8598-e1c7e880706e/cirros_sriov.tar.gz", - "dateCreated": 1568971740000, - "dateUpdated": 1568981100000, - "categories": [ - { - "id": 4, - "name": "tyu", - "productsCount": 1, - "appscount": 0, - "vxFscount": 1 - }, - { - "id": 5, - "name": "tyi", - "productsCount": 1, - "appscount": 0, - "vxFscount": 1 - } - ], - "extensions": [], - "validationJobs": [], - "screenshots": "", - "vendor": "OSM", - "published": false, - "termsOfUse": null, - "descriptor": "vnfd:vnfd-catalog:\n vnfd:\n - connection-point:\n - name: eth0\n type: VPORT\n - name: eth1\n type: VPORT\n description: Simple VNF example with a cirros SRIOV interface\n id: cirros_sriov_vnfd\n logo: cirros-64.png\n mgmt-interface:\n cp: eth0\n name: cirros_sriov_vnf\n short-name: cirros_sriov_vnf\n vdu:\n - count: 1\n description: cirros_sriov_vnfd-VM\n guest-epa:\n cpu-pinning-policy: DEDICATED\n cpu-thread-pinning-policy: PREFER\n mempage-size: SMALL\n numa-node-policy:\n mem-policy: STRICT\n node:\n - id: '1'\n node-cnt: '1'\n id: cirros_sriov_vnfd-VM\n image: cirros-0.3.6-x86_64\n interface:\n - external-connection-point-ref: eth0\n name: eth0\n position: '1'\n type: EXTERNAL\n virtual-interface:\n bandwidth: '0'\n type: VIRTIO\n vpci: 0000:00:0a.0\n - external-connection-point-ref: eth1\n name: eth1\n position: '2'\n type: EXTERNAL\n virtual-interface:\n type: SR-IOV\n name: cirros_sriov_vnfd-VM\n vm-flavor:\n memory-mb: 4096\n storage-gb: 10\n vcpu-count: 4\n vendor: OSM\n version: '1.0'\n", - "descriptorHTML": "

cirros_sriov_vnf


Vendor: OSM
Version: 1.0
Description: Simple VNF example with a cirros SRIOV interface
VM Count: 1
vCPU Count: 1
Memory: 4096 MB
Storage: 10 GB
", - "certified": false, - "certifiedBy": null, - "validationStatus": "UNDER_REVIEW", - "packagingFormat": "OSMvFIVE", - "supportedMANOPlatforms": [ - { - "id": 1, - "name": "osm fivee", - "version": "osm fivee", - "description": "osm five" - } - ], - "vxfOnBoardedDescriptors": [], - "vfimagesVDU": [ - { - "id": 2, - "name": "cirros-0.3.6-x86_64", - "uuid": "be121176-1d62-4a1b-a3c1-7dce2e069d22", - "shortDescription": "Automatically created during vxf cirros_sriov_vnfd submission. Owner must update.", - "packageLocation": null, - "publicURL": null, - "dateCreated": 1568971740000, - "dateUpdated": null, - "refVxFs": [ - { - "id": 2, - "name": "cirros_sriov_vnfd" - } - ], - "owner": { - "id": 1, - "organization": "ee", - "name": "Portal Administrator", - "email": "", - "username": "admin", - "active": true, - "currentSessionID": null, - "apikey": "e41c1cc4-aa56-4b7e-9f4d-64589549d768", - "createdAt": 1568711859000, - "roles": [ - "ROLE_ADMIN", - "ROLE_MENTOR" - ] - }, - "published": false, - "termsOfUse": null, - "deployedInfrastructures": [] - } - ] - } -] -``` - - diff --git a/doc/architecture/nfvweb.md b/doc/architecture/nfvweb.md deleted file mode 100644 index b96dee34a9662e14f4c5eb3f41f507817686b094..0000000000000000000000000000000000000000 --- a/doc/architecture/nfvweb.md +++ /dev/null @@ -1,11 +0,0 @@ -# Openslice NFV Web Portal - -The [NFV Web Portal](http://portal.openslice.io/nfvportal) offers a domain-specific UI to manage NFV Services. - -Indicatively, the portal can be used to: -- Register new a new MANO provider (e.g. OSM) -- Synchronize the onboarded VNF/NS packages, and the VIMs of the registered MANO provider -- Onboard/Delete VNF/NS packages on specific MANO provider -- Deploy a NS to a target MANO provider - -More information can be found at [NFV Services](../nfvcatalogs.md). \ No newline at end of file diff --git a/doc/architecture/oauth.md b/doc/architecture/oauth.md deleted file mode 100644 index e836842620424d03e68971f2406eba6d11269d7c..0000000000000000000000000000000000000000 --- a/doc/architecture/oauth.md +++ /dev/null @@ -1,35 +0,0 @@ -# Authentication Server - -Authentication is based on oAuth2. Our authentication service is a Keycloak server which is deployed with Openslice deployment - -API users needs to authenticate. -All APIs (except grant token request) must include Bearer token in request Authorization header. - - - -##OAuth token - -Get first an oauth token, using your username and password. -``` -curl -X POST http://portal.openslice.io/auth/realms/openslice/protocol/openid-connect/token -H 'Content-Type: application/x-www-form-urlencoded' -d 'username=demouser' -d 'password=demouser' -d 'grant_type=password' -d 'client_id=osapiWebClientId' -``` -response: - -``` - {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJHZFRjQnpxczg2VW10NTRVZV8ybTJyWHJkV3dzaWdSZE9EUldMYm1memNvIn0.eyJleHAiOjE1ODgyNDA1NzAsImlhdCI6MTU4ODI0MDI3MCwianRpIjoiOGI2ZTU0NWUtNDIyYy00NzFiLWEwN2UtYTUzYzY1NDQ0MzZmIiwiaXNzIjoiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8vYXV0aC9yZWFsbXMvb3BlbnNsaWNlIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImExYTI3NjVhLWVjODMtNDQ1Ni1iN2IyLTIwNzMxOTg2ZTAzNSIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9zYXBpV2ViQ2xpZW50SWQiLCJzZXNzaW9uX3N0YXRlIjoiMzM1MGY0OTMtNjYyNy00MzczLTg1NDQtZGVmZDI3YWQzYzc0IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjEzMDgyIiwiaHR0cDovL2xvY2FsaG9zdCIsImh0dHA6Ly9vcGVuc2xpY2UuaW8iLCJodHRwOi8vbG9jYWxob3N0OjEzMDAwIiwiaHR0cDovL2xvY2FsaG9zdDo0MjAwIiwiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8iXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIk5GVl9ERVZFTE9QRVIiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiRVhQRVJJTUVOVEVSIiwiVVNFUiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJkZW1vdXNlciJ9.TnzzpRLMD94UTKpT5_wkr1h4_3KUQmr4TGvFLpJ7cZx-Klrv8tB_eRkWnPqqzCAM9G21a1qXboL8MLMW8ECzG7HYKpobKOGr7LSczMOTdA2ZDyBCRUSOdW77pchu54tJ0ITEkFaDwSKMKKt04V_Sy4U-eIndj0XzzRlkDolWDnK4Z2lRaXAI6fMwOKx1Toa6RfOcemxtzl3pdtjPx92zo6MaKlbIqHK82lxdK0k8aQQaT6TmIrXbZKV2dU_1d3O0q0dVUEZJ_1kzwqOFkmxr9w0EnndC6ccYJlDAr_-GgUhhhNOn5v6tjYLUQdj5e4KEAsxIPzaCreK4un7mEAPmDw","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIwZjUxMDk5Yy0wNTIzLTRjNGQtODM0Zi1iNDc0YzBjOTA1MzkifQ.eyJleHAiOjE1ODgyNDIwNzAsImlhdCI6MTU4ODI0MDI3MCwianRpIjoiZmViOTg5NWEtOTY5ZS00MzIzLWJjY2QtZTY2YzQ0NGE1MzJlIiwiaXNzIjoiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8vYXV0aC9yZWFsbXMvb3BlbnNsaWNlIiwiYXVkIjoiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8vYXV0aC9yZWFsbXMvb3BlbnNsaWNlIiwic3ViIjoiYTFhMjc2NWEtZWM4My00NDU2LWI3YjItMjA3MzE5ODZlMDM1IiwidHlwIjoiUmVmcmVzaCIsImF6cCI6Im9zYXBpV2ViQ2xpZW50SWQiLCJzZXNzaW9uX3N0YXRlIjoiMzM1MGY0OTMtNjYyNy00MzczLTg1NDQtZGVmZDI3YWQzYzc0Iiwic2NvcGUiOiJwcm9maWxlIGVtYWlsIn0.cDTx9BE1Df8EfGYm3VLr_MNFeymxZtJhMtlK7PVbIuk","token_type":"bearer","not-before-policy":1586797346,"session_state":"3350f493-6627-4373-8544-defd27ad3c74","scope":"profile email"} - -``` - -The `access_token` will be used next as a Bearer. - -``` -curl http://portal.openslice.io/tmf-api/serviceCatalogManagement/v4/serviceCatalog -H 'Authorization: Bearer yJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJHZFRjQnpxczg2VW10NTRVZV8ybTJyWHJkV3dzaWdSZE9EUldMYm1memNvIn0.eyJleHAiOjE1ODgyNDA1MjQsImlhdCI6MTU4ODI0MDIyNCwianRpIjoiYjg0NGYxZDAtMzk3Mi00YTMyLThiMWEtZDAxMDY3OGZjMTQ4IiwiaXNzIjoiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8vYXV0aC9yZWFsbXMvb3BlbnNsaWNlIiwic3ViIjoiYTFhMjc2NWEtZWM4My00NDU2LWI3YjItMjA3MzE5ODZlMDM1IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWRtaW4tY2xpIiwic2Vzc2lvbl9zdGF0ZSI6ImFmMmMzZmY1LTE4YWQtNDFkNC1hYTAyLTFlMGJkNzNmOTM5MSIsImFjciI6IjEiLCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGVtb3VzZXIifQ.SMtgV1E44_K_MQumGXZtWsLGVhYNaoM8Pk-DiFIZtUP4Zu-ervOsxHVQMX1frgVERR4jJidBcSshy9TnJ3UjF4l33WujHltbs-1UPy-gaIufVuEpl8RmbjOti3Up70vLfLXbzb6kN6WaahgobWXlbJsSXXwaBPQP6vSX5KigCa8TmzXcuqom14lOrlU-RB2zQTlJ30p7d9ag-a7o3I5m9GZWLJCZW2UYMl1JkskTHKgilA8HFQY4C9DYwWu8YDMyzqQSNumrTlURalBFidFbZvb1kp4dAyct8TysSWSbxxiwaL2RX1PWUqk-5Fpc1Q6BnBC8muMheiukFuoSkuADAg'^C -ubuntu@portal:~$ curl http://portal.openslice.io/tmf-api/serviceCatalogManagement/v4/serviceCatalog -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJHZFRjQnpxczg2VW10NTRVZV8ybTJyWHJkV3dzaWdSZE9EUldMYm1memNvIn0.eyJleHAiOjE1ODgyNDA1NzAsImlhdCI6MTU4ODI0MDI3MCwianRpIjoiOGI2ZTU0NWUtNDIyYy00NzFiLWEwN2UtYTUzYzY1NDQ0MzZmIiwiaXNzIjoiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8vYXV0aC9yZWFsbXMvb3BlbnNsaWNlIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImExYTI3NjVhLWVjODMtNDQ1Ni1iN2IyLTIwNzMxOTg2ZTAzNSIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9zYXBpV2ViQ2xpZW50SWQiLCJzZXNzaW9uX3N0YXRlIjoiMzM1MGY0OTMtNjYyNy00MzczLTg1NDQtZGVmZDI3YWQzYzc0IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjEzMDgyIiwiaHR0cDovL2xvY2FsaG9zdCIsImh0dHA6Ly9vcGVuc2xpY2UuaW8iLCJodHRwOi8vbG9jYWxob3N0OjEzMDAwIiwiaHR0cDovL2xvY2FsaG9zdDo0MjAwIiwiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8iXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIk5GVl9ERVZFTE9QRVIiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiRVhQRVJJTUVOVEVSIiwiVVNFUiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJkZW1vdXNlciJ9.TnzzpRLMD94UTKpT5_wkr1h4_3KUQmr4TGvFLpJ7cZx-Klrv8tB_eRkWnPqqzCAM9G21a1qXboL8MLMW8ECzG7HYKpobKOGr7LSczMOTdA2ZDyBCRUSOdW77pchu54tJ0ITEkFaDwSKMKKt04V_Sy4U-eIndj0XzzRlkDolWDnK4Z2lRaXAI6fMwOKx1Toa6RfOcemxtzl3pdtjPx92zo6MaKlbIqHK82lxdK0k8aQQaT6TmIrXbZKV2dU_1d3O0q0dVUEZJ_1kzwqOFkmxr9w0EnndC6ccYJlDAr_-GgUhhhNOn5v6tjYLUQdj5e4KEAsxIPzaCreK4un7mEAPmDw' - -Response: - - -[{"uuid":"9e186cd5-b2b2-4a06-b1d6-895720193bc9","lastUpdate":"2020-03-11T23:19:05Z","@baseType":"BaseEntity","@schemaLocation":null,"@type":"ServiceCatalog","href":null,"name":"Example Facility Services","description":"Example Facility Services","lifecycleStatus":"Active","version":"1.0","validFor":{"endDateTime":"2039-11-20T23:07:21Z","startDateTime":"2019-11-20T23:07:21Z"},"relatedParty":null,"id":"9e186cd5-b2b2-4a06-b1d6-895720193bc9","category":[{"@baseType":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","@schemaLocation":null,"@type":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","href":null,"name":"Generic Services","@referredType":null,"id":"98b9adf1-a1d6-4165-855f-153ddc2131b1"},{"@baseType":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","@schemaLocation":null,"@type":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","href":null,"name":"External","@referredType":null,"id":"08ffdb3c-6237-45d0-9f3a-d43b5fc5f0b6"},{"@baseType":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","@schemaLocation":null,"@type":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","href":null,"name":"eMBB","@referredType":null,"id":"ef2c90dd-b65e-4a9f-a9c3-427c9fb0219b"}]}] - - -``` \ No newline at end of file diff --git a/doc/architecture/osom.md b/doc/architecture/osom.md deleted file mode 100644 index 173aa8017d603bb3854eb6b4c0e9baa905e512f6..0000000000000000000000000000000000000000 --- a/doc/architecture/osom.md +++ /dev/null @@ -1,137 +0,0 @@ -# Openslice Service Orchestration and Order Management - OSOM - -OSOM is a service responsible for: - -* Service Order Management (SOM) -* Service Orchestration (SO) - -It uses open source Flowable Business process engine (https://www.flowable.org) . - -A Service Order follows the states as defined in TMF641 specification: - -[![Service Order state](../images/service_order_states.png)](../images/service_order_states.png) - - -## Initial state - -When a new order is created, it goes into the Initial state. It is stored in the repository and triggers an Event. - -[![Initial state](../images/service_order_initial_state.png)](../images/service_order_initial_state.png) - -Administrators are notified usually from the Ticketing System of a new order. They login to Openslice and change the State of the order either to ACKNOWLEDGED or REJECTED. If ACKNOWLEDGED they can Propose a startDate, add Notes, and add any additional service items - -## Order scheduler - -A process checks every 1 minute for ACKNOWLEDGED orders. - -[![Order scheduler BPM](../images/order_scheduler_bpm.png)](../images/order_scheduler_bpm.png) - -[![Order scheduler](../images/order_scheduler_diagram.png)](../images/order_scheduler_diagram.png) - -It retrieves all orders that are in ACKNOWLEDGED state and if the start date is in time it will initialize the process by settingn the order in IN_PROGRESS state. Finally the Start Order Process will start. - - -## Start order process - -This process for now is a draft simple prototype to make a simple orchestration via NFVO. Here the actual Services (TMF638/640 model) are created and attached to Service Order and Service Inventory. - -[![Start Order BPM](../images/start_order_process_bpm.png)](../images/start_order_process_bpm.png) - -[![Start Order interactions](../images/start_order_process_diagram.png)](../images/start_order_process_diagram.png) - -We expect here to check which tasks can be orchestrated by NFVO and which by human. We create the equivalent Services(TMF638/640 model) for this order. - -1. During check it should decide to create Service(s) for this service order O1 and send it to ServiceInventory -2. The Services(TMF638 model) are assigned to the Order O1 In supportService List -3. Each OrderItem OI1 is related to one serviceSpecification -4. Each ServiceSpecification has also related serviceSpecRelationships -5. So if we receive an order O1 for a ServiceSpec A which relates to (a bundle of) 3 specs(2 CFS, 1 RFS) we do the following: - 1. Create a Service S_A(TMF638 model) for ServiceSpec A for Order O1 - 2. We create also 3 Services S_C1, S_C2 and S_R1 equivalent to the serviceSpecRelationships (2 CFS, 1 RFS) - 3. At this point the order will have 1 Local Service Orchestration Process(S_A), 2 supportingServices refs(S_C1, S_C2) and 1 supportingResource(S_R1) - 4. The 3 supportingServices and 1 supportingResource correspond to total 4 Services in ServiceInventory - 5. Service S_A will have: - 1. startMode 1: Automatically by the managed environment - 2. State: RESERVED and the Lifecycle will be handled by OSOM - 6. Services S_C1 and S_C2 we decide that cannot be orchestrated then they have - 1. startMode: 3: Manually by the Provider of the Service - 2. State: RESERVED and the Lifecycle will be handled by OSOM - 3. If the CFS is a bundle spec it is further recursively orchestrated - 7. Service S_R1 will have - 1. startMode 1: Automatically by the managed environment. - 2. State: RESERVED - 3. IF The Service has the characteristic CharacteristicByName( "NSDID") it will be further processed by the NFVO - - -There will be two instances of task "User Task Manual Complete Service" each for Services S_C1 and S_C2. The task is Transient for now. It displays only the services that are not automated! -Here is a flow for future: - -1. We wait here for human decision. -2. From API we get a result: - a. If set to ACTIVE/TERMINATED then we complete the task - b. In any other state we stay in this task until it is resolved as in step a - c. The Status of ORDER O1 is also updated to PARTIAL - -There will be an instance of NFVODeploymentRequest process each for Service S_R1. (see later) - -1. This process is related with the NFVO orchestration -2. It will send a msg to NFVO(s?) for a specific deployment request - - -All services in "Order Complete" are in a status: - -1. Depending on the result the service S_A is either ACTIVE or INACTIVE or TERMINATED -2. The Status of ORDER O1 is also updated to COMPLETED or PARTIAL (in case we have some services running) or FAILED (in cases we have errors) - - -A Service follows the states as defined in TMF638 Service Inventory specification: - -[![Service state](../images/service_states.png)](../images/service_states.png) - - - -## NFVODeploymentRequest process - - -[![NFVODeploymentRequest BPM](../images/NFVODeploymentReq_process.png)](../images/NFVODeploymentReq_process.png) - -This process is related with the NFVO orchestration -It will send a msg to NFVO(s?) for a specific deployment request -Then it checks the deployment status. It will wait 30 secs each time until the deployment is running (or failed) - - - -## Check In Progress orders process - -Every 1 minute the "Check In Progress Orders" process is executed checking if a supported Service changed state (i.e. to ACTIVE) then the whole Order will change state (e.g. go to COMPLETED) - -[![Check In Progress BPM](../images/check_inProgress_orders.png)](../images/check_inProgress_orders.png) - - -## External Service Provider Deployment Request process - -[![External Service Provider Deployment Request process BPM](../images/externalSPDeploymentReq.png)](../images/externalSPDeploymentReq.png) - -This process contains tasks for submitting order requests to external partners. -- Submit Order To External Service Provider Task: This task creates automatically a Service Order request to a 3rd party provider SO that hosts the Service Specification -- Check external service order fulfillment task: This task Check external partner for Service creations and updates our local inventory of services the service characteristics of remote Service Inventory - - -## Fetch Partner Services Process - - -[![Fetch Partner Services Process BPM](../images/fetchPartnerServices.png)](../images/fetchPartnerServices.png) - -Every 2 minutes the "fetchPartnerServicesProcess" process is executed checking remote Partner Organizations for changes in the published catalogues. -The Fetch and Update External Partner Services Task is executed in paralle l for each Partner Organization - - -## Local Service Orchestration Process - - -[![Local Service Orchestration Process BPM](../images/LocalServiceOrchestrationProcess.png)](../images/LocalServiceOrchestrationProcess.png) - -This process handles automatically services that need to be further orchestrated or processed by OSOM. For example, for a CFS Bundled service we create such automated service instances that just aggregate the underlying services. - - - \ No newline at end of file diff --git a/doc/architecture/tmfapi.md b/doc/architecture/tmfapi.md deleted file mode 100644 index f7944969741681c840db830a70fc688a0224295e..0000000000000000000000000000000000000000 --- a/doc/architecture/tmfapi.md +++ /dev/null @@ -1,65 +0,0 @@ -# TMF OpenAPI specification - -PLease check the complete specification [here](http://portal.openslice.io/tmf-api/swagger-ui/index.html). - -## API interaction - -### OAuth token - -See [oauth](./oauth.md) - -### Request a protected API resource - -Example: Get all Service Catalogs (check the `Authorization:Bearer` to be correct) - -``` -curl -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5hcGkiLCJhZG1pbiIsInJlYWQiLCJ3cml0ZSJdLCJvcmdhbml6YXRpb24iOiJteW9yZ2FuaXp0aW9uIiwiZXhwIjoxNTc4NTA1MDcyLCJhdXRob3JpdGllcyI6WyJST0xFX01FTlRPUiIsIlJPTEVfQURNSU4iXSwianRpIjoiMTFlNGYxYTUtZDY0Ny00YzA1LWE0ZGMtYWFhYzUyMjk4YzMwIiwiY2xpZW50X2lkIjoib3NhcGlXZWJDbGllbnRJZE91dCJ9.gm7cKdusDrdMRkxEiFU5sENKGRC1xwVj2SgPRmE9xxx" -H "accept: application/json;charset=utf-8" -X GET "http://portal.openslice.io/tmf-api/serviceCatalogManagement/v4/serviceCatalog" - -``` - - -response: - - -``` -[ - { - "uuid": "9e186cd5-b2b2-4a06-b1d6-895720193bc9", - "lastUpdate": "2019-12-19T10:45:55Z", - "@baseType": "BaseEntity", - "@schemaLocation": null, - "@type": "ServiceCatalog", - "href": null, - "name": "Example Facility Services", - "description": "Example Facility Services", - "lifecycleStatus": "Active", - "version": "1.0", - "validFor": { - "endDateTime": "2039-11-20T23:07:21Z", - "startDateTime": "2019-11-20T23:07:21Z" - }, - "relatedParty": null, - "id": "9e186cd5-b2b2-4a06-b1d6-895720193bc9", - "category": [ - { - "@baseType": "org.etsi.osl.tmf.scm633.model.ServiceCategoryRef", - "@schemaLocation": null, - "@type": "org.etsi.osl.tmf.scm633.model.ServiceCategoryRef", - "href": null, - "name": "eMBB", - "@referredType": null, - "id": "ef2c90dd-b65e-4a9f-a9c3-427c9fb0219b" - }, - { - "@baseType": "org.etsi.osl.tmf.scm633.model.ServiceCategoryRef", - "@schemaLocation": null, - "@type": "org.etsi.osl.tmf.scm633.model.ServiceCategoryRef", - "href": null, - "name": "Generic Services", - "@referredType": null, - "id": "98b9adf1-a1d6-4165-855f-153ddc2131b1" - } - ] - } -] -``` \ No newline at end of file diff --git a/doc/architecture/tmfweb.md b/doc/architecture/tmfweb.md deleted file mode 100644 index 729eed65fa1833982885623ca9108e67b44ab20c..0000000000000000000000000000000000000000 --- a/doc/architecture/tmfweb.md +++ /dev/null @@ -1,39 +0,0 @@ -# Openslice TMF Web Portal - -Openslice comprises of a [web landing page](http://portal.openslice.io/) that navigates to the respective TMF Portal. - -- [Services Portal](http://portal.openslice.io/services) -- [Products Portal](http://portal.openslice.io/products) -- [Testing Portal](http://portal.openslice.io/testing) -- [Resources Portal](http://portal.openslice.io/resources) - -The landing page and the TMF portals (Services, Products, Testing, Resources) are written in Angular and are maintained under the same Angular project. - -Following you may find the scope each portal focuses on and the main TMF APIs it supports. - -**Services Portal** is a designated portal for the: -- Service Designer - To design Customer Facing Services as bundles of Resource Facing Services that map to specific Resourses (e.g. NFV, Testing, General Resources). Then, it is charged with the designed Services' exposure to public Service Catalogs. -- Service Customer - To browse the public Service Catalogs and order the offered Services. The fulfilment process of the Service Order is also captured and the final deployed Services are exposed to the Customer. - -**Products Portal** is a designated portal for the: -- Product Designer - To design Products as bundles of available Services. Then, it is charged with the designed Products' exposure to public Product Catalogs. -- Product Customer - To browse the public Product Catalogs and navigate to the respective offered Services. - -**Testing Portal** is a designated portal for the: -- Testing Designer - To design Tests and provide the testing scripts as attachments to the latter. The Tests can be imported as Services at the Services Portal, and can be included in a Service Bundle. - -**Resources Portal** is a designated portal for the: -- Resource Administrator - To view the available Resources that are being synchronized from the underlying infrastructure. - - - - -| | TMF620 | TMF632 | TMF633 | TMF634 | TMF638 | TMF639 | TMF640 | TMF641 | TMF642 | TMF653 | TMF685 | -| ------------- |:------:|:------:|:------:|:------:|:------:|:------:|:------:|:------:|:------:|:------:|:------:| -|**Services Portal** | |x |x | |x | |x |x |x | | | -|**Products Portal** |x |x | | | | | | | | | | -|**Testing Portal** | |x | | | | | | | |x | | -|**Resources Portal** | |x | |x | |x | | | | |x | - - -From the landing page, the user can also navigate towards the NFV portal. See [NFV WEB](./nfvweb.md). \ No newline at end of file diff --git a/doc/catalogs.md b/doc/catalogs.md deleted file mode 100644 index 3b3538d0b5dcaedf1690154bea22e70caf85551b..0000000000000000000000000000000000000000 --- a/doc/catalogs.md +++ /dev/null @@ -1,147 +0,0 @@ -# Catalogs and Templates - -The Openslice Service Catalogue (accessible through the API or Services portal) contains the representation of Service Specifications, either created from the provider defining service attributes, or by supporting the GSMA Generic Slice Templates (GST) as well as the VINNI Service Blueprint. The following scenarios are supported by the Openslice Service Catalogue. - - -## Create/Design a Service Specification - -### First Import some Resources as Resource Facing Services (RFSs) - -If you have any NSDs as NFV artifacts, import them through the UI menu (Import from NSD list). Then an NSD is imported as a resource and an RFS automatically is created. RFSs then later are used to design a Customer Facing Service Specification - -### Create/Design a Customer Facing Service Specification - -Customer Facing Service Specification are the services offered to customers. -You can create a new Service Specification from the menu. The services created through the UI are Customer Facing Services (CFS). Usually you create a CFS as a bundle and then you include Service Specification Relationships with RFSs or/and CFSs. - -Any Service Specification Characteristics from the RFS are copied to the CFS specification. A CFS can include multiple RFS or/and CFSs. -For example you can create a CFS spec called "A 5G Service" which is a bundle of two other services (include them in Service Specification Relationships) such as 5G eMBB Slice and a Customer VPN. So when the user orders "A 5G Service" services from 5G eMBB Slice and a Customer VPN will be created during the order. - -### Initial configuration for OSM deployment - -if you have an initial configuration that needs to be applied in the NSD deployment, then you go to the RFS (or CFS) and in Service Specification Characteristics go and edit the OSM_CONFIG characteristic. -You can add in the Service Characteristic Value, in the Value field something like the following example which gives a floating IP to a VNF: - -``` -{ "nsdId": "e855be91-567b-45cf-9f86-18653e7ea", "vimAccountId": "4efd8bf4-5292-4634-87b7-7b3d49108" , "vnf": [ {"member-vnf-index": "1", "vdu": [ {"id": "MyCharmedVNF-VM", "interface": [{"name": "eth0", "floating-ip-required": true }]}]}]} - -``` - -or a more complex example (beautify it first if you want to view it, but in the parameter OSM_CONFIG must be minified like the example): - -``` -{"nsdId":"e855be91-567b-45cf-9f86-18653e7","vimAccountId":"4efd8bf4-5292-4634-87b7-7b3d491","vnf":[{"member-vnf-index":"1","vdu":[{"id":"haproxy_vdu","interface":[{"name":"haproxy_vdu_eth1","floating-ip-required":true}]}]}],"vld":[{"name":"pub_net","vim-network-name":"OSMFIVE_selfservice01"},{"name":"management","vim-network-name":"OSMFIVE_selfservice01"},{"name":"lba_net","vim-network-name":"lba_net","vnfd-connection-point-ref":[{"member-vnf-index-ref":"1","vnfd-connection-point-ref":"haproxy_private","ip-address":"192.168.28.2"}]},{"name":"backend_net","vim-network-name":"backend_net","vnfd-connection-point-ref":[{"member-vnf-index-ref":"3","vnfd-connection-point-ref":"haproxy_public","ip-address":"192.168.20.2"}]},{"name":"lb_sb_net","vim-network-name":"lb_sb_net","vnfd-connection-point-ref":[{"member-vnf-index-ref":"3","vnfd-connection-point-ref":"haproxy_private","ip-address":"192.168.28.2"}]},{"name":"breaking_point_Spain","vim-network-name":"sb_repo_net"},{"name":"breaking_point_Greece","vim-network-name":"5TONICexternal"}],"additionalParamsForVnf":[{"member-vnf-index":"2","additionalParams":{"target_IP":"192.168.20.2"}},{"member-vnf-index":"4","additionalParams":{"target1_IP":"192.168.21.2","target2_IP":"10.154.252.10"}}]} -``` - -You can leave the Alias and Unit of Measure as is. Check also the is Default. - - - -### Day 2 Primitive Actions - -NFVOs like OSM allow to perform actions while a service is running, for example change attributes or make actions on a specific VNF. To design this do something similar to the following example: - -- Go to the RFS related to the NSD that contains VNFs with primitives -- create a characteristic named Primitive:: , e.g. Primitive::touch -- select Value Type: ARRAY -- add Service Characteristic Value: i) alias=primitive, value= (e.g. touch), ii) alias=member_vnf_index, value= (e.g. 1), iii) add the params that the user will change in alias the name of param and in value an initial value (e.g. alias=filename, value=myfile.txt) - -In the above example, when the service is running and the user goes to service inventory to MODIFY it, changes the value of the alias=filename, value=myfile.txt, to value =secondfile.txt. Then inside the VNF a file will be created called secondfile.txt - - - -### Generic Slice Templates (GST) -(Offered only as a design for now. THere is no direct implementation to NFV) -On October 16th 2019 GSMA published NG.116 Version 2.0 which defines the Generic Network Slice Template (GST). GST is a set of attributes that can characterise a type of network slice/service. GST is generic and is not tied to any specific network deployment. Here is a list of the various attributes of the template: - -- Availability -- Area of Service -- Delay tolerance -- Deterministic communication -- Downlink throughput per network slice -- Downlink throughput per UE -- Energy efficiency -- Group communication support -- Isolation level -- Location based message delivery -- Maximum supported packet size -- Mission critical support -- MMTel support -- NB-IoT support -- Network Slice Customer network functions -- Number of connections -- Number of terminals -- Performance monitoring -- Performance prediction -- Positioning support -- Radio spectrum -- Reliability -- Root cause investigation -- Session and Service Continuity support -- Simultaneous use of the network slice -- Slice quality of service parameters -- Support for non-IP traffic -- Supported access technologies -- Supported device velocity -- Synchronicity -- Terminal density -- Uplink throughput per network slice -- Uplink throughput per UE -- User management openness -- User data access -- V2X communication mode - -Openslice offers the GST in a format that is machine readable and aligned with the TMF SID model. Here is a tentative approach in JSON : - -Providers can clone a GST as e NEST directly in Openslice Web portal and the adjust the default attributes to their Service Specification - - -### 5G-VINNI Service Blueprint -(Offered only as a design for now. THere is no direct implementation to NFV) -5G-VINNI Service Blueprint is a special Service Specification defined by teh 5G-VINNI project. Many details can be found in document - -5G-VINNI Service Blueprint is a reusable self-contained specification of required network slice service (instances). As described in GST mapping VINNI-SB is also machine readable. - -Here is a tentative approach in JSON : - -5G-VINNI SB has many commonalities with GST as well as it offers Testing as a Service attributes. - - Next figure presents the high-level object model of a 5G-VINNI service blueprint. - -The 5G-VINNI SB as a first prototype approach is conceived as a CFS of a ‘bundle’ of services. It has some characteristics, like name, description, service type (eMBB, etc) and others. The constituent services are: - -- A “Service Topology” Service Specification which is related to a Network Service Resource topology (a Logical Resource Spec). It is considered at this stage as an RFS but is subject to change in future -- A “VINNI SB Service Requirements” Service Specification which is related to Service requirements. This is very similar to GST. It is considered at this stage a CFS. -- A “VINNI SB Service Exposure Level 1” Service Specification which contains characteristics for service exposure on level 1 ( see D3.1 for details). It is considered at this stage a CFS. -- A “VINNI SB Service Exposure Level 2” Service Specification which contains characteristics for service exposure on level 2. It is considered at this stage a CFS. -- A “VINNI SB Service Exposure Level 3” Service Specification which contains characteristics for service exposure on level 3. It is considered at this stage a CFS. -- A “VINNI SB Service Exposure Level 4” Service Specification which contains characteristics for service exposure on level 4. It is considered at this stage a CFS. -- A “VINNI SB Service 3rd part VNF” Service Specification which contains characteristics for support 3rd party VNFs to be included in the service. It is considered at this stage as an RFS but is subject to change in future -- A “VINNI SB Service 3rd part NSD” Service Specification which contains characteristics for support 3rd party NSDs to be included in the service. It is considered at this stage as an RFS but is subject to change in future -- A “VINNI SB Service Monitoring” Service Specification which contains characteristics for offering Monitoring capabilities on the requested Service. It is considered at this stage a CFS. -- A “VINNI SB Service Testing” Service Specification which contains characteristics for offering Testing capabilities on the requested Service. It is considered at this stage a CFS. - - -[![VINNI SB Template Model diagram](../images/vinni_sb_model_diagram.png)](../images/vinni_sb_model_diagram.png) - - -## Manage a Service Specification - -You can manage them though the Web UI - - -## Assign a Service Specification to Service Categories and Publish - -Just create categories and from the menu select the category and add services - - -## Retire/Remove a Service Specification - -Delete it from the category - - -## Consume and expose Service Specifications from other Service Catalogues - -See more on [Consuming Services From External Partner Organizations](./architecture/consumingServicesFromExternalPartners.md) - - diff --git a/doc/contributing/developing.md b/doc/contributing/developing.md deleted file mode 100644 index e8939eeca651aadf2bedef512ed8009df305b4aa..0000000000000000000000000000000000000000 --- a/doc/contributing/developing.md +++ /dev/null @@ -1,89 +0,0 @@ - -# Developing - -OpenSlice backend services are mainly implemented with Java 17 or above and Spring boot. - -OpenSlice uses various subsystems and depending on the module would you like to work, other subsystems must be present (you can disable them though in the code, e.g. at docker-compose.yaml file). - -To get the latest development branch: -```bash -wget https://labs.etsi.org/rep/osl/code/org.etsi.osl.main/-/raw/develop/compose/deploy.sh -sudo ./deploy.sh develop #[or replace develop with other branch name] -``` - -You may follow the [installation process](https://osl.etsi.org/documentation/develop/deployment/), as described at "develop" tagged documentation. - -To work on a specific subsystem e.g. org.etsi.osl.tmf.api, you must: - -1a - Deploy only the core necessary subsystems through: -```bash -sudo docker compose --profile dev down;sudo docker compose --profile dev up -d --build -``` -> Note **--profile dev** that will only deploy the core dependency subsystems, instead of the whole OpenSlice. - -1b - Or alternatively, commend out the respective container from the docker-compose.yaml file, so as to deploy the whole OpenSlice, except the subsystem you want to work on, following the provided installation steps. - -2 - Clone the respective repository, e.g. https://labs.etsi.org/rep/osl/code/org.etsi.osl.tmf.api/-/tree/develop - -3 - Code :) - -## General requirements - -- Docker should be installed in your development environment -- Run the core subsystems (see above section) - - -## Slack - -Feel free to join OpenSlice [Slack](https://openslice.slack.com) for any development oriented questions. - -## Examples of developing on specific subsystems - -### VNF/NSD Catalog Management and NSD Deployment API service - -Clone the repository: https://labs.etsi.org/rep/osl/code/org.etsi.osl.portal.api/-/tree/develop - -Check the docker-compose.yml file. Default port is 13080. Check specifically the datasource username/password, server port. - -Make sure that the core subsystems are up and running. - -Execute it with -```bash -mvn spring-boot:run -``` - -For verification, Swagger API of the service is at `http://localhost:13000/osapi/swagger-ui/index.html`. - -There, you may try there various REST actions and authenticate via the OAuth server without the use of the UI. - - -### VNF/NSD Catalog Management and NSD Deployment WEB UI service - -The Web UI is written in AngularJS. - -Clone the repository: https://labs.etsi.org/rep/osl/code/org.etsi.osl.portal.web/-/tree/develop - -By default the project org.etsi.osl.portal.api exposes the folder ../org.etsi.osl.portal.web/src/ in a folder testweb (Check class MvcConfig.java in org.etsi.osl.portal.api) for development. (In production nginx is used). Point your browser to `http://localhost:13000/osapi/testweb/index.html/` - -## Version/release management - -Check this nice article on how we develop and release versions. - -https://nvie.com/posts/a-successful-git-branching-model/ - -We develop in the develop branch and follow a issue driven development model. - ---- -## Wishlist - -Check also our wishlist of new features. You can add your own. - -See [Wishlist](./wishlist.md). - - - - - - - - diff --git a/doc/contributing/wishlist.md b/doc/contributing/wishlist.md deleted file mode 100644 index de91c7741f0ed1a31bf6cd0e0802c2dccdf114aa..0000000000000000000000000000000000000000 --- a/doc/contributing/wishlist.md +++ /dev/null @@ -1,59 +0,0 @@ -# TMF API - -- RBAC of API endpoints -- TMF Ticketing API support -- TMF Product -- HATEOAS integration -- Select Specs that can be exposed to partners (maybe with a characteristic?) - -## resource management - -- Resource Activation and Configuration API TMF702 (NEW) (https://projects.tmforum.org/wiki/pages/viewpage.action?pageId=128855518) - -# TMF WEB - - -# OSOM - -- Actions on service order item and acknowledge order status will define the lifecycle -- action shutdown on specific date for service order -- action edit on service order item - -## Dynamic attribute transformation - -- DTM decision tables support per Service Specification -- Schedule Termination of completed order on running services - - - -# NFVO connectivity - -## OSM Client - -- VNF/NSD config params Day 2 -- NSD Lifcecylce -- NST support - -# NFV API - - -# NFV WEB - - -# 3rd party connectivity - -## FlowOne connector - - -## Openstack connector - - -## Kubernetes connector - - -# CentralLog - -- Events from TMF to be written to Central Log - -# Authentication - diff --git a/doc/deployment.md b/doc/deployment.md deleted file mode 100644 index df703d7c45cd06a954bcd341db50c587f4dbf5be..0000000000000000000000000000000000000000 --- a/doc/deployment.md +++ /dev/null @@ -1,646 +0,0 @@ -## Requirements - -### Hardware requirements: - - -| **Minimum Hardware Requirements** | **Recomended Hardware Requirements** | -| --------------------------------- | ------------------------------------ | -| 4 CPU cores | 8 CPU cores | -| 8 GB RAM | 16 GB RAM | -| 20 GB storage | 40 GB storage | - -### Software Requirements: - -* Docker (Docker Compose installation) -* Kubernetes (Kubernetes installation - experimental) - - -## Preparing the environment - -> See the [Kubernetes section](#Kubernetes-installation), if you would like to deploy OpenSlice in a Kubernetes cluster. - - -### 1. Backup your previous database if necessary: -```bash -sudo docker exec amysql /usr/bin/mysqldump -u root --password=letmein ostmfdb > backup_ostmfdb.sql -``` -### 2. Install docker - -> Since July 2023 Docker Compose V1 stopped receiving updates. OpenSlice fully reverted to Compose V2, which is integrated in the Docker installation. - -### 3. Configure containers to properly resolve the DNS of your domain (optional) - -``` -sudo nano /etc/docker/daemon.json -``` - -and add: - -``` -{ - "dns": ["8.8.8.8", "8.8.4.4"] -} -``` -After editing daemon.json restart docker daemon for the changes to take place - -```bash -sudo systemctl restart docker -``` - -## Downloading the project - -### 1. Create a new folder to download the project - -```bash -mkdir openslice -``` -```bash -cd openslice -``` - -### 2. Download the deployment script - -Download the deployment / environment preparation script - -```bash -wget https://labs.etsi.org/rep/osl/code/org.etsi.osl.main/-/raw/main/compose/deploy.sh -``` -Make it executable -```bash -sudo chmod +x deploy.sh -``` - -### 3. Run the deployment script - -OpenSlice is a multi repo project. This script selects the same branch for all repositories of the project to pull from. - -After that it builds the respective jar files locally and installs all the npm packages needed for the UI. - -If you run the script without selecting a branch the the main branch is going to be selected. - -We recommend: - -* main branch for the most stable experience and -* develop branch for an experience with the latest features (for develop branch installation, it is strongly advisable that you may as well follow the [develop documentation](https://osl.etsi.org/documentation/develop/deployment/)) - -```bash -sudo ./deploy.sh main #[or replace main with other branch name] -``` - -> **We recommend running the deploy.sh script with root permissions! In other case, some directories may not be accessible by the project building tools and hinder the smooth installation.** - - -## Configure docker-compose services - -### 1. Create configuration specific Docker Compose file from the template - -```bash -cd org.etsi.osl.main/compose/ -``` -```bash -sudo cp docker-compose.yaml.configure docker-compose.yaml -``` -### 2. Configure mysql-portal container *(optional)* - -1. In folder `org.etsi.osl.main/compose/mysql-init` edit the file `01-databases.sql`. -2. In the `org.etsi.osl.main/compose/docker-compose.yaml` edit the credentials of the users that services use to connect to the databases, if you wish. - * portaluser (default is 12345) and - * keycloak (default is password) - - -### 3. Configure keycloak container *(optional)* - -1. If you made changes to keycloak's mysql credentials: - - In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml`. - -``` -DB_DATABASE: keycloak -DB_USER: keycloak -DB_PASSWORD: password -``` - -2. If you want to change the keycloak admin password: - - In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml` - -``` -KEYCLOAK_PASSWORD: Pa55w0rd -``` - -### 4. Configure bugzilla container *(optional)* - -If you want to utilise the Bugzilla connector: - -In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml` - -``` -SPRING_APPLICATION_JSON: '{ - "spring.activemq.brokerUrl": "tcp://anartemis:61616?jms.watchTopicAdvisories=false", - "spring.activemq.user": "artemis", - "spring.activemq.password": "artemis", - "bugzillaurl":"", - "bugzillakey":"", - "main_operations_product":"" -}' -``` - -And add the provided Bugzilla installation information: - -``` -"bugzillaurl":"bugzillaurl.xx:443/bugzilla/", -"bugzillakey":"exampleKeyeqNNwxBlgxZgMEIne0Oeq0Bz", -"main_operations_product":"Main Site Operations" // this is the default product to issue tickets -``` - -Bugzilla should have the following components under the specified product: - -* NSD Deployment Request: Component used to schedule deployment req -* Onboarding: Issues related to VNF/NSD Onboarding -* Operations Support: Default component for operations support -* Validation: Use to track validation processes of VNFs and NSDs -* VPN Credentials/Access: Used for requesting VPN Credentials/Access - -Also in the 'Main Site Operations' product, a version named 'unspecified' must be created. - -### 5. Configure osportalapi container (NFV services) *(conditional)* - -Change the respective fields: - -- If you made changes to mysql and keycloak credentials. -- If you want to change logging level (TRACE / DEBUG / INFO / WARN / ERROR). - -> ***If you are using a non-local domain, replace everywhere the http://keycloak:8080 with the respective {{protocol://domain.name}}, as well as "spring.portal.main.domain" property.*** - -In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml` - -``` -SPRING_APPLICATION_JSON: '{ - "spring.datasource.username":"root", - "spring.datasource.password":"letmein", - "spring-addons.issuers[0].uri": "http://keycloak:8080/auth/realms/openslice", - "spring.security.oauth2.resourceserver.jwt.issuer-uri": "http://keycloak:8080/auth/realms/openslice", - "springdoc.oAuthFlow.authorizationUrl": "http://keycloak:8080/auth/realms/openslice/protocol/openid-connect/auth", - "springdoc.oAuthFlow.tokenUrl": "http://keycloak:8080/auth/realms/openslice/protocol/openid-connect/token", - "spring.portal.main.domain": "http://localhost", - "logging.level.org.springframework" : "INFO" -}' -``` - -### 6. osscapi container (TMF API service) *(conditional)* - -Change the respective fields: - -- If you made changes to mysql and keycloak credentials. -- If you want to change logging level (TRACE / DEBUG / INFO / WARN / ERROR). - -> **If you are using a non-local domain, replace everywhere the http://keycloak:8080 with the respective {{protocol://domain.name}}.** - -In folder `org.etsi.osl.main/compose/` edit the file `docker-compose.yaml` - -``` -SPRING_APPLICATION_JSON: '{ - "spring.datasource.username":"root", - "spring.datasource.password":"letmein", - "spring-addons.issuers[0].uri": "http://keycloak:8080/auth/realms/openslice", - "spring.security.oauth2.resourceserver.jwt.issuer-uri": "http://keycloak:8080/auth/realms/openslice", - "springdoc.oAuthFlow.authorizationUrl": "http://keycloak:8080/auth/realms/openslice/protocol/openid-connect/auth", - "springdoc.oAuthFlow.tokenUrl": "http://keycloak:8080/auth/realms/openslice/protocol/openid-connect/token", - "logging.level.org.springframework" : "INFO" -}' -``` - -## Configure nginx - -In folder `org.etsi.osl.main/compose/nginx` create a configuration specific `nginx.conf` file. - - -```bash -cd org.etsi.osl.main/compose/nginx/ -``` - -```bash -sudo cp nginx.conf.default nginx.conf -``` - -If needed, in the nginx.conf file, edit the server_name for an non-local deployment. - - - -## Configure Web UI - -In folder `org.etsi.osl.portal.web/src/js/` create a configuration specific `config.js` file. - -```bash -cd org.etsi.osl.portal.web/src/js -``` - -```bash -sudo cp config.js.default config.js -``` - -Edit the `config.js` file with the information of your domain - - -``` -{ - TITLE: "OpenSlice by ETSI", - WIKI: "https://openslice.readthedocs.io/en/stable/", - BUGZILLA: "ROOTURL/bugzilla/", - STATUS: "ROOTURL/status/", - APIURL: "http://localost:13000", - WEBURL: "ROOTURL/nfvportal", - APIOAUTHURL: "ROOTURL/auth/realms/openslice", - APITMFURL: "ROOTURL/tmf-api/serviceCatalogManagement/v4" -} -``` - -## Configure TMF Web UI - -In the folder `org.etsi.osl.tmf.web/src/assets/config` there are 3 files available for configuration: - -* config.prod.json (Basic information + API configuration) -* theming.scss (CSS color palette theming) -* config.theming.json (HTML configuration - Logo, Favicon, Footer) - - -The first 2 files above (i.e. config.prod.json, theming.scss) are essential for the successful deployment of OpenSlice, thus created automatically during the initial deployment at `org.etsi.osl.tmf.web/src/assets/config` directory as a copy of the default ones from the remote repository. - -Ensure that you check the `config.prod.json` and `theming.scss` files and readjust to your deployment if needed. - -```bash -# Starting from the root project directory -cd org.etsi.osl.tmf.web/src/assets/config -``` - -E.g. Edit "TITLE", "WIKI", etc properties with your domain title. Also configure TMF's API and Keycloak's location for the web application, if needed. - - -``` -{ - "TITLE": "OpenSlice by ETSI", - "PORTALVERSION":"2024-Q2 1.0.0-SNAPSHOT", - "WIKI": "https://osl.etsi.org/documentation", - "BUGZILLA": "{BASEURL}/bugzilla/", - "STATUS": "http://status.localhost/", - "WEBURL": "{BASEURL}", - "PORTAL_REPO_APIURL": "{BASEURL}/osapi", - "ASSURANCE_SERVICE_MGMT_APIURL": "{BASEURL}/oas-api", - "APITMFURL": "{BASEURL}/tmf-api", - "OAUTH_CONFIG" : { - "issuer": "{BASEURL}/auth/realms/openslice", - "loginUrl": "{BASEURL}/auth/realms/openslice/protocol/openid-connect/auth", - "tokenEndpoint": "{BASEURL}/auth/realms/openslice/protocol/openid-connect/token", - "userinfoEndpoint": "{BASEURL}/auth/realms/openslice/protocol/openid-connect/userinfo", - "redirectUri": "{BASEURL}/redirect", - "logoutUrl": "{BASEURL}/auth/realms/openslice/protocol/openid-connect/logout", - "postLogoutRedirectUri": "{BASEURL}", - - "responseType": "code", - "oidc": false, - "clientId": "osapiWebClientId", - "dummyClientSecret": "secret", - - "requireHttps": false, - "useHttpBasicAuth": true, - "clearHashAfterLogin": false, - - "showDebugInformation": true - } -} -``` - - -> The {BASEURL} placeholder in the file automatically detects the Origin (Protocol://Domain:Port) of the deployment and applies it to every respective property. E.g. If you are attempting a local deployment of Openslice, then {BASEURL} is automatically translated to "http://localhost". Similarly, you may use {BASEURL} to translate to a public deployment configuration, e.g. "https://portal.openslice.io". - -If further customization, apart from the default provided, is needed for branding (Logo, Footer) then `config.theming.json` needs to be created in io.openslice.tmf.web/src/assets/config directory, as follows: - -```bash -# Starting from the root project directory -cd org.etsi.osl.tmf.web/src/assets/config -``` - -```bash -sudo cp config.theming.default.json config.theming.json -``` - -> ***IMPORTANT NOTE:*** -If you want to apply changes to the JSON configuration files without the need to rebuild the application, you have to apply the changes at the `org.etsi.osl.tmf.web/dist/io-openslice-portal-web/assets/config` directory. Although, it is mandatory to also apply these changes to the `org.etsi.osl.tmf.web/src/assets/config` for persistancy, as after any future rebuild of OpenSlice the `/dist` directory is being overwritten along with its contents. The OpenSlice team strongly recommends to always apply your changes to the TMF web UI configuration files at `org.etsi.osl.tmf.web/src/assets/config` and rebuild the application. - -## Deploy OpenSlice via Docker Compose - -After configuring the services, and editing the docker compose file accordingly, the docker compose instantiation command can be performed. - -```bash -# Starting from the root project directory -cd org.etsi.osl.main/compose/ -``` - -```bash -sudo docker compose --profile prod down;sudo docker compose --profile prod up -d --build -``` - -> Depending on your machine, this process might take time. if for any reason the deployment fails during first time, please rerun the above before any further measures. - - -## Validating deployments and container monitoring - -You can monitor containers' status with portainer at port 9000 (http://your-ip:9000). - -Initially, you may monitor the local machine at portainer. - -Please check that all containers are in running state. - - -## Post installation steps - -After the successful deployment of OpenSlice, to ensure the E2E user experience, **this section is mandatory**. It contains crucial configuration in regard of authentication and user creation. - -### Configure Keycloak server - -The Keycloack server is managing authentication and running on a container at port 8080. It is also proxied to your host via nginx under http://localhost/auth. - -- Navigate to http://domain.com/auth/ or https://domain.com/auth/, (http://ipaddress:8080/auth/ or https://ipaddress:8443/auth/ which are directly accessible without proxy) - -- Navigate to Administration Console - -- Login with the credentials from section [Configure keycloak container](#3-configure-keycloak-container-optional). Default values are: - - user: admin and - - password: KEYCLOAK_PASSWORD - -> if you are running in HTTP you will get a message: HTTPS required. - -To resolve this issue when running in HTTP: - -- Select the master realm from top left corner -- Go to login Tab and select "Require SSL": None -- Repeat for realm Openslice - - -> If you are running in HTTPS, then "Require SSL" can be left unchanged to external requests. - -#### 1. Configure redirects - -Navigate to realm Openslice > client > osapiWebClientId and change the Root URL to your domain. - -Also, insert your domain, e.g. http://example.org/*, at: -* Valid Redirect URIs -* Web Origins - -#### 2. Configure email - -Keycloak allows new users to register. Subsequently, this will also allow new users to register to the OpenSlice portal. - -On Tab Login > check User registration, Verify email, Forgot password etc. - -Also, enter the details on Realm > Email > Enable Authentication. - -#### 3. Add an OpenSlice admin user - -This step is mandatory so as to access the OpenSlice Web UI. To add an OpenSlice admin user you must: -- Navigate to manage/users and add an OpenSlice admin user, e.g. username=admin. -- Set a password -- Navigate to Role Mappings and add ADMIN and MENTOR to Assigned Roles. - -> That user is different from the Keycloak admin user. It is required to login and browse the OpenSlice Web UI. The Roles ADMIN and MENTOR guarantee full access through the Openslice UI, thus such a user is always required. - -### Keycloak at localhost - -> **This is an important step if you run Keycloak on localhost!** - -1 - Edit your Hosts File, adding the line below - -```127.0.0.1 keycloak``` - -Hosts File Location: - - - In Linux/Unix, the file's location is at /etc/hosts - - - In Windows, its location is at c:\Windows\System32\Drivers\etc\hosts - -2 - Replace http://localhost/auth/ with http://keycloak:8080/auth/ in your Keycloak config for AngularJS and Angular (see examples below). - - -> Explanation - -Nginx uses the http://keycloak:8080 URL, which is accessible via the internal docker system's network. -The Front-end (TS/Angular) shall also use the http://keycloak:8080. -This way, you will not get the invalid token error, as the API is acquiring the token from http://keycloak:8080 (internally) and the Front-end is getting verified by an issuer at the same URL, as well. - - - -2.1 - For the Angular configuration (TMF portal UI), navigate to org.etsi.osl.tmf.web/src/assets/config and edit config.prod.json - -```bash -# Starting from the root project directory -cd org.etsi.osl.tmf.web/src/assets/config -``` - -```bash -nano config.prod.json -``` - -After editing it should look like the example bellow: - -```yaml -{ - "TITLE": "OpenSlice by ETSI", - "PORTALVERSION":"2023-Q3 1.2.0-SNAPSHOT", - "WIKI": "https://openslice.readthedocs.io/en/stable/", - "BUGZILLA": "{BASEURL}/bugzilla/", - "STATUS": "http://status.localhost/", - "WEBURL": "{BASEURL}", - "PORTAL_REPO_APIURL": "{BASEURL}/osapi", - "ASSURANCE_SERVICE_MGMT_APIURL": "{BASEURL}/oas-api", - "APITMFURL": "{BASEURL}/tmf-api", - "OAUTH_CONFIG" : { - "issuer": "http://keycloak:8080/auth/realms/openslice", - "loginUrl": "http://keycloak:8080/auth/realms/openslice/protocol/openid-connect/auth", - "tokenEndpoint": "http://keycloak:8080/auth/realms/openslice/protocol/openid-connect/token", - "userinfoEndpoint": "http://keycloak:8080/auth/realms/openslice/protocol/openid-connect/userinfo", - "redirectUri": "{BASEURL}/redirect", - "logoutUrl": "http://keycloak:8080/auth/realms/openslice/protocol/openid-connect/logout", - "postLogoutRedirectUri": "{BASEURL}", - - "responseType": "code", - "oidc": false, - "clientId": "osapiWebClientId", - "dummyClientSecret": "secret", - - "requireHttps": false, - "useHttpBasicAuth": true, - clearHashAfterLogin": false, - - "showDebugInformation": true - } -} -``` - -> Note the difference in changing {BASEURL} -> http://keycloak:8080 - -> If you want the changes to take place immediately without rebuilding the project, then repeat the process for org.etsi.osl.tmf.web/dist/org.etsi.osl.tmf.web/assets/config/config.prod.json - -2.2 - For the AngularJS configuration (NVF portal UI), navigate to org.etsi.osl.portal.web/src/js and edit config.js - -```bash -# Starting from the root project directory -cd org.etsi.osl.portal.web/src/js -``` - -```bash -nano config.js -``` - -after editing it should look like the example bellow: - -``` -var appConfig = angular.module('portalwebapp.config',[]); - - -appConfig.factory('APIEndPointService', function() { - return { - TITLE: "OpenSlice by ETSI", - WIKI: "https://openslice.readthedocs.io/en/stable/", - BUGZILLA: "ROOTURL/bugzilla/", - STATUS: "ROOTURL/status/", - APIURL: "http://localost:13000", - WEBURL: "ROOTURL/nfvportal", - APIOAUTHURL: "ROOTURL/auth/realms/openslice", - APITMFURL: "ROOTURL/tmf-api/serviceCatalogManagement/v4" - }; -}); -``` - -> Note the difference in "APIOAUTHURL" property - - -### NFV Orchestrator Configuration - -After successfully deploying and configuring OpenSlice, you may configure its environment (e.g. the NFVO) that will facilitate the deployment of NFV artifacts. - -See [NFV Orchestrator Configuration](./nfvoconfig.md). - - -
- -## Kubernetes installation - -Openslice can be installed in a Kubernetes cluster. - -**This is a work in progress, and should be used for stable deployments!**. - -Please reference "develop" tagged documentation for any latest progress. - -The related scripts are inside the kubernetes folder. Follow these steps along the lines. You need to configure the ingress properly depending on how you want to expose Openslice. - -1 - Create an openslice namespace - -```bash -kubectl create namespace openslice -``` - -2 - Apply or create an ingress. Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. Traffic routing is controlled by rules defined on the Ingress resource. -An Ingress may be configured to give Services externally-reachable URLs, load balance traffic, terminate SSL / TLS, and offer name-based virtual hosting. An Ingress controller is responsible for fulfilling the Ingress, usually with a load balancer, though it may also configure your edge router or additional frontends to help handle the traffic. You must have an Ingress controller to satisfy an Ingress. -You may need to deploy an Ingress controller such as ingress-nginx. - -You can also adapt it to connect to public cloud load balancers depending on your needs. - -The following will expose an ingress resource from one of your a k8s nodes on port 80. - -```bash - kubectl apply -f openslice-ingress.yaml -``` - - -Finding the ingress IP: - - -```bash - -kubectl describe -f openslice-ingress.yaml - - -Name: openslice-ingress -Namespace: openslice -Address: 10.10.10.35 -Default backend: default-http-backend:80 () -Rules: - Host Path Backends - ---- ---- -------- - * - /services tmfweb:80 () - /tmf-api osscapi:13082 () - /auth keycloak:8080 () - /osapi osportalapi:13000 () - / portalweb:80 () -Annotations: kubernetes.io/ingress.class: nginx -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Sync 9m29s (x2 over 9m58s) nginx-ingress-controller Scheduled for sync - -``` - -From the above example, our exposed ingress is at Address: 10.10.10.35 - -3 - We need to configure the expose address and deploy openslice (IP or URL e.g. http://myopenslice.xxx) - -```bash -./k8sdeploy.sh 10.10.10.35 -``` - - -4 - Check the status of Openslice in the cluster. Should be similar to the following: - -```bash - -kubectl get pods --namespace=openslice -o wide - -NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES -activemq-59d4bfdb4b-bvjqr 1/1 Running 0 109s 192.168.43.97 kc-2 -bugzilla-client-7dd7cb47cb-8qb8m 1/1 Running 0 100s 192.168.12.114 kc-3 -centrallog-95bbf7867-k8fpt 1/1 Running 0 100s 192.168.12.107 kc-3 -consul-b5dd76b76-64dzk 1/1 Running 0 107s 192.168.43.90 kc-2 -keycloak-7c5b6bbc95-k2qfl 1/1 Running 0 105s 192.168.12.106 kc-3 -manoclient-95f68f4c9-c9t6r 1/1 Running 0 104s 192.168.12.113 kc-3 -mysql-portal-0 1/1 Running 0 107s 192.168.43.99 kc-2 -osom-6d548cf555-q8ptj 1/1 Running 0 104s 192.168.43.93 kc-2 -osportalapi-5fff744db8-5g4zs 1/1 Running 0 103s 192.168.43.98 kc-2 -osscapi-6d68b54d97-jn8tz 0/1 Running 0 102s 192.168.12.104 kc-3 -portalweb-8469d57df4-94tfj 1/1 Running 0 101s 192.168.48.44 kc-nfs -tmfweb-868f7bb9c5-x4lfh 1/1 Running 0 102s 192.168.48.43 kc-nfs -``` -```bash -kubectl get deployments --namespace=openslice -o wide - -NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR -activemq 1/1 1 1 2m15s anactivemq webcenter/activemq:5.14.3 org.etsi.osl.service=activemq -bugzilla-client 1/1 1 1 2m6s bugzilla-client openslice/org.etsi.osl.bugzilla:latest org.etsi.osl.service=bugzilla-client -centrallog 1/1 1 1 2m6s centrallog openslice/org.etsi.osl.centrallog.service org.etsi.osl.service=centrallog -consul 1/1 1 1 2m13s aconsul consul org.etsi.osl.service=consul -keycloak 1/1 1 1 2m11s keycloak quay.io/keycloak/keycloak:11.0.3 org.etsi.osl.service=keycloak -manoclient 1/1 1 1 2m10s manoclient openslice/org.etsi.osl.mano:latest org.etsi.osl.service=manoclient -osom 1/1 1 1 2m10s openslice-osom openslice/org.etsi.osl.osom:latest org.etsi.osl.service=osom -osportalapi 1/1 1 1 2m9s openslice-portalapi openslice/org.etsi.osl.portal.api:latest org.etsi.osl.service=osportalapi -osscapi 1/1 1 1 2m8s openslice-scapi openslice/org.etsi.osl.tmf.api:latest org.etsi.osl.service=osscapi -portalweb 1/1 1 1 2m7s openslice-portalweb openslice/org.etsi.osl.portal.web:latest org.etsi.osl.service=portalweb -tmfweb 1/1 1 1 2m8s openslice-tmfweb openslice/org.etsi.osl.tmf.web:latest org.etsi.osl.service=tmfweb -``` -```bash -kubectl get services --namespace=openslice -o wide - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR -activemq ClusterIP 10.111.160.120 8161/TCP,61616/TCP 2m22s org.etsi.osl.service=activemq -bugzilla-client ClusterIP 10.101.43.28 13010/TCP 2m14s org.etsi.osl.service=bugzilla-client -centrallog ClusterIP 10.109.15.151 13013/TCP 2m14s org.etsi.osl.service=centrallog -consul ClusterIP 10.101.103.240 8500/TCP,8600/TCP 2m21s org.etsi.osl.service=consul -keycloak ClusterIP 10.110.216.62 8080/TCP,8443/TCP 2m19s org.etsi.osl.service=keycloak -manoclient ClusterIP 10.108.112.84 13011/TCP 2m18s org.etsi.osl.service=manoclient -mysql-portal ClusterIP None 3306/TCP 2m19s org.etsi.osl.service=mysql-portal -osom ClusterIP 10.105.173.85 13100/TCP 2m18s org.etsi.osl.service=osom -osportalapi ClusterIP 10.104.121.164 13000/TCP 2m17s org.etsi.osl.service=osportalapi -osscapi ClusterIP 10.108.6.161 13082/TCP 2m16s org.etsi.osl.service=osscapi -portalweb ClusterIP 10.97.126.98 80/TCP 2m15s org.etsi.osl.service=portalweb -tmfweb ClusterIP 10.98.56.82 80/TCP 2m15s org.etsi.osl.service=tmfweb - -``` \ No newline at end of file diff --git a/doc/index.md b/doc/index.md deleted file mode 100644 index e8c0cbc224a790cb4d22acc2ef3d2a0fdf3b1de3..0000000000000000000000000000000000000000 --- a/doc/index.md +++ /dev/null @@ -1,134 +0,0 @@ -drawing - -version: 2023Q4 - Release 0 - -The ETSI Software Development Group for OpenSlice (SDG OSL) is developing an open source service based Operations Support System (OSS) to deliver Network Slice as a Service (NSaaS) following specifications from major SDOs including ETSI, TM Forum and GSMA. See more details [here](https://osl.etsi.org/about/). - - -## Usage - -OpenSlice allows Vertical Customers to browse the available offered service specifications and also allows NFV developers to onboard and manage VNF and Network Service artifacts. It also supports TMFORUM OpenAPIs regarding Service Catalog Management, Ordering, Resource, etc. -The following figure displays the usage of OpenSlice. - -[![OpenSlice usage](./images/index_intro_architecture.png)](./images/index_intro_architecture.png) - - -There are two portals offering UI friendly access to users: - -* The Services portal allows users to access services and service providers to design services. -* The NFV portal allows users to self-manage NFV artifacts and onboard them to a target MANO/NFV Orchestrator. - -3rd party applications can use OpenSlice through TMForum Open APIs. - -Service Specifications reside into Service Catalogs, grouped in Categories. OpenSlice offers a Service Orchestrator called [OSOM](./architecture/osom.md). OSOM instantiates Service Specifications by requesting Network Services from target MANOs/NFVOs. NFV artifacts reside into a VNF/NSD catalog and are onboarded to a target MANO/NFV Orchestrator. Service Specifications reference NSD from the VNF/NSD catalog. - -Customers make Service Orders and OpenSlice instantiates the requested Service Specifications of the Service Order. Running Services instantiated by OpenSlice, reside in OpenSlice Service Inventory. The following picture displays how Service Specifications are related to Running Services and how Running Services relate with instantiated running Network Services. (See also [Service Inventory](./service_inventory.md)) - -[![OpenSlice Service Specification instantiation](./images/service_specification_instantiation.png)](./images/service_specification_instantiation.png) - -## Service Lifecycle Rules - -OpenSlice constains support for defining rules of services, affecting their behavior. See [LCM Rules](./lcm.md). - -## Multidomain scenarios and federation - -OpenSlice can be used to exchange service specifications/catalogs and make service orders between Organizations as the following figure displays. - -[![OpenSlice Service Specification instantiation](./images/multi-domain-organizations.png)](./images/multi-domain-organizations.png) - -An Identity federation is also possible since our authentication service is based on Keycloak (see [OAuth](./architecture/oauth.md)). - -See more on [Consuming Services From External Partner Organizations](./architecture/consumingServicesFromExternalPartners.md). - - -## Live Demo - -* OpenSlice demo: -* OpenSlice Service Catalogs and ordering: -* OpenSlice NFV Services onboarding: -> username=admin, password=openslice
or
username=admin, password=changeme - - -## Video demo - -* - ---- - -## Installing - -See [Deployment/Installation](./deployment.md). - ---- - -## Hardware requirements - -The complete environment consists of microservices deployed as docker containers. Portainer is also installed to monitor them at port 9000. - -If you would like to operate all APIs, OSOM, the Mysql Server, nginx, etc then you need at least: - -| **Minimum Hardware Requirements** | **Recomended Hardware Requirements** | -| --------------------------------- | ------------------------------------ | -| 4 CPU cores | 8 CPU cores | -| 8 GB RAM | 16 GB RAM | -| 20 GB storage | 40 GB storage | - - -(NOTE: Bugzilla or ELK are not included and we assume they run elsewhere) - -However, you may try the front facing services of OpenSlice with a minimum installation on a VM on a laptop with: - -- 2 cores -- 4GB of RAM - - - -## Supported APIs - -Quick overview of the supported APIs through our Swagger links: - -* [TMF APIs](http://portal.openslice.io/tmf-api/swagger-ui/index.html) -* [API for VNF/NSD management](http://portal.openslice.io/osapi/swagger-ui/index.html) - -## Source code - -OpenSlice source code is available at [OSL GitLab repository](https://labs.etsi.org/rep/osl/code). - -## Contributing - -You may contribute following the guidelines at [Contributing page](./contributing/developing.md). - -## Social Media - -* [Twitter](https://twitter.com/OpensliceOSS) -* [Slack](https://openslice.slack.com) - -## History - -* The NFV portal part of OpenSlice was initially developed in H2020 European Research project [5GinFIRE](https://5ginfire.eu) by University of Patras, Greece -* OpenSlice core services, APIs was further developed and maintained in H2020 European project [5G-VINNI](https://5g-vinni.eu/) by University of Patras, Greece -* OpenSlice has been a part off OSM's OSS/BSS ecosystem - - -[![Part of OSM Ecosystem](./images/osm_ecosystem_ossbss.png)](https://osm.etsi.org/wikipub/index.php/OSS_BSS) - -## Ecosystem - -Discover the current OpenSlice ecosystem [here](https://osl.etsi.org/ecosystem/). - -## Citation - -Please cite our [![paper]](https://arxiv.org/abs/2102.03290) if you use Openslice - -``` - -@misc{tranoris2021openslice, - title={Openslice: An opensource OSS for Delivering Network Slice as a Service}, - author={Christos Tranoris}, - year={2021}, - eprint={2102.03290}, - archivePrefix={arXiv}, - primaryClass={cs.NI} -} - -``` \ No newline at end of file diff --git a/doc/lcm.md b/doc/lcm.md deleted file mode 100644 index b668b908c58da2c6972e11a0b6ca50450fd02176..0000000000000000000000000000000000000000 --- a/doc/lcm.md +++ /dev/null @@ -1,158 +0,0 @@ -# Lifecycle Management (LCM) Rules - -* NOTE: This is a prototype/experimental feature. So issues might raise during operation - -LCM Rules are used for defining complex conditions and actions during the lifecycle of a service. In Openslice there are four types of rules defined: - -* PRE_PROVISION -* CREATION -* AFTER_ACTIVATION -* SUPERVISION -* AFTER_DEACTIVATION - - -The following figure displays the different phases that the rules are performed, during the lifecycle of a Network Slice Instance. - -[![Rules in NSI](../images/lcm/lcmfig1.png)](../images/lcm/lcmfig1.png) - -* PRE_PROVISION rules: Run only once just before creating a service with a given priority. -* CREATION rules: Run while the referenced service dependencies of a service are created -* AFTER_ACTIVATION rules: Run only once just after a service get the ACTIVE state -* SUPERVISION rules: Run when a characteristic of a service is changed and the service is in the ACTIVE state -* AFTER_DEACTIVATION rules: Run only once just after a service get the INACTIVE/TERMINATED state - -In general the rules allow to perform many actions during service LCM. Thes are some examples: - -* Modify service specification parameters before the instantiation of a service (or during operation) based on other dependencies. These parameters might be part of other services already included in Service order -* Translate GST/NEST parameter values to other values passed later to NFVO for instantiation or control -* Define complex OSM Configs based on other dependencies and passing variables -* Define any dependencies when creating the referenced services -* Dynamically include new service dependencies -* Create new service orders so include dynamically other services -* Call external (RESTful) services (via http(s), define payload, examine response) - - - - -## Examine if the rules are executed successfully - -Rules are transformed automatically to executable code (currently is Java). If a rule is performed successfully or has any issues (e.g. unexpected syntax errors or exceptions) appear in OSOM logfiles and also tey are attached as Notes to the running Service. - -## LCM Rules and OSOM Service Orchestration - -OSOM is the responsible service for executing the rules on a specific phase. The following image explains the design in the BPMN phases: - - -[![Rules](../images/lcm/lcmfig1_osom.png)](../images/lcm/lcmfig1_osom.png) - - - -## Define rules - -Rules are defined when designing a Service Spec. Here is an example of a list of rules: - - -[![lcmrules](../images/lcm/lcmfig2.png)](../images/lcm/lcmfig2.png) - -Execution order of rules on a specific phase is random - -* NOTE: There is a priority field. The lower the number the highest the priority of rule execution. For example Rule with priority 0 will run before rule with priority 1. - - -### Definition language - -* The visual language that Openslice used is based on Google's Blockly (see https://developers.google.com/blockly) -* The blockly graph is automatically translated to Java internally and then dynamically executed during orchestration phases. - -The following figure is an example of such a rule design. The rule for example will run in PRE_PROVISION phase: - -[![lcmrules](../images/lcm/lcmfig3.png)](../images/lcm/lcmfig3.png) - -* The goal of the above rule is to properly define a variable AreaCodes given the chosen AreaOfService from a Service Order. -* On the right side the user can define some rule properties or observe the underlying generated java code. - - -## The blocks library - -The following images describe some blocks found in the library. - -Blockly has syntax rules. It helps with colours to define them. - -So for example a parameter that is a Number cannot be "glued" with a String. Will need some conversion first - -[![lcmrules](../images/lcm/lcmfig4.png)](../images/lcm/lcmfig4.png) -[![lcmrules](../images/lcm/lcmfig5.png)](../images/lcm/lcmfig5.png) -[![lcmrules](../images/lcm/lcmfig6.png)](../images/lcm/lcmfig6.png) -[![lcmrules](../images/lcm/lcmfig7.png)](../images/lcm/lcmfig7.png) -[![lcmrules](../images/lcm/lcmfig8.png)](../images/lcm/lcmfig8.png) - -## Examples of Rules - - -The following images provide some examples of rules. - -### define variables according to cases - -In the following example we : - -* define a String variable. -* Then according to the Area of Service selected from the Service Order of the Service Specification we need to define it properly. -* We output the value to the OSOM Log -* Define dynamically the value of another parameter (This is fictional) and then do some other condition check - -The strAreaCodes could be passed then e.g. to NFVO for instantiation of services to these cells. - - -[![lcmrules](../images/lcm/lcmfig9.png)](../images/lcm/lcmfig9.png) - - - -### Define complex OSM configs for DAY 0 - -The following displays some complex examples for defining the parameters to pass to the NFV. In this case is OSM. - -* NOTE: The OSM_CONFIG characteristic of a service is the one that it is used in orchestration to instantiate NS from OSM - -* check the variable strTargetsParam. It is passed to the variable strOsmConfig3 which is executed if the Number of Cameras is more than 100. -* if the Video quality requested is 3, then the Maximum Namber of camers will be 8. Check the OSM detailed configuration block and its syntax. -* if the Video quality requested is 2, we use a simpler OSM Config block to configure the parameter OSM_CONFIG. We just injected a json text ( watch the Escape of the string for the Quotes!) -* if the Video quality requested is 1, again we use a simpler OSM Config block to configure the parameter OSM_CONFIG. We use as injected json text a variable constructed later - - -[![lcmrules](../images/lcm/lcmfig10.png)](../images/lcm/lcmfig10.png) - - -### Define and instantiate different services according to Service Order request - -In the following example we would like to offer a service either as Platinum, Gold or Silver. Depending on the selection we need to instantiate different services. - -There are different ways to accomplish this: - -* create dynamically New Service Orders of RFSs with equivalent quality of Services -* change for example the VIMs that you deploy the NS -* change the NSD (that is use different VNFs) - -The following image displays for example the latter case. - -[![lcmrules](../images/lcm/lcmfig11.png)](../images/lcm/lcmfig11.png) - - -### Call an external RESTful service - -This is useful in cases for example of alarms , external logging, calling other services e.g. email or even a complex algorithm written in other language e.g. call an external service and get a result. (service e.g. a Python service) - - -[![lcmrules](../images/lcm/lcmfig12.png)](../images/lcm/lcmfig12.png) - -[![lcmrules](../images/lcm/lcmfig13.png)](../images/lcm/lcmfig13.png) - -### Create New Service Orders - -The following example calls to Order a New Service Specification with specific Parameter Values - -[![lcmrules](../images/lcm/lcmfig14.png)](../images/lcm/lcmfig14.png) - - - - - diff --git a/doc/nfvcatalogs.md b/doc/nfvcatalogs.md deleted file mode 100644 index ad055840d65abfe337af4a5b60a3e7e10097ec42..0000000000000000000000000000000000000000 --- a/doc/nfvcatalogs.md +++ /dev/null @@ -1,42 +0,0 @@ -# NFV Services - -NFV Services are managed through a dedicate UI the NFV portal (eg http://portal.openslice.io/nfvportal) - -Users are able through this portal to manage their NFV artifacts towards the NFVO, ( for example onboard VNFs and NSDs to a target OSM) - - -Openslice NFV Services target to accommodate the following envisaged user roles. All users are assumed to be Authenticated: - -* NFV developer: This role is responsible to upload VNF and NSD Descriptors in the Openslice services towards NFVO like OSM -* Services administrator: This role represents the user that are responsible for maintenance of the Openslice services - -(obsolete: ) - -* Testbed provider: This role represents users that are responsible for testbed administration, configuration, integration, adaptation, support, etc -* Experimenter: This role represents the user that will utilize our services and tools to deploy an experiment. That is the experiment description in terms of e.g.: NSD (Network Service Descriptor) or TOSCA Specification (in future versions) - - -Finally an anonymous user role exists who has some really simple usage scenarios (e.g. signup through the portal) - - -During the onboarding process the following occurs: - -• A NFV developer submits a NFV archive (VNF or NSD) (he can later manage if needed some metadata) -• The administrator can manage the NFV artifact (e.g. edit it) -• The administrator On-Boards the NFV artifact to the target MANO -• The administrator can optionally mark the NFV: -o As public in order to be publicly visible by all portal users -o As Certified which means this is certified by a certain entity - - -## Request a new NSD deployment (this is different in comparison to Services) - - -A developer requests a new network service deployment (which NSD, tentative dates, target infrastructure, etc.). The request is marked as UNDER_REVIEW - -* The administrator is notified about the new request and he has the following options: -* Schedule the deployment for the requested dates or propose other dates. The request is marked as SCHEDULED -* Reject the request for some reason. The Request is marked as REJECTED -* Deploy the request to target VIM(s). The Request is marked as RUNNING -* Finalize the deployment and release resources. The Request is marked as COMPLETED -* every change of the request-lifecycle the experimenter is notified. \ No newline at end of file diff --git a/doc/nfvoconfig.md b/doc/nfvoconfig.md deleted file mode 100644 index 91efd366f981905fa7cd16fdf3838b2c6954c4ed..0000000000000000000000000000000000000000 --- a/doc/nfvoconfig.md +++ /dev/null @@ -1,19 +0,0 @@ -# NFV Orchestrator configuration - -NOTE: Currently we support Open Source MANO version SEVEN/EIGHT/TEN/ELEVEN. Later versions of OSM may also be supported by the existing configuration, as from OSM 9+ the project converged to the SOL005 interface, regarding the NBI, and SOL006 (YANG model), regarding the NFV/NSD packaging. Also an implementation of a generic SOL005 interface is supported, but not extensively tested. - -Configuration of your target(s) NFVOs/MANO services with Openslice is performed through the NFV portal. - -Login to http://yourdomain/nfvportal/ - -Navigate to Admin->Manage MANO Platforms and pick one of the supported MANO platform(s), e.g. Name=OSMvTEN, Version=OSMvTEN and save - -Navigate to Admin->Manage MANO providers and enter a New MANO Provider: - -* Name whatever you wish -* API URL Endpoint, eg: https://10.10.10.10:9999 (This is the SOL005 NBI endpoint) -* Username, password and Project of your OSM tenant. - -Check EnabledForONBOARDING, so when users onboard VNFs/NSDs they will be automatically ONBOARDED to this MANO. If left unchecked, the onboarding must be performed manually after the VNF/NSD is uploaded to the portal. - -Check EnabledForSYNC, if you want to support MANO->Openslice auto synchronization. When enabled, the existing VNFs/NSDs and VIMs (and any updates on them) of the registered MANO are also reflected to the portal. \ No newline at end of file diff --git a/doc/service_inventory.md b/doc/service_inventory.md deleted file mode 100644 index 480b92f111424c67ab3b4efbba089f4339ddb732..0000000000000000000000000000000000000000 --- a/doc/service_inventory.md +++ /dev/null @@ -1,39 +0,0 @@ -# Service Inventory - -After a Service Order completion, active services with their additional characteristics are found: - -* From the Order Items of a selected Service order -* from the menu of Service inventory and then selecting details of each service -* through the Service Inventory API (TMF 638 - Service Inventory Management ) - - -Openslice creates a Service for the requested CFS. Customers make Service Orders and Openslice instantiates the requested Service Specifications for each Service Order Item of a Service Order. Running Services instantiated by Openslice, reside in Openslice Service Inventory. The following picture displays how Service Specifications are related to Running Services and how Running Services relate with instantiated running Network Services. - - -[![Openslice Service Specification instantiation](./images/service_specification_instantiation.png)](./images/service_specification_instantiation.png) - - -There is a hierarchy of services. Usually an Instantiated CFS has Supporting Services some Instantiated RFSs. Then an Instantiated RFS is related to some running NS managed by NFVO - - -## Interacting with an Active Service (Day 2 config) - -In some cases, if the underlying service is configured with actions (for example in OSM Day 2 primitive actions), there are characteristics that can be modified. Usually they are named like : ::Primitive:: - -The user can edit the characteristic with a new value. The value is propagated through the OSOM and NFVO down to the related VNF. - -## Terminating/Inactivating a service - -You can terminate the service with one of the following processes: - -* Select the related Service Order and terminate the Order Item. This will delete all the underlying related active services. The Order goes to ACKNOWLEDGED->INPROGRESS->COMPLETE -* To terminate or inactivate a service, select the specific service from the inventory, press Edit and set the State either to Inactive or Terminated - -Warning: if you terminate or inactivate a service the action cannot be undone. - - -## uml: sequence diagram -Here I will embed PlantUML markup to generate a sequence diagram. - -I can include as many plantuml segments as I want in my Markdown, and the diagrams can be of any type supported by PlantUML. - diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index a3cb2d20b9beebf2d1667116965ff90878eb1325..0000000000000000000000000000000000000000 --- a/mkdocs.yml +++ /dev/null @@ -1,95 +0,0 @@ -# Project information -site_name: ETSI SDG OCF Documentation -site_description: ETSI SDG OCF Documentation page -site_author: OpenCAPIF by ETSI -site_url: https://ocf.etsi.org/ - -# Repository -repo_name: OCF GitLab -repo_url: https://labs.etsi.org/rep/ocf -edit_uri: "" -docs_dir: doc - -# edit_uri: edit/docs-revamp/docs/ -# edit_uri: blob/docs-revamp/docs/ - -# remote_name: origin -# remote_branch: gh-pages - -# Theme and overrides, i.e. language partial -# https://squidfunk.github.io/mkdocs-material/creating-your-site/ -theme: - name: material - - # Don't include MkDocs' JavaScript - #include_search_page: false - #search_index_only: true - - # Default values, taken from mkdocs_theme.yml - language: en -# feature: -# tabs: true - features: - - navigation.instant - - navigation.instant.progress - - navigation.top - - navigation.footer - - navigation.path - - search - - search.highlight - - palette: - primary: 'cyan' - accent: 'blue' - - logo: 'images/logo_osl.png' - favicon: images/favicon.png - - icon: - repo: fontawesome/brands/gitlab - -# Copyright -copyright: "Copyright © 2019-2024 OpenCAPIF Project" - -# Options -extra: -# search: -# languages: "en" - social: - - icon: fontawesome/solid/globe - link: https://ocf.etsi.org/ - - icon: fontawesome/brands/gitlab - link: https://labs.etsi.org/rep/ocf - - icon: fontawesome/brands/linkedin - link: https://www.linkedin.com/company/opencapif/ - - icon: fontawesome/brands/x-twitter - link: https://twitter.com/OpenCAPIF - version: - provider: mike - -# Page tree -nav: - - Overview: - - Introduction: index.md - - Getting Started: - - Deployment/Installation: deployment.md - - NFV Orchestrator Configuration: nfvoconfig.md - - Service Catalogs: catalogs.md - - NFV Catalogs: nfvcatalogs.md - - LCM Rules: lcm.md - - Consuming Services From External OSS: ./architecture/consumingServicesFromExternalPartners.md - - Service Inventory: service_inventory.md - - Alarms and Actions: alarms_actions.md - - Design & Architecture: - - Architecture: ./architecture/architecture.md - - Message bus: ./architecture/messagebus.md - - OSOM: ./architecture/osom.md - - Authentication: ./architecture/oauth.md - - TMF API: ./architecture/tmfapi.md - - NFV API: ./architecture/nfvapi.md - - TMF WEB: ./architecture/tmfweb.md - - NFV WEB: ./architecture/nfvweb.md - - Issue management: ./architecture/issuemgt.md - - Central logging: ./architecture/centrallog.md - - Contributing: - - Developing: ./contributing/developing.md diff --git a/public/develop/404.html b/public/develop/404.html new file mode 100644 index 0000000000000000000000000000000000000000..31264996b12f9b8fac8ae5075fb6a5baf1911714 --- /dev/null +++ b/public/develop/404.html @@ -0,0 +1,944 @@ + + + + + + + + + + + + + + + + + + + + + + + ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/alarms_actions/index.html b/public/develop/alarms_actions/index.html new file mode 100644 index 0000000000000000000000000000000000000000..68cea55004e81a6b0e709f26bd47fbc13d5a4e7a --- /dev/null +++ b/public/develop/alarms_actions/index.html @@ -0,0 +1,1216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Alarms and Actions - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Alarms

+

In Openslice parts of TMF642 Alarm Management API are currently implemented. Alarms can be managed through the TMF API endpoint as well as the UI.

+

Alarms and Actions

+

Note: Actions is an experimental feature. We expect to have a more mature solution in future. The component in the architecture is the Openslcie Assurance Services

+

Alarms can be automatically resolved by specific actions. Today only the following actions are offered.

+
    +
  • execDay2
  • +
  • scaleServiceEqually
  • +
+

execDay2

+

Usually used to perform a Day2 configuration (towards OSM). To use it, Create a New Action Specification Name=execDay2 as following

+

Openslice  usage

+

Now make a Service Order for your service. In this example ςε used a cirros NSD

+

Create a New Action Rule for the running services as the following example:

+

Openslice  usage

+

The scope is the running cirros service.

+

Params should be paramname=value;paramname2=value2;paramname3=value3 (must exist in the VNF otherwise OSM will raise an error).

+

In this case should be filename=test.txt

+

Primitive=touch

+

ServiceId = select the service which will accept the Day2. In this case is the same

+

To test it:

+

Go to the Service Inventory and select the active Service.

+

Note the UUID of the service (e.g. c4e7990a-e174-4cd2-9133-b10e56721e08 copy from address bar), DeploymentRequestID and NSDID from characteristics

+

You can either use the UUID of the service or the DeploymentRequestID and POST to the Alarms endpoint ( /tmf-api/alarmManagement/v4/alarm)

+

If the DeploymentRequestID is used then POST:

+

+{
+  "alarmRaisedTime": "2021-06-29T12:30:24.675Z",
+  "alarmReportingTime": "2021-06-29T12:30:54.675Z",
+  "state": "raised",
+  "alarmType": "qualityOfServiceAlarm",
+  "probableCause": "thresholdCrossed",
+  "ackState": "unacknowledged",
+  "perceivedSeverity": "major",
+  "sourceSystemId": "mano-client-service",
+  "alarmDetails": "NSID=3;DeploymentRequestID=1",
+  "specificProblem": "myalram raised"
+}
+
+
+

If the UUID is used then POST:

+

+{
+  "alarmRaisedTime": "2021-06-29T12:30:24.675Z",
+  "alarmReportingTime": "2021-06-29T12:30:54.675Z",
+  "state": "raised",
+  "alarmType": "qualityOfServiceAlarm",
+  "probableCause": "thresholdCrossed",
+  "ackState": "unacknowledged",
+  "perceivedSeverity": "major",
+  "sourceSystemId": "mano-client-service",
+  "alarmDetails": "analarm",
+  "specificProblem": "myalram raised",
+  "affectedService": [
+    {
+      "id": "c4e7990a-e174-4cd2-9133-b10e56721e08"
+    }
+  ]
+
+}
+
+
+

The Alarm to be created must have the affected Service ID equal to the running service from the scope (the cirros_ns)

+

Go to service inventory you will see the notes and also the service characteristics for any EXEC_ACTION updates

+

You can also adjust the alarm conditions. They must match true so the alarm to be acknowledged +So if another external service raises an Alarm (with POST) for the running service, a Day2 will be performed on another Service

+

scaleServiceEqually

+

This action is used from getting a scaling event from OSM. Please see the next demo for details on how it works

+

Prototype demo

+

You can watch how we used the prototype on the following ETSI ZMS PoC #2

+ + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/architecture/index.html b/public/develop/architecture/architecture/index.html new file mode 100644 index 0000000000000000000000000000000000000000..306dadb6fae746384fb443dd458d5147e39c3c07 --- /dev/null +++ b/public/develop/architecture/architecture/index.html @@ -0,0 +1,1109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Architecture - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Architecture

+

Openslice offers the following main functionalities:

+
    +
  • Service Catalog Management: A CSP will have the ability to manage the Service Catalog Items, their attributes , organize in categories and decide what to make available to Customers
  • +
  • Services Specifications: A CSP will be able to manage Service Specifications
  • +
  • Service Catalog Exposure: A CSP will be able to expose catalog to customers and related parties
  • +
  • Service Catalog to Service Catalog: Openslice able to consume and provide Service Catalog items to other catalogs
  • +
  • Service Order: The Customer will be able to place a Service Order
  • +
  • Service Inventory: The Customer and Provider will be able to view deployed Services status
  • +
+

The following figure displays the overall architecture of Openslice.

+

Openslice  architecture

+

Openslice allows Vertical Customers browsing the available offered service specifications. It consists of:

+
    +
  • Web frontend UIs that consist of mainly two portals: i) a NFV portal allowing users self-service management and onboarding VNFDs/NSDs to facility’s NFVO ii) a Services Portal, which allows users to browse the Service Catalog, Service Blueprints specifications and the Service Inventory
  • +
  • An API gateway that proxies the internal APIs and used by the web front end as well as any other 3rd party service
  • +
  • A Message Bus where all microservices use it to exchange messages either via message queues or via publish/subscribe topics
  • +
  • An authentication server implementing Oauth2 authentication scheme
  • +
  • A microservice offering TMF compliant API services (eg Service Catalog API, Service Ordering APIetc)
  • +
  • A microservice offering NFV API services (eg VNF/NSD onboarding etc) and allows to store VNFDs and NSDs in a catalog
  • +
  • A microservice that is capable to interface to an issue management system. For example it raises an issue to all related stakeholders (CSP, NOP, CSC) that a new Service Order is requested
  • +
  • Central logging microservice that is capable to log all distributed actions in to an Elasticsearch cluster
  • +
  • A Service Orchestrator solution that will propagate Service Ordering requests to the equivalent SOs and NFVOs
  • +
+

The following figure depicts how Openslice microservices are deployed

+

Openslice microservices network deployment

+

Deploying Openslice in multi domain scenarios

+

A typical deployment across domains, involves today some typical components: i) an OSS/BSS to allow customers access the service catalog and perform service orders, ii) a Service Orchestrator (SO) component for executing the service order workflow, as well as iii) a Network Functions Virtualization Orchestrator (NFVO) for configuring the iv) network resources.

+

TMF Open APIs are introduced not only for exposing catalogues and accepting service orders, but also implementing the East-West interfaces between the domains, fulfilling also the LSO requirements as introduced by MEF.

+

The following figure shows how openslice could be used in such scenarios:

+

Openslice  multi-domain-architecture

+

See more Consuming Services From External Partner Organizations

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/centrallog/index.html b/public/develop/architecture/centrallog/index.html new file mode 100644 index 0000000000000000000000000000000000000000..6798b28e5c9de28aa01383d1b2fe21bb0c020edd --- /dev/null +++ b/public/develop/architecture/centrallog/index.html @@ -0,0 +1,1024 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Central logging - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Central Logging

+

Openslice follows the centralized log management concept, i.e. a type of logging solution system that consolidates the log data from different services and pushes it to a central, accessible and easy-to-use interface.

+

For that reason, Elasticsearch is elected as an open-source centralized logging solution for collecting, parsing and storing logs towards a real-time data analytics tool that provides insights from any type of structured and unstructured data source.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/consumingServicesFromExternalPartners/index.html b/public/develop/architecture/consumingServicesFromExternalPartners/index.html new file mode 100644 index 0000000000000000000000000000000000000000..3903e7c857e0d597e85e01b223bf494f72da8227 --- /dev/null +++ b/public/develop/architecture/consumingServicesFromExternalPartners/index.html @@ -0,0 +1,1238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Consuming Services From External OSS - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Consuming Services From External Partner Organizations

+

A typical deployment across domains, involves today some typical components: i) an OSS/BSS to allow customers access the service catalog and perform service orders, ii) a Service Orchestrator (SO) component for executing the service order workflow, as well as iii) a Network Functions Virtualization Orchestrator (NFVO) for configuring the iv) network resources.

+

TMF Open APIs are introduced not only for exposing catalogues and accepting service orders, but also implementing the East-West interfaces between the domains, fulfilling also the LSO requirements as introduced by MEF.

+

The following figure shows how openslice could be used in such scenarios:

+

Openslice  multi-domain-architecture

+

In Openslice we can consume services from 3rd parties via Open APIs.

+

We use the TMF 632 Party Management model to specify Organizations that we can exchange items and other information such as:

+
    +
  • Import Service Specifications
  • +
  • Create a Service Order
  • +
  • Use the Service Inventory to query the status of the service ordered to the external partner organization
  • +
+

Define an Organization as 3rd party to consume services East-West

+

An organization must have the following characteristics in openslice catalog, like for example:

+

"EXTERNAL_TMFAPI_BASEURL", "http://portal.openslice.io"

+

"EXTERNAL_TMFAPI_CLIENTREGISTRATIONID", "authOpensliceProvider"

+

"EXTERNAL_TMFAPI_OAUTH2CLIENTID", "osapiWebClientId"

+

"EXTERNAL_TMFAPI_OAUTH2CLIENTSECRET", "secret"

+

"EXTERNAL_TMFAPI_OAUTH2SCOPES", scopes

+

"EXTERNAL_TMFAPI_OAUTH2TOKENURI", "http://portal.openslice.io/osapi-oauth-server/oauth/token"

+

"EXTERNAL_TMFAPI_USERNAME", "admin"

+

"EXTERNAL_TMFAPI_PASSWORD", "openslice"

+

"EXTERNAL_TMFAPI_SERVICE_CATALOG_URLS" = "/tmf-api/serviceCatalogManagement/v4/serviceSpecification?type=CustomerFacingServiceSpecification" (this is optional, fetch a list of service specs it will be relative with the BASEURL. If the url is empty then no specs will be fetched, the EXTERNAL_TMFAPI_SERVICE_CATEGORY_URLS might be used)

+

"EXTERNAL_TMFAPI_SERVICE_CATEGORY_URLS" = "/tmf-api/serviceCatalogManagement/v4/serviceCategory/{categoryid}" (this example will fetch all specs in a category. You may define comma separated URLs of categories API URL . This will fetch specifications of every defined category. If you want only one specific category put for example the uuid only of one category: "/tmf-api/serviceCatalogManagement/v4/serviceCategory/bda02821-bc4d-4bd6-b64b-d9c2aa5f8e6d". multiple urls should be "/tmf-api/serviceCatalogManagement/v4/serviceCategory/bda02821-bc4d-4bd6-b64b-d9c2aa5f8e6d,/tmf-api/serviceCatalogManagement/v4/serviceCategory/9b6d8bf3-abd2-43c4-8154-c8c6fe5545b2")

+

"EXTERNAL_TMFAPI_SERVICE_SPEC" = "/tmf-api/serviceCatalogManagement/v4/serviceSpecification"

+

"EXTERNAL_TMFAPI_SERVICE_ORDER_URLS"= "/test/v1/serviceorder" (this is optional)

+

An example Organization defined example in json:

+

+{
+  "uuid": "1a09a8b5-6bd5-444b-b0b9-a73c69eb42ae",
+  "@baseType": "BaseEntity",
+  "@schemaLocation": null,
+  "@type": null,
+  "href": null,
+  "name": "Openslice.io",
+  "id": "1a09a8b5-6bd5-444b-b0b9-a73c69eb42ae",
+  "isHeadOffice": null,
+  "isLegalEntity": null,
+  "nameType": null,
+  "organizationType": null,
+  "tradingName": null,
+  "contactMedium": [],
+  "creditRating": [],
+  "existsDuring": null,
+  "externalReference": [],
+  "organizationChildRelationship": [],
+  "organizationIdentification": [],
+  "organizationParentRelationship": null,
+  "otherName": [],
+  "partyCharacteristic": [
+    {
+      "uuid": "3a2f7221-e0a2-4a6b-88d1-534c8e1963f6",
+      "@baseType": "BaseEntity",
+      "@schemaLocation": null,
+      "@type": null,
+      "href": null,
+      "name": "EXTERNAL_TMFAPI_CLIENTREGISTRATIONID",
+      "valueType": null,
+      "value": {
+        "value": "authOpensliceProvider",
+        "alias": null
+      }
+    },
+    {
+      "uuid": "c24bb527-f178-4d38-9b93-2027c1732876",
+      "@baseType": "BaseEntity",
+      "@schemaLocation": null,
+      "@type": null,
+      "href": null,
+      "name": "EXTERNAL_TMFAPI_USERNAME",
+      "valueType": null,
+      "value": {
+        "value": "admin",
+        "alias": null
+      }
+    },
+    {
+      "uuid": "27e45df8-414b-44c6-a5d5-3f064e2cfd3b",
+      "@baseType": "BaseEntity",
+      "@schemaLocation": null,
+      "@type": null,
+      "href": null,
+      "name": "EXTERNAL_TMFAPI_PASSWORD",
+      "valueType": null,
+      "value": {
+        "value": "openslice",
+        "alias": null
+      }
+    },
+    {
+      "uuid": "e0e470b8-6024-4014-8a18-2333e5465ce1",
+      "@baseType": "BaseEntity",
+      "@schemaLocation": null,
+      "@type": null,
+      "href": null,
+      "name": "EXTERNAL_TMFAPI_OAUTH2CLIENTSECRET",
+      "valueType": null,
+      "value": {
+        "value": "secret",
+        "alias": null
+      }
+    },
+    {
+      "uuid": "3e0de762-ac80-4c1e-a0a1-f265ff0899b4",
+      "@baseType": "BaseEntity",
+      "@schemaLocation": null,
+      "@type": null,
+      "href": null,
+      "name": "EXTERNAL_TMFAPI_OAUTH2SCOPES",
+      "valueType": null,
+      "value": {
+        "value": "admin;read",
+        "alias": null
+      }
+    },
+    {
+      "uuid": "0bbb8314-f7f2-420d-9fed-ba054b15f886",
+      "@baseType": "BaseEntity",
+      "@schemaLocation": null,
+      "@type": null,
+      "href": null,
+      "name": "EXTERNAL_TMFAPI_OAUTH2TOKENURI",
+      "valueType": null,
+      "value": {
+        "value": "http://portal.openslice.io/osapi-oauth-server/oauth/token",
+        "alias": null
+      }
+    },
+    {
+      "uuid": "3a567de4-79eb-4006-a500-3e5229b44175",
+      "@baseType": "BaseEntity",
+      "@schemaLocation": null,
+      "@type": null,
+      "href": null,
+      "name": "EXTERNAL_TMFAPI_OAUTH2CLIENTID",
+      "valueType": null,
+      "value": {
+        "value": "osapiWebClientId",
+        "alias": null
+      }
+    },
+    {
+      "uuid": "6dca729f-dbe1-46b7-89f1-5c4f9fe89d4e",
+      "@baseType": "BaseEntity",
+      "@schemaLocation": null,
+      "@type": null,
+      "href": null,
+      "name": "EXTERNAL_TMFAPI_BASEURL",
+      "valueType": null,
+      "value": {
+        "value": "http://portal.openslice.io",
+        "alias": null
+      }
+    }
+  ],
+  "relatedParty": [],
+  "status": null,
+  "taxExemptionCertificate": []
+}
+
+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/issuemgt/index.html b/public/develop/architecture/issuemgt/index.html new file mode 100644 index 0000000000000000000000000000000000000000..790646b7206d1efd18df62ba8c89c8338b241120 --- /dev/null +++ b/public/develop/architecture/issuemgt/index.html @@ -0,0 +1,1025 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Issue management - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Issue Management

+

For issue management support, Openslice relies on Bugzilla. Bugzilla is a ticketing tool that allows issue reporting and tracking via tickets to all relevant stakeholders.

+

The figure below displays the overall issue management service architecture integrating Bugzilla as its core and how this tool interacts with other Openslice services presenting some distinctive scenarios. It should be noted that Bugzilla tickets will not only be used for bugs/errors, but also for general requests, e.g. Service Order procedure.

+

Issue management system

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/messagebus/index.html b/public/develop/architecture/messagebus/index.html new file mode 100644 index 0000000000000000000000000000000000000000..785a1699875da9940638d19bd4087f5082a0c448 --- /dev/null +++ b/public/develop/architecture/messagebus/index.html @@ -0,0 +1,3638 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Message bus - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Message Bus and exchanged Messages

+

Openslice has a Message bus which allows Openslice services to exchange messages via queues and topics.

+

It is based on ActiveMQ.

+

3rd party services can be attached to bus and subscribe to message topics or request resources via queues.

+

QUEUE MESSAGES

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_SERVICEORDERS
Namejms:queue:CATALOG.GET.SERVICEORDERS
Typequeue
DestinationTMF API service
ProducersOSOM
Body
DescriptionReturn a List as String Json
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_SERVICEORDER_BY_ID
Namejms:queue:CATALOG.GET.SERVICEORDER_BY_ID
Typequeue
DestinationTMF API service
ProducersOSOM
BodyString orderid
DescriptionReturn a ServiceOrder as String Json
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_UPD_SERVICEORDER_BY_ID
Namejms:queue:CATALOG.UPD.SERVICEORDER_BY_ID
Typequeue
DestinationTMF API service
ProducersOSOM
BodyServiceOrderUpdate serviceOrder
Headers"orderid"= orderid
DescriptionReturns a ServiceOrder as String
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_SERVICESPEC_BY_ID
Namejms:queue:CATALOG.GET.SERVICESPEC_BY_ID
Typequeue
DestinationTMF API service
ProducersOSOM
Bodyspecid
DescriptionReturn a ServiceSpecification
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_ADD_SERVICESPEC
Namejms:queue:CATALOG.ADD.SERVICESPEC
Typequeue
DestinationTMF API service
ProducersCRIDGE
BodyServiceSpecCreate
DescriptionCreates a ServiceSpecification and returns a ServiceSpecification as String
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_UPD_SERVICESPEC
Namejms:queue:CATALOG.UPD.SERVICESPEC
Typequeue
DestinationTMF API service
ProducersCRIDGE
BodyServiceSpecUpdate
Headers"serviceSpecid" = serviceSpecId
DescriptionUpdates a ServiceSpecification and returns a ServiceSpecification as String.
---
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_UPDADD_SERVICESPEC
Namejms:queue:CATALOG.UPDADD.SERVICESPEC
Typequeue
DestinationTMF API service
ProducersCRIDGE
BodyServiceSpecUpdate
Headers"serviceSpecid" = serviceSpecId, "forceId"=forceId
DescriptionUpdates a ServiceSpecification and returns a ServiceSpecification as String. If forceId is true then tries to assign the requested ID to the spec
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_ADD_SERVICEORDER
Namejms:queue:CATALOG.ADD.SERVICEORDER
Typequeue
DestinationTMF API service
ProducersOSOM
BodyServiceOrderCreate serviceOrder
Headers
DescriptionCreates a ServiceOrder and returns a ServiceOrder as String
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_INITIAL_SERVICEORDERS_IDS
Namejms:queue:CATALOG.GET.INITIAL_SERVICEORDERS
Typequeue
DestinationTMF API service
Producers
Body
DescriptionReturn a List as String Json
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_SERVICEORDER_IDS_BY_STATE
Namejms:queue:CATALOG.GET.ACKNOWLEDGED_SERVICEORDERS
Typequeue
DestinationTMF API service
ProducersOSOM
Body
Headers"orderstate"= orderState
DescriptionString Json ArrayList of ServiceOrders
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_ADD_SERVICE
Namejms:queue:CATALOG.ADD.SERVICE
Typequeue
DestinationTMF API service
ProducersOSOM
BodyServiceCreate String json
Headers"orderid"=orderid, "serviceSpecid"= specid
DescriptionCreates Service based an a Service Spec, Returns a Service object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_UPD_SERVICE
Namejms:queue:CATALOG.UPD.SERVICE
Typequeue
DestinationTMF API service
Producers
BodyServiceUpdate
Headers"serviceid" = serviceId, "propagateToSO" = true/false
Descriptionwill update a service by id and return the service instance. If propagateToSO=true then any service change will be handled by OSOM. This is needed to be controlled in order to avoid update loops
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_SERVICE_BY_ID
Namejms:queue:CATALOG.GET.SERVICE
Typequeue
DestinationTMF API service
ProducersOSOM
BodyString serviceID
Descriptionreturns a Service instance
+

---| Message | | +| ------------- |----------------| +|Alias | CATALOG_GET_SERVICE_BY_ORDERID | +|Name | jms:queue:CATALOG.GET.SERVICE_BY_ORDERID | +|Type | queue | +|Destination | TMF API service | +|Producers | | +|Body | String serviceID | +|Description | returns Service IDs of a specific order given then order id |

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_SERVICE_QUEUE_ITEMS_GET
Namejms:queue:CATALOG.SERVICEQUEUEITEMS.GET
Typequeue
DestinationTMF API service
ProducersOSOM
Body
Descriptionreturns a LIST OF Service Queue Items
---
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_SERVICE_QUEUE_ITEM_UPD
Namejms:queue:CATALOG.SERVICEQUEUEITEM.UPDATE
Typequeue
DestinationTMF API service
ProducersOSOM
BodyString SERVICEQUEUEITEM
Headers"itemid" = SERVICEQUEUEITEM id
Descriptionill update a service queue item by id and return the instance
---
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_SERVICE_QUEUE_ITEM_DELETE
Namejms:queue:CATALOG.SERVICEQUEUEITEM.DELETE
Typequeue
DestinationTMF API service
ProducersOSOM
Body
Headers"itemid" = SERVICEQUEUEITEM id
Descriptionill delete a service queue item by id
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_SERVICES_TO_TERMINATE
Namejms:queue:CATALOG.GET.SERVICETOTERMINATE
Typequeue
DestinationTMF API service
ProducersOSOM
Body
Headers
DescriptionGet a list of ACTIVE services with END DAte in the past
---
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_SERVICES_OF_PARTNERS
Namejms:queue:CATALOG.GET.SERVICESOFPARTNERS
Typequeue
DestinationTMF API service
ProducersOSOM
Body
Headers
DescriptionGet a list of ACTIVE services from the inventory of partners
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasNFV_CATALOG_GET_NSD_BY_ID
Namejms:queue:NFVCATALOG.GET.NSD_BY_ID
Typequeue
DestinationNFV Catalog service
ProducersTMF API, OSOM
BodyNSDid
DescriptionReturns a NetworkServiceDescriptor object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasNFV_CATALOG_DEPLOY_NSD_REQ
Namejms:queue:NFVCATALOG.DEPLOY.NSD_REQ
Typequeue
DestinationNFV Catalog service
ProducersOSOM
BodyDeploymentDescriptor as Json String
HeadersNSD id
DescriptionReturns a DeploymentDescriptor object as json string containing deployment info
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasNFV_CATALOG_UPD_DEPLOYMENT_BY_ID
Namejms:queue:NFVCATALOG.UPD.DEPLOYMENT_BY_ID
Typequeue
DestinationNFV Catalog service
ProducersOSOM
BodyDeploymentDescriptor as Json String
HeadersDeploymentDescriptor id
DescriptionUpdates and Returns a DeploymentDescriptor object as json string containing deployment info
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasGET_USER_BY_USERNAME
Namejms:queue:GET.USER_BY_USERNAME
Typequeue
DestinationNFV Catalog service (this is temproary for now)
ProducersTMF API
Bodyusername
Headers
DescriptionReturns a PortalUser object as json string containing user info
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasNFV_CATALOG_GET_DEPLOYMENT_BY_ID
Namejms:queue:NFVCATALOG.GET.DEPLOYMENT_BY_ID
Typequeue
DestinationNFV Catalog service
ProducersOSOM
BodyDeployment ID
DescriptionReturns a DeploymentDescriptor object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_EXTERNAL_SERVICE_PARTNERS
Namejms:queue:CATALOG.GET.EXTERNALSERVICEPARTNERS
Typequeue
DestinationTMF API service
ProducersOSOM
Body
Headers
DescriptionAs a String Json ArrayList of Organizaton objects containing the characteristic name EXTERNAL_TMFAPI
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_UPD_EXTERNAL_SERVICESPEC
Namejms:queue:CATALOG.UPD.EXTERNAL_SERVICESPEC
Typequeue
DestinationTMF API service
ProducersOSOM or maybe used by others that would like to update a Service Spec
BodyA serviceSpecification as json string
Headersservicespecification id, orgid id
DescriptionUpdates (or inserts if does not exist in catalog) an external service specification)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasNFV_CATALOG_NSACTIONS_SCALE
Namejms:queue:NSACTIONS.SCALE
Typequeue
DestinationTMF API service
ProducersOSOM or maybe used by others that would like scale a NS
BodyA ScaleDescriptor as json string
Headersnone
Descriptionperforms a scale
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasNFV_CATALOG_NS_LCMCHANGED
NameNFV_CATALOG_NS_LCMCHANGED
Typetopic
Destinationany
ProducersMANO client
BodyA json string
Headersnone
DescriptionA NFV_CATALOG_NS_LCMCHANGED message is published when LCM of a running NS is changed
+
+

ALARMS

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasALARMS_ADD_ALARM
Namejms:queue:ALARMS.ADD.ALARM
Typequeue
Publishers
ConsumersTMF API
BodyAlarmCreate
Headers
DescriptionAdd an alarm
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasALARMS_UPDATE_ALARM
Namejms:queue:ALARMS.UPDATE.ALARM
Typequeue
Publishers
ConsumersTMF API
BodyAlarmUpdate
Headersalarmid = alarm id, body (AlarmUpdate object)
DescriptionUpdate an alarm
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasALARMS_GET_ALARM
Namejms:queue:ALARMS.GET.ALARM
Typequeue
Publishers
ConsumersTMF API
Body
Headersalarmid = alarm id
Descriptionget an alarm
+
+

EVENT TOPICS IN Message Bus

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_SERVICE_CREATE
Namejms:topic:EVENT.SERVICE.CREATE
Typetopic
PublishersTMF API
Consumers-
BodyNotification object
Headers"eventid"=eventid, "objId"= objId
Descriptionxx
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_SERVICE_STATE_CHANGED
Namejms:topic:EVENT.SERVICE.STATECHANGED
Typetopic
PublishersTMF API
Consumers-
BodyNotification object
Headers"eventid"=eventid, "objId"= objId
Descriptionxx
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_SERVICE_DELETE
Namejms:topic:EVENT.SERVICE.DELETE
Typetopic
PublishersTMF API
Consumers-
BodyNotification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc
Headers"eventid"=eventid, "objId"= objId
Descriptionxx
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_SERVICE_ATTRIBUTE_VALUE_CHANGED
Namejms:topic:EVENT.SERVICE.ATTRCHANGED
Typetopic
PublishersTMF API
Consumers-
BodyNotification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc
Headers"eventid"=eventid, "objId"= objId
Descriptionxx
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_SERVICE_ORDER_CREATE
Namejms:topic:EVENT.SERVICEORDER.CREATE
Typetopic
PublishersTMF API
ConsumersBUGZILLA Service, CentralLog Service
BodyNotification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc
Headers"eventid"=eventid, "objId"= objId
DescriptionThe Event of the Notification object contains the ServiceOrder object. Bugzilla service for example uses this to create a new issue
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_SERVICE_ORDER_STATE_CHANGED
Namejms:topic:EVENT.SERVICEORDER.STATECHANGED
Typetopic
PublishersTMF API
ConsumersBUGZILLA Service, CentralLog Service
BodyNotification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc
Headers"eventid"=eventid, "objId"= objId
DescriptionThe Event of the Notification object contains the ServiceOrder object. Bugzilla service for example uses this to update an issue
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_SERVICE_ORDER_DELETE
Namejms:topic:EVENT.SERVICEORDER.DELETE
Typetopic
PublishersTMF API
ConsumersBUGZILLA Service, CentralLog Service
BodyNotification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc
Headers"eventid"=eventid, "objId"= objId
DescriptionThe Event of the Notification object contains the ServiceOrder object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_SERVICE_ORDER_ATTRIBUTE_VALUE_CHANGED
Namejms:topic:EVENT.SERVICEORDER.ATTRCHANGED
Typetopic
PublishersTMF API
ConsumersBUGZILLA Service, CentralLog Service
BodyNotification object. Can be one of ServiceOrderCreateNotification, ServiceOrderStateChangeNotification, ServiceOrderAttributeValueChangeNotification, ServiceOrderDeleteNotification, etc
Headers"eventid"=eventid, "objId"= objId
DescriptionThe Event of the Notification object contains the ServiceOrder object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_ALARM_CREATE
Namejms:topic:EVENT.ALARM.CREATE
Typetopic
PublishersTMF API
ConsumersOAS, BUGZILLA Service, CentralLog Service
BodyAlarmCreateEvent
Headers
DescriptionThe Event contains the Alarm object in payload
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_ADD_RESOURCE
Namejms:queue:CATALOG.ADD.RESOURCE
Typetopic
PublishersTMF API
Consumersany
BodyResourceCreate
Headers
DescriptionThe Body contains the ResourceCreate object to add
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_UPD_RESOURCE
Namejms:queue:CATALOG.UPD.RESOURCE
Typetopic
PublishersTMF API
Consumersany
BodyResourceUpdate
Headersresourceid , propagateToSO
DescriptionThe Body contains the ResourceCreate object to update
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_RESOURCE_BY_ID
Namejms:queue:CATALOG.GET.RESOURCE
Typetopic
PublishersTMF API
Consumersany
Bodyresourceid
Headers
DescriptionThe Body contains the ResourceCreate object to update
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_RESOURCES_OF_PARTNERS
Namejms:queue:CATALOG.GET.SERVICESOFPARTNERS
Typetopic
PublishersTMF API
Consumersany
Bodynone
Headersnone
Descriptionretrieve all active services of partners
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_ADD_RESOURCESPEC
Namejms:queue:CATALOG.ADD.RESOURCESPEC
Typetopic
PublishersTMF API
Consumersany
BodyResourceSpecificationCreate
Headers
DescriptionThe Body contains the ResourceSpecificationCreate object to add
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_UPD_RESOURCESPEC
Namejms:queue:CATALOG.UPD.RESOURCESPEC
Typetopic
PublishersTMF API
Consumersany
BodyResourceSpecificationUpdate
HeadersresourceSpecId
DescriptionThe Body contains the ResourceSpecificationCreate object to update
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_RESOURCESPEC_BY_ID
Namejms:queue:CATALOG.GET.RESOURCESPEC_BY_ID
Typetopic
PublishersTMF API
Consumersany
BodyresourceSpecid
Headers
DescriptionThe Body contains the object id to find
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_UPDADD_RESOURCESPEC
Namejms:queue:CATALOG.UPDADD.RESOURCESPEC
Typetopic
PublishersTMF API
Consumersany
Bodyresourceid
Headers
DescriptionThe Body contains the ResourceSpecificationCreate object to update or create if not exist
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_RESOURCE_CREATE
Namejms:topic:EVENT.RESOURCE.CREATE
Typetopic
PublishersTMF API
ConsumersBUGZILLA Service, CentralLog Service, other
BodyNotification object.
Headers"eventid"=eventid, "objId"= objId
DescriptionThe Event of the Notification object contains the Resource object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_RESOURCE_STATE_CHANGED
Namejms:topic:EVENT.RESOURCE.STATECHANGED
Typetopic
PublishersTMF API
ConsumersBUGZILLA Service, CentralLog Service, other
BodyNotification object.
Headers"eventid"=eventid, "objId"= objId
DescriptionThe Event of the Notification object contains the Resource object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_RESOURCE_DELETE
Namejms:topic:EVENT.SERVICE.RESOURCE
Typetopic
PublishersTMF API
ConsumersBUGZILLA Service, CentralLog Service, other
BodyNotification object.
Headers"eventid"=eventid, "objId"= objId
DescriptionThe Event of the Notification object contains the Resource object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasEVENT_RESOURCE_ATTRIBUTE_VALUE_CHANGED
Namejms:topic:EVENT.RESOURCE.ATTRCHANGED
Typetopic
PublishersTMF API
ConsumersBUGZILLA Service, CentralLog Service, other
BodyNotification object.
Headers"eventid"=eventid, "objId"= objId
DescriptionThe Event of the Notification object contains the Resource object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_LCMRULE_BY_ID
Namejms:queue:CATALOG.GET.LCMRULE
Typetopic
PublishersTMF API
Consumersany
Bodylcmid
Headers
DescriptionThe Body contains the LCMRuleSpec object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_LCMRULES_BY_SPECID_PHASE
Namejms:queue:CATALOG.GET.LCMRULES_BY_SPECID_PHASE
Typetopic
PublishersTMF API
Consumersany
Body
Headersheader.servicespecid, header.phasename
DescriptionThe Body contains the LCMRuleSpec objects of the specific Service Spec and the specific phase
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_SERVICETESTSPEC_BY_ID
Namejms:queue:CATALOG.GET.SERVICETESTSPEC_BY_ID
Typequeue
DestinationTMF API service
ProducersOSOM
Bodyspecid
DescriptionReturn a ServiceTestSpecification
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_ADD_SERVICETEST
Namejms:queue:CATALOG.ADD.SERVICETEST
Typequeue
DestinationTMF API service
ProducersOSOM
BodyServiceTestCreate String json
Headers"orderid"=orderid, "serviceTestSpecid"= specid
DescriptionCreates Service Test based an a Service Test Spec, Returns a ServiceTest object
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_UPD_SERVICETEST
Namejms:queue:CATALOG.UPD.SERVICETEST
Typequeue
DestinationTMF API service
Producers
BodyServiceTestUpdate
Headers"serviceid" = serviceId, "propagateToSO" = true/false
Descriptionwill update a service test by id and return the service instance. If propagateToSO=true then any service change will be handled by OSOM. This is needed to be controlled in order to avoid update loops
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCATALOG_GET_SERVICETEST_BY_ID
Namejms:queue:CATALOG.GET.SERVICETEST
Typequeue
DestinationTMF API service
ProducersOSOM
BodyString serviceID
Descriptionreturns a Service TEST instance
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCRD_DEPLOY_CR_REQ
Namejms:queue:CRD.DEPLOY.CR_REQ
Typequeue
DestinationCRD service
ProducersOSOM
BodyCR spec as String
Headersrelated service id
DescriptionReturns a String object containing deployment info
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCRD_PATCH_CR_REQ
Namejms:queue:CRD.PATCH.CR_REQ
Typequeue
DestinationCRD service
ProducersOSOM
BodyCR as String
Headersrelated service id
DescriptionReturns a String object containing PATCH info
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Message
AliasCRD_DELETE_CR_REQ
Namejms:queue:CRD.DELETE.CR_REQ
Typequeue
DestinationCRD service
ProducersOSOM
BodyCR as String
Headersrelated service id
DescriptionReturns a String object containing deletion info
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/nfvapi/index.html b/public/develop/architecture/nfvapi/index.html new file mode 100644 index 0000000000000000000000000000000000000000..a04358aa158c49d12ee59a0f59893700707a8390 --- /dev/null +++ b/public/develop/architecture/nfvapi/index.html @@ -0,0 +1,1292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NFV API - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

API interaction

+

OAuth token

+

See oauth

+

Request a protected API resource

+

Example: Get all vxfs (check the Authorization:Bearer to be correct)

+
curl -H "Authorization:Bearer eybGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5hcGkiLCJhZG1pbiIsInJlYWQiLCJ3cml0ZSJdLCJvcmdhbml6YXRpb24iOiJteW9yZ2FuaXp0aW9uIiwiZXhwIjoxNTcxOTI0MjU2LCJhdXRob3JpdGllcyI6WyJST0xFX01FTlRPUiIsIlJPTEVfQURNSU4iXSwianRpIjoiNzNkZmIxODEtNTMwOS00MmExLThkOWUtOGM3YmQ0YTE1YmU0IiwiY2xpZW50X2lkIjoib3NhcGlXZWJDbGllbnRJZE91dCJ9.Pj_hxnyMGhFhN8avU_DiAw1-LlcaIz5Hp9HNqalw-X4" http://localhost:13000/osapi/admin/vxfs
+
+

Example response:

+
[
+  {
+    "id": 1,
+    "owner": {
+      "id": 1,
+      "organization": "ee",
+      "name": "Portal Administrator",
+      "email": "",
+      "username": "admin",
+      "createdAt": null
+    },
+    "uuid": "a954daf2-16da-4b7e-ae42-4825936d453c",
+    "name": "cirros_vnfd",
+    "iconsrc": "/osapi/images/a954daf2-16da-4b7e-ae42-4825936d453c/cirros-64.png",
+    "shortDescription": "cirros_vnfd",
+    "longDescription": "Simple VNF example with a cirros",
+    "version": "1.0",
+    "packageLocation": "/osapi/packages/a954daf2-16da-4b7e-ae42-4825936d453c/cirros_vnf.tar.gz",
+    "dateCreated": 1568971426000,
+    "dateUpdated": 1568981107000,
+    "categories": [
+      {
+        "id": 3,
+        "name": "Service",
+        "productsCount": 1,
+        "appscount": 0,
+        "vxFscount": 1
+      },
+      {
+        "id": 2,
+        "name": "Networking",
+        "productsCount": 1,
+        "appscount": 0,
+        "vxFscount": 1
+      }
+    ],
+    "extensions": [],
+    "validationJobs": [],
+    "screenshots": "",
+    "vendor": "OSM",
+    "published": false,
+    "termsOfUse": null,
+    "descriptor": "vnfd-catalog:\n    vnfd:\n    -   connection-point:\n        -   name: eth0\n            type: VPORT\n        description: Simple VNF example with a cirros\n        id: cirros_vnfd\n        logo: cirros-64.png\n        mgmt-interface:\n            cp: eth0\n        name: cirros_vnfd\n        short-name: cirros_vnfd\n        vdu:\n        -   count: 1\n            description: cirros_vnfd-VM\n            id: cirros_vnfd-VM\n            image: cirros034\n            interface:\n            -   external-connection-point-ref: eth0\n                name: eth0\n                position: '1'\n                type: EXTERNAL\n                virtual-interface:\n                    bandwidth: '0'\n                    type: VIRTIO\n                    vpci: 0000:00:0a.0\n            name: cirros_vnfd-VM\n            vm-flavor:\n                memory-mb: 512\n                storage-gb: 1\n                vcpu-count: 1\n        vendor: OSM\n        version: '1.0'\n",
+    "descriptorHTML": "<h3>cirros_vnfd</h3><br><b>Vendor: </b>OSM<br><b>Version: </b>1.0<br><b>Description: </b>Simple VNF example with a cirros<br><b>VM Count: </b>1<br><b>vCPU Count: </b>1<br><b>Memory: </b>512 MB<br><b>Storage: </b>1 GB<br>",
+    "certified": false,
+    "certifiedBy": null,
+    "validationStatus": "UNDER_REVIEW",
+    "packagingFormat": "OSMvFIVE",
+    "supportedMANOPlatforms": [
+      {
+        "id": 1,
+        "name": "osm fivee",
+        "version": "osm fivee",
+        "description": "osm five"
+      }
+    ],
+    "vxfOnBoardedDescriptors": [],
+    "vfimagesVDU": [
+      {
+        "id": 1,
+        "name": "cirros034",
+        "uuid": "d4549610-8abd-42ad-97f4-0a64e1c93977",
+        "shortDescription": "Automatically created during vxf cirros_vnfd submission. Owner must update.",
+        "packageLocation": null,
+        "publicURL": null,
+        "dateCreated": 1568971426000,
+        "dateUpdated": null,
+        "refVxFs": [
+          {
+            "id": 1,
+            "name": "cirros_vnfd"
+          }
+        ],
+        "owner": {
+          "id": 1,
+          "organization": "ee",
+          "name": "Portal Administrator",
+          "email": "",
+          "username": "admin",
+          "active": true,
+          "currentSessionID": null,
+          "apikey": "e41c1cc4-aa56-4b7e-9f4d-64589549d768",
+          "createdAt": 1568711859000,
+          "roles": [
+            "ADMIN",
+            "MENTOR"
+          ]
+        },
+        "published": false,
+        "termsOfUse": null,
+        "deployedInfrastructures": []
+      }
+    ]
+  },
+  {
+    "id": 2,
+    "owner": {
+      "id": 1,
+      "organization": "ee",
+      "name": "Portal Administrator",
+      "email": "",
+      "username": "admin",
+      "createdAt": null
+    },
+    "uuid": "4ab80095-a63e-4fe7-8598-e1c7e880706e",
+    "name": "cirros_sriov_vnfd",
+    "iconsrc": null,
+    "shortDescription": "cirros_sriov_vnf",
+    "longDescription": "Simple VNF example with a cirros SRIOV interface",
+    "version": "1.0",
+    "packageLocation": "/osapi/packages/4ab80095-a63e-4fe7-8598-e1c7e880706e/cirros_sriov.tar.gz",
+    "dateCreated": 1568971740000,
+    "dateUpdated": 1568981100000,
+    "categories": [
+      {
+        "id": 4,
+        "name": "tyu",
+        "productsCount": 1,
+        "appscount": 0,
+        "vxFscount": 1
+      },
+      {
+        "id": 5,
+        "name": "tyi",
+        "productsCount": 1,
+        "appscount": 0,
+        "vxFscount": 1
+      }
+    ],
+    "extensions": [],
+    "validationJobs": [],
+    "screenshots": "",
+    "vendor": "OSM",
+    "published": false,
+    "termsOfUse": null,
+    "descriptor": "vnfd:vnfd-catalog:\n  vnfd:\n  - connection-point:\n    - name: eth0\n      type: VPORT\n    - name: eth1\n      type: VPORT\n    description: Simple VNF example with a cirros SRIOV interface\n    id: cirros_sriov_vnfd\n    logo: cirros-64.png\n    mgmt-interface:\n      cp: eth0\n    name: cirros_sriov_vnf\n    short-name: cirros_sriov_vnf\n    vdu:\n    - count: 1\n      description: cirros_sriov_vnfd-VM\n      guest-epa:\n        cpu-pinning-policy: DEDICATED\n        cpu-thread-pinning-policy: PREFER\n        mempage-size: SMALL\n        numa-node-policy:\n          mem-policy: STRICT\n          node:\n          - id: '1'\n          node-cnt: '1'\n      id: cirros_sriov_vnfd-VM\n      image: cirros-0.3.6-x86_64\n      interface:\n      - external-connection-point-ref: eth0\n        name: eth0\n        position: '1'\n        type: EXTERNAL\n        virtual-interface:\n          bandwidth: '0'\n          type: VIRTIO\n          vpci: 0000:00:0a.0\n      - external-connection-point-ref: eth1\n        name: eth1\n        position: '2'\n        type: EXTERNAL\n        virtual-interface:\n          type: SR-IOV\n      name: cirros_sriov_vnfd-VM\n      vm-flavor:\n        memory-mb: 4096\n        storage-gb: 10\n        vcpu-count: 4\n    vendor: OSM\n    version: '1.0'\n",
+    "descriptorHTML": "<h3>cirros_sriov_vnf</h3><br><b>Vendor: </b>OSM<br><b>Version: </b>1.0<br><b>Description: </b>Simple VNF example with a cirros SRIOV interface<br><b>VM Count: </b>1<br><b>vCPU Count: </b>1<br><b>Memory: </b>4096 MB<br><b>Storage: </b>10 GB<br>",
+    "certified": false,
+    "certifiedBy": null,
+    "validationStatus": "UNDER_REVIEW",
+    "packagingFormat": "OSMvFIVE",
+    "supportedMANOPlatforms": [
+      {
+        "id": 1,
+        "name": "osm fivee",
+        "version": "osm fivee",
+        "description": "osm five"
+      }
+    ],
+    "vxfOnBoardedDescriptors": [],
+    "vfimagesVDU": [
+      {
+        "id": 2,
+        "name": "cirros-0.3.6-x86_64",
+        "uuid": "be121176-1d62-4a1b-a3c1-7dce2e069d22",
+        "shortDescription": "Automatically created during vxf cirros_sriov_vnfd submission. Owner must update.",
+        "packageLocation": null,
+        "publicURL": null,
+        "dateCreated": 1568971740000,
+        "dateUpdated": null,
+        "refVxFs": [
+          {
+            "id": 2,
+            "name": "cirros_sriov_vnfd"
+          }
+        ],
+        "owner": {
+          "id": 1,
+          "organization": "ee",
+          "name": "Portal Administrator",
+          "email": "",
+          "username": "admin",
+          "active": true,
+          "currentSessionID": null,
+          "apikey": "e41c1cc4-aa56-4b7e-9f4d-64589549d768",
+          "createdAt": 1568711859000,
+          "roles": [
+            "ROLE_ADMIN",
+            "ROLE_MENTOR"
+          ]
+        },
+        "published": false,
+        "termsOfUse": null,
+        "deployedInfrastructures": []
+      }
+    ]
+  }
+]
+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/nfvweb/index.html b/public/develop/architecture/nfvweb/index.html new file mode 100644 index 0000000000000000000000000000000000000000..e784b8246c3dac5138897d7fe48fc9bae0229903 --- /dev/null +++ b/public/develop/architecture/nfvweb/index.html @@ -0,0 +1,1029 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NFV WEB - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Openslice NFV Web Portal

+

The NFV Web Portal offers a domain-specific UI to manage NFV Services.

+

Indicatively, the portal can be used to: +- Register new a new MANO provider (e.g. OSM) +- Synchronize the onboarded VNF/NS packages, and the VIMs of the registered MANO provider +- Onboard/Delete VNF/NS packages on specific MANO provider +- Deploy a NS to a target MANO provider

+

More information can be found at NFV Services.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/oauth/index.html b/public/develop/architecture/oauth/index.html new file mode 100644 index 0000000000000000000000000000000000000000..f09de07009b1c3d9c5370e0ce1287d20eb75e32a --- /dev/null +++ b/public/develop/architecture/oauth/index.html @@ -0,0 +1,1100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Authentication - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Authentication Server

+

Authentication is based on oAuth2. Our authentication service is a Keycloak server which is deployed with Openslice deployment

+

API users needs to authenticate. +All APIs (except grant token request) must include Bearer token in request Authorization header.

+

OAuth token

+

Get first an oauth token, using your username and password.

+
curl -X POST http://portal.openslice.io/auth/realms/openslice/protocol/openid-connect/token -H 'Content-Type: application/x-www-form-urlencoded' -d 'username=demouser' -d 'password=demouser' -d 'grant_type=password' -d 'client_id=osapiWebClientId' 
+
+

response:

+
                                                       {"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJHZFRjQnpxczg2VW10NTRVZV8ybTJyWHJkV3dzaWdSZE9EUldMYm1memNvIn0.eyJleHAiOjE1ODgyNDA1NzAsImlhdCI6MTU4ODI0MDI3MCwianRpIjoiOGI2ZTU0NWUtNDIyYy00NzFiLWEwN2UtYTUzYzY1NDQ0MzZmIiwiaXNzIjoiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8vYXV0aC9yZWFsbXMvb3BlbnNsaWNlIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImExYTI3NjVhLWVjODMtNDQ1Ni1iN2IyLTIwNzMxOTg2ZTAzNSIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9zYXBpV2ViQ2xpZW50SWQiLCJzZXNzaW9uX3N0YXRlIjoiMzM1MGY0OTMtNjYyNy00MzczLTg1NDQtZGVmZDI3YWQzYzc0IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjEzMDgyIiwiaHR0cDovL2xvY2FsaG9zdCIsImh0dHA6Ly9vcGVuc2xpY2UuaW8iLCJodHRwOi8vbG9jYWxob3N0OjEzMDAwIiwiaHR0cDovL2xvY2FsaG9zdDo0MjAwIiwiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8iXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIk5GVl9ERVZFTE9QRVIiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiRVhQRVJJTUVOVEVSIiwiVVNFUiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJkZW1vdXNlciJ9.TnzzpRLMD94UTKpT5_wkr1h4_3KUQmr4TGvFLpJ7cZx-Klrv8tB_eRkWnPqqzCAM9G21a1qXboL8MLMW8ECzG7HYKpobKOGr7LSczMOTdA2ZDyBCRUSOdW77pchu54tJ0ITEkFaDwSKMKKt04V_Sy4U-eIndj0XzzRlkDolWDnK4Z2lRaXAI6fMwOKx1Toa6RfOcemxtzl3pdtjPx92zo6MaKlbIqHK82lxdK0k8aQQaT6TmIrXbZKV2dU_1d3O0q0dVUEZJ_1kzwqOFkmxr9w0EnndC6ccYJlDAr_-GgUhhhNOn5v6tjYLUQdj5e4KEAsxIPzaCreK4un7mEAPmDw","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIwZjUxMDk5Yy0wNTIzLTRjNGQtODM0Zi1iNDc0YzBjOTA1MzkifQ.eyJleHAiOjE1ODgyNDIwNzAsImlhdCI6MTU4ODI0MDI3MCwianRpIjoiZmViOTg5NWEtOTY5ZS00MzIzLWJjY2QtZTY2YzQ0NGE1MzJlIiwiaXNzIjoiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8vYXV0aC9yZWFsbXMvb3BlbnNsaWNlIiwiYXVkIjoiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8vYXV0aC9yZWFsbXMvb3BlbnNsaWNlIiwic3ViIjoiYTFhMjc2NWEtZWM4My00NDU2LWI3YjItMjA3MzE5ODZlMDM1IiwidHlwIjoiUmVmcmVzaCIsImF6cCI6Im9zYXBpV2ViQ2xpZW50SWQiLCJzZXNzaW9uX3N0YXRlIjoiMzM1MGY0OTMtNjYyNy00MzczLTg1NDQtZGVmZDI3YWQzYzc0Iiwic2NvcGUiOiJwcm9maWxlIGVtYWlsIn0.cDTx9BE1Df8EfGYm3VLr_MNFeymxZtJhMtlK7PVbIuk","token_type":"bearer","not-before-policy":1586797346,"session_state":"3350f493-6627-4373-8544-defd27ad3c74","scope":"profile email"}
+
+
+

The access_token will be used next as a Bearer.

+
curl http://portal.openslice.io/tmf-api/serviceCatalogManagement/v4/serviceCatalog -H 'Authorization: Bearer yJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJHZFRjQnpxczg2VW10NTRVZV8ybTJyWHJkV3dzaWdSZE9EUldMYm1memNvIn0.eyJleHAiOjE1ODgyNDA1MjQsImlhdCI6MTU4ODI0MDIyNCwianRpIjoiYjg0NGYxZDAtMzk3Mi00YTMyLThiMWEtZDAxMDY3OGZjMTQ4IiwiaXNzIjoiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8vYXV0aC9yZWFsbXMvb3BlbnNsaWNlIiwic3ViIjoiYTFhMjc2NWEtZWM4My00NDU2LWI3YjItMjA3MzE5ODZlMDM1IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWRtaW4tY2xpIiwic2Vzc2lvbl9zdGF0ZSI6ImFmMmMzZmY1LTE4YWQtNDFkNC1hYTAyLTFlMGJkNzNmOTM5MSIsImFjciI6IjEiLCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGVtb3VzZXIifQ.SMtgV1E44_K_MQumGXZtWsLGVhYNaoM8Pk-DiFIZtUP4Zu-ervOsxHVQMX1frgVERR4jJidBcSshy9TnJ3UjF4l33WujHltbs-1UPy-gaIufVuEpl8RmbjOti3Up70vLfLXbzb6kN6WaahgobWXlbJsSXXwaBPQP6vSX5KigCa8TmzXcuqom14lOrlU-RB2zQTlJ30p7d9ag-a7o3I5m9GZWLJCZW2UYMl1JkskTHKgilA8HFQY4C9DYwWu8YDMyzqQSNumrTlURalBFidFbZvb1kp4dAyct8TysSWSbxxiwaL2RX1PWUqk-5Fpc1Q6BnBC8muMheiukFuoSkuADAg'^C
+ubuntu@portal:~$ curl http://portal.openslice.io/tmf-api/serviceCatalogManagement/v4/serviceCatalog -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJHZFRjQnpxczg2VW10NTRVZV8ybTJyWHJkV3dzaWdSZE9EUldMYm1memNvIn0.eyJleHAiOjE1ODgyNDA1NzAsImlhdCI6MTU4ODI0MDI3MCwianRpIjoiOGI2ZTU0NWUtNDIyYy00NzFiLWEwN2UtYTUzYzY1NDQ0MzZmIiwiaXNzIjoiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8vYXV0aC9yZWFsbXMvb3BlbnNsaWNlIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImExYTI3NjVhLWVjODMtNDQ1Ni1iN2IyLTIwNzMxOTg2ZTAzNSIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9zYXBpV2ViQ2xpZW50SWQiLCJzZXNzaW9uX3N0YXRlIjoiMzM1MGY0OTMtNjYyNy00MzczLTg1NDQtZGVmZDI3YWQzYzc0IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjEzMDgyIiwiaHR0cDovL2xvY2FsaG9zdCIsImh0dHA6Ly9vcGVuc2xpY2UuaW8iLCJodHRwOi8vbG9jYWxob3N0OjEzMDAwIiwiaHR0cDovL2xvY2FsaG9zdDo0MjAwIiwiaHR0cDovL3BvcnRhbC5vcGVuc2xpY2UuaW8iXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIk5GVl9ERVZFTE9QRVIiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiRVhQRVJJTUVOVEVSIiwiVVNFUiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJkZW1vdXNlciJ9.TnzzpRLMD94UTKpT5_wkr1h4_3KUQmr4TGvFLpJ7cZx-Klrv8tB_eRkWnPqqzCAM9G21a1qXboL8MLMW8ECzG7HYKpobKOGr7LSczMOTdA2ZDyBCRUSOdW77pchu54tJ0ITEkFaDwSKMKKt04V_Sy4U-eIndj0XzzRlkDolWDnK4Z2lRaXAI6fMwOKx1Toa6RfOcemxtzl3pdtjPx92zo6MaKlbIqHK82lxdK0k8aQQaT6TmIrXbZKV2dU_1d3O0q0dVUEZJ_1kzwqOFkmxr9w0EnndC6ccYJlDAr_-GgUhhhNOn5v6tjYLUQdj5e4KEAsxIPzaCreK4un7mEAPmDw'
+
+Response:
+
+
+[{"uuid":"9e186cd5-b2b2-4a06-b1d6-895720193bc9","lastUpdate":"2020-03-11T23:19:05Z","@baseType":"BaseEntity","@schemaLocation":null,"@type":"ServiceCatalog","href":null,"name":"Example Facility Services","description":"Example Facility Services","lifecycleStatus":"Active","version":"1.0","validFor":{"endDateTime":"2039-11-20T23:07:21Z","startDateTime":"2019-11-20T23:07:21Z"},"relatedParty":null,"id":"9e186cd5-b2b2-4a06-b1d6-895720193bc9","category":[{"@baseType":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","@schemaLocation":null,"@type":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","href":null,"name":"Generic Services","@referredType":null,"id":"98b9adf1-a1d6-4165-855f-153ddc2131b1"},{"@baseType":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","@schemaLocation":null,"@type":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","href":null,"name":"External","@referredType":null,"id":"08ffdb3c-6237-45d0-9f3a-d43b5fc5f0b6"},{"@baseType":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","@schemaLocation":null,"@type":"org.etsi.osl.tmf.scm633.model.ServiceCategoryRef","href":null,"name":"eMBB","@referredType":null,"id":"ef2c90dd-b65e-4a9f-a9c3-427c9fb0219b"}]}]
+
+
+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/osom/index.html b/public/develop/architecture/osom/index.html new file mode 100644 index 0000000000000000000000000000000000000000..b302dfb5575c47bf381a06d879a3eb412cee759e --- /dev/null +++ b/public/develop/architecture/osom/index.html @@ -0,0 +1,1297 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OSOM - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + +

Openslice Service Orchestration and Order Management - OSOM

+

OSOM is a service responsible for:

+
    +
  • Service Order Management (SOM)
  • +
  • Service Orchestration (SO)
  • +
+

It uses open source Flowable Business process engine (https://www.flowable.org) .

+

A Service Order follows the states as defined in TMF641 specification:

+

Service Order state

+

Initial state

+

When a new order is created, it goes into the Initial state. It is stored in the repository and triggers an Event.

+

Initial state

+

Administrators are notified usually from the Ticketing System of a new order. They login to Openslice and change the State of the order either to ACKNOWLEDGED or REJECTED. If ACKNOWLEDGED they can Propose a startDate, add Notes, and add any additional service items

+

Order scheduler

+

A process checks every 1 minute for ACKNOWLEDGED orders.

+

Order scheduler BPM

+

Order scheduler

+

It retrieves all orders that are in ACKNOWLEDGED state and if the start date is in time it will initialize the process by settingn the order in IN_PROGRESS state. Finally the Start Order Process will start.

+

Start order process

+

This process for now is a draft simple prototype to make a simple orchestration via NFVO. Here the actual Services (TMF638/640 model) are created and attached to Service Order and Service Inventory.

+

Start Order BPM

+

Start Order interactions

+

We expect here to check which tasks can be orchestrated by NFVO and which by human. We create the equivalent Services(TMF638/640 model) for this order.

+
    +
  1. During check it should decide to create Service(s) for this service order O1 and send it to ServiceInventory
  2. +
  3. The Services(TMF638 model) are assigned to the Order O1 In supportService List
  4. +
  5. Each OrderItem OI1 is related to one serviceSpecification
  6. +
  7. Each ServiceSpecification has also related serviceSpecRelationships
  8. +
  9. So if we receive an order O1 for a ServiceSpec A which relates to (a bundle of) 3 specs(2 CFS, 1 RFS) we do the following:
      +
    1. Create a Service S_A(TMF638 model) for ServiceSpec A for Order O1
    2. +
    3. We create also 3 Services S_C1, S_C2 and S_R1 equivalent to the serviceSpecRelationships (2 CFS, 1 RFS)
    4. +
    5. At this point the order will have 1 Local Service Orchestration Process(S_A), 2 supportingServices refs(S_C1, S_C2) and 1 supportingResource(S_R1)
    6. +
    7. The 3 supportingServices and 1 supportingResource correspond to total 4 Services in ServiceInventory
    8. +
    9. Service S_A will have:
        +
      1. startMode 1: Automatically by the managed environment
      2. +
      3. State: RESERVED and the Lifecycle will be handled by OSOM
      4. +
      +
    10. +
    11. Services S_C1 and S_C2 we decide that cannot be orchestrated then they have
        +
      1. startMode: 3: Manually by the Provider of the Service
      2. +
      3. State: RESERVED and the Lifecycle will be handled by OSOM
      4. +
      5. If the CFS is a bundle spec it is further recursively orchestrated
      6. +
      +
    12. +
    13. Service S_R1 will have
        +
      1. startMode 1: Automatically by the managed environment.
      2. +
      3. State: RESERVED
      4. +
      5. IF The Service has the characteristic CharacteristicByName( "NSDID") it will be further processed by the NFVO
      6. +
      +
    14. +
    +
  10. +
+

There will be two instances of task "User Task Manual Complete Service" each for Services S_C1 and S_C2. The task is Transient for now. It displays only the services that are not automated! +Here is a flow for future:

+
    +
  1. We wait here for human decision.
  2. +
  3. From API we get a result: + a. If set to ACTIVE/TERMINATED then we complete the task + b. In any other state we stay in this task until it is resolved as in step a + c. The Status of ORDER O1 is also updated to PARTIAL
  4. +
+

There will be an instance of NFVODeploymentRequest process each for Service S_R1. (see later)

+
    +
  1. This process is related with the NFVO orchestration
  2. +
  3. It will send a msg to NFVO(s?) for a specific deployment request
  4. +
+

All services in "Order Complete" are in a status:

+
    +
  1. Depending on the result the service S_A is either ACTIVE or INACTIVE or TERMINATED
  2. +
  3. The Status of ORDER O1 is also updated to COMPLETED or PARTIAL (in case we have some services running) or FAILED (in cases we have errors)
  4. +
+

A Service follows the states as defined in TMF638 Service Inventory specification:

+

Service state

+

NFVODeploymentRequest process

+

NFVODeploymentRequest BPM

+

This process is related with the NFVO orchestration +It will send a msg to NFVO(s?) for a specific deployment request +Then it checks the deployment status. It will wait 30 secs each time until the deployment is running (or failed)

+

Check In Progress orders process

+

Every 1 minute the "Check In Progress Orders" process is executed checking if a supported Service changed state (i.e. to ACTIVE) then the whole Order will change state (e.g. go to COMPLETED)

+

Check In Progress BPM

+

External Service Provider Deployment Request process

+

External Service Provider Deployment Request process BPM

+

This process contains tasks for submitting order requests to external partners. +- Submit Order To External Service Provider Task: This task creates automatically a Service Order request to a 3rd party provider SO that hosts the Service Specification +- Check external service order fulfillment task: This task Check external partner for Service creations and updates our local inventory of services the service characteristics of remote Service Inventory

+

Fetch Partner Services Process

+

Fetch Partner Services Process BPM

+

Every 2 minutes the "fetchPartnerServicesProcess" process is executed checking remote Partner Organizations for changes in the published catalogues. +The Fetch and Update External Partner Services Task is executed in paralle l for each Partner Organization

+

Local Service Orchestration Process

+

Local Service Orchestration Process BPM

+

This process handles automatically services that need to be further orchestrated or processed by OSOM. For example, for a CFS Bundled service we create such automated service instances that just aggregate the underlying services.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/tmfapi/index.html b/public/develop/architecture/tmfapi/index.html new file mode 100644 index 0000000000000000000000000000000000000000..28b12120825b44fbd1a4a6f7a5ad5148d0f23f98 --- /dev/null +++ b/public/develop/architecture/tmfapi/index.html @@ -0,0 +1,1177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TMF API - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

TMF OpenAPI specification

+

PLease check the complete specification here.

+

API interaction

+

OAuth token

+

See oauth

+

Request a protected API resource

+

Example: Get all Service Catalogs (check the Authorization:Bearer to be correct)

+
curl -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbIm9wZW5hcGkiLCJhZG1pbiIsInJlYWQiLCJ3cml0ZSJdLCJvcmdhbml6YXRpb24iOiJteW9yZ2FuaXp0aW9uIiwiZXhwIjoxNTc4NTA1MDcyLCJhdXRob3JpdGllcyI6WyJST0xFX01FTlRPUiIsIlJPTEVfQURNSU4iXSwianRpIjoiMTFlNGYxYTUtZDY0Ny00YzA1LWE0ZGMtYWFhYzUyMjk4YzMwIiwiY2xpZW50X2lkIjoib3NhcGlXZWJDbGllbnRJZE91dCJ9.gm7cKdusDrdMRkxEiFU5sENKGRC1xwVj2SgPRmE9xxx"  -H  "accept: application/json;charset=utf-8" -X GET "http://portal.openslice.io/tmf-api/serviceCatalogManagement/v4/serviceCatalog"
+
+
+

response:

+
[
+  {
+    "uuid": "9e186cd5-b2b2-4a06-b1d6-895720193bc9",
+    "lastUpdate": "2019-12-19T10:45:55Z",
+    "@baseType": "BaseEntity",
+    "@schemaLocation": null,
+    "@type": "ServiceCatalog",
+    "href": null,
+    "name": "Example Facility Services",
+    "description": "Example Facility Services",
+    "lifecycleStatus": "Active",
+    "version": "1.0",
+    "validFor": {
+      "endDateTime": "2039-11-20T23:07:21Z",
+      "startDateTime": "2019-11-20T23:07:21Z"
+    },
+    "relatedParty": null,
+    "id": "9e186cd5-b2b2-4a06-b1d6-895720193bc9",
+    "category": [
+      {
+        "@baseType": "org.etsi.osl.tmf.scm633.model.ServiceCategoryRef",
+        "@schemaLocation": null,
+        "@type": "org.etsi.osl.tmf.scm633.model.ServiceCategoryRef",
+        "href": null,
+        "name": "eMBB",
+        "@referredType": null,
+        "id": "ef2c90dd-b65e-4a9f-a9c3-427c9fb0219b"
+      },
+      {
+        "@baseType": "org.etsi.osl.tmf.scm633.model.ServiceCategoryRef",
+        "@schemaLocation": null,
+        "@type": "org.etsi.osl.tmf.scm633.model.ServiceCategoryRef",
+        "href": null,
+        "name": "Generic Services",
+        "@referredType": null,
+        "id": "98b9adf1-a1d6-4165-855f-153ddc2131b1"
+      }
+    ]
+  }
+]
+
+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/architecture/tmfweb/index.html b/public/develop/architecture/tmfweb/index.html new file mode 100644 index 0000000000000000000000000000000000000000..c17c6c8251d5955b7c837eee1b72169f43f4d041 --- /dev/null +++ b/public/develop/architecture/tmfweb/index.html @@ -0,0 +1,1118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TMF WEB - ETSI SDG OSL Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + +

Openslice TMF Web Portal

+

Openslice comprises of a web landing page that navigates to the respective TMF Portal.

+ +

The landing page and the TMF portals (Services, Products, Testing, Resources) are written in Angular and are maintained under the same Angular project.

+

Following you may find the scope each portal focuses on and the main TMF APIs it supports.

+

Services Portal is a designated portal for the: +- Service Designer - To design Customer Facing Services as bundles of Resource Facing Services that map to specific Resourses (e.g. NFV, Testing, General Resources). Then, it is charged with the designed Services' exposure to public Service Catalogs. +- Service Customer - To browse the public Service Catalogs and order the offered Services. The fulfilment process of the Service Order is also captured and the final deployed Services are exposed to the Customer.

+

Products Portal is a designated portal for the: +- Product Designer - To design Products as bundles of available Services. Then, it is charged with the designed Products' exposure to public Product Catalogs. +- Product Customer - To browse the public Product Catalogs and navigate to the respective offered Services.

+

Testing Portal is a designated portal for the: +- Testing Designer - To design Tests and provide the testing scripts as attachments to the latter. The Tests can be imported as Services at the Services Portal, and can be included in a Service Bundle.

+

Resources Portal is a designated portal for the: +- Resource Administrator - To view the available Resources that are being synchronized from the underlying infrastructure.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TMF620TMF632TMF633TMF634TMF638TMF639TMF640TMF641TMF642TMF653TMF685
Services Portalxxxxxx
Products Portalxx
Testing Portalxx
Resources Portalxxxx
+

From the landing page, the user can also navigate towards the NFV portal. See NFV WEB.

+ + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/public/develop/assets/images/favicon.png b/public/develop/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf13b9f9d978896599290a74f77d5dbe7d1655c Binary files /dev/null and b/public/develop/assets/images/favicon.png differ diff --git a/public/develop/assets/javascripts/bundle.8fd75fb4.min.js b/public/develop/assets/javascripts/bundle.8fd75fb4.min.js new file mode 100644 index 0000000000000000000000000000000000000000..2f2f130e2df82c26858e561e6493114556c4fc50 --- /dev/null +++ b/public/develop/assets/javascripts/bundle.8fd75fb4.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var _i=Object.create;var br=Object.defineProperty;var Ai=Object.getOwnPropertyDescriptor;var Ci=Object.getOwnPropertyNames,Ft=Object.getOwnPropertySymbols,ki=Object.getPrototypeOf,vr=Object.prototype.hasOwnProperty,eo=Object.prototype.propertyIsEnumerable;var Zr=(e,t,r)=>t in e?br(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,F=(e,t)=>{for(var r in t||(t={}))vr.call(t,r)&&Zr(e,r,t[r]);if(Ft)for(var r of Ft(t))eo.call(t,r)&&Zr(e,r,t[r]);return e};var to=(e,t)=>{var r={};for(var o in e)vr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Ft)for(var o of Ft(e))t.indexOf(o)<0&&eo.call(e,o)&&(r[o]=e[o]);return r};var gr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Hi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ci(t))!vr.call(e,n)&&n!==r&&br(e,n,{get:()=>t[n],enumerable:!(o=Ai(t,n))||o.enumerable});return e};var jt=(e,t,r)=>(r=e!=null?_i(ki(e)):{},Hi(t||!e||!e.__esModule?br(r,"default",{value:e,enumerable:!0}):r,e));var ro=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var no=gr((xr,oo)=>{(function(e,t){typeof xr=="object"&&typeof oo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(C){return!!(C&&C!==document&&C.nodeName!=="HTML"&&C.nodeName!=="BODY"&&"classList"in C&&"contains"in C.classList)}function c(C){var ct=C.type,Ne=C.tagName;return!!(Ne==="INPUT"&&s[ct]&&!C.readOnly||Ne==="TEXTAREA"&&!C.readOnly||C.isContentEditable)}function p(C){C.classList.contains("focus-visible")||(C.classList.add("focus-visible"),C.setAttribute("data-focus-visible-added",""))}function l(C){C.hasAttribute("data-focus-visible-added")&&(C.classList.remove("focus-visible"),C.removeAttribute("data-focus-visible-added"))}function f(C){C.metaKey||C.altKey||C.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(C){o=!1}function h(C){a(C.target)&&(o||c(C.target))&&p(C.target)}function w(C){a(C.target)&&(C.target.classList.contains("focus-visible")||C.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(C.target))}function A(C){document.visibilityState==="hidden"&&(n&&(o=!0),Z())}function Z(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function te(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(C){C.target.nodeName&&C.target.nodeName.toLowerCase()==="html"||(o=!1,te())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",A,!0),Z(),r.addEventListener("focus",h,!0),r.addEventListener("blur",w,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var zr=gr((kt,Vr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof kt=="object"&&typeof Vr=="object"?Vr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof kt=="object"?kt.ClipboardJS=r():t.ClipboardJS=r()})(kt,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Li}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(D){try{return document.execCommand(D)}catch(M){return!1}}var h=function(M){var O=f()(M);return u("cut"),O},w=h;function A(D){var M=document.documentElement.getAttribute("dir")==="rtl",O=document.createElement("textarea");O.style.fontSize="12pt",O.style.border="0",O.style.padding="0",O.style.margin="0",O.style.position="absolute",O.style[M?"right":"left"]="-9999px";var I=window.pageYOffset||document.documentElement.scrollTop;return O.style.top="".concat(I,"px"),O.setAttribute("readonly",""),O.value=D,O}var Z=function(M,O){var I=A(M);O.container.appendChild(I);var W=f()(I);return u("copy"),I.remove(),W},te=function(M){var O=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},I="";return typeof M=="string"?I=Z(M,O):M instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(M==null?void 0:M.type)?I=Z(M.value,O):(I=f()(M),u("copy")),I},J=te;function C(D){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?C=function(O){return typeof O}:C=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},C(D)}var ct=function(){var M=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},O=M.action,I=O===void 0?"copy":O,W=M.container,K=M.target,Ce=M.text;if(I!=="copy"&&I!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(K!==void 0)if(K&&C(K)==="object"&&K.nodeType===1){if(I==="copy"&&K.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(I==="cut"&&(K.hasAttribute("readonly")||K.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Ce)return J(Ce,{container:W});if(K)return I==="cut"?w(K):J(K,{container:W})},Ne=ct;function Pe(D){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Pe=function(O){return typeof O}:Pe=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},Pe(D)}function xi(D,M){if(!(D instanceof M))throw new TypeError("Cannot call a class as a function")}function Xr(D,M){for(var O=0;O0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof W.action=="function"?W.action:this.defaultAction,this.target=typeof W.target=="function"?W.target:this.defaultTarget,this.text=typeof W.text=="function"?W.text:this.defaultText,this.container=Pe(W.container)==="object"?W.container:document.body}},{key:"listenClick",value:function(W){var K=this;this.listener=p()(W,"click",function(Ce){return K.onClick(Ce)})}},{key:"onClick",value:function(W){var K=W.delegateTarget||W.currentTarget,Ce=this.action(K)||"copy",It=Ne({action:Ce,container:this.container,target:this.target(K),text:this.text(K)});this.emit(It?"success":"error",{action:Ce,text:It,trigger:K,clearSelection:function(){K&&K.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(W){return hr("action",W)}},{key:"defaultTarget",value:function(W){var K=hr("target",W);if(K)return document.querySelector(K)}},{key:"defaultText",value:function(W){return hr("text",W)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(W){var K=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(W,K)}},{key:"cut",value:function(W){return w(W)}},{key:"isSupported",value:function(){var W=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],K=typeof W=="string"?[W]:W,Ce=!!document.queryCommandSupported;return K.forEach(function(It){Ce=Ce&&!!document.queryCommandSupported(It)}),Ce}}]),O}(a()),Li=Mi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s},438:function(o,n,i){var s=i(828);function a(l,f,u,h,w){var A=p.apply(this,arguments);return l.addEventListener(u,A,w),{destroy:function(){l.removeEventListener(u,A,w)}}}function c(l,f,u,h,w){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(A){return a(A,f,u,h,w)}))}function p(l,f,u,h){return function(w){w.delegateTarget=s(w.target,f),w.delegateTarget&&h.call(l,w)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(o,n,i){var s=i(879),a=i(438);function c(u,h,w){if(!u&&!h&&!w)throw new Error("Missing required arguments");if(!s.string(h))throw new TypeError("Second argument must be a String");if(!s.fn(w))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,h,w);if(s.nodeList(u))return l(u,h,w);if(s.string(u))return f(u,h,w);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,h,w){return u.addEventListener(h,w),{destroy:function(){u.removeEventListener(h,w)}}}function l(u,h,w){return Array.prototype.forEach.call(u,function(A){A.addEventListener(h,w)}),{destroy:function(){Array.prototype.forEach.call(u,function(A){A.removeEventListener(h,w)})}}}function f(u,h,w){return a(document.body,u,h,w)}o.exports=c},817:function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var Va=/["'&<>]/;qn.exports=za;function za(e){var t=""+e,r=Va.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function V(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function z(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||a(u,h)})})}function a(u,h){try{c(o[u](h))}catch(w){f(i[0][3],w)}}function c(u){u.value instanceof ot?Promise.resolve(u.value.v).then(p,l):f(i[0][2],u)}function p(u){a("next",u)}function l(u){a("throw",u)}function f(u,h){u(h),i.shift(),i.length&&a(i[0][0],i[0][1])}}function so(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof ue=="function"?ue(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function k(e){return typeof e=="function"}function pt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Wt=pt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=ue(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(A){t={error:A}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(k(l))try{l()}catch(A){i=A instanceof Wt?A.errors:[A]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=ue(f),h=u.next();!h.done;h=u.next()){var w=h.value;try{co(w)}catch(A){i=i!=null?i:[],A instanceof Wt?i=z(z([],V(i)),V(A.errors)):i.push(A)}}}catch(A){o={error:A}}finally{try{h&&!h.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Wt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)co(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Er=Ie.EMPTY;function Dt(e){return e instanceof Ie||e&&"closed"in e&&k(e.remove)&&k(e.add)&&k(e.unsubscribe)}function co(e){k(e)?e():e.unsubscribe()}var ke={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var lt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?Er:(this.currentObservers=null,a.push(r),new Ie(function(){o.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new j;return r.source=this,r},t.create=function(r,o){return new vo(r,o)},t}(j);var vo=function(e){se(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Er},t}(v);var St={now:function(){return(St.delegate||Date).now()},delegate:void 0};var Ot=function(e){se(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=St);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(ut.cancelAnimationFrame(o),r._scheduled=void 0)},t}(zt);var yo=function(e){se(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(qt);var de=new yo(xo);var L=new j(function(e){return e.complete()});function Kt(e){return e&&k(e.schedule)}function _r(e){return e[e.length-1]}function Je(e){return k(_r(e))?e.pop():void 0}function Ae(e){return Kt(_r(e))?e.pop():void 0}function Qt(e,t){return typeof _r(e)=="number"?e.pop():t}var dt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Yt(e){return k(e==null?void 0:e.then)}function Bt(e){return k(e[ft])}function Gt(e){return Symbol.asyncIterator&&k(e==null?void 0:e[Symbol.asyncIterator])}function Jt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Di(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Xt=Di();function Zt(e){return k(e==null?void 0:e[Xt])}function er(e){return ao(this,arguments,function(){var r,o,n,i;return Ut(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,ot(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,ot(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,ot(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function tr(e){return k(e==null?void 0:e.getReader)}function N(e){if(e instanceof j)return e;if(e!=null){if(Bt(e))return Ni(e);if(dt(e))return Vi(e);if(Yt(e))return zi(e);if(Gt(e))return Eo(e);if(Zt(e))return qi(e);if(tr(e))return Ki(e)}throw Jt(e)}function Ni(e){return new j(function(t){var r=e[ft]();if(k(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Vi(e){return new j(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):ce,ye(1),r?Qe(t):jo(function(){return new or}))}}function $r(e){return e<=0?function(){return L}:x(function(t,r){var o=[];t.subscribe(S(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new v}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,h=0,w=!1,A=!1,Z=function(){f==null||f.unsubscribe(),f=void 0},te=function(){Z(),l=u=void 0,w=A=!1},J=function(){var C=l;te(),C==null||C.unsubscribe()};return x(function(C,ct){h++,!A&&!w&&Z();var Ne=u=u!=null?u:r();ct.add(function(){h--,h===0&&!A&&!w&&(f=Pr(J,c))}),Ne.subscribe(ct),!l&&h>0&&(l=new it({next:function(Pe){return Ne.next(Pe)},error:function(Pe){A=!0,Z(),f=Pr(te,n,Pe),Ne.error(Pe)},complete:function(){w=!0,Z(),f=Pr(te,s),Ne.complete()}}),N(C).subscribe(l))})(p)}}function Pr(e,t){for(var r=[],o=2;oe.next(document)),e}function R(e,t=document){return Array.from(t.querySelectorAll(e))}function P(e,t=document){let r=me(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function me(e,t=document){return t.querySelector(e)||void 0}function Re(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var la=T(d(document.body,"focusin"),d(document.body,"focusout")).pipe(be(1),q(void 0),m(()=>Re()||document.body),B(1));function vt(e){return la.pipe(m(t=>e.contains(t)),Y())}function Vo(e,t){return T(d(e,"mouseenter").pipe(m(()=>!0)),d(e,"mouseleave").pipe(m(()=>!1))).pipe(t?be(t):ce,q(!1))}function Ue(e){return{x:e.offsetLeft,y:e.offsetTop}}function zo(e){return T(d(window,"load"),d(window,"resize")).pipe(Me(0,de),m(()=>Ue(e)),q(Ue(e)))}function ir(e){return{x:e.scrollLeft,y:e.scrollTop}}function et(e){return T(d(e,"scroll"),d(window,"resize")).pipe(Me(0,de),m(()=>ir(e)),q(ir(e)))}function qo(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)qo(e,r)}function E(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)qo(o,n);return o}function ar(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function gt(e){let t=E("script",{src:e});return H(()=>(document.head.appendChild(t),T(d(t,"load"),d(t,"error").pipe(b(()=>Ar(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),_(()=>document.head.removeChild(t)),ye(1))))}var Ko=new v,ma=H(()=>typeof ResizeObserver=="undefined"?gt("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>{for(let t of e)Ko.next(t)})),b(e=>T(qe,$(e)).pipe(_(()=>e.disconnect()))),B(1));function pe(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Ee(e){return ma.pipe(y(t=>t.observe(e)),b(t=>Ko.pipe(g(({target:r})=>r===e),_(()=>t.unobserve(e)),m(()=>pe(e)))),q(pe(e)))}function xt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function sr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var Qo=new v,fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)Qo.next(t)},{threshold:0}))).pipe(b(e=>T(qe,$(e)).pipe(_(()=>e.disconnect()))),B(1));function yt(e){return fa.pipe(y(t=>t.observe(e)),b(t=>Qo.pipe(g(({target:r})=>r===e),_(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function Yo(e,t=16){return et(e).pipe(m(({y:r})=>{let o=pe(e),n=xt(e);return r>=n.height-o.height-t}),Y())}var cr={drawer:P("[data-md-toggle=drawer]"),search:P("[data-md-toggle=search]")};function Bo(e){return cr[e].checked}function Be(e,t){cr[e].checked!==t&&cr[e].click()}function We(e){let t=cr[e];return d(t,"change").pipe(m(()=>t.checked),q(t.checked))}function ua(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function da(){return T(d(window,"compositionstart").pipe(m(()=>!0)),d(window,"compositionend").pipe(m(()=>!1))).pipe(q(!1))}function Go(){let e=d(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:Bo("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Re();if(typeof o!="undefined")return!ua(o,r)}return!0}),le());return da().pipe(b(t=>t?L:e))}function ve(){return new URL(location.href)}function st(e,t=!1){if(G("navigation.instant")&&!t){let r=E("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function Jo(){return new v}function Xo(){return location.hash.slice(1)}function Zo(e){let t=E("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function ha(e){return T(d(window,"hashchange"),e).pipe(m(Xo),q(Xo()),g(t=>t.length>0),B(1))}function en(e){return ha(e).pipe(m(t=>me(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function At(e){let t=matchMedia(e);return nr(r=>t.addListener(()=>r(t.matches))).pipe(q(t.matches))}function tn(){let e=matchMedia("print");return T(d(window,"beforeprint").pipe(m(()=>!0)),d(window,"afterprint").pipe(m(()=>!1))).pipe(q(e.matches))}function Ur(e,t){return e.pipe(b(r=>r?t():L))}function Wr(e,t){return new j(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function De(e,t){return Wr(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),B(1))}function rn(e,t){let r=new DOMParser;return Wr(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),B(1))}function on(e,t){let r=new DOMParser;return Wr(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),B(1))}function nn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function an(){return T(d(window,"scroll",{passive:!0}),d(window,"resize",{passive:!0})).pipe(m(nn),q(nn()))}function sn(){return{width:innerWidth,height:innerHeight}}function cn(){return d(window,"resize",{passive:!0}).pipe(m(sn),q(sn()))}function pn(){return Q([an(),cn()]).pipe(m(([e,t])=>({offset:e,size:t})),B(1))}function pr(e,{viewport$:t,header$:r}){let o=t.pipe(X("size")),n=Q([o,r]).pipe(m(()=>Ue(e)));return Q([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function ba(e){return d(e,"message",t=>t.data)}function va(e){let t=new v;return t.subscribe(r=>e.postMessage(r)),t}function ln(e,t=new Worker(e)){let r=ba(t),o=va(t),n=new v;n.subscribe(o);let i=o.pipe(ee(),oe(!0));return n.pipe(ee(),$e(r.pipe(U(i))),le())}var ga=P("#__config"),Et=JSON.parse(ga.textContent);Et.base=`${new URL(Et.base,ve())}`;function we(){return Et}function G(e){return Et.features.includes(e)}function ge(e,t){return typeof t!="undefined"?Et.translations[e].replace("#",t.toString()):Et.translations[e]}function Te(e,t=document){return P(`[data-md-component=${e}]`,t)}function ne(e,t=document){return R(`[data-md-component=${e}]`,t)}function xa(e){let t=P(".md-typeset > :first-child",e);return d(t,"click",{once:!0}).pipe(m(()=>P(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function mn(e){if(!G("announce.dismiss")||!e.childElementCount)return L;if(!e.hidden){let t=P(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new v;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),xa(e).pipe(y(r=>t.next(r)),_(()=>t.complete()),m(r=>F({ref:e},r)))})}function ya(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function fn(e,t){let r=new v;return r.subscribe(({hidden:o})=>{e.hidden=o}),ya(e,t).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))}function Ct(e,t){return t==="inline"?E("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},E("div",{class:"md-tooltip__inner md-typeset"})):E("div",{class:"md-tooltip",id:e,role:"tooltip"},E("div",{class:"md-tooltip__inner md-typeset"}))}function un(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return E("aside",{class:"md-annotation",tabIndex:0},Ct(t),E("a",{href:r,class:"md-annotation__index",tabIndex:-1},E("span",{"data-md-annotation-id":e})))}else return E("aside",{class:"md-annotation",tabIndex:0},Ct(t),E("span",{class:"md-annotation__index",tabIndex:-1},E("span",{"data-md-annotation-id":e})))}function dn(e){return E("button",{class:"md-clipboard md-icon",title:ge("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function Dr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,E("del",null,p)," "],[]).slice(0,-1),i=we(),s=new URL(e.location,i.base);G("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=we();return E("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},E("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&E("div",{class:"md-search-result__icon md-icon"}),r>0&&E("h1",null,e.title),r<=0&&E("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return E("span",{class:`md-tag ${p}`},c)}),o>0&&n.length>0&&E("p",{class:"md-search-result__terms"},ge("search.result.term.missing"),": ",...n)))}function hn(e){let t=e[0].score,r=[...e],o=we(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreDr(l,1)),...c.length?[E("details",{class:"md-search-result__more"},E("summary",{tabIndex:-1},E("div",null,c.length>0&&c.length===1?ge("search.result.more.one"):ge("search.result.more.other",c.length))),...c.map(l=>Dr(l,1)))]:[]];return E("li",{class:"md-search-result__item"},p)}function bn(e){return E("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>E("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?ar(r):r)))}function Nr(e){let t=`tabbed-control tabbed-control--${e}`;return E("div",{class:t,hidden:!0},E("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function vn(e){return E("div",{class:"md-typeset__scrollwrap"},E("div",{class:"md-typeset__table"},e))}function Ea(e){let t=we(),r=new URL(`../${e.version}/`,t.base);return E("li",{class:"md-version__item"},E("a",{href:`${r}`,class:"md-version__link"},e.title))}function gn(e,t){return E("div",{class:"md-version"},E("button",{class:"md-version__current","aria-label":ge("select.version")},t.title),E("ul",{class:"md-version__list"},e.map(Ea)))}var wa=0;function Ta(e,t){document.body.append(e);let{width:r}=pe(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=sr(t),n=typeof o!="undefined"?et(o):$({x:0,y:0}),i=T(vt(t),Vo(t)).pipe(Y());return Q([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Ue(t),l=pe(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function Ge(e){let t=e.title;if(!t.length)return L;let r=`__tooltip_${wa++}`,o=Ct(r,"inline"),n=P(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new v;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),T(i.pipe(g(({active:s})=>s)),i.pipe(be(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Me(16,de)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(_t(125,de),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Ta(o,e).pipe(y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))}).pipe(ze(ie))}function Sa(e,t){let r=H(()=>Q([zo(e),et(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=pe(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return vt(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),ye(+!o||1/0))))}function xn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new v,s=i.pipe(ee(),oe(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),yt(e).pipe(U(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),T(i.pipe(g(({active:a})=>a)),i.pipe(be(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Me(16,de)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(_t(125,de),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),d(n,"click").pipe(U(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),d(n,"mousedown").pipe(U(s),ae(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Re())==null||p.blur()}}),r.pipe(U(s),g(a=>a===o),Ye(125)).subscribe(()=>e.focus()),Sa(e,t).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))})}function Oa(e){return e.tagName==="CODE"?R(".c, .c1, .cm",e):[e]}function Ma(e){let t=[];for(let r of Oa(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function yn(e,t){t.append(...Array.from(e.childNodes))}function lr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Ma(t)){let[,c]=a.textContent.match(/\((\d+)\)/);me(`:scope > li:nth-child(${c})`,e)&&(s.set(c,un(c,i)),a.replaceWith(s.get(c)))}return s.size===0?L:H(()=>{let a=new v,c=a.pipe(ee(),oe(!0)),p=[];for(let[l,f]of s)p.push([P(".md-typeset",f),P(`:scope > li:nth-child(${l})`,e)]);return o.pipe(U(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?yn(f,u):yn(u,f)}),T(...[...s].map(([,l])=>xn(l,t,{target$:r}))).pipe(_(()=>a.complete()),le())})}function En(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return En(t)}}function wn(e,t){return H(()=>{let r=En(e);return typeof r!="undefined"?lr(r,e,t):L})}var Tn=jt(zr());var La=0;function Sn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Sn(t)}}function _a(e){return Ee(e).pipe(m(({width:t})=>({scrollable:xt(e).width>t})),X("scrollable"))}function On(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new v,i=n.pipe($r(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[];if(Tn.default.isSupported()&&(e.closest(".copy")||G("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${La++}`;let p=dn(c.id);c.insertBefore(p,e),G("content.tooltips")&&s.push(Ge(p))}let a=e.closest(".highlight");if(a instanceof HTMLElement){let c=Sn(a);if(typeof c!="undefined"&&(a.classList.contains("annotate")||G("content.code.annotate"))){let p=lr(c,e,t);s.push(Ee(a).pipe(U(i),m(({width:l,height:f})=>l&&f),Y(),b(l=>l?p:L)))}}return _a(e).pipe(y(c=>n.next(c)),_(()=>n.complete()),m(c=>F({ref:e},c)),$e(...s))});return G("content.lazy")?yt(e).pipe(g(n=>n),ye(1),b(()=>o)):o}function Aa(e,{target$:t,print$:r}){let o=!0;return T(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),y(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Mn(e,t){return H(()=>{let r=new v;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Aa(e,t).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}var Ln=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var qr,ka=0;function Ha(){return typeof mermaid=="undefined"||mermaid instanceof Element?gt("https://unpkg.com/mermaid@10.7.0/dist/mermaid.min.js"):$(void 0)}function _n(e){return e.classList.remove("mermaid"),qr||(qr=Ha().pipe(y(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Ln,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),B(1))),qr.subscribe(()=>ro(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${ka++}`,r=E("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),qr.pipe(m(()=>({ref:e})))}var An=E("table");function Cn(e){return e.replaceWith(An),An.replaceWith(vn(e)),$({ref:e})}function $a(e){let t=e.find(r=>r.checked)||e[0];return T(...e.map(r=>d(r,"change").pipe(m(()=>P(`label[for="${r.id}"]`))))).pipe(q(P(`label[for="${t.id}"]`)),m(r=>({active:r})))}function kn(e,{viewport$:t,target$:r}){let o=P(".tabbed-labels",e),n=R(":scope > input",e),i=Nr("prev");e.append(i);let s=Nr("next");return e.append(s),H(()=>{let a=new v,c=a.pipe(ee(),oe(!0));Q([a,Ee(e)]).pipe(U(c),Me(1,de)).subscribe({next([{active:p},l]){let f=Ue(p),{width:u}=pe(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let h=ir(o);(f.xh.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),Q([et(o),Ee(o)]).pipe(U(c)).subscribe(([p,l])=>{let f=xt(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),T(d(i,"click").pipe(m(()=>-1)),d(s,"click").pipe(m(()=>1))).pipe(U(c)).subscribe(p=>{let{width:l}=pe(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(U(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=P(`label[for="${p.id}"]`);l.replaceChildren(E("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),d(l.firstElementChild,"click").pipe(U(c),g(f=>!(f.metaKey||f.ctrlKey)),y(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return G("content.tabs.link")&&a.pipe(Le(1),ae(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let w of R("[data-tabs]"))for(let A of R(":scope > input",w)){let Z=P(`label[for="${A.id}"]`);if(Z!==p&&Z.innerText.trim()===f){Z.setAttribute("data-md-switching",""),A.click();break}}window.scrollTo({top:e.offsetTop-u});let h=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...h])])}}),a.pipe(U(c)).subscribe(()=>{for(let p of R("audio, video",e))p.pause()}),$a(n).pipe(y(p=>a.next(p)),_(()=>a.complete()),m(p=>F({ref:e},p)))}).pipe(ze(ie))}function Hn(e,{viewport$:t,target$:r,print$:o}){return T(...R(".annotate:not(.highlight)",e).map(n=>wn(n,{target$:r,print$:o})),...R("pre:not(.mermaid) > code",e).map(n=>On(n,{target$:r,print$:o})),...R("pre.mermaid",e).map(n=>_n(n)),...R("table:not([class])",e).map(n=>Cn(n)),...R("details",e).map(n=>Mn(n,{target$:r,print$:o})),...R("[data-tabs]",e).map(n=>kn(n,{viewport$:t,target$:r})),...R("[title]",e).filter(()=>G("content.tooltips")).map(n=>Ge(n)))}function Ra(e,{alert$:t}){return t.pipe(b(r=>T($(!0),$(!1).pipe(Ye(2e3))).pipe(m(o=>({message:r,active:o})))))}function $n(e,t){let r=P(".md-typeset",e);return H(()=>{let o=new v;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ra(e,t).pipe(y(n=>o.next(n)),_(()=>o.complete()),m(n=>F({ref:e},n)))})}function Pa({viewport$:e}){if(!G("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Ke(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=We("search");return Q([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),q(!1))}function Rn(e,t){return H(()=>Q([Ee(e),Pa(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),B(1))}function Pn(e,{header$:t,main$:r}){return H(()=>{let o=new v,n=o.pipe(ee(),oe(!0));o.pipe(X("active"),je(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(R("[title]",e)).pipe(g(()=>G("content.tooltips")),re(s=>Ge(s)));return r.subscribe(o),t.pipe(U(n),m(s=>F({ref:e},s)),$e(i.pipe(U(n))))})}function Ia(e,{viewport$:t,header$:r}){return pr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=pe(e);return{active:o>=n}}),X("active"))}function In(e,t){return H(()=>{let r=new v;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=me(".md-content h1");return typeof o=="undefined"?L:Ia(o,t).pipe(y(n=>r.next(n)),_(()=>r.complete()),m(n=>F({ref:e},n)))})}function Fn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Ee(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),X("bottom"))));return Q([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function Fa(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(re(o=>d(o,"change").pipe(m(()=>o))),q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),B(1))}function jn(e){let t=R("input",e),r=E("meta",{name:"theme-color"});document.head.appendChild(r);let o=E("meta",{name:"color-scheme"});document.head.appendChild(o);let n=At("(prefers-color-scheme: light)");return H(()=>{let i=new v;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;a{let s=Te("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(Oe(ie)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),Fa(t).pipe(U(n.pipe(Le(1))),at(),y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))})}function Un(e,{progress$:t}){return H(()=>{let r=new v;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(y(o=>r.next({value:o})),_(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Kr=jt(zr());function ja(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Wn({alert$:e}){Kr.default.isSupported()&&new j(t=>{new Kr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||ja(P(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(y(t=>{t.trigger.focus()}),m(()=>ge("clipboard.copied"))).subscribe(e)}function Dn(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function Ua(e,t){let r=new Map;for(let o of R("url",e)){let n=P("loc",o),i=[Dn(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of R("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(Dn(new URL(a),t))}}return r}function mr(e){return on(new URL("sitemap.xml",e)).pipe(m(t=>Ua(t,new URL(e))),he(()=>$(new Map)))}function Wa(e,t){if(!(e.target instanceof Element))return L;let r=e.target.closest("a");if(r===null)return L;if(r.target||e.metaKey||e.ctrlKey)return L;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(new URL(r.href))):L}function Nn(e){let t=new Map;for(let r of R(":scope > *",e.head))t.set(r.outerHTML,r);return t}function Vn(e){for(let t of R("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function Da(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...G("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=me(o),i=me(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=Nn(document);for(let[o,n]of Nn(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values())o.remove();let r=Te("container");return Fe(R("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new j(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),L}),ee(),oe(e))}function zn({location$:e,viewport$:t,progress$:r}){let o=we();if(location.protocol==="file:")return L;let n=mr(o.base);$(document).subscribe(Vn);let i=d(document.body,"click").pipe(je(n),b(([c,p])=>Wa(c,p)),le()),s=d(window,"popstate").pipe(m(ve),le());i.pipe(ae(t)).subscribe(([c,{offset:p}])=>{history.replaceState(p,""),history.pushState(null,"",c)}),T(i,s).subscribe(e);let a=e.pipe(X("pathname"),b(c=>rn(c,{progress$:r}).pipe(he(()=>(st(c,!0),L)))),b(Vn),b(Da),le());return T(a.pipe(ae(e,(c,p)=>p)),e.pipe(X("pathname"),b(()=>e),X("hash")),e.pipe(Y((c,p)=>c.pathname===p.pathname&&c.hash===p.hash),b(()=>i),y(()=>history.back()))).subscribe(c=>{var p,l;history.state!==null||!c.hash?window.scrollTo(0,(l=(p=history.state)==null?void 0:p.y)!=null?l:0):(history.scrollRestoration="auto",Zo(c.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),d(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(X("offset"),be(100)).subscribe(({offset:c})=>{history.replaceState(c,"")}),a}var Qn=jt(Kn());function Yn(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,Qn.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function Ht(e){return e.type===1}function fr(e){return e.type===3}function Bn(e,t){let r=ln(e);return T($(location.protocol!=="file:"),We("search")).pipe(He(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:G("search.suggest")}}})),r}function Gn({document$:e}){let t=we(),r=De(new URL("../versions.json",t.base)).pipe(he(()=>L)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>d(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),ae(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?L:(i.preventDefault(),$(c))}}return L}),b(i=>{let{version:s}=n.get(i);return mr(new URL(i)).pipe(m(a=>{let p=ve().href.replace(t.base,"");return a.has(p.split("#")[0])?new URL(`../${s}/${p}`,t.base):new URL(i)}))})))).subscribe(n=>st(n,!0)),Q([r,o]).subscribe(([n,i])=>{P(".md-header__topic").appendChild(gn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var s;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let a=((s=t.version)==null?void 0:s.default)||"latest";Array.isArray(a)||(a=[a]);e:for(let c of a)for(let p of n.aliases.concat(n.version))if(new RegExp(c,"i").test(p)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let a of ne("outdated"))a.hidden=!1})}function Ka(e,{worker$:t}){let{searchParams:r}=ve();r.has("q")&&(Be("search",!0),e.value=r.get("q"),e.focus(),We("search").pipe(He(i=>!i)).subscribe(()=>{let i=ve();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=vt(e),n=T(t.pipe(He(Ht)),d(e,"keyup"),o).pipe(m(()=>e.value),Y());return Q([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),B(1))}function Jn(e,{worker$:t}){let r=new v,o=r.pipe(ee(),oe(!0));Q([t.pipe(He(Ht)),r],(i,s)=>s).pipe(X("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(X("focus")).subscribe(({focus:i})=>{i&&Be("search",i)}),d(e.form,"reset").pipe(U(o)).subscribe(()=>e.focus());let n=P("header [for=__search]");return d(n,"click").subscribe(()=>e.focus()),Ka(e,{worker$:t}).pipe(y(i=>r.next(i)),_(()=>r.complete()),m(i=>F({ref:e},i)),B(1))}function Xn(e,{worker$:t,query$:r}){let o=new v,n=Yo(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=P(":scope > :first-child",e),a=P(":scope > :last-child",e);We("search").subscribe(l=>a.setAttribute("role",l?"list":"presentation")),o.pipe(ae(r),Ir(t.pipe(He(Ht)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?ge("search.result.none"):ge("search.result.placeholder");break;case 1:s.textContent=ge("search.result.one");break;default:let u=ar(l.length);s.textContent=ge("search.result.other",u)}});let c=o.pipe(y(()=>a.innerHTML=""),b(({items:l})=>T($(...l.slice(0,10)),$(...l.slice(10)).pipe(Ke(4),jr(n),b(([f])=>f)))),m(hn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(re(l=>{let f=me("details",l);return typeof f=="undefined"?L:d(f,"toggle").pipe(U(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(fr),m(({data:l})=>l)).pipe(y(l=>o.next(l)),_(()=>o.complete()),m(l=>F({ref:e},l)))}function Qa(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=ve();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Zn(e,t){let r=new v,o=r.pipe(ee(),oe(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),d(e,"click").pipe(U(o)).subscribe(n=>n.preventDefault()),Qa(e,t).pipe(y(n=>r.next(n)),_(()=>r.complete()),m(n=>F({ref:e},n)))}function ei(e,{worker$:t,keyboard$:r}){let o=new v,n=Te("search-query"),i=T(d(n,"keydown"),d(n,"focus")).pipe(Oe(ie),m(()=>n.value),Y());return o.pipe(je(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{switch(a.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(g(fr),m(({data:a})=>a)).pipe(y(a=>o.next(a)),_(()=>o.complete()),m(()=>({ref:e})))}function ti(e,{index$:t,keyboard$:r}){let o=we();try{let n=Bn(o.search,t),i=Te("search-query",e),s=Te("search-result",e);d(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>Be("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Re();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of R(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,h])=>h-u);f.click()}c.claim()}break;case"Escape":case"Tab":Be("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...R(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Re()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Jn(i,{worker$:n});return T(a,Xn(s,{worker$:n,query$:a})).pipe($e(...ne("search-share",e).map(c=>Zn(c,{query$:a})),...ne("search-suggest",e).map(c=>ei(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,qe}}function ri(e,{index$:t,location$:r}){return Q([t,r.pipe(q(ve()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>Yn(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=E("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ya(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return Q([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function Qr(e,o){var n=o,{header$:t}=n,r=to(n,["header$"]);let i=P(".md-sidebar__scrollwrap",e),{y:s}=Ue(i);return H(()=>{let a=new v,c=a.pipe(ee(),oe(!0)),p=a.pipe(Me(0,de));return p.pipe(ae(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(He()).subscribe(()=>{for(let l of R(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=pe(f);f.scrollTo({top:u-h/2})}}}),fe(R("label[tabindex]",e)).pipe(re(l=>d(l,"click").pipe(Oe(ie),m(()=>l),U(c)))).subscribe(l=>{let f=P(`[id="${l.htmlFor}"]`);P(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),Ya(e,r).pipe(y(l=>a.next(l)),_(()=>a.complete()),m(l=>F({ref:e},l)))})}function oi(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return Lt(De(`${r}/releases/latest`).pipe(he(()=>L),m(o=>({version:o.tag_name})),Qe({})),De(r).pipe(he(()=>L),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>F(F({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return De(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ni(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return De(r).pipe(he(()=>L),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))}function ii(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return oi(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ni(r,o)}return L}var Ba;function Ga(e){return Ba||(Ba=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(ne("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return L}return ii(e.href).pipe(y(o=>__md_set("__source",o,sessionStorage)))}).pipe(he(()=>L),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),B(1)))}function ai(e){let t=P(":scope > :last-child",e);return H(()=>{let r=new v;return r.subscribe(({facts:o})=>{t.appendChild(bn(o)),t.classList.add("md-source__repository--active")}),Ga(e).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}function Ja(e,{viewport$:t,header$:r}){return Ee(document.body).pipe(b(()=>pr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),X("hidden"))}function si(e,t){return H(()=>{let r=new v;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(G("navigation.tabs.sticky")?$({hidden:!1}):Ja(e,t)).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}function Xa(e,{viewport$:t,header$:r}){let o=new Map,n=R(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=me(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(X("height"),m(({height:a})=>{let c=Te("main"),p=P(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Ee(document.body).pipe(X("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let h=f.offsetParent;for(;h;h=h.offsetParent)u+=h.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),je(i),b(([c,p])=>t.pipe(Rr(([l,f],{offset:{y:u},size:h})=>{let w=u+h.height>=Math.floor(a.height);for(;f.length;){let[,A]=f[0];if(A-p=u&&!w)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),q({prev:[],next:[]}),Ke(2,1),m(([a,c])=>a.prev.length{let i=new v,s=i.pipe(ee(),oe(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),G("toc.follow")){let a=T(t.pipe(be(1),m(()=>{})),t.pipe(be(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),je(o.pipe(Oe(ie))),ae(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=sr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=pe(f);f.scrollTo({top:u-h/2,behavior:p})}}})}return G("navigation.tracking")&&t.pipe(U(s),X("offset"),be(250),Le(1),U(n.pipe(Le(1))),at({delay:250}),ae(i)).subscribe(([,{prev:a}])=>{let c=ve(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),Xa(e,{viewport$:t,header$:r}).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))})}function Za(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),Ke(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return Q([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),U(o.pipe(Le(1))),oe(!0),at({delay:250}),m(s=>({hidden:s})))}function pi(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new v,s=i.pipe(ee(),oe(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(U(s),X("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),d(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),Za(e,{viewport$:t,main$:o,target$:n}).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))}function li({document$:e}){e.pipe(b(()=>R(".md-ellipsis")),re(t=>yt(t).pipe(U(e.pipe(Le(1))),g(r=>r),m(()=>t),ye(1))),g(t=>t.offsetWidth{let r=t.innerText,o=t.closest("a")||t;return o.title=r,Ge(o).pipe(U(e.pipe(Le(1))),_(()=>o.removeAttribute("title")))})).subscribe(),e.pipe(b(()=>R(".md-status")),re(t=>Ge(t))).subscribe()}function mi({document$:e,tablet$:t}){e.pipe(b(()=>R(".md-toggle--indeterminate")),y(r=>{r.indeterminate=!0,r.checked=!1}),re(r=>d(r,"change").pipe(Fr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),ae(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function es(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function fi({document$:e}){e.pipe(b(()=>R("[data-md-scrollfix]")),y(t=>t.removeAttribute("data-md-scrollfix")),g(es),re(t=>d(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function ui({viewport$:e,tablet$:t}){Q([We("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(Ye(r?400:100))),ae(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ts(){return location.protocol==="file:"?gt(`${new URL("search/search_index.js",Yr.base)}`).pipe(m(()=>__index),B(1)):De(new URL("search/search_index.json",Yr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var rt=No(),Rt=Jo(),wt=en(Rt),Br=Go(),_e=pn(),ur=At("(min-width: 960px)"),hi=At("(min-width: 1220px)"),bi=tn(),Yr=we(),vi=document.forms.namedItem("search")?ts():qe,Gr=new v;Wn({alert$:Gr});var Jr=new v;G("navigation.instant")&&zn({location$:Rt,viewport$:_e,progress$:Jr}).subscribe(rt);var di;((di=Yr.version)==null?void 0:di.provider)==="mike"&&Gn({document$:rt});T(Rt,wt).pipe(Ye(125)).subscribe(()=>{Be("drawer",!1),Be("search",!1)});Br.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=me("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=me("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Re();o instanceof HTMLLabelElement&&o.click()}});li({document$:rt});mi({document$:rt,tablet$:ur});fi({document$:rt});ui({viewport$:_e,tablet$:ur});var tt=Rn(Te("header"),{viewport$:_e}),$t=rt.pipe(m(()=>Te("main")),b(e=>Fn(e,{viewport$:_e,header$:tt})),B(1)),rs=T(...ne("consent").map(e=>fn(e,{target$:wt})),...ne("dialog").map(e=>$n(e,{alert$:Gr})),...ne("header").map(e=>Pn(e,{viewport$:_e,header$:tt,main$:$t})),...ne("palette").map(e=>jn(e)),...ne("progress").map(e=>Un(e,{progress$:Jr})),...ne("search").map(e=>ti(e,{index$:vi,keyboard$:Br})),...ne("source").map(e=>ai(e))),os=H(()=>T(...ne("announce").map(e=>mn(e)),...ne("content").map(e=>Hn(e,{viewport$:_e,target$:wt,print$:bi})),...ne("content").map(e=>G("search.highlight")?ri(e,{index$:vi,location$:Rt}):L),...ne("header-title").map(e=>In(e,{viewport$:_e,header$:tt})),...ne("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Ur(hi,()=>Qr(e,{viewport$:_e,header$:tt,main$:$t})):Ur(ur,()=>Qr(e,{viewport$:_e,header$:tt,main$:$t}))),...ne("tabs").map(e=>si(e,{viewport$:_e,header$:tt})),...ne("toc").map(e=>ci(e,{viewport$:_e,header$:tt,main$:$t,target$:wt})),...ne("top").map(e=>pi(e,{viewport$:_e,header$:tt,main$:$t,target$:wt})))),gi=rt.pipe(b(()=>os),$e(rs),B(1));gi.subscribe();window.document$=rt;window.location$=Rt;window.target$=wt;window.keyboard$=Br;window.viewport$=_e;window.tablet$=ur;window.screen$=hi;window.print$=bi;window.alert$=Gr;window.progress$=Jr;window.component$=gi;})(); +//# sourceMappingURL=bundle.8fd75fb4.min.js.map + diff --git a/public/develop/assets/javascripts/bundle.8fd75fb4.min.js.map b/public/develop/assets/javascripts/bundle.8fd75fb4.min.js.map new file mode 100644 index 0000000000000000000000000000000000000000..1a287892a696f6d0e4b7e4a08262d94f3d95d425 --- /dev/null +++ b/public/develop/assets/javascripts/bundle.8fd75fb4.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/tslib.es6.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n 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;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n 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;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n 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); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n 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; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n 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);\r\n 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); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n 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\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n 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\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an