├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── docker.yml │ └── typehub.yml ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── specification └── typeapi.json └── www ├── .env ├── build.php ├── cache └── empty ├── composer.json ├── composer.lock ├── configuration.php ├── container.php ├── psalm.xml ├── public ├── .htaccess ├── css │ ├── app.css │ ├── app.min.css │ ├── bootstrap.min.css │ ├── bulma-no-dark-mode.min.css │ ├── bulma.min.css │ └── highlight.min.css ├── img │ ├── favicon.ico │ ├── github-32.png │ └── header_bg.png ├── index.php └── js │ ├── app.js │ ├── app.min.js │ ├── bootstrap.min.js │ ├── highlight.min.js │ ├── jquery.min.js │ └── popper.min.js ├── resources ├── container.php ├── examples │ ├── argument_body.json │ ├── argument_query.json │ ├── exception.json │ ├── operation_group.json │ └── simple.json ├── template │ ├── ecosystem.php │ ├── generator.php │ ├── generator │ │ └── form.php │ ├── inc │ │ ├── footer.php │ │ └── header.php │ ├── index.php │ ├── integration.php │ ├── integration │ │ ├── client-csharp.html │ │ ├── client-go.html │ │ ├── client-java.html │ │ ├── client-php.html │ │ ├── client-python.html │ │ ├── client-typescript.html │ │ ├── detail.php │ │ ├── server-csharp.html │ │ ├── server-java.html │ │ ├── server-php.html │ │ ├── server-python.html │ │ └── server-typescript.html │ └── specification.php └── typeschema.json └── src ├── Controller ├── Ecosystem.php ├── Generator.php ├── Index.php ├── Integration.php └── Specification.php ├── Model └── Generate.php └── Service ├── CaptchaVerifier.php └── TypeName.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: chriskapp 3 | patreon: fusio 4 | custom: https://www.paypal.me/fusioapi 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - pull_request 4 | - push 5 | defaults: 6 | run: 7 | working-directory: www 8 | jobs: 9 | psalm: 10 | name: Psalm 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | - name: Install PHP 16 | uses: shivammathur/setup-php@v2 17 | with: 18 | php-version: 8.4 19 | coverage: none 20 | - name: Composer install 21 | run: composer install --no-interaction --no-ansi --no-progress 22 | - name: Run Psalm 23 | run: vendor/bin/psalm --no-progress --show-info=false --stats 24 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | on: 3 | push: 4 | branches: 5 | - 'main' 6 | tags: 7 | - 'v*' 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: ${{ github.repository }} 11 | jobs: 12 | push_to_registry: 13 | name: Push Docker image to Docker Hub 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | packages: write 18 | steps: 19 | - name: Check out the repo 20 | uses: actions/checkout@v4 21 | - name: Log in to the Container registry 22 | uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 23 | with: 24 | registry: ${{ env.REGISTRY }} 25 | username: ${{ github.actor }} 26 | password: ${{ secrets.GITHUB_TOKEN }} 27 | - name: Extract metadata (tags, labels) for Docker 28 | id: meta 29 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 30 | with: 31 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 32 | - name: Build and push Docker image 33 | uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc 34 | with: 35 | context: . 36 | push: true 37 | tags: ${{ steps.meta.outputs.tags }} 38 | labels: ${{ steps.meta.outputs.labels }} 39 | -------------------------------------------------------------------------------- /.github/workflows/typehub.yml: -------------------------------------------------------------------------------- 1 | name: TypeHub 2 | on: 3 | push: 4 | branches: 5 | - 'main' 6 | tags: 7 | - 'v*' 8 | jobs: 9 | push_to_typehub: 10 | name: Push specification to TypeHub 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out the repo 14 | uses: actions/checkout@v4 15 | - name: TypeHub push 16 | uses: apioo/typehub-push-action@v0.1.0 17 | with: 18 | client_id: ${{ secrets.TYPEHUB_CLIENT_ID }} 19 | client_secret: ${{ secrets.TYPEHUB_CLIENT_SECRET }} 20 | directory: 'specification' 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.4-apache 2 | MAINTAINER Christoph Kappestein 3 | LABEL description="TypeAPI website" 4 | 5 | ENV COMPOSER_VERSION "2.5.5" 6 | ENV COMPOSER_SHA256 "566a6d1cf4be1cc3ac882d2a2a13817ffae54e60f5aa7c9137434810a5809ffc" 7 | 8 | ENV APACHE_DOCUMENT_ROOT "/var/www/html/public" 9 | 10 | # install default packages 11 | RUN apt-get update && apt-get -y install \ 12 | wget \ 13 | git \ 14 | cron \ 15 | libcurl3-dev \ 16 | libzip-dev \ 17 | libonig-dev 18 | 19 | # install php extensions 20 | RUN docker-php-ext-install \ 21 | bcmath \ 22 | curl \ 23 | zip \ 24 | mbstring 25 | 26 | # install composer 27 | RUN wget -O /usr/bin/composer https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar 28 | RUN echo "${COMPOSER_SHA256} */usr/bin/composer" | sha256sum -c - 29 | RUN chmod +x /usr/bin/composer 30 | 31 | # adjust apache config 32 | RUN sed -ri -e "s!/var/www/html!${APACHE_DOCUMENT_ROOT}!g" /etc/apache2/sites-available/*.conf 33 | RUN sed -ri -e "s!/var/www/!${APACHE_DOCUMENT_ROOT}!g" /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf 34 | RUN a2enmod rewrite 35 | 36 | # install app 37 | COPY www /var/www/html 38 | RUN cd /var/www/html && /usr/bin/composer install 39 | RUN chown -R www-data: /var/www/html 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017-2023 Christoph Kappestein 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TypeAPI 2 | 3 | ## About 4 | 5 | TypeAPI is an OpenAPI alternative to describe REST APIs for type-safe code generation. 6 | 7 | > Our goal is to remove the need to develop custom client SDKs for an REST API. 8 | > Once you have described your API with a TypeAPI specification you automatically get a ready to use client SDK. 9 | 10 | For more information please visit our website at: https://typeapi.org/ 11 | 12 | The TypeAPI meta specification which describes the specification itself is located at `./specification/typeapi.json`. 13 | We automatically push the specification to the [TypeHub platform](https://app.typehub.cloud/d/typehub/typeapi) where 14 | you can see also a rendered version of the specification and download the auto generated models. 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | typeapi: 4 | image: ghcr.io/apioo/typeapi:main 5 | environment: 6 | APP_URL: "https://typeapi.org" 7 | APP_ENV: "prod" 8 | APP_DEBUG: "off" 9 | restart: always 10 | ports: 11 | - "9700:80" 12 | -------------------------------------------------------------------------------- /specification/typeapi.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": { 3 | "typeschema": "typehub://typehub:typeschema@0.1.1" 4 | }, 5 | "definitions": { 6 | "Security": { 7 | "description": "", 8 | "type": "struct", 9 | "base": true, 10 | "properties": { 11 | "type": { 12 | "description": "The global security type of the API must be one of: httpBasic, httpBearer, apiKey or oauth2", 13 | "type": "string" 14 | } 15 | }, 16 | "discriminator": "type", 17 | "mapping": { 18 | "SecurityHttpBasic": "httpBasic", 19 | "SecurityHttpBearer": "httpBearer", 20 | "SecurityApiKey": "apiKey", 21 | "SecurityOAuth": "oauth2" 22 | } 23 | }, 24 | "SecurityHttpBasic": { 25 | "type": "struct", 26 | "parent": { 27 | "type": "reference", 28 | "target": "Security" 29 | } 30 | }, 31 | "SecurityHttpBearer": { 32 | "type": "struct", 33 | "parent": { 34 | "type": "reference", 35 | "target": "Security" 36 | } 37 | }, 38 | "SecurityApiKey": { 39 | "type": "struct", 40 | "parent": { 41 | "type": "reference", 42 | "target": "Security" 43 | }, 44 | "properties": { 45 | "name": { 46 | "description": "The name of the header or query parameter i.e. \"X-Api-Key\"", 47 | "type": "string" 48 | }, 49 | "in": { 50 | "description": "Must be either \"header\" or \"query\"", 51 | "type": "string" 52 | } 53 | } 54 | }, 55 | "SecurityOAuth": { 56 | "type": "struct", 57 | "parent": { 58 | "type": "reference", 59 | "target": "Security" 60 | }, 61 | "properties": { 62 | "tokenUrl": { 63 | "description": "The OAuth2 token endpoint", 64 | "type": "string" 65 | }, 66 | "authorizationUrl": { 67 | "description": "Optional the OAuth2 authorization endpoint", 68 | "type": "string" 69 | }, 70 | "scopes": { 71 | "description": "Optional OAuth2 scopes", 72 | "type": "array", 73 | "items": { 74 | "type": "string" 75 | } 76 | } 77 | } 78 | }, 79 | "Operation": { 80 | "description": "", 81 | "type": "struct", 82 | "properties": { 83 | "method": { 84 | "description": "The HTTP method which is associated with this operation, must be a valid HTTP method i.e. GET, POST, PUT etc.", 85 | "type": "string" 86 | }, 87 | "path": { 88 | "description": "The HTTP path which is associated with this operation. A path can also include variable path fragments i.e. /my/path/:year then you can map the variable year path fragment to a specific argument", 89 | "type": "string" 90 | }, 91 | "return": { 92 | "description": "The return type of this operation. The return has also an assigned HTTP success status code which is by default 200", 93 | "type": "reference", 94 | "target": "Response" 95 | }, 96 | "arguments": { 97 | "description": "All arguments provided to this operation. Each argument is mapped to a location from the HTTP request i.e. query or body", 98 | "type": "map", 99 | "schema": { 100 | "type": "reference", 101 | "target": "Argument" 102 | } 103 | }, 104 | "throws": { 105 | "description": "All exceptional states which can occur in case the operation fails. Each exception is assigned to an HTTP error status code", 106 | "type": "array", 107 | "schema": { 108 | "type": "reference", 109 | "target": "Response" 110 | } 111 | }, 112 | "description": { 113 | "description": "A short description of this operation. The generated code will include this description at the method so it is recommend to use simple alphanumeric characters and no new lines", 114 | "type": "string" 115 | }, 116 | "stability": { 117 | "description": "Indicates the stability of this operation: 0 - Deprecated, 1 - Experimental, 2 - Stable, 3 - Legacy. If not explicit provided the operation is by default experimental", 118 | "type": "integer" 119 | }, 120 | "security": { 121 | "description": "An array of scopes which are required to access this operation", 122 | "type": "array", 123 | "schema": { 124 | "type": "string" 125 | } 126 | }, 127 | "authorization": { 128 | "description": "Indicates whether this operation needs authorization, if set to false the client will not send an authorization header, default it is true", 129 | "type": "boolean" 130 | } 131 | } 132 | }, 133 | "Argument": { 134 | "description": "Describes arguments of the operation", 135 | "type": "struct", 136 | "properties": { 137 | "in": { 138 | "description": "The location where the value can be found either in the path, query, header or body. If you choose path, then your path must have a fitting variable path fragment", 139 | "type": "string" 140 | }, 141 | "schema": { 142 | "description": "Schema of the JSON payload", 143 | "type": "reference", 144 | "target": "typeschema:PropertyType" 145 | }, 146 | "contentType": { 147 | "description": "In case the data is not a JSON payload which you can describe with a schema you can select a content type", 148 | "type": "string" 149 | }, 150 | "name": { 151 | "description": "Optional the actual path, query or header name. If not provided the key of the argument map is used", 152 | "type": "string" 153 | } 154 | } 155 | }, 156 | "Response": { 157 | "description": "Describes the response of the operation", 158 | "type": "struct", 159 | "properties": { 160 | "code": { 161 | "description": "The associated HTTP response code. For error responses it is possible to use the 499, 599 or 999 status code to catch all errors", 162 | "type": "integer" 163 | }, 164 | "contentType": { 165 | "description": "In case the data is not a JSON payload which you can describe with a schema you can select a content type", 166 | "type": "string" 167 | }, 168 | "schema": { 169 | "description": "Schema of the JSON payload", 170 | "type": "reference", 171 | "target": "typeschema:PropertyType" 172 | } 173 | } 174 | }, 175 | "TypeAPI": { 176 | "description": "The TypeAPI Root", 177 | "type": "struct", 178 | "parent": { 179 | "type": "reference", 180 | "target": "typeschema:TypeSchema" 181 | }, 182 | "properties": { 183 | "baseUrl": { 184 | "description": "Optional the base url of the service, if provided the user does not need to provide a base url for your client", 185 | "type": "string" 186 | }, 187 | "security": { 188 | "description": "Describes the authorization mechanism which is used by your API", 189 | "type": "reference", 190 | "target": "Security" 191 | }, 192 | "operations": { 193 | "description": "A map of operations which are provided by the API. The key of the operation should be separated by a dot to group operations into logical units i.e. product.getAll or enterprise.product.execute", 194 | "type": "map", 195 | "schema": { 196 | "type": "reference", 197 | "target": "Operation" 198 | } 199 | } 200 | } 201 | } 202 | }, 203 | "root": "TypeAPI" 204 | } -------------------------------------------------------------------------------- /www/.env: -------------------------------------------------------------------------------- 1 | APP_URL="http://127.0.0.1/projects/typeapi/www/public" 2 | APP_ENV="prod" 3 | APP_DEBUG="off" 4 | APP_CONNECTION="mysql://root:test1234@localhost/psx" 5 | APP_MAILER="native://default" 6 | APP_RECAPTCHA_KEY="" 7 | APP_RECAPTCHA_SECRET="" 8 | APP_SDKGEN_CLIENT_ID="" 9 | APP_SDKGEN_CLIENT_SECRET="" 10 | -------------------------------------------------------------------------------- /www/build.php: -------------------------------------------------------------------------------- 1 | =8.1", 4 | "psx/framework": "^7.0" 5 | }, 6 | "require-dev": { 7 | "vimeo/psalm": "^6.0" 8 | }, 9 | "autoload": { 10 | "psr-4": { 11 | "App\\": "src/" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /www/configuration.php: -------------------------------------------------------------------------------- 1 | env('APP_RECAPTCHA_KEY')->string(), 9 | 'recaptcha_secret' => env('APP_RECAPTCHA_SECRET')->string(), 10 | 11 | 'sdkgen_client_id' => env('APP_SDKGEN_CLIENT_ID')->string(), 12 | 'sdkgen_client_secret' => env('APP_SDKGEN_CLIENT_SECRET')->string(), 13 | 14 | // The url to the psx public folder (i.e. http://api.acme.com or http://127.0.0.1/psx/public) 15 | 'psx_url' => env('APP_URL')->string(), 16 | 17 | // The input path 'index.php/' or '' if every request is served to the index.php file 18 | 'psx_dispatch' => '', 19 | 20 | // Defines the current environment i.e. prod or dev 21 | 'psx_env' => env('APP_ENV')->string()->default('prod'), 22 | 23 | // Whether the app runs in debug mode or not. If not error reporting is set to 0, also several caches are used if 24 | // the debug mode is false 25 | 'psx_debug' => env('APP_DEBUG')->bool()->default(false), 26 | 27 | // Database parameters which are used for the doctrine DBAL connection 28 | // https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html 29 | 'psx_connection' => env('APP_CONNECTION')->string(), 30 | 31 | // Mailer connection which is used to send mails 32 | // https://symfony.com/doc/current/mailer.html#using-built-in-transports 33 | 'psx_mailer' => env('APP_MAILER')->string(), 34 | 35 | // The log level 36 | 'psx_log_level' => Logger::ERROR, 37 | 38 | // Folder locations 39 | 'psx_path_cache' => __DIR__ . '/cache', 40 | 'psx_path_src' => __DIR__ . '/src', 41 | 'psx_path_log' => __DIR__ . '/log', 42 | 43 | // Supported writers 44 | 'psx_supported_writer' => [ 45 | \PSX\Data\Writer\Json::class, 46 | \PSX\Data\Writer\Jsonp::class, 47 | \PSX\Data\Writer\Jsonx::class, 48 | ], 49 | 50 | ]; 51 | -------------------------------------------------------------------------------- /www/container.php: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /www/public/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteBase / 3 | RewriteCond %{REQUEST_FILENAME} !-f 4 | RewriteCond %{REQUEST_FILENAME} !-d 5 | RewriteRule (.*) /index.php/$1 [L] 6 | -------------------------------------------------------------------------------- /www/public/css/app.css: -------------------------------------------------------------------------------- 1 | 2 | textarea { 3 | font-family: monospace; 4 | } 5 | 6 | .typeapi-header { 7 | background: url('../img/header_bg.png') no-repeat top center; 8 | background-size: cover; 9 | } 10 | 11 | pre { 12 | padding: 0!important; 13 | max-height: 400px; 14 | } 15 | 16 | .typeschema-edit { 17 | margin-top:32px; 18 | margin-bottom:32px; 19 | text-align:right; 20 | } 21 | 22 | /* psx schema */ 23 | .psx-object { 24 | margin:0; 25 | margin-bottom:8px; 26 | border:1px solid #ccc; 27 | } 28 | 29 | .psx-object > h1 { 30 | font-weight:bold; 31 | font-size:1em; 32 | margin:0; 33 | padding: 10px 15px; 34 | padding-left: 8px; 35 | border-bottom: 1px solid #999; 36 | color: #333; 37 | background-color: #f5f5f5; 38 | background-repeat: repeat-x; 39 | background-image: -webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%); 40 | background-image: -o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%); 41 | background-image: -webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8)); 42 | background-image: linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%); 43 | } 44 | 45 | .psx-object-description { 46 | margin:8px 0; 47 | padding:0 8px; 48 | } 49 | 50 | .psx-object-json { 51 | border-radius:0; 52 | border:0; 53 | margin:0; 54 | padding:8px; 55 | background-color:#F0F0F0; 56 | } 57 | 58 | .psx-object-json-key { 59 | color:#880000; 60 | } 61 | 62 | .psx-object-json-pun { 63 | color:#65b042; 64 | } 65 | 66 | .psx-object-properties { 67 | } 68 | 69 | .psx-property-type { 70 | font-family:monospace; 71 | font-weight:bold; 72 | } 73 | 74 | .psx-property-name { 75 | font-family:monospace; 76 | font-weight:bold; 77 | } 78 | 79 | .psx-property-description { 80 | margin-top:8px; 81 | font-size:0.9em; 82 | } 83 | 84 | .psx-property-constraint { 85 | margin-top:8px; 86 | font-size:0.9em; 87 | } 88 | 89 | .psx-property-required { 90 | border-bottom:1px dotted #999; 91 | } 92 | 93 | .psx-out { 94 | overflow-x:auto; 95 | } 96 | 97 | .apioo-brand { 98 | background-color:#000; 99 | text-align: center; 100 | color:#eee; 101 | padding:24px; 102 | font-weight:bold; 103 | } 104 | 105 | .apioo-brand a { 106 | color:#00d1b2; 107 | text-decoration:none; 108 | } 109 | -------------------------------------------------------------------------------- /www/public/css/highlight.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline} 2 | -------------------------------------------------------------------------------- /www/public/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apioo/typeapi/f55557b6cb82bdf915a9f6a127b6b74f41bbd476/www/public/img/favicon.ico -------------------------------------------------------------------------------- /www/public/img/github-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apioo/typeapi/f55557b6cb82bdf915a9f6a127b6b74f41bbd476/www/public/img/github-32.png -------------------------------------------------------------------------------- /www/public/img/header_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apioo/typeapi/f55557b6cb82bdf915a9f6a127b6b74f41bbd476/www/public/img/header_bg.png -------------------------------------------------------------------------------- /www/public/index.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Copyright 2010-2023 Christoph Kappestein 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | require_once(__DIR__ . '/../vendor/autoload.php'); 22 | 23 | $container = require_once(__DIR__ . '/../container.php'); 24 | 25 | $engine = new \PSX\Engine\WebServer\Engine($container->getParameter('psx_url')); 26 | $dispatcher = $container->get(\PSX\Engine\DispatchInterface::class); 27 | $environment = new \PSX\Framework\Environment\Environment($dispatcher, $engine); 28 | 29 | return $environment->serve(); 30 | -------------------------------------------------------------------------------- /www/public/js/app.js: -------------------------------------------------------------------------------- 1 | 2 | document.addEventListener('DOMContentLoaded', function () { 3 | document.querySelectorAll('.navbar-burger').forEach( function (el) { 4 | el.addEventListener('click', function () { 5 | var target = el.dataset.target; 6 | var targetEl = document.getElementById(target); 7 | el.classList.toggle('is-active'); 8 | targetEl.classList.toggle('is-active'); 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /www/public/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v4.6.2 (https://getbootstrap.com/) 3 | * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery)}(this,(function(t,e){"use strict";function n(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var i=n(e);function o(t,e){for(var n=0;n=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};u.jQueryDetection(),i.default.fn.emulateTransitionEnd=function(t){var e=this,n=!1;return i.default(this).one(u.TRANSITION_END,(function(){n=!0})),setTimeout((function(){n||u.triggerTransitionEnd(e)}),t),this},i.default.event.special[u.TRANSITION_END]={bindType:l,delegateType:l,handle:function(t){if(i.default(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var f="bs.alert",d=i.default.fn.alert,c=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){i.default.removeData(this._element,f),this._element=null},e._getRootElement=function(t){var e=u.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=i.default(t).closest(".alert")[0]),n},e._triggerCloseEvent=function(t){var e=i.default.Event("close.bs.alert");return i.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(i.default(t).removeClass("show"),i.default(t).hasClass("fade")){var n=u.getTransitionDurationFromElement(t);i.default(t).one(u.TRANSITION_END,(function(n){return e._destroyElement(t,n)})).emulateTransitionEnd(n)}else this._destroyElement(t)},e._destroyElement=function(t){i.default(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data(f);o||(o=new t(this),n.data(f,o)),"close"===e&&o[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},r(t,null,[{key:"VERSION",get:function(){return"4.6.2"}}]),t}();i.default(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',c._handleDismiss(new c)),i.default.fn.alert=c._jQueryInterface,i.default.fn.alert.Constructor=c,i.default.fn.alert.noConflict=function(){return i.default.fn.alert=d,c._jQueryInterface};var h="bs.button",p=i.default.fn.button,m="active",g='[data-toggle^="button"]',_='input:not([type="hidden"])',v=".btn",b=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=i.default(this._element).closest('[data-toggle="buttons"]')[0];if(n){var o=this._element.querySelector(_);if(o){if("radio"===o.type)if(o.checked&&this._element.classList.contains(m))t=!1;else{var r=n.querySelector(".active");r&&i.default(r).removeClass(m)}t&&("checkbox"!==o.type&&"radio"!==o.type||(o.checked=!this._element.classList.contains(m)),this.shouldAvoidTriggerChange||i.default(o).trigger("change")),o.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains(m)),t&&i.default(this._element).toggleClass(m))},e.dispose=function(){i.default.removeData(this._element,h),this._element=null},t._jQueryInterface=function(e,n){return this.each((function(){var o=i.default(this),r=o.data(h);r||(r=new t(this),o.data(h,r)),r.shouldAvoidTriggerChange=n,"toggle"===e&&r[e]()}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.2"}}]),t}();i.default(document).on("click.bs.button.data-api",g,(function(t){var e=t.target,n=e;if(i.default(e).hasClass("btn")||(e=i.default(e).closest(v)[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var o=e.querySelector(_);if(o&&(o.hasAttribute("disabled")||o.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==n.tagName&&"LABEL"===e.tagName||b._jQueryInterface.call(i.default(e),"toggle","INPUT"===n.tagName)}})).on("focus.bs.button.data-api blur.bs.button.data-api",g,(function(t){var e=i.default(t.target).closest(v)[0];i.default(e).toggleClass("focus",/^focus(in)?$/.test(t.type))})),i.default(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide(C)},e.nextWhenVisible=function(){var t=i.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide(S)},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(u.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(D);var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)i.default(this._element).one(N,(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var o=t>n?C:S;this._slide(o,this._items[t])}},e.dispose=function(){i.default(this._element).off(".bs.carousel"),i.default.removeData(this._element,E),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=a({},A,t),u.typeCheckConfig(y,t,k),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&i.default(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&i.default(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&I[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},n=function(e){t._pointerEvent&&I[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};i.default(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(i.default(this._element).on("pointerdown.bs.carousel",(function(t){return e(t)})),i.default(this._element).on("pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(i.default(this._element).on("touchstart.bs.carousel",(function(t){return e(t)})),i.default(this._element).on("touchmove.bs.carousel",(function(e){return function(e){t.touchDeltaX=e.originalEvent.touches&&e.originalEvent.touches.length>1?0:e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),i.default(this._element).on("touchend.bs.carousel",(function(t){return n(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var n=t===C,i=t===S,o=this._getItemIndex(e),r=this._items.length-1;if((i&&0===o||n&&o===r)&&!this._config.wrap)return e;var a=(o+(t===S?-1:1))%this._items.length;return-1===a?this._items[this._items.length-1]:this._items[a]},e._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),o=this._getItemIndex(this._element.querySelector(D)),r=i.default.Event("slide.bs.carousel",{relatedTarget:t,direction:e,from:o,to:n});return i.default(this._element).trigger(r),r},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));i.default(e).removeClass(T);var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&i.default(n).addClass(T)}},e._updateInterval=function(){var t=this._activeElement||this._element.querySelector(D);if(t){var e=parseInt(t.getAttribute("data-interval"),10);e?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=e):this._config.interval=this._config.defaultInterval||this._config.interval}},e._slide=function(t,e){var n,o,r,a=this,s=this._element.querySelector(D),l=this._getItemIndex(s),f=e||s&&this._getItemByDirection(t,s),d=this._getItemIndex(f),c=Boolean(this._interval);if(t===C?(n="carousel-item-left",o="carousel-item-next",r="left"):(n="carousel-item-right",o="carousel-item-prev",r="right"),f&&i.default(f).hasClass(T))this._isSliding=!1;else if(!this._triggerSlideEvent(f,r).isDefaultPrevented()&&s&&f){this._isSliding=!0,c&&this.pause(),this._setActiveIndicatorElement(f),this._activeElement=f;var h=i.default.Event(N,{relatedTarget:f,direction:r,from:l,to:d});if(i.default(this._element).hasClass("slide")){i.default(f).addClass(o),u.reflow(f),i.default(s).addClass(n),i.default(f).addClass(n);var p=u.getTransitionDurationFromElement(s);i.default(s).one(u.TRANSITION_END,(function(){i.default(f).removeClass(n+" "+o).addClass(T),i.default(s).removeClass("active "+o+" "+n),a._isSliding=!1,setTimeout((function(){return i.default(a._element).trigger(h)}),0)})).emulateTransitionEnd(p)}else i.default(s).removeClass(T),i.default(f).addClass(T),this._isSliding=!1,i.default(this._element).trigger(h);c&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data(E),o=a({},A,i.default(this).data());"object"==typeof e&&(o=a({},o,e));var r="string"==typeof e?e:o.slide;if(n||(n=new t(this,o),i.default(this).data(E,n)),"number"==typeof e)n.to(e);else if("string"==typeof r){if("undefined"==typeof n[r])throw new TypeError('No method named "'+r+'"');n[r]()}else o.interval&&o.ride&&(n.pause(),n.cycle())}))},t._dataApiClickHandler=function(e){var n=u.getSelectorFromElement(this);if(n){var o=i.default(n)[0];if(o&&i.default(o).hasClass("carousel")){var r=a({},i.default(o).data(),i.default(this).data()),s=this.getAttribute("data-slide-to");s&&(r.interval=!1),t._jQueryInterface.call(i.default(o),r),s&&i.default(o).data(E).to(s),e.preventDefault()}}},r(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return A}}]),t}();i.default(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",O._dataApiClickHandler),i.default(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),e=0,n=t.length;e0&&(this._selector=a,this._triggerArray.push(r))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){i.default(this._element).hasClass(P)?this.hide():this.show()},e.show=function(){var e,n,o=this;if(!(this._isTransitioning||i.default(this._element).hasClass(P)||(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof o._config.parent?t.getAttribute("data-parent")===o._config.parent:t.classList.contains(F)}))).length&&(e=null),e&&(n=i.default(e).not(this._selector).data(j))&&n._isTransitioning))){var r=i.default.Event("show.bs.collapse");if(i.default(this._element).trigger(r),!r.isDefaultPrevented()){e&&(t._jQueryInterface.call(i.default(e).not(this._selector),"hide"),n||i.default(e).data(j,null));var a=this._getDimension();i.default(this._element).removeClass(F).addClass(R),this._element.style[a]=0,this._triggerArray.length&&i.default(this._triggerArray).removeClass(B).attr("aria-expanded",!0),this.setTransitioning(!0);var s="scroll"+(a[0].toUpperCase()+a.slice(1)),l=u.getTransitionDurationFromElement(this._element);i.default(this._element).one(u.TRANSITION_END,(function(){i.default(o._element).removeClass(R).addClass("collapse show"),o._element.style[a]="",o.setTransitioning(!1),i.default(o._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(l),this._element.style[a]=this._element[s]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&i.default(this._element).hasClass(P)){var e=i.default.Event("hide.bs.collapse");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",u.reflow(this._element),i.default(this._element).addClass(R).removeClass("collapse show");var o=this._triggerArray.length;if(o>0)for(var r=0;r=0)return 1;return 0}(),Y=U&&window.Promise?function(t){var e=!1;return function(){e||(e=!0,window.Promise.resolve().then((function(){e=!1,t()})))}}:function(t){var e=!1;return function(){e||(e=!0,setTimeout((function(){e=!1,t()}),V))}};function z(t){return t&&"[object Function]"==={}.toString.call(t)}function K(t,e){if(1!==t.nodeType)return[];var n=t.ownerDocument.defaultView.getComputedStyle(t,null);return e?n[e]:n}function X(t){return"HTML"===t.nodeName?t:t.parentNode||t.host}function G(t){if(!t)return document.body;switch(t.nodeName){case"HTML":case"BODY":return t.ownerDocument.body;case"#document":return t.body}var e=K(t),n=e.overflow,i=e.overflowX,o=e.overflowY;return/(auto|scroll|overlay)/.test(n+o+i)?t:G(X(t))}function $(t){return t&&t.referenceNode?t.referenceNode:t}var J=U&&!(!window.MSInputMethodContext||!document.documentMode),Z=U&&/MSIE 10/.test(navigator.userAgent);function tt(t){return 11===t?J:10===t?Z:J||Z}function et(t){if(!t)return document.documentElement;for(var e=tt(10)?document.body:null,n=t.offsetParent||null;n===e&&t.nextElementSibling;)n=(t=t.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&"BODY"!==i&&"HTML"!==i?-1!==["TH","TD","TABLE"].indexOf(n.nodeName)&&"static"===K(n,"position")?et(n):n:t?t.ownerDocument.documentElement:document.documentElement}function nt(t){return null!==t.parentNode?nt(t.parentNode):t}function it(t,e){if(!(t&&t.nodeType&&e&&e.nodeType))return document.documentElement;var n=t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING,i=n?t:e,o=n?e:t,r=document.createRange();r.setStart(i,0),r.setEnd(o,0);var a,s,l=r.commonAncestorContainer;if(t!==l&&e!==l||i.contains(o))return"BODY"===(s=(a=l).nodeName)||"HTML"!==s&&et(a.firstElementChild)!==a?et(l):l;var u=nt(t);return u.host?it(u.host,e):it(t,nt(e).host)}function ot(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"top",n="top"===e?"scrollTop":"scrollLeft",i=t.nodeName;if("BODY"===i||"HTML"===i){var o=t.ownerDocument.documentElement,r=t.ownerDocument.scrollingElement||o;return r[n]}return t[n]}function rt(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=ot(e,"top"),o=ot(e,"left"),r=n?-1:1;return t.top+=i*r,t.bottom+=i*r,t.left+=o*r,t.right+=o*r,t}function at(t,e){var n="x"===e?"Left":"Top",i="Left"===n?"Right":"Bottom";return parseFloat(t["border"+n+"Width"])+parseFloat(t["border"+i+"Width"])}function st(t,e,n,i){return Math.max(e["offset"+t],e["scroll"+t],n["client"+t],n["offset"+t],n["scroll"+t],tt(10)?parseInt(n["offset"+t])+parseInt(i["margin"+("Height"===t?"Top":"Left")])+parseInt(i["margin"+("Height"===t?"Bottom":"Right")]):0)}function lt(t){var e=t.body,n=t.documentElement,i=tt(10)&&getComputedStyle(n);return{height:st("Height",e,n,i),width:st("Width",e,n,i)}}var ut=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},ft=function(){function t(t,e){for(var n=0;n2&&void 0!==arguments[2]&&arguments[2],i=tt(10),o="HTML"===e.nodeName,r=pt(t),a=pt(e),s=G(t),l=K(e),u=parseFloat(l.borderTopWidth),f=parseFloat(l.borderLeftWidth);n&&o&&(a.top=Math.max(a.top,0),a.left=Math.max(a.left,0));var d=ht({top:r.top-a.top-u,left:r.left-a.left-f,width:r.width,height:r.height});if(d.marginTop=0,d.marginLeft=0,!i&&o){var c=parseFloat(l.marginTop),h=parseFloat(l.marginLeft);d.top-=u-c,d.bottom-=u-c,d.left-=f-h,d.right-=f-h,d.marginTop=c,d.marginLeft=h}return(i&&!n?e.contains(s):e===s&&"BODY"!==s.nodeName)&&(d=rt(d,e)),d}function gt(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=t.ownerDocument.documentElement,i=mt(t,n),o=Math.max(n.clientWidth,window.innerWidth||0),r=Math.max(n.clientHeight,window.innerHeight||0),a=e?0:ot(n),s=e?0:ot(n,"left"),l={top:a-i.top+i.marginTop,left:s-i.left+i.marginLeft,width:o,height:r};return ht(l)}function _t(t){var e=t.nodeName;if("BODY"===e||"HTML"===e)return!1;if("fixed"===K(t,"position"))return!0;var n=X(t);return!!n&&_t(n)}function vt(t){if(!t||!t.parentElement||tt())return document.documentElement;for(var e=t.parentElement;e&&"none"===K(e,"transform");)e=e.parentElement;return e||document.documentElement}function bt(t,e,n,i){var o=arguments.length>4&&void 0!==arguments[4]&&arguments[4],r={top:0,left:0},a=o?vt(t):it(t,$(e));if("viewport"===i)r=gt(a,o);else{var s=void 0;"scrollParent"===i?"BODY"===(s=G(X(e))).nodeName&&(s=t.ownerDocument.documentElement):s="window"===i?t.ownerDocument.documentElement:i;var l=mt(s,a,o);if("HTML"!==s.nodeName||_t(a))r=l;else{var u=lt(t.ownerDocument),f=u.height,d=u.width;r.top+=l.top-l.marginTop,r.bottom=f+l.top,r.left+=l.left-l.marginLeft,r.right=d+l.left}}var c="number"==typeof(n=n||0);return r.left+=c?n:n.left||0,r.top+=c?n:n.top||0,r.right-=c?n:n.right||0,r.bottom-=c?n:n.bottom||0,r}function yt(t){return t.width*t.height}function Et(t,e,n,i,o){var r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;if(-1===t.indexOf("auto"))return t;var a=bt(n,i,r,o),s={top:{width:a.width,height:e.top-a.top},right:{width:a.right-e.right,height:a.height},bottom:{width:a.width,height:a.bottom-e.bottom},left:{width:e.left-a.left,height:a.height}},l=Object.keys(s).map((function(t){return ct({key:t},s[t],{area:yt(s[t])})})).sort((function(t,e){return e.area-t.area})),u=l.filter((function(t){var e=t.width,i=t.height;return e>=n.clientWidth&&i>=n.clientHeight})),f=u.length>0?u[0].key:l[0].key,d=t.split("-")[1];return f+(d?"-"+d:"")}function wt(t,e,n){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=i?vt(e):it(e,$(n));return mt(n,o,i)}function Tt(t){var e=t.ownerDocument.defaultView.getComputedStyle(t),n=parseFloat(e.marginTop||0)+parseFloat(e.marginBottom||0),i=parseFloat(e.marginLeft||0)+parseFloat(e.marginRight||0);return{width:t.offsetWidth+i,height:t.offsetHeight+n}}function Ct(t){var e={left:"right",right:"left",bottom:"top",top:"bottom"};return t.replace(/left|right|bottom|top/g,(function(t){return e[t]}))}function St(t,e,n){n=n.split("-")[0];var i=Tt(t),o={width:i.width,height:i.height},r=-1!==["right","left"].indexOf(n),a=r?"top":"left",s=r?"left":"top",l=r?"height":"width",u=r?"width":"height";return o[a]=e[a]+e[l]/2-i[l]/2,o[s]=n===s?e[s]-i[u]:e[Ct(s)],o}function Nt(t,e){return Array.prototype.find?t.find(e):t.filter(e)[0]}function Dt(t,e,n){return(void 0===n?t:t.slice(0,function(t,e,n){if(Array.prototype.findIndex)return t.findIndex((function(t){return t.name===n}));var i=Nt(t,(function(t){return t.name===n}));return t.indexOf(i)}(t,0,n))).forEach((function(t){t.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var n=t.function||t.fn;t.enabled&&z(n)&&(e.offsets.popper=ht(e.offsets.popper),e.offsets.reference=ht(e.offsets.reference),e=n(e,t))})),e}function At(){if(!this.state.isDestroyed){var t={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};t.offsets.reference=wt(this.state,this.popper,this.reference,this.options.positionFixed),t.placement=Et(this.options.placement,t.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),t.originalPlacement=t.placement,t.positionFixed=this.options.positionFixed,t.offsets.popper=St(this.popper,t.offsets.reference,t.placement),t.offsets.popper.position=this.options.positionFixed?"fixed":"absolute",t=Dt(this.modifiers,t),this.state.isCreated?this.options.onUpdate(t):(this.state.isCreated=!0,this.options.onCreate(t))}}function kt(t,e){return t.some((function(t){var n=t.name;return t.enabled&&n===e}))}function It(t){for(var e=[!1,"ms","Webkit","Moz","O"],n=t.charAt(0).toUpperCase()+t.slice(1),i=0;i1&&void 0!==arguments[1]&&arguments[1],n=Qt.indexOf(t),i=Qt.slice(n+1).concat(Qt.slice(0,n));return e?i.reverse():i}var Ut={placement:"bottom",positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(t){var e=t.placement,n=e.split("-")[0],i=e.split("-")[1];if(i){var o=t.offsets,r=o.reference,a=o.popper,s=-1!==["bottom","top"].indexOf(n),l=s?"left":"top",u=s?"width":"height",f={start:dt({},l,r[l]),end:dt({},l,r[l]+r[u]-a[u])};t.offsets.popper=ct({},a,f[i])}return t}},offset:{order:200,enabled:!0,fn:function(t,e){var n,i=e.offset,o=t.placement,r=t.offsets,a=r.popper,s=r.reference,l=o.split("-")[0];return n=Rt(+i)?[+i,0]:function(t,e,n,i){var o=[0,0],r=-1!==["right","left"].indexOf(i),a=t.split(/(\+|\-)/).map((function(t){return t.trim()})),s=a.indexOf(Nt(a,(function(t){return-1!==t.search(/,|\s/)})));a[s]&&-1===a[s].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var l=/\s*,\s*|\s+/,u=-1!==s?[a.slice(0,s).concat([a[s].split(l)[0]]),[a[s].split(l)[1]].concat(a.slice(s+1))]:[a];return u=u.map((function(t,i){var o=(1===i?!r:r)?"height":"width",a=!1;return t.reduce((function(t,e){return""===t[t.length-1]&&-1!==["+","-"].indexOf(e)?(t[t.length-1]=e,a=!0,t):a?(t[t.length-1]+=e,a=!1,t):t.concat(e)}),[]).map((function(t){return function(t,e,n,i){var o=t.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+o[1],a=o[2];return r?0===a.indexOf("%")?ht("%p"===a?n:i)[e]/100*r:"vh"===a||"vw"===a?("vh"===a?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*r:r:t}(t,o,e,n)}))})),u.forEach((function(t,e){t.forEach((function(n,i){Rt(n)&&(o[e]+=n*("-"===t[i-1]?-1:1))}))})),o}(i,a,s,l),"left"===l?(a.top+=n[0],a.left-=n[1]):"right"===l?(a.top+=n[0],a.left+=n[1]):"top"===l?(a.left+=n[0],a.top-=n[1]):"bottom"===l&&(a.left+=n[0],a.top+=n[1]),t.popper=a,t},offset:0},preventOverflow:{order:300,enabled:!0,fn:function(t,e){var n=e.boundariesElement||et(t.instance.popper);t.instance.reference===n&&(n=et(n));var i=It("transform"),o=t.instance.popper.style,r=o.top,a=o.left,s=o[i];o.top="",o.left="",o[i]="";var l=bt(t.instance.popper,t.instance.reference,e.padding,n,t.positionFixed);o.top=r,o.left=a,o[i]=s,e.boundaries=l;var u=e.priority,f=t.offsets.popper,d={primary:function(t){var n=f[t];return f[t]l[t]&&!e.escapeWithReference&&(i=Math.min(f[n],l[t]-("right"===t?f.width:f.height))),dt({},n,i)}};return u.forEach((function(t){var e=-1!==["left","top"].indexOf(t)?"primary":"secondary";f=ct({},f,d[e](t))})),t.offsets.popper=f,t},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(t){var e=t.offsets,n=e.popper,i=e.reference,o=t.placement.split("-")[0],r=Math.floor,a=-1!==["top","bottom"].indexOf(o),s=a?"right":"bottom",l=a?"left":"top",u=a?"width":"height";return n[s]r(i[s])&&(t.offsets.popper[l]=r(i[s])),t}},arrow:{order:500,enabled:!0,fn:function(t,e){var n;if(!Mt(t.instance.modifiers,"arrow","keepTogether"))return t;var i=e.element;if("string"==typeof i){if(!(i=t.instance.popper.querySelector(i)))return t}else if(!t.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),t;var o=t.placement.split("-")[0],r=t.offsets,a=r.popper,s=r.reference,l=-1!==["left","right"].indexOf(o),u=l?"height":"width",f=l?"Top":"Left",d=f.toLowerCase(),c=l?"left":"top",h=l?"bottom":"right",p=Tt(i)[u];s[h]-pa[h]&&(t.offsets.popper[d]+=s[d]+p-a[h]),t.offsets.popper=ht(t.offsets.popper);var m=s[d]+s[u]/2-p/2,g=K(t.instance.popper),_=parseFloat(g["margin"+f]),v=parseFloat(g["border"+f+"Width"]),b=m-t.offsets.popper[d]-_-v;return b=Math.max(Math.min(a[u]-p,b),0),t.arrowElement=i,t.offsets.arrow=(dt(n={},d,Math.round(b)),dt(n,c,""),n),t},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(t,e){if(kt(t.instance.modifiers,"inner"))return t;if(t.flipped&&t.placement===t.originalPlacement)return t;var n=bt(t.instance.popper,t.instance.reference,e.padding,e.boundariesElement,t.positionFixed),i=t.placement.split("-")[0],o=Ct(i),r=t.placement.split("-")[1]||"",a=[];switch(e.behavior){case"flip":a=[i,o];break;case"clockwise":a=Wt(i);break;case"counterclockwise":a=Wt(i,!0);break;default:a=e.behavior}return a.forEach((function(s,l){if(i!==s||a.length===l+1)return t;i=t.placement.split("-")[0],o=Ct(i);var u=t.offsets.popper,f=t.offsets.reference,d=Math.floor,c="left"===i&&d(u.right)>d(f.left)||"right"===i&&d(u.left)d(f.top)||"bottom"===i&&d(u.top)d(n.right),m=d(u.top)d(n.bottom),_="left"===i&&h||"right"===i&&p||"top"===i&&m||"bottom"===i&&g,v=-1!==["top","bottom"].indexOf(i),b=!!e.flipVariations&&(v&&"start"===r&&h||v&&"end"===r&&p||!v&&"start"===r&&m||!v&&"end"===r&&g),y=!!e.flipVariationsByContent&&(v&&"start"===r&&p||v&&"end"===r&&h||!v&&"start"===r&&g||!v&&"end"===r&&m),E=b||y;(c||_||E)&&(t.flipped=!0,(c||_)&&(i=a[l+1]),E&&(r=function(t){return"end"===t?"start":"start"===t?"end":t}(r)),t.placement=i+(r?"-"+r:""),t.offsets.popper=ct({},t.offsets.popper,St(t.instance.popper,t.offsets.reference,t.placement)),t=Dt(t.instance.modifiers,t,"flip"))})),t},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(t){var e=t.placement,n=e.split("-")[0],i=t.offsets,o=i.popper,r=i.reference,a=-1!==["left","right"].indexOf(n),s=-1===["top","left"].indexOf(n);return o[a?"left":"top"]=r[n]-(s?o[a?"width":"height"]:0),t.placement=Ct(e),t.offsets.popper=ht(o),t}},hide:{order:800,enabled:!0,fn:function(t){if(!Mt(t.instance.modifiers,"hide","preventOverflow"))return t;var e=t.offsets.reference,n=Nt(t.instance.modifiers,(function(t){return"preventOverflow"===t.name})).boundaries;if(e.bottomn.right||e.top>n.bottom||e.right2&&void 0!==arguments[2]?arguments[2]:{};ut(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=Y(this.update.bind(this)),this.options=ct({},t.Defaults,o),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=e&&e.jquery?e[0]:e,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(ct({},t.Defaults.modifiers,o.modifiers)).forEach((function(e){i.options.modifiers[e]=ct({},t.Defaults.modifiers[e]||{},o.modifiers?o.modifiers[e]:{})})),this.modifiers=Object.keys(this.options.modifiers).map((function(t){return ct({name:t},i.options.modifiers[t])})).sort((function(t,e){return t.order-e.order})),this.modifiers.forEach((function(t){t.enabled&&z(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)})),this.update();var r=this.options.eventsEnabled;r&&this.enableEventListeners(),this.state.eventsEnabled=r}return ft(t,[{key:"update",value:function(){return At.call(this)}},{key:"destroy",value:function(){return Ot.call(this)}},{key:"enableEventListeners",value:function(){return Pt.call(this)}},{key:"disableEventListeners",value:function(){return Ft.call(this)}}]),t}();Vt.Utils=("undefined"!=typeof window?window:global).PopperUtils,Vt.placements=qt,Vt.Defaults=Ut;var Yt=Vt,zt="dropdown",Kt="bs.dropdown",Xt=i.default.fn[zt],Gt=new RegExp("38|40|27"),$t="disabled",Jt="show",Zt="dropdown-menu-right",te="hide.bs.dropdown",ee="hidden.bs.dropdown",ne="click.bs.dropdown.data-api",ie="keydown.bs.dropdown.data-api",oe='[data-toggle="dropdown"]',re=".dropdown-menu",ae={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic",popperConfig:null},se={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string",popperConfig:"(null|object)"},le=function(){function t(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var e=t.prototype;return e.toggle=function(){if(!this._element.disabled&&!i.default(this._element).hasClass($t)){var e=i.default(this._menu).hasClass(Jt);t._clearMenus(),e||this.show(!0)}},e.show=function(e){if(void 0===e&&(e=!1),!(this._element.disabled||i.default(this._element).hasClass($t)||i.default(this._menu).hasClass(Jt))){var n={relatedTarget:this._element},o=i.default.Event("show.bs.dropdown",n),r=t._getParentFromElement(this._element);if(i.default(r).trigger(o),!o.isDefaultPrevented()){if(!this._inNavbar&&e){if("undefined"==typeof Yt)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");var a=this._element;"parent"===this._config.reference?a=r:u.isElement(this._config.reference)&&(a=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(a=this._config.reference[0])),"scrollParent"!==this._config.boundary&&i.default(r).addClass("position-static"),this._popper=new Yt(a,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===i.default(r).closest(".navbar-nav").length&&i.default(document.body).children().on("mouseover",null,i.default.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),i.default(this._menu).toggleClass(Jt),i.default(r).toggleClass(Jt).trigger(i.default.Event("shown.bs.dropdown",n))}}},e.hide=function(){if(!this._element.disabled&&!i.default(this._element).hasClass($t)&&i.default(this._menu).hasClass(Jt)){var e={relatedTarget:this._element},n=i.default.Event(te,e),o=t._getParentFromElement(this._element);i.default(o).trigger(n),n.isDefaultPrevented()||(this._popper&&this._popper.destroy(),i.default(this._menu).toggleClass(Jt),i.default(o).toggleClass(Jt).trigger(i.default.Event(ee,e)))}},e.dispose=function(){i.default.removeData(this._element,Kt),i.default(this._element).off(".bs.dropdown"),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},e.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},e._addEventListeners=function(){var t=this;i.default(this._element).on("click.bs.dropdown",(function(e){e.preventDefault(),e.stopPropagation(),t.toggle()}))},e._getConfig=function(t){return t=a({},this.constructor.Default,i.default(this._element).data(),t),u.typeCheckConfig(zt,t,this.constructor.DefaultType),t},e._getMenuElement=function(){if(!this._menu){var e=t._getParentFromElement(this._element);e&&(this._menu=e.querySelector(re))}return this._menu},e._getPlacement=function(){var t=i.default(this._element.parentNode),e="bottom-start";return t.hasClass("dropup")?e=i.default(this._menu).hasClass(Zt)?"top-end":"top-start":t.hasClass("dropright")?e="right-start":t.hasClass("dropleft")?e="left-start":i.default(this._menu).hasClass(Zt)&&(e="bottom-end"),e},e._detectNavbar=function(){return i.default(this._element).closest(".navbar").length>0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=a({},e.offsets,t._config.offset(e.offsets,t._element)),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),a({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this).data(Kt);if(n||(n=new t(this,"object"==typeof e?e:null),i.default(this).data(Kt,n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},t._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=[].slice.call(document.querySelectorAll(oe)),o=0,r=n.length;o0&&a--,40===e.which&&adocument.documentElement.clientHeight;n||(this._element.style.overflowY="hidden"),this._element.classList.add(pe);var o=u.getTransitionDurationFromElement(this._dialog);i.default(this._element).off(u.TRANSITION_END),i.default(this._element).one(u.TRANSITION_END,(function(){t._element.classList.remove(pe),n||i.default(t._element).one(u.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,o)})).emulateTransitionEnd(o),this._element.focus()}},e._showElement=function(t){var e=this,n=i.default(this._element).hasClass(ce),o=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),i.default(this._dialog).hasClass("modal-dialog-scrollable")&&o?o.scrollTop=0:this._element.scrollTop=0,n&&u.reflow(this._element),i.default(this._element).addClass(he),this._config.focus&&this._enforceFocus();var r=i.default.Event("shown.bs.modal",{relatedTarget:t}),a=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,i.default(e._element).trigger(r)};if(n){var s=u.getTransitionDurationFromElement(this._dialog);i.default(this._dialog).one(u.TRANSITION_END,a).emulateTransitionEnd(s)}else a()},e._enforceFocus=function(){var t=this;i.default(document).off(_e).on(_e,(function(e){document!==e.target&&t._element!==e.target&&0===i.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?i.default(this._element).on(ye,(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||i.default(this._element).off(ye)},e._setResizeEvent=function(){var t=this;this._isShown?i.default(window).on(ve,(function(e){return t.handleUpdate(e)})):i.default(window).off(ve)},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){i.default(document.body).removeClass(de),t._resetAdjustments(),t._resetScrollbar(),i.default(t._element).trigger(me)}))},e._removeBackdrop=function(){this._backdrop&&(i.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,n=i.default(this._element).hasClass(ce)?ce:"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",n&&this._backdrop.classList.add(n),i.default(this._backdrop).appendTo(document.body),i.default(this._element).on(be,(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&("static"===e._config.backdrop?e._triggerBackdropTransition():e.hide())})),n&&u.reflow(this._backdrop),i.default(this._backdrop).addClass(he),!t)return;if(!n)return void t();var o=u.getTransitionDurationFromElement(this._backdrop);i.default(this._backdrop).one(u.TRANSITION_END,t).emulateTransitionEnd(o)}else if(!this._isShown&&this._backdrop){i.default(this._backdrop).removeClass(he);var r=function(){e._removeBackdrop(),t&&t()};if(i.default(this._element).hasClass(ce)){var a=u.getTransitionDurationFromElement(this._backdrop);i.default(this._backdrop).one(u.TRANSITION_END,r).emulateTransitionEnd(a)}else r()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",customClass:"",sanitize:!0,sanitizeFn:null,whiteList:{"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},popperConfig:null},We={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},Ue={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},Ve=function(){function t(t,e){if("undefined"==typeof Yt)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=i.default(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(i.default(this.getTipElement()).hasClass(Fe))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),i.default.removeData(this.element,this.constructor.DATA_KEY),i.default(this.element).off(this.constructor.EVENT_KEY),i.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&i.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===i.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=i.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){i.default(this.element).trigger(e);var n=u.findShadowRoot(this.element),o=i.default.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!o)return;var r=this.getTipElement(),a=u.getUID(this.constructor.NAME);r.setAttribute("id",a),this.element.setAttribute("aria-describedby",a),this.setContent(),this.config.animation&&i.default(r).addClass(Pe);var s="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,l=this._getAttachment(s);this.addAttachmentClass(l);var f=this._getContainer();i.default(r).data(this.constructor.DATA_KEY,this),i.default.contains(this.element.ownerDocument.documentElement,this.tip)||i.default(r).appendTo(f),i.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new Yt(this.element,r,this._getPopperConfig(l)),i.default(r).addClass(Fe),i.default(r).addClass(this.config.customClass),"ontouchstart"in document.documentElement&&i.default(document.body).children().on("mouseover",null,i.default.noop);var d=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,i.default(t.element).trigger(t.constructor.Event.SHOWN),e===Be&&t._leave(null,t)};if(i.default(this.tip).hasClass(Pe)){var c=u.getTransitionDurationFromElement(this.tip);i.default(this.tip).one(u.TRANSITION_END,d).emulateTransitionEnd(c)}else d()}},e.hide=function(t){var e=this,n=this.getTipElement(),o=i.default.Event(this.constructor.Event.HIDE),r=function(){e._hoverState!==Re&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),i.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(i.default(this.element).trigger(o),!o.isDefaultPrevented()){if(i.default(n).removeClass(Fe),"ontouchstart"in document.documentElement&&i.default(document.body).children().off("mouseover",null,i.default.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,i.default(this.tip).hasClass(Pe)){var a=u.getTransitionDurationFromElement(n);i.default(n).one(u.TRANSITION_END,r).emulateTransitionEnd(a)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){i.default(this.getTipElement()).addClass("bs-tooltip-"+t)},e.getTipElement=function(){return this.tip=this.tip||i.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(i.default(t.querySelectorAll(".tooltip-inner")),this.getTitle()),i.default(t).removeClass("fade show")},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=ke(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?i.default(e).parent().is(t)||t.empty().append(e):t.text(i.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return a({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=a({},e.offsets,t.config.offset(e.offsets,t.element)),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:u.isElement(this.config.container)?i.default(this.config.container):i.default(document).find(this.config.container)},e._getAttachment=function(t){return qe[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)i.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n=e===He?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,o=e===He?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;i.default(t.element).on(n,t.config.selector,(function(e){return t._enter(e)})).on(o,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},i.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=a({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||i.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Me:He]=!0),i.default(e.getTipElement()).hasClass(Fe)||e._hoverState===Re?e._hoverState=Re:(clearTimeout(e._timeout),e._hoverState=Re,e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){e._hoverState===Re&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||i.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),i.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Me:He]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=Be,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){e._hoverState===Be&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=i.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==Le.indexOf(t)&&delete e[t]})),"number"==typeof(t=a({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),u.typeCheckConfig(Ie,t,this.constructor.DefaultType),t.sanitize&&(t.template=ke(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=i.default(this.getTipElement()),e=t.attr("class").match(je);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(i.default(t).removeClass(Pe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data(Oe),r="object"==typeof e&&e;if((o||!/dispose|hide/.test(e))&&(o||(o=new t(this,r),n.data(Oe,o)),"string"==typeof e)){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return Qe}},{key:"NAME",get:function(){return Ie}},{key:"DATA_KEY",get:function(){return Oe}},{key:"Event",get:function(){return Ue}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return We}}]),t}();i.default.fn.tooltip=Ve._jQueryInterface,i.default.fn.tooltip.Constructor=Ve,i.default.fn.tooltip.noConflict=function(){return i.default.fn.tooltip=xe,Ve._jQueryInterface};var Ye="bs.popover",ze=i.default.fn.popover,Ke=new RegExp("(^|\\s)bs-popover\\S+","g"),Xe=a({},Ve.Default,{placement:"right",trigger:"click",content:"",template:''}),Ge=a({},Ve.DefaultType,{content:"(string|element|function)"}),$e={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},Je=function(t){var e,n;function o(){return t.apply(this,arguments)||this}n=t,(e=o).prototype=Object.create(n.prototype),e.prototype.constructor=e,s(e,n);var a=o.prototype;return a.isWithContent=function(){return this.getTitle()||this._getContent()},a.addAttachmentClass=function(t){i.default(this.getTipElement()).addClass("bs-popover-"+t)},a.getTipElement=function(){return this.tip=this.tip||i.default(this.config.template)[0],this.tip},a.setContent=function(){var t=i.default(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(".popover-body"),e),t.removeClass("fade show")},a._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},a._cleanTipClass=function(){var t=i.default(this.getTipElement()),e=t.attr("class").match(Ke);null!==e&&e.length>0&&t.removeClass(e.join(""))},o._jQueryInterface=function(t){return this.each((function(){var e=i.default(this).data(Ye),n="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new o(this,n),i.default(this).data(Ye,e)),"string"==typeof t)){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},r(o,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return Xe}},{key:"NAME",get:function(){return"popover"}},{key:"DATA_KEY",get:function(){return Ye}},{key:"Event",get:function(){return $e}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return Ge}}]),o}(Ve);i.default.fn.popover=Je._jQueryInterface,i.default.fn.popover.Constructor=Je,i.default.fn.popover.noConflict=function(){return i.default.fn.popover=ze,Je._jQueryInterface};var Ze="scrollspy",tn="bs.scrollspy",en=i.default.fn[Ze],nn="active",on="position",rn=".nav, .list-group",an={offset:10,method:"auto",target:""},sn={offset:"number",method:"string",target:"(string|element)"},ln=function(){function t(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,i.default(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return n._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":on,n="auto"===this._config.method?e:this._config.method,o=n===on?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,r=u.getSelectorFromElement(t);if(r&&(e=document.querySelector(r)),e){var a=e.getBoundingClientRect();if(a.width||a.height)return[i.default(e)[n]().top+o,r]}return null})).filter(Boolean).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){i.default.removeData(this._element,tn),i.default(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=a({},an,"object"==typeof t&&t?t:{})).target&&u.isElement(t.target)){var e=i.default(t.target).attr("id");e||(e=u.getUID(Ze),i.default(t.target).attr("id",e)),t.target="#"+e}return u.typeCheckConfig(Ze,t,sn),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;)this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t li > .active",gn=function(){function t(t){this._element=t}var e=t.prototype;return e.show=function(){var t=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&i.default(this._element).hasClass(dn)||i.default(this._element).hasClass("disabled")||this._element.hasAttribute("disabled"))){var e,n,o=i.default(this._element).closest(".nav, .list-group")[0],r=u.getSelectorFromElement(this._element);if(o){var a="UL"===o.nodeName||"OL"===o.nodeName?mn:pn;n=(n=i.default.makeArray(i.default(o).find(a)))[n.length-1]}var s=i.default.Event("hide.bs.tab",{relatedTarget:this._element}),l=i.default.Event("show.bs.tab",{relatedTarget:n});if(n&&i.default(n).trigger(s),i.default(this._element).trigger(l),!l.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(e=document.querySelector(r)),this._activate(this._element,o);var f=function(){var e=i.default.Event("hidden.bs.tab",{relatedTarget:t._element}),o=i.default.Event("shown.bs.tab",{relatedTarget:n});i.default(n).trigger(e),i.default(t._element).trigger(o)};e?this._activate(e,e.parentNode,f):f()}}},e.dispose=function(){i.default.removeData(this._element,un),this._element=null},e._activate=function(t,e,n){var o=this,r=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?i.default(e).children(pn):i.default(e).find(mn))[0],a=n&&r&&i.default(r).hasClass(cn),s=function(){return o._transitionComplete(t,r,n)};if(r&&a){var l=u.getTransitionDurationFromElement(r);i.default(r).removeClass(hn).one(u.TRANSITION_END,s).emulateTransitionEnd(l)}else s()},e._transitionComplete=function(t,e,n){if(e){i.default(e).removeClass(dn);var o=i.default(e.parentNode).find("> .dropdown-menu .active")[0];o&&i.default(o).removeClass(dn),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}i.default(t).addClass(dn),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),u.reflow(t),t.classList.contains(cn)&&t.classList.add(hn);var r=t.parentNode;if(r&&"LI"===r.nodeName&&(r=r.parentNode),r&&i.default(r).hasClass("dropdown-menu")){var a=i.default(t).closest(".dropdown")[0];if(a){var s=[].slice.call(a.querySelectorAll(".dropdown-toggle"));i.default(s).addClass(dn)}t.setAttribute("aria-expanded",!0)}n&&n()},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data(un);if(o||(o=new t(this),n.data(un,o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.2"}}]),t}();i.default(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),gn._jQueryInterface.call(i.default(this),"show")})),i.default.fn.tab=gn._jQueryInterface,i.default.fn.tab.Constructor=gn,i.default.fn.tab.noConflict=function(){return i.default.fn.tab=fn,gn._jQueryInterface};var _n="bs.toast",vn=i.default.fn.toast,bn="hide",yn="show",En="showing",wn="click.dismiss.bs.toast",Tn={animation:!0,autohide:!0,delay:500},Cn={animation:"boolean",autohide:"boolean",delay:"number"},Sn=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var e=t.prototype;return e.show=function(){var t=this,e=i.default.Event("show.bs.toast");if(i.default(this._element).trigger(e),!e.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var n=function(){t._element.classList.remove(En),t._element.classList.add(yn),i.default(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove(bn),u.reflow(this._element),this._element.classList.add(En),this._config.animation){var o=u.getTransitionDurationFromElement(this._element);i.default(this._element).one(u.TRANSITION_END,n).emulateTransitionEnd(o)}else n()}},e.hide=function(){if(this._element.classList.contains(yn)){var t=i.default.Event("hide.bs.toast");i.default(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},e.dispose=function(){this._clearTimeout(),this._element.classList.contains(yn)&&this._element.classList.remove(yn),i.default(this._element).off(wn),i.default.removeData(this._element,_n),this._element=null,this._config=null},e._getConfig=function(t){return t=a({},Tn,i.default(this._element).data(),"object"==typeof t&&t?t:{}),u.typeCheckConfig("toast",t,this.constructor.DefaultType),t},e._setListeners=function(){var t=this;i.default(this._element).on(wn,'[data-dismiss="toast"]',(function(){return t.hide()}))},e._close=function(){var t=this,e=function(){t._element.classList.add(bn),i.default(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove(yn),this._config.animation){var n=u.getTransitionDurationFromElement(this._element);i.default(this._element).one(u.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},e._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(e){return this.each((function(){var n=i.default(this),o=n.data(_n);if(o||(o=new t(this,"object"==typeof e&&e),n.data(_n,o)),"string"==typeof e){if("undefined"==typeof o[e])throw new TypeError('No method named "'+e+'"');o[e](this)}}))},r(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"DefaultType",get:function(){return Cn}},{key:"Default",get:function(){return Tn}}]),t}();i.default.fn.toast=Sn._jQueryInterface,i.default.fn.toast.Constructor=Sn,i.default.fn.toast.noConflict=function(){return i.default.fn.toast=vn,Sn._jQueryInterface},t.Alert=c,t.Button=b,t.Carousel=O,t.Collapse=W,t.Dropdown=le,t.Modal=Se,t.Popover=Je,t.Scrollspy=ln,t.Tab=gn,t.Toast=Sn,t.Tooltip=Ve,t.Util=u,Object.defineProperty(t,"__esModule",{value:!0})})); 7 | //# sourceMappingURL=bootstrap.bundle.min.js.map 8 | -------------------------------------------------------------------------------- /www/public/js/popper.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Federico Zivolo 2020 3 | Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). 4 | */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function i(e){return e&&e.referenceNode?e.referenceNode:e}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f]),E=parseFloat(w['border'+f+'Width']),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.rightwindow.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge}); 5 | //# sourceMappingURL=popper.min.js.map 6 | -------------------------------------------------------------------------------- /www/resources/container.php: -------------------------------------------------------------------------------- 1 | services(); 8 | $services->defaults() 9 | ->autowire() 10 | ->autoconfigure(); 11 | 12 | $services 13 | ->instanceof(ControllerInterface::class) 14 | ->tag('psx.controller'); 15 | 16 | $services->load('App\\Controller\\', __DIR__ . '/../src/Controller') 17 | ->public(); 18 | 19 | $services->load('App\\Service\\', __DIR__ . '/../src/Service'); 20 | 21 | }; 22 | -------------------------------------------------------------------------------- /www/resources/examples/argument_body.json: -------------------------------------------------------------------------------- 1 | { 2 | "operations": { 3 | "create": { 4 | "description": "Inserts a new todo entry", 5 | "method": "POST", 6 | "path": "/todo", 7 | "arguments": { 8 | "payload": { 9 | "in": "body", 10 | "schema": { 11 | "type": "reference", 12 | "target": "Todo" 13 | } 14 | } 15 | }, 16 | "return": { 17 | "schema": { 18 | "type": "reference", 19 | "target": "Message" 20 | } 21 | } 22 | } 23 | }, 24 | "definitions": { 25 | "Todo": { 26 | "type": "object", 27 | "properties": { 28 | "title": { 29 | "type": "string" 30 | } 31 | } 32 | }, 33 | "Message": { 34 | "type": "object", 35 | "properties": { 36 | "success": { 37 | "type": "boolean" 38 | }, 39 | "message": { 40 | "type": "string" 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /www/resources/examples/argument_query.json: -------------------------------------------------------------------------------- 1 | { 2 | "operations": { 3 | "getAll": { 4 | "description": "Returns available todo entries", 5 | "method": "GET", 6 | "path": "/todo", 7 | "arguments": { 8 | "startIndex": { 9 | "in": "query", 10 | "schema": { 11 | "type": "integer" 12 | } 13 | }, 14 | "count": { 15 | "in": "query", 16 | "schema": { 17 | "type": "integer" 18 | } 19 | } 20 | }, 21 | "return": { 22 | "schema": { 23 | "type": "reference", 24 | "target": "Todos" 25 | } 26 | } 27 | } 28 | }, 29 | "definitions": { 30 | "Todos": { 31 | "type": "object", 32 | "properties": { 33 | "entries": { 34 | "type": "array", 35 | "schema": { 36 | "type": "reference", 37 | "target": "Todo" 38 | } 39 | } 40 | } 41 | }, 42 | "Todo": { 43 | "type": "object", 44 | "properties": { 45 | "title": { 46 | "type": "string" 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /www/resources/examples/exception.json: -------------------------------------------------------------------------------- 1 | { 2 | "operations": { 3 | "getMessage": { 4 | "description": "Returns a hello world message", 5 | "method": "GET", 6 | "path": "/hello/world", 7 | "return": { 8 | "schema": { 9 | "type": "reference", 10 | "target": "Hello_World" 11 | } 12 | }, 13 | "throws": [{ 14 | "code": 500, 15 | "schema": { 16 | "type": "reference", 17 | "target": "Error" 18 | } 19 | }] 20 | } 21 | }, 22 | "definitions": { 23 | "Hello_World": { 24 | "type": "object", 25 | "properties": { 26 | "message": { 27 | "type": "string" 28 | } 29 | } 30 | }, 31 | "Error": { 32 | "type": "object", 33 | "properties": { 34 | "success": { 35 | "type": "boolean" 36 | }, 37 | "message": { 38 | "type": "string" 39 | } 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /www/resources/examples/operation_group.json: -------------------------------------------------------------------------------- 1 | { 2 | "operations": { 3 | "todo.create": { 4 | "description": "Inserts a new todo entry", 5 | "method": "POST", 6 | "path": "/todo", 7 | "arguments": { 8 | "payload": { 9 | "in": "body", 10 | "schema": { 11 | "type": "reference", 12 | "target": "Todo" 13 | } 14 | } 15 | }, 16 | "return": { 17 | "schema": { 18 | "type": "reference", 19 | "target": "Message" 20 | } 21 | } 22 | }, 23 | "product.create": { 24 | "description": "Inserts a new product", 25 | "method": "POST", 26 | "path": "/product", 27 | "arguments": { 28 | "payload": { 29 | "in": "body", 30 | "schema": { 31 | "type": "reference", 32 | "target": "Product" 33 | } 34 | } 35 | }, 36 | "return": { 37 | "schema": { 38 | "type": "reference", 39 | "target": "Message" 40 | } 41 | } 42 | } 43 | }, 44 | "definitions": { 45 | "Todo": { 46 | "type": "object", 47 | "properties": { 48 | "title": { 49 | "type": "string" 50 | } 51 | } 52 | }, 53 | "Product": { 54 | "type": "object", 55 | "properties": { 56 | "title": { 57 | "type": "string" 58 | } 59 | } 60 | }, 61 | "Message": { 62 | "type": "object", 63 | "properties": { 64 | "success": { 65 | "type": "boolean" 66 | }, 67 | "message": { 68 | "type": "string" 69 | } 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /www/resources/examples/simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "operations": { 3 | "getMessage": { 4 | "description": "Returns a hello world message", 5 | "method": "GET", 6 | "path": "/hello/world", 7 | "return": { 8 | "schema": { 9 | "type": "reference", 10 | "target": "Hello_World" 11 | } 12 | } 13 | } 14 | }, 15 | "definitions": { 16 | "Hello_World": { 17 | "type": "object", 18 | "properties": { 19 | "message": { 20 | "type": "string" 21 | } 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /www/resources/template/ecosystem.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 |

Ecosystem

7 |

The following page lists libraries and other projects related to TypeAPI.

8 |
9 |
10 | 11 |
12 |
13 |

Model

14 |

We provide auto-generated models of the TypeAPI meta specification which describes itself. 15 | These models can be used to parse or generate a TypeAPI specification.

16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
LanguageGitHubPackage
C#GitHubNuget
GoGitHub
JavaGitHubMaven
JavaScriptGitHubNPM
PHPGitHubPackagist
PythonGitHubPyPI
57 |
58 |
59 | 60 | 61 |
62 |
63 |

Tools

64 |

Tools which help to work with a TypeAPI specification.

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 91 | 92 | 93 | 94 | 96 | 97 | 98 |
NameDescription
TypeAPI-EditorAngular component which allows you to build and view TypeAPI specifications. We provide a hosted version at 76 | our Sandbox page.
SDKgen-Generator-ActionGitHub action which allows you to generate code through a GitHub workflow action.
SDKgen-Generator-CLIA simple binary written in go which allows you to generate code.
SDKgenSDKgen is a service which provides a code generator as REST API, you can consume the API either manually or through 89 | a simple CLI app which helps to integrate it into different 90 | environments. Besides this it also provides additional languages like Java and Go.
TypeHubTypeHub is a new platform to quickly build and share client SDKs and data models. It internally also uses the 95 | SDKgen API and covers the complete flow to manage and evolve your TypeAPI specification.
99 |
100 |
101 | 102 |
103 |
104 |
105 | Edit this page 106 |
107 |
108 |
109 | 110 | 111 | -------------------------------------------------------------------------------- /www/resources/template/generator.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 |

SDK Generator

7 |

This list gives you access to the reference code generator implementation. 8 | To prevent misuse the code generator is protected by recaptcha, if you want to invoke the code generator 9 | programmatically please take a look at the SDKgen project 10 | which offers various integration options like an CLI, GitHub action or REST API.

11 |
12 |
13 | 21 |
22 |
23 | 31 |
32 |
33 |
34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /www/resources/template/generator/form.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |

Generator

6 |
7 |
8 |
9 |
10 |
11 | 12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 | 21 |
22 |
23 |
24 |
25 | 26 |
27 | 28 | 29 | 30 | " class="button">Report Issue 31 |
32 | 33 | $chunk): ?> 34 |
35 |
36 |

37 |
38 |
39 |
40 |
41 |
42 | 43 | 44 |
45 |
46 |

Output

47 |
48 |
49 |
50 |
51 |
52 | 53 | 54 |
55 |
56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /www/resources/template/inc/footer.php: -------------------------------------------------------------------------------- 1 | 2 | 39 | 40 |
part of the Apioo-Project
41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /www/resources/template/inc/header.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <?php echo $title ?? 'TypeAPI'; ?> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 56 | 57 |
58 |
59 |

TypeAPI

60 |

An OpenAPI alternative to describe REST APIs for type-safe code generation.

61 | Specification 62 | Editor 63 | Generator 64 |
65 |
66 | -------------------------------------------------------------------------------- /www/resources/template/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $example): ?> 5 |
6 |
7 |

title; ?>

8 |

description; ?>

9 |
10 |
11 |
12 |
13 |

TypeAPI

14 |
15 |
16 |
schema; ?>
17 |
18 |
19 |
20 |
21 |
22 |
23 |

Client SDK

24 |
25 |
26 |
code); ?>
27 |
28 |
29 |
30 |
31 |
32 |
33 | 34 | 35 |
36 |
37 |

Code-Generator

38 |

Our code generator uses proven technology to generate fully type-safe client/server pairs.

39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
LanguageClientServer
C#HttpClientASP Web-API
JavaApache HttpClientSpring
TypeScriptAxiosNestJS
PHPGuzzleSymfony
PythonRequestsFastAPI
75 |
76 |
77 | 78 |
79 |
80 | Edit this page 81 |
82 |
83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /www/resources/template/integration.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 |

Integration

7 |

The following page explains and shows examples how you can use the generated code.

8 |
9 |
10 | 18 |
19 |
20 | 28 |
29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 | Edit this page 37 |
38 |
39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /www/resources/template/integration/client-csharp.html: -------------------------------------------------------------------------------- 1 | 2 |

To integrate the generated client SDK you need to add the Sdkgen.Client package to your 3 | project. Then you can simply paste the generated code into the source folder and start using the client for example:

4 | 5 |
Anonymous credentials = new Anonymous();
 6 | Client client = new Client("http://127.0.0.1:1080", credentials);
 7 | 
 8 | HelloWorld message = await client.Test().GetHello();
 9 | 
10 | Console.WriteLine("Message: " + message.Message);
11 | 
12 | -------------------------------------------------------------------------------- /www/resources/template/integration/client-go.html: -------------------------------------------------------------------------------- 1 | 2 |

To integrate the generated client SDK you need to add the github.com/apioo/sdkgen-go package to your 3 | project. Then you can simply paste the generated code into the source folder and start using the client for example:

4 | 5 |
credentials := sdkgen.Anonymous{}
 6 | 
 7 | client, err := NewClient("http://127.0.0.1:1080", credentials)
 8 | if err != nil {
 9 |   log.Fatal(err)
10 | }
11 | 
12 | message, err := client.Test().GetHello()
13 | if err != nil {
14 |   log.Fatal(err)
15 | }
16 | 
17 | fmt.Println("Message: " + message.Message);
18 | 
19 | -------------------------------------------------------------------------------- /www/resources/template/integration/client-java.html: -------------------------------------------------------------------------------- 1 | 2 |

To integrate the generated client SDK you need to add the app.sdkgen.client package to your 3 | project. Then you can simply paste the generated code into the source folder and start using the client for example:

4 | 5 |
Anonymous credentials = new Anonymous();
 6 | Client client = new Client("http://127.0.0.1:1080", credentials);
 7 | 
 8 | HelloWorld message = client.test().getHello();
 9 | 
10 | System.out.println("Message: " + message.getMessage());
11 | 
12 | -------------------------------------------------------------------------------- /www/resources/template/integration/client-php.html: -------------------------------------------------------------------------------- 1 | 2 |

To integrate the generated client SDK you need to add the sdkgen/client package to your 3 | project. Then you can simply paste the generated code into the source folder and start using the client for example:

4 | 5 |
<?php
 6 | 
 7 | $credentials = new \Sdkgen\Client\Credentials\Anonymous();
 8 | $client = new Client('http://127.0.0.1:1080', $credentials);
 9 | 
10 | $message = $client->test()->getHello();
11 | 
12 | echo 'Message: ' . $message->getMessage();
13 | 
14 | -------------------------------------------------------------------------------- /www/resources/template/integration/client-python.html: -------------------------------------------------------------------------------- 1 | 2 |

To integrate the generated client SDK you need to add the sdkgen-client package to your 3 | project. Then you can simply paste the generated code into the source folder and start using the client for example:

4 | 5 |
credentials = Anonymous()
 6 | client = Client('http://127.0.0.1:1080', credentials)
 7 | 
 8 | message = client.test().get_hello()
 9 | 
10 | print('Message: ' + message.message);
11 | 
12 | -------------------------------------------------------------------------------- /www/resources/template/integration/client-typescript.html: -------------------------------------------------------------------------------- 1 | 2 |

To integrate the generated client SDK you need to add the sdkgen-client package to your 3 | project. Then you can simply paste the generated code into the source folder and start using the client for example:

4 | 5 |
const credentials = new Anonymous();
 6 | const client = new Client('http://127.0.0.1:1080', credentials);
 7 | 
 8 | message = client.test().getHello()
 9 | 
10 | console.log('Message: ' + message.message);
11 | 
12 | -------------------------------------------------------------------------------- /www/resources/template/integration/detail.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 |

Integration

7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /www/resources/template/integration/server-csharp.html: -------------------------------------------------------------------------------- 1 | 2 |

The server code is based on ASP Web-API. The generated code contains the controller and model classes 3 | which are described at the specification. You only need to copy and paste the generated code into your source folder. A simple controller would 4 | look like:

5 | 6 |
[ApiController]
 7 | public class App : ControllerBase
 8 | {
 9 |     [Route("hello/world")]
10 |     [HttpGet]
11 |     [ProducesResponseType(200)]
12 |     public HelloWorld getMessage()
13 |     {
14 |         // @TODO implement method
15 |     }
16 | 
17 | }
18 | 
19 | -------------------------------------------------------------------------------- /www/resources/template/integration/server-java.html: -------------------------------------------------------------------------------- 1 | 2 |

The server code is based on Spring. The generated code contains the controller and model classes 3 | which are described at the specification. You only need to copy and paste the generated code into your source folder. A simple controller would 4 | look like:

5 | 6 |
@RestController
 7 | class App {
 8 |     @GetMapping("/hello/world")
 9 |     @ResponseStatus(200)
10 |     HelloWorld getMessage() {
11 |         // @TODO implement method
12 |     }
13 | 
14 | }
15 | 
16 | -------------------------------------------------------------------------------- /www/resources/template/integration/server-php.html: -------------------------------------------------------------------------------- 1 | 2 |

The server code is based on Symfony. The generated code contains the controller and model classes 3 | which are described at the specification. You only need to copy and paste the generated code into your source folder. A simple controller would 4 | look like:

5 | 6 |
class App extends AbstractController
 7 | {
 8 |     #[Route('/hello/world', methods: ['GET'])]
 9 |     #[StatusCode(200)]
10 |     public function getMessage(): Model\HelloWorld
11 |     {
12 |         // @TODO implement method
13 |     }
14 | 
15 | }
16 | 
17 | -------------------------------------------------------------------------------- /www/resources/template/integration/server-python.html: -------------------------------------------------------------------------------- 1 | 2 |

The server code is based on FastAPI. The generated code contains the controller and model classes 3 | which are described at the specification. You only need to copy and paste the generated code into your source folder. A simple controller would 4 | look like:

5 | 6 |
app = FastAPI()
 7 | 
 8 | @app.get("/hello/world", status_code=200)
 9 | async def getMessage():
10 |     # @TODO implement method
11 |     pass
12 | 
13 | 
14 | -------------------------------------------------------------------------------- /www/resources/template/integration/server-typescript.html: -------------------------------------------------------------------------------- 1 | 2 |

The server code is based on NestJS. The generated code contains the controller and model classes 3 | which are described at the specification. You only need to copy and paste the generated code into your source folder. A simple controller would 4 | look like:

5 | 6 |
@Controller()
 7 | export class AppController {
 8 |   @Get('/hello/world')
 9 |   @HttpCode(200)
10 |   getMessage(): HelloWorld {
11 |     // @TODO implement method
12 |     return {};
13 |   }
14 | 
15 | }
16 | 
17 | -------------------------------------------------------------------------------- /www/resources/template/specification.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 |
7 |

Specification

8 |

Table of Contents

9 | 28 | 29 |
30 | 31 | 32 |

Introduction

33 |

This document describes the TypeAPI specification. 34 | The TypeAPI specification defines a JSON format to describe REST APIs for type-safe code generation.

35 | 36 | 37 |

Goals

38 |
    39 |
  • Provide a format to generate clean and ready to use code
  • 40 |
  • Provide a simple and stable specification
  • 41 |
  • Optimized for static typed and object-oriented programming languages
  • 42 |
43 | 44 | 45 |

Non-Goals

46 |
    47 |
  • Describe every possible REST API structure and JSON payload
  • 48 |
  • Providing complex JSON validation capabilities
  • 49 |
50 | 51 | 52 |

Reasoning

53 |

54 |

We believe that the API world needs a specification which can be used to automatically generate solid type-safe 55 | client and server code. The OpenAPI swagger-codegen project 56 | exists for a long time to implement such a code generator for the OpenAPI specification, but it has turned out, 57 | that the OpenAPI specification and JSON Schema makes it difficult for code generators to generate solid type-safe code. 58 | The problems 59 | are at the specification level, this means a code generator which 60 | is based on OpenAPI needs to somehow solve these inherited problems, by either restricting the specification or by providing a custom format.

61 |

With TypeAPI we want to provide an alternativ specification to solve these problems. TypeAPI is basically a stricter 62 | version of OpenAPI/JSON Schema and it is easy possible to generate an OpenAPI specification based on a TypeAPI specification but not vice versa. 63 | We also see already many commercial projects like Fern, Liblab or 64 | Stainless to solve these problems, but we believe that it would be much better to 65 | solve this at the specification level.

66 | 67 | 68 |

Vision

69 |

We see that the world is connected through APIs but integrating external APIs is still a complex problem. 70 | We want to move the API ecosystem into a direction where it is no longer needed to implement a client SDK for your API, 71 | you only need to describe the API through a TypeAPI specification and everything else can be generated automatically. 72 | In the future we also want to extend the TypeAPI specification and code generator to describe GraphQL or RPC APIs so that 73 | we have a single client which can talk to various protocols. This means the generated client is always stable, but it is 74 | possible to change the underlying technology i.e. if you want to switch from REST to RPC.

75 |

On the server-side we also want to generate great server-stubs so that it is easy possible to switch the underlying 76 | server technology. The code generator automatically generates all controller and model classes 77 | for the target server technology i.e. Spring or Symfony and then you only need to implement the actual business logic.

78 |

At TypeAPI we heavily support the code-first approach, we think it should be possible to generate an API specification 79 | directly from your code without the need to add many additional annotations. In the future we want to provide tools to 80 | automatically generate a TypeAPI specification directly from various frameworks without the need to manually build the specification. 81 | We see many APIs which are not in sync with the specification and we believe that code-first is the correct approach to prevent 82 | this, so that the specification is always in sync with the actual implementation. While theoretical the design-first approach would 83 | be great we have seen in the past that there is basically no way to prevent API drift at scale and keep the API in sync with the 84 | actual implementation.

85 | 86 |
87 | 88 | 89 |

Operations

90 | 91 |

Every TypeAPI has a Root definition. The 92 | Root must contain at least the operations and definitions keyword i.e.:

93 |
{
 94 |     "operations": {
 95 |         "getMessage": { ... },
 96 |     },
 97 |     "definitions": {
 98 |         "TypeA": { ... },
 99 |         "TypeB": { ... }
100 |     }
101 | }
102 | 103 |
104 | 105 |

The operations keyword contains a map containing Operation 106 | objects. The key represents the identifier of this operation, through the dot notation i.e. user.getMessage you can group your 107 | operations into logical units.

108 | 109 |
{
110 |     "operations": {
111 |         "getMessage": {
112 |             "description": "Returns a hello world message",
113 |             "method": "GET",
114 |             "path": "/hello/world",
115 |             "return": {
116 |                 "schema": {
117 |                     "type": "reference",
118 |                     "target": "Hello_World"
119 |                 }
120 |             }
121 |         }
122 |     },
123 |     "definitions": {
124 |         "Hello_World": {
125 |             "type": "struct",
126 |             "properties": {
127 |                 "message": {
128 |                     "type": "string"
129 |                 }
130 |             }
131 |         }
132 |     }
133 | }
134 | 135 |
136 | 137 | 138 |

Return

139 | 140 |

Every operation can define a return type. In the above example the operation simply returns a Hello_World 141 | object.

142 | 143 |
144 | 145 | 146 |

Arguments

147 | 148 |

Through the arguments keywords you can map values from the HTTP request to specific method arguments. In 149 | the following example we have an argument status which maps to a query parameter and an argument 150 | payload which contains the request payload.

151 | 152 |
{
153 |     "operations": {
154 |         "insertMessage": {
155 |             "description": "Inserts and returns a hello world message",
156 |             "method": "POST",
157 |             "path": "/hello/world",
158 |             "arguments": {
159 |                 "status": {
160 |                     "in": "query",
161 |                     "schema": {
162 |                         "type": "integer"
163 |                     }
164 |                 },
165 |                 "payload": {
166 |                     "in": "body",
167 |                     "schema": {
168 |                         "type": "reference",
169 |                         "target": "Hello_World"
170 |                     }
171 |                 }
172 |             },
173 |             "return": {
174 |                 "schema": {
175 |                     "type": "reference",
176 |                     "target": "Hello_World"
177 |                 }
178 |             }
179 |         }
180 |     },
181 |     "definitions": {
182 |         "Hello_World": {
183 |             "type": "struct",
184 |             "properties": {
185 |                 "message": {
186 |                     "type": "string"
187 |                 }
188 |             }
189 |         }
190 |     }
191 | }
192 | 193 |

This would map to the following HTTP request.

194 | 195 |
POST https://api.acme.com/hello/world?status=2
196 | Content-Type: application/json
197 | 
198 | {
199 |   "message": "Hello"
200 | }
201 | 
202 | 203 |
204 | 205 | 206 |

Throws

207 | 208 |

Besides the return type an operation can return multiple exceptional states in case an error occurred. Every 209 | exceptional state is then mapped to a specific status code i.e. 404 or 500. The generated 210 | client SDK will throw a fitting exception containing the JSON payload in case the server returns such an error 211 | response code. The client will either return the success response or throw an exception. This greatly simplifies error 212 | handling at your client code.

213 | 214 |
{
215 |     "operations": {
216 |         "getMessage": {
217 |             "description": "Returns a hello world message",
218 |             "method": "POST",
219 |             "path": "/hello/world",
220 |             "return": {
221 |                 "schema": {
222 |                     "type": "reference",
223 |                     "target": "Hello_World"
224 |                 }
225 |             },
226 |             "throws": [{
227 |                 "code": 404,
228 |                 "schema": {
229 |                     "type": "reference",
230 |                     "target": "Error"
231 |                 }
232 |             }, {
233 |                 "code": 500,
234 |                 "schema": {
235 |                     "type": "reference",
236 |                     "target": "Error"
237 |                 }
238 |             }]
239 |         }
240 |     },
241 |     "definitions": {
242 |         "Hello_World": {
243 |             "type": "struct",
244 |             "properties": {
245 |                 "message": {
246 |                     "type": "string"
247 |                 }
248 |             }
249 |         },
250 |         "Error": {
251 |             "type": "struct",
252 |             "properties": {
253 |                 "message": {
254 |                     "type": "string"
255 |                 }
256 |             }
257 |         }
258 |     }
259 | }
260 | 261 |
262 | 263 | 264 |

Definitions

265 | 266 |

The definitions keyword maps to the TypeSchema 267 | specification and represents a map containing Struct, 268 | Map or Reference 269 | types. Those types are then used to describe incoming and outgoing JSON payloads.

270 | 271 |
272 | 273 | 274 |

Security

275 | 276 |

The security keyword describes the authorization mechanism of the API, the following types are supported:

277 |
    278 |
  • 279 |

    apiKey

    280 |

    Describes an arbitrary HTTP header containing an access token i.e. X-Api-Key which can be specified with the in and name keyword.

    281 |
  • 282 |
  • 283 |

    httpBasic

    284 |

    Describes an Authorization header using the Basic type. See RFC7617, base64-encoded credentials.

    285 |
  • 286 |
  • 287 |

    httpBearer

    288 |

    Describes an Authorization header using the Bearer type. See RFC6750, bearer tokens to access OAuth 2.0-protected resources.

    289 |
  • 290 |
  • 291 |

    oauth2

    292 |

    Describes an OAuth2 endpoint. The client will automatically request an access token using the client_credentials authorization grant on usage. The following keywords can be used: tokenUrl, authorizationUrl and optionally scopes

    293 |
  • 294 |
295 | 296 |

297 | 298 |
{
299 |     "security": {
300 |         "type": "httpBearer",
301 |     },
302 |     "operations": {
303 |         "getMessage": { ... }
304 |     },
305 |     "definitions": {
306 |         "Hello_World": { ... }
307 |     }
308 | }
309 | 310 |
311 |
312 | Edit this page 313 |
314 |
315 |
316 | 317 | 318 | 319 | 325 | 326 | 327 | -------------------------------------------------------------------------------- /www/resources/typeschema.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Generate": { 4 | "type": "object", 5 | "properties": { 6 | "namespace": { 7 | "type": "string" 8 | }, 9 | "schema": { 10 | "type": "string" 11 | }, 12 | "g-recaptcha-response": { 13 | "type": "string" 14 | } 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /www/src/Controller/Ecosystem.php: -------------------------------------------------------------------------------- 1 | reverseRouter = $reverseRouter; 18 | } 19 | 20 | #[Get] 21 | #[Path('/ecosystem')] 22 | public function show(): Template 23 | { 24 | $data = [ 25 | 'method' => explode('::', __METHOD__), 26 | 'title' => 'Ecosystem | TypeAPI', 27 | ]; 28 | 29 | $templateFile = __DIR__ . '/../../resources/template/ecosystem.php'; 30 | return new Template($data, $templateFile, $this->reverseRouter); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /www/src/Controller/Generator.php: -------------------------------------------------------------------------------- 1 | generatorFactory->factory()->getPossibleTypes() as $type) { 42 | $displayName = TypeName::getDisplayName($type); 43 | if ($displayName === null) { 44 | continue; 45 | } 46 | 47 | if (str_starts_with($type, 'client-')) { 48 | $clientTypes[$type] = $displayName; 49 | } elseif (str_starts_with($type, 'server-')) { 50 | $serverTypes[$type] = $displayName; 51 | } 52 | } 53 | 54 | ksort($clientTypes); 55 | ksort($serverTypes); 56 | 57 | $data = [ 58 | 'title' => 'SDK Generator | TypeAPI', 59 | 'method' => explode('::', __METHOD__), 60 | 'clientTypes' => $clientTypes, 61 | 'serverTypes' => $serverTypes, 62 | ]; 63 | 64 | $templateFile = __DIR__ . '/../../resources/template/generator.php'; 65 | return new Template($data, $templateFile, $this->reverseRouter); 66 | } 67 | 68 | #[Get] 69 | #[Path('/generator/:type')] 70 | public function showType(string $type): Template 71 | { 72 | $registry = $this->generatorFactory->factory(); 73 | if (!in_array($type, $registry->getPossibleTypes())) { 74 | throw new BadRequestException('Provided an invalid type'); 75 | } 76 | 77 | $data = [ 78 | 'title' => TypeName::getDisplayName($type) . ' SDK Generator | TypeAPI', 79 | 'method' => explode('::', __METHOD__), 80 | 'parameters' => ['type' => $type], 81 | 'schema' => $this->getSchema(), 82 | 'type' => $type, 83 | 'typeName' => TypeName::getDisplayName($type), 84 | 'js' => ['https://www.google.com/recaptcha/api.js'], 85 | 'recaptcha_key' => $this->config->get('recaptcha_key'), 86 | ]; 87 | 88 | $templateFile = __DIR__ . '/../../resources/template/generator/form.php'; 89 | return new Template($data, $templateFile, $this->reverseRouter); 90 | } 91 | 92 | #[Post] 93 | #[Path('/generator/:type')] 94 | public function generate(string $type, Generate $generate): Template 95 | { 96 | [$schema, $config, $parsedSchema] = $this->parse($type, $generate); 97 | 98 | try { 99 | $registry = $this->generatorFactory->factory(); 100 | $generator = $registry->getGenerator($type, $config); 101 | $result = $generator->generate($parsedSchema); 102 | 103 | if ($result instanceof Chunks) { 104 | $output = $this->buildArray($result); 105 | } else { 106 | $output = $result; 107 | } 108 | } catch (\Throwable $e) { 109 | $output = $e->getMessage(); 110 | } 111 | 112 | $data = [ 113 | 'title' => TypeName::getDisplayName($type) . ' SDK Generator | TypeAPI', 114 | 'method' => explode('::', __METHOD__), 115 | 'parameters' => ['type' => $type], 116 | 'namespace' => $config->get(Config::NAMESPACE), 117 | 'schema' => $schema, 118 | 'type' => $type, 119 | 'typeName' => TypeName::getDisplayName($type), 120 | 'output' => $output, 121 | 'js' => ['https://www.google.com/recaptcha/api.js'], 122 | 'recaptcha_key' => $this->config->get('recaptcha_key'), 123 | ]; 124 | 125 | $templateFile = __DIR__ . '/../../resources/template/generator/form.php'; 126 | return new Template($data, $templateFile, $this->reverseRouter); 127 | } 128 | 129 | #[Post] 130 | #[Path('/generator/:type/download')] 131 | public function download(#[Param] string $type, #[Body] Generate $generate): mixed 132 | { 133 | [$schema, $config, $parsedSchema] = $this->parse($type, $generate); 134 | 135 | try { 136 | $zipFile = $this->config->get('psx_path_cache') . '/typeapi_' . $type . '_' . sha1($schema) . '.zip'; 137 | if (is_file($zipFile)) { 138 | return new File($zipFile, 'typeapi_' . $type . '.zip', 'application/zip'); 139 | } 140 | 141 | $registry = $this->generatorFactory->factory(); 142 | $generator = $registry->getGenerator($type, $config); 143 | $result = $generator->generate($parsedSchema); 144 | 145 | if ($result instanceof Chunks) { 146 | $result->writeToZip($zipFile); 147 | 148 | return new File($zipFile, 'typeapi_' . $type . '.zip', 'application/zip'); 149 | } else { 150 | return new HttpResponse(200, ['Content-Type' => 'text/plain'], $result); 151 | } 152 | } catch (\Throwable $e) { 153 | return new HttpResponse(500, ['Content-Type' => 'text/plain'], $e->getMessage()); 154 | } 155 | } 156 | 157 | private function buildArray(Chunks $result, ?string $prefix = null): array 158 | { 159 | $chunks = []; 160 | foreach ($result->getChunks() as $fileName => $code) { 161 | if (is_string($code)) { 162 | $chunks[$fileName] = $code; 163 | } else { 164 | $chunks = array_merge($chunks, $this->buildArray($code, isset($prefix) ? $prefix . '/' . $fileName : $fileName)); 165 | } 166 | } 167 | 168 | return $chunks; 169 | } 170 | 171 | /** 172 | * @return array{string, Config, SpecificationInterface} 173 | */ 174 | private function parse(string $type, Generate $generate): array 175 | { 176 | $recaptchaSecret = $this->config->get('recaptcha_secret'); 177 | if (!empty($recaptchaSecret) && !$this->captchaVerifier->verify($generate->getGRecaptchaResponse())) { 178 | throw new BadRequestException('Invalid captcha'); 179 | } 180 | 181 | $namespace = $generate->getNamespace(); 182 | $schema = $generate->getSchema() ?? throw new \RuntimeException('Provided no schema'); 183 | 184 | if (strlen($schema) > self::MAX_SCHEMA_LENGTH) { 185 | throw new BadRequestException('Provided schema is too large, allowed max ' . self::MAX_SCHEMA_LENGTH . ' characters'); 186 | } 187 | 188 | $config = new Config(); 189 | if ($namespace !== null && $namespace !== '') { 190 | $config->put(Config::NAMESPACE, $namespace); 191 | } 192 | 193 | $registry = $this->generatorFactory->factory(); 194 | if (!in_array($type, $registry->getPossibleTypes())) { 195 | throw new BadRequestException('Provided an invalid type'); 196 | } 197 | 198 | $result = (new TypeAPI($this->schemaManager))->parse($schema); 199 | 200 | return [$schema, $config, $result]; 201 | } 202 | 203 | private function getSchema(): string 204 | { 205 | return <<<'JSON' 206 | { 207 | "operations": { 208 | "getMessage": { 209 | "description": "Returns a hello world message", 210 | "method": "GET", 211 | "path": "/hello/world", 212 | "return": { 213 | "schema": { 214 | "type": "reference", 215 | "target": "Hello_World" 216 | } 217 | } 218 | } 219 | }, 220 | "definitions": { 221 | "Hello_World": { 222 | "type": "object", 223 | "properties": { 224 | "message": { 225 | "type": "string" 226 | } 227 | } 228 | } 229 | } 230 | } 231 | JSON; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /www/src/Controller/Index.php: -------------------------------------------------------------------------------- 1 | cache = $cache; 25 | $this->reverseRouter = $reverseRouter; 26 | $this->apiManager = $apiManager; 27 | $this->generatorFactory = $generatorFactory; 28 | } 29 | 30 | #[Get] 31 | #[Path('/')] 32 | public function show(): Template 33 | { 34 | $item = $this->cache->getItem('example-cache'); 35 | if (!$item->isHit()) { 36 | $examples = $this->getExamples(); 37 | foreach ($examples as $key => $example) { 38 | $examples[$key]['schema'] = file_get_contents($example['file']); 39 | $examples[$key]['code'] = $this->convert(LocalRepository::MARKUP_CLIENT, $example['file']); 40 | } 41 | 42 | $item->expiresAfter(null); 43 | $item->set($examples); 44 | $this->cache->save($item); 45 | } else { 46 | $examples = $item->get(); 47 | } 48 | 49 | $data = [ 50 | 'method' => explode('::', __METHOD__), 51 | 'title' => 'OpenAPI alternative for type-safe code generation | TypeAPI', 52 | 'examples' => $examples 53 | ]; 54 | 55 | $templateFile = __DIR__ . '/../../resources/template/index.php'; 56 | return new Template($data, $templateFile, $this->reverseRouter); 57 | } 58 | 59 | 60 | private function convert(string $type, string $file): string 61 | { 62 | $schema = $this->apiManager->getApi($file); 63 | $generator = $this->generatorFactory->factory()->getGenerator($type); 64 | 65 | return (string) $generator->generate($schema); 66 | } 67 | 68 | private function getExamples(): array 69 | { 70 | $examples = []; 71 | $examples[] = [ 72 | 'title' => 'Simple API', 73 | 'description' => 'A simple GET endpoint which returns a hello world message.', 74 | 'file' => __DIR__ . '/../../resources/examples/simple.json', 75 | ]; 76 | 77 | $examples[] = [ 78 | 'title' => 'Argument Query', 79 | 'description' => 'Through the arguments keyword you can map values from the HTTP request to an argument, in this example we map the HTTP query parameters to the startIndex and count argument', 80 | 'file' => __DIR__ . '/../../resources/examples/argument_query.json', 81 | ]; 82 | 83 | $examples[] = [ 84 | 'title' => 'Argument Body', 85 | 'description' => 'In this example we map the HTTP request body to the payload argument', 86 | 'file' => __DIR__ . '/../../resources/examples/argument_body.json', 87 | ]; 88 | 89 | $examples[] = [ 90 | 'title' => 'Throws', 91 | 'description' => 'Through the throws keyword you can define specific error payloads, the generated client will then also throw an exception in case the server returns such an error code', 92 | 'file' => __DIR__ . '/../../resources/examples/exception.json', 93 | ]; 94 | 95 | $examples[] = [ 96 | 'title' => 'Operation group', 97 | 'description' => 'Through the dot notation at the operation key you can group your operations into logical units', 98 | 'file' => __DIR__ . '/../../resources/examples/operation_group.json', 99 | ]; 100 | 101 | return $examples; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /www/src/Controller/Integration.php: -------------------------------------------------------------------------------- 1 | reverseRouter = $reverseRouter; 21 | } 22 | 23 | #[Get] 24 | #[Path('/integration')] 25 | public function show(): Template 26 | { 27 | $clientTypes = []; 28 | $serverTypes = []; 29 | foreach ($this->generatorFactory->factory()->getPossibleTypes() as $type) { 30 | $displayName = TypeName::getDisplayName($type); 31 | if ($displayName === null) { 32 | continue; 33 | } 34 | 35 | if (str_starts_with($type, 'client-')) { 36 | $clientTypes[$type] = $displayName; 37 | } elseif (str_starts_with($type, 'server-')) { 38 | $serverTypes[$type] = $displayName; 39 | } 40 | } 41 | 42 | ksort($clientTypes); 43 | ksort($serverTypes); 44 | 45 | $data = [ 46 | 'method' => explode('::', __METHOD__), 47 | 'title' => 'Integration | TypeAPI', 48 | 'clientTypes' => $clientTypes, 49 | 'serverTypes' => $serverTypes, 50 | ]; 51 | 52 | $templateFile = __DIR__ . '/../../resources/template/integration.php'; 53 | return new Template($data, $templateFile, $this->reverseRouter); 54 | } 55 | 56 | #[Get] 57 | #[Path('/integration/:type')] 58 | public function showType(string $type): Template 59 | { 60 | $registry = $this->generatorFactory->factory(); 61 | if (!in_array($type, $registry->getPossibleTypes())) { 62 | throw new BadRequestException('Provided an invalid type'); 63 | } 64 | 65 | $data = [ 66 | 'title' => TypeName::getDisplayName($type) . ' SDK Integration | TypeAPI', 67 | 'method' => explode('::', __METHOD__), 68 | 'parameters' => ['type' => $type], 69 | 'type' => $type, 70 | 'typeName' => TypeName::getDisplayName($type), 71 | 'description' => file_get_contents(__DIR__ . '/../../resources/template/integration/' . $type . '.html'), 72 | ]; 73 | 74 | $templateFile = __DIR__ . '/../../resources/template/integration/detail.php'; 75 | return new Template($data, $templateFile, $this->reverseRouter); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /www/src/Controller/Specification.php: -------------------------------------------------------------------------------- 1 | reverseRouter = $reverseRouter; 18 | } 19 | 20 | #[Get] 21 | #[Path('/specification')] 22 | public function show(): Template 23 | { 24 | $data = [ 25 | 'method' => explode('::', __METHOD__), 26 | 'title' => 'Specification | TypeAPI', 27 | ]; 28 | 29 | $templateFile = __DIR__ . '/../../resources/template/specification.php'; 30 | return new Template($data, $templateFile, $this->reverseRouter); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /www/src/Model/Generate.php: -------------------------------------------------------------------------------- 1 | namespace = $namespace; 18 | } 19 | public function getNamespace(): ?string 20 | { 21 | return $this->namespace; 22 | } 23 | public function setSchema(?string $schema): void 24 | { 25 | $this->schema = $schema; 26 | } 27 | public function getSchema(): ?string 28 | { 29 | return $this->schema; 30 | } 31 | public function setGRecaptchaResponse(?string $gRecaptchaResponse): void 32 | { 33 | $this->gRecaptchaResponse = $gRecaptchaResponse; 34 | } 35 | public function getGRecaptchaResponse(): ?string 36 | { 37 | return $this->gRecaptchaResponse; 38 | } 39 | public function toRecord(): \PSX\Record\RecordInterface 40 | { 41 | /** @var \PSX\Record\Record $record */ 42 | $record = new \PSX\Record\Record(); 43 | $record->put('namespace', $this->namespace); 44 | $record->put('schema', $this->schema); 45 | $record->put('g-recaptcha-response', $this->gRecaptchaResponse); 46 | return $record; 47 | } 48 | public function jsonSerialize(): object 49 | { 50 | return (object) $this->toRecord()->getAll(); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /www/src/Service/CaptchaVerifier.php: -------------------------------------------------------------------------------- 1 | httpClient = new Client(); 17 | $this->secret = $config->get('recaptcha_secret'); 18 | } 19 | 20 | public function verify(?string $recaptchaResponse): bool 21 | { 22 | if ($recaptchaResponse === null) { 23 | return false; 24 | } 25 | 26 | $response = $this->httpClient->post('https://www.google.com/recaptcha/api/siteverify', [ 27 | 'headers' => [ 28 | 'User-Agent' => 'typeapi.org' 29 | ], 30 | 'form_params' => [ 31 | 'secret' => $this->secret, 32 | 'response' => $recaptchaResponse, 33 | 'remoteip' => $_SERVER['REMOTE_ADDR'] ?? '', 34 | ], 35 | 'verify' => false 36 | ]); 37 | 38 | if ($response->getStatusCode() == 200) { 39 | $data = Json\Parser::decode((string) $response->getBody()); 40 | if (isset($data->success) && $data->success === true) { 41 | return true; 42 | } 43 | } 44 | 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /www/src/Service/TypeName.php: -------------------------------------------------------------------------------- 1 | 'C# Client', 13 | 'client-go' => 'Go Client', 14 | 'client-java' => 'Java Client', 15 | 'client-php' => 'PHP Client', 16 | 'client-python' => 'Python Client', 17 | 'client-typescript' => 'TypeScript Client', 18 | 19 | 'server-csharp' => 'C# Server', 20 | 'server-java' => 'Java Server', 21 | 'server-php' => 'PHP Server', 22 | 'server-python' => 'Python Server', 23 | 'server-typescript' => 'TypeScript Server', 24 | 25 | default => null, 26 | }; 27 | } 28 | } 29 | --------------------------------------------------------------------------------