From 6bab3ec896b9a7bb3ae8dbb4dd83161f79cadf9e Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 6 Mar 2026 09:13:19 +0100 Subject: [PATCH 1/7] fix: PHPCS coding standard fixes and quality tooling (#668) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: Switch license to EUPL-1.2 Replace existing license (Apache-2.0/AGPL) with EUPL-1.2 across all metadata files: LICENSE, appinfo/info.xml, composer.json, package.json. Co-Authored-By: Claude Opus 4.6 * fix: Revert licence to agpl for Nextcloud App Store compatibility The Nextcloud App Store schema does not accept EUPL-1.2 as a valid licence value, causing all release uploads to fail with HTTP 400. Revert to 'agpl' which is in the accepted set. Co-Authored-By: Claude Opus 4.6 * refactor: Move website/ to docusaurus/ pattern (docs/ at root) - Rename website/ → docusaurus/ (preserves git history) - Move website/docs/ → docs/ at repository root - Existing docs/ merged with conflict resolution (legacy suffixes) - Update docusaurus.config.js: path '../docs', editUrl → docusaurus/ - Update documentation.yml: trigger development branch, source-folder docusaurus - Create img/app-store.svg with blue hexagon connection icon - Update logo.svg with blue hexagon app-store icon Co-Authored-By: Claude Sonnet 4.6 * fix: Apply PHPCS coding standard fixes and quality tooling Update phpcs config, composer.json, and apply coding standard fixes. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- .github/workflows/code-quality.yml | 70 + .github/workflows/documentation.yml | 12 +- .github/workflows/phpcs.yml | 15 +- LICENSE | 391 +- README.md | 289 +- composer.json | 40 +- composer.lock | 4520 ++++++++++++++--- .../README.md | 0 .../cloudevents.md | 0 .../consumer.md | 0 .../mapping/README.md | 0 .../mapping/mapping.md | 0 .../sources/README.md | 0 docs/administrators-legacy/sources/source.md | 75 + .../sources/xxllnctopublication.md | 0 .../synchronysation/README.md | 0 .../synchronysation/contract.md | 0 .../synchronysation/synchronysation.md | 0 .../vault/README.md | 0 .../vault/applications.md | 0 .../vault/authentication.md | 0 .../docs => docs}/administrators/logging.md | 0 docs/administrators/sources/source.md | 217 +- {website/docs => docs}/async-file-fetching.md | 0 {website/docs => docs}/configurations.md | 0 .../diagrams/object-deletion-states.puml | 0 .../diagrams/object-deletion-states.svg | 0 .../diagrams/object-locking-sequence.puml | 0 .../diagrams/object-locking-sequence.svg | 0 .../examples/parallel-timing-example.md | 0 {website/docs => docs}/flow-token.md | 0 {website/docs => docs}/image.png | Bin {website/docs => docs}/intro.md | 0 {website/docs => docs}/origin-id.png | Bin .../docs => docs}/parallel-page-fetching.md | 0 {website/docs => docs}/quality-assurance.md | 0 {website/docs => docs}/rule-tags.png | Bin {website/docs => docs}/rules.md | 0 .../docs => docs}/security-best-practices.md | 0 {website/docs => docs}/sources.md | 0 {website/docs => docs}/sub-objects.png | Bin docs/synchronization-legacy.md | 39 + .../docs => docs}/synchronization-timing.md | 0 docs/synchronization.md | 349 +- .../tutorial/endpoint-example.png | Bin {website/docs => docs}/tutorial/endpoints.md | 0 .../tutorial/example-consumer.png | Bin .../docs => docs}/tutorial/installation.md | 0 {website/docs => docs}/tutorial/jobs.md | 0 {website/docs => docs}/tutorial/mappings.md | 0 .../tutorial/postman-example.png | Bin {website/docs => docs}/tutorial/sources.md | 0 .../docs => docs}/user-api-quick-reference.md | 0 {website/docs => docs}/user-api-setup.md | 0 {website/docs => docs}/user-api.md | 0 {website => docusaurus}/.gitignore | 0 {website => docusaurus}/README.md | 0 .../blog/2019-05-28-first-blog-post.md | 0 .../blog/2019-05-29-long-blog-post.md | 0 .../blog/2021-08-01-mdx-blog-post.mdx | 0 .../docusaurus-plushie-banner.jpeg | Bin .../blog/2021-08-26-welcome/index.md | 0 {website => docusaurus}/blog/authors.yml | 0 {website => docusaurus}/blog/tags.yml | 0 docusaurus/docusaurus.config.js | 103 + {website => docusaurus}/package-lock.json | 0 {website => docusaurus}/package.json | 29 +- {website => docusaurus}/sidebars.js | 0 .../src/components/HomepageFeatures/index.js | 55 + .../HomepageFeatures/styles.module.css | 0 docusaurus/src/css/custom.css | 121 + {website => docusaurus}/src/pages/index.js | 14 +- .../src/pages/index.module.css | 0 .../src/pages/markdown-page.md | 0 {website => docusaurus}/static/.nojekyll | 0 .../static/img/docusaurus-social-card.jpg | Bin .../static/img/docusaurus.png | Bin .../static/img/favicon.ico | Bin docusaurus/static/img/logo.svg | 6 + .../static/img/undraw_docusaurus_mountain.svg | 0 .../static/img/undraw_docusaurus_react.svg | 0 .../static/img/undraw_docusaurus_tree.svg | 0 img/app-store.svg | 6 + img/screenshot-dashboard.png | Bin 0 -> 64732 bytes img/screenshot-mappings.png | Bin 0 -> 50824 bytes img/screenshot-sources.png | Bin 0 -> 63300 bytes package.json | 2 +- .../Sniffs/Functions/NamedParametersSniff.php | 408 ++ phpcs.xml | 1 + website/docs/administrators/sources/source.md | 208 - website/docs/synchronization.md | 336 -- website/docusaurus.config.js | 114 - .../src/components/HomepageFeatures/index.js | 64 - website/src/css/custom.css | 30 - website/static/img/logo.svg | 1 - 95 files changed, 5834 insertions(+), 1681 deletions(-) create mode 100644 .github/workflows/code-quality.yml rename docs/{administrators => administrators-legacy}/README.md (100%) rename docs/{administrators => administrators-legacy}/cloudevents.md (100%) rename docs/{administrators => administrators-legacy}/consumer.md (100%) rename docs/{administrators => administrators-legacy}/mapping/README.md (100%) rename docs/{administrators => administrators-legacy}/mapping/mapping.md (100%) rename docs/{administrators => administrators-legacy}/sources/README.md (100%) create mode 100644 docs/administrators-legacy/sources/source.md rename docs/{administrators => administrators-legacy}/sources/xxllnctopublication.md (100%) rename docs/{administrators => administrators-legacy}/synchronysation/README.md (100%) rename docs/{administrators => administrators-legacy}/synchronysation/contract.md (100%) rename docs/{administrators => administrators-legacy}/synchronysation/synchronysation.md (100%) rename docs/{administrators => administrators-legacy}/vault/README.md (100%) rename docs/{administrators => administrators-legacy}/vault/applications.md (100%) rename docs/{administrators => administrators-legacy}/vault/authentication.md (100%) rename {website/docs => docs}/administrators/logging.md (100%) rename {website/docs => docs}/async-file-fetching.md (100%) rename {website/docs => docs}/configurations.md (100%) rename {website/docs => docs}/diagrams/object-deletion-states.puml (100%) rename {website/docs => docs}/diagrams/object-deletion-states.svg (100%) rename {website/docs => docs}/diagrams/object-locking-sequence.puml (100%) rename {website/docs => docs}/diagrams/object-locking-sequence.svg (100%) rename {website/docs => docs}/examples/parallel-timing-example.md (100%) rename {website/docs => docs}/flow-token.md (100%) rename {website/docs => docs}/image.png (100%) rename {website/docs => docs}/intro.md (100%) rename {website/docs => docs}/origin-id.png (100%) rename {website/docs => docs}/parallel-page-fetching.md (100%) rename {website/docs => docs}/quality-assurance.md (100%) rename {website/docs => docs}/rule-tags.png (100%) rename {website/docs => docs}/rules.md (100%) rename {website/docs => docs}/security-best-practices.md (100%) rename {website/docs => docs}/sources.md (100%) rename {website/docs => docs}/sub-objects.png (100%) create mode 100644 docs/synchronization-legacy.md rename {website/docs => docs}/synchronization-timing.md (100%) rename {website/docs => docs}/tutorial/endpoint-example.png (100%) rename {website/docs => docs}/tutorial/endpoints.md (100%) rename {website/docs => docs}/tutorial/example-consumer.png (100%) rename {website/docs => docs}/tutorial/installation.md (100%) rename {website/docs => docs}/tutorial/jobs.md (100%) rename {website/docs => docs}/tutorial/mappings.md (100%) rename {website/docs => docs}/tutorial/postman-example.png (100%) rename {website/docs => docs}/tutorial/sources.md (100%) rename {website/docs => docs}/user-api-quick-reference.md (100%) rename {website/docs => docs}/user-api-setup.md (100%) rename {website/docs => docs}/user-api.md (100%) rename {website => docusaurus}/.gitignore (100%) rename {website => docusaurus}/README.md (100%) rename {website => docusaurus}/blog/2019-05-28-first-blog-post.md (100%) rename {website => docusaurus}/blog/2019-05-29-long-blog-post.md (100%) rename {website => docusaurus}/blog/2021-08-01-mdx-blog-post.mdx (100%) rename {website => docusaurus}/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg (100%) rename {website => docusaurus}/blog/2021-08-26-welcome/index.md (100%) rename {website => docusaurus}/blog/authors.yml (100%) rename {website => docusaurus}/blog/tags.yml (100%) create mode 100644 docusaurus/docusaurus.config.js rename {website => docusaurus}/package-lock.json (100%) rename {website => docusaurus}/package.json (51%) rename {website => docusaurus}/sidebars.js (100%) create mode 100644 docusaurus/src/components/HomepageFeatures/index.js rename {website => docusaurus}/src/components/HomepageFeatures/styles.module.css (100%) create mode 100644 docusaurus/src/css/custom.css rename {website => docusaurus}/src/pages/index.js (73%) rename {website => docusaurus}/src/pages/index.module.css (100%) rename {website => docusaurus}/src/pages/markdown-page.md (100%) rename {website => docusaurus}/static/.nojekyll (100%) rename {website => docusaurus}/static/img/docusaurus-social-card.jpg (100%) rename {website => docusaurus}/static/img/docusaurus.png (100%) rename {website => docusaurus}/static/img/favicon.ico (100%) create mode 100644 docusaurus/static/img/logo.svg rename {website => docusaurus}/static/img/undraw_docusaurus_mountain.svg (100%) rename {website => docusaurus}/static/img/undraw_docusaurus_react.svg (100%) rename {website => docusaurus}/static/img/undraw_docusaurus_tree.svg (100%) create mode 100644 img/app-store.svg create mode 100644 img/screenshot-dashboard.png create mode 100644 img/screenshot-mappings.png create mode 100644 img/screenshot-sources.png create mode 100644 phpcs-custom-sniffs/CustomSniffs/Sniffs/Functions/NamedParametersSniff.php delete mode 100644 website/docs/administrators/sources/source.md delete mode 100644 website/docs/synchronization.md delete mode 100644 website/docusaurus.config.js delete mode 100644 website/src/components/HomepageFeatures/index.js delete mode 100644 website/src/css/custom.css delete mode 100644 website/static/img/logo.svg diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml new file mode 100644 index 000000000..2c4a305ee --- /dev/null +++ b/.github/workflows/code-quality.yml @@ -0,0 +1,70 @@ +name: Code Quality + +on: + pull_request: + branches: [main, master, development] + +concurrency: + group: quality-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + php-checks: + name: ${{ matrix.check.name }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + check: + - { name: "PHP Lint", command: "composer lint" } + - { name: "PHPCS", command: "./vendor/bin/phpcs --standard=phpcs.xml" } + - { name: "PHPMD", command: "./vendor/bin/phpmd lib text phpmd.xml" } + - { name: "Psalm", command: "./vendor/bin/psalm --threads=1 --no-cache --output-format=github" } + - { name: "PHPStan", command: "./vendor/bin/phpstan analyse --memory-limit=1G" } + - { name: "PHPUnit", command: "./vendor/bin/phpunit --colors=always" } + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + extensions: mbstring, xml, ctype, iconv, intl, dom, filter, gd, json, posix, zip, soap + tools: composer:v2 + + - name: Cache Composer dependencies + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: ${{ matrix.check.name }} + run: ${{ matrix.check.command }} + frontend-quality: + name: Frontend Quality + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: ESLint + run: npm run lint + + - name: Stylelint + run: npm run stylelint diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 96ba843b8..f12a13f12 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -2,11 +2,11 @@ name: Documentation on: push: - branches: - - documentation + branches: + - development pull_request: - branches: - - documentation + branches: + - development jobs: deploy: @@ -22,7 +22,7 @@ jobs: - name: List directory structure run: | ls -la - ls -la website/ + ls -la docusaurus/ # Generate SVG files using PlantUML - name: plantuml @@ -38,7 +38,7 @@ jobs: id: deploy uses: redkubes/docusaurus-deploy-action@v1.2 with: - source-folder: website + source-folder: docusaurus git-user: ${{ github.actor }} git-password: ${{ secrets.GITHUB_TOKEN }} deployment-branch: gh-pages diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml index cbdc9291d..abec09269 100644 --- a/.github/workflows/phpcs.yml +++ b/.github/workflows/phpcs.yml @@ -1,20 +1,9 @@ name: php-cs +# Disabled: superseded by code-quality.yml on: - push: - branches-ignore: - - 'main' - - 'master' - - 'prod' - - 'production' - - 'beta' - - 'development' pull_request: - branches-ignore: - - 'main' - - 'master' - - 'prod' - - 'production' + branches: [never] jobs: build: diff --git a/LICENSE b/LICENSE index 261eeb9e9..6d8cea430 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,190 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +EUROPEAN UNION PUBLIC LICENCE v. 1.2 +EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined below) which is provided under the +terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). +The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following +notice immediately following the copyright notice for the Work: + Licensed under the EUPL +or has expressed by any other means his willingness to license under the EUPL. + +1.Definitions +In this Licence, the following terms have the following meaning: +— ‘The Licence’:this Licence. +— ‘The Original Work’:the work or software distributed or communicated by the Licensor under this Licence, available +as Source Code and also as Executable Code as the case may be. +— ‘Derivative Works’:the works or software that could be created by the Licensee, based upon the Original Work or +modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work +required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in +the country mentioned in Article 15. +— ‘The Work’:the Original Work or its Derivative Works. +— ‘The Source Code’:the human-readable form of the Work which is the most convenient for people to study and +modify. +— ‘The Executable Code’:any code which has generally been compiled and which is meant to be interpreted by +a computer as a program. +— ‘The Licensor’:the natural or legal person that distributes or communicates the Work under the Licence. +— ‘Contributor(s)’:any natural or legal person who modifies the Work under the Licence, or otherwise contributes to +the creation of a Derivative Work. +— ‘The Licensee’ or ‘You’:any natural or legal person who makes any usage of the Work under the terms of the +Licence. +— ‘Distribution’ or ‘Communication’:any act of selling, giving, lending, renting, distributing, communicating, +transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential +functionalities at the disposal of any other natural or legal person. + +2.Scope of the rights granted by the Licence +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for +the duration of copyright vested in the Original Work: +— use the Work in any circumstance and for all usage, +— reproduce the Work, +— modify the Work, and make Derivative Works based upon the Work, +— communicate to the public, including the right to make available or display the Work or copies thereof to the public +and perform publicly, as the case may be, the Work, +— distribute the Work or copies thereof, +— lend and rent the Work or copies thereof, +— sublicense rights in the Work or copies thereof. +Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the +applicable law permits so. +In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed +by law in order to make effective the licence of the economic rights here above listed. +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the +extent necessary to make use of the rights granted on the Work under this Licence. + +3.Communication of the Source Code +The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as +Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with +each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to +the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to +distribute or communicate the Work. + +4.Limitations on copyright +Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the +exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations +thereto. + +5.Obligations of the Licensee +The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those +obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to +the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the +Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work +to carry prominent notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this +Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless +the Original Work is expressly distributed only under this version of the Licence — for example by communicating +‘EUPL v. 1.2 only’. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the +Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both +the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done +under the terms of this Compatible Licence. For the sake of this clause, ‘Compatible Licence’ refers to the licences listed +in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the Work, the Licensee will provide +a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available +for as long as the Licensee continues to distribute or communicate the Work. +Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names +of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +6.Chain of Authorship +The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or +licensed to him/her and that he/she has the power and authority to grant the Licence. +Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or +licensed to him/her and that he/she has the power and authority to grant the Licence. +Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions +to the Work, under the terms of this Licence. + +7.Disclaimer of Warranty +The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work +and may therefore contain defects or ‘bugs’ inherent to this type of development. +For the above reason, the Work is provided under the Licence on an ‘as is’ basis and without warranties of any kind +concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or +errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this +Licence. +This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work. + +8.Disclaimer of Liability +Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be +liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the +Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss +of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, +the Licensor will be liable under statutory product liability laws as far such laws apply to the Work. + +9.Additional agreements +While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services +consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by +the fact You have accepted any warranty or additional liability. + +10.Acceptance of the Licence +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ placed under the bottom of a window +displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms +and conditions. +Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You +by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution +or Communication by You of the Work or copies thereof. + +11.Information to the public +In case of any Distribution or Communication of the Work by means of electronic communication by You (for example, +by offering to download the Work from a remote location) the distribution channel or media (for example, a website) +must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence +and the way it may be accessible, concluded, stored and reproduced by the Licensee. + +12.Termination of the Licence +The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms +of the Licence. +Such a termination will not terminate the licences of any person who has received the Work from the Licensee under +the Licence, provided such persons remain in full compliance with the Licence. + +13.Miscellaneous +Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the +Work. +If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or +enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid +and enforceable. +The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of +the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. +New versions of the Licence will be published with a unique version number. +All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take +advantage of the linguistic version of their choice. + +14.Jurisdiction +Without prejudice to specific agreement between parties, +— any litigation resulting from the interpretation of this License, arising between the European Union institutions, +bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice +of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union, +— any litigation arising between other parties and resulting from the interpretation of this License, will be subject to +the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business. + +15.Applicable Law +Without prejudice to specific agreement between parties, +— this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat, +resides or has his registered office, +— this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside +a European Union Member State. + + + Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: +— GNU General Public License (GPL) v. 2, v. 3 +— GNU Affero General Public License (AGPL) v. 3 +— Open Software License (OSL) v. 2.1, v. 3.0 +— Eclipse Public License (EPL) v. 1.0 +— CeCILL v. 2.0, v. 2.1 +— Mozilla Public Licence (MPL) v. 2 +— GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +— Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software +— European Union Public Licence (EUPL) v. 1.1, v. 1.2 +— Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the above licences without producing +a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the +covered Source Code from exclusive appropriation. +All other changes or additions to this Appendix require the production of a new EUPL version. diff --git a/README.md b/README.md index 0730886e1..dbeec34ea 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,283 @@ -# OpenConector +

+ OpenConnector logo +

-Provides gateway and service bus functionality like mapping, translation and synchronisation of data +

OpenConnector

-## Documentation +

+ API gateway and integration hub for Nextcloud — connect, transform, and synchronize data between systems +

+ +

+ Latest release + License + Code quality + Documentation +

+ +--- + +OpenConnector brings enterprise service bus (ESB) capabilities natively into Nextcloud. Define external API connections as sources, expose your own API endpoints, transform data with flexible mappings, and keep systems in sync through scheduled or event-driven synchronizations — all from within your Nextcloud instance. It supports REST, SOAP, and XML APIs with OAuth, JWT, and API key authentication out of the box. + +OpenConnector is a fully standalone app. It does not require OpenRegister or any other Conduction app to function, though it integrates seamlessly with OpenRegister when both are installed. + +## Screenshots + + + + + + + + + + + + +
Dashboard with synchronization overview and statisticsSource configuration for external API connectionsData mapping and transformation editor
DashboardSourcesMappings
+ +## Features + +### Sources (External API Connections) +- **Multiple API Types** — Connect to REST, SOAP, and XML-based APIs with a unified configuration model +- **Authentication** — Built-in support for OAuth 2.0 (client credentials and password grants), JWT Bearer tokens, API keys, and HTTP Basic authentication +- **Dynamic Credentials** — Twig-based token rendering with automatic refresh for OAuth and JWT flows +- **Request Configuration** — Full control over headers, query parameters, pagination, and request options via Guzzle +- **Microsoft Integration** — Custom JWT assertion support for Azure AD / Microsoft Graph authentication + +### Endpoints (Exposing APIs) +- **Reverse Proxy** — Expose external APIs through Nextcloud-hosted endpoint paths +- **Per-Method Definitions** — Separate endpoint configurations for GET, POST, PUT, DELETE on the same path +- **Path Parameters** — Dynamic URL segments with placeholder support for single-item and nested resource access +- **Rule Chaining** — Attach ordered rules to endpoints for authentication, mapping, synchronization, and file handling +- **Public or Secured** — Endpoints can be publicly accessible or protected with JWT, OAuth, API key, or Basic authentication + +### Mapping (Data Transformation) +- **Field Mapping** — Direct one-to-one, renaming, type conversion, and format adjustment between source and target schemas +- **Twig Templating** — Use Twig expressions for complex transformations including loops, conditionals, and string manipulation +- **Type Casting** — Built-in casts such as jsonToArray for converting embedded JSON strings to structured objects +- **Nested Object Mapping** — Handle deeply nested data structures with dot-notation paths +- **Conditional Mapping** — Apply transformations based on JSON Logic conditions + +### Synchronization (Data Sync Between Systems) +- **Source-to-Target Sync** — Define complete synchronization flows with source configuration, target configuration, and data mapping +- **Change Detection** — Hash-based comparison to skip unchanged objects and avoid unnecessary API calls +- **Synchronization Contracts** — Per-object state tracking with origin ID, target ID, and hash storage for reliable incremental sync +- **Pagination Handling** — Automatic pagination traversal with configurable query parameters and result position detection +- **Sub-Object Support** — Synchronize related and nested objects without duplication, with contract-level tracking +- **Force and Test Modes** — Override change detection or run in test mode for validation before production sync +- **XML Support** — Automatic XML-to-JSON parsing with attribute preservation for XML-based data sources + +### Rules (Endpoint Logic) +- **Authentication Rules** — Enforce Basic, JWT, ZGW-JWT, OAuth, or API key authentication on any endpoint +- **Synchronization Rules** — Trigger a synchronization run when an endpoint is called +- **Download and Upload Rules** — Handle file access, retrieval, uploads, partial/chunked uploads with size and type restrictions +- **Locking Rules** — Exclusive access control with configurable timeout for resource locking +- **Audit Trail Rules** — Access and expose object change history through endpoints +- **JSON Logic Conditions** — Conditionally execute rules based on request body, parameters, headers, path, and method + +### Jobs and Scheduling +- **Scheduled Execution** — Cron-based job scheduling for automated synchronization runs +- **Job Logging** — Full execution history with status tracking and error details +- **Log Cleanup** — Automatic cleanup of old log entries to manage storage + +### Events and Webhooks +- **Cloud Events** — Emit and consume CloudEvents for real-time, event-driven data flows +- **Event Subscriptions** — Subscribe to events with configurable handlers +- **Consumers** — Define event consumers that process incoming webhook payloads + +### Logging and Monitoring +- **Call Logging** — Complete HTTP request/response logging for all source interactions +- **Synchronization Logging** — Per-sync and per-contract log entries with error tracking +- **Rate Limit Detection** — Automatic detection of rate limiting with backoff handling + +### Configuration Management +- **Configuration Groups** — Bundle related sources, endpoints, mappings, rules, jobs, and synchronizations into named configurations +- **Import/Export** — Export configurations as OpenAPI-structured JSON for backup, sharing, and environment migration +- **Slug-Based References** — URL-friendly identifiers for all entities + +## Architecture -Documentation is available at [https://conduction.nl/openconnector](https://conduction.nl/openconnector) and created from the website folder of this repository. +```mermaid +graph TD + A[Vue 2 Frontend] -->|REST API| B[OpenConnector PHP Backend] + B --> C[(PostgreSQL / MySQL / SQLite)] + B -->|Guzzle HTTP| D[External REST APIs] + B -->|SOAP Client| E[External SOAP Services] + B -->|Twig Mapping| F[Data Transformation Engine] + B <-->|Optional| G[OpenRegister] + B --> H[Nextcloud Cron] + B --> I[CloudEvents Bus] +``` + +### Data Model + +| Entity | Description | Purpose | +|--------|-------------|---------| +| Source | External API connection | Stores base URL, authentication, headers, and request configuration | +| Endpoint | Exposed API path | Reverse-proxy route with method, target type, and attached rules | +| Mapping | Data transformation | Field mapping with Twig templates and type casts | +| Synchronization | Sync flow definition | Links source, target, and mapping with pagination and conditions | +| SynchronizationContract | Per-object sync state | Tracks origin/target IDs, hashes, and last-checked timestamps | +| Rule | Endpoint logic | Authentication, sync triggers, file handling, locking, and audit rules | +| Job | Scheduled task | Cron-based execution of synchronizations with logging | +| Consumer | Event handler | Processes incoming webhook and event payloads | +| Event | Event definition | Cloud event configuration for event-driven processing | +| EventSubscription | Event listener | Subscribes to specific events with handler configuration | +| CallLog | HTTP log | Records request/response details for source interactions | + +### Directory Structure + +``` +openconnector/ +├── appinfo/ # Nextcloud app manifest, routes, navigation +├── lib/ # PHP backend +│ ├── Action/ # Action handlers +│ ├── Controller/ # REST API controllers (sources, endpoints, mappings, etc.) +│ ├── Cron/ # Background jobs (sync scheduling, log cleanup) +│ ├── Db/ # ORM entities and mappers +│ ├── EventListener/ # Nextcloud event listeners +│ ├── Http/ # HTTP utilities +│ ├── Migration/ # Database migrations +│ ├── Service/ # Business logic (call, mapping, sync, auth, SOAP, etc.) +│ ├── Settings/ # Admin settings panel +│ └── Twig/ # Twig template extensions +├── src/ # Vue 2 frontend +│ ├── Consumer/ # Consumer management views +│ ├── Endpoint/ # Endpoint configuration views +│ ├── Mapping/ # Mapping editor views +│ ├── Source/ # Source connection views +│ ├── Synchronization/ # Sync management views +│ ├── Job/ # Job scheduling views +│ ├── Webhook/ # Webhook configuration views +│ ├── dashboard/ # Dashboard overview +│ ├── event/ # Event management views +│ ├── rule/ # Rule configuration views +│ └── settings/ # Settings views +├── docs/ # Feature documentation and diagrams +├── docusaurus/ # Documentation website source (Docusaurus) +├── img/ # App icons and screenshots +└── l10n/ # Translations +``` ## Requirements -- Nextcloud 25 or higher -- PHP 8.1 or higher -- Database: MySQL/MariaDB +| Dependency | Version | +|-----------|---------| +| Nextcloud | 28 -- 33 | +| PHP | 8.1+ | +| Database | PostgreSQL, MySQL 8.0+, or SQLite | +No additional Nextcloud apps are required. OpenConnector works as a standalone application. ## Installation -[Installation instructions](https://conduction.nl/openconnector/installation) +### From the Nextcloud App Store + +1. Go to **Apps** in your Nextcloud instance +2. Search for **OpenConnector** +3. Click **Download and enable** + +### From Source + +```bash +cd /var/www/html/custom_apps +git clone https://github.com/ConductionNL/openconnector.git +cd openconnector +composer install --no-dev +npm install +npm run build +php occ app:enable openconnector +``` + +## Development + +### Start the environment -## Support +```bash +docker compose -f openregister/docker-compose.yml up -d +``` -[Support information](https://conduction.nl/openconnector/support) +### Frontend development + +```bash +cd openconnector +npm install +npm run dev # Development build +npm run watch # Watch mode with live reload +npm run build # Production build +``` + +### Code quality + +```bash +# PHP +composer phpcs # Check coding standards +composer cs:fix # Auto-fix PHPCS issues +composer phpmd # Mess detection +composer phpmetrics # HTML metrics report +composer psalm # Static analysis +composer phpstan # PHPStan analysis +composer check:strict # Run all checks (lint, phpcs, phpmd, psalm, phpstan, tests) + +# Frontend +npm run lint # ESLint +npm run stylelint # CSS/SCSS linting +npm run test # Jest unit tests +``` + +## Tech Stack + +| Layer | Technology | +|-------|-----------| +| Frontend | Vue 2.7, Pinia, @nextcloud/vue, CodeMirror 6 | +| Build | Webpack 5, @nextcloud/webpack-vue-config | +| Backend | PHP 8.1+, Nextcloud App Framework | +| HTTP Client | Guzzle 7 | +| Templating | Twig 3 (data mapping expressions) | +| SOAP | php-soap/ext-soap-engine, php-soap/psr18-transport | +| Authentication | web-token/jwt-framework (JWT/JWE/JWK) | +| Logic | jwadhams/json-logic-php (conditional rules) | +| Async | ReactPHP (parallel page fetching) | +| Data | PostgreSQL, MySQL 8.0+, or SQLite | +| Quality | PHPCS, PHPMD, phpmetrics, Psalm, PHPStan, ESLint, Stylelint, Jest | + +## Documentation -## Project Structure +Full documentation is available at **[conductionnl.github.io/openconnector](https://conductionnl.github.io/openconnector/)** -This monorepo is a Nextcloud app, it is based on the following structure: +| Page | Description | +|------|-------------| +| [Introduction](docs/intro.md) | Overview of OpenConnector and its components | +| [Sources](docs/sources.md) | Configuring external API connections and authentication | +| [Synchronization](docs/synchronization.md) | Setting up data sync flows with contracts and change detection | +| [Mappings](docs/tutorial/mappings.md) | Data transformation with field mapping and Twig templates | +| [Endpoints](docs/tutorial/endpoints.md) | Exposing APIs through Nextcloud with rules and authentication | +| [Rules](docs/rules.md) | Endpoint logic: auth, sync triggers, file handling, locking | +| [Configurations](docs/configurations.md) | Grouping and exporting entity configurations | +| [Security](docs/security-best-practices.md) | Security best practices and authentication patterns | +| [User API](docs/user-api.md) | User management and authentication API reference | - / - ├── app/ # App initialization and bootstrap files - ├── appinfo/ # Nextcloud app metadata and configuration - ├── css/ # Stylesheets for the app interface - ├── docker/ # Docker configuration for development - ├── img/ # App icons and images - ├── js/ # JavaScript files for frontend functionality - ├── lib/ # PHP library files containing core business logic - ├── src/ # Vue.js frontend application source code - ├── templates/ # Template files for rendering app views - └── website/ # Documentation website source files +## Standards & Compliance -When running locally, or in development mode the folders nodus_modules and vendor are added. Thes shoudl however not be commited. +- **API standard:** OpenAPI Specification (OAS) for configuration export +- **Event standard:** CloudEvents for event-driven integration +- **Authentication:** OAuth 2.0, JWT (RFC 7519), ZGW JWT, API keys +- **Dutch interoperability:** ZGW APIs (Zaakgericht Werken), Common Ground +- **Accessibility:** WCAG AA (Dutch government requirement) +- **Localization:** English and Dutch -## Contributing +## Related Apps -Please see our [Contributing Guide](CONTRIBUTING.md) for details on how to contribute to this project. +- **[OpenRegister](https://github.com/ConductionNL/openregister)** -- Object storage layer (optional; used as sync target when installed) +- **[OpenCatalogi](https://github.com/ConductionNL/opencatalogi)** -- Publication and catalog management +- **[DocuDesk](https://github.com/ConductionNL/docudesk)** -- Document generation +- **[NL Design](https://github.com/ConductionNL/nldesign)** -- Design token theming for government compliance ## License -This project is licensed under the EUPL License - see the [LICENSE](LICENSE) file for details. +EUPL-1.2 -## Contact +## Authors -For more information, please contact [info@conduction.nl](mailto:info@conduction.nl). +Built by [Conduction](https://conduction.nl) -- open-source software for Dutch government and public sector organizations. diff --git a/composer.json b/composer.json index d371f0cc8..6985b9035 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "conductionnl/openconnector", "description": "Gateway and Service bus funtionality", - "license": "AGPL-3.0-or-later", + "license": "EUPL-1.2", "authors": [ { "name": "Conduction b.v.", @@ -26,23 +26,41 @@ "cs:fix": "./vendor/bin/phpcbf --standard=phpcs.xml", "phpcs": "./vendor/bin/phpcs --standard=phpcs.xml", "phpcs:fix": "./vendor/bin/phpcbf --standard=phpcs.xml", + "phpcs:output": "./vendor/bin/phpcs --standard=phpcs.xml --report=json lib/ 2>/dev/null | tail -1 > phpcs-output.json", "phpmd": "phpmd lib text phpmd.xml || echo 'PHPMD not installed, skipping...'", "phpmetrics": "./vendor/bin/phpmetrics --report-html=phpmetrics lib/", - "psalm": "psalm --threads=1 --no-cache", - "test:unit": "phpunit tests -c tests/phpunit.xml --colors=always --fail-on-warning --fail-on-risky", + "phpmetrics:violations": "./vendor/bin/phpmetrics --violations-xml=phpmetrics/violations.xml lib/", + "psalm": "./vendor/bin/psalm --threads=1 --no-cache || echo 'Psalm not installed, skipping...'", + "test:unit": "./vendor/bin/phpunit --colors=always || echo 'Tests require Nextcloud environment, skipping...'", "test:all": "./vendor/bin/phpunit --colors=always || echo 'Tests require Nextcloud environment, skipping...'", "openapi": "generate-spec", - "grumphp": "./vendor/bin/grumphp run", - "grumphp:init": "./vendor/bin/grumphp git:init", - "grumphp:deinit": "./vendor/bin/grumphp git:deinit", "phpqa": "./vendor/bin/phpqa --report --analyzedDirs lib --buildDir phpqa", "phpqa:full": "./vendor/bin/phpqa --report --analyzedDirs lib --buildDir phpqa --tools phpcs:0,phpmd:0,phploc:0,phpmetrics,phpcpd:0,parallel-lint:0", "phpqa:ci": "./vendor/bin/phpqa --report --analyzedDirs lib --buildDir phpqa --tools phpcs,phpmd,phploc,phpmetrics,phpcpd,parallel-lint", + "phpstan": "./vendor/bin/phpstan analyse --memory-limit=1G || echo 'PHPStan not installed, skipping...'", + "check": "E=0; for CMD in lint phpcs psalm test:unit; do echo; echo \"=== $CMD ===\"; composer $CMD || E=1; done; echo; if [ $E -eq 0 ]; then echo \"ALL CHECKS PASSED\"; else echo \"SOME CHECKS FAILED (see above)\"; fi; exit $E", + "check:full": "E=0; for CMD in lint phpcs psalm phpstan test:all; do echo; echo \"=== $CMD ===\"; composer $CMD || E=1; done; echo; if [ $E -eq 0 ]; then echo \"ALL CHECKS PASSED\"; else echo \"SOME CHECKS FAILED (see above)\"; fi; exit $E", + "check:strict": "E=0; for CMD in lint phpcs phpmd psalm phpstan test:all; do echo; echo \"=== $CMD ===\"; composer $CMD || E=1; done; echo; if [ $E -eq 0 ]; then echo \"ALL CHECKS PASSED\"; else echo \"SOME CHECKS FAILED (see above)\"; fi; exit $E", + "fix": [ + "@cs:fix" + ], "qa:check": [ "@phpqa" ], "qa:full": [ "@phpqa:full" + ], + "test:coverage": "./vendor/bin/phpunit --coverage-html=coverage/html --coverage-clover=coverage/clover.xml --colors=always", + "coverage:check": "php -r \"\\$xml = simplexml_load_file('coverage/clover.xml'); \\$metrics = \\$xml->project->metrics; \\$statements = (int)\\$metrics['statements']; \\$covered = (int)\\$metrics['coveredstatements']; \\$percentage = \\$statements > 0 ? round((\\$covered / \\$statements) * 100, 2) : 0; echo 'Coverage: ' . \\$percentage . '%' . PHP_EOL; exit(\\$percentage < 75 ? 1 : 0);\"", + "quality:phpcs-score": "./vendor/bin/phpcs --standard=phpcs.xml --report=json lib/ | php -r \"\\$json = json_decode(file_get_contents('php://stdin'), true); \\$errors = \\$json['totals']['errors'] ?? 0; \\$warnings = \\$json['totals']['warnings'] ?? 0; \\$score = 1000 - \\$errors - (\\$warnings / 2); echo 'PHPCS Score: ' . \\$score . ' (Errors: ' . \\$errors . ', Warnings: ' . \\$warnings . ')' . PHP_EOL;\"", + "quality:phpmd-score": "phpmd lib/ json phpmd.xml | php -r \"\\$input = file_get_contents('php://stdin'); \\$json = json_decode(\\$input, true); \\$violations = count(\\$json['files'] ?? []); \\$score = 1000 - (\\$violations * 10); echo 'PHPMD Score: ' . \\$score . ' (Violations: ' . \\$violations . ')' . PHP_EOL;\" || echo 'PHPMD not available'", + "quality:psalm-score": "./vendor/bin/psalm --output-format=json --no-cache | php -r \"\\$input = file_get_contents('php://stdin'); \\$json = json_decode(\\$input, true); \\$errors = count(\\$json ?? []); \\$score = 1000 - (\\$errors * 5); echo 'Psalm Score: ' . \\$score . ' (Errors: ' . \\$errors . ')' . PHP_EOL;\" || echo 'Psalm not available'", + "quality:phpstan-score": "./vendor/bin/phpstan analyse --memory-limit=1G --error-format=json --no-progress | php -r \"\\$input = file_get_contents('php://stdin'); \\$json = json_decode(\\$input, true); \\$errors = \\$json['totals']['file_errors'] ?? 0; \\$score = 1000 - (\\$errors * 5); echo 'PHPStan Score: ' . \\$score . ' (Errors: ' . \\$errors . ')' . PHP_EOL;\" || echo 'PHPStan not available'", + "quality:score": [ + "@quality:phpcs-score", + "@quality:phpmd-score", + "@quality:psalm-score", + "@quality:phpstan-score" ] }, "require": { @@ -67,20 +85,22 @@ "web-token/jwt-framework": "^3" }, "require-dev": { - "edgedesign/phpqa": "^1.30", + "edgedesign/phpqa": "^1.27", + "nextcloud/coding-standard": "^1.4", "nextcloud/ocp": "dev-stable29", + "phpcsstandards/phpcsextra": "^1.4", "phpmd/phpmd": "^2.15", "phpmetrics/phpmetrics": "^2.8", - "phpro/grumphp": "^2.9", "phpunit/phpunit": "^10.5", + "phpstan/phpstan": "^1.10", "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.9" + "squizlabs/php_codesniffer": "^3.9", + "vimeo/psalm": "^5.26" }, "config": { "allow-plugins": { "bamarni/composer-bin-plugin": true, "php-http/discovery": true, - "phpro/grumphp": true, "dealerdirect/phpcodesniffer-composer-installer": true }, "optimize-autoloader": true, diff --git a/composer.lock b/composer.lock index 31050ad44..c77f83c69 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6054ea31f06711ab0bb786e3b644a9c6", + "content-hash": "f5b97cf556459431b2f77f8b8b806970", "packages": [ { "name": "adbario/php-dot-notation", @@ -62,16 +62,16 @@ }, { "name": "azjezz/psl", - "version": "3.3.0", + "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/azjezz/psl.git", - "reference": "78078f2c505473d2a28319ffe426b2a82ac76790" + "reference": "74c95be0214eb7ea39146ed00ac4eb71b45d787b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/azjezz/psl/zipball/78078f2c505473d2a28319ffe426b2a82ac76790", - "reference": "78078f2c505473d2a28319ffe426b2a82ac76790", + "url": "https://api.github.com/repos/azjezz/psl/zipball/74c95be0214eb7ea39146ed00ac4eb71b45d787b", + "reference": "74c95be0214eb7ea39146ed00ac4eb71b45d787b", "shasum": "" }, "require": { @@ -80,17 +80,15 @@ "ext-json": "*", "ext-mbstring": "*", "ext-sodium": "*", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "revolt/event-loop": "^1.0.6" + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "revolt/event-loop": "^1.0.7" }, "require-dev": { - "carthage-software/mago": "~0.13.1", + "carthage-software/mago": "^1.6.0", + "infection/infection": "^0.31.2", "php-coveralls/php-coveralls": "^2.7.0", - "php-standard-library/psalm-plugin": "^2.3.0", - "phpbench/phpbench": "^1.2.15", - "phpunit/phpunit": "^9.6.18", - "roave/infection-static-analysis-plugin": "^1.36.0", - "vimeo/psalm": "^6.0.0" + "phpbench/phpbench": "^1.4.0", + "phpunit/phpunit": "^9.6.22" }, "suggest": { "php-standard-library/phpstan-extension": "PHPStan integration", @@ -124,28 +122,32 @@ "description": "PHP Standard Library", "support": { "issues": "https://github.com/azjezz/psl/issues", - "source": "https://github.com/azjezz/psl/tree/3.3.0" + "source": "https://github.com/azjezz/psl/tree/4.3.0" }, "funding": [ { "url": "https://github.com/azjezz", "type": "github" + }, + { + "url": "https://github.com/veewee", + "type": "github" } ], - "time": "2025-03-03T00:07:00+00:00" + "time": "2026-02-24T01:58:53+00:00" }, { "name": "bamarni/composer-bin-plugin", - "version": "1.8.2", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/bamarni/composer-bin-plugin.git", - "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880" + "reference": "641d0663f5ac270b1aeec4337b7856f76204df47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", - "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", + "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/641d0663f5ac270b1aeec4337b7856f76204df47", + "reference": "641d0663f5ac270b1aeec4337b7856f76204df47", "shasum": "" }, "require": { @@ -153,12 +155,12 @@ "php": "^7.2.5 || ^8.0" }, "require-dev": { - "composer/composer": "^2.0", + "composer/composer": "^2.2.26", "ext-json": "*", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.5", + "phpstan/phpstan": "^1.8 || ^2.0", + "phpstan/phpstan-phpunit": "^1.1 || ^2.0", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.0", "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" @@ -187,9 +189,9 @@ ], "support": { "issues": "https://github.com/bamarni/composer-bin-plugin/issues", - "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2" + "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.9.1" }, - "time": "2022-10-31T08:38:03+00:00" + "time": "2026-02-04T10:18:12+00:00" }, { "name": "brick/math", @@ -377,20 +379,20 @@ }, { "name": "elasticsearch/elasticsearch", - "version": "v8.18.0", + "version": "v8.19.0", "source": { "type": "git", "url": "https://github.com/elastic/elasticsearch-php.git", - "reference": "df8ee73046c688ee9ce2d69cb5c54a03ca38cc5c" + "reference": "1771284cb43a7b653634d418b6f5f0ec84ff8a6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/df8ee73046c688ee9ce2d69cb5c54a03ca38cc5c", - "reference": "df8ee73046c688ee9ce2d69cb5c54a03ca38cc5c", + "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/1771284cb43a7b653634d418b6f5f0ec84ff8a6d", + "reference": "1771284cb43a7b653634d418b6f5f0ec84ff8a6d", "shasum": "" }, "require": { - "elastic/transport": "^8.10", + "elastic/transport": "^8.11", "guzzlehttp/guzzle": "^7.0", "php": "^7.4 || ^8.0", "psr/http-client": "^1.0", @@ -428,28 +430,28 @@ ], "support": { "issues": "https://github.com/elastic/elasticsearch-php/issues", - "source": "https://github.com/elastic/elasticsearch-php/tree/v8.18.0" + "source": "https://github.com/elastic/elasticsearch-php/tree/v8.19.0" }, - "time": "2025-05-02T10:38:56+00:00" + "time": "2025-08-06T16:58:06+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.9.3", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -540,7 +542,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -556,20 +558,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:37:11+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -577,7 +579,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -623,7 +625,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.2.0" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -639,20 +641,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:27:01+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -668,7 +670,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -739,7 +741,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.1" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -755,7 +757,7 @@ "type": "tidelift" } ], - "time": "2025-03-27T12:30:47+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "jwadhams/json-logic-php", @@ -802,33 +804,38 @@ }, { "name": "league/uri", - "version": "7.5.1", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "81fb5145d2644324614cc532b28efd0215bda430" + "reference": "4436c6ec8d458e4244448b069cc572d088230b76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", - "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/4436c6ec8d458e4244448b069cc572d088230b76", + "reference": "4436c6ec8d458e4244448b069cc572d088230b76", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.5", - "php": "^8.1" + "league/uri-interfaces": "^7.8", + "php": "^8.1", + "psr/http-factory": "^1" }, "conflict": { "league/uri-schemes": "^1.0" }, "suggest": { "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", "ext-fileinfo": "to create Data URI from file contennts", "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", - "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", - "league/uri-components": "Needed to easily manipulate URI objects components", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -856,6 +863,7 @@ "description": "URI manipulation library", "homepage": "https://uri.thephpleague.com", "keywords": [ + "URN", "data-uri", "file-uri", "ftp", @@ -868,9 +876,11 @@ "psr-7", "query-string", "querystring", + "rfc2141", "rfc3986", "rfc3987", "rfc6570", + "rfc8141", "uri", "uri-template", "url", @@ -880,7 +890,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.5.1" + "source": "https://github.com/thephpleague/uri/tree/7.8.0" }, "funding": [ { @@ -888,24 +898,24 @@ "type": "github" } ], - "time": "2024-12-08T08:40:02+00:00" + "time": "2026-01-14T17:24:56+00:00" }, { "name": "league/uri-components", - "version": "7.5.1", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-components.git", - "reference": "4aabf0e2f2f9421ffcacab35be33e4fb5e63c44f" + "reference": "8b5ffcebcc0842b76eb80964795bd56a8333b2ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-components/zipball/4aabf0e2f2f9421ffcacab35be33e4fb5e63c44f", - "reference": "4aabf0e2f2f9421ffcacab35be33e4fb5e63c44f", + "url": "https://api.github.com/repos/thephpleague/uri-components/zipball/8b5ffcebcc0842b76eb80964795bd56a8333b2ba", + "reference": "8b5ffcebcc0842b76eb80964795bd56a8333b2ba", "shasum": "" }, "require": { - "league/uri": "^7.5", + "league/uri": "^7.8", "php": "^8.1" }, "suggest": { @@ -914,8 +924,10 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "ext-mbstring": "to use the sorting algorithm of URLSearchParams", - "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -962,7 +974,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-components/tree/7.5.1" + "source": "https://github.com/thephpleague/uri-components/tree/7.8.0" }, "funding": [ { @@ -970,26 +982,25 @@ "type": "github" } ], - "time": "2024-12-08T08:40:02+00:00" + "time": "2026-01-14T17:24:56+00:00" }, { "name": "league/uri-interfaces", - "version": "7.5.0", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/c5c5cd056110fc8afaba29fa6b72a43ced42acd4", + "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4", "shasum": "" }, "require": { "ext-filter": "*", "php": "^8.1", - "psr/http-factory": "^1", "psr/http-message": "^1.1 || ^2.0" }, "suggest": { @@ -997,6 +1008,7 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -1021,7 +1033,7 @@ "homepage": "https://nyamsprod.com" } ], - "description": "Common interfaces and classes for URI representation and interaction", + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", "homepage": "https://uri.thephpleague.com", "keywords": [ "data-uri", @@ -1046,7 +1058,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.0" }, "funding": [ { @@ -1054,30 +1066,30 @@ "type": "github" } ], - "time": "2024-12-08T08:18:47+00:00" + "time": "2026-01-15T06:54:53+00:00" }, { "name": "open-telemetry/api", - "version": "1.3.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "4e3bb38e069876fb73c2ce85c89583bf2b28cd86" + "reference": "df5197c6fd0ddd8e9883b87de042d9341300e2ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/4e3bb38e069876fb73c2ce85c89583bf2b28cd86", - "reference": "4e3bb38e069876fb73c2ce85c89583bf2b28cd86", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/df5197c6fd0ddd8e9883b87de042d9341300e2ad", + "reference": "df5197c6fd0ddd8e9883b87de042d9341300e2ad", "shasum": "" }, "require": { - "open-telemetry/context": "^1.0", + "open-telemetry/context": "^1.4", "php": "^8.1", "psr/log": "^1.1|^2.0|^3.0", "symfony/polyfill-php82": "^1.26" }, "conflict": { - "open-telemetry/sdk": "<=1.0.8" + "open-telemetry/sdk": "<=1.11" }, "type": "library", "extra": { @@ -1087,7 +1099,7 @@ ] }, "branch-alias": { - "dev-main": "1.1.x-dev" + "dev-main": "1.8.x-dev" } }, "autoload": { @@ -1120,24 +1132,24 @@ ], "support": { "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", - "docs": "https://opentelemetry.io/docs/php", + "docs": "https://opentelemetry.io/docs/languages/php", "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-05-07T12:32:21+00:00" + "time": "2026-01-21T04:14:03+00:00" }, { "name": "open-telemetry/context", - "version": "1.2.1", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/context.git", - "reference": "1eb2b837ee9362db064a6b65d5ecce15a9f9f020" + "reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/1eb2b837ee9362db064a6b65d5ecce15a9f9f020", - "reference": "1eb2b837ee9362db064a6b65d5ecce15a9f9f020", + "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/d4c4470b541ce72000d18c339cfee633e4c8e0cf", + "reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf", "shasum": "" }, "require": { @@ -1183,28 +1195,30 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-05-07T23:36:50+00:00" + "time": "2025-09-19T00:05:49+00:00" }, { "name": "paragonie/constant_time_encoding", - "version": "v3.0.0", + "version": "v3.1.3", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", - "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", "shasum": "" }, "require": { "php": "^8" }, "require-dev": { - "phpunit/phpunit": "^9", - "vimeo/psalm": "^4|^5" + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" }, "type": "library", "autoload": { @@ -1250,20 +1264,20 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2024-05-08T12:36:18+00:00" + "time": "2025-09-24T15:06:41+00:00" }, { "name": "paragonie/sodium_compat", - "version": "v2.1.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "a673d5f310477027cead2e2f2b6db5d8368157cb" + "reference": "4714da6efdc782c06690bc72ce34fae7941c2d9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/a673d5f310477027cead2e2f2b6db5d8368157cb", - "reference": "a673d5f310477027cead2e2f2b6db5d8368157cb", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/4714da6efdc782c06690bc72ce34fae7941c2d9f", + "reference": "4714da6efdc782c06690bc72ce34fae7941c2d9f", "shasum": "" }, "require": { @@ -1271,8 +1285,10 @@ "php-64bit": "*" }, "require-dev": { - "phpunit/phpunit": "^7|^8|^9", - "vimeo/psalm": "^4|^5" + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^7|^8|^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" }, "suggest": { "ext-sodium": "Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." @@ -1286,7 +1302,10 @@ "autoload": { "files": [ "autoload.php" - ] + ], + "psr-4": { + "ParagonIE\\Sodium\\": "namespaced/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1339,22 +1358,22 @@ ], "support": { "issues": "https://github.com/paragonie/sodium_compat/issues", - "source": "https://github.com/paragonie/sodium_compat/tree/v2.1.0" + "source": "https://github.com/paragonie/sodium_compat/tree/v2.5.0" }, - "time": "2024-09-04T12:51:01+00:00" + "time": "2025-12-30T16:12:18+00:00" }, { "name": "php-http/client-common", - "version": "2.7.2", + "version": "2.7.3", "source": { "type": "git", "url": "https://github.com/php-http/client-common.git", - "reference": "0cfe9858ab9d3b213041b947c881d5b19ceeca46" + "reference": "dcc6de29c90dd74faab55f71b79d89409c4bf0c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/client-common/zipball/0cfe9858ab9d3b213041b947c881d5b19ceeca46", - "reference": "0cfe9858ab9d3b213041b947c881d5b19ceeca46", + "url": "https://api.github.com/repos/php-http/client-common/zipball/dcc6de29c90dd74faab55f71b79d89409c4bf0c1", + "reference": "dcc6de29c90dd74faab55f71b79d89409c4bf0c1", "shasum": "" }, "require": { @@ -1364,15 +1383,13 @@ "psr/http-client": "^1.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0 || ^2.0", - "symfony/options-resolver": "~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0 || ^6.0 || ^7.0", + "symfony/options-resolver": "~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0 || ^6.0 || ^7.0 || ^8.0", "symfony/polyfill-php80": "^1.17" }, "require-dev": { "doctrine/instantiator": "^1.1", "guzzlehttp/psr7": "^1.4", "nyholm/psr7": "^1.2", - "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1", - "phpspec/prophecy": "^1.10.2", "phpunit/phpunit": "^7.5.20 || ^8.5.33 || ^9.6.7" }, "suggest": { @@ -1408,9 +1425,9 @@ ], "support": { "issues": "https://github.com/php-http/client-common/issues", - "source": "https://github.com/php-http/client-common/tree/2.7.2" + "source": "https://github.com/php-http/client-common/tree/2.7.3" }, - "time": "2024-09-24T06:21:48+00:00" + "time": "2025-11-29T19:12:34+00:00" }, { "name": "php-http/discovery", @@ -1671,27 +1688,28 @@ }, { "name": "php-soap/engine", - "version": "2.15.0", + "version": "2.17.0", "source": { "type": "git", "url": "https://github.com/php-soap/engine.git", - "reference": "c19daa91254f64513c87bbafb257eb2da55cefca" + "reference": "dd097d8d996f76ce378df48e4e93f44500c4d3da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-soap/engine/zipball/c19daa91254f64513c87bbafb257eb2da55cefca", - "reference": "c19daa91254f64513c87bbafb257eb2da55cefca", + "url": "https://api.github.com/repos/php-soap/engine/zipball/dd097d8d996f76ce378df48e4e93f44500c4d3da", + "reference": "dd097d8d996f76ce378df48e4e93f44500c4d3da", "shasum": "" }, "require": { - "azjezz/psl": "^3.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "php-soap/xml": "^1.8.0" + "azjezz/psl": "^3.0 || ^4.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "php-soap/xml": "^1.9.0" }, "require-dev": { + "php-cs-fixer/shim": "^3.88", "php-standard-library/psalm-plugin": "^2.2", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "^5.26" + "phpunit/phpunit": "^12.3", + "vimeo/psalm": "^6.13" }, "type": "library", "autoload": { @@ -1712,7 +1730,7 @@ "description": "SOAP engine design", "support": { "issues": "https://github.com/php-soap/engine/issues", - "source": "https://github.com/php-soap/engine/tree/2.15.0" + "source": "https://github.com/php-soap/engine/tree/2.17.0" }, "funding": [ { @@ -1720,35 +1738,37 @@ "type": "open_collective" } ], - "time": "2025-01-03T09:16:20+00:00" + "time": "2025-11-24T11:11:05+00:00" }, { "name": "php-soap/ext-soap-engine", - "version": "1.7.0", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/php-soap/ext-soap-engine.git", - "reference": "b11e1949f4654f27f997b6e2bb44c1bd14735340" + "reference": "cd76481c039ddf70d837a2835db3fef06fab0bb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-soap/ext-soap-engine/zipball/b11e1949f4654f27f997b6e2bb44c1bd14735340", - "reference": "b11e1949f4654f27f997b6e2bb44c1bd14735340", + "url": "https://api.github.com/repos/php-soap/ext-soap-engine/zipball/cd76481c039ddf70d837a2835db3fef06fab0bb6", + "reference": "cd76481c039ddf70d837a2835db3fef06fab0bb6", "shasum": "" }, "require": { - "azjezz/psl": "^3.0", + "azjezz/psl": "^3.0 || ^4.0", "ext-dom": "*", "ext-soap": "*", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "php-soap/engine": "^2.13", - "php-soap/wsdl": "^1.12", - "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0" + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "php-soap/engine": "^2.16", + "php-soap/wsdl": "^1.14", + "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { - "php-soap/engine-integration-tests": "^1.9", - "php-soap/xml": "^1.8", - "phpunit/phpunit": "^10.0.19" + "php-cs-fixer/shim": "~3.88", + "php-soap/engine-integration-tests": "^1.10", + "php-soap/xml": "^1.9", + "phpunit/phpunit": "~12.3", + "vimeo/psalm": "~6.13" }, "type": "library", "autoload": { @@ -1769,7 +1789,7 @@ "description": "An ext-soap engine implementation", "support": { "issues": "https://github.com/php-soap/ext-soap-engine/issues", - "source": "https://github.com/php-soap/ext-soap-engine/tree/1.7.0" + "source": "https://github.com/php-soap/ext-soap-engine/tree/1.9.0" }, "funding": [ { @@ -1777,30 +1797,30 @@ "type": "open_collective" } ], - "time": "2024-10-25T08:37:25+00:00" + "time": "2025-12-01T07:15:53+00:00" }, { "name": "php-soap/psr18-transport", - "version": "1.7.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/php-soap/psr18-transport.git", - "reference": "1c0a42924ac39d8fadb9e4196f5f7b5e8e68d0a9" + "reference": "fee4be6b02bd2d0fb3886c98d72b2ae97564cee4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-soap/psr18-transport/zipball/1c0a42924ac39d8fadb9e4196f5f7b5e8e68d0a9", - "reference": "1c0a42924ac39d8fadb9e4196f5f7b5e8e68d0a9", + "url": "https://api.github.com/repos/php-soap/psr18-transport/zipball/fee4be6b02bd2d0fb3886c98d72b2ae97564cee4", + "reference": "fee4be6b02bd2d0fb3886c98d72b2ae97564cee4", "shasum": "" }, "require": { "ext-dom": "*", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "php-http/client-common": "^2.3", "php-http/discovery": "^1.12", - "php-soap/engine": "^2.13", - "php-soap/wsdl": "^1.12", - "php-soap/xml": "^1.8", + "php-soap/engine": "^2.16", + "php-soap/wsdl": "^1.14", + "php-soap/xml": "^1.9", "psr/http-client-implementation": "^1.0", "psr/http-factory-implementation": "^1.0", "psr/http-message": "^1.0.1|^2.0", @@ -1811,10 +1831,12 @@ "ext-soap": "*", "guzzlehttp/guzzle": "^7.5", "nyholm/psr7": "^1.5", + "php-cs-fixer/shim": "~3.88", "php-http/mock-client": "^1.5", - "php-soap/engine-integration-tests": "^1.9", + "php-soap/engine-integration-tests": "^1.10", "php-soap/ext-soap-engine": "^1.7", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "~12.3", + "vimeo/psalm": "~6.13" }, "type": "library", "autoload": { @@ -1835,7 +1857,7 @@ "description": "PSR-18 HTTP Client transport for SOAP", "support": { "issues": "https://github.com/php-soap/psr18-transport/issues", - "source": "https://github.com/php-soap/psr18-transport/tree/1.7.0" + "source": "https://github.com/php-soap/psr18-transport/tree/1.8.0" }, "funding": [ { @@ -1843,36 +1865,37 @@ "type": "open_collective" } ], - "time": "2024-10-25T08:43:55+00:00" + "time": "2025-10-20T10:43:29+00:00" }, { "name": "php-soap/wsdl", - "version": "1.13.0", + "version": "1.16.0", "source": { "type": "git", "url": "https://github.com/php-soap/wsdl.git", - "reference": "0c5a1afe7ab648c227ed23a612b8c69509a22eb4" + "reference": "331c520f6b3edbf14064d0000d8472155aea6c91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-soap/wsdl/zipball/0c5a1afe7ab648c227ed23a612b8c69509a22eb4", - "reference": "0c5a1afe7ab648c227ed23a612b8c69509a22eb4", + "url": "https://api.github.com/repos/php-soap/wsdl/zipball/331c520f6b3edbf14064d0000d8472155aea6c91", + "reference": "331c520f6b3edbf14064d0000d8472155aea6c91", "shasum": "" }, "require": { - "azjezz/psl": "^3.0", + "azjezz/psl": "^3.0 || ^4.0", "ext-dom": "*", "league/uri": "^7.0", "league/uri-components": "^7.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "php-soap/xml": "^1.8", - "symfony/console": "^5.4 || ^6.0 || ^7.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "php-soap/xml": "^1.9", + "symfony/console": "^5.4 || ^6.0 || ^7.0 || ^8.0", "veewee/xml": "~3.0" }, "require-dev": { - "phpunit/phpunit": "^10.5", + "php-cs-fixer/shim": "^3.88", + "phpunit/phpunit": "^12.3", "psalm/plugin-symfony": "^5.0", - "vimeo/psalm": "^5.26.0" + "vimeo/psalm": "^6.13" }, "bin": [ "bin/wsdl" @@ -1896,7 +1919,7 @@ "description": "Deals with WSDLs", "support": { "issues": "https://github.com/php-soap/wsdl/issues", - "source": "https://github.com/php-soap/wsdl/tree/1.13.0" + "source": "https://github.com/php-soap/wsdl/tree/1.16.0" }, "funding": [ { @@ -1904,29 +1927,31 @@ "type": "open_collective" } ], - "time": "2025-01-24T10:35:01+00:00" + "time": "2025-12-01T07:15:42+00:00" }, { "name": "php-soap/xml", - "version": "1.8.0", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/php-soap/xml.git", - "reference": "7aa20a359f67b18dc3bdb0f52c9065cc54492bc3" + "reference": "c7f702640b97bcd608ef6bc2d2300d20775f0191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-soap/xml/zipball/7aa20a359f67b18dc3bdb0f52c9065cc54492bc3", - "reference": "7aa20a359f67b18dc3bdb0f52c9065cc54492bc3", + "url": "https://api.github.com/repos/php-soap/xml/zipball/c7f702640b97bcd608ef6bc2d2300d20775f0191", + "reference": "c7f702640b97bcd608ef6bc2d2300d20775f0191", "shasum": "" }, "require": { "ext-dom": "*", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "veewee/xml": "^3.0" + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "veewee/xml": "^3.4" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "php-cs-fixer/shim": "^3.88", + "phpunit/phpunit": "~12.3", + "vimeo/psalm": "~6.13" }, "type": "library", "autoload": { @@ -1947,7 +1972,7 @@ "description": "XML wrappers for SOAP", "support": { "issues": "https://github.com/php-soap/xml/issues", - "source": "https://github.com/php-soap/xml/tree/1.8.0" + "source": "https://github.com/php-soap/xml/tree/1.9.0" }, "funding": [ { @@ -1955,7 +1980,7 @@ "type": "open_collective" } ], - "time": "2024-10-25T06:49:38+00:00" + "time": "2025-10-20T09:03:44+00:00" }, { "name": "psr/cache", @@ -2488,16 +2513,16 @@ }, { "name": "react/event-loop", - "version": "v1.5.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, "require": { @@ -2548,7 +2573,7 @@ ], "support": { "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, "funding": [ { @@ -2556,27 +2581,27 @@ "type": "open_collective" } ], - "time": "2023-11-13T13:48:05+00:00" + "time": "2025-11-17T20:46:25+00:00" }, { "name": "react/promise", - "version": "v3.2.0", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", "shasum": "" }, "require": { "php": ">=7.1.0" }, "require-dev": { - "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpstan/phpstan": "1.12.28 || 1.4.10", "phpunit/phpunit": "^9.6 || ^7.5" }, "type": "library", @@ -2621,7 +2646,7 @@ ], "support": { "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.2.0" + "source": "https://github.com/reactphp/promise/tree/v3.3.0" }, "funding": [ { @@ -2629,20 +2654,20 @@ "type": "open_collective" } ], - "time": "2024-05-24T10:39:05+00:00" + "time": "2025-08-19T18:57:03+00:00" }, { "name": "revolt/event-loop", - "version": "v1.0.7", + "version": "v1.0.8", "source": { "type": "git", "url": "https://github.com/revoltphp/event-loop.git", - "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3" + "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/09bf1bf7f7f574453efe43044b06fafe12216eb3", - "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/b6fc06dce8e9b523c9946138fa5e62181934f91c", + "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c", "shasum": "" }, "require": { @@ -2699,9 +2724,9 @@ ], "support": { "issues": "https://github.com/revoltphp/event-loop/issues", - "source": "https://github.com/revoltphp/event-loop/tree/v1.0.7" + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.8" }, - "time": "2025-01-25T19:27:39+00:00" + "time": "2025-08-27T21:33:23+00:00" }, { "name": "spomky-labs/aes-key-wrap", @@ -2781,20 +2806,20 @@ }, { "name": "spomky-labs/pki-framework", - "version": "1.2.3", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/pki-framework.git", - "reference": "5ff1dcc21e961b60149a80e77f744fc047800b31" + "reference": "f0e9a548df4e3942886adc9b7830581a46334631" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/5ff1dcc21e961b60149a80e77f744fc047800b31", - "reference": "5ff1dcc21e961b60149a80e77f744fc047800b31", + "url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/f0e9a548df4e3942886adc9b7830581a46334631", + "reference": "f0e9a548df4e3942886adc9b7830581a46334631", "shasum": "" }, "require": { - "brick/math": "^0.10|^0.11|^0.12|^0.13", + "brick/math": "^0.10|^0.11|^0.12|^0.13|^0.14", "ext-mbstring": "*", "php": ">=8.1" }, @@ -2802,7 +2827,7 @@ "ekino/phpstan-banned-code": "^1.0|^2.0|^3.0", "ext-gmp": "*", "ext-openssl": "*", - "infection/infection": "^0.28|^0.29", + "infection/infection": "^0.28|^0.29|^0.31", "php-parallel-lint/php-parallel-lint": "^1.3", "phpstan/extension-installer": "^1.3|^2.0", "phpstan/phpstan": "^1.8|^2.0", @@ -2812,8 +2837,8 @@ "phpunit/phpunit": "^10.1|^11.0|^12.0", "rector/rector": "^1.0|^2.0", "roave/security-advisories": "dev-latest", - "symfony/string": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", "symplify/easy-coding-standard": "^12.0" }, "suggest": { @@ -2874,7 +2899,7 @@ ], "support": { "issues": "https://github.com/Spomky-Labs/pki-framework/issues", - "source": "https://github.com/Spomky-Labs/pki-framework/tree/1.2.3" + "source": "https://github.com/Spomky-Labs/pki-framework/tree/1.4.1" }, "funding": [ { @@ -2886,26 +2911,26 @@ "type": "patreon" } ], - "time": "2025-04-25T15:57:13+00:00" + "time": "2025-12-20T12:57:40+00:00" }, { "name": "symfony/config", - "version": "v7.3.0", + "version": "v7.4.6", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "ba62ae565f1327c2f6366726312ed828c85853bc" + "reference": "9400e2f9226b3b64ebb0a8ae967ae84e54e39640" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/ba62ae565f1327c2f6366726312ed828c85853bc", - "reference": "ba62ae565f1327c2f6366726312ed828c85853bc", + "url": "https://api.github.com/repos/symfony/config/zipball/9400e2f9226b3b64ebb0a8ae967ae84e54e39640", + "reference": "9400e2f9226b3b64ebb0a8ae967ae84e54e39640", "shasum": "" }, "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^7.1", + "symfony/filesystem": "^7.1|^8.0", "symfony/polyfill-ctype": "~1.8" }, "conflict": { @@ -2913,11 +2938,11 @@ "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -2945,7 +2970,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.3.0" + "source": "https://github.com/symfony/config/tree/v7.4.6" }, "funding": [ { @@ -2956,12 +2981,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-15T09:04:05+00:00" + "time": "2026-02-25T16:50:00+00:00" }, { "name": "symfony/console", @@ -3064,24 +3093,24 @@ }, { "name": "symfony/dependency-injection", - "version": "v7.3.0", + "version": "v7.4.6", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "f64a8f3fa7d4ad5e85de1b128a0e03faed02b732" + "reference": "a3f7d594ca53a34a7d39ae683fbca09408b0c598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f64a8f3fa7d4ad5e85de1b128a0e03faed02b732", - "reference": "f64a8f3fa7d4ad5e85de1b128a0e03faed02b732", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a3f7d594ca53a34a7d39ae683fbca09408b0c598", + "reference": "a3f7d594ca53a34a7d39ae683fbca09408b0c598", "shasum": "" }, "require": { "php": ">=8.2", "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/service-contracts": "^3.5", - "symfony/var-exporter": "^6.4.20|^7.2.5" + "symfony/service-contracts": "^3.6", + "symfony/var-exporter": "^6.4.20|^7.2.5|^8.0" }, "conflict": { "ext-psr": "<1.1|>=2", @@ -3094,9 +3123,9 @@ "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3124,7 +3153,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.3.0" + "source": "https://github.com/symfony/dependency-injection/tree/v7.4.6" }, "funding": [ { @@ -3135,12 +3164,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-19T13:28:56+00:00" + "time": "2026-02-25T16:50:00+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3211,32 +3244,33 @@ }, { "name": "symfony/error-handler", - "version": "v7.3.0", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "cf68d225bc43629de4ff54778029aee6dc191b83" + "reference": "8da531f364ddfee53e36092a7eebbbd0b775f6b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/cf68d225bc43629de4ff54778029aee6dc191b83", - "reference": "cf68d225bc43629de4ff54778029aee6dc191b83", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/8da531f364ddfee53e36092a7eebbbd0b775f6b8", + "reference": "8da531f364ddfee53e36092a7eebbbd0b775f6b8", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/deprecation-contracts": "<2.5", "symfony/http-kernel": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ @@ -3268,7 +3302,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.0" + "source": "https://github.com/symfony/error-handler/tree/v7.4.4" }, "funding": [ { @@ -3279,25 +3313,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-29T07:19:49+00:00" + "time": "2026-01-20T16:42:42+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.3.0", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" + "reference": "dc2c0eba1af673e736bb851d747d266108aea746" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/dc2c0eba1af673e736bb851d747d266108aea746", + "reference": "dc2c0eba1af673e736bb851d747d266108aea746", "shasum": "" }, "require": { @@ -3314,13 +3352,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3348,7 +3387,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.4" }, "funding": [ { @@ -3359,12 +3398,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-22T09:11:45+00:00" + "time": "2026-01-05T11:45:34+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -3444,16 +3487,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.3.0", + "version": "v7.4.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + "reference": "3ebc794fa5315e59fd122561623c2e2e4280538e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/3ebc794fa5315e59fd122561623c2e2e4280538e", + "reference": "3ebc794fa5315e59fd122561623c2e2e4280538e", "shasum": "" }, "require": { @@ -3462,7 +3505,7 @@ "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^6.4|^7.0" + "symfony/process": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3490,7 +3533,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.0" + "source": "https://github.com/symfony/filesystem/tree/v7.4.6" }, "funding": [ { @@ -3501,25 +3544,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:15:23+00:00" + "time": "2026-02-25T16:50:00+00:00" }, { "name": "symfony/http-client", - "version": "v7.3.0", + "version": "v7.4.6", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "57e4fb86314015a695a750ace358d07a7e37b8a9" + "reference": "2bde8afd5ab2fe0b05a9c2d4c3c0e28ceb98a154" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/57e4fb86314015a695a750ace358d07a7e37b8a9", - "reference": "57e4fb86314015a695a750ace358d07a7e37b8a9", + "url": "https://api.github.com/repos/symfony/http-client/zipball/2bde8afd5ab2fe0b05a9c2d4c3c0e28ceb98a154", + "reference": "2bde8afd5ab2fe0b05a9c2d4c3c0e28ceb98a154", "shasum": "" }, "require": { @@ -3527,10 +3574,12 @@ "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/polyfill-php83": "^1.29", "symfony/service-contracts": "^2.5|^3" }, "conflict": { "amphp/amp": "<2.5", + "amphp/socket": "<1.1", "php-http/discovery": "<1.15", "symfony/http-foundation": "<6.4" }, @@ -3543,18 +3592,18 @@ "require-dev": { "amphp/http-client": "^4.2.1|^5.0", "amphp/http-tunnel": "^1.0|^2.0", - "amphp/socket": "^1.1", "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", "symfony/amphp-http-client-meta": "^1.0|^2.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3585,7 +3634,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.0" + "source": "https://github.com/symfony/http-client/tree/v7.4.6" }, "funding": [ { @@ -3596,12 +3645,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-02T08:23:16+00:00" + "time": "2026-02-18T09:46:18+00:00" }, { "name": "symfony/http-client-contracts", @@ -3683,23 +3736,22 @@ }, { "name": "symfony/http-foundation", - "version": "v7.3.0", + "version": "v7.4.6", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "4236baf01609667d53b20371486228231eb135fd" + "reference": "fd97d5e926e988a363cef56fbbf88c5c528e9065" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4236baf01609667d53b20371486228231eb135fd", - "reference": "4236baf01609667d53b20371486228231eb135fd", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/fd97d5e926e988a363cef56fbbf88c5c528e9065", + "reference": "fd97d5e926e988a363cef56fbbf88c5c528e9065", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php83": "^1.27" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.1" }, "conflict": { "doctrine/dbal": "<3.6", @@ -3708,13 +3760,13 @@ "require-dev": { "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5", - "symfony/clock": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0" + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3742,7 +3794,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.0" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.6" }, "funding": [ { @@ -3753,25 +3805,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-12T14:48:23+00:00" + "time": "2026-02-21T16:25:55+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.22", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "15c105b839a7cfa1bc0989c091bfb6477f23b673" + "reference": "006a49fc4f41ee21a6ca61e69caed1c30b29f07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/15c105b839a7cfa1bc0989c091bfb6477f23b673", - "reference": "15c105b839a7cfa1bc0989c091bfb6477f23b673", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/006a49fc4f41ee21a6ca61e69caed1c30b29f07c", + "reference": "006a49fc4f41ee21a6ca61e69caed1c30b29f07c", "shasum": "" }, "require": { @@ -3812,7 +3868,7 @@ "symfony/config": "^6.1|^7.0", "symfony/console": "^5.4|^6.0|^7.0", "symfony/css-selector": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4.1|^7.0.1", "symfony/dom-crawler": "^5.4|^6.0|^7.0", "symfony/expression-language": "^5.4|^6.0|^7.0", "symfony/finder": "^5.4|^6.0|^7.0", @@ -3856,7 +3912,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.22" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.34" }, "funding": [ { @@ -3867,29 +3923,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-29T07:23:40+00:00" + "time": "2026-02-26T08:27:11+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.3.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca" + "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/afb9a8038025e5dbc657378bfab9198d75f10fca", - "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d2b592535ffa6600c265a3893a7f7fd2bad82dd7", + "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -3923,7 +3983,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.0" + "source": "https://github.com/symfony/options-resolver/tree/v8.0.0" }, "funding": [ { @@ -3934,16 +3994,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-04T13:12:05+00:00" + "time": "2025-11-12T15:55:31+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -4002,7 +4066,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -4013,6 +4077,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4022,16 +4090,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -4080,7 +4148,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -4091,16 +4159,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -4161,7 +4233,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -4172,6 +4244,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4181,7 +4257,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -4242,7 +4318,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -4253,6 +4329,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4262,7 +4342,7 @@ }, { "name": "symfony/polyfill-php73", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", @@ -4318,7 +4398,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.33.0" }, "funding": [ { @@ -4329,6 +4409,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4338,7 +4422,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -4398,7 +4482,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -4409,6 +4493,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4418,7 +4506,7 @@ }, { "name": "symfony/polyfill-php82", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php82.git", @@ -4474,7 +4562,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php82/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php82/tree/v1.33.0" }, "funding": [ { @@ -4485,6 +4573,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4494,16 +4586,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -4550,7 +4642,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -4561,16 +4653,100 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-23T16:12:55+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", @@ -4629,7 +4805,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" }, "funding": [ { @@ -4640,6 +4816,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -4649,16 +4829,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -4712,7 +4892,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -4723,25 +4903,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/string", - "version": "v6.4.21", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" + "reference": "2adaf4106f2ef4c67271971bde6d3fe0a6936432" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", - "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", + "url": "https://api.github.com/repos/symfony/string/zipball/2adaf4106f2ef4c67271971bde6d3fe0a6936432", + "reference": "2adaf4106f2ef4c67271971bde6d3fe0a6936432", "shasum": "" }, "require": { @@ -4755,7 +4939,6 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", "symfony/http-client": "^5.4|^6.0|^7.0", "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -4798,7 +4981,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.21" + "source": "https://github.com/symfony/string/tree/v6.4.34" }, "funding": [ { @@ -4809,25 +4992,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-18T15:23:29+00:00" + "time": "2026-02-08T20:44:54+00:00" }, { "name": "symfony/uid", - "version": "v6.4.13", + "version": "v6.4.32", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007" + "reference": "6b973c385f00341b246f697d82dc01a09107acdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/18eb207f0436a993fffbdd811b5b8fa35fa5e007", - "reference": "18eb207f0436a993fffbdd811b5b8fa35fa5e007", + "url": "https://api.github.com/repos/symfony/uid/zipball/6b973c385f00341b246f697d82dc01a09107acdd", + "reference": "6b973c385f00341b246f697d82dc01a09107acdd", "shasum": "" }, "require": { @@ -4872,7 +5059,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v6.4.13" + "source": "https://github.com/symfony/uid/tree/v6.4.32" }, "funding": [ { @@ -4883,25 +5070,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2025-12-23T15:07:59+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.4.21", + "version": "v6.4.32", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "22560f80c0c5cd58cc0bcaf73455ffd81eb380d5" + "reference": "131fc9915e0343052af5ed5040401b481ca192aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/22560f80c0c5cd58cc0bcaf73455ffd81eb380d5", - "reference": "22560f80c0c5cd58cc0bcaf73455ffd81eb380d5", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/131fc9915e0343052af5ed5040401b481ca192aa", + "reference": "131fc9915e0343052af5ed5040401b481ca192aa", "shasum": "" }, "require": { @@ -4913,7 +5104,6 @@ "symfony/console": "<5.4" }, "require-dev": { - "ext-iconv": "*", "symfony/console": "^5.4|^6.0|^7.0", "symfony/error-handler": "^6.3|^7.0", "symfony/http-kernel": "^5.4|^6.0|^7.0", @@ -4957,7 +5147,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.21" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.32" }, "funding": [ { @@ -4968,35 +5158,38 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-09T07:34:50+00:00" + "time": "2026-01-01T13:34:06+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.3.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "c9a1168891b5aaadfd6332ef44393330b3498c4c" + "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/c9a1168891b5aaadfd6332ef44393330b3498c4c", - "reference": "c9a1168891b5aaadfd6332ef44393330b3498c4c", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", + "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" + "php": ">=8.4" }, "require-dev": { - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5034,7 +5227,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.3.0" + "source": "https://github.com/symfony/var-exporter/tree/v8.0.0" }, "funding": [ { @@ -5045,25 +5238,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-15T09:04:05+00:00" + "time": "2025-11-05T18:53:00+00:00" }, { "name": "symfony/yaml", - "version": "v6.4.21", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "f01987f45676778b474468aa266fe2eda1f2bc7e" + "reference": "7bca30dabed7900a08c5ad4f1d6483f881a64d0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/f01987f45676778b474468aa266fe2eda1f2bc7e", - "reference": "f01987f45676778b474468aa266fe2eda1f2bc7e", + "url": "https://api.github.com/repos/symfony/yaml/zipball/7bca30dabed7900a08c5ad4f1d6483f881a64d0f", + "reference": "7bca30dabed7900a08c5ad4f1d6483f881a64d0f", "shasum": "" }, "require": { @@ -5106,7 +5303,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.21" + "source": "https://github.com/symfony/yaml/tree/v6.4.34" }, "funding": [ { @@ -5117,25 +5314,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-04T09:48:44+00:00" + "time": "2026-02-06T18:32:11+00:00" }, { "name": "twig/twig", - "version": "v3.21.1", + "version": "v3.23.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d" + "reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/285123877d4dd97dd7c11842ac5fb7e86e60d81d", - "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9", + "reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9", "shasum": "" }, "require": { @@ -5189,7 +5390,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.21.1" + "source": "https://github.com/twigphp/Twig/tree/v3.23.0" }, "funding": [ { @@ -5201,38 +5402,41 @@ "type": "tidelift" } ], - "time": "2025-05-03T07:21:55+00:00" + "time": "2026-01-23T21:00:41+00:00" }, { "name": "veewee/xml", - "version": "3.3.0", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/veewee/xml.git", - "reference": "6ae6b154d64d427bf1b2840b40c23131eb1feba5" + "reference": "8799d7b81b60759b9776c10b8047a7e1ae364800" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/veewee/xml/zipball/6ae6b154d64d427bf1b2840b40c23131eb1feba5", - "reference": "6ae6b154d64d427bf1b2840b40c23131eb1feba5", + "url": "https://api.github.com/repos/veewee/xml/zipball/8799d7b81b60759b9776c10b8047a7e1ae364800", + "reference": "8799d7b81b60759b9776c10b8047a7e1ae364800", "shasum": "" }, "require": { - "azjezz/psl": "^3.0", + "azjezz/psl": "^3.0 || ^4.0", "ext-dom": "*", "ext-libxml": "*", "ext-xml": "*", "ext-xmlreader": "*", "ext-xmlwriter": "*", "ext-xsl": "*", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "webmozart/assert": "^1.10" }, "require-dev": { + "infection/infection": "^0.31.7", + "php-cs-fixer/shim": "~3.88", "php-standard-library/psalm-plugin": "^2.2", - "symfony/finder": "^6.1", + "phpunit/phpunit": "~12.3", + "symfony/finder": "^6.1 || ^7.0", "veewee/composer-run-parallel": "^1.0.0", - "vimeo/psalm": "^5.4" + "vimeo/psalm": "~6.13" }, "type": "library", "autoload": { @@ -5270,7 +5474,7 @@ ], "support": { "issues": "https://github.com/veewee/xml/issues", - "source": "https://github.com/veewee/xml/tree/3.3.0" + "source": "https://github.com/veewee/xml/tree/3.4.0" }, "funding": [ { @@ -5278,20 +5482,20 @@ "type": "github" } ], - "time": "2024-10-25T06:12:30+00:00" + "time": "2025-10-15T08:12:16+00:00" }, { "name": "web-token/jwt-framework", - "version": "3.4.8", + "version": "3.4.9", "source": { "type": "git", "url": "https://github.com/web-token/jwt-framework.git", - "reference": "788267095b0b0c3ba42559eb2ec955f416628ec0" + "reference": "112045db98dc78f10611647cab49e0b3d8a59057" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-token/jwt-framework/zipball/788267095b0b0c3ba42559eb2ec955f416628ec0", - "reference": "788267095b0b0c3ba42559eb2ec955f416628ec0", + "url": "https://api.github.com/repos/web-token/jwt-framework/zipball/112045db98dc78f10611647cab49e0b3d8a59057", + "reference": "112045db98dc78f10611647cab49e0b3d8a59057", "shasum": "" }, "require": { @@ -5435,7 +5639,7 @@ ], "support": { "issues": "https://github.com/web-token/jwt-framework/issues", - "source": "https://github.com/web-token/jwt-framework/tree/3.4.8" + "source": "https://github.com/web-token/jwt-framework/tree/3.4.9" }, "funding": [ { @@ -5447,32 +5651,32 @@ "type": "patreon" } ], - "time": "2025-05-07T09:11:18+00:00" + "time": "2025-11-17T20:20:37+00:00" }, { "name": "webmozart/assert", - "version": "1.11.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", "shasum": "" }, "require": { "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", "php": "^7.2 || ^8.0" }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" }, "type": "library", "extra": { @@ -5503,100 +5707,1932 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2025-10-29T15:56:20+00:00" } ], "packages-dev": [ { - "name": "nextcloud/ocp", - "version": "dev-stable29", + "name": "amphp/amp", + "version": "v2.6.5", "source": { "type": "git", - "url": "https://github.com/nextcloud-deps/ocp.git", - "reference": "be97344a46d9a19169e20c10ab4f7b0a2b769df7" + "url": "https://github.com/amphp/amp.git", + "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/be97344a46d9a19169e20c10ab4f7b0a2b769df7", - "reference": "be97344a46d9a19169e20c10ab4f7b0a2b769df7", + "url": "https://api.github.com/repos/amphp/amp/zipball/d7dda98dae26e56f3f6fcfbf1c1f819c9a993207", + "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207", "shasum": "" }, "require": { - "php": "~8.0 || ~8.1 || ~8.2 || ~8.3", - "psr/clock": "^1.0", - "psr/container": "^2.0.2", - "psr/event-dispatcher": "^1.0", - "psr/log": "^1.1.4" + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "react/promise": "^2", + "vimeo/psalm": "^3.12" }, "type": "library", - "extra": { - "branch-alias": { - "dev-stable29": "29.0.0-dev" + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "AGPL-3.0-or-later" + "MIT" ], "authors": [ { - "name": "Christoph Wurst", - "email": "christoph@winzerhof-wurst.at" + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "Composer package containing Nextcloud's public API (classes, interfaces)", + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], "support": { - "issues": "https://github.com/nextcloud-deps/ocp/issues", - "source": "https://github.com/nextcloud-deps/ocp/tree/stable29" + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.5" }, - "time": "2025-05-15T00:50:47+00:00" + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-09-03T19:41:28+00:00" }, { - "name": "roave/security-advisories", - "version": "dev-latest", + "name": "amphp/byte-stream", + "version": "v1.8.2", "source": { "type": "git", - "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "c44d680c1033ece6eec5a1e6fef573548d916670" + "url": "https://github.com/amphp/byte-stream.git", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/c44d680c1033ece6eec5a1e6fef573548d916670", - "reference": "c44d680c1033ece6eec5a1e6fef573548d916670", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", "shasum": "" }, - "conflict": { - "3f/pygmentize": "<1.2", - "adaptcms/adaptcms": "<=1.3", - "admidio/admidio": "<4.3.12", - "adodb/adodb-php": "<=5.22.8", - "aheinze/cockpit": "<2.2", - "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", - "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", - "aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7", - "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9|==2024.04.1", - "aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7", - "aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5", - "airesvsg/acf-to-rest-api": "<=3.1", - "akaunting/akaunting": "<2.1.13", - "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", - "alextselegidis/easyappointments": "<=1.5.1", - "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", - "amazing/media2click": ">=1,<1.3.3", - "ameos/ameos_tarteaucitron": "<1.2.23", - "amphp/artax": "<1.0.6|>=2,<2.0.6", - "amphp/http": "<=1.7.2|>=2,<=2.1", - "amphp/http-client": ">=4,<4.4", - "anchorcms/anchor-cms": "<=0.12.7", - "andreapollastri/cipi": "<=3.1.15", + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-13T18:00:56+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.6", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" + }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.3", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" + }, + "time": "2024-04-30T00:40:11+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.5.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.5.0" + }, + "time": "2024-09-08T10:13:13+00:00" + }, + { + "name": "nextcloud/ocp", + "version": "dev-stable29", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-deps/ocp.git", + "reference": "6e97cdf621ae674246fec9efc7d12c9130346a60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/6e97cdf621ae674246fec9efc7d12c9130346a60", + "reference": "6e97cdf621ae674246fec9efc7d12c9130346a60", + "shasum": "" + }, + "require": { + "php": "~8.0 || ~8.1 || ~8.2 || ~8.3", + "psr/clock": "^1.0", + "psr/container": "^2.0.2", + "psr/event-dispatcher": "^1.0", + "psr/log": "^1.1.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-stable29": "29.0.0-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at" + } + ], + "description": "Composer package containing Nextcloud's public API (classes, interfaces)", + "support": { + "issues": "https://github.com/nextcloud-deps/ocp/issues", + "source": "https://github.com/nextcloud-deps/ocp/tree/stable29" + }, + "time": "2026-02-20T01:07:13+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.19.5", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/51bd93cc741b7fc3d63d20b6bdcd99fdaa359837", + "reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.1" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.5" + }, + "time": "2025-12-06T11:45:25+00:00" + }, + { + "name": "pdepend/pdepend", + "version": "2.16.2", + "source": { + "type": "git", + "url": "https://github.com/pdepend/pdepend.git", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/f942b208dc2a0868454d01b29f0c75bbcfc6ed58", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58", + "shasum": "" + }, + "require": { + "php": ">=5.3.7", + "symfony/config": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/polyfill-mbstring": "^1.19" + }, + "require-dev": { + "easy-doc/easy-doc": "0.0.0|^1.2.3", + "gregwar/rst": "^1.0", + "squizlabs/php_codesniffer": "^2.0.0" + }, + "bin": [ + "src/bin/pdepend" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "PDepend\\": "src/main/php/PDepend" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Official version of pdepend to be handled with Composer", + "keywords": [ + "PHP Depend", + "PHP_Depend", + "dev", + "pdepend" + ], + "support": { + "issues": "https://github.com/pdepend/pdepend/issues", + "source": "https://github.com/pdepend/pdepend/tree/2.16.2" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/pdepend/pdepend", + "type": "tidelift" + } + ], + "time": "2023-12-17T18:09:59+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.6", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.6" + }, + "time": "2025-12-22T21:13:58+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" + }, + "time": "2025-11-21T15:09:14+00:00" + }, + { + "name": "phpmd/phpmd", + "version": "2.15.0", + "source": { + "type": "git", + "url": "https://github.com/phpmd/phpmd.git", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/74a1f56e33afad4128b886e334093e98e1b5e7c0", + "reference": "74a1f56e33afad4128b886e334093e98e1b5e7c0", + "shasum": "" + }, + "require": { + "composer/xdebug-handler": "^1.0 || ^2.0 || ^3.0", + "ext-xml": "*", + "pdepend/pdepend": "^2.16.1", + "php": ">=5.3.9" + }, + "require-dev": { + "easy-doc/easy-doc": "0.0.0 || ^1.3.2", + "ext-json": "*", + "ext-simplexml": "*", + "gregwar/rst": "^1.0", + "mikey179/vfsstream": "^1.6.8", + "squizlabs/php_codesniffer": "^2.9.2 || ^3.7.2" + }, + "bin": [ + "src/bin/phpmd" + ], + "type": "library", + "autoload": { + "psr-0": { + "PHPMD\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Manuel Pichler", + "email": "github@manuel-pichler.de", + "homepage": "https://github.com/manuelpichler", + "role": "Project Founder" + }, + { + "name": "Marc Würth", + "email": "ravage@bluewin.ch", + "homepage": "https://github.com/ravage84", + "role": "Project Maintainer" + }, + { + "name": "Other contributors", + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", + "role": "Contributors" + } + ], + "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", + "homepage": "https://phpmd.org/", + "keywords": [ + "dev", + "mess detection", + "mess detector", + "pdepend", + "phpmd", + "pmd" + ], + "support": { + "irc": "irc://irc.freenode.org/phpmd", + "issues": "https://github.com/phpmd/phpmd/issues", + "source": "https://github.com/phpmd/phpmd/tree/2.15.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/phpmd/phpmd", + "type": "tidelift" + } + ], + "time": "2023-12-11T08:22:20+00:00" + }, + { + "name": "phpmetrics/phpmetrics", + "version": "v2.9.1", + "source": { + "type": "git", + "url": "https://github.com/phpmetrics/PhpMetrics.git", + "reference": "e2e68ddd1543bc3f44402c383f7bccb62de1ece3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpmetrics/PhpMetrics/zipball/e2e68ddd1543bc3f44402c383f7bccb62de1ece3", + "reference": "e2e68ddd1543bc3f44402c383f7bccb62de1ece3", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^3|^4|^5" + }, + "replace": { + "halleck45/php-metrics": "*", + "halleck45/phpmetrics": "*" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "bin": [ + "bin/phpmetrics" + ], + "type": "library", + "autoload": { + "files": [ + "./src/functions.php" + ], + "psr-0": { + "Hal\\": "./src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Lépine", + "email": "lepinejeanfrancois@yahoo.fr", + "homepage": "http://www.lepine.pro", + "role": "Copyright Holder" + } + ], + "description": "Static analyzer tool for PHP : Coupling, Cyclomatic complexity, Maintainability Index, Halstead's metrics... and more !", + "homepage": "http://www.phpmetrics.org", + "keywords": [ + "analysis", + "qa", + "quality", + "testing" + ], + "support": { + "issues": "https://github.com/PhpMetrics/PhpMetrics/issues", + "source": "https://github.com/phpmetrics/PhpMetrics/tree/v2.9.1" + }, + "funding": [ + { + "url": "https://github.com/Halleck45", + "type": "github" + } + ], + "time": "2025-09-25T05:21:02+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" + }, + "time": "2026-01-25T14:56:51+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.12.33", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37982d6fc7cbb746dda7773530cda557cdf119e1", + "reference": "37982d6fc7cbb746dda7773530cda557cdf119e1", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2026-02-28T20:30:03+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.16", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:31:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.63", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "33198268dad71e926626b618f3ec3966661e4d90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33198268dad71e926626b618f3ec3966661e4d90", + "reference": "33198268dad71e926626b618f3ec3966661e4d90", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.5", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.4", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.63" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2026-01-27T05:48:37+00:00" + }, + { + "name": "roave/security-advisories", + "version": "dev-latest", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "5a17df9a478a03dff7428a3172cf7204d4d017a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/5a17df9a478a03dff7428a3172cf7204d4d017a8", + "reference": "5a17df9a478a03dff7428a3172cf7204d4d017a8", + "shasum": "" + }, + "conflict": { + "3f/pygmentize": "<1.2", + "adaptcms/adaptcms": "<=1.3", + "admidio/admidio": "<=4.3.16", + "adodb/adodb-php": "<=5.22.9", + "aheinze/cockpit": "<2.2", + "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", + "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", + "aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7", + "aimeos/ai-cms-grapesjs": ">=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.9|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.10.8|>=2025.04.1,<2025.10.2", + "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9|==2024.04.1", + "aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7", + "aimeos/aimeos-laravel": "==2021.10", + "aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5", + "airesvsg/acf-to-rest-api": "<=3.1", + "akaunting/akaunting": "<2.1.13", + "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", + "alextselegidis/easyappointments": "<=1.5.2", + "alexusmai/laravel-file-manager": "<=3.3.1", + "algolia/algoliasearch-magento-2": "<=3.16.1|>=3.17.0.0-beta1,<=3.17.1", + "alt-design/alt-redirect": "<1.6.4", + "altcha-org/altcha": "<1.3.1", + "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", + "amazing/media2click": ">=1,<1.3.3", + "ameos/ameos_tarteaucitron": "<1.2.23", + "amphp/artax": "<1.0.6|>=2,<2.0.6", + "amphp/http": "<=1.7.2|>=2,<=2.1", + "amphp/http-client": ">=4,<4.4", + "amphp/http-server": ">=2.0.0.0-RC1-dev,<2.1.10|>=3.0.0.0-beta1,<3.4.4", + "anchorcms/anchor-cms": "<=0.12.7", + "andreapollastri/cipi": "<=3.1.15", "andrewhaine/silverstripe-form-capture": ">=0.2,<=0.2.3|>=1,<1.0.2|>=2,<2.2.5", "aoe/restler": "<1.7.1", "apache-solr-for-typo3/solr": "<2.8.3", "apereo/phpcas": "<1.6", - "api-platform/core": "<3.4.17|>=4.0.0.0-alpha1,<4.0.22", - "api-platform/graphql": "<3.4.17|>=4.0.0.0-alpha1,<4.0.22", + "api-platform/core": "<3.4.17|>=4,<4.0.22|>=4.1,<4.1.5", + "api-platform/graphql": "<3.4.17|>=4,<4.0.22|>=4.1,<4.1.5", "appwrite/server-ce": "<=1.2.1", "arc/web": "<3", "area17/twill": "<1.2.5|>=2,<2.5.3", @@ -5606,22 +7642,22 @@ "athlon1600/php-proxy-app": "<=3", "athlon1600/youtube-downloader": "<=4", "austintoddj/canvas": "<=3.4.2", - "auth0/auth0-php": ">=8.0.0.0-beta1,<8.14", - "auth0/login": "<7.17", - "auth0/symfony": "<5.4", - "auth0/wordpress": "<5.3", + "auth0/auth0-php": ">=3.3,<8.18", + "auth0/login": "<7.20", + "auth0/symfony": "<=5.5", + "auth0/wordpress": "<=5.4", "automad/automad": "<2.0.0.0-alpha5", "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", - "aws/aws-sdk-php": "<3.288.1", - "azuracast/azuracast": "<0.18.3", + "aws/aws-sdk-php": "<3.368", + "azuracast/azuracast": "<=0.23.1", "b13/seo_basics": "<0.8.2", - "backdrop/backdrop": "<1.27.3|>=1.28,<1.28.2", + "backdrop/backdrop": "<=1.32", "backpack/crud": "<3.4.9", "backpack/filemanager": "<2.0.2|>=3,<3.0.9", - "bacula-web/bacula-web": "<8.0.0.0-RC2-dev", - "badaso/core": "<2.7", - "bagisto/bagisto": "<2.1", + "bacula-web/bacula-web": "<9.7.1", + "badaso/core": "<=2.9.11", + "bagisto/bagisto": "<2.3.10", "barrelstrength/sprout-base-email": "<1.2.7", "barrelstrength/sprout-forms": "<3.9", "barryvdh/laravel-translation-manager": "<0.6.8", @@ -5634,7 +7670,8 @@ "bedita/bedita": "<4", "bednee/cooluri": "<1.0.30", "bigfork/silverstripe-form-capture": ">=3,<3.1.1", - "billz/raspap-webgui": "<=3.1.4", + "billz/raspap-webgui": "<3.3.6", + "binarytorch/larecipe": "<2.8.1", "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", "blueimp/jquery-file-upload": "==6.4.4", "bmarshall511/wordpress_zero_spam": "<5.2.13", @@ -5652,7 +7689,8 @@ "bvbmedia/multishop": "<2.0.39", "bytefury/crater": "<6.0.2", "cachethq/cachet": "<2.5.1", - "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", + "cadmium-org/cadmium-cms": "<=0.4.9", + "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10|>=5.2.10,<5.2.12|==5.3", "cakephp/database": ">=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", "cardgate/magento2": "<2.0.33", "cardgate/woocommerce": "<=3.1.15", @@ -5663,37 +7701,46 @@ "causal/oidc": "<4", "cecil/cecil": "<7.47.1", "centreon/centreon": "<22.10.15", + "cesargb/laravel-magiclink": ">=2,<2.25.1", "cesnet/simplesamlphp-module-proxystatistics": "<3.1", "chriskacerguis/codeigniter-restserver": "<=2.7.1", "chrome-php/chrome": "<1.14", + "ci4-cms-erp/ci4ms": "<0.28.5", "civicrm/civicrm-core": ">=4.2,<4.2.9|>=4.3,<4.3.3", "ckeditor/ckeditor": "<4.25", "clickstorm/cs-seo": ">=6,<6.8|>=7,<7.5|>=8,<8.4|>=9,<9.3", "co-stack/fal_sftp": "<0.2.6", - "cockpit-hq/cockpit": "<2.7|==2.7", + "cockpit-hq/cockpit": "<2.11.4", + "code16/sharp": "<9.11.1", "codeception/codeception": "<3.1.3|>=4,<4.1.22", - "codeigniter/framework": "<3.1.9", - "codeigniter4/framework": "<4.5.8", + "codeigniter/framework": "<3.1.10", + "codeigniter4/framework": "<4.6.2", "codeigniter4/shield": "<1.0.0.0-beta8", "codiad/codiad": "<=2.8.4", "codingms/additional-tca": ">=1.7,<1.15.17|>=1.16,<1.16.9", + "codingms/modules": "<4.3.11|>=5,<5.7.4|>=6,<6.4.2|>=7,<7.5.5", "commerceteam/commerce": ">=0.9.6,<0.9.9", "components/jquery": ">=1.0.3,<3.5", - "composer/composer": "<1.10.27|>=2,<2.2.24|>=2.3,<2.7.7", - "concrete5/concrete5": "<9.4.0.0-RC2-dev", + "composer/composer": "<1.10.27|>=2,<2.2.26|>=2.3,<2.9.3", + "concrete5/concrete5": "<9.4.3", "concrete5/core": "<8.5.8|>=9,<9.1", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", - "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.9.40|>=4.10,<4.11.7|>=4.13,<4.13.21|>=5.1,<5.1.4", + "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.13.56|>=5,<5.3.38|>=5.4.0.0-RC1-dev,<5.6.1", "contao/core": "<3.5.39", - "contao/core-bundle": "<4.13.54|>=5,<5.3.30|>=5.4,<5.5.6", + "contao/core-bundle": "<4.13.57|>=5,<5.3.42|>=5.4,<5.6.5", "contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8", "contao/managed-edition": "<=1.5", + "coreshop/core-shop": "<4.1.9", "corveda/phpsandbox": "<1.3.5", "cosenary/instagram": "<=2.3", "couleurcitron/tarteaucitron-wp": "<0.3", - "craftcms/cms": "<4.15.3|>=5,<5.7.5", - "croogo/croogo": "<4", + "cpsit/typo3-mailqueue": "<0.4.3|>=0.5,<0.5.1", + "craftcms/cms": "<4.17.0.0-beta1|>=5,<5.9.0.0-beta1", + "craftcms/commerce": ">=4.0.0.0-RC1-dev,<=4.10|>=5,<=5.5.1", + "craftcms/composer": ">=4.0.0.0-RC1-dev,<=4.10|>=5.0.0.0-RC1-dev,<=5.5.1", + "craftcms/craft": ">=3.5,<=4.16.17|>=5.0.0.0-RC1-dev,<=5.8.21", + "croogo/croogo": "<=4.0.7", "cuyz/valinor": "<0.12", "czim/file-handling": "<1.5|>=2,<2.3", "czproject/git-php": "<4.0.3", @@ -5701,6 +7748,7 @@ "dapphp/securimage": "<3.6.6", "darylldoyle/safe-svg": "<1.9.10", "datadog/dd-trace": ">=0.30,<0.30.2", + "datahihi1/tiny-env": "<1.0.3|>=1.0.9,<1.0.11", "datatables/datatables": "<1.10.10", "david-garcia/phpwhois": "<=4.3.1", "dbrisinajumi/d2files": "<1", @@ -5709,9 +7757,11 @@ "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1|>=7,<7.4", "desperado/xml-bundle": "<=0.1.7", "dev-lancer/minecraft-motd-parser": "<=1.0.5", + "devcode-it/openstamanager": "<=2.9.8", "devgroup/dotplant": "<2020.09.14-dev", "digimix/wp-svg-upload": "<=1", "directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2", + "directorytree/imapengine": "<1.22.3", "dl/yag": "<3.0.1", "dmk/webkitpdf": "<1.1.4", "dnadesign/silverstripe-elemental": "<5.3.12", @@ -5724,47 +7774,59 @@ "doctrine/mongodb-odm": "<1.0.2", "doctrine/mongodb-odm-bundle": "<3.0.1", "doctrine/orm": ">=1,<1.2.4|>=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<19.0.2|==21.0.0.0-beta", + "dolibarr/dolibarr": "<21.0.3", "dompdf/dompdf": "<2.0.4", "doublethreedigital/guest-entries": "<3.1.2", + "drupal-pattern-lab/unified-twig-extensions": "<=0.1", + "drupal/access_code": "<2.0.5", + "drupal/acquia_dam": "<1.1.5", "drupal/admin_audit_trail": "<1.0.5", "drupal/ai": "<1.0.5", "drupal/alogin": "<2.0.6", "drupal/cache_utility": "<1.2.1", + "drupal/civictheme": "<1.12", "drupal/commerce_alphabank_redirect": "<1.0.3", "drupal/commerce_eurobank_redirect": "<2.1.1", "drupal/config_split": "<1.10|>=2,<2.0.2", - "drupal/core": ">=6,<6.38|>=7,<7.102|>=8,<10.3.14|>=10.4,<10.4.5|>=11,<11.0.13|>=11.1,<11.1.5", + "drupal/core": ">=6,<6.38|>=7,<7.103|>=8,<10.4.9|>=10.5,<10.5.6|>=11,<11.1.9|>=11.2,<11.2.8", "drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/currency": "<3.5", "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/email_tfa": "<2.0.6", "drupal/formatter_suite": "<2.1", "drupal/gdpr": "<3.0.1|>=3.1,<3.1.2", "drupal/google_tag": "<1.8|>=2,<2.0.8", "drupal/ignition": "<1.0.4", + "drupal/json_field": "<1.5", "drupal/lightgallery": "<1.6", "drupal/link_field_display_mode_formatter": "<1.6", "drupal/matomo": "<1.24", "drupal/oauth2_client": "<4.1.3", "drupal/oauth2_server": "<2.1", "drupal/obfuscate": "<2.0.1", + "drupal/plausible_tracking": "<1.0.2", "drupal/quick_node_block": "<2", "drupal/rapidoc_elements_field_formatter": "<1.0.1", + "drupal/reverse_proxy_header": "<1.1.2", + "drupal/simple_multistep": "<2", + "drupal/simple_oauth": ">=6,<6.0.7", "drupal/spamspan": "<3.2.1", "drupal/tfa": "<1.10", + "drupal/umami_analytics": "<1.0.1", "duncanmcclean/guest-entries": "<3.1.2", "dweeves/magmi": "<=0.7.24", "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", "ecodev/newsletter": "<=4", "ectouch/ectouch": "<=2.7.2", - "egroupware/egroupware": "<23.1.20240624", + "egroupware/egroupware": "<23.1.20260113|>=26.0.20251208,<26.0.20260113", "elefant/cms": "<2.0.7", "elgg/elgg": "<3.3.24|>=4,<4.0.5", "elijaa/phpmemcacheadmin": "<=1.3", - "elmsln/haxcms": "<11", + "elmsln/haxcms": "<11.0.14", "encore/laravel-admin": "<=1.8.19", "endroid/qr-code-bundle": "<3.4.2", "enhavo/enhavo-app": "<=0.13.1", - "enshrined/svg-sanitize": "<0.15", + "enshrined/svg-sanitize": "<0.22", "erusev/parsedown": "<1.7.2", "ether/logs": "<3.0.4", "evolutioncms/evolution": "<=3.2.3", @@ -5775,33 +7837,34 @@ "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1-dev", "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1-dev|>=5.4,<5.4.11.1-dev|>=2017.12,<2017.12.0.1-dev", "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", - "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.26|>=3.3,<3.3.39", - "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.39|>=3.3,<3.3.39", + "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1|>=5.3.0.0-beta1,<5.3.5", "ezsystems/ezplatform-graphql": ">=1.0.0.0-RC1-dev,<1.0.13|>=2.0.0.0-beta1,<2.3.12", "ezsystems/ezplatform-http-cache": "<2.3.16", - "ezsystems/ezplatform-kernel": "<1.2.5.1-dev|>=1.3,<1.3.35", + "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<1.3.35", "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", "ezsystems/ezplatform-richtext": ">=2.3,<2.3.26|>=3.3,<3.3.40", "ezsystems/ezplatform-solr-search-engine": ">=1.7,<1.7.12|>=2,<2.0.2|>=3.3,<3.3.15", "ezsystems/ezplatform-user": ">=1,<1.0.1", - "ezsystems/ezpublish-kernel": "<6.13.8.2-dev|>=7,<7.5.31", + "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.31", "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.03.5.1", "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", "ezsystems/repository-forms": ">=2.3,<2.3.2.1-dev|>=2.5,<2.5.15", "ezyang/htmlpurifier": "<=4.2", "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", - "facturascripts/facturascripts": "<=2022.08", + "facturascripts/facturascripts": "<2025.81", "fastly/magento2": "<1.2.26", "feehi/cms": "<=2.1.1", "feehi/feehicms": "<=2.1.1", "fenom/fenom": "<=2.12.1", "filament/actions": ">=3.2,<3.2.123", + "filament/filament": ">=4,<4.3.1", "filament/infolists": ">=3,<3.2.115", "filament/tables": ">=3,<3.2.115", "filegator/filegator": "<7.8", "filp/whoops": "<2.1.13", "fineuploader/php-traditional-server": "<=1.2.2", - "firebase/php-jwt": "<6", + "firebase/php-jwt": "<7", "fisharebest/webtrees": "<=2.1.18", "fixpunkt/fp-masterquiz": "<2.2.1|>=3,<3.5.2", "fixpunkt/fp-newsletter": "<1.1.1|>=1.2,<2.1.2|>=2.2,<3.2.6", @@ -5814,6 +7877,7 @@ "floriangaerber/magnesium": "<0.3.1", "fluidtypo3/vhs": "<5.1.1", "fof/byobu": ">=0.3.0.0-beta2,<1.1.7", + "fof/pretty-mail": "<=1.1.2", "fof/upload": "<1.2.3", "foodcoopshop/foodcoopshop": ">=3.2,<3.6.1", "fooman/tcpdf": "<6.2.22", @@ -5829,17 +7893,18 @@ "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", "friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6", "froala/wysiwyg-editor": "<=4.3", - "froxlor/froxlor": "<=2.2.5", + "frosh/adminer-platform": "<2.2.1", + "froxlor/froxlor": "<=2.3.3", "frozennode/administrator": "<=5.0.12", "fuel/core": "<1.8.1", - "funadmin/funadmin": "<=5.0.2", + "funadmin/funadmin": "<=7.1.0.0-RC4", "gaoming13/wechat-php-sdk": "<=1.10.2", "genix/cms": "<=1.1.11", "georgringer/news": "<1.3.3", - "geshi/geshi": "<1.0.8.11-dev", - "getformwork/formwork": "<1.13.1|>=2.0.0.0-beta1,<2.0.0.0-beta4", - "getgrav/grav": "<1.7.46", - "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", + "geshi/geshi": "<=1.0.9.1", + "getformwork/formwork": "<=2.3.3", + "getgrav/grav": "<1.11.0.0-beta1", + "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1|>=5,<=5.2.1", "getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", "getkirby/panel": "<2.5.14", "getkirby/starterkit": "<=3.7.0.2", @@ -5848,8 +7913,9 @@ "globalpayments/php-sdk": "<2", "goalgorilla/open_social": "<12.3.11|>=12.4,<12.4.10|>=13.0.0.0-alpha1,<13.0.0.0-alpha11", "gogentooss/samlbase": "<1.2.7", - "google/protobuf": "<3.15", + "google/protobuf": "<3.4", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", + "gp247/core": "<1.1.24", "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", "grumpydictator/firefly-iii": "<6.1.17", @@ -5858,6 +7924,7 @@ "guzzlehttp/oauth-subscriber": "<0.8.1", "guzzlehttp/psr7": "<1.9.1|>=2,<2.4.5", "haffner/jh_captcha": "<=2.1.3|>=3,<=3.0.2", + "handcraftedinthealps/goodby-csv": "<1.4.3", "harvesthq/chosen": "<1.8.7", "helloxz/imgurl": "<=2.31", "hhxsv5/laravel-s": "<3.7.36", @@ -5867,16 +7934,17 @@ "hov/jobfair": "<1.0.13|>=2,<2.0.2", "httpsoft/http-message": "<1.0.12", "hyn/multi-tenant": ">=5.6,<5.7.2", - "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.14", + "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.25|>=5,<5.0.3", + "ibexa/admin-ui-assets": ">=4.6.0.0-alpha1,<4.6.21", "ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3|>=4.5,<4.5.6|>=4.6,<4.6.2", - "ibexa/fieldtype-richtext": ">=4.6,<4.6.19", + "ibexa/fieldtype-richtext": ">=4.6,<4.6.25|>=5,<5.0.3", "ibexa/graphql": ">=2.5,<2.5.31|>=3.3,<3.3.28|>=4.2,<4.2.3", "ibexa/http-cache": ">=4.6,<4.6.14", "ibexa/post-install": "<1.0.16|>=4.6,<4.6.14", "ibexa/solr": ">=4.5,<4.5.4", - "ibexa/user": ">=4,<4.4.3", + "ibexa/user": ">=4,<4.4.3|>=5,<5.0.4", "icecoder/icecoder": "<=8.1", - "idno/known": "<=1.3.1", + "idno/known": "<1.6.4", "ilicmiljan/secure-props": ">=1.2,<1.2.2", "illuminate/auth": "<5.5.10", "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<6.18.31|>=7,<7.22.4", @@ -5886,10 +7954,10 @@ "imdbphp/imdbphp": "<=5.1.1", "impresscms/impresscms": "<=1.4.5", "impresspages/impresspages": "<1.0.13", - "in2code/femanager": "<5.5.5|>=6,<6.4.1|>=7,<7.4.2|>=8,<8.2.2", + "in2code/femanager": "<6.4.2|>=7,<7.5.3|>=8,<8.3.1", "in2code/ipandlanguageredirect": "<5.1.2", "in2code/lux": "<17.6.1|>=18,<24.0.2", - "in2code/powermail": "<7.5.1|>=8,<8.5.1|>=9,<10.9.1|>=11,<12.4.1", + "in2code/powermail": "<7.5.1|>=8,<8.5.1|>=9,<10.9.1|>=11,<12.5.3|==13", "innologi/typo3-appointments": "<2.0.6", "intelliants/subrion": "<4.2.2", "inter-mediator/inter-mediator": "==5.5", @@ -5900,17 +7968,17 @@ "jackalope/jackalope-doctrine-dbal": "<1.7.4", "jambagecom/div2007": "<0.10.2", "james-heinrich/getid3": "<1.9.21", - "james-heinrich/phpthumb": "<1.7.12", + "james-heinrich/phpthumb": "<=1.7.23", "jasig/phpcas": "<1.3.3", "jbartels/wec-map": "<3.0.3", "jcbrand/converse.js": "<3.3.3", "joelbutcher/socialstream": "<5.6|>=6,<6.2", - "johnbillion/wp-crontrol": "<1.16.2", + "johnbillion/wp-crontrol": "<1.16.2|>=1.17,<1.19.2", "joomla/application": "<1.0.13", "joomla/archive": "<1.1.12|>=2,<2.0.1", "joomla/database": ">=1,<2.2|>=3,<3.4", "joomla/filesystem": "<1.6.2|>=2,<2.0.1", - "joomla/filter": "<1.4.4|>=2,<2.0.1", + "joomla/filter": "<2.0.6|>=3,<3.0.5|==4", "joomla/framework": "<1.5.7|>=2.5.4,<=3.8.12", "joomla/input": ">=2,<2.0.2", "joomla/joomla-cms": "<3.9.12|>=4,<4.4.13|>=5,<5.2.6", @@ -5919,14 +7987,14 @@ "joyqi/hyper-down": "<=2.4.27", "jsdecena/laracom": "<2.0.9", "jsmitty12/phpwhois": "<5.1", - "juzaweb/cms": "<=3.4", + "juzaweb/cms": "<=3.4.2", "jweiland/events2": "<8.3.8|>=9,<9.0.6", "jweiland/kk-downloader": "<1.2.2", "kazist/phpwhois": "<=4.2.6", "kelvinmo/simplexrd": "<3.1.1", "kevinpapst/kimai2": "<1.16.7", "khodakhah/nodcms": "<=3", - "kimai/kimai": "<=2.20.1", + "kimai/kimai": "<2.46", "kitodo/presentation": "<3.2.3|>=3.3,<3.3.4", "klaviyo/magento2-extension": ">=1,<3", "knplabs/knp-snappy": "<=1.4.2", @@ -5945,10 +8013,11 @@ "laravel/framework": "<10.48.29|>=11,<11.44.1|>=12,<12.1.1", "laravel/laravel": ">=5.4,<5.4.22", "laravel/pulse": "<1.3.1", - "laravel/reverb": "<1.4", + "laravel/reverb": "<1.7", "laravel/socialite": ">=1,<2.0.10", "latte/latte": "<2.10.8", - "lavalite/cms": "<=9|==10.1", + "lavalite/cms": "<=10.1", + "lavitto/typo3-form-to-database": "<2.2.5|>=3,<3.2.2|>=4,<4.2.3|>=5,<5.0.2", "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", "league/commonmark": "<2.7", "league/flysystem": "<1.1.4|>=2,<2.1.1", @@ -5956,12 +8025,13 @@ "leantime/leantime": "<3.3", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", "libreform/libreform": ">=2,<=2.0.8", - "librenms/librenms": "<2017.08.18", + "librenms/librenms": "<26.2", "liftkit/database": "<2.13.2", "lightsaml/lightsaml": "<1.3.5", "limesurvey/limesurvey": "<6.5.12", "livehelperchat/livehelperchat": "<=3.91", - "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.5.2", + "livewire-filemanager/filemanager": "<=1.0.4", + "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.6.4", "livewire/volt": "<1.7", "lms/routes": "<2.1.1", "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", @@ -5970,56 +8040,64 @@ "luyadev/yii-helpers": "<1.2.1", "macropay-solutions/laravel-crud-wizard-free": "<3.4.17", "maestroerror/php-heic-to-jpg": "<1.0.5", - "magento/community-edition": "<2.4.5|==2.4.5|>=2.4.5.0-patch1,<2.4.5.0-patch12|==2.4.6|>=2.4.6.0-patch1,<2.4.6.0-patch10|>=2.4.7.0-beta1,<2.4.7.0-patch5|>=2.4.8.0-beta1,<2.4.8.0-beta2", + "magento/community-edition": "<2.4.6.0-patch13|>=2.4.7.0-beta1,<2.4.7.0-patch8|>=2.4.8.0-beta1,<2.4.8.0-patch3|>=2.4.9.0-alpha1,<2.4.9.0-alpha3|==2.4.9", "magento/core": "<=1.9.4.5", "magento/magento1ce": "<1.9.4.3-dev", "magento/magento1ee": ">=1,<1.14.4.3-dev", "magento/product-community-edition": "<2.4.4.0-patch9|>=2.4.5,<2.4.5.0-patch8|>=2.4.6,<2.4.6.0-patch6|>=2.4.7,<2.4.7.0-patch1", "magento/project-community-edition": "<=2.0.2", "magneto/core": "<1.9.4.4-dev", + "mahocommerce/maho": "<25.9", "maikuolan/phpmussel": ">=1,<1.6", "mainwp/mainwp": "<=4.4.3.3", - "mantisbt/mantisbt": "<=2.26.3", + "manogi/nova-tiptap": "<=3.2.6", + "mantisbt/mantisbt": "<2.27.2", "marcwillmann/turn": "<0.3.3", + "marshmallow/nova-tiptap": "<5.7", "matomo/matomo": "<1.11", "matyhtf/framework": "<3.0.6", - "mautic/core": "<5.2.6|>=6.0.0.0-alpha,<6.0.2", + "mautic/core": "<5.2.10|>=6,<6.0.8|>=7.0.0.0-alpha,<7.0.1", "mautic/core-lib": ">=1.0.0.0-beta,<4.4.13|>=5.0.0.0-alpha,<5.1.1", + "mautic/grapes-js-builder-bundle": ">=4,<4.4.18|>=5,<5.2.9|>=6,<6.0.7", "maximebf/debugbar": "<1.19", "mdanter/ecc": "<2", "mediawiki/abuse-filter": "<1.39.9|>=1.40,<1.41.3|>=1.42,<1.42.2", - "mediawiki/cargo": "<3.6.1", + "mediawiki/cargo": "<3.8.3", "mediawiki/core": "<1.39.5|==1.40", "mediawiki/data-transfer": ">=1.39,<1.39.11|>=1.41,<1.41.3|>=1.42,<1.42.2", "mediawiki/matomo": "<2.4.3", "mediawiki/semantic-media-wiki": "<4.0.2", "mehrwert/phpmyadmin": "<3.2", "melisplatform/melis-asset-manager": "<5.0.1", - "melisplatform/melis-cms": "<5.0.1", + "melisplatform/melis-cms": "<5.3.4", + "melisplatform/melis-cms-slider": "<5.3.1", + "melisplatform/melis-core": "<5.3.11", "melisplatform/melis-front": "<5.0.1", "mezzio/mezzio-swoole": "<3.7|>=4,<4.3", "mgallegos/laravel-jqgrid": "<=1.3", "microsoft/microsoft-graph": ">=1.16,<1.109.1|>=2,<2.0.1", "microsoft/microsoft-graph-beta": "<2.0.1", "microsoft/microsoft-graph-core": "<2.0.2", - "microweber/microweber": "<=2.0.16", + "microweber/microweber": "<2.0.20", "mikehaertl/php-shellcommand": "<1.6.1", + "mineadmin/mineadmin": "<=3.0.9", "miniorange/miniorange-saml": "<1.4.3", "mittwald/typo3_forum": "<1.2.1", "mobiledetect/mobiledetectlib": "<2.8.32", "modx/revolution": "<=3.1", "mojo42/jirafeau": "<4.4", "mongodb/mongodb": ">=1,<1.9.2", + "mongodb/mongodb-extension": "<1.21.2", "monolog/monolog": ">=1.8,<1.12", - "moodle/moodle": "<4.3.12|>=4.4,<4.4.8|>=4.5.0.0-beta,<4.5.4", + "moodle/moodle": "<4.5.9|>=5.0.0.0-beta,<5.0.5|>=5.1.0.0-beta,<5.1.2", + "moonshine/moonshine": "<=3.12.5", "mos/cimage": "<0.7.19", "movim/moxl": ">=0.8,<=0.10", "movingbytes/social-network": "<=1.2.1", "mpdf/mpdf": "<=7.1.7", - "munkireport/comment": "<4.1", + "munkireport/comment": "<4", "munkireport/managedinstalls": "<2.6", "munkireport/munki_facts": "<1.5", - "munkireport/munkireport": ">=2.5.3,<5.6.3", "munkireport/reportdata": "<3.5", "munkireport/softwareupdate": "<1.6", "mustache/mustache": ">=2,<2.14.1", @@ -6039,12 +8117,14 @@ "netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15", "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", + "neuron-core/neuron-ai": "<=2.8.11", "nilsteampassnet/teampass": "<3.1.3.1-dev", "nitsan/ns-backup": "<13.0.1", "nonfiction/nterchange": "<4.1.1", "notrinos/notrinos-erp": "<=0.7", "noumo/easyii": "<=0.9", "novaksolutions/infusionsoft-php-sdk": "<1", + "novosga/novosga": "<=2.2.12", "nukeviet/nukeviet": "<4.5.02", "nyholm/psr7": "<1.6.1", "nystudio107/craft-seomatic": "<3.4.12", @@ -6054,15 +8134,15 @@ "october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1", "october/october": "<3.7.5", "october/rain": "<1.0.472|>=1.1,<1.1.2", - "october/system": "<3.7.5", + "october/system": "<=3.7.12|>=4,<=4.0.11", "oliverklee/phpunit": "<3.5.15", "omeka/omeka-s": "<4.0.3", - "onelogin/php-saml": "<2.10.4", + "onelogin/php-saml": "<2.21.1|>=3,<3.8.1|>=4,<4.3.1", "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", - "open-web-analytics/open-web-analytics": "<1.7.4", + "open-web-analytics/open-web-analytics": "<1.8.1", "opencart/opencart": ">=0", "openid/php-openid": "<2.3", - "openmage/magento-lts": "<20.12.3", + "openmage/magento-lts": "<20.16.1", "opensolutions/vimbadmin": "<=3.0.15", "opensource-workshop/connect-cms": "<1.8.7|>=2,<2.4.7", "orchid/platform": ">=8,<14.43", @@ -6081,6 +8161,7 @@ "pagekit/pagekit": "<=1.0.18", "paragonie/ecc": "<2.0.1", "paragonie/random_compat": "<2", + "paragonie/sodium_compat": "<1.24|>=2,<2.5", "passbolt/passbolt_api": "<4.6.2", "paypal/adaptivepayments-sdk-php": "<=3.9.2", "paypal/invoice-sdk-php": "<=3.9", @@ -6093,6 +8174,7 @@ "pear/pear": "<=1.10.1", "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", "personnummer/personnummer": "<3.0.2", + "ph7software/ph7builder": "<=17.9.1", "phanan/koel": "<5.1.4", "phenx/php-svg-lib": "<0.5.2", "php-censor/php-censor": "<2.0.13|>=2.1,<2.1.5", @@ -6103,32 +8185,35 @@ "phpmailer/phpmailer": "<6.5", "phpmussel/phpmussel": ">=1,<1.6", "phpmyadmin/phpmyadmin": "<5.2.2", - "phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5|>=3.2.10,<=4.0.1", + "phpmyfaq/phpmyfaq": "<=4.0.16", "phpoffice/common": "<0.2.9", "phpoffice/math": "<=0.2", "phpoffice/phpexcel": "<=1.8.2", - "phpoffice/phpspreadsheet": "<1.29.9|>=2,<2.1.8|>=2.2,<2.3.7|>=3,<3.9", + "phpoffice/phpspreadsheet": "<1.30|>=2,<2.1.12|>=2.2,<2.4|>=3,<3.10|>=4,<5", + "phppgadmin/phppgadmin": "<=7.13", "phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36", "phpservermon/phpservermon": "<3.6", "phpsysinfo/phpsysinfo": "<3.4.3", - "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", + "phpunit/phpunit": "<8.5.52|>=9,<9.6.33|>=10,<10.5.62|>=11,<11.5.50|>=12,<12.5.8", "phpwhois/phpwhois": "<=4.2.5", "phpxmlrpc/extras": "<0.6.1", "phpxmlrpc/phpxmlrpc": "<4.9.2", + "phraseanet/phraseanet": "==4.0.3", "pi/pi": "<=2.5", - "pimcore/admin-ui-classic-bundle": "<1.7.6", + "pimcore/admin-ui-classic-bundle": "<=1.7.15|>=2.0.0.0-RC1-dev,<=2.2.2", "pimcore/customer-management-framework-bundle": "<4.2.1", "pimcore/data-hub": "<1.2.4", "pimcore/data-importer": "<1.8.9|>=1.9,<1.9.3", "pimcore/demo": "<10.3", "pimcore/ecommerce-framework-bundle": "<1.0.10", "pimcore/perspective-editor": "<1.5.1", - "pimcore/pimcore": "<11.5.4", + "pimcore/pimcore": "<=11.5.14.1|>=12,<12.3.3", + "pimcore/web2print-tools-bundle": "<=5.2.1|>=6.0.0.0-RC1-dev,<=6.1", "piwik/piwik": "<1.11", "pixelfed/pixelfed": "<0.12.5", "plotly/plotly.js": "<2.25.2", "pocketmine/bedrock-protocol": "<8.0.2", - "pocketmine/pocketmine-mp": "<5.25.2", + "pocketmine/pocketmine-mp": "<5.32.1", "pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1", "pressbooks/pressbooks": "<5.18", "prestashop/autoupgrade": ">=4,<4.10.1", @@ -6136,17 +8221,19 @@ "prestashop/blockwishlist": ">=2,<2.1.1", "prestashop/contactform": ">=1.0.1,<4.3", "prestashop/gamification": "<2.3.2", - "prestashop/prestashop": "<8.1.6", + "prestashop/prestashop": "<8.2.4|>=9.0.0.0-alpha1,<9.0.3", "prestashop/productcomments": "<5.0.2", + "prestashop/ps_checkout": "<4.4.1|>=5,<5.0.5", "prestashop/ps_contactinfo": "<=3.3.2", "prestashop/ps_emailsubscription": "<2.6.1", "prestashop/ps_facetedsearch": "<3.4.1", "prestashop/ps_linklist": "<3.1", - "privatebin/privatebin": "<1.4|>=1.5,<1.7.4", - "processwire/processwire": "<=3.0.229", + "privatebin/privatebin": "<1.4|>=1.5,<1.7.4|>=1.7.7,<2.0.3", + "processwire/processwire": "<=3.0.246", "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7", "propel/propel1": ">=1,<=1.7.1", - "pterodactyl/panel": "<1.11.8", + "psy/psysh": "<=0.11.22|>=0.12,<=0.12.18", + "pterodactyl/panel": "<1.12.1", "ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2", "ptrofimov/beanstalk_console": "<1.7.14", "pubnub/pubnub": "<6.1", @@ -6164,18 +8251,18 @@ "rap2hpoutre/laravel-log-viewer": "<0.13", "react/http": ">=0.7,<1.9", "really-simple-plugins/complianz-gdpr": "<6.4.2", - "redaxo/source": "<5.18.3", + "redaxo/source": "<=5.20.1", "remdex/livehelperchat": "<4.29", "renolit/reint-downloadmanager": "<4.0.2|>=5,<5.0.1", "reportico-web/reportico": "<=8.1", "rhukster/dom-sanitizer": "<1.0.7", "rmccue/requests": ">=1.6,<1.8", - "robrichards/xmlseclibs": ">=1,<3.0.4", + "robrichards/xmlseclibs": "<=3.1.3", "roots/soil": "<4.1", "roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11", "rudloff/alltube": "<3.0.3", "rudloff/rtmpdump-bin": "<=2.3.1", - "s-cart/core": "<6.9", + "s-cart/core": "<=9.0.5", "s-cart/s-cart": "<6.9", "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", "sabre/dav": ">=1.6,<1.7.11|>=1.8,<1.8.9", @@ -6183,13 +8270,14 @@ "scheb/two-factor-bundle": "<3.26|>=4,<4.11", "sensiolabs/connect": "<4.2.3", "serluck/phpwhois": "<=4.2.6", + "setasign/fpdi": "<2.6.4", "sfroemken/url_redirect": "<=1.2.1", "sheng/yiicms": "<1.2.1", - "shopware/core": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev", - "shopware/platform": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev", + "shopware/core": "<6.6.10.9-dev|>=6.7,<6.7.6.1-dev", + "shopware/platform": "<6.6.10.7-dev|>=6.7,<6.7.3.1-dev", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<=5.7.17", - "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", + "shopware/shopware": "<=5.7.17|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.6.1-dev", + "shopware/storefront": "<6.6.10.10-dev|>=6.7,<6.7.5.1-dev", "shopxo/shopxo": "<=6.4", "showdoc/showdoc": "<2.10.4", "shuchkin/simplexlsx": ">=1.0.12,<1.1.13", @@ -6211,6 +8299,7 @@ "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1", "silverstripe/userforms": "<3|>=5,<5.4.2", "silverstripe/versioned-admin": ">=1,<1.11.1", + "simogeo/filemanager": "<=2.5", "simple-updates/phpwhois": "<=1", "simplesamlphp/saml2": "<=4.16.15|>=5.0.0.0-alpha1,<=5.0.0.0-alpha19", "simplesamlphp/saml2-legacy": "<=4.16.15", @@ -6229,20 +8318,25 @@ "slim/slim": "<2.6", "slub/slub-events": "<3.0.3", "smarty/smarty": "<4.5.3|>=5,<5.1.1", - "snipe/snipe-it": "<8.1", + "snipe/snipe-it": "<=8.3.4", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", + "solspace/craft-freeform": "<4.1.29|>=5,<=5.14.6", + "soosyze/soosyze": "<=2", "spatie/browsershot": "<5.0.5", "spatie/image-optimizer": "<1.7.3", "spencer14420/sp-php-email-handler": "<1", "spipu/html2pdf": "<5.2.8", + "spiral/roadrunner": "<2025.1", "spoon/library": "<1.4.1", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "ssddanbrown/bookstack": "<24.05.1", - "starcitizentools/citizen-skin": ">=2.4.2,<3.3.1", - "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2", - "statamic/cms": "<=5.16", + "starcitizentools/citizen-skin": ">=1.9.4,<3.9", + "starcitizentools/short-description": ">=4,<4.0.1", + "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2|>=3,<3.1.1", + "starcitizenwiki/embedvideo": "<=4", + "statamic/cms": "<5.73.11|>=6,<6.4", "stormpath/sdk": "<9.9.99", "studio-42/elfinder": "<=2.1.64", "studiomitte/friendlycaptcha": "<0.1.4", @@ -6273,7 +8367,7 @@ "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4", "symfony/http-client": ">=4.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", - "symfony/http-foundation": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", + "symfony/http-foundation": "<5.4.50|>=6,<6.4.29|>=7,<7.3.7", "symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", @@ -6281,7 +8375,7 @@ "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/polyfill": ">=1,<1.10", "symfony/polyfill-php55": ">=1,<1.10", - "symfony/process": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", + "symfony/process": "<5.4.51|>=6,<6.4.33|>=7,<7.1.7|>=7.3,<7.3.11|>=7.4,<7.4.5|>=8,<8.0.5", "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/routing": ">=2,<2.0.19", "symfony/runtime": ">=5.3,<5.4.46|>=6,<6.4.14|>=7,<7.1.7", @@ -6292,7 +8386,7 @@ "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", - "symfony/symfony": "<5.4.47|>=6,<6.4.15|>=7,<7.1.8", + "symfony/symfony": "<5.4.51|>=6,<6.4.33|>=7,<7.3.11|>=7.4,<7.4.5|>=8,<8.0.5", "symfony/translation": ">=2,<2.0.17", "symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8", "symfony/ux-autocomplete": "<2.11.2", @@ -6316,7 +8410,7 @@ "thelia/thelia": ">=2.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", "thinkcmf/thinkcmf": "<6.0.8", - "thorsten/phpmyfaq": "<=4.0.1", + "thorsten/phpmyfaq": "<4.0.18|>=4.1.0.0-alpha,<=4.1.0.0-beta2", "tikiwiki/tiki-manager": "<=17.1", "timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1", "tinymce/tinymce": "<7.2", @@ -6327,19 +8421,20 @@ "topthink/framework": "<6.0.17|>=6.1,<=8.0.4", "topthink/think": "<=6.1.1", "topthink/thinkphp": "<=3.2.3|>=6.1.3,<=8.0.4", - "torrentpier/torrentpier": "<=2.4.3", + "torrentpier/torrentpier": "<=2.8.8", "tpwd/ke_search": "<4.0.3|>=4.1,<4.6.6|>=5,<5.0.2", "tribalsystems/zenario": "<=9.7.61188", "truckersmp/phpwhois": "<=4.3.1", "ttskch/pagination-service-provider": "<1", - "twbs/bootstrap": "<=3.4.1|>=4,<=4.6.2", + "twbs/bootstrap": "<3.4.1|>=4,<4.3.1", "twig/twig": "<3.11.2|>=3.12,<3.14.1|>=3.16,<3.19", + "typicms/core": "<16.1.7", "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", - "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<10.4.46|>=11,<11.5.40|>=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", "typo3/cms-belog": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", - "typo3/cms-beuser": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", - "typo3/cms-core": "<=8.7.56|>=9,<=9.5.50|>=10,<=10.4.49|>=11,<=11.5.43|>=12,<=12.4.30|>=13,<=13.4.11", - "typo3/cms-dashboard": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-beuser": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", + "typo3/cms-core": "<=8.7.56|>=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", + "typo3/cms-dashboard": ">=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1", "typo3/cms-extensionmanager": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", "typo3/cms-felogin": ">=4.2,<4.2.3", @@ -6349,10 +8444,14 @@ "typo3/cms-indexed-search": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", "typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8|==13.4.2", "typo3/cms-lowlevel": ">=11,<=11.5.41", + "typo3/cms-recordlist": ">=11,<11.5.48", + "typo3/cms-recycler": ">=9,<9.5.55|>=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", + "typo3/cms-redirects": ">=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", "typo3/cms-rte-ckeditor": ">=9.5,<9.5.42|>=10,<10.4.39|>=11,<11.5.30", "typo3/cms-scheduler": ">=11,<=11.5.41", "typo3/cms-setup": ">=9,<=9.5.50|>=10,<=10.4.49|>=11,<=11.5.43|>=12,<=12.4.30|>=13,<=13.4.11", "typo3/cms-webhooks": ">=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-workspaces": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", "typo3/html-sanitizer": ">=1,<=1.5.2|>=2,<=2.1.3", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", @@ -6362,7 +8461,8 @@ "ua-parser/uap-php": "<3.8", "uasoft-indonesia/badaso": "<=2.9.7", "unisharp/laravel-filemanager": "<2.9.1", - "unopim/unopim": "<0.1.5", + "universal-omega/dynamic-page-list3": "<3.6.4", + "unopim/unopim": "<=0.3", "userfrosting/userfrosting": ">=0.3.1,<4.6.3", "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", "uvdesk/community-skeleton": "<=1.1.1", @@ -6376,7 +8476,7 @@ "vertexvaar/falsftp": "<0.2.6", "villagedefrance/opencart-overclocked": "<=1.11.1", "vova07/yii2-fileapi-widget": "<0.1.9", - "vrana/adminer": "<4.8.1", + "vrana/adminer": "<5.4.2", "vufind/vufind": ">=2,<9.1.1", "waldhacker/hcaptcha": "<2.1.2", "wallabag/tcpdf": "<6.2.22", @@ -6392,11 +8492,12 @@ "webklex/laravel-imap": "<5.3", "webklex/php-imap": "<5.3", "webpa/webpa": "<3.1.2", + "webreinvent/vaahcms": "<=2.3.1", "wikibase/wikibase": "<=1.39.3", "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", "winter/wn-backend-module": "<1.2.4", - "winter/wn-cms-module": "<1.0.476|>=1.1,<1.1.11|>=1.2,<1.2.7", + "winter/wn-cms-module": "<=1.2.9", "winter/wn-dusk-plugin": "<2.1", "winter/wn-system-module": "<1.2.4", "wintercms/winter": "<=1.2.3", @@ -6408,11 +8509,11 @@ "wpanel/wpanel4-cms": "<=4.3.1", "wpcloud/wp-stateless": "<3.2", "wpglobus/wpglobus": "<=1.9.6", - "wwbn/avideo": "<14.3", + "wwbn/avideo": "<=21", "xataface/xataface": "<3", "xpressengine/xpressengine": "<3.0.15", "yab/quarx": "<2.4.5", - "yeswiki/yeswiki": "<4.5.4", + "yeswiki/yeswiki": "<=4.5.4", "yetiforce/yetiforce-crm": "<6.5", "yidashi/yii2cmf": "<=2", "yii2mod/yii2-cms": "<1.9.2", @@ -6427,8 +8528,10 @@ "yiisoft/yii2-redis": "<2.0.20", "yikesinc/yikes-inc-easy-mailchimp-extender": "<6.8.6", "yoast-seo-for-typo3/yoast_seo": "<7.2.3", - "yourls/yourls": "<=1.8.2", + "yourls/yourls": "<=1.10.2", "yuan1994/tpadmin": "<=1.3.12", + "yungifez/skuul": "<=2.6.5", + "z-push/z-push-dev": "<2.7.6", "zencart/zencart": "<=1.5.7.0-beta", "zendesk/zendesk_api_client_php": "<2.2.11", "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", @@ -6465,45 +8568,1306 @@ "zf-commons/zfc-user": "<1.2.2", "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", "zfr/zfr-oauth2-server-module": "<0.1.2", - "zoujingli/thinkadmin": "<=6.1.53" + "zoujingli/thinkadmin": "<=6.1.53", + "zumba/json-serializer": "<3.2.3" + }, + "default-branch": true, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "role": "maintainer" + }, + { + "name": "Ilya Tribusean", + "email": "slash3b@gmail.com", + "role": "maintainer" + } + ], + "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "keywords": [ + "dev" + ], + "support": { + "issues": "https://github.com/Roave/SecurityAdvisories/issues", + "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + }, + "funding": [ + { + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", + "type": "tidelift" + } + ], + "time": "2026-03-03T18:18:18+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-01-24T09:25:16+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "0735b90f4da94969541dac1da743446e276defa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "reference": "0735b90f4da94969541dac1da743446e276defa6", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:09:11+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-10T07:50:56+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "spatie/array-to-xml", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/88b2f3852a922dd73177a68938f8eb2ec70c7224", + "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } }, - "default-branch": true, - "type": "metapackage", "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "role": "maintainer" + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/3.4.4" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" }, { - "name": "Ilya Tribusean", - "email": "slash3b@gmail.com", - "role": "maintainer" + "url": "https://github.com/spatie", + "type": "github" } ], - "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "time": "2025-12-15T09:00:41+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.13.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", "keywords": [ - "dev" + "phpcs", + "standards", + "static analysis" ], "support": { - "issues": "https://github.com/Roave/SecurityAdvisories/issues", - "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" }, "funding": [ { - "url": "https://github.com/Ocramius", + "url": "https://github.com/PHPCSStandards", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", - "type": "tidelift" + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-04T16:30:35+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-11-17T20:03:58+00:00" + }, + { + "name": "vimeo/psalm", + "version": "5.26.1", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer-runtime-api": "^2", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.1", + "felixfbecker/language-server-protocol": "^1.5.2", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.17", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" + }, + "conflict": { + "nikic/php-parser": "4.17.0" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "amphp/phpunit-util": "^2.0", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalter" + ], + "type": "project", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev", + "dev-3.x": "3.x-dev", + "dev-4.x": "4.x-dev", + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown" } ], - "time": "2025-06-11T20:05:14+00:00" + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php", + "static analysis" + ], + "support": { + "docs": "https://psalm.dev/docs", + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm" + }, + "time": "2024-09-08T18:53:08+00:00" } ], "aliases": [], @@ -6520,7 +9884,7 @@ "ext-libxml": "*", "ext-simplexml": "*" }, - "platform-dev": {}, + "platform-dev": [], "platform-overrides": { "php": "8.3" }, diff --git a/docs/administrators/README.md b/docs/administrators-legacy/README.md similarity index 100% rename from docs/administrators/README.md rename to docs/administrators-legacy/README.md diff --git a/docs/administrators/cloudevents.md b/docs/administrators-legacy/cloudevents.md similarity index 100% rename from docs/administrators/cloudevents.md rename to docs/administrators-legacy/cloudevents.md diff --git a/docs/administrators/consumer.md b/docs/administrators-legacy/consumer.md similarity index 100% rename from docs/administrators/consumer.md rename to docs/administrators-legacy/consumer.md diff --git a/docs/administrators/mapping/README.md b/docs/administrators-legacy/mapping/README.md similarity index 100% rename from docs/administrators/mapping/README.md rename to docs/administrators-legacy/mapping/README.md diff --git a/docs/administrators/mapping/mapping.md b/docs/administrators-legacy/mapping/mapping.md similarity index 100% rename from docs/administrators/mapping/mapping.md rename to docs/administrators-legacy/mapping/mapping.md diff --git a/docs/administrators/sources/README.md b/docs/administrators-legacy/sources/README.md similarity index 100% rename from docs/administrators/sources/README.md rename to docs/administrators-legacy/sources/README.md diff --git a/docs/administrators-legacy/sources/source.md b/docs/administrators-legacy/sources/source.md new file mode 100644 index 000000000..75abe4c31 --- /dev/null +++ b/docs/administrators-legacy/sources/source.md @@ -0,0 +1,75 @@ +# Source Configuration + +## Source + +A **Source** represents an external service or system that OpenConnector can interact with. This is typically an API but can also be databases, file servers, or other services. Sources allow the Connector to retrieve, process, and synchronize data. + +### Basic setup + +To configure a source, you need to define its basic properties: + +1. **Name**: A descriptive identifier for the source (e.g., "Noordwijk"). This name will appear in the UI to help you quickly locate the source. +2. **Description**: A brief explanation of the source's purpose or content (optional). +3. **Type**: Defines the type of connection (e.g., API, database, file server). +4. **Location**: The endpoint or base URL of the source (e.g., `https://zaaksysteem.noordwijk.nl/api/v1`). + +![alt text](../../sources/image-6.png) + +**Note**: Ensure the location does not end with a `/`. If included, it will be removed during saving. + +### Authentication + +If the source requires authentication (e.g., an API key, certificate, or OAuth token), these credentials are managed securely in the **Vault**. To authenticate: + +1. Add the required authentication details in the Vault. +2. Link the authentication method to the source. + +Multiple authentication methods can be associated with a single source, offering flexibility for different connection scenarios. + +### Synchronisations + +Synchronizations between sources and the system can be managed in the **Synchronizations** tab for each source. This provides an overview of all configured synchronization tasks related to a source. + +@todo add screenshots + +### Configuration + +Sources are called using the **Call Service**, based on the popular [Guzzle library](https://docs.guzzlephp.org/en/stable/). This enables flexible configuration of connection parameters. You can add custom configurations using a **dot.notation** format. + +**Example Configuration:** + +To set the `Accept` header for API calls: + +* **Key**: `headers.Accept` +* **Value**: `application/json` + +This creates the following request header: + +![the add Configuration modal](../../sources/image-3.png) + +This configuration will lead to the following configuration on the call service: + +``` +headers: + - name: Accept + value: application/json +``` + +Other commonly used options include: + +* **Headers**: Define custom request headers. +* **Query**: Add query parameters to requests. + +But all [guzzle options](https://docs.guzzlephp.org/en/stable/request-options.html) are supported. So for example, you can change the timeout of the call by adding the following configuration to the source: + +`timeout = 10` + +## Logging + +The Call Service logs all requests and responses for each source. Logs can be viewed directly in the **Logs** tab of the source's detail page. + +![alt text](../../sources/image-4.png) + +![alt text](../../sources/image-5.png) + +**Note**: Future updates will allow configuring the log retention period. diff --git a/docs/administrators/sources/xxllnctopublication.md b/docs/administrators-legacy/sources/xxllnctopublication.md similarity index 100% rename from docs/administrators/sources/xxllnctopublication.md rename to docs/administrators-legacy/sources/xxllnctopublication.md diff --git a/docs/administrators/synchronysation/README.md b/docs/administrators-legacy/synchronysation/README.md similarity index 100% rename from docs/administrators/synchronysation/README.md rename to docs/administrators-legacy/synchronysation/README.md diff --git a/docs/administrators/synchronysation/contract.md b/docs/administrators-legacy/synchronysation/contract.md similarity index 100% rename from docs/administrators/synchronysation/contract.md rename to docs/administrators-legacy/synchronysation/contract.md diff --git a/docs/administrators/synchronysation/synchronysation.md b/docs/administrators-legacy/synchronysation/synchronysation.md similarity index 100% rename from docs/administrators/synchronysation/synchronysation.md rename to docs/administrators-legacy/synchronysation/synchronysation.md diff --git a/docs/administrators/vault/README.md b/docs/administrators-legacy/vault/README.md similarity index 100% rename from docs/administrators/vault/README.md rename to docs/administrators-legacy/vault/README.md diff --git a/docs/administrators/vault/applications.md b/docs/administrators-legacy/vault/applications.md similarity index 100% rename from docs/administrators/vault/applications.md rename to docs/administrators-legacy/vault/applications.md diff --git a/docs/administrators/vault/authentication.md b/docs/administrators-legacy/vault/authentication.md similarity index 100% rename from docs/administrators/vault/authentication.md rename to docs/administrators-legacy/vault/authentication.md diff --git a/website/docs/administrators/logging.md b/docs/administrators/logging.md similarity index 100% rename from website/docs/administrators/logging.md rename to docs/administrators/logging.md diff --git a/docs/administrators/sources/source.md b/docs/administrators/sources/source.md index 75abe4c31..7c95db835 100644 --- a/docs/administrators/sources/source.md +++ b/docs/administrators/sources/source.md @@ -1,75 +1,208 @@ -# Source Configuration +# Source -## Source +## Configuration -A **Source** represents an external service or system that OpenConnector can interact with. This is typically an API but can also be databases, file servers, or other services. Sources allow the Connector to retrieve, process, and synchronize data. +Sources can be configured with various options to customize how they connect to external systems. All [Guzzle options](https://docs.guzzlephp.org/en/stable/request-options.html) are supported for HTTP-based sources. -### Basic setup +### Common Configuration Options -To configure a source, you need to define its basic properties: +Some commonly used configuration options include: -1. **Name**: A descriptive identifier for the source (e.g., "Noordwijk"). This name will appear in the UI to help you quickly locate the source. -2. **Description**: A brief explanation of the source's purpose or content (optional). -3. **Type**: Defines the type of connection (e.g., API, database, file server). -4. **Location**: The endpoint or base URL of the source (e.g., `https://zaaksysteem.noordwijk.nl/api/v1`). +**Authentication:** +```json +headers: + - name: Authorization + value: Bearer YOUR_TOKEN_HERE +``` + +**Custom Headers:** +```json +headers: + - name: Accept + value: application/json +``` + +**Query Parameters:** +```json +query: + - name: limit + value: 100 +``` -![alt text](../../sources/image-6.png) +**Timeout Configuration:** +```json +timeout: 10 +``` -**Note**: Ensure the location does not end with a `/`. If included, it will be removed during saving. +### Additional Configuration Parameters -### Authentication +You can set additional configuration parameters for sources: -If the source requires authentication (e.g., an API key, certificate, or OAuth token), these credentials are managed securely in the **Vault**. To authenticate: +- **logBody**: Controls response body logging behavior + - `true` or `1`: Log all response bodies + - `false` or `0`: Log only error response bodies (status codes 400-599) + - Default: `false` (only error responses are logged) -1. Add the required authentication details in the Vault. -2. Link the authentication method to the source. +Example: +```json +{ + "logBody": true, + "timeout": 30 +} +``` -Multiple authentication methods can be associated with a single source, offering flexibility for different connection scenarios. +## Call Logs Management -### Synchronisations +The OpenConnector provides comprehensive call log management for monitoring API interactions with your sources. The **Source Logs** page offers professional-grade tools for analyzing, filtering, and managing your API call history. -Synchronizations between sources and the system can be managed in the **Synchronizations** tab for each source. This provides an overview of all configured synchronization tasks related to a source. +### Accessing Source Logs -@todo add screenshots +You can access source logs in two ways: -### Configuration +1. **From the main navigation**: Go to Sources → Logs +2. **From individual sources**: Click 'View Logs' on any source in the Sources list -Sources are called using the **Call Service**, based on the popular [Guzzle library](https://docs.guzzlephp.org/en/stable/). This enables flexible configuration of connection parameters. You can add custom configurations using a **dot.notation** format. +### Call Logs Interface -**Example Configuration:** +The Source Logs page provides a streamlined, table-based interface optimized for log analysis: -To set the `Accept` header for API calls: +#### Key Features -* **Key**: `headers.Accept` -* **Value**: `application/json` +- **Table-only view**: Focused on readability and data density +- **Advanced filtering**: Comprehensive filtering system via sidebar +- **Export functionality**: Export filtered logs for external analysis +- **Bulk operations**: Select and delete multiple logs efficiently +- **Real-time updates**: Refresh logs with latest data -This creates the following request header: +#### Log Information Displayed -![the add Configuration modal](../../sources/image-3.png) +Each log entry shows: -This configuration will lead to the following configuration on the call service: +- **Status Code**: HTTP response status with color-coded indicators + - 🟢 Green: Success (2xx) + - 🟡 Yellow: Client errors (4xx) + - 🔴 Red: Server errors (5xx) +- **Source**: Name of the source that made the call +- **HTTP Method**: GET, POST, PUT, PATCH, DELETE with color-coded badges +- **Endpoint**: Target URL or endpoint path +- **Response Time**: Request duration with performance indicators + - 🟢 Fast: < 1 second + - 🟡 Medium: 1-3 seconds + - 🔴 Slow: > 3 seconds +- **Created**: Timestamp of the API call -``` -headers: - - name: Accept - value: application/json +### Advanced Filtering + +The sidebar provides powerful filtering capabilities: + +#### Source Filtering +- Filter logs by specific sources +- View logs from all sources or focus on individual sources + +#### Status Code Filtering +- Filter by HTTP response codes: + - Success codes: 200, 201 + - Client errors: 400, 401, 403, 404 + - Server errors: 500, 502, 503 + +#### HTTP Method Filtering +- Filter by request methods: GET, POST, PUT, PATCH, DELETE + +#### Date Range Filtering +- **From Date**: Start of time range +- **To Date**: End of time range +- Custom date/time picker for precise filtering + +#### Advanced Filters +- **Endpoint Filter**: Text-based filtering for specific endpoints +- **Show Only Errors**: Toggle to display only failed requests (4xx, 5xx) +- **Show Slow Requests**: Toggle to display only slow requests (>5 seconds) + +#### Filter Statistics + +The sidebar also provides real-time statistics: + +- **Total Logs**: Overall count of call logs +- **Success Rate**: Percentage of successful calls (2xx status codes) +- **Average Response Time**: Mean response time across all calls +- **Status Distribution**: Breakdown of response codes +- **Most Active Sources**: Sources generating the most calls + +### Log Actions + +#### Individual Log Actions +- **View Details**: Open detailed log information in a modal +- **Copy Data**: Copy complete log data to clipboard (JSON format) +- **Delete**: Remove individual log entries + +#### Bulk Operations +- **Select All**: Select all visible logs for bulk operations +- **Bulk Delete**: Remove multiple selected logs at once +- **Export**: Download filtered logs in CSV format + +### Performance Monitoring + +#### Response Time Analysis +The interface provides visual indicators for performance monitoring: + +- **Fast responses** (< 1s): Green highlighting +- **Medium responses** (1-3s): Yellow highlighting +- **Slow responses** (> 3s): Red highlighting + +#### Error Analysis +- Color-coded status indicators help identify patterns +- Filter by error types to troubleshoot issues +- Export error logs for detailed analysis + +### Export and Integration + +#### Export Formats +- **CSV Export**: Download logs in comma-separated format +- **Filtered Export**: Export only logs matching current filters +- **Complete Data**: Include all log fields and metadata + +#### Integration with Monitoring Tools +The log data can be integrated with external monitoring systems: + +```php +// Example: Export log metrics +$metrics = [ + 'total_calls' => $logCount, + 'success_rate' => $successRate, + 'average_response_time' => $avgResponseTime, + 'error_count' => $errorCount +]; ``` -Other commonly used options include: +### Best Practices -* **Headers**: Define custom request headers. -* **Query**: Add query parameters to requests. +#### Log Management +1. **Regular Cleanup**: Periodically remove old logs to maintain performance +2. **Filter Strategy**: Use specific filters to focus on relevant data +3. **Export Important Data**: Download critical logs before cleanup +4. **Monitor Trends**: Watch for patterns in response times and errors -But all [guzzle options](https://docs.guzzlephp.org/en/stable/request-options.html) are supported. So for example, you can change the timeout of the call by adding the following configuration to the source: +#### Performance Optimization +1. **Response Time Monitoring**: Track slow endpoints for optimization +2. **Error Pattern Analysis**: Identify recurring issues +3. **Source Performance**: Compare performance across different sources +4. **Rate Limit Awareness**: Monitor for rate limiting indicators -`timeout = 10` +#### Troubleshooting +1. **Error Investigation**: Use status code filters to isolate issues +2. **Timeline Analysis**: Use date filters to correlate issues with events +3. **Endpoint Analysis**: Filter by specific endpoints to debug problems +4. **Source Comparison**: Compare behavior across different sources -## Logging +**Note**: The log retention period and cleanup policies can be configured in the system settings. -The Call Service logs all requests and responses for each source. Logs can be viewed directly in the **Logs** tab of the source's detail page. +## Testing Sources -![alt text](../../sources/image-4.png) +Use the test functionality to verify your source configuration before deploying: -![alt text](../../sources/image-5.png) +1. Navigate to your source configuration +2. Click the 'Test Source' button +3. Review the test results for connectivity and authentication +4. Adjust configuration as needed based on test feedback -**Note**: Future updates will allow configuring the log retention period. +Test results will appear in the source logs and can be analyzed using the same filtering and analysis tools. \ No newline at end of file diff --git a/website/docs/async-file-fetching.md b/docs/async-file-fetching.md similarity index 100% rename from website/docs/async-file-fetching.md rename to docs/async-file-fetching.md diff --git a/website/docs/configurations.md b/docs/configurations.md similarity index 100% rename from website/docs/configurations.md rename to docs/configurations.md diff --git a/website/docs/diagrams/object-deletion-states.puml b/docs/diagrams/object-deletion-states.puml similarity index 100% rename from website/docs/diagrams/object-deletion-states.puml rename to docs/diagrams/object-deletion-states.puml diff --git a/website/docs/diagrams/object-deletion-states.svg b/docs/diagrams/object-deletion-states.svg similarity index 100% rename from website/docs/diagrams/object-deletion-states.svg rename to docs/diagrams/object-deletion-states.svg diff --git a/website/docs/diagrams/object-locking-sequence.puml b/docs/diagrams/object-locking-sequence.puml similarity index 100% rename from website/docs/diagrams/object-locking-sequence.puml rename to docs/diagrams/object-locking-sequence.puml diff --git a/website/docs/diagrams/object-locking-sequence.svg b/docs/diagrams/object-locking-sequence.svg similarity index 100% rename from website/docs/diagrams/object-locking-sequence.svg rename to docs/diagrams/object-locking-sequence.svg diff --git a/website/docs/examples/parallel-timing-example.md b/docs/examples/parallel-timing-example.md similarity index 100% rename from website/docs/examples/parallel-timing-example.md rename to docs/examples/parallel-timing-example.md diff --git a/website/docs/flow-token.md b/docs/flow-token.md similarity index 100% rename from website/docs/flow-token.md rename to docs/flow-token.md diff --git a/website/docs/image.png b/docs/image.png similarity index 100% rename from website/docs/image.png rename to docs/image.png diff --git a/website/docs/intro.md b/docs/intro.md similarity index 100% rename from website/docs/intro.md rename to docs/intro.md diff --git a/website/docs/origin-id.png b/docs/origin-id.png similarity index 100% rename from website/docs/origin-id.png rename to docs/origin-id.png diff --git a/website/docs/parallel-page-fetching.md b/docs/parallel-page-fetching.md similarity index 100% rename from website/docs/parallel-page-fetching.md rename to docs/parallel-page-fetching.md diff --git a/website/docs/quality-assurance.md b/docs/quality-assurance.md similarity index 100% rename from website/docs/quality-assurance.md rename to docs/quality-assurance.md diff --git a/website/docs/rule-tags.png b/docs/rule-tags.png similarity index 100% rename from website/docs/rule-tags.png rename to docs/rule-tags.png diff --git a/website/docs/rules.md b/docs/rules.md similarity index 100% rename from website/docs/rules.md rename to docs/rules.md diff --git a/website/docs/security-best-practices.md b/docs/security-best-practices.md similarity index 100% rename from website/docs/security-best-practices.md rename to docs/security-best-practices.md diff --git a/website/docs/sources.md b/docs/sources.md similarity index 100% rename from website/docs/sources.md rename to docs/sources.md diff --git a/website/docs/sub-objects.png b/docs/sub-objects.png similarity index 100% rename from website/docs/sub-objects.png rename to docs/sub-objects.png diff --git a/docs/synchronization-legacy.md b/docs/synchronization-legacy.md new file mode 100644 index 000000000..00e6664ca --- /dev/null +++ b/docs/synchronization-legacy.md @@ -0,0 +1,39 @@ +# Synchronization Actions + +## Concept + +Just as with Endpoints, synchronizations can trigger additional actions (at this moment called rules). +These can change the content of an object, but also trigger additional synchronizations. + +These actions can be created by creating a rule (an action) and adding it by id to the property +actions in the Synchronization. + +## Synchronizing files +In order to fetch a file from an external source and store it in the Nextcloud Filesystem in a way that OpenRegister can +connect it to an object, there are two predefined actions: + +- `fetch_file`: This action downloads the file and substitutes the base encoded content into the variable that contained the file url +- `write_file`: This action takes a file's content in base encoding and the filename (otherwise it will use a default filename), and writes it to the filesystem. + +### Fetch file + +This action should be run on timing `after` (when the object has been stored). +The action takes the following parameters in the `configuration` property: + +- `source` (required): The id of the source where the file can be downloaded +- `filePath` (required): The dot path of the location in the input object that contains the file url or file path. +- `method` (optional): The HTTP method that should be used to fetch the file. Defaults to GET +- `sourceConfiguration` (optional): Additional configuration for the source that only holds for fetching files. + +When properly configured this action will download the file from the given source and substitute the base64 encoded content in the returned object. +It is preferred to run this action in combination with `write_file` immediately after, so the file contents are properly stored in the Nextcloud file system instead of written to a database. + +### Write file + +This action should be run on timing `after`, and if combined with `fetch_file` it should be run in order after `fetch_file`. +The action takes the followin parameters in the `configuration` property: + +- `filePath` (required): The dot path of the location in the input object that contains the base64 encoded content of the file. +- `fileNamePath` (required): The dot path of the location in the input object that contains the filename + +This will write the file to the nextcloud filesystem in the folder that belongs to the written object, and substitutes the file content in the returned data with the path of the object in the Nextcloud File System. diff --git a/website/docs/synchronization-timing.md b/docs/synchronization-timing.md similarity index 100% rename from website/docs/synchronization-timing.md rename to docs/synchronization-timing.md diff --git a/docs/synchronization.md b/docs/synchronization.md index 00e6664ca..1f62751b4 100644 --- a/docs/synchronization.md +++ b/docs/synchronization.md @@ -1,39 +1,336 @@ -# Synchronization Actions +# Synchronization -## Concept +Synchronization is a core feature that enables data transfer between different systems and APIs. Here's how it works: -Just as with Endpoints, synchronizations can trigger additional actions (at this moment called rules). -These can change the content of an object, but also trigger additional synchronizations. +## Overview +- Synchronizations define how data should be synchronized between a source and target system +- Each synchronization has a source configuration (where to get data from) and target configuration (where to send data to) +- The synchronization process handles pagination, data mapping, and maintaining sync state -These actions can be created by creating a rule (an action) and adding it by id to the property -actions in the Synchronization. +## Key Components -## Synchronizing files -In order to fetch a file from an external source and store it in the Nextcloud Filesystem in a way that OpenRegister can -connect it to an object, there are two predefined actions: +### Source Configuration +- Defines where to fetch data from (API endpoint, database etc) +- Specifies how to map source data to target format (incomming data) +- Specifies how to locate objects in the response using resultsPosition: + - `_root`: Uses the entire response body as the objects array + - Dot notation (e.g. `data.items`): Extracts objects from a nested path + - Common keys (`items`, `result`, `results`): Automatically checks these standard locations + - Custom path: Specify any JSON path to locate the objects +- Configures pagination settings (paginationQuery) +- Can include conditions to filter which objects to sync using JSON Logic +- Source type can be set to API or other supported types +- Source ID mapping allows specifying position of IDs in source objects +- Optional endpoint configuration for fetching data +- usesPagination: configure this field if you know this source **does not** uses pagination or next endpoint. Use value "false". If this source uses next endpoint it will auto detect. +- Optional configuration of sub objects. +- (Optional) restrictDeletion: if set to true or 1, restricts the deletion of objects in the synchronization to objects of which the origin id was in the request to the source, to prevent deletion of objects when a specified set of objects is synchronized. -- `fetch_file`: This action downloads the file and substitutes the base encoded content into the variable that contained the file url -- `write_file`: This action takes a file's content in base encoding and the filename (otherwise it will use a default filename), and writes it to the filesystem. +#### Related or sub objects -### Fetch file +If you don't want mapped related- or sub- objects to duplicate and do want sub objects to have their own contracts you need to let the synchronization know. +You can configure this under the source configuration of a synchronization. + +![Sub objects](sub-objects.png) + +The synchronization know knows we need to find and update a existing contract for zaaktype and the zaaktype its own sub object statustype. The contract can find the related object and update that object instead of duplicating objects each time the synchronization has ran. + +Also make sure you first map a `originId` in the mapping of a sub object so the code can find this object and update it instead of duplicate it. + +![Origin id](origin-id.png) + +### Target Configuration +- Defines where to send synchronized data +- Specifies how to map target data to source format (outgoing data) +- Handles create/update/delete operations +- Target type can be Register/Schema or other supported types +- Target ID and schema selection for Register/Schema targets +- Target source mapping for data transformations +- `idInRequestBody` is a config option u can use when the id of the object in the target is not managed by itself but needs to be given with the request. Set the id to the required key in a mapping and configure `idInRequestBody` to that key so we set the targetId of the contract to that id. + +### Synchronization Contracts +- Tracks the sync state for each individual object +- Stores origin ID and target ID mappings +- Maintains hashes to detect changes +- Logs synchronization events and errors + +## Process Flow +1. Fetch all objects from source system with pagination +2. Determining if an object has changed +3. Create/update synchronization contracts for each object +4. Transform data according to mapping rules +5. Write objects to target system (POST/PUT/DELETE) +6. Update contract status and hashes +7. Handle any follow-up synchronizations + +### Determining if an object has changed +The synchronization service prevents unnecessary updates and code execution by checking the following conditions: + +1. The origin hash matches the stored hash in the synchronization contract (indicating the source object hasn't changed) +2. The synchronization configuration hasn't been updated since the last check (synchronization.updated < contract.sourceLastChecked) +3. If a source target mapping exists, verify it hasn't been updated since the last check (mapping.updated < contract.sourceLastChecked) +4. The target ID and hash exist in the synchronization contract (object hasn't been removed from target system) +5. The force parameter is false (if true, the update will proceed regardless of other conditions) + +If all these conditions are met, the synchronization service will skip updating the object since no changes are needed. This optimization prevents unnecessary API calls and processing. + +This procces might be overridden by the user by setting the force option to true. In that case the synchronization service will update the target object regardless of whether it has changed or not. + +## Error Handling +- Rate limiting detection and backoff +- Logging of failed operations +- Contract state tracking for retry attempts + +The synchronization system provides a robust way to keep data in sync across different systems while maintaining state and handling errors gracefully. + +## Form Configuration +The synchronization form allows configuring: + +- Name: Descriptive name for the synchronization +- Description: Optional details about the synchronization +- Conditions: JSON Logic conditions for filtering objects +- Source Configuration: + - Source Type: API or other supported types + - Source ID: Selection of configured source + - Source hash mapping: Hash configuration + - Source target mapping: Data mapping rules + - Position of ID in source object (optional) + - Position of results in source object (optional) + - Custom endpoint for data fetching (optional) +- Target Configuration: + - Target Type: Register/Schema or other types + - Target ID: Selection of target system + - Register and Schema selection for Register/Schema targets + - Target source mapping: Data transformation rules +- Test sync option to validate configuration + +![alt text](image.png) + +## XML + +All XML files are first parsed to a json object before being processed. During this parings all element atributes are added to the `@attributes` property of the element Resulting in a clean json object. Lets take a look at the XML example, and the json result that it will produce. + + + + +```xml + + + GEMMA + De GEMeentelijk Model Architectuur (GEMMA) bevat een blauwdruk van de gemeente en haar informatievoorziening. De GEMMA kan worden gebruikt als basis voor de projectmodellen + + + Publiceren en gebruiken van informatie over datadiensten + Dienstenafnemers moeten in online catalogi kunnen opvragen welke diensten, met welke kenmerken, door dienstenaanbieder worden aangeboden. +Onder andere ontwikkelaars hebben baat bij informatie over beschikbare diensten en de vereisten voor het gebruik van de dienst (bijv. specificatie van een dienst conform de OAS-standaard). + + + Thema-architectuur Common Ground + + + a10869bf-a895-4a66-8f81-a4f96c58cc3e + + + + + Bieden van 'regie op gegevens' aan burgers en bedrijven + Burgers en bedrijven moet zeggenschap worden geboden over hun persoonlijke gegevens. Ze moeten o.a. in staat worden gesteld om te weten welke gegevens de overheid over hen vastlegt en gebruikt, persoonlijke gegevens (digitaal) te delen, eenmalig te verstrekken, de eigen gegevens in te zien, te controleren en waar nodig te (laten) corrigeren,in te zien welke gegevens worden en zijn uitgewisseld. + + + Thema-architectuur Common Ground + + + 6868ab87-f5db-40b7-aba6-17b1ba477d54 + + + + + +``` + +```json +{ + "@attributes": { + "identifier": "id-d47463497ef83403bedb663bff370727" + }, + "name": "GEMMA", + "documentation": "De GEMeentelijk Model Architectuur (GEMMA) bevat een blauwdruk van de gemeente en haar informatievoorziening. De GEMMA kan worden gebruikt als basis voor de projectmodellen", + "elements": { + "element": [ + { + "@attributes": { + "identifier": "id-009fa62f25844aa3a87d252bf2b6bb0c", + "xsi:type": "Capability" + }, + "name": "Publiceren en gebruiken van informatie over datadiensten", + "documentation": "Dienstenafnemers moeten in online catalogi kunnen opvragen welke diensten, met welke kenmerken, door dienstenaanbieder worden aangeboden. \nOnder andere ontwikkelaars hebben baat bij informatie over beschikbare diensten en de vereisten voor het gebruik van de dienst (bijv. specificatie van een dienst conform de OAS-standaard).", + "properties": { + "property": [ + { + "@attributes": { + "propertyDefinitionRef": "propid-1" + }, + "value": "Thema-architectuur Common Ground" + }, + { + "@attributes": { + "propertyDefinitionRef": "propid-2" + }, + "value": "a10869bf-a895-4a66-8f81-a4f96c58cc3e" + } + ] + } + }, + { + "@attributes": { + "identifier": "id-096b6619f4cd4164a0572b04ef082c0f", + "xsi:type": "Capability" + }, + "name": "Bieden van 'regie op gegevens' aan burgers en bedrijven", + "documentation": "Burgers en bedrijven moet zeggenschap worden geboden over hun persoonlijke gegevens. Ze moeten o.a. in staat worden gesteld om te weten welke gegevens de overheid over hen vastlegt en gebruikt, persoonlijke gegevens (digitaal) te delen, eenmalig te verstrekken, de eigen gegevens in te zien, te controleren en waar nodig te (laten) corrigeren,in te zien welke gegevens worden en zijn uitgewisseld.", + "properties": { + "property": [ + { + "@attributes": { + "propertyDefinitionRef": "propid-1" + }, + "value": "Thema-architectuur Common Ground" + }, + { + "@attributes": { + "propertyDefinitionRef": "propid-2" + }, + "value": "6868ab87-f5db-40b7-aba6-17b1ba477d54" + } + ] + } + } + ] + } +} +``` + +So in this case we have a json object with an @attributes property. This property is used to store the identifier of the element. The name property is used to store the name of the element. And the properties property is used to store the properties of the element. + +That means that we can configure the psoition of the `id` in the source mapping as `@attributes.identifier` and the position of result within the source object as `elements.element`. + +Oke that setsup geting the data from the source, but how does the data get to the destination? Wel ugly is the answer, we have to map the data to the destination object. Right now the above code would result in the following object: + +```json +{ + "@attributes": { + "identifier": "id-8576c90ac5104cf8ade8acadf2764432" + }, + "name": "IngeschrevenPersoon", + "documentation": "Een INGEZETENE of NIET-INGEZETENE", + "properties": { + "property": [ + { + "@attributes": { + "propertyDefinitionRef": "propid-6" + }, + "value": "{AF9534B0-7CBD-4061-9276-A7B6698B383A}" + }, + { + "@attributes": { + "propertyDefinitionRef": "propid-8" + }, + "value": "Class" + }, + { + "@attributes": { + "propertyDefinitionRef": "propid-7" + }, + "value": "10122024-112046" + }, + { + "@attributes": { + "propertyDefinitionRef": "propid-9" + }, + "value": "\"ggm-\" properties worden beheerd in het GGM informatiemodel" + }, + { + "@attributes": { + "propertyDefinitionRef": "propid-15" + }, + "value": "Ingezetene, Client, Leerling, Ouder Of Verzorger" + }, + { + "@attributes": { + "propertyDefinitionRef": "propid-2" + }, + "value": "e1371e07-969e-4a34-be81-0a23ffd541dd" + }, + { + "@attributes": { + "propertyDefinitionRef": "propid-10" + }, + "value": "https://gemmaonline.nl/index.php/GEMMA/id-e1371e07-969e-4a34-be81-0a23ffd541dd" + } + ] + }, + "id": "faea7cdb-fe32-42fb-bbd4-98c3744ef333" +} +``` + + +So lets create a mapping for this, like: + +```json +{ + "identifier": "@attributes.identifier", + "name": "name", + "documentation": "documentation", + "properties": "{ {% for property in properties.property %}\"{{property['@attributes']['propertyDefinitionRef']}}\":\"{{property['value']}}\"{% if not loop.last %},{% endif %}{% endfor %} }" +} +``` + +Lets not forget that this will output the properties like + +```json +{ + "properties": "{ \"propid-6\":\"{AF9534B0-7CBD-4061-9276-A7B6698B383A}\",\"propid-8\":\"Class\",\"propid-7\":\"10122024-112046\",\"propid-9\":\""ggm-" properties worden beheerd in het GGM informatiemodel\",\"propid-15\":\"Ingezetene, Client, Leerling, Ouder Of Verzorger\",\"propid-2\":\"e1371e07-969e-4a34-be81-0a23ffd541dd\",\"propid-10\":\"https://gemmaonline.nl/index.php/GEMMA/id-e1371e07-969e-4a34-be81-0a23ffd541dd\" }" +} +``` + +So in the mapping we must add a cast to turn that into a json object. + +```json +{ + "properties": "jsonToArray" +} +``` + +And that will result in the following object: + +```json +{ + "properties": { + "propid-6": "{AF9534B0-7CBD-4061-9276-A7B6698B383A}", +But we want to map the data to the following object: + +```json +{ + "identifier": "id-8576c90ac5104cf8ade8acadf2764432", + "name": "IngeschrevenPersoon", + "documentation": "Een INGEZETENE of NIET-INGEZETENE", + "properties": { + "propid-6": "{AF9534B0-7CBD-4061-9276-A7B6698B383A}", + "propid-8": "Class", + "propid-7": "10122024-112046", + "propid-9": "\"ggm-\" properties worden beheerd in het GGM informatiemodel", + "propid-15": "Ingezetene, Client, Leerling, Ouder Of Verzorger", + "propid-2": "e1371e07-969e-4a34-be81-0a23ffd541dd", + "propid-10": "https://gemmaonline.nl/index.php/GEMMA/id-e1371e07-969e-4a34-be81-0a23ffd541dd" + } +} +``` + +Now of course propeties like `propid-6` are not very readable, so we can rename them to something more readable later using a translation table. -This action should be run on timing `after` (when the object has been stored). -The action takes the following parameters in the `configuration` property: -- `source` (required): The id of the source where the file can be downloaded -- `filePath` (required): The dot path of the location in the input object that contains the file url or file path. -- `method` (optional): The HTTP method that should be used to fetch the file. Defaults to GET -- `sourceConfiguration` (optional): Additional configuration for the source that only holds for fetching files. -When properly configured this action will download the file from the given source and substitute the base64 encoded content in the returned object. -It is preferred to run this action in combination with `write_file` immediately after, so the file contents are properly stored in the Nextcloud file system instead of written to a database. -### Write file -This action should be run on timing `after`, and if combined with `fetch_file` it should be run in order after `fetch_file`. -The action takes the followin parameters in the `configuration` property: -- `filePath` (required): The dot path of the location in the input object that contains the base64 encoded content of the file. -- `fileNamePath` (required): The dot path of the location in the input object that contains the filename -This will write the file to the nextcloud filesystem in the folder that belongs to the written object, and substitutes the file content in the returned data with the path of the object in the Nextcloud File System. diff --git a/website/docs/tutorial/endpoint-example.png b/docs/tutorial/endpoint-example.png similarity index 100% rename from website/docs/tutorial/endpoint-example.png rename to docs/tutorial/endpoint-example.png diff --git a/website/docs/tutorial/endpoints.md b/docs/tutorial/endpoints.md similarity index 100% rename from website/docs/tutorial/endpoints.md rename to docs/tutorial/endpoints.md diff --git a/website/docs/tutorial/example-consumer.png b/docs/tutorial/example-consumer.png similarity index 100% rename from website/docs/tutorial/example-consumer.png rename to docs/tutorial/example-consumer.png diff --git a/website/docs/tutorial/installation.md b/docs/tutorial/installation.md similarity index 100% rename from website/docs/tutorial/installation.md rename to docs/tutorial/installation.md diff --git a/website/docs/tutorial/jobs.md b/docs/tutorial/jobs.md similarity index 100% rename from website/docs/tutorial/jobs.md rename to docs/tutorial/jobs.md diff --git a/website/docs/tutorial/mappings.md b/docs/tutorial/mappings.md similarity index 100% rename from website/docs/tutorial/mappings.md rename to docs/tutorial/mappings.md diff --git a/website/docs/tutorial/postman-example.png b/docs/tutorial/postman-example.png similarity index 100% rename from website/docs/tutorial/postman-example.png rename to docs/tutorial/postman-example.png diff --git a/website/docs/tutorial/sources.md b/docs/tutorial/sources.md similarity index 100% rename from website/docs/tutorial/sources.md rename to docs/tutorial/sources.md diff --git a/website/docs/user-api-quick-reference.md b/docs/user-api-quick-reference.md similarity index 100% rename from website/docs/user-api-quick-reference.md rename to docs/user-api-quick-reference.md diff --git a/website/docs/user-api-setup.md b/docs/user-api-setup.md similarity index 100% rename from website/docs/user-api-setup.md rename to docs/user-api-setup.md diff --git a/website/docs/user-api.md b/docs/user-api.md similarity index 100% rename from website/docs/user-api.md rename to docs/user-api.md diff --git a/website/.gitignore b/docusaurus/.gitignore similarity index 100% rename from website/.gitignore rename to docusaurus/.gitignore diff --git a/website/README.md b/docusaurus/README.md similarity index 100% rename from website/README.md rename to docusaurus/README.md diff --git a/website/blog/2019-05-28-first-blog-post.md b/docusaurus/blog/2019-05-28-first-blog-post.md similarity index 100% rename from website/blog/2019-05-28-first-blog-post.md rename to docusaurus/blog/2019-05-28-first-blog-post.md diff --git a/website/blog/2019-05-29-long-blog-post.md b/docusaurus/blog/2019-05-29-long-blog-post.md similarity index 100% rename from website/blog/2019-05-29-long-blog-post.md rename to docusaurus/blog/2019-05-29-long-blog-post.md diff --git a/website/blog/2021-08-01-mdx-blog-post.mdx b/docusaurus/blog/2021-08-01-mdx-blog-post.mdx similarity index 100% rename from website/blog/2021-08-01-mdx-blog-post.mdx rename to docusaurus/blog/2021-08-01-mdx-blog-post.mdx diff --git a/website/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg b/docusaurus/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg similarity index 100% rename from website/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg rename to docusaurus/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg diff --git a/website/blog/2021-08-26-welcome/index.md b/docusaurus/blog/2021-08-26-welcome/index.md similarity index 100% rename from website/blog/2021-08-26-welcome/index.md rename to docusaurus/blog/2021-08-26-welcome/index.md diff --git a/website/blog/authors.yml b/docusaurus/blog/authors.yml similarity index 100% rename from website/blog/authors.yml rename to docusaurus/blog/authors.yml diff --git a/website/blog/tags.yml b/docusaurus/blog/tags.yml similarity index 100% rename from website/blog/tags.yml rename to docusaurus/blog/tags.yml diff --git a/docusaurus/docusaurus.config.js b/docusaurus/docusaurus.config.js new file mode 100644 index 000000000..ffef9a129 --- /dev/null +++ b/docusaurus/docusaurus.config.js @@ -0,0 +1,103 @@ +// @ts-check + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: 'OpenConnector', + tagline: 'API gateway and integration hub for Nextcloud', + url: 'https://conductionnl.github.io', + baseUrl: '/openconnector/', + + // GitHub pages deployment config + organizationName: 'ConductionNL', + projectName: 'openconnector', + trailingSlash: false, + + onBrokenLinks: 'warn', + onBrokenMarkdownLinks: 'warn', + + i18n: { + defaultLocale: 'en', + locales: ['en'], + }, + + presets: [ + [ + 'classic', + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ + docs: { + path: '../docs', + sidebarPath: require.resolve('./sidebars.js'), + editUrl: + 'https://github.com/ConductionNL/openconnector/tree/main/docusaurus/', + }, + blog: false, + theme: { + customCss: require.resolve('./src/css/custom.css'), + }, + }), + ], + ], + + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + ({ + navbar: { + title: 'OpenConnector', + logo: { + alt: 'OpenConnector Logo', + src: 'img/logo.svg', + }, + items: [ + { + type: 'docSidebar', + sidebarId: 'tutorialSidebar', + position: 'left', + label: 'Documentation', + }, + { + href: 'https://github.com/ConductionNL/openconnector', + label: 'GitHub', + position: 'right', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Docs', + items: [ + { + label: 'Documentation', + to: '/docs/FEATURES', + }, + ], + }, + { + title: 'Community', + items: [ + { + label: 'GitHub', + href: 'https://github.com/ConductionNL/openconnector', + }, + ], + }, + ], + copyright: `Copyright © ${new Date().getFullYear()} for Open Webconcept by Conduction B.V.`, + }, + prism: { + theme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/dracula'), + }, + mermaid: { + theme: { light: 'default', dark: 'dark' }, + }, + }), + markdown: { + mermaid: true, + }, + themes: ['@docusaurus/theme-mermaid'], +}; + +module.exports = config; diff --git a/website/package-lock.json b/docusaurus/package-lock.json similarity index 100% rename from website/package-lock.json rename to docusaurus/package-lock.json diff --git a/website/package.json b/docusaurus/package.json similarity index 51% rename from website/package.json rename to docusaurus/package.json index 95d2dd02e..93b107e46 100644 --- a/website/package.json +++ b/docusaurus/package.json @@ -1,5 +1,5 @@ { - "name": "website", + "name": "openconnector-docs", "version": "0.0.0", "private": true, "scripts": { @@ -11,20 +11,21 @@ "clear": "docusaurus clear", "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", - "write-heading-ids": "docusaurus write-heading-ids" + "write-heading-ids": "docusaurus write-heading-ids", + "ci": "npm ci --legacy-peer-deps && npm run build" }, "dependencies": { - "@docusaurus/core": "3.7.0", - "@docusaurus/preset-classic": "3.7.0", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "prism-react-renderer": "^1.2.1", - "react": "^19.0.0", - "react-dom": "^19.0.0" + "@docusaurus/core": "^3.7.0", + "@docusaurus/preset-classic": "^3.7.0", + "@docusaurus/theme-mermaid": "^3.7.0", + "@mdx-js/react": "^3.1.0", + "clsx": "^1.2.1", + "prism-react-renderer": "^1.3.5", + "react": "^18.3.1", + "react-dom": "^18.3.1" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.7.0", - "@docusaurus/types": "3.7.0" + "@docusaurus/module-type-aliases": "^3.7.0" }, "browserslist": { "production": [ @@ -33,9 +34,9 @@ "not op_mini all" ], "development": [ - "last 3 chrome version", - "last 3 firefox version", - "last 5 safari version" + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" ] }, "engines": { diff --git a/website/sidebars.js b/docusaurus/sidebars.js similarity index 100% rename from website/sidebars.js rename to docusaurus/sidebars.js diff --git a/docusaurus/src/components/HomepageFeatures/index.js b/docusaurus/src/components/HomepageFeatures/index.js new file mode 100644 index 000000000..38da61abf --- /dev/null +++ b/docusaurus/src/components/HomepageFeatures/index.js @@ -0,0 +1,55 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; + +const FeatureList = [ + { + title: 'API Gateway & Mapping', + description: ( + <> + Connect to any external API — REST, SOAP, or XML. Transform and map data between formats using Twig templates and flexible mapping rules. + + ), + }, + { + title: 'Data Synchronization', + description: ( + <> + Keep data in sync between Nextcloud and external systems. Scheduled jobs, webhooks, and event-driven processing with full audit logging. + + ), + }, + { + title: 'Enterprise Service Bus', + description: ( + <> + Route, transform, and orchestrate API calls across your organization. OAuth, JWT, and ZGW authentication built in. + + ), + }, +]; + +function Feature({title, description}) { + return ( +
+
+

{title}

+

{description}

+
+
+ ); +} + +export default function HomepageFeatures() { + return ( +
+
+
+ {FeatureList.map((props, idx) => ( + + ))} +
+
+
+ ); +} diff --git a/website/src/components/HomepageFeatures/styles.module.css b/docusaurus/src/components/HomepageFeatures/styles.module.css similarity index 100% rename from website/src/components/HomepageFeatures/styles.module.css rename to docusaurus/src/components/HomepageFeatures/styles.module.css diff --git a/docusaurus/src/css/custom.css b/docusaurus/src/css/custom.css new file mode 100644 index 000000000..74c97bee0 --- /dev/null +++ b/docusaurus/src/css/custom.css @@ -0,0 +1,121 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-first websites. + */ + +/* Import Poppins font from Google Fonts */ +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); + +/* You can override the default Infima variables here. */ +:root { + /* Primary color: Open Webconcept green */ + --ifm-color-primary: #2fb298; + --ifm-color-primary-dark: #28a088; + --ifm-color-primary-darker: #248e79; + --ifm-color-primary-darkest: #1d7563; + --ifm-color-primary-light: #34c4a7; + --ifm-color-primary-lighter: #3dd1b3; + --ifm-color-primary-lightest: #5ad9c1; + + /* Typography settings */ + --ifm-font-family-base: 'Poppins', system-ui, -apple-system, sans-serif; + --ifm-heading-font-family: 'Poppins', system-ui, -apple-system, sans-serif; + --ifm-font-weight-semibold: 600; + --ifm-heading-font-weight: 600; + --ifm-h1-font-size: 2.5rem; + --ifm-h2-font-size: 2rem; + --ifm-h3-font-size: 1.5rem; + --ifm-h4-font-size: 1.25rem; + + /* Code settings */ + --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); +} + +/* Dark mode color palette */ +[data-theme='dark'] { + /* Primary colors */ + --ifm-color-primary: #34c4a7; + --ifm-color-primary-dark: #2fb298; + --ifm-color-primary-darker: #2ba68d; + --ifm-color-primary-darkest: #248e79; + --ifm-color-primary-light: #47cbb1; + --ifm-color-primary-lighter: #54cfb7; + --ifm-color-primary-lightest: #71d8c4; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); + + /* Background colors */ + --ifm-background-color: #1e1e1e; + --ifm-background-surface-color: #242526; + + /* Text colors */ + --ifm-font-color-base: #e5e5e5; + --ifm-heading-color: #ffffff; + --ifm-color-content: #e5e5e5; + --ifm-color-content-secondary: #b0b0b0; + + /* Navbar */ + --ifm-navbar-background-color: #242526; + --ifm-navbar-link-color: #e5e5e5; + --ifm-navbar-link-hover-color: #34c4a7; + + /* Sidebar */ + --ifm-sidebar-background-color: #1e1e1e; + --ifm-menu-color: #e5e5e5; + --ifm-menu-color-active: #34c4a7; + + /* Code blocks */ + --ifm-code-background: rgba(0, 0, 0, 0.3); + --ifm-code-color: #e5e5e5; + + /* Tables */ + --ifm-table-border-color: #3a3a3a; + --ifm-table-stripe-background: rgba(255, 255, 255, 0.05); + + /* Cards */ + --ifm-card-background-color: #242526; + --ifm-card-border-color: #3a3a3a; + + /* Performance optimizations */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Typography adjustments */ +.markdown { + font-weight: 400; + line-height: 1.8; +} + +.markdown h1, .markdown h2, .markdown h3, .markdown h4 { + margin-top: 2rem; + margin-bottom: 1rem; + font-weight: 600; +} + +/* Navbar adjustments */ +.navbar { + font-weight: 500; +} + +/* Sidebar adjustments */ +.menu { + font-weight: 400; +} + +/* Smooth transitions for theme switching */ +html { + transition: background-color 0.2s ease, color 0.2s ease; +} + +/* Reduce motion for accessibility */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/website/src/pages/index.js b/docusaurus/src/pages/index.js similarity index 73% rename from website/src/pages/index.js rename to docusaurus/src/pages/index.js index a8c61f2ba..fef7ead05 100644 --- a/website/src/pages/index.js +++ b/docusaurus/src/pages/index.js @@ -1,10 +1,10 @@ +import React from 'react'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import Layout from '@theme/Layout'; import HomepageFeatures from '@site/src/components/HomepageFeatures'; -import Heading from '@theme/Heading'; import styles from './index.module.css'; function HomepageHeader() { @@ -12,15 +12,13 @@ function HomepageHeader() { return (
- - {siteConfig.title} - +

{siteConfig.title}

{siteConfig.tagline}

- Docusaurus Tutorial - 5min ⏱️ + to="/docs/FEATURES"> + Documentation
@@ -32,8 +30,8 @@ export default function Home() { const {siteConfig} = useDocusaurusContext(); return ( + title={`${siteConfig.title}`} + description="API gateway and integration hub for Nextcloud">
diff --git a/website/src/pages/index.module.css b/docusaurus/src/pages/index.module.css similarity index 100% rename from website/src/pages/index.module.css rename to docusaurus/src/pages/index.module.css diff --git a/website/src/pages/markdown-page.md b/docusaurus/src/pages/markdown-page.md similarity index 100% rename from website/src/pages/markdown-page.md rename to docusaurus/src/pages/markdown-page.md diff --git a/website/static/.nojekyll b/docusaurus/static/.nojekyll similarity index 100% rename from website/static/.nojekyll rename to docusaurus/static/.nojekyll diff --git a/website/static/img/docusaurus-social-card.jpg b/docusaurus/static/img/docusaurus-social-card.jpg similarity index 100% rename from website/static/img/docusaurus-social-card.jpg rename to docusaurus/static/img/docusaurus-social-card.jpg diff --git a/website/static/img/docusaurus.png b/docusaurus/static/img/docusaurus.png similarity index 100% rename from website/static/img/docusaurus.png rename to docusaurus/static/img/docusaurus.png diff --git a/website/static/img/favicon.ico b/docusaurus/static/img/favicon.ico similarity index 100% rename from website/static/img/favicon.ico rename to docusaurus/static/img/favicon.ico diff --git a/docusaurus/static/img/logo.svg b/docusaurus/static/img/logo.svg new file mode 100644 index 000000000..ff1e1c167 --- /dev/null +++ b/docusaurus/static/img/logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/website/static/img/undraw_docusaurus_mountain.svg b/docusaurus/static/img/undraw_docusaurus_mountain.svg similarity index 100% rename from website/static/img/undraw_docusaurus_mountain.svg rename to docusaurus/static/img/undraw_docusaurus_mountain.svg diff --git a/website/static/img/undraw_docusaurus_react.svg b/docusaurus/static/img/undraw_docusaurus_react.svg similarity index 100% rename from website/static/img/undraw_docusaurus_react.svg rename to docusaurus/static/img/undraw_docusaurus_react.svg diff --git a/website/static/img/undraw_docusaurus_tree.svg b/docusaurus/static/img/undraw_docusaurus_tree.svg similarity index 100% rename from website/static/img/undraw_docusaurus_tree.svg rename to docusaurus/static/img/undraw_docusaurus_tree.svg diff --git a/img/app-store.svg b/img/app-store.svg new file mode 100644 index 000000000..ff1e1c167 --- /dev/null +++ b/img/app-store.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/img/screenshot-dashboard.png b/img/screenshot-dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..410997704753be11645c1bf96bf5f97f698d19e2 GIT binary patch literal 64732 zcmb@u1yEc~w>C-=NM05g2Lc3l2G<0a0D-|hBtUR?cLtjTcLD?*+9!^cdxac=UF>gMM)YThYSY;0|Q@H=A9Y_1{P3! z@ZyjAz`xUL?NSVkKQUzAiK)A#?JZzw6X}sZI;xZpa*_?DVSGu${4iu;;q$_R!@eC% zd-}a=?es#(H~$bC5=yyEWV;(u??ap9JqIM(b7RJ0qd$2jc|m~dG=uA9YI1V#Nx{_n z`z*5UY^)YQQ@=l04Di1T7AgL&Bh^u|w|kdks>^_ZKRdpZ-`;&oM2RFr96 zSyg4K_VAydyj8sa-~qL;TUKf5-rinmVPUsi4DK_wFjB6-`%Bd}QzYl)4yPP6DW)i^6_dnWTW5x>LmX~=fs5bY?YO{xp^t(<s5OB6PWTZq3X5OFl(#Pi-KRqq2FOI1r??I**8~d%1&GYFd0#qDr zGa?F`www6933C*A?ZH@LS0}@p;P0Ifm`S*~f_2sYxbI{WF`nG_qi-@d#o@g6bEmi_ zkUFpP1ZTRbN0DCIy>D;A+h_~oDxH}O3i>8?U8fOH+s%7)n+#YV_8aghxV<=8vLvLH zly5EC4*Rp$SgacFGg`NPPh}o%%g1E4f@h|s30sZ6qNB?nD^ylaKmWU#-=9tlGt=4AlDw8qUsXzTxEjGa2kwdZbRXy<@Fas$!@m9ZEby ze>UC?v$Ej>!*`rE#K*oJgr6DcWaoE zO>%iBK=kB5rkhfX@!e2s);fI?OV55JwUL_ISfX3Hy?}L@&M8z0MDp11`;CMpH;dG1 zZJNWDp#4DscrWt0vJrYq0Fr#k{o0>*TL?*2*{*U6&uq0A$_V)S6^}}ozk6bk2z#nT zE0Qc|b!BCm`swrMZfiZ!O#Udb~{rQc>?lM9%7u$Izm`NA) zRF>g>(x0tmRPeBH`N-!&>Gv^?3-8Y8xL=Bi>n@zq(9%LqzB5b>q;rVCKH4L&3iCHz zbHDlf`#0z^X&UR-IW;T7yEfSk;S9>^rcQ2duclzdIZbeg^C|tgvnt zV+#mh>nce~cE-z_Kc=2%5fGrGd)KX~u#TrF5#GWOD|ds3rJk2^)!_9a~l zE>6M2Wox#wBAQQ^N!ve+T2)s%;e07oJ65Ku6V*lwCyb_38h4KA>*Ui!ohK!G?;AfZbBFBQ8Jg}>wEBdm3VUaVn>;8!&My#; zhRfpCbV3fdA&FM;&~5ECBdNZ*iF}vm&ixkZtauNy?iM5>B(OoU^dQM{cvL zr3*`v&TQO-Azk$lrfbCc0P`43wbAE>LQkDz>DY~}y4WFIib=7Q0LB=4}L&O}}Ze2JFjx4s6p2pMsPNA0R`y~FsL{Tb zF534`_&h1^=59%}UMTFuCa$*>6^(%5=Tb&&Mm*T%+h3Xtb#d~4Di;fOa@|{VXXe*y zzF0SO{eYV=tfSh@N+oYaEtF+ntGJt>GMwT0EP9neQCXRq%R#AQV?m)$V|Up0NFL2s z@B4YaM7DgPNnXC(X65av)to!Ve&d1YS$2MYXKp?vpL==MN0M}$$#wd0@*S1lK%X3n z8%T71px$12bKsabugz-L-C(`1#;gD?>>C0;B@pt8#n7Wc&~9PQb$fWH=eSZ~>w?-& zOX@~qHh#ofjV6Hbb>v;fcM3>PGZzsBzLm`yn-23Jxqnf@cTN1lnX#^iSiW9K-mgjO z*T>2`pqjYlok^&I!Xd*y5O3nq$8<1>FGuvhaN>~*iQ}q6gI9Ouw#i2;M|%(CA8FLG zN<7)l(+Da->gv~=pDD%iUNzr|yGy-ZESY-W?$2r=s64A)u+DAG?Q?NWn=j;I^mcnL z0%6DP6fap~b35nUPYsOhR#WsRg0Z$7>vfY?Cw5Kl3_YJSDJ%hNUWPY#4n*UD`3f z;`SG+`z|WC_Bd1X3NeuieV(E>k>#A%_9J%V(+x}FLrTnN*fYCg?2 z)#K4jr4ZvYm%SEB>Ia1kx@>WYwDb^Qx@VIt-E!R?+k|BbyT{~8C-o%hsL7&0Kh>!k zT7uG8Xk zd<#8*Z@xBcQBs-lT^SFM!ru{Y@D7|cZ9tOB5(mfe`;P34S+K+Oc+j%}5>J*@?;a|@ z*U^$%zh$#~EGyef+dZ&1-qLy2OZ$w~Kwf&Ld2Y%MaeD|xJS**aCncFUIRz=#Ot!&n zaoF|wa$T*V9>8?+qrKI}GQ0UT1qPPpr6T9n@3bX^wB3c|j zcEzd6YuRNj6h$7wbxt+=GkK1$R^^*LoU*zY?TVE%C6qFbMJjx`D4hc%Zi9S$HGd5n z)`@#c;mksP585?`?(P@a`J7(UF(?@w%{sq9I*PPF5*`OtS&y%bRO&T0p*SmSyHS!5 z2C$DH;cS&NAH0R7V9JjpB7UC=A+oAg(vZcwwsj1#ne-e?=UG0Px$EtPnlKD5k8X1x zHz6nP8YeSBH6|0YtewMNaf$P(CCShyclU|QSfTNO9z{G7YxHHB#YCMy-R~AU_x7$> z&ZKvDx5vwM&gTP4^jG{lZ6eb75semw1G|YkJJ$Bv&6ab4>(=?|R{53&m}528i{FaB zVj>+zx)4=AO5VTb!NgWRKORe@@|Lc)9gC9#d7E?}NYrupC`BlKBaS&)e_h0EaXJYS zkQ)ddTO2cICFdL-KrVO6xe#xUr|rxhBKQS7+(fmWS?C7VvfE*^E64Y_5Xp^vRVR@n z-?|Y&TFj)<;ExvQ{Sd#xHVGQ`n1E@OpreATWFd%?e&l)s{+xv}wO(_vgvc%xDXI7A z#TS>d)D7*+`NnS)qD;i2vpnf21K!=Pby`QqijHojA&y7UC0BH>N~I5@tDX;0=_heG zJ1OkdvhF6@O*erxr7lnd;KpW67AEF`KUfV_b5Bh!R#Ez<(Cm~HHc*gNQOO9etX_eC zQ&SIXNMGDh$n&l3HO81d0@)#%p=fb=Pb6x!J81v*N376WP&5^|JFwLmy&_l#~Ri z@GhGRgD3qV?X&jSvJTk2h4$A}^|xzaZG|al>fv?E{_HPVMGdRvn#xe&p^>ykg++zz zlNqWaY74IMJG!kixv%#s)+Zt3C;a{iumrHR|b_(x#H;B}G4o-FzYqfF& z2g?d3HN|@fFS71%GP!O6?Jr!InR1| zb!u@T&1%r#)StvbD;=R<^HJyEhd#c4TvSNVd(thl0Eqn;e+`YeMi*nLE^5XxX{BjWE}c1N;Gcp6bp&?oCq@|!`_7div=**po@x&#allVUa(qC)X8p4JP`G&P%NQ$wh6FEHjz5Z6mafW_U&2`E9?p1 zX;e|}xMrK*=48D%@Rsh5pyGGRBCk$SN)_gW83>dSm62&}OX$&SxU4}Orm}N&stvq7 zTylIfR}}huHw~k;3@xpT4^MOz8=qPC6)?LO&n%B3Dfvel%xH^MbMUy(q!JoS(h-?( z3z2x1nNMQuJQ~%u6a1GQ41zlDq(^~6n;KPY(i~hG)So=L1aS+Cl2SvAd1r5`t!6G) z;`OddZ@PzGo>{$*I-LlucYRX-tkk5D1PBf`3c4g7)vLm>wPG1xIP%;XVw&IcKzE1LPgt>21XShJ_dUv%BC77Q3)LG2tHjkC|pAp7_LCRjD_liE+p1hwDUG?P6 z@dMy@ACW|Uu?G39Rc2hnHa zwNZ;aJJp?=o6~}-nZXlS4-u`;lv=&6Dr#$8SG&T|OSqqXXZ`yo(ntuR3*Ry^F`-Z> zm{Ds175zdU4TjQyNWW=kgKRHuVtLuv_b=vFb95||V+Ub4w`a2o=gu? zBoB4_;HqoQ9BCF$XFWwJU>7J+O;4m~(CW_4!&B`s(MNQT*LHsraIs@8*l_5x1;ufm zK&6$~cBSPNS&CC<7%zq0r%#77@T*;iqLF1uTvzfEh#$;9 znyPe;u))&U4O?*Bh#8B-vQ{MKp0ap2<#-L;43FIDRaJ9)o>TAsaA$xEN+Wa2BH&qB zFO16dNp*L<%*B^6Csv1(NxcCGC2NOV5MCISq#I~ulgE2x*DMtWRRK@^`pm1|#w*Lf z;WXRr6GKGD#8+j3Padmg@hWTB{D|ubVWx^-X zt=10ZGN`(1&d`wRBAtrQHv2kbT^ob6Hwe^l`+OLgyiUsvDB5ukFZ9$hi98)+*tn7R zfavK{m*q1%Vly+qcu$ZvyOW?C_NXAU)Lm+&ea)C`Qm+HNA{FP=pW~ZLp1wEO(8OlcDj{TeHfW%Bf2lrw|{ysyt*N>pTUs=y3E(1r*tul2t8)02~v<70uuaE}_> zIZbiH)LUtpPEWs9uin{?gvvLW0`>wLjci8z=}PH*02EhU5=Jpws8={?F=(Vy?{WGg zc2k-9{3)bHeTj>Uvre#*Vswnle84FBrjb7+B;<^#Bk@CLKmb9j{zj@8 zD{m`KtxnGuTCbq03aqI|$JoK@`vz?d`Ue7D*!b*Pyd`N)J0kC7bw<3`DXcNJ=TC3A zKD&CBWxI^57P8?{3Y@of6hbVN-gn9v7=&wnHsgEPf_wel1o^UfIKK@n-DIg40NH3w zySg>2?>&;#qU!BAg6?y4uNoTOx?;6aw=F#AKL_J}I>6f&70G@DQ`8+sTcGU{ygjKV zG>7X$U#tTTGtqdkXwZh@F1vO`U>{cX1F)}%!1ib%@c28tJD~ZR&c6?`yj;^khaJz}vZr&7GMPu<(P+i4 zg!?8Al7T*G8XG^YrZ|}0P|6%{_VNfAG@7zID%OGRPnDR(iJVb0RMT9 zvkfI2sn#)6o!hgk#aaWHtg=5?Q;S15?`rh%H-=75b*OHd?|WHsP~k&z6AY~%rg3^l zmxWhn*l8-HPWd{m&!%fqUXS{GF%I+=n%umEoFX_jAeKwK+2R&?iRb0BS?r?16SBOX z$q>vx#^01M8u?X<1?)YCdf)Yd^wgidTJ*S%ZKO78t@yj24-z_ zc?+WWyNw|y4LA)s)n=rSSooA(W~*D7PWf(TKp`g|*YUBp*fB3$f@7X5x7;u!rN`1F zw`|vW3As5jLeuPiIyg8fn7N)U=qUYY?uNkIf61{)h2K`n@|&$a(2W5dQjmh&4t4}Ub5olmPifZk)J)A*&K?OwJ4 z0BRfk$%ZQk}8_|Mfa$>jPJ?~hK52s-8cfv9QMs2T}Q|8&L@r3KAKL?swqZVv2f z4K&mEuDQG}Zz-+#QpP_5LznQ)-}ScEBj66)3M>`yfOnic961_>^-lEF!;b7>e_m13 zF%%AG1iA3H!4CIlTBJ-B9YnelS46qFVYc#_Eih4s$o3pYZzLsbJC0+nHfW%L!zN{mpSO0%*#eA z4gtDdg6f`(osrN|d%WOFGKv)EM<050w9^#yYoBfXV4v9b_&6)pC`#LLyXY*% zbB4P2Is3ztQ<8cuh+`J-y^s=5Ehw`TW=oFt{YQH|!flvhPpsw(y? zler*#JA^kQ9o_j^J)CZT0qJgA{`!Uus#i-rs(j43q-f}_t;y3yjcObl8)LGzU8sm& z8kuBG;o(2@j>loIomP*RKnumI%$``~xA=rM0$|X=#Vv1HO6mDg9@KiO=ZY6=8QS1% z7aJAD?>)#VXyJjPc@J=A0yQ|xzo88ih9M-?8<*~bjw-w=filC|GO^}aBG-{g=ao3V z{NWlti?&vETnRnT2rI~I<#oB5#tSNFjCW*1dN}buUglLxsoIPo!wnnQNEDqk4MX?q z7Uz@_CG6f&iyF9Ej|lplw96p}s?w_Z@l#!M-wPqxXu6fV5|hA-gvl{j(`da@d6g-`(k}f9-*PD|r#|lnl zMyrjWmot3ChKrL^Z>kz*r?qAugaQ~Z(XIMSQPzi3&?3_bi}luy&H1|X7fTv1i0826 zCsoGm26i-lU6qSgPo|`#)YQ~W6l>&Ck*TYy&a`-Y0te71b;_9{y}iAq&zs$jSKx{4 zSDV>*>_#o7o#J)kn}7`hoxaU|r$lr7iWj5BX}zE(8bsOOr>84i?-}7)`s3zgCSML3 zSElWGaT_0mw964qu{I?pCJKx|br;9%mU~N2p@brtPfn9K%C-l;l^#J9b}Cld>UI98YBRDR>oOde0;bw@a54N zzso1wveKre`8E(a)6ee}F!{}UK^Jzykb{a8U8t)V^{g|gO0|o;;QLaeYCmH3gTbS~C7oiJ|oO zT6TJWG+(#!a~TY{_BRBXYdb{R7baXJL#YiutB>XN5(o5LH|?@+R^Qo`n~oo~RJELe zhnd6;-M{r@{@`DzyL&;uQL>Z+cw&zhYU?fma(g7xuq1Hoh+|C8bOaS@Ho!*8-fH!zwQk^(Xp;W9Gy() z?KeIq6S7izXR3Z+6hE8wuKe4lXj3{_CXM(VXWhu;H=gg&2X+gymBJUE0P{CFHsNA5 zES9!WP@4Op6ZreGu^{Mb*^Bz>&c!gi?09_QOjGM+El)At`Un6yaGi=4s|*r9w?`7( zZXd5lQjodyAM$-~#tlekM+AI*_shjjJ>PSzxLXU~#-Mg|>hfL4d+0uLxZ-7T9}ZTE ztpH&&_<+`0lNkNQL!lUt9~y4vV!NCjr5 z#gGw`15W4gPL0`pe{p|_96??uebMSiEFQiteDUaJbxG?LQCv%oy7C<7tIUHVf%rY< zex2`qBlS8mmY0LJB@ofAcX=_DlQ=jyhK7dcI}@HqOJZo$osNV1*_LV~70BoMA${L) zv!^RHHMQnhU&0+aU@JH{7zr$N%o%3&qMvyW+0nGIhF?}%8rgk6Bqne%k~LpPW-s|J z&-dbCWPQz+!nQVBCz~wlH3PUf!oub|DkEc+FvH8%^6lBlO%xW+6P@U6msf(ZsOOh{ zDcjn?4(Metyuyr|r?NQai@&Ja`;XeXzGDq^cuS(bH+IDU)i=)HWWL^XUs2=!lprgs z?cVf{GQC>6@uDXzB+?bZ$@Sj6nr9b>2;Yl69S1w+>;%6ARy~)!>1g5@Kr5oggUKQ{ zK6tpZ)8`YfE{uumW4+L|uO8sOIqUEcL*---%6fT?XQPJE0WAa1u7sx39C~0rWoR=i-owD z9Ai}Ny^ycz5JO>MVK?E7quk<9#9vPS-PHrRwDff8u;*=g*leEzOH1m{oShvJ3V-}1 zIKm>Pq*SyW$(JJ@n=6wfq6QFUM=su}ZDZ}u;d6{Gv6l_X}NFNVsu;rstcwf+%c{WQW>%mwse0snWhFO2__ zKI~|Qx_$Ndta^f5o{8(P@y77W(NbXjNpvu)N0y&&p`E@vx1Qr*} zB1x8dV8_UeM`q^cod2{%^V8%@?K+n(vYPq^#A}8x8X(#2C7*{v+1jv@%>XzSpq^+{ zTG&Igy~?Z)_-A~8=j_ii>*;3;Cza~@e&gX9XRlYhA>EFG3 z`-HI_gP)GIxxib;`o{Z=^U~_)BOVU_IqyyC|GI$}9ci;`T)K43X?MlCkIm5Ah|&+| zILE&`X0PEQa#FoP2uY?zG2P}YHxQa)ePjNRoDUOWn ziLpV~TmIA4#5N0i(7){?S z=9zM%@BcMCZ%3HZFs9pO3iZaUjcAPLrd;{m?~+qOi~%f=lr-60vK_$jZ8P7}k3atB zX{+SnY(Cw(%!~W77&))2ZdB7DB*LL1t9r2CedIO!*+z>^t7Fms-;qf~*xpM>j9qB_ z-db9!U`2Vp$kiNC{yZnY{A3KeW@^^e$g{)mK>P39C*kaeDvIQ4w^lTGY+g6jU8JG-7&@ z|F%DWSc6duGn$#`-8sWY09|IBtmfsbb3=%}-kC$45?p$BrS5{H-SoUX7##l{&wC`$Hix)CLL^P;VJC!t!V>I|% zaIU7Y-FP^IJyo;Y(7%(f^YG!%O1`wUV>duCwTLwI3YTBW*iY-|{+`4J<^?NDhK|O1 z@Hsd$!%7)j(d`odK5%`UWv1ce87t+`78*#Ex|vMyi#2hDH0 z^8KYx^5p;A&Jj`q_kU37{x|dXKg)Ulj}=ew{J8ke%mDT5QJ`Bz&gW75-<^zMg+Z*U z{{YO6mVEqloHld4^01N36iP7&S{EWVky#HOm!1%wBL;KwdpC8ZFBV}Y{ zrW1#chB75c2GU2%6~{7_sz<#cKX-qsq`Z2iprNsTGUKg4Gj2c0!^XBa{8rh>XsWMb zuWsbQZ%gnK@c>XN#}PRkV|SUAY{+mAPx0_F+nQRw*Vb(fgv3v>lKog&-+lIdVi&z^ zV{Tr+54J^kA9)`!l(*(o)5Q|W2Ft<>Tk|8X2Nc5p@&SG#uzvrd<;(2laS))7y9V(d zIyqKla@$$120H(>&%C{NObWF8g=TpW6y&le@=8D;;IGL@^v(hEXELH`c4mk9yp z$_k)IaB_Ai*XWQ1gF)Rzp^5fkWTJnsFQRupFn?MxkuKX8ne-yGWdTK@EQhtY%~o z!{HF8IyS_?ylNwJ-CbCriSOp+uKA0T24VKw5g~E=YM+H)Xvq zMJJKHBs&{5wk|6x>%DS%TW|DMN-7YD!JG}!+=UHMu_{WfiLi{TmatIyB2%Nb#Tx&L z3N;acj=k_cIF$=*+Vjh_CKa z()2WQ{663~udS{99{Zn&(xUrI{o`w4Yo)juS8_Xhtz+UpGSaUzOXUloBS}5XCq!{q z*7;XtQo=2G5B!8*>(%n#f9qHeB~iyNEK7QY;J=- z2F2yY-DNK%U~c-{$=MvmMPK<|$PpM6znFYrP{J;Y&Xkp*L0XfNpIUygEGclBnVN-D z>UhYRJL-FU^Of3rrgLg7YHNNkO1FSMu-ck`JyuLws08#WbaRo8MVKvP#>U+G7m8Vf zc}{g+W*~n4y}3C(mc9N@m(|HTAo`M2A|@pTnYZsnMIA8_24X*b#%9pylpoY{%P^!b z?76&yO5bHH9ROVnk8lB;)nK&ScHe+|s1{6HSYi!tb!D}&4nH?ajX!!a$E3E+vj$cM zZftnL0Rp9q+k~h}4sH&Qj8(Phff7`gm}OpXh2Y{oMk%;^oKts5wtk&W-sW1;c%Q0t zhZRg=O-;q&BKX^6UZ%!x$!EDMNJ(j=?ka0d%K7?=g06E*6J#&I)%Q7LTYjv$WmE_n z6;dR!a~vZx==t2uJ>=wuA0#~Qh#NPrj$x@!2a&U=+^fH}H$ipr5jxKd-6$%3$ntDr)YGtHx8o}R5 z!Qe4tw%qk*@Y2xia)L`wZl^;a;B#QbAP7@%byU(XEkFTn{EpdDzX>3hPsnE^zzE` z=o4|bK~fGQ(}A=ixI&mL!IcOz?aLH%ICwIyx|Ai?BA)7s4MY<~-U(Ju9I1?}EQOCT zrlUI9L2PPoO8UPRofRP~Jl3yt%}KF2wyC|{m}ec=caC_%D(+kPiH0}c#^)d^yIoPv z1FaO4wu;Mb_J|fyy~VZ`D#Bt+mDyB@=HI$BzZ@MeG;0W~={087>XjDWhe^!SJf zkHoSQGWXrA_UJ`5hNn@3!_5J$SV(DU=~O}dq0z!Z{Xnz64&=9saKy*bPqK8jML^1t zStMN^R;l7?QE&wMxy_NumGh-utff3Z>$4Vxu)C!7$-vI?u_@Zd)vRHCw8f{_qot~X z1m536CcFHjv?;8n{-~dBL{j-pXww+bAcfu@uK(ho3Bxw#WFQ!4|v z5bFxT8NPeP+h$$l7Sc5d+^=%&j=aSwL*sgoI%R$S)iR1PJg`9?_Cm=fGem1yk)Cob^n`2p?)6~EGmz14$euI?Ii{s_D3&GF>=*qYLc-fLegBw&k#o;o5A*0lLJY-q0{B$`-ceTCAg^`-iA*y)#^@-Q>%n?~;d7+V!&LVtKkQ z?lmC$OXVwLf9`d|#xE$?ic{UBss9SO4k@~qOqT44+$t(BcM&V9Pck;CJ)p$=qN_^= zK;gdwq9vGysq*InuEJ55V2w$o)~ zc@qiS1PWoQ{!REe@*$Z<&33@7I+ zlDPK@{^i!b6{lx@Jn6o?*emqG0y5N?O8Df-TDC!yBq?|qTGtK67C!Z4qEHX5U#VC9 z;(}ze#ry)~02qm*5D~UnuvA}XU|0Z81u)9*eV9Pj2Lt`w#-!WhoC+hbv}{Ewx_}_Ex0V49u2ep{HGJCr9wfmF);z;wM-}d9Kkh%1fPozA--%FY{Ma& zlU1q)B4~8teY#il*pU;&hdh4;PQIb-1b$rgHDD^y{aE%`t%M0yc+ykJ*6rXQ+hRzl zfy7{wppj}kQ?WV9jmac~OF_Q(k|crTT1^T@VI<4SA}b}r#lg!(%gO~}1-*I^+ihXh zt1~4T)V#t}bgW*@729I&gS*l18@)+j3l2=v;J&9nGelb5S26?T)C0mMVuG2W8PV#MpzE~&yOqtmQ+zh{&Kkt0T0ubX3mQZiAh z&spy+S$hSW9*Wt~fzn~~<6S=NMOq`@HcQS+u{INx?u z{1$9zu-wbsv!!z!H_n+OdNd(_N~F$}gRtFBy8pM%OSd5J}na>IPLxONwTxO7h1$XsK!1UwK|rE!tr4@!ou<; z!YgKE(SoL(OU?Ug*^@lJKUJDG^}_&iz%dxXlhS;5NVZC$Z;%GPxutVLw-I(Ldy1YP z6H*D83|7$K_%X#1+jUP)jV&M4e}8R*+N&2$Ov=qpHESK$avT2nq9pioN>l%NCQ{;C zNp4ww)(dtw4^0KU3teDTU#Q~nU=&OmfTc4kNS@uDjN=srBzUNSW3*3dDMk*562pu~ z(Cs5>2wE0)i;rmZhw4Ln?)Z^6Icyf@0`Bgq=IG3_2mxorX-Q%MTM5I>y%B+)qHTE5 z_NK0+PXtyh?C8#&w<4cqqbJQd`XZ7b_aUR&c(H6czW};xD{-Z3=G52itwX5!IkYX- zgG$7MUZ$$Hx-1+C>e=ZSpo6_zqfCk?ge~;lsjKl z=toMDa*1K2Wn?J9O)O5)iAa%CZzkbbxf!?HRVq7ZUYvOj1x0mqW@sLS@}?|bI> z4W^ASOIRpd0amIj;FyVorwZO6i98JrOCm_-tU$oLNR;2X+t6n_8D4Q=FlJqT{p$0D z0Z6TUnKlh`eOAt})n%zmDYs%iisPztQj^fTPX*mScoUG-7;>bF+Dl4rK-6KuI zgawoiTYZ&r1VzoNE-w_yptm&C{ooHDGjDzXDE!zo=Kib2Yb*Q%Ys&Xk1D~#V}0vLhqeb z*NgV}F8rM)Y`hd+Pn~za(1>prXEULGTAwx7;Xry^DJ1fxyae{uRQLR=i6?k3xX1L$;f*h2k%%?|QQf7FQ0GnkLo~%~FFaNFHBd^_iI$zh= z?hU?jG$Z3FB_tu?FzYg2*21g>G}Tj@X~EeoHf6hfhu9ApP+X!QP?=d};9{b^3a;1Q zl>I*zw&>m`DP|*hTA^*5*446yfDR=tKw7*d!ZB=i;Byt^$$v~a4VkR^;ZIa@LV~0g zA;xpr<)ML*4kk5v4W1nv%m=EbGUqE~v_!3c=y}WMvkbbw(}d*NaV(rF*Qu5|Kl~t! zRTSGzK&Zc=I8bvJq|^_>=0Pi69f~9GmtB4~pi;nlaP6YEy3AC!&dF}FCK_8JR=@iiwW=Yn9ZyVvI4d@`Zw*YZnBF5|Zc$cI1ngmgCtQl`rl z!D4dzDHG}%8n5A~H7qM208Zj}dBgHbU~!8j3s!h~II7dmL>qKL%3JgdPOi98?~or- z!O5qcMQpL6s-bFF{gKxd;`~@C;^K;(3h2aJ>>w?DjJLrBQ0ReL30bE>Ny0#b=YC!H zP5^yZK4gu_=7Y=KO7BLSU>_;Mpn%Hi$`_+uY&L$wd?I%!_+DE zC)~|AU^O*;VSDCq;`xf3o3027eye#ENlC2bU&R{CltSMpD&`5Y`6bVBH&{>Ggz8;_=!Y-yxT z?Y3Ar4Pb4U4_|oye#by(DBmxzsfVE%p^V$RwRF!&35gB^YmM5P>*a(gAdfKf^78y} zOoN|8Q1GISHWS%KooiyOGa34=Z@ffLLP8>)_%o0j`XES|j-GydcU!acy=)ou%2Y{S z-V<=_w(hzU@7Wj0PX3yXx^G^L+r#23rln5-U_UKGchIq^sj@@eP?9gdv8-FV6Tw6& z47?X4m_LC-3=H4UronvN^OxFx9bsV55#N9Rckw@?eJV>Bb(hpwm>BIR4cSIsZeb+9 zYs)bF{{%w*hc_MkXSDLaRSaRqh`77nKPEKFv|xW48)Ky+D?5@MqX?u*<*RG_1?>T| z=?BX#20!;}V?d*HLy+DNd^qH!q`JyHgOb70Hda>u@)gGFJ@>vN;93K7XE_^2ii+n6 z17y>hRlHn0IcGpZYDVjksQY!!)Zhak&9TABB@-I_@&N`TiItTV;7DxG0PE!WPekz0 zCfqbOF)=Zo^(!rZ$U~WjMEDrdIL($af#Rr_ymm8^BZo0p=(EEyIwY*gu~#8-pwWEL zpyk8qU;u<+Q?vxw)q$+v%kfRDeLo;0rrKt~>Bx@?V9))Y5)uObnxj$d{iCVgo&-Rb zla^Kryx}1)@0ZU5pe4M&TB@~_!4qLoH)`!10Wy(Y0dBn)Gg*g!7x3BSb;T)EPHTCa&GP``Px6WR?yjy;DBhsS+xB64Me>U&n2{B&1y)eb zy%-$H2XFHbugRVk$fUFn0oi-$^~u5M;ql2(4!GUm`T40Y^{s5bfXZOjQY4&2(SH1A zfc*A&OaA*kXk{YsrUA#B!H&`VbD?T`4)b@u<<;)PN4?Ri9Dw*I57pDNRl!>g$ds){ zszmT7t){b$4%sqWzuF$pHo7mwG_VcY@O>18960_g`Pmo$u`7&XNw-i+POkW-p$vE& zI3DO|&eu9ek)K=G6dO&DA7BQLjGA%soUP`I4#u%N*4NjcW5l`5V|~PXEAF6Wg%S@J z0Y;4X_lWt_TgoJf+uhqp1GU8FC|0X6Td?aJG`58{Ko+N}s<@e=rGmL0ae-+mDa$Au z2?D+bZa#NLFOOw-OHVktcw7v79)un5^4b|Kx5%cs16n+IV-^9?-DHuW*}33Q{6Xr@ zL|MJ}ZgIi@IWse}sl{}YC*p?yS2v>0>DSMs3rbd7vWMz+HZgpx(Ue*JkLkbhwr%Tj+?Je zE&x{IW?T5mD>>Ze+3}SQhiJg$LSs$3pacJTV*@{g)a6iUHrJ-Hp}O*+g)+H?<+9UZ z8~FcW@2#V%YTJF`g$OEwJPL?_AQmMdQqqbdNOzZXcf$fi0YL$k?q<>5EvO)kEV?A5 zyPNOAIPbgn*<+t?d@;uP{Py#@`o#$-Wpv97D zmOq~8i7gdmfCMZKZSJ-!`?>L!u2^*CNQ#Nkr$9-VsbxmnqyR3*w6yS5kUF*J)tHDg z?&IOTJ&$v2DEmpo(NxLtDG2{SBi4OCFCoV^g02YZx_@2Zq9k4)cwW<{zZ-8kcUV}h z;UbQ)g7jJq60h zVXm2L#n#_ieRX^wj{(`{EZISRE+=}Zu^1N7b8lirk6@bFbX<4`FPy%E{~(B;BX}0# zn9l`7NAkHuPzp**e>&KtFh0<$eVQGd5uIh~_>^!fjG1H0orvbz*2K?yHnNl7v7OOO z)YQ~v_IDwKA-CMf#KhRhsA|(ahWo={j?KBc*VAK_AlQl-nvHyTo!d!6mjXc}ZAphI zlNcYx_4l*$(oa9z9{p$@dLNo5F%gkeLUnR8lgt9N&Dy%Z_m;XFmk)S*&HD>lpayao z!Xb$u0m0SrRpPN^X?0SzJk6cZrGu=YvTB3UZHHyP!NQY%PtKsSbo!P^&043h8V-7T z?ZK=R9tx)1J$y2>NwyC$o9x1@VV8hRrR`Fryw4?D)e7q;F@rs@H#}deR^{2D7^xj- z-1!Msg$i3^GdUIa!WoUw9vZXHAR=mecWjP@<@B3}d+#G~$wR(iCCzPjHzFvh8szzc z;q^MS`%9w+x=O3vaqT_=V+J)$_}970b@%pj$0pL|c2`IJStUsYKg^rwvYq6N@>qP} z{=(CZdNtuvxJ!Jt#~(Hn!ua@1vNF-vB#66H5XIaBEdw9>{G!N`G`iNbE*qs z4eTvTeWI_AN}c8%ArOgcf2)QToaMc@9$*-FCe+AoAyX@!`4v((lojN%rT|HX(%|Zn zGb%Y5=Yflv#z2WY0rn~x&HfTvXBIYjCHrdMJ74OmZpLOA9;(b8FFX|#WJb(^0{^a? zKCD8D(^W{6W_VZ)H2~w(RV_e5@y(+nnH~0OUQxG4*S6lBpPR2*uhzFvP0-(>11c(8 zzQI?tud96nHG@A=FOr8Fh=D7Dc)ZDNwk}MnmZyt{mq`BmfPG2_bd{ZL5m@}xc--fc z8arHLtA$0{ov+EOs6={t9@fMxR94_|+gmP38hD;3a1;6Gs6B@Br3+J$r)ND#m@Z@f z;>taQ1=s(fPs~xsfb#izX$x$YQn;-MoF^wY3266Y_+2L6+(ecU;=FR=83pZ?Avz#C z^Y;~ayidgO*brd8k4b~8BXr3!TQ&(!k9uDyIi+Rfe#D0TepM*5$}qJ&>SYE#g;osD zLHYZK`Yr!21D<}d(ZRxjvAgVM-6pGV(wUhxNj!=wtQL-6HlRA9YwP`#q=O#@YttD0 zq8T4uC^k4*Q_LN6(yVcsn`gkL%zpMRhEwnB#3VIS-mUF}yNtZNM*z@|%^Z6@l=P_g z5{<_|6zJ;eUgvV&p5NjQjN?<3m$%Bx?Js)~^A%6+nul!^v%{BVWMi5Tlq)-*oR-Zl zkyqe3O-u^$hW>2ri0l96Csx{?9anq)K0DpWk4EBqeu1PhtgPs3k6r{dC4F59D|?ZI zlFiCYPM+Oqs?3|dAXRpHt-3Ldn`95>_S;;BwUivEh8#Wt+Pct}xv@!mo5X|gZGi7j z=d>3yT?At)Y|&Q;f}n<&zLJ!vqbzN0U35~6h{7pe+3;AZ#OHOKvKcv`3~bavcnQe_ zlGdtC_&LQQYELO#M5WCMvjaspSbW{7{G}9RTz5b3e9b-p7xDCi3P?r)O<|xl|wFhcXtm3k2YP8SE=b} zJP6)Qc&k7Q@4WrP^{Fm;Uz|H`x9U8ut7Ona`TQF!RdJ!AUja$pzQMHz{olKHV)t4e zyxMngNSF6N*bd1oRV#M`m9i8A!&bpneb!QF768^DCWx^cqA-ht-W>g1@K-hX?Hld9 zaf)`WThddUcj;FBGXpKRsoQy7j@Mz~Rd5ZWNqcJ@4CrWCm#*d_!D%UbFQvk8bjoZ9 zfA?G!5wx3E_3b zoSR`#5l>QJiWBE{pI5FiCNPUY+idS{L+PaVvB%B%pukpB!fyIau4K?*pq3Xe>qsqb?qS2m-0)_0=Hd&4x=1Yb~ z4$HjOGozERa|#N%7t>jE zbsQW&dL4|G^HU)roSKXF=6dYSOfVB$Z5gtF3R(mThsSYu5&a?CHZkrby4;?c>x6>E z>XH`@A^vCTJN65_euFq=xW~C?_+1Z&`?9-R6fO4gW1iBIk{TC0C@5X#OKjKKI-~NA zmL;?^9oL`ELYazcdI4SoHgwNFh$n0DvS#~5h50q0gHEFENRN|9pX=`Z>L(Gy!oos} zN%Xq(@h4m3cJV8R;{ehK{_EDi(R}E8&lvDB- zw}b7Ys5;6Sth zsHVLb7;aVi2QgN2@W%5=PMo!|QHHG2y|Rjz^`>0LQuyKmURJ$_?pPZa0^cl7u-BV( zTW8c=xpRq}ZAn#)r>|Dm>l~5KAX{cZ+=sBBq(=@X39^J<*0e?TXgyv1OJq|D)fo*o zwTg1zL`CHbD|8|rq?XS-y6hNOlayX({YoxdOx^J*I_(7#%jxMc9~0(i=WDrW`QQ*5 zQ@6GCEeH;)v@?E%xQ2s+|LF`uOCrFYD63f-zxY$H$)Mfw1RH!c$@UE9^jkJvR&-ze z{$c@4J+5Q5R<0u%PV@(^+Ww8(aqSWHn4w4yNePj;LFUX`?r&%K824lDJ9j3jqU_BN z92y3_Y_*@xFa`@>UhmmB$c-LrVT;KgdcQ?PDBMXcKN`nf^NKXI)Ix(9`GB$2Te}{s z4v1V^zZ1x3q9d+x!w8mXOTQ9!lYN^E$!@afytg>|sCL0Zb2xs(G*tcBDen*~U)d|3 zewIOX7oWp%waI9$5hqE6hK>*4A~s9qODuLA?+(iInL(@CCO5LV2{Z#@Q9PdQc?L{M zg5#TwlA1L8Dbnb#c87J-LZy&CePeI&QmEGQIVtLR>#6z|FM1mnvy$lsrYySv(unJ?`JXoP=D7YjaI9u0M zX>hLLyYn!HSAEx8l*5gYQr}C=MW#jXA#%;LoaEYUEa!CbD3Xt3_wY;R^#$Q^E+T_@ z4ZiZv|41&?C2h@No2gV>ThGVj7F@Fe3|<&yoA!Oh`mzZ?4Mb@U?81 zf8=`zS-PoA_9t`6orTeJ=`|i#I7|nQ)?|f`Ydd6b@aF`WZuxP_yIdwau39gXVm^EUQy6FtrQ#AEhx@hieO!CEBD3Y5oaCPOXee=f7 z9V??gR-}(+u^JzKZhO3hq+OtYEGvtgjgQ?_ZW-|9+LwtlP)3Q^suUvo5@6%DoZ+x2 zAg&wCpl{*BjHfC9UptU-PrNt?@ww)bLrLbP--kJ5m=edm7tUtn<6}aLq{~Fz52JoS zcTWJSVu#h)xl1G7av8lv9q|{2(^gl7$II>JIz2_Cd`jA8hr_Vr49 zVMC3Zk`CD0_YK4>R}IYhv$vY{n#49KOrJmRyDjb zU-s#`4FNl{-8$oI$cGQg#impaBi##qD^y|;nz=@$1?+ZZeyGmQ^HkWz*K3eWnGqE^ zqIN3eI=3-%==^RC4GpyQ?H%$doG{EM{#>A~cL#}u_zWFx{EuICNLOEX!m|W;kIR{& zzL3ivY1B|~8!=)bJbe%s7^jo#Lipxht6IjrIG(La=yDIl_J8xPJ@S=z#j31R4f4Q^ zd~oDI@>snvglqoAz|2`F&}Q1AAK6W9stZX*YZlZenNKnXzr-M{o}E?7K|Ytz$Wf6$ zH_y3%^e=Hm(QSkO$hCujK$L98Dd-t+6+2SG#)1;lj>DH~!grch4AkJm+7<=C#oumU!bX(ZQ8JlQT$Zd_rD`?y2QJf7H;(l;yEa z!DF@CuRKq}Z;VjPeX3{hW0jVeaRfLJ3A`qLb*2~6(qn-r;}o}tvHDCyGlwWU+dTFA zE@uRPSe$v)Yca@ay16;Mv@gYvNke&hB%Yu$5{DsELw!g=nuV#Q=FXye=Q*T0x%pyW zu9cdk?RJnwp?Plz(ZX|_W)VM671yQ3$b-&K%N%hJ9b@gLE!%}*@5(f>&aP4^6C# zyvOG3y?Z07qCH%XSNIDSjpIE7+Rm}j;F0tPIfGS7y`h+VXPzodrKhZtF(y-<&&Bm1 zAp1my!nokApI-#C0T$-2*{wiQ*!PD8&vm#S-Qa09rZoGZ^43^jBQ7L#dPmenK%idf z(}j9{{20hLPSy3b&*R_fg%f4scFOY#WgR2ACfENfUg zdV6P)UxJr)cg}{!Vdbi{y!>SzU!P1Y)CSoyUYc46|Cf^Ceh9rXL`Q5WuAeZ@%!kae z@ZDUS^}R@l3Dw2o@sk@^SbLE@h11i>Ic$d;gfa0Q%4L3)RQ?%5eP+W63DSP;{E@7O z$BUV(O~TkxpUYaJl()%|AmyT?rL{K5gxsq%6(2G|TH2GIY1gJv=W>+phv48Fj2|Al zjMJI*YgAscDiy)Wk-2KHD7ogyqfX8cicB3Xd)e|HgSW&&BVX@G+kA1~pBobMo>+1B z+)@tf!11Lumwf(Q4^eyPBnjKhe^|aT5E;ww6lquWW7)P%vp8ccN=7oae3OAOcG_uw zQSVbj;%CLK3LZsHm&GYBz{>pG$`{DfmWmSXIM-%Cj5qYgip2<{we214G+{C-SwCa} z;z_zHx~*%R34&vGt6Ast$Lb%Q%Z1pEq~oEN=hH%Del8DIJ0F=VIo>G_wK=CCQyux_ zxaG-4bY;`gaRUmx+gEtb->!Hw_8I%i1<9A#2(9IX(o>gu#hgv(MU=w=T18SZ%6O;4 zNi(T%1ELrePZS5l?DjAZY;}|+^^cF`I+l^o@)vtO4r?d}_2Pt)T%nj#!5Z1x$Z9SA?A zR(Cuh^r$Eb4HT&J);>dEN3L3W;wsiyL&g-(i)2;`~i=OtF-hNkjj{_P2bu$M=$F!2%%}}E`j^C zI9YPpv|+-uw{F}G!N2|F$SCNU6wy3zw=F{_CqyF965Z>(m|wcW^;!J^`R1{X-psU` z_RJ-QH0$|6|4Lgzlim^s6=$v;Ee1ckIhyBfKiZ!E>rZ~MSKRc|tL^J0RJXsLPE8w|pKP8kMvy~F!lI;r*}Gj6$u zm}gqT$rQ7+=B5lKKT(Ih*zy}7D_Kl!-USR9BI_U|2Qo%8KO%b%aML5*(RnrJF2yAV zAwYA<5ZObU+)FS?Qu5^VTO zbfpgvPH<$*Cj?_ID*eEOfQ!eIk zi?r0bFm3jM&8Ud^dzFqdgG;n@wC8IN2;&{AU~6c8cNZ<&Tc4#@h>eR7!BsnRDu{Ql z-ZqP5R4;$gH<|?f;f7PyYl5tFc&agAej&?eM;;AeKQk24|C)txB-gO&Q0*G?0}jCtLEfn80IX$2am-)e z)lJ$O#BRAJ0tr~BmbPiQWC`?__+-3Gqes+#L(t>@SyKiFnrn{CKtM&+`uVS^D1#~f z(v*9=kox*twGPsF+GQUtX~ILkUD?b_NWp+=m!+r24(Eyel9P1-r7-5=mM_SxgXaW4 zkGKwBq8=^G8;Vo#i%rpxo3*`ZGThFDEO97eoJJ2aItqyu6!aS3UF*zndSZ?~enQ=s zwS((;#13fDU`Cd2{tGOV7GOr7cV8WKUuEUIn__);==r3#oQ<{CuTMZw`W`<*WF;lB z*-flgef1W4ny*>h+t7kMV#nX{YVRR;Smaymwb=aD5DUSxBV~5(t8ZuvQGpA+l`G~MTc_x9}VchM1$p8X50LqRwX*zUib?+V#ASsw_zqBDb?@# zLca6mxDK6KU|K$z#|*NXEs7ig-NI|V*m||2nh?S7IQv9;C;ajeZMAz`j%pQ1EM5K1 z`~b*;A`pej%uYz)OLNEn4Ob#QX6{mW%A*AuZh3n0E98|BCilw~;kX{eIxm`L-@cu6 zgJ(VAnB(Q+8+@+zS*D^#z)zskv&iSS$a#*Jj~f4&o9^`8a6BIhZtJ=3wL_wyTtHNG+3hT;#Yia4R%@K%sSu+X|m6!h*ScEh9|3UZ&J!MeN*Lau$N`6m- z(qSKEN@~hznVshS*(pPQ+xRF*!GN!-$19fh+gJ8^o4MnI&{=EFcY5c4I(904e1^MuL5!l%k zVI58IpS{P$rLc>v2l=l*Ij6_1A>|FlnhUrO=(9^(O#F-G2Ns>yQIaHVF2^E_sEG8g z_+wsgym>)}4tN~k1=&zlb7iCU>A5%k$0+w+>I*T#=ED*k2rtGza6a=AdMeYn z?`3RBQjTCiqbG&%6B&h|!|SN9H*Vv_iquxmjX8&EQyDD!U;?o;Z2P1O@y`W8%;T(yiymfXS#vi1vqlrT_Iah>4Cviw zuTqr&gby7GIN2%qlJVL0np?i5q$X!%&@3!0Ovm=j?w6d{Tf;X@=`Cx(Xf{|fv-ei9 zT!_J844vk-boq(%(0~dHg7(!ZXXb|;e3pmZF{P3TzF5hVk|LP96NBpdpYS*m0&8G{Xf5fy}jbBr$&;q)MkfW|7FB}}c_ zjpM|$NOWbSuwlyT2|#!xgd_|M3~MY&hJ>_){#URBlcpYZ(MaRSu^wwU#fTCw@YaFr zLAuc|CdPj&9}~-v6&vB<3RvDI8PA$tUB{S8SVY8J@rIO~++v0v3yDYapNAw8%)d%# z2&#=t@$iC2yFAabgN>ooB1LEFDd&i3Ys&!5Jc+6bjD5(|WI0jfa0M1j9$q8&2(?+N$~cKHMTy??~=E47|*4`(72<7wP#aIeDsWX!`-;o7bOb z&3d{GKT-pD3D9z^S8(*hS>1GRW0+(T=!3Jbd;(&@wxfsmc>d4Ty*%x%J<8xXyvuW{ z&lwL2xsEHkz>%dWdVhCIbr`BY5|29qzh4-UUyUMV4p4kJ_>f~ud2+}Qb@YCGBG5np zaRu}5pM-S+Ye`hBRS}FpSg-U`zoy`AuE*VJ^w+?r*t?JL{rhH!8DhW&mcLtzLr9UO zt2mAdGBh#@Cndwd2JZ~S4&6t5^ZavTV!v*T9?t8F_@T@kmksDHUX&gGq-Zh9{6GCi z7Ngh~@aN}@eOW>vA`zeE0FFU?D8z)t{{8R#dT>ft+;~Ey<)r>Gpu=)1 zchx^biSeKQNTc$b=qe?lf1I%P{;OYud%#f&*!bvlUH8_|$k4BdI~V=1J%njUr5>F2 zkcfO5x+BXi6>FJ-hq91lhJz1H9em;c>A5tEh}6HLO;4W?3yJ=?J}Cyw?MLjtcl`TG z35ndZT29uk-(}*=I1F*)xpC$_;?Fzw?G!3Qd`?WtrsdgPQmuL@`vuZ)I>Zon5dZO7 z!Xrp8;(Nb-{gy>u*zomge0c;j=6m}8_*b-zO=)?p&g?)a5PHb`AwkkB+Z|gi|Jc zS#;Ol1q5t4ZQnY1o-y)1iq@N=o_%cXoK^uV=71-5V7ohg$P= zD8NPhSWfr5Cx@qEM~W=XQYy^%Pyc-21-WpHRRFD92Fi*|$9%Zvod~WKIew`Ca#yA= z%Reec_O_oEEZVOriv#Y9C>AtYj^C+2XhGt}NRC_;Q@#0M|JILXJb?Z-I-ZHQJ^plq z1mQtwI9Qegrm{~z)?`OHM-^`_0zZcm2s!G>xk2bX$5`l z>EobJPfXG&--N_LsTLctMaB8RckfbG7w-1fCTHv(v~R;uk*OH3mDL5z13MJO!un6QnMgMr z@h#h8rnsS6yM}e6!cacPsfLum*!v6ZM~w##IoxFiPhkJQF6*@X@X^KXsbx;Tue5$O zC#y!ix4uvpE%A6b=Vx137i&E&0~I8hVlR#=1v4E>y8f zg*X=jm6R)NCo-A22Y#F(+c1Ntwyo+%hc~^xv_jb zqCU->ta3_qjmH|D9bx03gOKUi2+N%-RV z&4VFQ3<*fFhAx7hnrY`q)aejP?GIz$Q)CxF6RTijBwwlD15b*p$gF+Rmv|WyWRf7) zVzz3H*UMzTAOp2`Xr;xK2xSF@UgQ8hi>wYd<=mg0=}O8frWXEAmdUG_g_6{|z{<1x zn_WwsEWSJT8p{+~R1}P@g#3e3*3He$QxqYgv0vSY*bX%w79V(>ECivL0^)cARl-5> z_5#e@db5-q@@f$o2=SIc*R;skl%5$0%k#5L*K^7LUR6XKNSUbAdE!#oy1a99Nz?Io`vFgROQ*Ub+^H(%cx5h4Nkbi zWTzS>Dc1PcH1(qKg(@B_HtbvG+ry%97`bOGM%|>!N=rbRl&xMkuY%7cbPjZ@uToB& zr<#5s_nxi9lEY#P%Uaz9y-=|%naJW``Bs5E8I`0atNZq607=8>HOC57xg$gRHKVpF zv_iMeF18e#$jK`**&Qq=0X+&SRdF9bPLv~SKYmQCsbPF|>sbhCQ8n#(buxU!m-wQW zqp;2!-_4%v&TYK!1d*5_C#SL*1KuFBjQA~c1V;MbLh7s+3BqCJd#HevY*1EcKNS6- z8wY^{_T~~*v_ZqX7lf&zbs|R)QT*dRT9;S4U7(K+mOKV!TwoMWsarQ=Mg|?^?6h9K z#K}3>p*rQZwNGxt5~LoRnVsD%ljvPBnBJgKIGvlOlBoy!cmR5`)E>MgdYvylmw-8v z+M)66$V8z%Q%l`E8#5&oEhm1Iz4x!GX+4DeHXj3Zx5(Clnm`K%vm+m7W z=EiAQz|9va+)UEa(zuVfpa}DgBN4<*K6iTh|3zHh&e3*aeYsKLMHCC|fgr1a6`DbF zzq$V^uAc|xehjlX}*n-RkyRv7yRzMm448pj- z%SlOvb2@NpYyD~z|4Dz1UFWsjnoo{D}^+BQM z(x1L{#bW&WgNNra?jL5;areuFaq~95YW)4L5C5VR{|`pB{Vy|n{wv7kza9Sezw=)t zG(ot+7>3Ng6@ZGSgP>hwOH<4=4iWi-6~wO+Y9d_c7gIpn>Kg>=Kf6YJvuxF&h~Ezy zLHH4R%eVuq&Kc*OQ_^xz_h5BxBd@D_dBolGZ%7i(ndKaMD`t1!|c(J3l+N zQLXr%RHd#CweRT7#PlKHhy$YGDud2PO%_{)m)g3`rMyu`#6h# zwhoeRWvMT9n4HQk59C2sz{8CdCW(d_DAnTS626Bo>^D_u!%gw)yx z5B5LZzA^7kaBXwE!Jx6Z1kLa4=pNZTn4mHCX>w)Mz4iBBd1QU`(nl*I@>(ok1#d1c z)6rO-=B<0TgSeR)c>VbRGf(gyJAOj-`Kw!D-u);J6YWNO2AXOJaE?15rr>icS^2S+ znmuy0HGGP{sHlt+$n-{bR-jXe+~;6D1^m8#KsNJC1A zcX}|>8G_c>of2^=@Qq4=cD1F*kb?MGj6A5DM8riQ#l1$W^IMnO>?4lAHg51?WR9!p zft?hFs9C6#20!asUhJOfgfV}RP7xeTNKS5AjvPLPlKv<7NEin`Ji6I~_5Ec#47uYy z=|AFZxz5C!PRDgm^2Eg7Nl?57ZZrUq@AdsO$E z@`^?obJ=Jw5mATQ>wT?~t7ye6?zQWYpWRcYFNvE zYeNA60%d;MG#?{l03Vd^LMR(jA|j%UK?5wPX8vU<@rHD%JA@7xWPp(k%SUGY z0JB5EY0b0QBKV~K^br2`)MYO@`3^Q^m*H)u*}YTwI_59?MuMSmY-07<{fVDsQ6)>` zYmQqBz4Md3&mTXAs)6t#*b>Oq{u^6D{~3^fyTEKF!l|F+i)P!4{3-+Gb1-xbEH_G0 z>U@~*&(wCdenBMvLQM=Y7&8#N;#!?JJL31QcFguwz6~gu9O~xaFTy{P1I9#n}q`wu~Em zZUK_QYWa@ONNi+l-nEumthM)C#}i$6%jr}8b{KH{_Ul8Vxej4qgrbk;c514Nr9Zgj z52U(k(Sn&;iSesxb&vzlgeOZhToP4uwq0OL6!VHiyKj9}XN)1;N3|=dK{RfVbx&l8 z{gBI#hhZA|)<+;qEk6ce!0=kX6ee2>PG3^`$@h>bfIJhQr;YD}4CaZ@k!)|O{bVR_ zn!3uWKWioo5|obBHyd6CLiw(B(3tzKEs!-SEpon+!u|ONmPh*PMlVfzOgLcpWbpq; znER3metjHUa2G5L>zo&?hpHM9s$4eMXNP@bv?XGrqpcx(SYk=Fxp~?&Nn>GrYbCM` zG~>wQU74s*@!@@cn1oiNbt;m8acxUBCjtj0Z3np5qCl$Y3t1T(!%kWd&&d?4LBiFv zG>KxGpl^uAI$?}G>F|9rWH_!)T9hlFG@sof6hcTuEf&$JSLU`sA^7FgP3gSiI&&$T z%brXuyVvuZQ7|+JNe3M6dsoG40y?{Q0Sx6aKFgS?UmnA0Z(w{xe5j zK~9cFUZf}Sol$wgG+-mph#b#vsh)T|veLhDg;XLx6LiAM(i+_OVDU*c(ow{-E0%89 z=jvA+o+wwmUt-g!06GeVdnxlOC~USF?CO)If;bb&xIqy5aQHCc_6YqAD5q~yxTj>lArYK zV2n{28c^ykEj1lGjUCdinG%=K2w0Gol6!xWtEr(8tAZ*&o^itfYeUcdyzvSc^Xh&` zcq_chZ6s1nQqZIY8jg3@IUw=uuUF{T<@%|=z^()9nP#m~hiXU+Y91?sL$W-7TOJdHc)i~daQzn_6qoZkZ-@pFxwz|SIDu?N!&A6&ke^NK5 z=t1N{a~CLO{o{CY4aTIi?l(29ayuB35KK_dDlkOdy_QLXZmk6CP?%&c?v3xKJChx$ z{X1XZKQyaU6%GRefJ@DVaZa=Kw6e0@?|Kchtj_mQ~dr0}p z`hrQ`ZbU>SCJF?3+kw<4WHMz6vacZR;~2x@ENxAPCTOVh<6zfhCu)|AxNmyUE05|> zhrX5pJbgs0Y&K!fiG@jQx)1PU{q_cJGtSJ8Bu&O-Hm2P@?Ekj$r$YBrOu(uGE4R0| zpsnGM>GUuWBm+Wyw5i-s?EeGjMcelhpTOAXA|m#hmBp$vC4`>Vfokn7Fy{#71!3dH z*g_r^5J=*&HF(G#zbpvA_L|-Xk;4Sv?((F!xQ6yb3OabLqm;?E5<%PV+?Ncif z#QTG%BDcwHwkX~A^6#SDV?y3=1p`(^{mcfzl`BJ~W))T~=3ui54n}(@T7KtCwAp>Jg&RZL$r?0&$BK;r=jfLLA$ZGvfU$XnvqWR7+Y=)K)<%%CSjGM?I z(hJ#@wRaEb50cQ{p`XQH2Qy5MTlD2?79q$U&@=G0Kf73h+^7Yk0Zq)U84BbxW3mBm zg%VXB3z5cUZQkpm@vvQ2@+ju2RS$g4Pl!mDhI0F9&4dyRT2}dTaEVkXzaqnL{atzz zRLk1GD|o4@e+f3Qo?_f7?#xkPhcvT~*o|V*F`=PuM-w+84^!i)@fzto{*7U3C~9sp z`zAidAx24DZ%0t#|Mn9J%pH>n{&#+&qN`d3dPg74tA+YG*^;T0Pdw_tQgP0~;{7)_ z)1J=9)z0|WTtMmk1oH^Y%3NUoe9;>3s(*?)JTE%+6@eU}V0Rx2Opt#5N4<^Nw=q59 zzzgxvayK}jBvssN-FCXwtpVs$07zL_m{lIQXLx&)LKYmMJ|Zh2!EJlDeaPX4^aV)u z%hl+4=|X%tU3z5gxGe}=YSU|uOGB*PXt@q|g*W&Zj~*nWP(I#*PoF&lKC@6j-qr?t zR}cC-5Gm8AWIz-9==y~VzQB_v#2!~yo@%d;ykUlHt@BYp#aBfOJHsLRlh%ix9<29T z5#h4k7Bm_$3K(GEWbYZ5IXH27NBXetIPWa#0haSq<`_bENj-NMT}L^gBm9JwVtxgwhy4k zas2r~7!*|29z{m9RK9sKn4_4V~xCXJzR zK#fyfKegH^2Z+w37D*{KJ@5^JYp#V$}>>cmU zy%qnmeoMm7uAHVKN@IkyzT5iT`N0&X zNDil=ZA{oSp-6sqX|6j_8Oey*)0#6>w&5E%27U{=r*=k+D5C+g(=bK;Ig2pK?K7{fxw`}g*KonIQ-ky}pt4E*JD zHd{37ppnfHq~~;|Y5;@|uD}@LcGmRK=V|4fN!r(41jm)B(U7mAoS$ z$2FB=TG-COQ?)-mx0{~K+$DI1@9emt+e5d_y1Q8J4v1C>UJIRKhVUzh;NdRKj^x(4 zz9q}WaY3*aZ=q*-vbSq6W6>*!8`G7s7HQS6Gw!rJz6Z~@xqajPTetLmn3{`8jc=g} zBKNJg(Hk)?fpgtmpSFkB$iaLzRO56yIhW*7!WGm**h3`a9(?qB(qXPlEPK}d&~@~H zs~FpcdKrDm{y#Z0EUxZu^RgvclNW!&34CzBlh>xzr24?sd==yso1iLZh9${ez`wM* z;io=SZ0>yWbxrT$$v8F~*5YwMcG`Fub`5`%s{ytPB(e@SF3>%lplLc07OQyjd8uWu(2Yq%&b>z?0e49ezELd&t zM3;GR9cS!k8kH< zd@5;Fd(;wR`GK?NM+MAr zJ!W;c6;rtRT}pA1aeFUaX26Gf*{b`4w6b`kvGEwbl5L)pj7)NDU|db!nD^grE^ETw z;pS=Hi_lWp%a@FO8w|@X_pEF;cdk8B72Pmk(f902nE5I=0)-Ti#R;+O3}kH)d?io` zxbjK97m}THv0b}+XAxz?{s28zWxlpYz)kH>E_PIO%Y>&&1I9HYaGpPY+W_`XwLq2a zVxsmQx;Wk%WMi+p5AGr@H8n925sOYc4mPED5o=+jd@b@V!SJP^K#-q+|8T&)@eC#= zuepl3shaDVzKqtk!bg^8F^s(7@S)7aiE|;Oj>(lgG#K{@)Rd8tQ7p3nZ)Qe@pEXos ztn>3*m1sXf6unPi%+J&ejka-=O>bL`vwyoZpZf*>=ANq7=dEtvFX+V+1P~wR5)?=p z8e%Bf*VX+5rtmD1&A-}p%U;7K^=pwGnS-HWaN=y@^M(~k#tVV(bP-;!jm!!trK7o1<8pFg>pGXUcS|cTbN=tUL_GqVtnveQkqFLDg(6U|I(X>A~VBf^<2R8eYQ<*po7U^g76%d-b$z08?E;xFPbekKkxlqjxw}d z^V@=?f|`LH))MnBDGiMPjKktGAFT`64yH$n;_`Rn4nb|s<-E?rwseV%HwIfzR~IA{ zSb_4y{bhFec51ay%8qHoq#J<6lf#_X@y21kOPsW4G-!PWi;bFb&I|qXUjJ0!(*(Mb zVuyoycqwg|=w~ZQjEWkQ28=Qt*NDY%%A?Yu@_XQVwzXIeVa9)#kqVB~Y z@)x^51S~}k$!BIQj!-FcDfLCm}*^lisDK?yDA!dg8((@{`Q^ z?dg8ZLP1s~KJ^B;ITFMn=dbEn`1xFbpPx43`(sg2(Zg1t-N0IGR*8GKOZ&P#Z$*Rq zE3ehL9~RQKf=O>o*9J>1=eXuMOWcF1)v>UpSpN#$Ca-{24e`%7C=07o)#rltQDWD< zPt#rRAkJu={^q3$GLuU_@!5}b0w2X}PgM-ii(UCmU3G=F$7Q6*|KP|%)a4otWIg_^ zbe^B*ci-Vg9Fm@%AqYy+L`{zg-S}Kla!ilJU&0~yS48svZKg}$GIkx1IauzycdoIr zF+X5o`_qLW+)sbyvVB7p(0DD&_1zc1tNfjP;4bh%^Gx+95(5b@qyJxciqlo$^L6q? zAZGOLY6u8u0~Rnz)$ynOrS$dVLD=UERd}0_DOkc#H0b87GI|8wuR}&$N^qgw3&BSc z5+xti1>ZhM{nMV_k%mk7osbMF1sNe;R;BoFNw;*sw+Fh;!~oH9*Pm1=PoF+bNlD4C zr=q4VbvpWZ`N=+^?yb<^;Kh6=RN#u}9}eTL>g7?);Up98`Hb?Si8V(6s12|PFO$ea zLJ7!`!2lk1&qRV&F=xbRwCEVp6>cSIZ*JMr=q)G8!>E@n%tSL;J+X4ZuwbS)Qvs6ivt*Pa5338Qy7}go^?0bHL zxsBIAS>vDhoF+2GQZZV(J|M0l9+ipQ8$Qk^3zdX~VxW~ol5?-|Xp1y%GsN;&lxeTM z%Y(Kr=vhU@LwpM6fu=OpHk+00Vpe%IHJ(2`_s8V>svbc>vb~OuaAJX~MfUwJ9N+t< zT{)$cUZk(d%6b#o<&_5@L_)=*2Q9*8rq-dekptBwSZd77_0_74e=bUQsl`GrycI7< ztw+lA`Wl4zKqqWP3o$k(Xvzii@&6!e;U~YF745YLWMUdh0goEbj?LI0M;Re-JnKy8#bSF_x{QdvjQZc7HV%9kNOL3YyeAm1vWo}wU+Lq6-V;5CGtaD2L3AuWl=U3B z(!!DU!U;wxx$`cMq!uDwYfE%f8&?u0!>o`-w9o z7m4@+$bIQ})z{hZzR6;mySM*0Qqc(74^*utu?gdsBT47clPV~0$k)N_8yx0sfHeH! zCwXbKy9=ddAVck%sS;d9-ywrw}GEEolMPb zyj~pgpe-Z1BO|#KsT_!M-uxWqDH*zDnZ<;@J7v{ypKV6J6am7Hs;c3}x^te=kt*{a z2KrQXR=U}!woOpWPjq#6M=FbkO01kfWiitc387$*bRc7&mJVzo0p$_j7(06E74>FV z=25$@#{j$OcW8dYi;N(J6r-eERRdo2C3k&BCkXQAOiaLM3;Xnlp7N23e$H`d^Q}sS1Rk0)S`Jl9* z@+T9C(Lx_6I$#~4ovTKXFPk{0*yi`YG>2UwM!>8k zqKb=*ZY%mZUfw+O>mq`6y$BE+d&(UjaE@2!U^@bqj*hO`Q#vqiA7=)}kOaRhke*)r z#H0Vv_Tqs&2?geQq$FR`=jaXar>EyT01*js0pI>z?tY^F`}S5F%b(7`$?_WuAd)~t zfB3L9F;;ypK(?`9$Pp{4?BFF<8rwtgcYt{Fm?OLq*PY23k`5u*;~&mA*Jr*uqgfYN zrs*v@vrJUB?}aUzWjk1CCPk93-k!kv^%*qZaHtWGA;;y^FMB^NG3#j@d{rQo%Ao8* ze}2Qqk)=U%n5ZQibjwKz9ihYR_9FP##}gq<`QXl@ zU`{TL$!2?XEA@9gkiRxe>Jf-H3}&Svz5Dc~-`BBwxi)hj4^GD#(d2^yF120FdTW2g zb=?t?ZPuEzM|ru$C^+O!`88pTKgzJFN2kNPun6MbcUr7|2AIz+5<)M!O=c3(()#6F z^nt5}2)SR*fnTPpcr7Y1WlC3yBn6RTmoRn!s|)BAshF8*f5@NeTfx6gtozVvu+r16 z93n+n5We33;r~X!+HyFau=^CBnCRI!kHwb3ZT-qtB`Fsri?ZLczN+7%V0J~Uz(6Dw=55gqZw=bz0hhY3AHaymC1+zK z$m_3S%WpLp)pFmVD+75>vY`HbM8w{HTP5r84Sa;u=Tc2$L-2v}I=WuH{ zhVa!tFS{eygIXNn3l;5>;FOOSMT~)khVS{Mnt;ZKoWRLm;B2daH^~aM9(MZ6*S4G z@)fYQ>FL#WjHM_PyH#_;XV`uE$Od)h8hE-@t6@Ge327}^F#k3$Y}p61Vm z8GgfE-FpWW_7ZqP0a1^~?(%HIG}7XQQjX%RQH!S6r^ATv@|h2%*D5<1_{@ug@T>mI zIQX(m{<;v7kMckgz9(sY)QcQ{47&Pj9-seuA5!aaJ;0PME-FffuH|Oi#<{{$n1Vv_ z10glhe-lpFNv`qrRauyOWX6pjAG<(|zp||&>V@j4v#KY#bc5H$vg7|E@2%saY`?wH zK?D>59|I97ix!X;NrM)U&H<$x=?)b^LAqPIyBksI?(UH88tPr(^X&cG@7d>^{dwc_ zIWd1J4&y!deO=dD>lx^M*aWSlKoWN=fy-$s{p}lAlz0?uWa9*N0+^7Qk9_+_&F%YaJrN>$?&hwp6>2^9%x5( zV}EKcJjnd`cw8pCd$FzZ%x>ukc?1jdEVj0V)gF6WnxJ!^if>pl4Xo=#(%b_WG`mn)5=_N?W2s@I~KYZxRsuVn1 zV7*jex|~a*AmjZ65*&Tql>7`Vb&L)jXm(p_4v^_BD@aN{i?rQ5P&9%j2*s*J`afnCJm=b@p*_ZL z|JfxVSLEwPWPDpSy4yD5u|`Wzb<_K+-XE8Dw*U|W&CQ0v;|rCRP8)L9aaE$ZhVMB! zIr)|iSDM0G#Ov+`DyyVK!);+ zXh>hY1Sle;-7DiX)YJR)7luj#pnGEzdHzKXn3@4|!}R+MhLbd$VFN=~x?g!@6nS_epY&xPKgX_>LX86Jl&(*gKiYV@PdA%BuS zQK8WOwvW&)q3u{kBCTR=FwLNr}6@Se8RI%s(b%HO$1Jemb`Iu zZjIGEXdf{rv^L#mySts+6M5LpQLx1Hn31v3U`qsi?k62|8NQk}IB)Jg0|9tJ;{&~S zaMXkF`~l)b>`4$cTS2+g(Y7s^Szc-uK%?TKU^h^uz+xRqNBeer;!(Bc^UnS0*DN9w zH@$fA(B4gA)kK2Gu^s3_p1t`kTdN{`0OR0!{EB2_dGuUO!X0f!?7Va2+n$_^R!fj+ zgqeqkzQXy6*k_-V!A!tv+e8DSAL1|JCiIC~hf8vJ5Eb-$-^F?36UV9Tr-E8lYMT=* z2Y~FnqrQnLJv#Z{w~@S$+J7(1zP#bKRx_b4MR034Qq+ z{JR+q3Ui65${rY#W@<*STD7_#n`vpld6u=G-OJ}v4ruG<)d-ZzT2;-@fyXh1P~VfX z*$q`5O8j#l`R+~SPt_t1R1)(@ENy46eHD{vZ*VpSfm&d3dh?$TLj05-x%xehYTt1^ z87eyuV*9_S2!s<=T zd$=Hd$sB^>+3T*K2Pa}~LjigvzYr9|=jhYexz7_*?-T?F#pu&Qy`h016ig$upBV_F zi?*jmIIVZM$E`9xKuO!de2-!qFy!su#HgW8P30n$kdOzLkK^l8=uCe8dMLlaeK9@d zlNFOze_3HL7m)g8a?#+ z$B&=&$B+M`Td;UOXfpAXXjfEIOVf-p35L4p#ADL;tPeXkxDE`M+`6T`r7gBRkcE*b zpO-#iep(CO`CSsOv4X;3Xg=%)80nu2JG-;?E)VXJPk&4M_q!t1l=8Nz39 z-Y|I|>xkD0RDWGp@1Oerg9UU>9@2(XFd)?m4XvPoFGrGpNJ#owt7~+}N=u@m2Oy|8 zd-=-z4{OZz$AYqZ?*1_7Ho$Oc z{uSHjbAtEM>G}rqjR!cFyI_xzZDEkInL{?B8&ke$Z1&)dTxr zN?`Z+KNYB}AwVuD7V|?u;s#~@$4W6Cje{4(k8`zba6+qX3c=gGe$O$bQC?M?o|Rly zmS0g#jeRdepuhOIIDi@5r8))#7~)!dcN#w1VQT(&x_h{EW8iq8(?#}pMTj#SsgL}4 zp*A#QaM*nPsu^4TU$7dbz8A48=U~kX#&w1kY{bbe;G0rYQ~y&#!LPED|3jmJ%iq3z z^9>Aqcw)YTI1!LBcokvyo&vIcy|cFBT?{vI8y~EMxS+cvfS?-W)i``{)U>pxwO-z! z;@vn)3C?x&aA>*wl#-G8X~}c?JV<4`3@9u*S5=f4P!F6AWefte)>u$KYxmi6f zex_tUGxI0Z(%rd^ex~H+88pN}IDriN43=@Z_+s#HiX+wKA1lv)o&WhS6Gi{4-uT)T zZ9Ssq+e$eeW)-fSq-55dOe&036mY{_E(TZK6^FwrAj&g*pwaVu5gPgv8{EKx$U7Eq&d(4?9kEZZ{|1K+yWyCYk!;`L_Lx)} zjfy%9E@WpQGa9HE1{4~U8f=X8$K3}N|GBsup8+OTkSNQU9s|oDBI1#;?&>DsaJ>6B zt;Uy2HFIkMnvfR~Rix;2mnEufAM)(qu zJSBv~RKZ?{!`ooIX$ngUPT|ttOyzP^VKiYZmQ+BdxPy4mOr{n6dHG?h&9*US=8q) zFU(JU6DBXNAA~=G{z|MVu&a!J8kVRUO6OP09x2xWnf)G`eD`U+>5)vHpr&+@M;wE<6GS0_NyJG zRcl+^FYfddQ>BsyiZv{Y+l)kRGCYdy zuWQ?wjOQcV3=+of)n^Y-P%g;f&x#zZdljms*hU|ofKogl4j|a!w!UW&{MG+ z#iq?MDpNQ5Ix+F1Yy<6ZO3d`D+*mdNA>~hHGu7!>6Ei%t+XBDa&+|~*uMMd^T%H!f z!7Fi;;0e4F#5_wy6T7!s38qHYJq3RxViOatnw~_dxmZ&v<$1Few>K(M>iogx(pX(s z&(~=;881}>?1u1*tO1kOD--F|$l>kI!F++>8=n4wVhR7 znlyavS25!1=KDB=sTVJAS7qTG+>Ueb{8~uE_}2;Xn}e5&Fq|^2NMp|lE^t6TT|kTa z_kvrMSLzKnF8hh(usc2ZvQ>i5t}v^`kQm6fNceGu;ed{G$XeTJR2B3Mm{l(OABhwRxMXFr!Y#$BO)3t zJ>;7zeriv@{9Bz+Q|@_w?n%8587tj*<>|LunvdBcB!hnoeu>vnOJDTUNN(FlA=j&= zvc<(x8ChOMQFy(Vaq73LYMZ!xW3lLarK-)-|a9^LGXd+*4Jg~?-Twc4A3!>Wr#AeI;3qb~K_>bAeqMpji7;Vlqmg;5JA7l9}FBWFVR`*6by4zdm z(P}ivrUzylOUEV@9v@r@d1z^}yJ>@3w%zC|>SM#ZHJ1eQ&Lm^#7kY~95;tWRBk;3) zX|z1<^wQ|}jZ~Twx!V%|x$NwZ!Ra9pni6 zKMXc~gTZ)%n0O?`*4?djNYK)W_3YbTB1s&L6C!Yk?v^?bht40j8Li6ZYNmO(hxV3w zpG`&L=sArlY_2pV=J#|eO}GpHwtQzXT73FBFta&GWo03$bY@^py#5#R3lVRQ?A&cH^-=7SXW#{B*-Kc$NkN z=00l3ehiPk!1Djv98y)1wdx)LmcyqWK1X0paISXzudfrf(=QBw^g|g?;LRywKa9 zz8xZp31|OYbLu&J?akdWi=`PuWR_<7E!-dSR%VtW{!b)x?_=CNd&|D_#WzP;*aS== z3rlwM!Cv_1Kg32mmvMZ(Y?Vx{>{o zrA*%}R4)-fzKGHiRZf$pPLCtwb!stEwVLR-9F7@fk5iO2#D`7Bb5SCcseY*LUb%4N zt+r88E{6=ZfgD?R{@fW~XZ0b)YASD) zFHz4r(pqFU-GC$Ld~F=bP3c{HqS%#yd|DvY{Qj9l&+~Co5~X6@ZQ16BXvJ&P;}V$f zmj>m>i!|06Vk}J94-M>`BgcvT;h^hDan6S*|_%&(lw5u1+6E~B=B(3QJz3!in5zQ$X zUpvxfA%Z<&HF%w#)H;F@O>mqY9yZ5gbyUETVx$xA8r88hkRlFwgcRx1Z}xMMzr0C! zrOPyRU4tUIOqXjc^l%d+#~-cPR*U*MyH30zA&J^vz`@0lDl}S2IlmSs_6?olCaQn1 zfNL(~ngU8|&Id!^fACBiJWQftymdl6F;be>tO1WMj9F`atW@Gc&R*6rKeqBrQ<#`~ z&cj8<8`_NtwyUG|S6Q?i9UTk5rKb}S6B92OKr8q%$^6}r)U+{QqXV7%2XAnP~T_(NPuqS7XWT^1Bps~#|LxEXLxSw>YV0G$K4#Z>!9x(;VSr&aOQ`7z`&F6wY`qH|hDwah_`u0KCJ>&7kmkW3Zp!?xp; zX!Sr==`ZS^g4X0KbiWf1dX?=E8Y^b_X1vU_fY^C7!!g?w_%6kOcKXMak&tX<4z>BE zuSDX(kHVm_#Ekh=OMLw^wxA|ZqYdAW(Zg7h(rHJI%vG}67iIkWjk8%Q`^7|>9-Ob; zXmKe1J0&|ojk$XS^jzKDUN*Ez7cXB|xJ8?ioJ?@6{{w>C@<`sRN|Se);j*A_cw+ea zYHOTvRHUV)B{W^^(W%kBV%}YpNgOXxy(sdKa(Lirl2EkaR@pCYiqiRV1~8PaH_K;} zD@nw#sQkULECQ`O?<3c3#YM@DRko{@y2^_9ow~(TJ#lcPs1f($r>C-hdX)^9!P{L< zKR~G3uppbb1gHFXj@{d)n~gzaaZ6=<2faL{9JFW8%VoTt;ZhA@SMuo0Y&4gm)U4n& zp7Z}|e2L)V1U!)Q(+V-wpUrXc&iOCc2>j1P1Q)q0moFczPyVQX9K+*ymYTYZyJux( zsfM~*&r3AK^c zOYjco#}^*EIA1#pgX>tHN#$_C@KeP9MTFeAoAK>a*!g=q@Tw3ES%-ipN_#&K@MH025FC#~gsgkc(XSgRSpM`~m6%jte)D%A0!qOgJ|GpCI z>n?}W+_C%BPglbCR#(+wGhRUKe`)F`Yi5Vx&2^i0fTLGP1tv1(ub`$hY0O z%yI#*7)Bjb=yQy%a*IXlX@MG)?3JzrHih!H!=vR_1J$t+6IZ3vWT-JdGq_(RlXIdq zy!%~@oXzADyWIV4$M=zo5D~Jnyf@>yg0qa5Uzdk_6$Ww^o0M7|_hGCy zlOxD@Cuvj54eI-(#)ncnFZj%+SFnj9PW~c>9>4)SQL*ozr4?n8;3H~csI-~_%gQ#a zTd(NmOT_R{R}-=Q^)q7J6#aY5Ji%8)y`Q_!1ry8>M&{P|)W(?hpL@#~)C!>ym>AsL zPF=;2{L5#LRzO!VzuGye@x&KP0VLAzB*}c_NriCX^|n~mvQED6vvQnK zIPd-4v7B^_Q;h0~`wDdeVZ|BB#IhefPvnEh{P`r-mYRHh?rkb0N+}ZV8?AJ09c+bg zm}?U;CdePdAmA!^`q}N?H3l`(-#GqoZK9@za%(iiJ)4oa z(MT;lP3dIP(bvr_wL5`vy?WckiO2dx-S4@YacP11-U$RwxdM8UNkyovLh@*6{^KFe z+Y3jI4|>ek2o_EUuS%q>7{dF&$oIpG4_ApdJ6mqiO4SsKF&7_*!jpcaCu^~xW{@?0 zDU{uy;<{DUcL;SpG26ZU{Sy8UP5qwFm6erAahQ!*cseBEXNX2|Tbuq}_MB}>7v3I* zl1st4lEGrCtCO_diL5<=8cx?Tz**&99rLaxNCS!u(L53+vrRDeGwX<)hScLo2dKPV z>LD2@-s8(07S8B^-)u=Of6{<>gJ=mX1F3Mp_=PZ6Rt3;1%|y^NIHDD3saN_-^#3 zDRA}_1__@ZySnf9uhh&Ph|T6YhM+O(sig3o=msyA(Z5z-4v5%l$nEY2^S4?lYz5kjx{ih_JhafTgP zy~v3Pu%Jty*+P%WuSGAZclyJ~FE&a+>b{K5=Jj?e>=%_haW7VLt_t8HKHpdyt85R; zHgsJ8{A9LTf2(K|&zoMLl&)DetN87f_xes^#!CJijcTeud_ZzrYS6Q15?DBdY6+T_ z`Ooo{%6IKHHiMEX$A2}8DO8hIcln{P;lrGXN(8|eWyZI`UYkqz_ z$uUOdp!p`9?29`uQ9t2i3p3|R>oL6FUYNOL(RtcThfscE9Wa)xt>2%U{b4QHYBfIB zVo+(8AXR;Md#P0<$%l-W#`IAd?o3lJ5$ke;Zz|5es;W6%y8lQmW~gDN=Jokd33+YMXI+^JKC9L1-gsxPG;!S zr}!6HjcG|t7lb)kEQ22NTD;jispO1$vcUPK;fW>pNl2Ekb@eN1(eY_cJ8SFQOXVm0 z2_qpHW)a1jsF_I~EcR;0t+re(%G3($)3-+MeTqyvq1=`-`E{1CsH+WUQQ0w^7Bhjk z5mqclQr2~W*I?VVDp_}qyvzZ_rk!*@wRtPNMG!pI4XQWNo-TH`?8yiwIP3Vi5|a-tvUi37QA<>O7_pASzBj^=Px~3Y`W-r& z`V_$k)@{T$Sz@9gPS*77U_fjqx>B8EFC3t@-;r>?m8;uc(3T*AXIVCShzDl{Bl78w zE^<%HwdF{FD&`qfZxyB8lOSZSX!l?x%0`yUcGPB=yUTU!l*jCq%apu!N)35Z={P^G zL0ML4GMw?OGQ3G5MOy<|q5JH`j~*#{=^-i1JUkK(KW6vb?b_C@umW?dKvXPxIo*i? z^dns2EPG;07ZLH)l4hqA&i4)+O|K}Hbp6>)dZTcWQnO!qofT6@{LREf{nYYFu7OzF zD-Yw}!W>8jm4>69$xKYlSOaho90jk~;}LJ_b#?I6nq%#)(R_cP*Y!|JJn-?`AKEJ> zBNcROa?c(u4ClX)p)?}la&Q<)7;Iu-(p%;ux2SHxFSpAK?1)e+kUVwOy!fPR<#$tW z1kw(VPBmj9=ly74%wN5jd8S)!5op#{h8uzWc$P+Rsd+Vywx2c=<4`w_4{$YCJZ$wp z%F*4SSE|@-QLHZ8?X7k~!O7g0F+9a%BE>fSh{@8hU%iglM|D)?Iz>-N;u5>vc&T}H ze0snKvdx&$f7W}%R}x{I;LplKrN42CfBEYrc0H76gRZuX_fJLTW!GJmvEKl zgExMzqccFKO;sp{X9ea=@2>DZHJXd-{9&K1P)R+pi);^Ca%MtYDKyV^9o11K?KL1r zAxmTmgwP6A)zqpL{4QLtPRtOlo*K+2Xbd0^c>eC)de$fwYHvYvtpin%9kV^^j`KU1 zQ+_vFZE3W8kKicVVr6^w`B(lc$_E9`hkJ%@5yYxlP`5mm~ndMP#!_H#}?Eqh%%m?Zhn&B zy31fdC3P33`WCbYF-P=(B_>DAK=7jsb-P+gAdQwtd`8$!2cL0cbhO&(zjP6NBmNdc z8M5+WFcUnrufpb_sVQ$gmRr@^YdOTtGDNFDf7tDp=&e`5n;QW-S_9Hs7KF6BVcoMXTiO%g^R=Y*EX! z$_JDzgj$xXi~T?g7gMdz7w}&$nCc)zTD&qqs~r6GvZ!s%#0CI*G>C%B4{D&bIk}@) zSi+S8m9SXUCWDXxsp;mMbTHH-)|7MNV z$iW##Esu_8ozPx@bCBr?*INr`@?B!y1x{Pkk*srRB91-3E$_8EFH;tV%tOP3jdv89 z_Q;KmAO3>{tnoHXhhB3e|15G((CWcb8ZI8`CbiT6SN5;!X&Eqsb$5HtoQ|&>IPEUm z?(sY*vog#aBgm0ImE0nrlO|x*)8(}vqAKLFAG=OCGF-jY9OTYY|Dow~aK5xgv8^bD zH(|nWdo6QSe=<;Z8{kB63*o$+#%8vHrpxfaPk z+ssD0UfxLzIhu6L*`&QVQZ?U`WDmJex(v$9N*#*(Xm00Jz4h(J{Fb+CDy-VI8IXjo zjuqVpz~@w=JCRwYmghqmi$pZ>CU8x(gojV_Og_#ja^w-E3r`Gg5$Lx9;*b6@T2m-S zJnDG;UWrzD^q^Nh%tlenTo@}AOP|B-HERlN?5$m`!~#L-;sOu1L9h_sL?9#d_EqI| z-e8h#x(P$RH;PCcE8jPa?@GpWhhs9OM6^dA`zmb-3tl>IFW| zGuX8Ht1AmPH}}G5^zII=(!f;9;1&J)Vm`F|+gX!~g%Mk~=uCzhs$BSA#as`9O-00J zo~aVs;=&Wf67F?|yUlHm!=c`uL5{6cr2?DTMJ;m{%D(_OcqHFu$V2In zJ}l#_xYNgAyEWr42-w7=8ZCf5zdUaRelNh>F$~I)WF^d|Ri@aEEQ7?Shy(?vLYKjy z3ak7TLb?#@qdNe>^bRHV2X4HmRTKXGt66@rMCCWT`Ek-$Q$6hsEKhAtf9|``TbN2U z!Ntf+q^bRj1-c*a^cU%QOiUi(2sW>lCbDX+jpvJT*{3fxw9qfFtqfZ~EsNFJYE+HB z#A%03(855w!Qqr7K7eU|<7Ah~OjU8>=T;nFuj{033O5CEuz zPIAZlRz2p?h{DC8*rmJFF!C?(%M>b3jfl~zF5FTX3ojz&C=#-6%R5iz8?;rbjaxsu z`;a}EnRZ?X?_oDfar}PTRPiaA={Boj&&@A{J4>$7ft=2rd6LsfFf06cwpqSQAqlb% znz!;vkQQt__rbSb9V6p*(uS>$E#TKeLe#IAtN)Q}qjS23aau<)q_D|2QkLmex%lxB zz3S?4{11+5(Y)<}jczqw`AxnbNm(nA5qGp&GpbOKXKPxt<$YbLIu`u!@ngTqrfQC^ z?fh@6w`V=)>7h*rnpc#i%^bS%^m4_GN` zC;*tr#Ulp>s6=C(#Y-TQzYYHCBN6WMBC!C9ow`;|@n{aR>zlcX0c9UiRvri42E%ft zmP_K?hj*IV2Xpk?FCxQ=?A9a=REAhcIOAe)ULDW!IQ9%I{1B92Wo#_8`o*;~ReU3< z#qnU#@{k^##h_%)`ZcO=Wth$R=!q^M1RDXP%ldMJ9h^<=Q$0PC-#^wzmEqa}e{{LI z7HWLa*+km@%Dzvk{`KEG`HkdiMl4S0voEz>0^w}^9``KyO$XjD!Y3iwpQ^7b+vNS`Em6Lgwj#u|oUv@lt9Pt zA?$A;jN_1s7r=e0`I$qfe+=S^TXmqR&1Uh;>*Bcz#p(M2X8&fw7886Ktv!7<`%oeCy>8{@1Z9&oeM402hFT*w+#sHdB->D~pDhkRL9y zOuuj!+wd-;KqLd>4dh>N(=)>gi_=8_;W{!S7Y&v{ynK&)(WR(UR{aYEKd#-C!M6b+ z_5%(Njq1cBk@@0RS}oSsO=k%Pfj@XwcLd{{Ur)J-COc`-^x@*+$vL&b#?t;o6IuQ4 zPq}*(sc@x+anku<5tz_~ziw~KV84GR5vS*{)whBvhTKvLY&Vw513bc4w8QDlNLc&ExTz!suxDjO?s_*A4;Rp5 z3f7ov%Jk9WM}n<(lt`bEI z*v`_oG1gd&5SOhfoZ~sVa0I6{nQv{0a6Wm?CpmuZ3z4l)L;=MMw#l-Eg|Kf|z8C}~ z7qzK;6ifo=D`g^=N)}ISD{EC|vQvoJqO?vF^2=y<>C~T(01d$U+ma5|Yv#IfMDpO8;|9#)5|FB+H^@*4t znaf;%xW6jO=m{kq|JF93s7-s&e?Y1$K^Tw}Nu7Ezv!z~V8RX$&70*}|52t#Gzk==g zrukMl^$-D}M2OVgCydQ(*<#p^jyxd~wKTqcxW=EO{z%JX26g#57amTfk(8G{{>K;3 z@knJY8rPi_Fk@Y;x#23U9Jd~JLxSUoiYPtG9YB_G) zc(`v?b1)fCX-E@dZq)tAv8>>ln?7(%Q>%ZTA)99aV!6;boojX2S#zqV)MTszIlG{Z z%u3K+fSp@S=mNX`_rz}|gVibEv`=_!3PjuQL0tn-13<2J?^ubJ$eSx1+K~lb&k)AL zLmSJsr>V@h8q#30-R{0Y;U@PT))cKbzIp96j6Uce+afC;8&54mc_x(NJ=smhgT372 z_&Of55GLkQn(!q-IShM`L9OZp)f#bll@ynhM_3?&j2BO8Sf?>^0jAlqn~d&`9at|{ z1@I07Lor@``t7c^h2``P<^(KCJ_nDaaw7TT38q93lTEV_LFy0&jYJ2^%Pol_H<5+f z0rAqwLp#bb8&7^x4XDSX*OTq3(4BpX z8x_Hlw>H#i`(^%4VWp>eHvwQsG?JO8zNXj2aZVboH?aaFw*h-(G3<+Gj?oa3SQBi! za<;Xcu5?;hTmBoJH!Db}*}~9S3=}@ofc^UV1^}ypIS*pA*q%3Q9I}Vcv?e+vp8-^K zvKl=dW4KGh)E&mx?MR4`dn>$gt}E<{XKIz(|EN_=l345xcQemvt}k#P`4IrTSYKk{7twT9cAG^C^@a&upATkep)E*<(IL4;p*~#|Nbpx&7PKMdfi29 zd2dOiUcgdQs{3tT*u0C*wC+m|hgf10#!i}kkdGPGR z#ZH`48dK{_7A$PGrOBcODM!99zg(XKb{*|zqB5nV4H=(6t>#zCG;>2%r)LQIGoal`Y4Z6TlOu9%Bro?vs2|xSAm$0N zPj1h3Crh011?z_$y}i9x0v^A8PIz*xeW(Vt;gR*jOYvvRLdnIO0rA&!%FchV0QlMe zmqj-lBrYF6M60e>Uyo(t_n7&FJ@ZGY@tGM|Q;;fmrXkw_ZFj2F?HYKN8FlAee~I|% zWrJHoRJWi8|nqpT_i|D*8L2cb~a}>5K@gHselZ3-1yt zFuaeJmc}|;Nuc3ONH*G7=?Xe))YU@G@Bi2L5% zelQ@HozUm*>I%7756C|uxq0=<%yQ+op?rK^S&GW6T@foy49~Rn_C9s|qcFFt3tGs& zML;3DawMn}-69UhvApjjh$tqiD}pG@BRGxlt&u2KrKZB+=BxowVM8+19Lkx$$^uwO*zmsD>9aawWBG zTLKv!f!lJ#bZBe@#Jibq<3jOe|H&8qihYYqI}Vrdg6q!|3=+WwfwGcef0SU^>jv~6 z3XOkQef)ax;FUk6#{PD@AE{_26|A2zoDvP~a6#z+-k3)4h_mFQPQTt>T6A-I#~LNj z)WT9sam-`8nKAw?mps#eNSnH&aQ-!+v)qznJF+31JU-kh-oKqi0_1<3mSmcWea0w5 zYeE*ev*nMduFWpJ6zp0Dv5;Q@W#Zp+P-n-KBoRU-SqsXumnjn8lJ_j9&(oa%MrE-< zP^1yDqYL0H$RzR@Q2H`fI!RknEm+Jy;;) z<}SRBgrz`2GS!Z{oK{C&uY{W*o2lZoGt^pHR(QqvPR<|o&k+N%w425YTE?p>Cw*N|e$r>s_08&O{PQJt zTTWJkZ|C1|O1^^hLM9;^52yvSqi!uG5WdFkUmZ@rGf-^)X5t1E<#{xhQ((4QQRdP% zNKN|LT7$F4avbZcAkK=%tT-JD@SVXlrA3dzn4`eN0p^P_&QtFks<+P_3B!5JcWYs% zgQ~bS>NMoKRYoBfn^_L?W1&Io4h7q*_NP2t>MaG?{S*?!k2Q}5w@&;6D8(YOU8BV# z`OmrDpXF-I7ak4nbokv+YkDS8t)J{5J;uesp1JdT)cNcPVv3-&_TGp`E&cutLe7lz zbWY8gne={R(1V{a_z+4W=V-VH=99)a_wZ>X=>YzNoYo`9VzlW1WcE!!f^tCPE4n3z=sYaG&vVcP4fNHP4!gB-Msu%wCV4^q3Xg?*PC^c`lJrM=H1J1z zBffo8EaEF+*KAu5^86p~%Kch%bg{H}C1&<(CQau2S^vy} zfN=h&Bbxv1f>hJhiOK0^xvXm2rCr#SyK9?GzC4*S^vEKswUZy9{uTe0Tx{OVgn{_0 z{#-|Ey81IU|GLWq8RM~{X8ZYZsRo^JK{Uiq?lVzs4f{nbtZc<%%b@-_gMJf01}a=W zDWaJ(58nwA4SAFVsUq3gS919sxv^s~u2lmWXn6PwLGgweHfKFhZxEDt$-{oWeD+~{ zfpFmIff~JciG9%!f{Cs9e1+MH!0QfaZtlwPUK$z@Tw$FlbnlALN1;GAEGVdR7Fm^8 zWR@hNc=nEdHDk|;3Li8E=os`FZE#H5A8Xe@S6)!MMz9)CY#mXM3>Q+VG^iYiLsVAD zH(na6z6#{1#bI0Jgd!$7Q?pMQBjPP65nFVZ@_W>6!AZ_|3*Eb0if z+_AvIjoCjr#u95yZ0^_GR4TTFb*nGrT?(xTpswg^IRhoS3lu%iPM17ebojg*e?D(( zY^OL$rM=2drWHpjF+;JkE4tEML=m5vv2kbOdpyz}j|UIOpZ^%sha0sgKwigYI2J2h zP3}QJuNc?Rs%(u%$`Sq0G!7)^prMJmk&U*W(w+*sQm2z6j&L?Gd<4-1(yJDHgB)>P z6qrCh>9)Xojx2NptzoyFtf1Tk=*j=XX`%s?NaEmTxX#?4HQE)JEOAHMpB)Z@*V_O- z*8vy|x*AwZywBvnFf3+u#C@Xka4Xk+c`-Z@9=3;*(KKcGY#~)P zY_y+YI1AnSO?zZK9%*5s^G^Dx)*$BLqrvGcwI??xKmQH|bc~Qa?-YWwR*$?iHJUx7 zIh80cko@sdRxx}ar{0&VL6+OmJwV!RuX%OG+#{xiGGeXDLY6vR9fPg~qK6ArAhi~U zV^O)1Dq!zMJ0fbuwuif;U`OaRGe{NyVmc^BuIg*hD|Yl%!+`>g_lJ3K6GUg#+gan? z_QZo(QsG8P{_mBuA^u7dNz6NF-9#ZPGF*|Znjo$Q4Nc%Pr7-XjG^{gr2h&HUxgrxp zjIsxPvP*6KUz_+Oq#v%mwa#NurTe|;kP6e%Dzz6NaiePy>rNJp7=2<8f|sdU<-lsX zsA4(${=Gy1;SCqGiTdLYh)6c0aHqV*wo6%((>g6-JV^jE!M-0Yxt8P9`++4Dx6gRMYHt-iF%(w&H~$Pyow-nK zS)wL!tFS1J`XA=AWe!QUV^#Lfi`yKjdaEOr`5}U31#XLCK5kz_mz@_DJqfbU_LpL# z!=4uSKiWd?MI|LU1z5$q?G~k~(Es2K0!mCKtJ9{jHWg}p^OX@^pri1cLxL+0?tT9b z=%?&(1F3^XJh)>gD1TZbf*q4UB3^2>O0XbRyr2P~RUp}Hu7#zB`w+lMi6f1h!@bbbna25C)W0$6Ka7o z_6VYGcp|w6eqVR@QumKSooYXkf$zHds;yx^N&p?yKiHV=FjUEV*{LZUQ025G&;%{x zeX~Aae6wUGM=C0xn(qH>+Gxxkpp%CWDw$qZ^xFHp3MUW=V)_NlS?_lpM9o#tYR@;n z3jd?dZ)RwxBVs}EAEBRo)4$04R=Z*gxta@Je+pi)Bi`Xigl*=K`20YMv-pw0P>J}y z=VSQ-`pU*;$L)0szBNZf~r8QAp zDB3>Xt~S>aX7tlnb!Vw35cT1fHeh0!?n@rML}|VHZ2Af&2d8H)fqiERTl3aPCaI*9 zJ1hoL@=2G1&V+IaVVT+*j>omJmyA%`x@40>REzjIHxYcq`xx^Ga9xG1RdG3Z0c8#X z^4`s>WWyS|p-Q`HC^Zy2Z_HWZ7|T$sG6WsZdoI7w(lK(?X;dASlH=EE3cYun!Qpwi z2JR_hJsE|8wr@HSU>cz9o}F8qo_4v0u^LzEozVLG7d+vE>ipOxMS61(T?e~@$um9Z zPYhat4%pK63gHLgR+;_UTrV3`U zd7ZiLj=~`L&cxP~cUGxt!R84!Yhn1X1H9h)-VMAGvkZV4go|DXh*#OIXwSV`INc0= ztM5;17*&RTXhr%oZvY*H*ME8kn&k~KMVJPI!W0IF&_8GgL8j#@BTT*R5B`NM_HQcj z*eM1?dgJcQg1Jv^)~DNJ(=~T@=Fp}k3UuuXf#muQ-OHY9BZ#go9 z8v?S+N1pf|NnV;tv-V$acbRwOd}rCk!OHcPkJJ6y#OJoK8zRFA=kIP%WT?Vh_DCv?MmFJb;~I$KZLcoS8z7L;inJ^#!H^9FxOWQ_2j3I zmyCKvph=BqX~aBIWQu~BNC#74K-5*)?I@jB2S*F)Tf}dKlbJ_G02$qcx=sw>q1b-S zEXCwcNdlmE^|xGr{t*;Hf5po?@1R;wJ_?S8WTY7x1b{4$ypBsgBcI-Y98IB#E`nWQ zwy@FQxF{MqTxoj<^N`rJnr4`axYUIh&L6}#40UD%h?SVlmbYs^wFFQILmmdKJT#i_ z?RYE(NI++ngzff|4)B2$j>L-&uT^@jgCod^fWe^e?yJqY;QDJH?X6ZBHPiRTP?q{| z>O{9b@jOFq5&PZ0v=Jb^rvbT-0I-f-GD^XP0r$q~mTnp7)1%br^pJ;;GmNXme%+&2 ze7pnv1!TT-YJD_r59;nPwG4lYUOA(gdzwSvwJrvMC=n#uYUE9WnaZW#G{5|92J+dB z;lWbieeVP(owyju9gd#G4$B%S4Xb*VmJGb}oKEjG<$nO*Dk%9W9zL8|;-{8=2p-V4 z$BUmpQ(~c(C-u)=r$k_lRLSRyduq}43lA2Gp6((ssoo|}h@7Ew7mUY)KD4;euZ&bX z_ID(9DbUP*VN^T~opc3Fef7PjE!I5K7{HB(gN@^G zf0ivXd<3V2S$E*4PJ6Ukm02YSY=OE33dj~vx?M6Z3R64K8PB&J5@{W;9x|U_mlJ2M z`K4{(kl_2r21~?nWRGPC)n+D1VQYyficG>0>CYe3;i)kiB5ZUZmMWbb4kqv`^Txe~ zP5s%@`N9tltK~A9!_#d5WzdlzvYg9^Nz#HIR{~xUClj3-d!+$Qfix!}hue+a^YaW+ z9I&fxPi{wjQMsEDnd)Zud3LU$s!sjjDXCp1T6 zodqemW>?FQT=qk0S60y}^7Cf4E=6CW5ERdY4v5JAv*hA`i6fwM@3y~8v%Hb7@MWUu-bYgHSbe^o6E4Gh zp#475Ve`QE7dpSSXO8lBp!7V`?c_K5Q~1?W;I1?W(qyHwuwhq@u0}4ztGRQo^$&c$ z^7{N2rd}*xoJbH32n0knR!S=O)ioN=n?-zzE;Sc;7YWdglDtM~171KqX)?;Thl^*SM3>l=b>o+#+WeYwYK{ z;Y>kOlzwT~G4dSUUfk2b5kbg}H#u)*wuwNr0CGbj@OPItch=_ABaeY(!adzV?jZik ztsBF71PzY1)gE0E#`VK7Za~>1>#x>i2Mrud1=i~8h7Xgo=RwNBmR>)Q z9%7K)7{cB5V354|bpKfGA=N3A0S*v_OXH8W%ct_r(ha|XY*GOn9bJhB#soRhikWJ> zwPoq;>JaXrl88x<9KqP3On*p^idkUKX;+@&d2Z|NSLLV&7kTpaI}Kjl5mr`GN+Hjm zV*)MMSXnE8R`&}X{GpA+o0otC3WiPWjmv4?l&<4?kEib^fIb(hTI~G-=ThR*xL^8S z6@+M#Aj>m`-5Gl8wXB$ry?2P>e_SCz5QcaaABWRQe1n$v)NbpfBGP)KB9kQ2e{F*QgiiCfG{L;U*q&9L7h%ye!qPeYCjy&!jQ(Kwu~Z>znn6<#g{ zBdBO4WM2ye_3hQpK)z7C(!6FF^$7UX%gyn@cLek23-l^q8(`Mf5>}yDfFk9@M_tXi z9BL}`<8+`1sXIqA}r0_3>uf^VblykAN za!!;@-p#8|PufP&!jIW+V>i6P-~_9!R`t!_!oL}9^ZQG>Vr zl($2z76f$SmlD>CZ1}yVzmsdwsnY>TK!CVDrCoLvu!Bpi$fP++0TuGl;k#^_oJU63 z{gKG(68zm@j85Zug34fsbL(Uo=KB~$%uj5b#;C!LQj)7d|3Lk3D_ zdaCoEd)sG?3h&MSIHrvDbze@AyQiOP;0CaJ6!yrD%o1>-Xj(LcEtLcJz6bTzz1`2S zn(EM>^WESvEKuX~slP2yh-ln2imZ$O^l0PMP046JS1mi+(OHQ6q5>oj2WV8&yE9%3 zyXHq$6u09d-dfp*H+u7Y^^1|U>LuDidP8r@Zz*&4_~wGFo-^M>rCB8)F{`OIUnaIY zTwo*1K2sB$aF{LeNZDMuu0JqP7#iDY%(efFYh%P^hUhU7oV9woVpdoTSN_K{leUxB z($PTd?Ndo8A%ofi(r|9#8DLVR(Jj4ujUpQ7l4Lwks`JD?g)s=R!9{ea`rP|?&k02f zbjFB;t~XEPoC~hsjnliG@R=WCGYYf1-6=vZJi`iyTmlz&MK{O^VpcI4zSbN=SKm+s z+I8ONAY2L7eQ{g|Xk{kJU_jJq}JK-_^dUbO?n3 z2(V?mKDyH@miw}2B`ykx7b_z`zR_qSx#toAd+P=22{owZcm64o*z-0g@4_hLpU7ID z#m#Su>oZ?8hGLd0Nn00hHOCtE!7D!cODA?Wl4ariVFw=v{KPqNvs{@vpZOXs5b|q? zx&ma5`{51_pY=s|sMrW~!Yd+;skzA`yzp$im^lYQHioaU4}kC<%vM6TXQm3nS-R^V z%c^qS7WR>jS656fu^BROyv1CzjqqoHS?ih%{_2r^xRD(=UN1RhKotbuJGwbEiLb#5 z>nn=u2#ripsQV%Uctf8uDsVFH!@OU$7g^{)?GZ-{lnlSl~ zh4F^H>8O%wXJ?zx2G7&k@)!2&=V)RDuXtZ|;Vdhg%3*)6gbh$rL)xA+5MMYs36UP* z3HOx`k!CmCp3YAO%-aeOi(*c9uQ)%)yo`v*y&JWc>=>h4j{mmUT|Gs4UlM0~T43QM z<+0`C>N(ENhsSd)vCcc)f)Q*N2Jm@}^_^MkbAVRv&&rnzTdIiu-N2dDK4Y$kwi)?G zotv@n5k0l|0$8^h&^W>x< z9p6uP_^hrBE^;J5^NpI_!1ZwlnG9u5NJRTnH&f5_Guj?9K|e(NA_&%4b$_%ht~Bt>7YPK=8R zc_0k*H=xpb-9o5aJZ&rRTJqKs%B{|Bop2oD&l0RTEcXQ#c*T&vCfYk;RxE*w!41O- zET8^}g8#P3{-yrdK#IYy?_Z;`GF(j>`r|}m!}pm5k$W>E+bfeVj$X8bQ!finJvC;B z8Q=~A2Zdura$YjmYx*T4ZQ)zKn05ob2{tt4`b%V`hSAig-W-jLxBDL7W%( zBUwZB_;qvr^P1{C@Ka!WNN5aAlA@+093Mt&R+=4q5$n(i&R1dMG>Fz6jPX79*{h5Ay^uR}bM6 z8#D?d1(%|iUWvy0d&;}AdwV+x7?(HG6WkZ*(|;PvMFi&H;;;Tl*Z`*J!{2No8*deT zC=1}`P+YACga=WI-s8n?6qEv1>A;)jpiN4!V0O5q&3??MiR}qM9?^X7#^{xwj7mSd zaQIBsHwi_@cTQRA3ni*ItEdAjj`eAeDs}ms7NIe9Vy5fCRT?)~+yfpTZ3HNYeA5a5 zVcJ?i5#%dIpR~wyr3{x>`wp8&V)_0s8_uWCt9BfdYDpHl{~-6A*TOi@WSzW*3&7zT zpE6tsu)#LCVujQd&&35x3?l*!4gjC*$nsShiLjs^m|e-kHf4uu9uA!F z>54{7RkTSu6&e4b%*0Ay44_r3Uu+g(7@*Mid!}c3|4hRLL%l?r{BqE4myZy1?qK19 zI8Gq6)D}DZCC9{j;17dZz98L*fmoUUd@_nYeukieMu^c_P5t{My*7rKtD$8hc%iR| zQ|6@q@Q`l68f1?d&E5-52SI3N7fm4pOGE^Zx7UgQ$$A9!kTq(wB zuvlXZR$wekL+GBGvq^V0q*kZ9ALt`Ii&r6jQJPBCq>HJQ1yD~tPls6T``2JmOknnU zLpLY@7I`P#<~{eLG;vxqw1NTH{LeL!WvdecHchvc(lx(J+pCZcCPs;cD!*=)`d^ZD zRqgsnd}~N=Y181jGfe$p9J`e3zeC)={e35Jxs`=AV)m7U%}LsDj2xMAH~ZpL%+X{J>^7*bGB@UnHBZhyzR5v&uaN1IB?Gh2 zg=;oVT1mTuI^o$qf8%4=Ce&-Fa^rEJTCJ;MV>A=&$~HZubgJT z#HS=^dqd@C7Sm~!#|THt*Ccd`m{mO%?^vnjkh8uAlHaKQcA~TVCbyC=v7$KD^8=t+ zVrdVl-2u>uB}GU!cPGK2-LT0i+_=br=qGl;SJ9I&TmlGemsM5q%ZD15GlT$*SF$=9x(BpPE@ zke4^qA+T<^nmA+V*zZcEkFcX(iGlb>RTL}-s9weF5a(DR>BdRC3X}Qz>|;aR8oc2# zTjp+>*dov?=XSL9bMJ})=sIgV8CeH>!%Vf)Ayv7)v7xQ)-od`$V6gP-r$KMa0&!-Z z%EB3!(Z=S&4XKo9%YVWc7RInG<*3vYL|6{}MP2(JO`pKgoS@>c>+S846mi#WC^A}Q z1=`l+S9zvRs|;n#trg1CpLEKdV&g}ZsS7NBcx6qaH4Yk7i7DETrb8bb@QRvc2wp9V{Z*TKexlOuo~TWA*QJw8GnVHz}TVuxl4#| zZI(eXN|!Q$_2UUV>}Si?;POpMTz?#Q;axEMj@|SGjfAO|xSu(A)j~?UJ#{+m^7Paf zRIBguc*AXMdxh=QfxP?|G8BYUTx+|=w3TF%Y%#b06|1+Rf|W-O)pOl;+~5Vd^kiSB z$|@dUAYoPR(A-oiwSC0hbiFAa7}?kkqkdpkxg=+ zrIHSW=_o@52=76Y<2Uz+sA1K>nA#o>Ghw z`BAmk$L99-9EWS4{$4eNC62LQH)@~5rY=`2r$K_Z-0621yP=5iZ%ynDVZ=*wIBe41 zPWgUIEJ9+CpEd>k$+pb$uGfSmk_>VmHuuUi^`v#BDm*(lE~XHE znq2L&DB0L+t>an!QIhUw#7DY7qgU=ZVc`;o7A2w7e$&Y`XLg$0+t_A@e~+z64QJ($ zc!a;NUq1?$Y2y|f)-y4o|8E@I3e-XXH({i6UGAssxTiZBANQ@ULHqd3DPHSgwu-Yx zowGYyYLUOz2zj2%#9WiUqaa2^=mMn!bF3eNE(i5y*y-|39Y%p-y6wN*PD-utZ1`~< z4VYUBfWDDPPWiK}tkGg68J-`I30G(PyDqg)@XfLKj>cOMqE|NSMBZeG#n8V*_4PV0klF5Bpq zl4tVxp!P$aKgFupoD&>DY1IK2tfSL}l3@5_N z0RTun{pvU9xHq;r*W5?LvXt@;VR!4=V1PX%?F4{sNIY|;Z0}F#7i2Vr_BXzpJE#zQ z{kfh;(ZIg^yUr7Q^Xg7|nqp92<-u1$U5uzjjgj5P`Z~b(od7@p5LRd)+~~KQ;ZU7a zAPUi9<4+B-U|YgFb)6xXO$O%;KLsr kq~L3W;fE84gZ?qP`*eEUK4-Q5TT7{-s-seR`~LI)09AJ`AOHXW literal 0 HcmV?d00001 diff --git a/img/screenshot-mappings.png b/img/screenshot-mappings.png new file mode 100644 index 0000000000000000000000000000000000000000..8abddcc02157c09bc4a4fd0d7f8bd38c33389095 GIT binary patch literal 50824 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 literal 0 HcmV?d00001 diff --git a/img/screenshot-sources.png b/img/screenshot-sources.png new file mode 100644 index 0000000000000000000000000000000000000000..7f4efbecc182cc5f92d7b3aa047eb5b49deefd36 GIT binary patch literal 63300 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 mYIAe>&mA14%Cl^jTiR!A_DmhBGpZ*Js6k{a- literal 0 HcmV?d00001 diff --git a/package.json b/package.json index d886a92f6..830557c72 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "openconnector", "version": "1.0.0", - "license": "AGPL-3.0-or-later", + "license": "EUPL-1.2", "engines": { "node": "^20.0.0", "npm": "^10.0.0" diff --git a/phpcs-custom-sniffs/CustomSniffs/Sniffs/Functions/NamedParametersSniff.php b/phpcs-custom-sniffs/CustomSniffs/Sniffs/Functions/NamedParametersSniff.php new file mode 100644 index 000000000..4e21f8d35 --- /dev/null +++ b/phpcs-custom-sniffs/CustomSniffs/Sniffs/Functions/NamedParametersSniff.php @@ -0,0 +1,408 @@ +method(name: $value) + * - self::method(name: $value) / static::method(name: $value) + * - parent::method(name: $value) + * - new OurClass(name: $value) — classes from the same app namespace + * + * Allows positional arguments for external code: + * - PHP built-in functions (strlen, array_map, sprintf, etc.) + * - Nextcloud/third-party method calls ($variable->method() where $variable !== $this) + * - Any call we cannot determine is "our code" + * + * @author Conduction + * @package CustomSniffs + */ + +namespace CustomSniffs\Sniffs\Functions; + +use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Files\File; + +/** + * NamedParametersSniff — enforces named parameters for internal code. + */ +class NamedParametersSniff implements Sniff +{ + + + /** + * Returns tokens this sniff listens for. + * + * @return array + */ + public function register(): array + { + return [T_STRING]; + + }//end register() + + + /** + * Process a T_STRING token — check if it's a function/method call to our code. + * + * @param File $phpcsFile The file being scanned. + * @param int $stackPtr Position of the T_STRING token. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr): void + { + $tokens = $phpcsFile->getTokens(); + $functionName = $tokens[$stackPtr]['content']; + + // Must be followed by ( to be a function/method call. + $openParen = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); + if ($openParen === false || $tokens[$openParen]['code'] !== T_OPEN_PARENTHESIS) { + return; + } + + // Skip function/method definitions. + if ($this->isFunctionDefinition(phpcsFile: $phpcsFile, stackPtr: $stackPtr) === true) { + return; + } + + // Only check calls to our own code. + if ($this->isInternalCall(phpcsFile: $phpcsFile, stackPtr: $stackPtr) === false) { + return; + } + + // Get the closing parenthesis. + if (isset($tokens[$openParen]['parenthesis_closer']) === false) { + return; + } + + $closeParen = $tokens[$openParen]['parenthesis_closer']; + + // Check if there are any arguments. + $firstContent = $phpcsFile->findNext( + types: [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], + start: ($openParen + 1), + end: $closeParen, + exclude: true + ); + if ($firstContent === false) { + return; + } + + // Check if all arguments use named parameters. + if ($this->hasUnnamedArguments(phpcsFile: $phpcsFile, openParen: $openParen, closeParen: $closeParen) === true) { + $error = 'All arguments in calls to internal code must use named parameters: %s(paramName: $value)'; + $phpcsFile->addError($error, $stackPtr, 'RequireNamedParameters', [$functionName]); + } + + }//end process() + + + /** + * Check if the T_STRING at $stackPtr is part of a function/method definition (not a call). + * + * @param File $phpcsFile The file being scanned. + * @param int $stackPtr Position of the T_STRING token. + * + * @return bool True if this is a definition, false if it's a call. + */ + private function isFunctionDefinition(File $phpcsFile, int $stackPtr): bool + { + $tokens = $phpcsFile->getTokens(); + $prev = ($stackPtr - 1); + while ($prev >= 0) { + $code = $tokens[$prev]['code']; + if ($code === T_FUNCTION) { + return true; + } + + // Stop at statement/block boundaries. + if ($code === T_SEMICOLON + || $code === T_OPEN_CURLY_BRACKET + || $code === T_CLOSE_CURLY_BRACKET + ) { + return false; + } + + $prev--; + } + + return false; + + }//end isFunctionDefinition() + + + /** + * Determine if the function/method call at $stackPtr is to our own code. + * + * Matches: + * - $this->method() + * - self::method() / static::method() / parent::method() + * - new OurClass() where OurClass is from the same app namespace + * + * @param File $phpcsFile The file being scanned. + * @param int $stackPtr Position of the T_STRING (function/method name). + * + * @return bool True if call is to internal code. + */ + private function isInternalCall(File $phpcsFile, int $stackPtr): bool + { + $tokens = $phpcsFile->getTokens(); + + $prev = $phpcsFile->findPrevious( + types: [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], + start: ($stackPtr - 1), + end: null, + exclude: true + ); + if ($prev === false) { + return false; + } + + $prevCode = $tokens[$prev]['code']; + + // Case 1: $this->method(). + if ($prevCode === T_OBJECT_OPERATOR) { + $beforeArrow = $phpcsFile->findPrevious( + types: [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], + start: ($prev - 1), + end: null, + exclude: true + ); + return ($beforeArrow !== false + && $tokens[$beforeArrow]['code'] === T_VARIABLE + && $tokens[$beforeArrow]['content'] === '$this'); + } + + // Case 2: self::method() / static::method() / parent::method(). + if ($prevCode === T_DOUBLE_COLON) { + $beforeColon = $phpcsFile->findPrevious( + types: [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], + start: ($prev - 1), + end: null, + exclude: true + ); + return ($beforeColon !== false + && in_array($tokens[$beforeColon]['code'], [T_SELF, T_STATIC, T_PARENT], true) === true); + } + + // Case 3: new OurClass(). + if ($prevCode === T_NEW) { + return $this->isClassFromOurNamespace(phpcsFile: $phpcsFile, classNamePtr: $stackPtr); + } + + return false; + + }//end isInternalCall() + + + /** + * Check if the class at $classNamePtr is from the same app namespace as the current file. + * + * Compares the class's use-import path against the file's OCA\AppName\ prefix. + * + * @param File $phpcsFile The file being scanned. + * @param int $classNamePtr Position of the class name token. + * + * @return bool True if the class is from our app namespace. + */ + private function isClassFromOurNamespace(File $phpcsFile, int $classNamePtr): bool + { + $tokens = $phpcsFile->getTokens(); + $className = $tokens[$classNamePtr]['content']; + + // Find the file's namespace declaration. + $appPrefix = $this->getAppNamespacePrefix(phpcsFile: $phpcsFile); + if ($appPrefix === null) { + return false; + } + + // Scan use-statements for an import matching this class name from our namespace. + for ($i = 0; $i < $phpcsFile->numTokens; $i++) { + if ($tokens[$i]['code'] === T_USE) { + $usePath = $this->getUseStatementPath(phpcsFile: $phpcsFile, usePtr: $i); + if ($usePath === null) { + continue; + } + + // Extract the imported short name (last segment). + $segments = explode(separator: '\\', string: $usePath); + $importedName = end($segments); + + if ($importedName === $className + && str_starts_with(haystack: $usePath, needle: $appPrefix) === true + ) { + return true; + } + }//end if + + // Stop scanning after the class declaration. + if (in_array($tokens[$i]['code'], [T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM], true) === true) { + break; + } + }//end for + + return false; + + }//end isClassFromOurNamespace() + + + /** + * Get the app namespace prefix (e.g., "OCA\MyDash") from the file's namespace declaration. + * + * @param File $phpcsFile The file being scanned. + * + * @return string|null The app prefix or null if not found. + */ + private function getAppNamespacePrefix(File $phpcsFile): ?string + { + $tokens = $phpcsFile->getTokens(); + for ($i = 0; $i < $phpcsFile->numTokens; $i++) { + if ($tokens[$i]['code'] === T_NAMESPACE) { + $namespace = ''; + $j = ($i + 1); + while ($j < $phpcsFile->numTokens && $tokens[$j]['code'] !== T_SEMICOLON) { + if ($tokens[$j]['code'] !== T_WHITESPACE) { + $namespace .= $tokens[$j]['content']; + } + + $j++; + } + + // Extract first two segments: OCA\AppName. + $parts = explode(separator: '\\', string: $namespace); + if (count($parts) >= 2) { + return $parts[0].'\\'.$parts[1]; + } + + return null; + }//end if + }//end for + + return null; + + }//end getAppNamespacePrefix() + + + /** + * Extract the full path from a use-statement starting at $usePtr. + * + * @param File $phpcsFile The file being scanned. + * @param int $usePtr Position of the T_USE token. + * + * @return string|null The use path or null if parsing failed. + */ + private function getUseStatementPath(File $phpcsFile, int $usePtr): ?string + { + $tokens = $phpcsFile->getTokens(); + $usePath = ''; + $j = ($usePtr + 1); + while ($j < $phpcsFile->numTokens) { + $code = $tokens[$j]['code']; + if ($code === T_SEMICOLON || $code === T_OPEN_CURLY_BRACKET) { + break; + } + + // Stop at 'as' keyword (aliases) — use the path before it. + if ($code === T_AS) { + break; + } + + if ($code !== T_WHITESPACE) { + $usePath .= $tokens[$j]['content']; + } + + $j++; + } + + $usePath = trim(string: $usePath); + return ($usePath !== '') ? $usePath : null; + + }//end getUseStatementPath() + + + /** + * Check if the arguments between $openParen and $closeParen contain any unnamed arguments. + * + * An argument is "named" if its first significant token is followed by T_COLON. + * Handles nested parentheses, brackets, and braces correctly. + * + * @param File $phpcsFile The file being scanned. + * @param int $openParen Position of the opening parenthesis. + * @param int $closeParen Position of the closing parenthesis. + * + * @return bool True if any argument is positional (unnamed). + */ + private function hasUnnamedArguments(File $phpcsFile, int $openParen, int $closeParen): bool + { + $tokens = $phpcsFile->getTokens(); + $parenDepth = 0; + $bracketDepth = 0; + $braceDepth = 0; + $atArgumentStart = true; + + for ($i = ($openParen + 1); $i < $closeParen; $i++) { + $code = $tokens[$i]['code']; + + // Track nesting depth. + if ($code === T_OPEN_PARENTHESIS) { + $parenDepth++; + } elseif ($code === T_CLOSE_PARENTHESIS) { + $parenDepth--; + } elseif ($code === T_OPEN_SHORT_ARRAY || $code === T_OPEN_SQUARE_BRACKET) { + $bracketDepth++; + } elseif ($code === T_CLOSE_SHORT_ARRAY || $code === T_CLOSE_SQUARE_BRACKET) { + $bracketDepth--; + } elseif ($code === T_OPEN_CURLY_BRACKET) { + $braceDepth++; + } elseif ($code === T_CLOSE_CURLY_BRACKET) { + $braceDepth--; + } + + // Only examine tokens at the top level of the argument list. + if ($parenDepth > 0 || $bracketDepth > 0 || $braceDepth > 0) { + continue; + } + + // Comma at top level → next argument starts. + if ($code === T_COMMA) { + $atArgumentStart = true; + continue; + } + + // Skip whitespace and comments at argument start. + if ($atArgumentStart === true + && in_array($code, [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true) === true + ) { + continue; + } + + if ($atArgumentStart === true) { + $atArgumentStart = false; + + // Skip spread operator (...$args). + if ($code === T_ELLIPSIS) { + continue; + } + + // Check if this argument is named: token followed by ':'. + $nextNonWs = $phpcsFile->findNext( + types: [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], + start: ($i + 1), + end: $closeParen, + exclude: true + ); + + $isNamed = ($nextNonWs !== false && $tokens[$nextNonWs]['code'] === T_COLON); + + if ($isNamed === false) { + return true; + } + }//end if + }//end for + + return false; + + }//end hasUnnamedArguments() + + +}//end class diff --git a/phpcs.xml b/phpcs.xml index eb935f625..f7b530602 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -151,4 +151,5 @@ tests/Core/Tokenizer/StableCommentWhitespaceWinTest\.php + diff --git a/website/docs/administrators/sources/source.md b/website/docs/administrators/sources/source.md deleted file mode 100644 index 7c95db835..000000000 --- a/website/docs/administrators/sources/source.md +++ /dev/null @@ -1,208 +0,0 @@ -# Source - -## Configuration - -Sources can be configured with various options to customize how they connect to external systems. All [Guzzle options](https://docs.guzzlephp.org/en/stable/request-options.html) are supported for HTTP-based sources. - -### Common Configuration Options - -Some commonly used configuration options include: - -**Authentication:** -```json -headers: - - name: Authorization - value: Bearer YOUR_TOKEN_HERE -``` - -**Custom Headers:** -```json -headers: - - name: Accept - value: application/json -``` - -**Query Parameters:** -```json -query: - - name: limit - value: 100 -``` - -**Timeout Configuration:** -```json -timeout: 10 -``` - -### Additional Configuration Parameters - -You can set additional configuration parameters for sources: - -- **logBody**: Controls response body logging behavior - - `true` or `1`: Log all response bodies - - `false` or `0`: Log only error response bodies (status codes 400-599) - - Default: `false` (only error responses are logged) - -Example: -```json -{ - "logBody": true, - "timeout": 30 -} -``` - -## Call Logs Management - -The OpenConnector provides comprehensive call log management for monitoring API interactions with your sources. The **Source Logs** page offers professional-grade tools for analyzing, filtering, and managing your API call history. - -### Accessing Source Logs - -You can access source logs in two ways: - -1. **From the main navigation**: Go to Sources → Logs -2. **From individual sources**: Click 'View Logs' on any source in the Sources list - -### Call Logs Interface - -The Source Logs page provides a streamlined, table-based interface optimized for log analysis: - -#### Key Features - -- **Table-only view**: Focused on readability and data density -- **Advanced filtering**: Comprehensive filtering system via sidebar -- **Export functionality**: Export filtered logs for external analysis -- **Bulk operations**: Select and delete multiple logs efficiently -- **Real-time updates**: Refresh logs with latest data - -#### Log Information Displayed - -Each log entry shows: - -- **Status Code**: HTTP response status with color-coded indicators - - 🟢 Green: Success (2xx) - - 🟡 Yellow: Client errors (4xx) - - 🔴 Red: Server errors (5xx) -- **Source**: Name of the source that made the call -- **HTTP Method**: GET, POST, PUT, PATCH, DELETE with color-coded badges -- **Endpoint**: Target URL or endpoint path -- **Response Time**: Request duration with performance indicators - - 🟢 Fast: < 1 second - - 🟡 Medium: 1-3 seconds - - 🔴 Slow: > 3 seconds -- **Created**: Timestamp of the API call - -### Advanced Filtering - -The sidebar provides powerful filtering capabilities: - -#### Source Filtering -- Filter logs by specific sources -- View logs from all sources or focus on individual sources - -#### Status Code Filtering -- Filter by HTTP response codes: - - Success codes: 200, 201 - - Client errors: 400, 401, 403, 404 - - Server errors: 500, 502, 503 - -#### HTTP Method Filtering -- Filter by request methods: GET, POST, PUT, PATCH, DELETE - -#### Date Range Filtering -- **From Date**: Start of time range -- **To Date**: End of time range -- Custom date/time picker for precise filtering - -#### Advanced Filters -- **Endpoint Filter**: Text-based filtering for specific endpoints -- **Show Only Errors**: Toggle to display only failed requests (4xx, 5xx) -- **Show Slow Requests**: Toggle to display only slow requests (>5 seconds) - -#### Filter Statistics - -The sidebar also provides real-time statistics: - -- **Total Logs**: Overall count of call logs -- **Success Rate**: Percentage of successful calls (2xx status codes) -- **Average Response Time**: Mean response time across all calls -- **Status Distribution**: Breakdown of response codes -- **Most Active Sources**: Sources generating the most calls - -### Log Actions - -#### Individual Log Actions -- **View Details**: Open detailed log information in a modal -- **Copy Data**: Copy complete log data to clipboard (JSON format) -- **Delete**: Remove individual log entries - -#### Bulk Operations -- **Select All**: Select all visible logs for bulk operations -- **Bulk Delete**: Remove multiple selected logs at once -- **Export**: Download filtered logs in CSV format - -### Performance Monitoring - -#### Response Time Analysis -The interface provides visual indicators for performance monitoring: - -- **Fast responses** (< 1s): Green highlighting -- **Medium responses** (1-3s): Yellow highlighting -- **Slow responses** (> 3s): Red highlighting - -#### Error Analysis -- Color-coded status indicators help identify patterns -- Filter by error types to troubleshoot issues -- Export error logs for detailed analysis - -### Export and Integration - -#### Export Formats -- **CSV Export**: Download logs in comma-separated format -- **Filtered Export**: Export only logs matching current filters -- **Complete Data**: Include all log fields and metadata - -#### Integration with Monitoring Tools -The log data can be integrated with external monitoring systems: - -```php -// Example: Export log metrics -$metrics = [ - 'total_calls' => $logCount, - 'success_rate' => $successRate, - 'average_response_time' => $avgResponseTime, - 'error_count' => $errorCount -]; -``` - -### Best Practices - -#### Log Management -1. **Regular Cleanup**: Periodically remove old logs to maintain performance -2. **Filter Strategy**: Use specific filters to focus on relevant data -3. **Export Important Data**: Download critical logs before cleanup -4. **Monitor Trends**: Watch for patterns in response times and errors - -#### Performance Optimization -1. **Response Time Monitoring**: Track slow endpoints for optimization -2. **Error Pattern Analysis**: Identify recurring issues -3. **Source Performance**: Compare performance across different sources -4. **Rate Limit Awareness**: Monitor for rate limiting indicators - -#### Troubleshooting -1. **Error Investigation**: Use status code filters to isolate issues -2. **Timeline Analysis**: Use date filters to correlate issues with events -3. **Endpoint Analysis**: Filter by specific endpoints to debug problems -4. **Source Comparison**: Compare behavior across different sources - -**Note**: The log retention period and cleanup policies can be configured in the system settings. - -## Testing Sources - -Use the test functionality to verify your source configuration before deploying: - -1. Navigate to your source configuration -2. Click the 'Test Source' button -3. Review the test results for connectivity and authentication -4. Adjust configuration as needed based on test feedback - -Test results will appear in the source logs and can be analyzed using the same filtering and analysis tools. \ No newline at end of file diff --git a/website/docs/synchronization.md b/website/docs/synchronization.md deleted file mode 100644 index 1f62751b4..000000000 --- a/website/docs/synchronization.md +++ /dev/null @@ -1,336 +0,0 @@ -# Synchronization - -Synchronization is a core feature that enables data transfer between different systems and APIs. Here's how it works: - -## Overview -- Synchronizations define how data should be synchronized between a source and target system -- Each synchronization has a source configuration (where to get data from) and target configuration (where to send data to) -- The synchronization process handles pagination, data mapping, and maintaining sync state - -## Key Components - -### Source Configuration -- Defines where to fetch data from (API endpoint, database etc) -- Specifies how to map source data to target format (incomming data) -- Specifies how to locate objects in the response using resultsPosition: - - `_root`: Uses the entire response body as the objects array - - Dot notation (e.g. `data.items`): Extracts objects from a nested path - - Common keys (`items`, `result`, `results`): Automatically checks these standard locations - - Custom path: Specify any JSON path to locate the objects -- Configures pagination settings (paginationQuery) -- Can include conditions to filter which objects to sync using JSON Logic -- Source type can be set to API or other supported types -- Source ID mapping allows specifying position of IDs in source objects -- Optional endpoint configuration for fetching data -- usesPagination: configure this field if you know this source **does not** uses pagination or next endpoint. Use value "false". If this source uses next endpoint it will auto detect. -- Optional configuration of sub objects. -- (Optional) restrictDeletion: if set to true or 1, restricts the deletion of objects in the synchronization to objects of which the origin id was in the request to the source, to prevent deletion of objects when a specified set of objects is synchronized. - -#### Related or sub objects - -If you don't want mapped related- or sub- objects to duplicate and do want sub objects to have their own contracts you need to let the synchronization know. -You can configure this under the source configuration of a synchronization. - -![Sub objects](sub-objects.png) - -The synchronization know knows we need to find and update a existing contract for zaaktype and the zaaktype its own sub object statustype. The contract can find the related object and update that object instead of duplicating objects each time the synchronization has ran. - -Also make sure you first map a `originId` in the mapping of a sub object so the code can find this object and update it instead of duplicate it. - -![Origin id](origin-id.png) - -### Target Configuration -- Defines where to send synchronized data -- Specifies how to map target data to source format (outgoing data) -- Handles create/update/delete operations -- Target type can be Register/Schema or other supported types -- Target ID and schema selection for Register/Schema targets -- Target source mapping for data transformations -- `idInRequestBody` is a config option u can use when the id of the object in the target is not managed by itself but needs to be given with the request. Set the id to the required key in a mapping and configure `idInRequestBody` to that key so we set the targetId of the contract to that id. - -### Synchronization Contracts -- Tracks the sync state for each individual object -- Stores origin ID and target ID mappings -- Maintains hashes to detect changes -- Logs synchronization events and errors - -## Process Flow -1. Fetch all objects from source system with pagination -2. Determining if an object has changed -3. Create/update synchronization contracts for each object -4. Transform data according to mapping rules -5. Write objects to target system (POST/PUT/DELETE) -6. Update contract status and hashes -7. Handle any follow-up synchronizations - -### Determining if an object has changed -The synchronization service prevents unnecessary updates and code execution by checking the following conditions: - -1. The origin hash matches the stored hash in the synchronization contract (indicating the source object hasn't changed) -2. The synchronization configuration hasn't been updated since the last check (synchronization.updated < contract.sourceLastChecked) -3. If a source target mapping exists, verify it hasn't been updated since the last check (mapping.updated < contract.sourceLastChecked) -4. The target ID and hash exist in the synchronization contract (object hasn't been removed from target system) -5. The force parameter is false (if true, the update will proceed regardless of other conditions) - -If all these conditions are met, the synchronization service will skip updating the object since no changes are needed. This optimization prevents unnecessary API calls and processing. - -This procces might be overridden by the user by setting the force option to true. In that case the synchronization service will update the target object regardless of whether it has changed or not. - -## Error Handling -- Rate limiting detection and backoff -- Logging of failed operations -- Contract state tracking for retry attempts - -The synchronization system provides a robust way to keep data in sync across different systems while maintaining state and handling errors gracefully. - -## Form Configuration -The synchronization form allows configuring: - -- Name: Descriptive name for the synchronization -- Description: Optional details about the synchronization -- Conditions: JSON Logic conditions for filtering objects -- Source Configuration: - - Source Type: API or other supported types - - Source ID: Selection of configured source - - Source hash mapping: Hash configuration - - Source target mapping: Data mapping rules - - Position of ID in source object (optional) - - Position of results in source object (optional) - - Custom endpoint for data fetching (optional) -- Target Configuration: - - Target Type: Register/Schema or other types - - Target ID: Selection of target system - - Register and Schema selection for Register/Schema targets - - Target source mapping: Data transformation rules -- Test sync option to validate configuration - -![alt text](image.png) - -## XML - -All XML files are first parsed to a json object before being processed. During this parings all element atributes are added to the `@attributes` property of the element Resulting in a clean json object. Lets take a look at the XML example, and the json result that it will produce. - - - - -```xml - - - GEMMA - De GEMeentelijk Model Architectuur (GEMMA) bevat een blauwdruk van de gemeente en haar informatievoorziening. De GEMMA kan worden gebruikt als basis voor de projectmodellen - - - Publiceren en gebruiken van informatie over datadiensten - Dienstenafnemers moeten in online catalogi kunnen opvragen welke diensten, met welke kenmerken, door dienstenaanbieder worden aangeboden. -Onder andere ontwikkelaars hebben baat bij informatie over beschikbare diensten en de vereisten voor het gebruik van de dienst (bijv. specificatie van een dienst conform de OAS-standaard). - - - Thema-architectuur Common Ground - - - a10869bf-a895-4a66-8f81-a4f96c58cc3e - - - - - Bieden van 'regie op gegevens' aan burgers en bedrijven - Burgers en bedrijven moet zeggenschap worden geboden over hun persoonlijke gegevens. Ze moeten o.a. in staat worden gesteld om te weten welke gegevens de overheid over hen vastlegt en gebruikt, persoonlijke gegevens (digitaal) te delen, eenmalig te verstrekken, de eigen gegevens in te zien, te controleren en waar nodig te (laten) corrigeren,in te zien welke gegevens worden en zijn uitgewisseld. - - - Thema-architectuur Common Ground - - - 6868ab87-f5db-40b7-aba6-17b1ba477d54 - - - - - -``` - -```json -{ - "@attributes": { - "identifier": "id-d47463497ef83403bedb663bff370727" - }, - "name": "GEMMA", - "documentation": "De GEMeentelijk Model Architectuur (GEMMA) bevat een blauwdruk van de gemeente en haar informatievoorziening. De GEMMA kan worden gebruikt als basis voor de projectmodellen", - "elements": { - "element": [ - { - "@attributes": { - "identifier": "id-009fa62f25844aa3a87d252bf2b6bb0c", - "xsi:type": "Capability" - }, - "name": "Publiceren en gebruiken van informatie over datadiensten", - "documentation": "Dienstenafnemers moeten in online catalogi kunnen opvragen welke diensten, met welke kenmerken, door dienstenaanbieder worden aangeboden. \nOnder andere ontwikkelaars hebben baat bij informatie over beschikbare diensten en de vereisten voor het gebruik van de dienst (bijv. specificatie van een dienst conform de OAS-standaard).", - "properties": { - "property": [ - { - "@attributes": { - "propertyDefinitionRef": "propid-1" - }, - "value": "Thema-architectuur Common Ground" - }, - { - "@attributes": { - "propertyDefinitionRef": "propid-2" - }, - "value": "a10869bf-a895-4a66-8f81-a4f96c58cc3e" - } - ] - } - }, - { - "@attributes": { - "identifier": "id-096b6619f4cd4164a0572b04ef082c0f", - "xsi:type": "Capability" - }, - "name": "Bieden van 'regie op gegevens' aan burgers en bedrijven", - "documentation": "Burgers en bedrijven moet zeggenschap worden geboden over hun persoonlijke gegevens. Ze moeten o.a. in staat worden gesteld om te weten welke gegevens de overheid over hen vastlegt en gebruikt, persoonlijke gegevens (digitaal) te delen, eenmalig te verstrekken, de eigen gegevens in te zien, te controleren en waar nodig te (laten) corrigeren,in te zien welke gegevens worden en zijn uitgewisseld.", - "properties": { - "property": [ - { - "@attributes": { - "propertyDefinitionRef": "propid-1" - }, - "value": "Thema-architectuur Common Ground" - }, - { - "@attributes": { - "propertyDefinitionRef": "propid-2" - }, - "value": "6868ab87-f5db-40b7-aba6-17b1ba477d54" - } - ] - } - } - ] - } -} -``` - -So in this case we have a json object with an @attributes property. This property is used to store the identifier of the element. The name property is used to store the name of the element. And the properties property is used to store the properties of the element. - -That means that we can configure the psoition of the `id` in the source mapping as `@attributes.identifier` and the position of result within the source object as `elements.element`. - -Oke that setsup geting the data from the source, but how does the data get to the destination? Wel ugly is the answer, we have to map the data to the destination object. Right now the above code would result in the following object: - -```json -{ - "@attributes": { - "identifier": "id-8576c90ac5104cf8ade8acadf2764432" - }, - "name": "IngeschrevenPersoon", - "documentation": "Een INGEZETENE of NIET-INGEZETENE", - "properties": { - "property": [ - { - "@attributes": { - "propertyDefinitionRef": "propid-6" - }, - "value": "{AF9534B0-7CBD-4061-9276-A7B6698B383A}" - }, - { - "@attributes": { - "propertyDefinitionRef": "propid-8" - }, - "value": "Class" - }, - { - "@attributes": { - "propertyDefinitionRef": "propid-7" - }, - "value": "10122024-112046" - }, - { - "@attributes": { - "propertyDefinitionRef": "propid-9" - }, - "value": "\"ggm-\" properties worden beheerd in het GGM informatiemodel" - }, - { - "@attributes": { - "propertyDefinitionRef": "propid-15" - }, - "value": "Ingezetene, Client, Leerling, Ouder Of Verzorger" - }, - { - "@attributes": { - "propertyDefinitionRef": "propid-2" - }, - "value": "e1371e07-969e-4a34-be81-0a23ffd541dd" - }, - { - "@attributes": { - "propertyDefinitionRef": "propid-10" - }, - "value": "https://gemmaonline.nl/index.php/GEMMA/id-e1371e07-969e-4a34-be81-0a23ffd541dd" - } - ] - }, - "id": "faea7cdb-fe32-42fb-bbd4-98c3744ef333" -} -``` - - -So lets create a mapping for this, like: - -```json -{ - "identifier": "@attributes.identifier", - "name": "name", - "documentation": "documentation", - "properties": "{ {% for property in properties.property %}\"{{property['@attributes']['propertyDefinitionRef']}}\":\"{{property['value']}}\"{% if not loop.last %},{% endif %}{% endfor %} }" -} -``` - -Lets not forget that this will output the properties like - -```json -{ - "properties": "{ \"propid-6\":\"{AF9534B0-7CBD-4061-9276-A7B6698B383A}\",\"propid-8\":\"Class\",\"propid-7\":\"10122024-112046\",\"propid-9\":\""ggm-" properties worden beheerd in het GGM informatiemodel\",\"propid-15\":\"Ingezetene, Client, Leerling, Ouder Of Verzorger\",\"propid-2\":\"e1371e07-969e-4a34-be81-0a23ffd541dd\",\"propid-10\":\"https://gemmaonline.nl/index.php/GEMMA/id-e1371e07-969e-4a34-be81-0a23ffd541dd\" }" -} -``` - -So in the mapping we must add a cast to turn that into a json object. - -```json -{ - "properties": "jsonToArray" -} -``` - -And that will result in the following object: - -```json -{ - "properties": { - "propid-6": "{AF9534B0-7CBD-4061-9276-A7B6698B383A}", -But we want to map the data to the following object: - -```json -{ - "identifier": "id-8576c90ac5104cf8ade8acadf2764432", - "name": "IngeschrevenPersoon", - "documentation": "Een INGEZETENE of NIET-INGEZETENE", - "properties": { - "propid-6": "{AF9534B0-7CBD-4061-9276-A7B6698B383A}", - "propid-8": "Class", - "propid-7": "10122024-112046", - "propid-9": "\"ggm-\" properties worden beheerd in het GGM informatiemodel", - "propid-15": "Ingezetene, Client, Leerling, Ouder Of Verzorger", - "propid-2": "e1371e07-969e-4a34-be81-0a23ffd541dd", - "propid-10": "https://gemmaonline.nl/index.php/GEMMA/id-e1371e07-969e-4a34-be81-0a23ffd541dd" - } -} -``` - -Now of course propeties like `propid-6` are not very readable, so we can rename them to something more readable later using a translation table. - - - - - - - diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js deleted file mode 100644 index 8e2c89fbc..000000000 --- a/website/docusaurus.config.js +++ /dev/null @@ -1,114 +0,0 @@ -// @ts-check -// `@type` JSDoc annotations allow editor autocompletion and type checking -// (when paired with `@ts-check`). -// There are various equivalent ways to declare your Docusaurus config. -// See: https://docusaurus.io/docs/api/docusaurus-config - -/** @type {import('@docusaurus/types').Config} */ -const config = { - title: 'Open Connector', - tagline: 'Synchronize data between Nextcloud and external sources', - url: 'https://conductionnl.github.io', - baseUrl: '/openconnector/', - organizationName: 'conductionnl', - projectName: 'openconnector', - favicon: 'img/favicon.ico', - onBrokenLinks: 'warn', - onBrokenMarkdownLinks: 'warn', - - // Even if you don't use internationalization, you can use this field to set - // useful metadata like html lang. For example, if your site is Chinese, you - // may want to replace "en" with "zh-Hans". - i18n: { - defaultLocale: 'en', - locales: ['en'], - }, - - presets: [ - [ - 'classic', - /** @type {import('@docusaurus/preset-classic').Options} */ - ({ - docs: { - sidebarPath: require.resolve('./sidebars.js'), - editUrl: 'https://github.com/conductionnl/openconnector/tree/main/website/', - }, - blog: false, - theme: { - customCss: require.resolve('./src/css/custom.css'), - }, - }), - ], - ], - - themeConfig: - /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ - ({ - navbar: { - title: 'Open Connector', - logo: { - alt: 'Open Connector Logo', - src: 'img/logo.svg', - }, - items: [ - { - type: 'docSidebar', - sidebarId: 'tutorialSidebar', - position: 'left', - label: 'Documentation', - }, - { - href: 'https://github.com/conductionnl/openconnector', - label: 'GitHub', - position: 'right', - }, - ], - }, - footer: { - style: 'dark', - links: [ - { - title: 'Documentation', - items: [ - { - label: 'Getting Started', - to: '/docs/getting-started', - }, - { - label: 'Tutorial', - to: '/docs/tutorial', - }, - ], - }, - { - title: 'Community', - items: [ - { - label: 'GitHub', - href: 'https://github.com/conductionnl/openconnector', - }, - ], - }, - ], - copyright: `Copyright © ${new Date().getFullYear()} Conduction. Built with Docusaurus.`, - }, - prism: { - theme: { - plain: { - color: "#393A34", - backgroundColor: "#f6f8fa" - }, - styles: [] - }, - darkTheme: { - plain: { - color: "#F8F8F2", - backgroundColor: "#282A36" - }, - styles: [] - } - }, - }), -}; - -module.exports = config; diff --git a/website/src/components/HomepageFeatures/index.js b/website/src/components/HomepageFeatures/index.js deleted file mode 100644 index acc762199..000000000 --- a/website/src/components/HomepageFeatures/index.js +++ /dev/null @@ -1,64 +0,0 @@ -import clsx from 'clsx'; -import Heading from '@theme/Heading'; -import styles from './styles.module.css'; - -const FeatureList = [ - { - title: 'Easy to Use', - Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, - description: ( - <> - Docusaurus was designed from the ground up to be easily installed and - used to get your website up and running quickly. - - ), - }, - { - title: 'Focus on What Matters', - Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, - description: ( - <> - Docusaurus lets you focus on your docs, and we'll do the chores. Go - ahead and move your docs into the docs directory. - - ), - }, - { - title: 'Powered by React', - Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, - description: ( - <> - Extend or customize your website layout by reusing React. Docusaurus can - be extended while reusing the same header and footer. - - ), - }, -]; - -function Feature({Svg, title, description}) { - return ( -

- ); -} - -export default function HomepageFeatures() { - return ( -
-
-
- {FeatureList.map((props, idx) => ( - - ))} -
-
-
- ); -} diff --git a/website/src/css/custom.css b/website/src/css/custom.css deleted file mode 100644 index 2bc6a4cfd..000000000 --- a/website/src/css/custom.css +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Any CSS included here will be global. The classic template - * bundles Infima by default. Infima is a CSS framework designed to - * work well for content-centric websites. - */ - -/* You can override the default Infima variables here. */ -:root { - --ifm-color-primary: #2e8555; - --ifm-color-primary-dark: #29784c; - --ifm-color-primary-darker: #277148; - --ifm-color-primary-darkest: #205d3b; - --ifm-color-primary-light: #33925d; - --ifm-color-primary-lighter: #359962; - --ifm-color-primary-lightest: #3cad6e; - --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); -} - -/* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme='dark'] { - --ifm-color-primary: #25c2a0; - --ifm-color-primary-dark: #21af90; - --ifm-color-primary-darker: #1fa588; - --ifm-color-primary-darkest: #1a8870; - --ifm-color-primary-light: #29d5b0; - --ifm-color-primary-lighter: #32d8b4; - --ifm-color-primary-lightest: #4fddbf; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); -} diff --git a/website/static/img/logo.svg b/website/static/img/logo.svg deleted file mode 100644 index 9db6d0d06..000000000 --- a/website/static/img/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 7855a3467e98af0b6673bad51b0cf46c025968fb Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Sat, 7 Mar 2026 09:40:56 +0100 Subject: [PATCH 2/7] refactor: Delegate MappingService to OpenRegister's mapping engine Add fallback logging when OpenRegister MappingService is not available. OpenConnector now depends on OpenRegister for mapping functionality. --- lib/Service/MappingService.php | 95 ++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 16 deletions(-) diff --git a/lib/Service/MappingService.php b/lib/Service/MappingService.php index 3ea528531..194a7d81c 100644 --- a/lib/Service/MappingService.php +++ b/lib/Service/MappingService.php @@ -10,12 +10,6 @@ use OCA\OpenConnector\Twig\MappingExtension; use OCA\OpenConnector\Twig\MappingRuntimeLoader; use OCA\OpenRegister\Service\FileService; -use OCP\Files\IRootFolder; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\HttpFoundation\Session\SessionInterface; -//use Twig\Environment; -//use Twig\Error\LoaderError; -//use Twig\Error\SyntaxError; use Adbar\Dot; use Twig\Environment; use Twig\Error\LoaderError; @@ -24,6 +18,21 @@ use Throwable; use Exception; +/** + * Mapping service that delegates core execution to OpenRegister's MappingService + * when available, falling back to its own implementation. + * + * @deprecated The mapping engine has moved to OpenRegister. This service delegates + * to OCA\OpenRegister\Service\MappingService for executeMapping(). + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassLength) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) Mapping execution requires comprehensive handling + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) $list parameter clearly indicates list processing mode + */ class MappingService { /** @@ -33,11 +42,22 @@ class MappingService */ private Environment $twig; + /** + * The OpenRegister mapping service (if available). + * + * @var \OCA\OpenRegister\Service\MappingService|null + */ + private $openRegisterMappingService = null; + /** * Setting up the base class with required services. * * @param ArrayLoader $loader The ArrayLoader for Twig. * @param MappingMapper $mappingMapper The mapping mapper. + * @param CallService $callService The call service. + * @param SourceMapper $sourceMapper The source mapper. + * @param FileService $fileService The file service. + * @param ObjectService $objectService The object service. */ public function __construct( ArrayLoader $loader, @@ -51,6 +71,20 @@ public function __construct( $this->twig->addExtension(new MappingExtension()); $this->twig->addRuntimeLoader(new MappingRuntimeLoader(mappingService: $this, mappingMapper: $this->mappingMapper, callService: $callService, sourceMapper: $sourceMapper, fileService: $fileService, objectService: $objectService->getOpenRegisters())); + // Try to load OpenRegister's MappingService for delegation. + try { + $container = \OC::$server; + $this->openRegisterMappingService = $container->get( + \OCA\OpenRegister\Service\MappingService::class + ); + } catch (\Throwable $e) { + // OpenRegister not available, falling back to local implementation. + $this->openRegisterMappingService = null; + \OC::$server->getLogger()->info( + 'OpenConnector MappingService: OpenRegister not available, using local implementation', + ['app' => 'openconnector'] + ); + } }//end __construct() /** @@ -83,6 +117,9 @@ public function encodeArrayKeys(array $array, string $toReplace, string $replace /** * Maps (transforms) an array (input) to a different array (output). * + * Delegates to OpenRegister's MappingService when available, otherwise + * uses the local implementation. + * * @param Mapping $mapping The mapping object that forms the recipe for the mapping * @param array $input The array that need to be mapped (transformed) otherwise known as input * @param bool $list Whether we want a list instead of a single item @@ -93,7 +130,42 @@ public function encodeArrayKeys(array $array, string $toReplace, string $replace */ public function executeMapping(Mapping $mapping, array $input, bool $list = false): array { + // Delegate to OpenRegister's MappingService if available. + if ($this->openRegisterMappingService !== null) { + $orMapping = new \OCA\OpenRegister\Db\Mapping(); + $orMapping->hydrate([ + 'name' => $mapping->getName(), + 'mapping' => $mapping->getMapping(), + 'unset' => ($mapping->getUnset() ?? []), + 'cast' => ($mapping->getCast() ?? []), + 'passThrough' => $mapping->getPassThrough(), + ]); + + return $this->openRegisterMappingService->executeMapping( + mapping: $orMapping, + input: $input, + list: $list + ); + } + + return $this->executeMappingLocal($mapping, $input, $list); + }//end executeMapping() + /** + * Local mapping execution (fallback when OpenRegister is not available). + * + * @param Mapping $mapping The mapping object + * @param array $input The input array + * @param bool $list Whether to process as list + * + * @return array The mapped output + * + * @throws Exception When mapping fails + * + * @SuppressWarnings(PHPMD.ElseExpression) + */ + private function executeMappingLocal(Mapping $mapping, array $input, bool $list = false): array + { // Check for list if ($list === true) { $list = []; @@ -122,16 +194,12 @@ public function executeMapping(Mapping $mapping, array $input, bool $list = fals $originalInput = $input; $input = $this->encodeArrayKeys($input, '.', '.'); - // @todo: error logging - // Determine pass through. // Let's get the dot array based on https://github.com/adbario/php-dot-notation. if ($mapping->getPassThrough()) { $dotArray = new Dot($input); - // @todo: error logging } else { $dotArray = new Dot(); - // @todo: error logging } $dotInput = new Dot($input); @@ -160,7 +228,6 @@ public function executeMapping(Mapping $mapping, array $input, bool $list = fals $unsets = ($mapping->getUnset() ?? []); foreach ($unsets as $unset) { if ($dotArray->has($unset) === false) { - // @todo: error logging continue; } @@ -172,7 +239,6 @@ public function executeMapping(Mapping $mapping, array $input, bool $list = fals foreach ($casts as $key => $cast) { if ($dotArray->has($key) === false) { - // @todo: error logging continue; } @@ -181,7 +247,6 @@ public function executeMapping(Mapping $mapping, array $input, bool $list = fals } if ($cast === false) { - // @todo: error logging continue; } @@ -214,7 +279,7 @@ public function executeMapping(Mapping $mapping, array $input, bool $list = fals return $output; - }//end mapping() + }//end executeMappingLocal() /** * Handles a single cast. @@ -376,8 +441,6 @@ private function handleCast(Dot $dotArray, string $key, string $cast) $value = number_format($value, 2, ',', '.'); break; default: - // @todo: error handling - //isset($this->style) === true && $this->style->info('Trying to cast to an unsupported cast type: '.$cast); break; }//end switch From 70ca945b44e7dffe6e61014760496fdacbb21b4d Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 13 Mar 2026 09:59:15 +0100 Subject: [PATCH 3/7] ci: Migrate to reusable quality workflow with PHPUnit Replace custom quality-check workflow with ConductionNL reusable workflow. Enable PHPUnit tests. --- .github/workflows/quality-check.yml | 191 +++------------------------- 1 file changed, 17 insertions(+), 174 deletions(-) diff --git a/.github/workflows/quality-check.yml b/.github/workflows/quality-check.yml index 82fd839fa..d240aea76 100644 --- a/.github/workflows/quality-check.yml +++ b/.github/workflows/quality-check.yml @@ -1,178 +1,21 @@ -name: Quality Assurance Checks +name: Quality on: - pull_request: - branches: - # - main - # - master - # - development - # - dev - - never push: - branches: - # - main - # - master - # - development - # - dev - - never -jobs: - quality-check: - name: Code Quality Analysis - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.1' - extensions: mbstring, xml, ctype, iconv, intl, pdo, pdo_mysql, dom, filter, gd, json, posix, simplexml, xmlreader, xmlwriter, zip - coverage: xdebug - tools: composer:v2 - - - name: Validate composer.json - run: composer validate --strict - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v3 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-composer- - - - name: Install dependencies - run: composer install --prefer-dist --no-progress --no-interaction - - - name: Run PHP Lint - run: composer lint - continue-on-error: false - - - name: Run PHPCS (Coding Standards) - id: phpcs - run: | - composer cs:check - composer phpcs:output - if [ -f phpcs-output.json ]; then - ERRORS=$(jq '.totals.errors' phpcs-output.json) - WARNINGS=$(jq '.totals.warnings' phpcs-output.json) - SCORE=$(echo "scale=2; 100 - (($ERRORS * 2) + ($WARNINGS * 0.5))" | bc) - echo "score=$SCORE" >> $GITHUB_OUTPUT - echo "errors=$ERRORS" >> $GITHUB_OUTPUT - echo "warnings=$WARNINGS" >> $GITHUB_OUTPUT - echo "### PHPCS Results" >> $GITHUB_STEP_SUMMARY - echo "- **Score:** $SCORE%" >> $GITHUB_STEP_SUMMARY - echo "- **Errors:** $ERRORS" >> $GITHUB_STEP_SUMMARY - echo "- **Warnings:** $WARNINGS" >> $GITHUB_STEP_SUMMARY - if [ "$ERRORS" -gt "0" ]; then - echo "❌ PHPCS found $ERRORS errors. All errors must be fixed." >> $GITHUB_STEP_SUMMARY - exit 1 - fi - fi - continue-on-error: false - - - name: Run PHPMD (Mess Detector) - id: phpmd - run: | - composer phpmd > phpmd-output.txt || true - if [ -f phpmd-output.txt ]; then - VIOLATIONS=$(grep -c "^" phpmd-output.txt || echo "0") - SCORE=$(echo "scale=2; 100 - ($VIOLATIONS * 0.5)" | bc) - echo "score=$SCORE" >> $GITHUB_OUTPUT - echo "violations=$VIOLATIONS" >> $GITHUB_OUTPUT - echo "### PHPMD Results" >> $GITHUB_STEP_SUMMARY - echo "- **Score:** $SCORE%" >> $GITHUB_STEP_SUMMARY - echo "- **Violations:** $VIOLATIONS" >> $GITHUB_STEP_SUMMARY - if (( $(echo "$SCORE < 80" | bc -l) )); then - echo "❌ PHPMD score is below 80%. Please review and fix violations." >> $GITHUB_STEP_SUMMARY - exit 1 - fi - fi - continue-on-error: false - - - name: Run PHPQA (Full Quality Analysis) - id: phpqa - run: | - composer phpqa:ci - if [ -f phpqa/phpqa.json ]; then - # Parse PHPQA results - echo "### PHPQA Results" >> $GITHUB_STEP_SUMMARY - echo "Full report available in artifacts." >> $GITHUB_STEP_SUMMARY - - # Calculate overall quality score - # This is a composite score based on all analyzers - PHPCS_SCORE="${{ steps.phpcs.outputs.score }}" - PHPMD_SCORE="${{ steps.phpmd.outputs.score }}" - OVERALL_SCORE=$(echo "scale=2; ($PHPCS_SCORE + $PHPMD_SCORE) / 2" | bc) - - echo "overall_score=$OVERALL_SCORE" >> $GITHUB_OUTPUT - echo "- **Overall Quality Score:** $OVERALL_SCORE%" >> $GITHUB_STEP_SUMMARY - - if (( $(echo "$OVERALL_SCORE < 90" | bc -l) )); then - echo "❌ Overall quality score ($OVERALL_SCORE%) is below required 90%." >> $GITHUB_STEP_SUMMARY - exit 1 - else - echo "✅ Quality score meets requirements!" >> $GITHUB_STEP_SUMMARY - fi - fi - continue-on-error: false - - - name: Upload PHPQA Reports - if: always() - uses: actions/upload-artifact@v3 - with: - name: phpqa-reports - path: phpqa/ - retention-days: 30 - - - name: Comment PR with Quality Report - if: github.event_name == 'pull_request' - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs'); - const phpcsScore = '${{ steps.phpcs.outputs.score }}'; - const phpcsErrors = '${{ steps.phpcs.outputs.errors }}'; - const phpcsWarnings = '${{ steps.phpcs.outputs.warnings }}'; - const phpmdScore = '${{ steps.phpmd.outputs.score }}'; - const phpmdViolations = '${{ steps.phpmd.outputs.violations }}'; - const overallScore = '${{ steps.phpqa.outputs.overall_score }}'; - - const passed = parseFloat(overallScore) >= 90; - const emoji = passed ? '✅' : '❌'; - - const body = `## ${emoji} Code Quality Report - - ### Overall Score: **${overallScore}%** ${passed ? '(PASS)' : '(FAIL - Requires 90%+)'} - - | Analyzer | Score | Details | - |----------|-------|---------| - | PHPCS | ${phpcsScore}% | ${phpcsErrors} errors, ${phpcsWarnings} warnings | - | PHPMD | ${phpmdScore}% | ${phpmdViolations} violations | - - ### Requirements - - ✅ PHPCS Errors: Must be 0 (Current: ${phpcsErrors}) - - ${passed ? '✅' : '❌'} Overall Score: Must be ≥ 90% (Current: ${overallScore}%) - - ${parseFloat(phpmdScore) >= 80 ? '✅' : '⚠️'} PHPMD Score: Should be ≥ 80% (Current: ${phpmdScore}%) - - ${passed ? - '### ✅ This PR meets all quality requirements and can be merged.' : - '### ❌ This PR does not meet quality requirements. Please fix the issues above before merging.'} - - 📊 Full quality reports are available in the workflow artifacts. - `; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: body - }); + branches: [main, development, feature/**, bugfix/**, hotfix/**] + pull_request: + branches: [main, master, development] +jobs: + quality: + uses: ConductionNL/.github/.github/workflows/quality.yml@main + with: + app-name: openconnector + php-version: "8.3" + nextcloud-ref: stable32 + enable-psalm: true + enable-phpstan: true + enable-phpmetrics: true + enable-frontend: true + enable-eslint: true + enable-phpunit: true From 6dcc8fe7a8831dff68243ae1dc04d9061932b8eb Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 13 Mar 2026 16:35:46 +0100 Subject: [PATCH 4/7] feat: Adopt centralized release workflows Replace old per-repo release, sync, and branch protection workflows with thin callers to ConductionNL/.github reusable workflows. --- .github/workflows/beta-release.yaml | 213 --------------- .github/workflows/branch-protection.yml | 9 + .github/workflows/phpcs.yml | 22 -- .../pull-request-from-branch-check.yaml | 32 --- .../workflows/pull-request-lint-check.yaml | 22 -- .../workflows/pull-request-quality-gate.yml | 247 ------------------ .../workflows/push-beta-to-beta-release.yaml | 71 ----- ...sh-development-to-development-release.yaml | 71 ----- .github/workflows/quality-check.yml | 21 -- .github/workflows/release-beta.yml | 12 + .github/workflows/release-stable.yml | 12 + .github/workflows/stable-release.yaml | 221 ---------------- .github/workflows/sync-to-beta.yml | 9 + .github/workflows/unstable-release.yaml | 215 --------------- 14 files changed, 42 insertions(+), 1135 deletions(-) delete mode 100644 .github/workflows/beta-release.yaml create mode 100644 .github/workflows/branch-protection.yml delete mode 100644 .github/workflows/phpcs.yml delete mode 100644 .github/workflows/pull-request-from-branch-check.yaml delete mode 100644 .github/workflows/pull-request-lint-check.yaml delete mode 100644 .github/workflows/pull-request-quality-gate.yml delete mode 100644 .github/workflows/push-beta-to-beta-release.yaml delete mode 100644 .github/workflows/push-development-to-development-release.yaml delete mode 100644 .github/workflows/quality-check.yml create mode 100644 .github/workflows/release-beta.yml create mode 100644 .github/workflows/release-stable.yml delete mode 100644 .github/workflows/stable-release.yaml create mode 100644 .github/workflows/sync-to-beta.yml delete mode 100644 .github/workflows/unstable-release.yaml diff --git a/.github/workflows/beta-release.yaml b/.github/workflows/beta-release.yaml deleted file mode 100644 index ffc7017c7..000000000 --- a/.github/workflows/beta-release.yaml +++ /dev/null @@ -1,213 +0,0 @@ -name: Beta Release - -on: - push: - branches: - - beta-release - -jobs: - release-management: - runs-on: ubuntu-latest - steps: - - # Step 1: Checkout the repository with full history - - name: Checkout Code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ssh-key: ${{ secrets.DEPLOY_KEY }} - - # Step 2: Set the app name from repository name - - name: Set app env - run: | - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - # Step 3: Calculate the next beta version - # This reads the main version, increments the patch, and adds/increments the beta suffix - - name: Get current version and append beta suffix - id: increment_version - run: | - # Get the stable version from main branch as base - git fetch origin main - main_version=$(git show origin/main:appinfo/info.xml | grep -oP '(?<=)[^<]+' || echo "") - - # Get current version from beta-release branch - current_version=$(grep -oP '(?<=)[^<]+' appinfo/info.xml || echo "") - - # Split main version into parts (e.g., 1.2.5 -> [1, 2, 5]) - IFS='.' read -ra main_version_parts <<< "$main_version" - - # Increment patch version by 1 from main (e.g., 1.2.5 -> 1.2.6) - next_patch=$((main_version_parts[2] + 1)) - - # Extract beta counter from current version if it exists - # If current version is 1.2.6-beta.1, we'll increment to 1.2.6-beta.2 - beta_counter=1 - if [[ $current_version =~ -beta\.([0-9]+)$ ]]; then - # If current patch version matches next patch, increment the counter - current_patch=$(echo $current_version | grep -oP '^[0-9]+\.[0-9]+\.(\d+)' | cut -d. -f3) - if [ "$current_patch" -eq "$next_patch" ]; then - beta_counter=$((BASH_REMATCH[1] + 1)) - fi - fi - - # Build the new beta version string - beta_version="${main_version_parts[0]}.${main_version_parts[1]}.${next_patch}-beta.${beta_counter}" - - # Export version for use in subsequent steps - echo "NEW_VERSION=$beta_version" >> $GITHUB_ENV - echo "new_version=$beta_version" >> $GITHUB_OUTPUT - echo "Main version: $main_version" - echo "Current version: $current_version" - echo "Using beta version: $beta_version" - - # Step 4: Update the version in info.xml - - name: Update version in info.xml - run: | - sed -i "s|.*|${{ env.NEW_VERSION }}|" appinfo/info.xml - - # Step 5: Commit the new version if there are changes - # The [skip ci] prevents triggering the sync workflow again - - name: Commit version update - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - - # Only commit if there are actual changes - if git diff --quiet && git diff --cached --quiet; then - echo "No changes to commit" - else - git add appinfo/info.xml - git commit -m "Bump beta version to ${{ env.NEW_VERSION }} [skip ci]" - git push - fi - - # Step 6: Prepare signing certificates for Nextcloud app signing - - name: Prepare Signing Certificate and Key - run: | - echo "${{ secrets.NEXTCLOUD_SIGNING_CERT }}" > signing-cert.crt - echo "${{ secrets.NEXTCLOUD_SIGNING_KEY }}" > signing-key.key - - # Step 7: Install Node.js dependencies - - name: Install npm dependencies - uses: actions/setup-node@v3 - with: - node-version: '18.x' - - # Step 8: Set up PHP with required extensions - - name: Set up PHP and install extensions - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - extensions: zip, gd - - # Step 9: Build the app (install dependencies and compile assets) - - run: npm ci - - run: npm run build - # Use production-optimized composer flags for better performance - - run: composer install --no-dev --optimize-autoloader --classmap-authoritative - - # Step 10: Copy files to package directory, excluding development files - # This creates a clean distribution package without test files, config files, etc. - - name: Copy the package files into the package - run: | - mkdir -p package/${{ github.event.repository.name }} - rsync -av --progress \ - --exclude='/package' \ - --exclude='/.git' \ - --exclude='/.github' \ - --exclude='/.cursor' \ - --exclude='/.vscode' \ - --exclude='/node_modules' \ - --exclude='/src' \ - --exclude='/tests' \ - --exclude='/package.json' \ - --exclude='/package-lock.json' \ - --exclude='/composer.json' \ - --exclude='/composer.lock' \ - --exclude='/phpcs.xml' \ - --exclude='/phpmd.xml' \ - --exclude='/psalm.xml' \ - --exclude='/phpunit.xml' \ - --exclude='/.phpunit.cache' \ - --exclude='.phpunit.result.cache' \ - --exclude='/jest.config.js' \ - --exclude='/webpack.config.js' \ - --exclude='/tsconfig.json' \ - --exclude='/.babelrc' \ - --exclude='/.eslintrc.js' \ - --exclude='/.prettierrc' \ - --exclude='/stylelint.config.js' \ - --exclude='/.gitignore' \ - --exclude='/.gitattributes' \ - --exclude='/signing-key.key' \ - --exclude='/signing-cert.crt' \ - ./ package/${{ github.event.repository.name }}/ - - # Step 11: Create compressed tarball archive - - name: Create Tarball - run: | - cd package && tar -czf ../nextcloud-release.tar.gz ${{ github.event.repository.name }} - - # Step 12: Sign the tarball with private key for Nextcloud verification - - name: Sign the TAR.GZ file with OpenSSL - run: | - openssl dgst -sha512 -sign signing-key.key nextcloud-release.tar.gz | openssl base64 -out nextcloud-release.signature - - # Step 13: Upload tarball and signature as GitHub artifact for debugging/reference - - name: Upload tarball as artifact - uses: actions/upload-artifact@v4 - with: - name: nextcloud-release-${{ env.NEW_VERSION }} - path: | - nextcloud-release.tar.gz - nextcloud-release.signature - retention-days: 30 - - # Step 14: Generate git version information - - name: Git Version - id: version - uses: codacy/git-version@2.7.1 - with: - release-branch: beta-release - - # Step 15: Create GitHub release (marked as prerelease) - - name: Upload Beta Release - uses: ncipollo/release-action@v1.12.0 - with: - tag: v${{ env.NEW_VERSION }} - name: Beta Release ${{ env.NEW_VERSION }} - draft: false - prerelease: true - skipIfReleaseExists: true - - # Step 16: Attach tarball to the GitHub release - - name: Attach tarball to GitHub release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: nextcloud-release.tar.gz - asset_name: ${{ env.APP_NAME }}-${{ env.NEW_VERSION }}.tar.gz - tag: v${{ env.NEW_VERSION }} - overwrite: true - - # Step 17: Upload the app to Nextcloud App Store - # nightly: false means this is a regular beta release, not a nightly build - - name: Upload app to Nextcloud appstore - uses: nextcloud-releases/nextcloud-appstore-push-action@a011fe619bcf6e77ddebc96f9908e1af4071b9c1 - with: - app_name: ${{ env.APP_NAME }} - appstore_token: ${{ secrets.NEXTCLOUD_APPSTORE_TOKEN }} - download_url: https://github.com/${{ github.repository }}/releases/download/v${{ env.NEW_VERSION }}/${{ env.APP_NAME }}-${{ env.NEW_VERSION }}.tar.gz - app_private_key: ${{ secrets.NEXTCLOUD_SIGNING_KEY }} - nightly: false - - # Step 18: Verify the release contents - # head -50 limits output to first 50 files for readability - - name: Verify release - run: | - echo "App version: ${{ env.NEW_VERSION }}" - echo "Tarball contents:" - tar -tvf nextcloud-release.tar.gz | head -50 - echo "info.xml contents:" - tar -xOf nextcloud-release.tar.gz ${{ env.APP_NAME }}/appinfo/info.xml diff --git a/.github/workflows/branch-protection.yml b/.github/workflows/branch-protection.yml new file mode 100644 index 000000000..a6d20bbf7 --- /dev/null +++ b/.github/workflows/branch-protection.yml @@ -0,0 +1,9 @@ +name: Branch Protection + +on: + pull_request: + branches: [main, beta] + +jobs: + check: + uses: ConductionNL/.github/.github/workflows/branch-protection.yml@main diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml deleted file mode 100644 index abec09269..000000000 --- a/.github/workflows/phpcs.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: php-cs - -# Disabled: superseded by code-quality.yml -on: - pull_request: - branches: [never] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - tools: cs2pr, phpcs - - - name: Run phpcs - run: phpcs -q --report=checkstyle lib | cs2pr - continue-on-error: true \ No newline at end of file diff --git a/.github/workflows/pull-request-from-branch-check.yaml b/.github/workflows/pull-request-from-branch-check.yaml deleted file mode 100644 index 1c264ed99..000000000 --- a/.github/workflows/pull-request-from-branch-check.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: Branch Protection - -on: - pull_request: - branches: - - main - - beta - -jobs: - check-branch: - runs-on: ubuntu-latest - steps: - - name: Check branch - run: | - TARGET="${{ github.base_ref }}" - SOURCE="${{ github.head_ref }}" - - if [[ "$TARGET" == "main" ]]; then - if [[ "$SOURCE" != "beta" ]] && ! [[ "$SOURCE" =~ ^hotfix ]]; then - echo "Error: Pull requests to main must come from 'beta' or a branch starting with 'hotfix'" - echo "Source branch: $SOURCE" - exit 1 - fi - elif [[ "$TARGET" == "beta" ]]; then - if [[ "$SOURCE" != "development" ]] && ! [[ "$SOURCE" =~ ^hotfix ]]; then - echo "Error: Pull requests to beta must come from 'development' or a branch starting with 'hotfix'" - echo "Source branch: $SOURCE" - exit 1 - fi - fi - - echo "Branch check passed: $SOURCE -> $TARGET" diff --git a/.github/workflows/pull-request-lint-check.yaml b/.github/workflows/pull-request-lint-check.yaml deleted file mode 100644 index a31957f7b..000000000 --- a/.github/workflows/pull-request-lint-check.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: Lint Check - -on: - pull_request: - branches: - - development - - beta - - main - -jobs: - lint-check: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Install dependencies - run: npm i - - - name: Linting - run: npm run lint diff --git a/.github/workflows/pull-request-quality-gate.yml b/.github/workflows/pull-request-quality-gate.yml deleted file mode 100644 index 7d5edcb94..000000000 --- a/.github/workflows/pull-request-quality-gate.yml +++ /dev/null @@ -1,247 +0,0 @@ -name: Pull Request Quality Gate - -on: - pull_request: - branches: - # - main - # - master - # - development - # - dev - # - beta - - never - -jobs: - quality-gate: - name: Quality Gate Check - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.1' - extensions: mbstring, xml, ctype, iconv, intl, pdo, pdo_mysql, dom, filter, gd, json, posix, simplexml, xmlreader, xmlwriter, zip - coverage: xdebug - tools: composer:v2 - - - name: Install dependencies - run: composer install --prefer-dist --no-progress --no-interaction - - - name: Initialize Quality Gate Results - run: | - echo "QUALITY_GATE_PASSED=true" >> $GITHUB_ENV - echo "## 🚦 Quality Gate Status" >> $GITHUB_STEP_SUMMARY - - - name: Check 1 - PHP Syntax - id: syntax - run: | - echo "### ✅ Check 1: PHP Syntax" >> $GITHUB_STEP_SUMMARY - if composer lint; then - echo "✅ No syntax errors found" >> $GITHUB_STEP_SUMMARY - echo "status=pass" >> $GITHUB_OUTPUT - else - echo "❌ FAILED: Syntax errors detected" >> $GITHUB_STEP_SUMMARY - echo "status=fail" >> $GITHUB_OUTPUT - echo "QUALITY_GATE_PASSED=false" >> $GITHUB_ENV - fi - - - name: Check 2 - Coding Standards (PHPCS) - id: phpcs - run: | - echo "### Check 2: Coding Standards (PHPCS)" >> $GITHUB_STEP_SUMMARY - composer cs:check - composer phpcs:output - if [ -f phpcs-output.json ]; then - ERRORS=$(jq '.totals.errors' phpcs-output.json) - echo "errors=$ERRORS" >> $GITHUB_OUTPUT - if [ "$ERRORS" -eq "0" ]; then - echo "✅ PASSED: No PHPCS errors" >> $GITHUB_STEP_SUMMARY - echo "status=pass" >> $GITHUB_OUTPUT - else - echo "❌ FAILED: Found $ERRORS PHPCS errors (must be 0)" >> $GITHUB_STEP_SUMMARY - echo "status=fail" >> $GITHUB_OUTPUT - echo "QUALITY_GATE_PASSED=false" >> $GITHUB_ENV - fi - fi - continue-on-error: true - - - name: Check 3 - Code Quality (PHPMD) - id: phpmd - run: | - echo "### Check 3: Code Quality (PHPMD)" >> $GITHUB_STEP_SUMMARY - composer phpmd > phpmd-output.txt || true - VIOLATIONS=$(cat phpmd-output.txt | wc -l) - echo "violations=$VIOLATIONS" >> $GITHUB_OUTPUT - - # Allow up to 50 minor violations, fail if more - if [ "$VIOLATIONS" -le "50" ]; then - echo "✅ PASSED: $VIOLATIONS PHPMD violations (acceptable)" >> $GITHUB_STEP_SUMMARY - echo "status=pass" >> $GITHUB_OUTPUT - else - echo "❌ FAILED: $VIOLATIONS PHPMD violations (max 50)" >> $GITHUB_STEP_SUMMARY - echo "status=fail" >> $GITHUB_OUTPUT - echo "QUALITY_GATE_PASSED=false" >> $GITHUB_ENV - fi - continue-on-error: true - - - name: Check 4 - Overall Quality Score - id: quality - run: | - echo "### Check 4: Overall Quality Score" >> $GITHUB_STEP_SUMMARY - composer phpqa:ci || true - - if [ -f phpqa/phpqa.json ]; then - # Calculate composite quality score - PHPCS_ERRORS="${{ steps.phpcs.outputs.errors }}" - PHPMD_VIOLATIONS="${{ steps.phpmd.outputs.violations }}" - - # Score calculation: Start at 100, deduct points for issues - SCORE=$(echo "scale=2; 100 - ($PHPCS_ERRORS * 2) - ($PHPMD_VIOLATIONS * 0.1)" | bc) - - # Ensure score doesn't go negative - if (( $(echo "$SCORE < 0" | bc -l) )); then - SCORE=0 - fi - - echo "score=$SCORE" >> $GITHUB_OUTPUT - echo "Overall Quality Score: **$SCORE%**" >> $GITHUB_STEP_SUMMARY - - if (( $(echo "$SCORE >= 90" | bc -l) )); then - echo "✅ PASSED: Quality score meets 90% threshold" >> $GITHUB_STEP_SUMMARY - echo "status=pass" >> $GITHUB_OUTPUT - else - echo "❌ FAILED: Quality score ($SCORE%) below 90% threshold" >> $GITHUB_STEP_SUMMARY - echo "status=fail" >> $GITHUB_OUTPUT - echo "QUALITY_GATE_PASSED=false" >> $GITHUB_ENV - fi - else - echo "⚠️ WARNING: Could not calculate quality score" >> $GITHUB_STEP_SUMMARY - echo "status=unknown" >> $GITHUB_OUTPUT - fi - continue-on-error: true - - - name: Check 5 - Unit Tests - id: tests - run: | - echo "### Check 5: Unit Tests" >> $GITHUB_STEP_SUMMARY - if composer test:unit; then - echo "✅ PASSED: All unit tests pass" >> $GITHUB_STEP_SUMMARY - echo "status=pass" >> $GITHUB_OUTPUT - else - echo "⚠️ WARNING: Tests require Nextcloud environment" >> $GITHUB_STEP_SUMMARY - echo "status=skipped" >> $GITHUB_OUTPUT - fi - continue-on-error: true - - - name: Final Quality Gate Decision - run: | - echo "## 🏁 Final Result" >> $GITHUB_STEP_SUMMARY - - if [ "$QUALITY_GATE_PASSED" = "true" ]; then - echo "### ✅ QUALITY GATE PASSED" >> $GITHUB_STEP_SUMMARY - echo "This PR meets all quality requirements and can be merged." >> $GITHUB_STEP_SUMMARY - exit 0 - else - echo "### ❌ QUALITY GATE FAILED" >> $GITHUB_STEP_SUMMARY - echo "This PR does not meet quality requirements." >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Required actions:**" >> $GITHUB_STEP_SUMMARY - - if [ "${{ steps.syntax.outputs.status }}" = "fail" ]; then - echo "- Fix PHP syntax errors" >> $GITHUB_STEP_SUMMARY - fi - - if [ "${{ steps.phpcs.outputs.status }}" = "fail" ]; then - echo "- Fix PHPCS errors (run 'composer cs:fix')" >> $GITHUB_STEP_SUMMARY - fi - - if [ "${{ steps.phpmd.outputs.status }}" = "fail" ]; then - echo "- Refactor code to reduce PHPMD violations" >> $GITHUB_STEP_SUMMARY - fi - - if [ "${{ steps.quality.outputs.status }}" = "fail" ]; then - echo "- Improve overall code quality to reach 90% score" >> $GITHUB_STEP_SUMMARY - fi - - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Hint:** Run 'composer phpqa' locally to see detailed quality reports." >> $GITHUB_STEP_SUMMARY - exit 1 - fi - - - name: Upload Quality Reports - if: always() - uses: actions/upload-artifact@v3 - with: - name: quality-gate-reports - path: | - phpqa/ - phpcs-output.json - phpmd-output.txt - retention-days: 30 - - - name: Post Quality Gate Summary to PR - if: github.event_name == 'pull_request' - uses: actions/github-script@v7 - with: - script: | - const passed = '${{ env.QUALITY_GATE_PASSED }}' === 'true'; - const syntaxStatus = '${{ steps.syntax.outputs.status }}'; - const phpcsStatus = '${{ steps.phpcs.outputs.status }}'; - const phpmdStatus = '${{ steps.phpmd.outputs.status }}'; - const qualityStatus = '${{ steps.quality.outputs.status }}'; - const testsStatus = '${{ steps.tests.outputs.status }}'; - - const qualityScore = '${{ steps.quality.outputs.score }}'; - const phpcsErrors = '${{ steps.phpcs.outputs.errors }}'; - const phpmdViolations = '${{ steps.phpmd.outputs.violations }}'; - - const statusEmoji = (status) => { - if (status === 'pass') return '✅'; - if (status === 'fail') return '❌'; - if (status === 'skipped') return '⚠️'; - return '❓'; - }; - - const body = `## 🚦 Quality Gate Status: ${passed ? '✅ PASSED' : '❌ FAILED'} - - | Check | Status | Details | - |-------|--------|---------| - | PHP Syntax | ${statusEmoji(syntaxStatus)} ${syntaxStatus.toUpperCase()} | All PHP files must be valid | - | Coding Standards (PHPCS) | ${statusEmoji(phpcsStatus)} ${phpcsStatus.toUpperCase()} | Errors: ${phpcsErrors} (must be 0) | - | Code Quality (PHPMD) | ${statusEmoji(phpmdStatus)} ${phpmdStatus.toUpperCase()} | Violations: ${phpmdViolations} (max 50) | - | Overall Quality Score | ${statusEmoji(qualityStatus)} ${qualityStatus.toUpperCase()} | Score: ${qualityScore}% (min 90%) | - | Unit Tests | ${statusEmoji(testsStatus)} ${testsStatus.toUpperCase()} | Test suite status | - - ### Requirements for Merge - - ${passed ? - '✅ **All quality checks passed!** This PR meets the requirements for merging.' : - '❌ **Quality gate failed.** Please address the issues above before merging.'} - - ${!passed ? ` - ### How to Fix - - 1. Run \`composer cs:fix\` to auto-fix coding standards - 2. Run \`composer phpqa\` to see detailed quality reports - 3. Review the generated report at \`phpqa/phpqa-offline.html\` - 4. Fix any critical issues and re-push your changes - - 📚 See [Quality Assurance Documentation](../docs/quality-assurance.md) for more details. - ` : ''} - - 📊 Detailed reports are available in the workflow artifacts. - `; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: body - }); - continue-on-error: true - diff --git a/.github/workflows/push-beta-to-beta-release.yaml b/.github/workflows/push-beta-to-beta-release.yaml deleted file mode 100644 index d3eddf26a..000000000 --- a/.github/workflows/push-beta-to-beta-release.yaml +++ /dev/null @@ -1,71 +0,0 @@ -name: Sync Beta to Beta-Release - -permissions: - contents: write - actions: write - -on: - push: - branches: - - beta - -jobs: - sync-to-beta-release: - runs-on: ubuntu-latest - steps: - # Step 1: Checkout the repository with full history - - name: Checkout Code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.sha }} - ssh-key: ${{ secrets.DEPLOY_KEY }} - - # Step 2: Configure Git to use SSH for authentication - - name: Configure Git SSH - run: | - git config --global user.email "action@github.com" - git config --global user.name "GitHub Action" - git remote set-url origin git@github.com:${{ github.repository }}.git - - # Step 3: Sync beta to beta-release while preserving the beta version - - name: Update beta-release branch - run: | - # Fetch all branches from remote - git fetch origin - - # Get the commit message that triggered this workflow - COMMIT_MSG=$(git log -1 --pretty=%B) - - # Store the current beta-release version if the branch exists - # This version will be restored after syncing to maintain version continuity - BETA_RELEASE_VERSION="" - if git show-ref --quiet refs/remotes/origin/beta-release; then - git checkout origin/beta-release - BETA_RELEASE_VERSION=$(grep -oP '(?<=)[^<]+' appinfo/info.xml || echo "") - echo "Found existing beta-release version: $BETA_RELEASE_VERSION" - fi - - # Check if beta-release branch exists - if git show-ref --quiet refs/remotes/origin/beta-release; then - # Branch exists: checkout and reset to latest beta state - git checkout beta-release - git reset --hard origin/beta # Sync all changes from beta - else - # Branch doesn't exist: create new branch from beta - git checkout -b beta-release origin/beta - fi - - # Restore the beta-release version if it existed - # This ensures version increments are preserved across syncs - if [ ! -z "$BETA_RELEASE_VERSION" ]; then - sed -i "s|.*|${BETA_RELEASE_VERSION}|" appinfo/info.xml - git add appinfo/info.xml - git commit -m "${COMMIT_MSG} - - Restored beta-release version to ${BETA_RELEASE_VERSION}" - fi - - # Push to beta-release branch using SSH with force - # Force push is safe here because we control this branch - git push -f origin beta-release diff --git a/.github/workflows/push-development-to-development-release.yaml b/.github/workflows/push-development-to-development-release.yaml deleted file mode 100644 index 6251c8551..000000000 --- a/.github/workflows/push-development-to-development-release.yaml +++ /dev/null @@ -1,71 +0,0 @@ -name: Sync Development to Development-Release - -permissions: - contents: write - -on: - push: - branches: - # - development - - never - -jobs: - sync-to-development-release: - runs-on: ubuntu-latest - steps: - # Step 1: Checkout the repository with full history - - name: Checkout Code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.sha }} - ssh-key: ${{ secrets.DEPLOY_KEY }} - - # Step 2: Configure Git to use SSH for authentication - - name: Configure Git SSH - run: | - git config --global user.email "action@github.com" - git config --global user.name "GitHub Action" - git remote set-url origin git@github.com:${{ github.repository }}.git - - # Step 3: Sync development to development-release while preserving the unstable version - - name: Update development-release branch - run: | - # Fetch all branches from remote - git fetch origin - - # Get the commit message that triggered this workflow - COMMIT_MSG=$(git log -1 --pretty=%B) - - # Store the current development-release version if the branch exists - # This version will be restored after syncing to maintain version continuity - DEV_RELEASE_VERSION="" - if git show-ref --quiet refs/remotes/origin/development-release; then - git checkout origin/development-release - DEV_RELEASE_VERSION=$(grep -oP '(?<=)[^<]+' appinfo/info.xml || echo "") - echo "Found existing development-release version: $DEV_RELEASE_VERSION" - fi - - # Check if development-release branch exists - if git show-ref --quiet refs/remotes/origin/development-release; then - # Branch exists: checkout and reset to latest development state - git checkout development-release - git reset --hard origin/development # Sync all changes from development - else - # Branch doesn't exist: create new branch from development - git checkout -b development-release origin/development - fi - - # Restore the development-release version if it existed - # This ensures version increments are preserved across syncs - if [ ! -z "$DEV_RELEASE_VERSION" ]; then - sed -i "s|.*|${DEV_RELEASE_VERSION}|" appinfo/info.xml - git add appinfo/info.xml - git commit -m "${COMMIT_MSG} - - Restored development-release version to ${DEV_RELEASE_VERSION}" - fi - - # Push to development-release branch using SSH with force - # Force push is safe here because we control this branch - git push -f origin development-release diff --git a/.github/workflows/quality-check.yml b/.github/workflows/quality-check.yml deleted file mode 100644 index d240aea76..000000000 --- a/.github/workflows/quality-check.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Quality - -on: - push: - branches: [main, development, feature/**, bugfix/**, hotfix/**] - pull_request: - branches: [main, master, development] - -jobs: - quality: - uses: ConductionNL/.github/.github/workflows/quality.yml@main - with: - app-name: openconnector - php-version: "8.3" - nextcloud-ref: stable32 - enable-psalm: true - enable-phpstan: true - enable-phpmetrics: true - enable-frontend: true - enable-eslint: true - enable-phpunit: true diff --git a/.github/workflows/release-beta.yml b/.github/workflows/release-beta.yml new file mode 100644 index 000000000..9219ba053 --- /dev/null +++ b/.github/workflows/release-beta.yml @@ -0,0 +1,12 @@ +name: Beta Release + +on: + push: + branches: [beta] + +jobs: + release: + uses: ConductionNL/.github/.github/workflows/release-beta.yml@main + with: + app-name: openconnector + secrets: inherit diff --git a/.github/workflows/release-stable.yml b/.github/workflows/release-stable.yml new file mode 100644 index 000000000..d55c1bfbf --- /dev/null +++ b/.github/workflows/release-stable.yml @@ -0,0 +1,12 @@ +name: Stable Release + +on: + push: + branches: [main] + +jobs: + release: + uses: ConductionNL/.github/.github/workflows/release-stable.yml@main + with: + app-name: openconnector + secrets: inherit diff --git a/.github/workflows/stable-release.yaml b/.github/workflows/stable-release.yaml deleted file mode 100644 index 583318028..000000000 --- a/.github/workflows/stable-release.yaml +++ /dev/null @@ -1,221 +0,0 @@ -name: Stable Release - -on: - push: - branches: - - main - workflow_dispatch: - inputs: - version: - description: 'Version to release (leave empty to use info.xml version)' - required: false - default: '' - -jobs: - release-management: - runs-on: ubuntu-latest - steps: - - - name: Checkout Code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ssh-key: ${{ secrets.DEPLOY_KEY }} - - - name: Set app env - run: | - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Get current version and increment - id: increment_version - run: | - current_version=$(grep -oP '(?<=)[^<]+' appinfo/info.xml) - IFS='.' read -ra version_parts <<< "$current_version" - ((version_parts[2]++)) - new_version="${version_parts[0]}.${version_parts[1]}.${version_parts[2]}" - echo "NEW_VERSION=$new_version" >> $GITHUB_ENV - echo "new_version=$new_version" >> $GITHUB_OUTPUT - - - name: Update version in info.xml - run: | - sed -i "s|.*|${{ env.NEW_VERSION }}|" appinfo/info.xml - - - name: Commit version update - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git commit -am "Bump version to ${{ env.NEW_VERSION }}" -m "[skip ci]" - git push - - # Step 1: Prepare the signing certificate and key - - name: Prepare Signing Certificate and Key - run: | - echo "${{ secrets.NEXTCLOUD_SIGNING_CERT }}" > signing-cert.crt - echo "${{ secrets.NEXTCLOUD_SIGNING_KEY }}" > signing-key.key - - # Step 3: Install Node.js dependencies using npm - - name: Install npm dependencies - uses: actions/setup-node@v3 - with: - node-version: '18.x' # Specify Node.js version - - # Step 4: Install PHP extensions - - name: Set up PHP and install extensions - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - extensions: zip, gd - - # Step 5: Build the node dependencies - - run: npm ci - - # Step 6: Build the node dependencies - - run: npm run build - - # Step 7: Build composer dependencies - - run: composer i --no-dev - - # Step 8: Copy the files into the package directory - - name: Copy the package files into the package - run: | - mkdir -p package/${{ github.event.repository.name }} - rsync -av --progress \ - --exclude='package' \ - --exclude='.git' \ - --exclude='.github' \ - --exclude='.vscode' \ - --exclude='docker' \ - --exclude='docs' \ - --exclude='website' \ - --exclude='node_modules' \ - --exclude='/src' \ - --exclude='test' \ - --exclude='package-lock.json' \ - --exclude='composer.lock' \ - --exclude='composer-setup.php' \ - --exclude='.phpunit.result.cache' \ - --exclude='phpmd.xml' \ - --exclude='signing-key.key' \ - --exclude='package.json' \ - --exclude='composer.json' \ - --exclude='coverage.txt' \ - --exclude='signing-cert.crt' \ - --exclude='docker-compose.yml' \ - --exclude='webpack.config.js' \ - --exclude='.prettierrc' \ - --exclude='psalm.xml' \ - --exclude='phpunit.xml' \ - --exclude='tsconfig.json' \ - --exclude='changelog-ci-config.json' \ - --exclude='jest.config.js' \ - --exclude='.gitattributes' \ - --exclude='.php-cs-fixer.dist.php' \ - --exclude='.gitignore' \ - --exclude='.eslintrc.js' \ - --exclude='stylelint.config.js' \ - --exclude='.babelrc' \ - --exclude='.nvmrc' \ - ./ package/${{ github.event.repository.name }}/ - - # Step 9: Create the TAR.GZ archive - - name: Create Tarball - run: | - cd package && tar -czf ../nextcloud-release.tar.gz ${{ github.event.repository.name }} - - # Step 10: Sign the TAR.GZ file with OpenSSL - - name: Sign the TAR.GZ file with OpenSSL - run: | - openssl dgst -sha512 -sign signing-key.key nextcloud-release.tar.gz | openssl base64 -out nextcloud-release.signature - - # Step 11: Generate Git version information - - name: Git Version - id: version - uses: codacy/git-version@2.7.1 - with: - release-branch: main - - # Step 12: Extract repository description - - name: Extract repository description - id: repo-description - run: | - description=$(jq -r '.description' <(curl -s https://api.github.com/repos/${{ github.repository }})) - echo "REPO_DESCRIPTION=$description" >> $GITHUB_ENV - - # Step 14: Output the version - - name: Use the version - run: | - echo ${{ steps.version.outputs.version }} - - # Step 15: Copy the package files into the package (this step seems redundant, consider removing) - - name: Copy the package files into the package - run: | - mkdir -p package/${{ github.event.repository.name }} - rsync -av --progress --exclude='package' --exclude='.git' ./ package/${{ github.event.repository.name }}/ - - # Step 18: Create a new release on GitHub - - name: Upload Release - uses: ncipollo/release-action@v1.12.0 - with: - tag: v${{ env.NEW_VERSION }} - name: Release ${{ env.NEW_VERSION }} - draft: false - prerelease: false - - - name: Attach tarball to github release - uses: svenstaro/upload-release-action@04733e069f2d7f7f0b4aebc4fbdbce8613b03ccd # v2 - id: attach_to_release - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: nextcloud-release.tar.gz # Corrected spelling - asset_name: ${{ env.APP_NAME }}-${{ env.NEW_VERSION }}.tar.gz - tag: v${{ env.NEW_VERSION }} - overwrite: true - - - name: Upload app to Nextcloud appstore - uses: nextcloud-releases/nextcloud-appstore-push-action@a011fe619bcf6e77ddebc96f9908e1af4071b9c1 # v1 - with: - app_name: ${{ env.APP_NAME }} - appstore_token: ${{ secrets.NEXTCLOUD_APPSTORE_TOKEN }} - download_url: https://github.com/${{ github.repository }}/releases/download/v${{ env.NEW_VERSION }}/${{ env.APP_NAME }}-${{ env.NEW_VERSION }}.tar.gz - app_private_key: ${{ secrets.NEXTCLOUD_SIGNING_KEY }} - nightly: false - - - name: Verify version and contents - run: | - echo "App version: ${{ env.NEW_VERSION }}" - echo "Tarball contents:" - tar -tvf nextcloud-release.tar.gz - echo "info.xml contents:" - tar -xOf nextcloud-release.tar.gz ${{ env.APP_NAME }}/appinfo/info.xml - - update-changelog: - runs-on: ubuntu-latest - steps: - - - name: Checkout Code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set app env - run: | - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Get current version and increment - id: increment_version - run: | - current_version=$(grep -oP '(?<=)[^<]+' appinfo/info.xml) - IFS='.' read -ra version_parts <<< "$current_version" - ((version_parts[2]++)) - new_version="${version_parts[0]}.${version_parts[1]}.${version_parts[2]}" - echo "NEW_VERSION=$new_version" >> $GITHUB_ENV - echo "new_version=$new_version" >> $GITHUB_OUTPUT - - # Step 13: Run Changelog CI - - name: Run Changelog CI - if: github.ref == 'refs/heads/main' - uses: saadmk11/changelog-ci@v1.1.2 - with: - persist-credentials: true - release_version: ${{ env.NEW_VERSION }} - config_file: changelog-ci-config.json diff --git a/.github/workflows/sync-to-beta.yml b/.github/workflows/sync-to-beta.yml new file mode 100644 index 000000000..d5e325bb2 --- /dev/null +++ b/.github/workflows/sync-to-beta.yml @@ -0,0 +1,9 @@ +name: Sync to Beta + +on: + push: + branches: [development] + +jobs: + sync: + uses: ConductionNL/.github/.github/workflows/sync-to-beta.yml@main diff --git a/.github/workflows/unstable-release.yaml b/.github/workflows/unstable-release.yaml deleted file mode 100644 index 9d9d1ae37..000000000 --- a/.github/workflows/unstable-release.yaml +++ /dev/null @@ -1,215 +0,0 @@ -name: Unstable Release - -on: - push: - branches: - # - development-release - - never - -jobs: - release-management: - runs-on: ubuntu-latest - steps: - - # Step 1: Checkout the repository with full history - - name: Checkout Code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ssh-key: ${{ secrets.DEPLOY_KEY }} - - # Step 2: Set the app name from repository name - - name: Set app env - run: | - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - # Step 3: Calculate the next unstable version - # This reads the main version, increments the patch, and adds/increments the unstable suffix - - name: Get current version and append unstable suffix - id: increment_version - run: | - # Get the stable version from main branch as base - git fetch origin main - main_version=$(git show origin/main:appinfo/info.xml | grep -oP '(?<=)[^<]+' || echo "") - - # Get current version from development-release branch - current_version=$(grep -oP '(?<=)[^<]+' appinfo/info.xml || echo "") - - # Split main version into parts (e.g., 1.2.5 -> [1, 2, 5]) - IFS='.' read -ra main_version_parts <<< "$main_version" - - # Increment patch version by 1 from main (e.g., 1.2.5 -> 1.2.6) - next_patch=$((main_version_parts[2] + 1)) - - # Extract unstable counter from current version if it exists - # If current version is 1.2.6-unstable.1, we'll increment to 1.2.6-unstable.2 - unstable_counter=1 - if [[ $current_version =~ -unstable\.([0-9]+)$ ]]; then - # If current patch version matches next patch, increment the counter - current_patch=$(echo $current_version | grep -oP '^[0-9]+\.[0-9]+\.(\d+)' | cut -d. -f3) - if [ "$current_patch" -eq "$next_patch" ]; then - unstable_counter=$((BASH_REMATCH[1] + 1)) - fi - fi - - # Build the new unstable version string - unstable_version="${main_version_parts[0]}.${main_version_parts[1]}.${next_patch}-unstable.${unstable_counter}" - - # Export version for use in subsequent steps - echo "NEW_VERSION=$unstable_version" >> $GITHUB_ENV - echo "new_version=$unstable_version" >> $GITHUB_OUTPUT - echo "Main version: $main_version" - echo "Current version: $current_version" - echo "Using unstable version: $unstable_version" - - # Step 4: Update the version in info.xml - - name: Update version in info.xml - run: | - sed -i "s|.*|${{ env.NEW_VERSION }}|" appinfo/info.xml - - # Step 5: Commit the new version if there are changes - # The [skip ci] prevents triggering the sync workflow again - - name: Commit version update - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - - # Only commit if there are actual changes - if git diff --quiet && git diff --cached --quiet; then - echo "No changes to commit" - else - git add appinfo/info.xml - git commit -m "Bump unstable version to ${{ env.NEW_VERSION }} [skip ci]" - git push - fi - - # Step 6: Prepare signing certificates for Nextcloud app signing - - name: Prepare Signing Certificate and Key - run: | - echo "${{ secrets.NEXTCLOUD_SIGNING_CERT }}" > signing-cert.crt - echo "${{ secrets.NEXTCLOUD_SIGNING_KEY }}" > signing-key.key - - # Step 7: Install Node.js dependencies - - name: Install npm dependencies - uses: actions/setup-node@v3 - with: - node-version: '18.x' - - # Step 8: Set up PHP with required extensions - - name: Set up PHP and install extensions - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - extensions: zip, gd - - # Step 9: Build the app (install dependencies and compile assets) - - run: npm ci - - run: npm run build - # Use production-optimized composer flags for better performance - - run: composer install --no-dev --optimize-autoloader --classmap-authoritative - - # Step 10: Copy files to package directory, excluding development files - # This creates a clean distribution package without test files, config files, etc. - - name: Copy the package files into the package - run: | - mkdir -p package/${{ github.event.repository.name }} - rsync -av --progress \ - --exclude='/package' \ - --exclude='/.git' \ - --exclude='/.github' \ - --exclude='/.cursor' \ - --exclude='/.vscode' \ - --exclude='/node_modules' \ - --exclude='/src' \ - --exclude='/tests' \ - --exclude='/package.json' \ - --exclude='/package-lock.json' \ - --exclude='/composer.json' \ - --exclude='/composer.lock' \ - --exclude='/phpcs.xml' \ - --exclude='/phpmd.xml' \ - --exclude='/psalm.xml' \ - --exclude='/phpunit.xml' \ - --exclude='/.phpunit.cache' \ - --exclude='.phpunit.result.cache' \ - --exclude='/jest.config.js' \ - --exclude='/webpack.config.js' \ - --exclude='/tsconfig.json' \ - --exclude='/.babelrc' \ - --exclude='/.eslintrc.js' \ - --exclude='/.prettierrc' \ - --exclude='/stylelint.config.js' \ - --exclude='/.gitignore' \ - --exclude='/.gitattributes' \ - --exclude='/signing-key.key' \ - --exclude='/signing-cert.crt' \ - ./ package/${{ github.event.repository.name }}/ - - # Step 11: Create compressed tarball archive - - name: Create Tarball - run: | - cd package && tar -czf ../nextcloud-release.tar.gz ${{ github.event.repository.name }} - - # Step 12: Sign the tarball with private key for Nextcloud verification - - name: Sign the TAR.GZ file with OpenSSL - run: | - openssl dgst -sha512 -sign signing-key.key nextcloud-release.tar.gz | openssl base64 -out nextcloud-release.signature - - # Step 13: Upload tarball and signature as GitHub artifact for debugging/reference - - name: Upload tarball as artifact - uses: actions/upload-artifact@v4 - with: - name: nextcloud-release-${{ env.NEW_VERSION }} - path: | - nextcloud-release.tar.gz - nextcloud-release.signature - retention-days: 30 - - # Step 14: Generate git version information - - name: Git Version - id: version - uses: codacy/git-version@2.7.1 - with: - release-branch: development-release - - # Step 15: Create GitHub release (marked as prerelease) - # skipIfReleaseExists prevents errors if the release was already created - - name: Upload Unstable Release - uses: ncipollo/release-action@v1.12.0 - with: - tag: v${{ env.NEW_VERSION }} - name: Unstable Release ${{ env.NEW_VERSION }} - draft: false - prerelease: true - skipIfReleaseExists: true - - # Step 16: Attach tarball to the GitHub release - - name: Attach tarball to GitHub release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: nextcloud-release.tar.gz - asset_name: ${{ env.APP_NAME }}-${{ env.NEW_VERSION }}.tar.gz - tag: v${{ env.NEW_VERSION }} - overwrite: true - - # Step 17: Upload the app to Nextcloud App Store - # nightly: true marks this as a nightly/unstable build in the app store - - name: Upload app to Nextcloud appstore - uses: nextcloud-releases/nextcloud-appstore-push-action@a011fe619bcf6e77ddebc96f9908e1af4071b9c1 - with: - app_name: ${{ env.APP_NAME }} - appstore_token: ${{ secrets.NEXTCLOUD_APPSTORE_TOKEN }} - download_url: https://github.com/${{ github.repository }}/releases/download/v${{ env.NEW_VERSION }}/${{ env.APP_NAME }}-${{ env.NEW_VERSION }}.tar.gz - app_private_key: ${{ secrets.NEXTCLOUD_SIGNING_KEY }} - nightly: true - - # Step 18: Verify the release contents - # head -50 limits output to first 50 files for readability - - name: Verify release - run: | - echo "App version: ${{ env.NEW_VERSION }}" - echo "Tarball contents:" - tar -tvf nextcloud-release.tar.gz | head -50 - echo "info.xml contents:" - tar -xOf nextcloud-release.tar.gz ${{ env.APP_NAME }}/appinfo/info.xml From 3f3b3ae51d100a3f1a73337be2efa87e08a0142e Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 13 Mar 2026 16:40:35 +0100 Subject: [PATCH 5/7] fix: Fix composer.lock out-of-sync with composer.json Update edgedesign/phpqa constraint from ^1.27 to ^1.9 and phpcsstandards/phpcsextra from ^1.4 to ^1.5 to resolve the symfony/console conflict that prevented composer install from succeeding. --- composer.json | 6 +- composer.lock | 1217 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 1076 insertions(+), 147 deletions(-) diff --git a/composer.json b/composer.json index 6985b9035..bbd5652ae 100644 --- a/composer.json +++ b/composer.json @@ -85,14 +85,14 @@ "web-token/jwt-framework": "^3" }, "require-dev": { - "edgedesign/phpqa": "^1.27", + "edgedesign/phpqa": "^1.9", "nextcloud/coding-standard": "^1.4", "nextcloud/ocp": "dev-stable29", - "phpcsstandards/phpcsextra": "^1.4", + "phpcsstandards/phpcsextra": "^1.5", "phpmd/phpmd": "^2.15", "phpmetrics/phpmetrics": "^2.8", - "phpunit/phpunit": "^10.5", "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", "roave/security-advisories": "dev-latest", "squizlabs/php_codesniffer": "^3.9", "vimeo/psalm": "^5.26" diff --git a/composer.lock b/composer.lock index c77f83c69..e05e8182a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f5b97cf556459431b2f77f8b8b806970", + "content-hash": "16fd4c2c3661ab9a448b662c0cd542c4", "packages": [ { "name": "adbario/php-dot-notation", @@ -645,16 +645,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.8.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "21dc724a0583619cd1652f673303492272778051" + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", - "reference": "21dc724a0583619cd1652f673303492272778051", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", "shasum": "" }, "require": { @@ -670,6 +670,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", + "jshttp/mime-db": "1.54.0.1", "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { @@ -741,7 +742,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.8.0" + "source": "https://github.com/guzzle/psr7/tree/2.9.0" }, "funding": [ { @@ -757,7 +758,7 @@ "type": "tidelift" } ], - "time": "2025-08-23T21:21:41+00:00" + "time": "2026-03-10T16:41:02+00:00" }, { "name": "jwadhams/json-logic-php", @@ -1688,20 +1689,20 @@ }, { "name": "php-soap/engine", - "version": "2.17.0", + "version": "2.18.0", "source": { "type": "git", "url": "https://github.com/php-soap/engine.git", - "reference": "dd097d8d996f76ce378df48e4e93f44500c4d3da" + "reference": "f2c04747ffa9143a9ba5f8950a455b78d718ddce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-soap/engine/zipball/dd097d8d996f76ce378df48e4e93f44500c4d3da", - "reference": "dd097d8d996f76ce378df48e4e93f44500c4d3da", + "url": "https://api.github.com/repos/php-soap/engine/zipball/f2c04747ffa9143a9ba5f8950a455b78d718ddce", + "reference": "f2c04747ffa9143a9ba5f8950a455b78d718ddce", "shasum": "" }, "require": { - "azjezz/psl": "^3.0 || ^4.0", + "azjezz/psl": "^3.0 || ^4.0 || ^5.0", "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "php-soap/xml": "^1.9.0" }, @@ -1730,7 +1731,7 @@ "description": "SOAP engine design", "support": { "issues": "https://github.com/php-soap/engine/issues", - "source": "https://github.com/php-soap/engine/tree/2.17.0" + "source": "https://github.com/php-soap/engine/tree/2.18.0" }, "funding": [ { @@ -1738,24 +1739,24 @@ "type": "open_collective" } ], - "time": "2025-11-24T11:11:05+00:00" + "time": "2026-03-11T13:20:54+00:00" }, { "name": "php-soap/ext-soap-engine", - "version": "1.9.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/php-soap/ext-soap-engine.git", - "reference": "cd76481c039ddf70d837a2835db3fef06fab0bb6" + "reference": "66a35ea255a3f4a9483a4c4e63a9a6af7bb5e350" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-soap/ext-soap-engine/zipball/cd76481c039ddf70d837a2835db3fef06fab0bb6", - "reference": "cd76481c039ddf70d837a2835db3fef06fab0bb6", + "url": "https://api.github.com/repos/php-soap/ext-soap-engine/zipball/66a35ea255a3f4a9483a4c4e63a9a6af7bb5e350", + "reference": "66a35ea255a3f4a9483a4c4e63a9a6af7bb5e350", "shasum": "" }, "require": { - "azjezz/psl": "^3.0 || ^4.0", + "azjezz/psl": "^3.0 || ^4.0 || ^5.0", "ext-dom": "*", "ext-soap": "*", "php": "~8.3.0 || ~8.4.0 || ~8.5.0", @@ -1789,7 +1790,7 @@ "description": "An ext-soap engine implementation", "support": { "issues": "https://github.com/php-soap/ext-soap-engine/issues", - "source": "https://github.com/php-soap/ext-soap-engine/tree/1.9.0" + "source": "https://github.com/php-soap/ext-soap-engine/tree/1.10.0" }, "funding": [ { @@ -1797,7 +1798,7 @@ "type": "open_collective" } ], - "time": "2025-12-01T07:15:53+00:00" + "time": "2026-03-11T15:08:22+00:00" }, { "name": "php-soap/psr18-transport", @@ -1869,20 +1870,20 @@ }, { "name": "php-soap/wsdl", - "version": "1.16.0", + "version": "1.17.0", "source": { "type": "git", "url": "https://github.com/php-soap/wsdl.git", - "reference": "331c520f6b3edbf14064d0000d8472155aea6c91" + "reference": "68f5d0f565a614e02c29e74cabf5a54d46d32dcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-soap/wsdl/zipball/331c520f6b3edbf14064d0000d8472155aea6c91", - "reference": "331c520f6b3edbf14064d0000d8472155aea6c91", + "url": "https://api.github.com/repos/php-soap/wsdl/zipball/68f5d0f565a614e02c29e74cabf5a54d46d32dcf", + "reference": "68f5d0f565a614e02c29e74cabf5a54d46d32dcf", "shasum": "" }, "require": { - "azjezz/psl": "^3.0 || ^4.0", + "azjezz/psl": "^3.0 || ^4.0 || ^5.0", "ext-dom": "*", "league/uri": "^7.0", "league/uri-components": "^7.0", @@ -1919,7 +1920,7 @@ "description": "Deals with WSDLs", "support": { "issues": "https://github.com/php-soap/wsdl/issues", - "source": "https://github.com/php-soap/wsdl/tree/1.16.0" + "source": "https://github.com/php-soap/wsdl/tree/1.17.0" }, "funding": [ { @@ -1927,7 +1928,7 @@ "type": "open_collective" } ], - "time": "2025-12-01T07:15:42+00:00" + "time": "2026-03-11T14:47:12+00:00" }, { "name": "php-soap/xml", @@ -2915,16 +2916,16 @@ }, { "name": "symfony/config", - "version": "v7.4.6", + "version": "v7.4.7", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "9400e2f9226b3b64ebb0a8ae967ae84e54e39640" + "reference": "6c17162555bfb58957a55bb0e43e00035b6ae3d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/9400e2f9226b3b64ebb0a8ae967ae84e54e39640", - "reference": "9400e2f9226b3b64ebb0a8ae967ae84e54e39640", + "url": "https://api.github.com/repos/symfony/config/zipball/6c17162555bfb58957a55bb0e43e00035b6ae3d5", + "reference": "6c17162555bfb58957a55bb0e43e00035b6ae3d5", "shasum": "" }, "require": { @@ -2970,7 +2971,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.4.6" + "source": "https://github.com/symfony/config/tree/v7.4.7" }, "funding": [ { @@ -2990,7 +2991,7 @@ "type": "tidelift" } ], - "time": "2026-02-25T16:50:00+00:00" + "time": "2026-03-06T10:41:14+00:00" }, { "name": "symfony/console", @@ -3093,16 +3094,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v7.4.6", + "version": "v7.4.7", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "a3f7d594ca53a34a7d39ae683fbca09408b0c598" + "reference": "0f651e58f4917fb0e2cd261ccbfe3d71e6e0f5db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a3f7d594ca53a34a7d39ae683fbca09408b0c598", - "reference": "a3f7d594ca53a34a7d39ae683fbca09408b0c598", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/0f651e58f4917fb0e2cd261ccbfe3d71e6e0f5db", + "reference": "0f651e58f4917fb0e2cd261ccbfe3d71e6e0f5db", "shasum": "" }, "require": { @@ -3153,7 +3154,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.4.6" + "source": "https://github.com/symfony/dependency-injection/tree/v7.4.7" }, "funding": [ { @@ -3173,7 +3174,7 @@ "type": "tidelift" } ], - "time": "2026-02-25T16:50:00+00:00" + "time": "2026-03-03T07:48:48+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3557,16 +3558,16 @@ }, { "name": "symfony/http-client", - "version": "v7.4.6", + "version": "v7.4.7", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "2bde8afd5ab2fe0b05a9c2d4c3c0e28ceb98a154" + "reference": "1010624285470eb60e88ed10035102c75b4ea6af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/2bde8afd5ab2fe0b05a9c2d4c3c0e28ceb98a154", - "reference": "2bde8afd5ab2fe0b05a9c2d4c3c0e28ceb98a154", + "url": "https://api.github.com/repos/symfony/http-client/zipball/1010624285470eb60e88ed10035102c75b4ea6af", + "reference": "1010624285470eb60e88ed10035102c75b4ea6af", "shasum": "" }, "require": { @@ -3634,7 +3635,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.4.6" + "source": "https://github.com/symfony/http-client/tree/v7.4.7" }, "funding": [ { @@ -3654,7 +3655,7 @@ "type": "tidelift" } ], - "time": "2026-02-18T09:46:18+00:00" + "time": "2026-03-05T11:16:58+00:00" }, { "name": "symfony/http-client-contracts", @@ -3736,16 +3737,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.4.6", + "version": "v7.4.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "fd97d5e926e988a363cef56fbbf88c5c528e9065" + "reference": "f94b3e7b7dafd40e666f0c9ff2084133bae41e81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/fd97d5e926e988a363cef56fbbf88c5c528e9065", - "reference": "fd97d5e926e988a363cef56fbbf88c5c528e9065", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f94b3e7b7dafd40e666f0c9ff2084133bae41e81", + "reference": "f94b3e7b7dafd40e666f0c9ff2084133bae41e81", "shasum": "" }, "require": { @@ -3794,7 +3795,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.4.6" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.7" }, "funding": [ { @@ -3814,20 +3815,20 @@ "type": "tidelift" } ], - "time": "2026-02-21T16:25:55+00:00" + "time": "2026-03-06T13:15:18+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.34", + "version": "v6.4.35", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "006a49fc4f41ee21a6ca61e69caed1c30b29f07c" + "reference": "ece1a0da7745a5243683f178155c0412c92691eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/006a49fc4f41ee21a6ca61e69caed1c30b29f07c", - "reference": "006a49fc4f41ee21a6ca61e69caed1c30b29f07c", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/ece1a0da7745a5243683f178155c0412c92691eb", + "reference": "ece1a0da7745a5243683f178155c0412c92691eb", "shasum": "" }, "require": { @@ -3912,7 +3913,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.34" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.35" }, "funding": [ { @@ -3932,24 +3933,24 @@ "type": "tidelift" } ], - "time": "2026-02-26T08:27:11+00:00" + "time": "2026-03-06T16:28:07+00:00" }, { "name": "symfony/options-resolver", - "version": "v8.0.0", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7" + "reference": "b38026df55197f9e39a44f3215788edf83187b80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d2b592535ffa6600c265a3893a7f7fd2bad82dd7", - "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b38026df55197f9e39a44f3215788edf83187b80", + "reference": "b38026df55197f9e39a44f3215788edf83187b80", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -3983,7 +3984,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v8.0.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.4.0" }, "funding": [ { @@ -4003,7 +4004,7 @@ "type": "tidelift" } ], - "time": "2025-11-12T15:55:31+00:00" + "time": "2025-11-12T15:39:26+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5171,25 +5172,26 @@ }, { "name": "symfony/var-exporter", - "version": "v8.0.0", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04" + "reference": "03a60f169c79a28513a78c967316fbc8bf17816f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/03a60f169c79a28513a78c967316fbc8bf17816f", + "reference": "03a60f169c79a28513a78c967316fbc8bf17816f", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" }, "require-dev": { - "symfony/property-access": "^7.4|^8.0", - "symfony/serializer": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^8.0" + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5227,7 +5229,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v8.0.0" + "source": "https://github.com/symfony/var-exporter/tree/v7.4.0" }, "funding": [ { @@ -5247,7 +5249,7 @@ "type": "tidelift" } ], - "time": "2025-11-05T18:53:00+00:00" + "time": "2025-09-11T10:15:23+00:00" }, { "name": "symfony/yaml", @@ -5406,20 +5408,20 @@ }, { "name": "veewee/xml", - "version": "3.4.0", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/veewee/xml.git", - "reference": "8799d7b81b60759b9776c10b8047a7e1ae364800" + "reference": "05d8539c3c166cbf872b38fa1f0ff00e3a410959" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/veewee/xml/zipball/8799d7b81b60759b9776c10b8047a7e1ae364800", - "reference": "8799d7b81b60759b9776c10b8047a7e1ae364800", + "url": "https://api.github.com/repos/veewee/xml/zipball/05d8539c3c166cbf872b38fa1f0ff00e3a410959", + "reference": "05d8539c3c166cbf872b38fa1f0ff00e3a410959", "shasum": "" }, "require": { - "azjezz/psl": "^3.0 || ^4.0", + "azjezz/psl": "^3.0 || ^4.0 || ^5.0", "ext-dom": "*", "ext-libxml": "*", "ext-xml": "*", @@ -5474,7 +5476,7 @@ ], "support": { "issues": "https://github.com/veewee/xml/issues", - "source": "https://github.com/veewee/xml/tree/3.4.0" + "source": "https://github.com/veewee/xml/tree/3.5.0" }, "funding": [ { @@ -5482,7 +5484,7 @@ "type": "github" } ], - "time": "2025-10-15T08:12:16+00:00" + "time": "2026-03-11T13:15:00+00:00" }, { "name": "web-token/jwt-framework", @@ -6090,6 +6092,145 @@ ], "time": "2024-05-06T16:37:16+00:00" }, + { + "name": "consolidation/robo", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/consolidation/Robo.git", + "reference": "5eefc310a749d89c5247eb011e2d45abbdd4404f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/5eefc310a749d89c5247eb011e2d45abbdd4404f", + "reference": "5eefc310a749d89c5247eb011e2d45abbdd4404f", + "shasum": "" + }, + "require": { + "symfony/console": "*" + }, + "bin": [ + "robo" + ], + "type": "library", + "autoload": { + "psr-4": { + "Robo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "Modern task runner", + "support": { + "issues": "https://github.com/consolidation/Robo/issues", + "source": "https://github.com/consolidation/Robo/tree/0.1.1" + }, + "time": "2014-01-25T02:13:51+00:00" + }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1", + "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.2", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "^2.2", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev", + "yoast/phpunit-polyfills": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "opensource@frenck.dev", + "homepage": "https://frenck.dev", + "role": "Open source developer" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", + "source": "https://github.com/PHPCSStandards/composer-installer" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-11T04:32:07+00:00" + }, { "name": "dnoegel/php-xdg-base-dir", "version": "v0.1.1", @@ -6175,6 +6316,81 @@ }, "time": "2026-02-07T07:09:04+00:00" }, + { + "name": "edgedesign/phpqa", + "version": "v1.9.1", + "source": { + "type": "git", + "url": "https://github.com/EdgedesignCZ/phpqa.git", + "reference": "1f6cd80a9a597ee3b71e6afda6903af67c788143" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/EdgedesignCZ/phpqa/zipball/1f6cd80a9a597ee3b71e6afda6903af67c788143", + "reference": "1f6cd80a9a597ee3b71e6afda6903af67c788143", + "shasum": "" + }, + "require": { + "consolidation/robo": "<1", + "ext-xsl": "*", + "pdepend/pdepend": "*", + "php": ">=5.4", + "phploc/phploc": "*", + "phpmd/phpmd": "*", + "phpmetrics/phpmetrics": "*", + "sebastian/phpcpd": "*", + "squizlabs/php_codesniffer": "*", + "symfony/dependency-injection": ">=2.8", + "symfony/filesystem": ">=2.8", + "symfony/finder": ">=2.8", + "symfony/process": ">=2.8", + "symfony/yaml": ">=2.8" + }, + "require-dev": { + "hamcrest/hamcrest-php": "*", + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "jakub-onderka/php-console-highlighter": "Colored output in parallel-lint", + "jakub-onderka/php-parallel-lint": "Check PHP syntax", + "phpstan/phpstan": "PHP Static Analysis Tool - discover bugs in your code without running it!" + }, + "bin": [ + "phpqa" + ], + "type": "library", + "autoload": { + "files": [ + "src/report.php", + "src/paths.php" + ], + "psr-4": { + "Edge\\QA\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Zdenek Drahos", + "email": "drahoszdenek@gmail.com" + } + ], + "description": "Analyze PHP code with one command.", + "keywords": [ + "code analysis", + "qa", + "static analysis" + ], + "support": { + "docs": "https://edgedesigncz.github.io/phpqa/", + "issues": "https://github.com/EdgedesignCZ/phpqa/issues", + "source": "https://github.com/EdgedesignCZ/phpqa" + }, + "time": "2017-01-08T09:19:53+00:00" + }, { "name": "felixfbecker/advanced-json-rpc", "version": "v3.2.1", @@ -6337,6 +6553,58 @@ ], "time": "2025-08-14T07:29:31+00:00" }, + { + "name": "kubawerlos/php-cs-fixer-custom-fixers", + "version": "v3.36.1", + "source": { + "type": "git", + "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", + "reference": "ec9776e80f5ea9bf0d8ec16d662e431bfab19a24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/ec9776e80f5ea9bf0d8ec16d662e431bfab19a24", + "reference": "ec9776e80f5ea9bf0d8ec16d662e431bfab19a24", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-tokenizer": "*", + "friendsofphp/php-cs-fixer": "^3.87", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6.24 || ^10.5.51 || ^11.5.44" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpCsFixerCustomFixers\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kuba Werłos", + "email": "werlos@gmail.com" + } + ], + "description": "A set of custom fixers for PHP CS Fixer", + "support": { + "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.36.1" + }, + "funding": [ + { + "url": "https://github.com/kubawerlos", + "type": "github" + } + ], + "time": "2026-03-07T11:35:13+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.13.4", @@ -6448,6 +6716,51 @@ }, "time": "2024-09-08T10:13:13+00:00" }, + { + "name": "nextcloud/coding-standard", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/nextcloud/coding-standard.git", + "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/8e06808c1423e9208d63d1bd205b9a38bd400011", + "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011", + "shasum": "" + }, + "require": { + "kubawerlos/php-cs-fixer-custom-fixers": "^3.22", + "php": "^8.0", + "php-cs-fixer/shim": "^3.17" + }, + "type": "library", + "autoload": { + "psr-4": { + "Nextcloud\\CodingStandard\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at" + } + ], + "description": "Nextcloud coding standards for the php cs fixer", + "keywords": [ + "dev" + ], + "support": { + "issues": "https://github.com/nextcloud/coding-standard/issues", + "source": "https://github.com/nextcloud/coding-standard/tree/v1.4.0" + }, + "time": "2025-06-19T12:27:27+00:00" + }, { "name": "nextcloud/ocp", "version": "dev-stable29", @@ -6725,75 +7038,302 @@ "time": "2022-02-21T01:04:05+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "name": "php-cs-fixer/shim", + "version": "v3.94.2", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "url": "https://github.com/PHP-CS-Fixer/shim.git", + "reference": "80fd29f44a736136a2f05bae5464816a444b91d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/80fd29f44a736136a2f05bae5464816a444b91d1", + "reference": "80fd29f44a736136a2f05bae5464816a444b91d1", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } + "replace": { + "friendsofphp/php-cs-fixer": "self.version" }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." }, + "bin": [ + "php-cs-fixer", + "php-cs-fixer.phar" + ], + "type": "application", "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" } ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], + "description": "A tool to automatically fix PHP code style", "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "issues": "https://github.com/PHP-CS-Fixer/shim/issues", + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.94.2" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2026-02-20T16:14:17+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "5.6.6", + "name": "phpcsstandards/phpcsextra", + "version": "1.5.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8" + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "b598aa890815b8df16363271b659d73280129101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8", - "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/b598aa890815b8df16363271b659d73280129101", + "reference": "b598aa890815b8df16363271b659d73280129101", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.1", - "ext-filter": "*", + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.2.0", + "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.2.0", + "phpcsstandards/phpcsdevtools": "^1.2.1", + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" + } + ], + "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", + "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSExtra" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-12T23:06:57+00:00" + }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/c216317e96c8b3f5932808f9b0f1f7a14e3bbf55", + "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.2.0", + "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPCSUtils/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" + } + ], + "description": "A suite of utility functions for use with PHP_CodeSniffer", + "homepage": "https://phpcsutils.com/", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "phpcs3", + "phpcs4", + "standards", + "static analysis", + "tokens", + "utility" + ], + "support": { + "docs": "https://phpcsutils.com/", + "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", + "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSUtils" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-12-08T14:27:58+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.6", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", "phpdocumentor/type-resolver": "^1.7", @@ -6899,6 +7439,61 @@ }, "time": "2025-11-21T15:09:14+00:00" }, + { + "name": "phploc/phploc", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phploc.git", + "reference": "d177c22e2a08e448f7bdfa762045f7bd086834d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phploc/zipball/d177c22e2a08e448f7bdfa762045f7bd086834d7", + "reference": "d177c22e2a08e448f7bdfa762045f7bd086834d7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/finder-facade": ">=1.1.0", + "sebastian/git": ">=1.0.0", + "sebastian/version": ">=1.0.3", + "symfony/console": ">=2.2.0" + }, + "bin": [ + "phploc" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "A tool for quickly measuring the size of a PHP project.", + "homepage": "https://github.com/sebastianbergmann/phploc", + "support": { + "issues": "https://github.com/sebastianbergmann/phploc/issues", + "source": "https://github.com/sebastianbergmann/phploc/tree/2.0.5" + }, + "abandoned": true, + "time": "2014-04-27T06:47:27+00:00" + }, { "name": "phpmd/phpmd", "version": "2.15.0", @@ -7588,18 +8183,18 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "5a17df9a478a03dff7428a3172cf7204d4d017a8" + "reference": "ed005b7ebb25c82bb29af88d634357073f3b7a33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/5a17df9a478a03dff7428a3172cf7204d4d017a8", - "reference": "5a17df9a478a03dff7428a3172cf7204d4d017a8", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/ed005b7ebb25c82bb29af88d634357073f3b7a33", + "reference": "ed005b7ebb25c82bb29af88d634357073f3b7a33", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", "adaptcms/adaptcms": "<=1.3", - "admidio/admidio": "<=4.3.16", + "admidio/admidio": "<5.0.6", "adodb/adodb-php": "<=5.22.9", "aheinze/cockpit": "<2.2", "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", @@ -7650,7 +8245,7 @@ "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", "aws/aws-sdk-php": "<3.368", - "azuracast/azuracast": "<=0.23.1", + "azuracast/azuracast": "<=0.23.3", "b13/seo_basics": "<0.8.2", "backdrop/backdrop": "<=1.32", "backpack/crud": "<3.4.9", @@ -7722,7 +8317,7 @@ "commerceteam/commerce": ">=0.9.6,<0.9.9", "components/jquery": ">=1.0.3,<3.5", "composer/composer": "<1.10.27|>=2,<2.2.26|>=2.3,<2.9.3", - "concrete5/concrete5": "<9.4.3", + "concrete5/concrete5": "<9.4.8", "concrete5/core": "<8.5.8|>=9,<9.1", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", @@ -7736,8 +8331,8 @@ "cosenary/instagram": "<=2.3", "couleurcitron/tarteaucitron-wp": "<0.3", "cpsit/typo3-mailqueue": "<0.4.3|>=0.5,<0.5.1", - "craftcms/cms": "<4.17.0.0-beta1|>=5,<5.9.0.0-beta1", - "craftcms/commerce": ">=4.0.0.0-RC1-dev,<=4.10|>=5,<=5.5.1", + "craftcms/cms": "<=4.17.3|>=5,<=5.9.8", + "craftcms/commerce": ">=4,<4.11|>=5,<5.6", "craftcms/composer": ">=4.0.0.0-RC1-dev,<=4.10|>=5.0.0.0-RC1-dev,<=5.5.1", "craftcms/craft": ">=3.5,<=4.16.17|>=5.0.0.0-RC1-dev,<=5.8.21", "croogo/croogo": "<=4.0.7", @@ -7815,7 +8410,7 @@ "drupal/umami_analytics": "<1.0.1", "duncanmcclean/guest-entries": "<3.1.2", "dweeves/magmi": "<=0.7.24", - "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", + "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.3.1", "ecodev/newsletter": "<=4", "ectouch/ectouch": "<=2.7.2", "egroupware/egroupware": "<23.1.20260113|>=26.0.20251208,<26.0.20260113", @@ -7872,6 +8467,7 @@ "flarum/flarum": "<0.1.0.0-beta8", "flarum/framework": "<1.8.10", "flarum/mentions": "<1.6.3", + "flarum/nicknames": "<1.8.3", "flarum/sticky": ">=0.1.0.0-beta14,<=0.1.0.0-beta15", "flarum/tags": "<=0.1.0.0-beta13", "floriangaerber/magnesium": "<0.3.1", @@ -7918,7 +8514,7 @@ "gp247/core": "<1.1.24", "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", - "grumpydictator/firefly-iii": "<6.1.17", + "grumpydictator/firefly-iii": "<6.1.17|>=6.4.23,<=6.5", "gugoan/economizzer": "<=0.9.0.0-beta1", "guzzlehttp/guzzle": "<6.5.8|>=7,<7.4.5", "guzzlehttp/oauth-subscriber": "<0.8.1", @@ -7994,7 +8590,7 @@ "kelvinmo/simplexrd": "<3.1.1", "kevinpapst/kimai2": "<1.16.7", "khodakhah/nodcms": "<=3", - "kimai/kimai": "<2.46", + "kimai/kimai": "<=2.50", "kitodo/presentation": "<3.2.3|>=3.3,<3.3.4", "klaviyo/magento2-extension": ">=1,<3", "knplabs/knp-snappy": "<=1.4.2", @@ -8019,7 +8615,7 @@ "lavalite/cms": "<=10.1", "lavitto/typo3-form-to-database": "<2.2.5|>=3,<3.2.2|>=4,<4.2.3|>=5,<5.0.2", "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", - "league/commonmark": "<2.7", + "league/commonmark": "<=2.8", "league/flysystem": "<1.1.4|>=2,<2.1.1", "league/oauth2-server": ">=8.3.2,<8.4.2|>=8.5,<8.5.3", "leantime/leantime": "<3.3", @@ -8028,7 +8624,7 @@ "librenms/librenms": "<26.2", "liftkit/database": "<2.13.2", "lightsaml/lightsaml": "<1.3.5", - "limesurvey/limesurvey": "<6.5.12", + "limesurvey/limesurvey": "<6.15.4", "livehelperchat/livehelperchat": "<=3.91", "livewire-filemanager/filemanager": "<=1.0.4", "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.6.4", @@ -8273,8 +8869,8 @@ "setasign/fpdi": "<2.6.4", "sfroemken/url_redirect": "<=1.2.1", "sheng/yiicms": "<1.2.1", - "shopware/core": "<6.6.10.9-dev|>=6.7,<6.7.6.1-dev", - "shopware/platform": "<6.6.10.7-dev|>=6.7,<6.7.3.1-dev", + "shopware/core": "<6.6.10.15-dev|>=6.7,<6.7.8.1-dev", + "shopware/platform": "<6.6.10.15-dev|>=6.7,<6.7.8.1-dev", "shopware/production": "<=6.3.5.2", "shopware/shopware": "<=5.7.17|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.6.1-dev", "shopware/storefront": "<6.6.10.10-dev|>=6.7,<6.7.5.1-dev", @@ -8318,7 +8914,7 @@ "slim/slim": "<2.6", "slub/slub-events": "<3.0.3", "smarty/smarty": "<4.5.3|>=5,<5.1.1", - "snipe/snipe-it": "<=8.3.4", + "snipe/snipe-it": "<8.3.7", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", "solspace/craft-freeform": "<4.1.29|>=5,<=5.14.6", @@ -8355,7 +8951,7 @@ "sylius/grid-bundle": "<1.10.1", "sylius/paypal-plugin": "<1.6.2|>=1.7,<1.7.2|>=2,<2.0.2", "sylius/resource-bundle": ">=1,<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", - "sylius/sylius": "<1.12.19|>=1.13.0.0-alpha1,<1.13.4", + "sylius/sylius": "<1.9.12|>=1.10,<1.10.16|>=1.11,<1.11.17|>=1.12,<=1.12.22|>=1.13,<=1.13.14|>=1.14,<=1.14.17|>=2,<=2.0.15|>=2.1,<=2.1.11|>=2.2,<=2.2.2", "symbiote/silverstripe-multivaluefield": ">=3,<3.1", "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", "symbiote/silverstripe-seed": "<6.0.3", @@ -8483,8 +9079,9 @@ "wallabag/wallabag": "<2.6.11", "wanglelecc/laracms": "<=1.0.3", "wapplersystems/a21glossary": "<=0.4.10", - "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9", - "web-auth/webauthn-lib": ">=4.5,<4.9", + "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9|>=5.2,<5.2.4", + "web-auth/webauthn-lib": ">=4.5,<4.9|>=5.2,<5.2.4", + "web-auth/webauthn-symfony-bundle": ">=5.2,<5.2.4", "web-feet/coastercms": "==5.5", "web-tp3/wec_map": "<3.0.3", "webbuilders-group/silverstripe-kapost-bridge": "<0.4", @@ -8496,7 +9093,7 @@ "wikibase/wikibase": "<=1.39.3", "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", - "winter/wn-backend-module": "<1.2.4", + "winter/wn-backend-module": "<1.2.12", "winter/wn-cms-module": "<=1.2.9", "winter/wn-dusk-plugin": "<2.1", "winter/wn-system-module": "<1.2.4", @@ -8509,7 +9106,8 @@ "wpanel/wpanel4-cms": "<=4.3.1", "wpcloud/wp-stateless": "<3.2", "wpglobus/wpglobus": "<=1.9.6", - "wwbn/avideo": "<=21", + "wpmetabox/meta-box": "<5.11.2", + "wwbn/avideo": "<25", "xataface/xataface": "<3", "xpressengine/xpressengine": "<3.0.15", "yab/quarx": "<2.4.5", @@ -8607,7 +9205,7 @@ "type": "tidelift" } ], - "time": "2026-03-03T18:18:18+00:00" + "time": "2026-03-12T14:21:15+00:00" }, { "name": "sebastian/cli-parser", @@ -9145,6 +9743,100 @@ ], "time": "2025-09-24T06:09:11+00:00" }, + { + "name": "sebastian/finder-facade", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/finder-facade.git", + "reference": "1e396fda3449fce9df032749fa4fa2619e0347e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/1e396fda3449fce9df032749fa4fa2619e0347e0", + "reference": "1e396fda3449fce9df032749fa4fa2619e0347e0", + "shasum": "" + }, + "require": { + "symfony/finder": ">=2.2.0", + "theseer/fdomdocument": ">=1.3.1" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", + "homepage": "https://github.com/sebastianbergmann/finder-facade", + "support": { + "issues": "https://github.com/sebastianbergmann/finder-facade/issues", + "source": "https://github.com/sebastianbergmann/finder-facade/tree/master" + }, + "abandoned": true, + "time": "2013-05-28T06:10:03+00:00" + }, + { + "name": "sebastian/git", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/git.git", + "reference": "815bbbc963cf35e5413df195aa29df58243ecd24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/git/zipball/815bbbc963cf35e5413df195aa29df58243ecd24", + "reference": "815bbbc963cf35e5413df195aa29df58243ecd24", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Simple wrapper for Git", + "homepage": "http://www.github.com/sebastianbergmann/git", + "keywords": [ + "git" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/git/issues", + "source": "https://github.com/sebastianbergmann/git/tree/master" + }, + "abandoned": true, + "time": "2017-01-23T20:57:12+00:00" + }, { "name": "sebastian/global-state", "version": "6.0.2", @@ -9377,6 +10069,62 @@ ], "time": "2023-02-03T07:06:18+00:00" }, + { + "name": "sebastian/phpcpd", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpcpd.git", + "reference": "a9462153f2dd90466a010179901d31fbff598365" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/a9462153f2dd90466a010179901d31fbff598365", + "reference": "a9462153f2dd90466a010179901d31fbff598365", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-timer": ">=1.0.4", + "sebastian/finder-facade": ">=1.1.0", + "sebastian/version": ">=1.0.3", + "symfony/console": ">=2.2.0", + "theseer/fdomdocument": "~1.4" + }, + "bin": [ + "phpcpd" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Copy/Paste Detector (CPD) for PHP code.", + "homepage": "https://github.com/sebastianbergmann/phpcpd", + "support": { + "issues": "https://github.com/sebastianbergmann/phpcpd/issues", + "source": "https://github.com/sebastianbergmann/phpcpd/tree/2.0.1" + }, + "abandoned": true, + "time": "2014-03-31T09:25:30+00:00" + }, { "name": "sebastian/recursion-context", "version": "5.0.1", @@ -9709,6 +10457,187 @@ ], "time": "2025-11-04T16:30:35+00:00" }, + { + "name": "symfony/finder", + "version": "v7.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "8655bf1076b7a3a346cb11413ffdabff50c7ffcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/8655bf1076b7a3a346cb11413ffdabff50c7ffcf", + "reference": "8655bf1076b7a3a346cb11413ffdabff50c7ffcf", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-29T09:40:50+00:00" + }, + { + "name": "symfony/process", + "version": "v7.4.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "608476f4604102976d687c483ac63a79ba18cc97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/608476f4604102976d687c483ac63a79ba18cc97", + "reference": "608476f4604102976d687c483ac63a79ba18cc97", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.4.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-26T15:07:59+00:00" + }, + { + "name": "theseer/fdomdocument", + "version": "1.6.7", + "source": { + "type": "git", + "url": "https://github.com/theseer/fDOMDocument.git", + "reference": "5cddd4f9076a9a2b85c5135935bba2dcb3ed7574" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/5cddd4f9076a9a2b85c5135935bba2dcb3ed7574", + "reference": "5cddd4f9076a9a2b85c5135935bba2dcb3ed7574", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "lib-libxml": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "php": ">=7.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "lead" + } + ], + "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.", + "homepage": "https://github.com/theseer/fDOMDocument", + "support": { + "issues": "https://github.com/theseer/fDOMDocument/issues", + "source": "https://github.com/theseer/fDOMDocument/tree/1.6.7" + }, + "abandoned": true, + "time": "2022-01-25T23:10:35+00:00" + }, { "name": "theseer/tokenizer", "version": "1.3.1", @@ -9884,9 +10813,9 @@ "ext-libxml": "*", "ext-simplexml": "*" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "8.3" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } From f4106bc1661f3e78a82f36f7eb8216f19f117e19 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 13 Mar 2026 16:44:45 +0100 Subject: [PATCH 6/7] fix: Rename branch-protection job to lint-check and add development branch Rename the caller job from 'check' to 'lint-check' so the GitHub status check context satisfies the required 'lint-check' check on development, beta, and main branch protection rulesets. --- .github/workflows/branch-protection.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/branch-protection.yml b/.github/workflows/branch-protection.yml index a6d20bbf7..c58709ea5 100644 --- a/.github/workflows/branch-protection.yml +++ b/.github/workflows/branch-protection.yml @@ -2,8 +2,8 @@ name: Branch Protection on: pull_request: - branches: [main, beta] + branches: [main, beta, development] jobs: - check: + lint-check: uses: ConductionNL/.github/.github/workflows/branch-protection.yml@main From 789d59b457ef36a20071a5ddf4d86f2d7b0767b9 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Fri, 13 Mar 2026 16:47:35 +0100 Subject: [PATCH 7/7] fix: Add inline lint-check job to branch-protection workflow Add a direct 'lint-check' job (npm lint) alongside the reusable branch-protection caller so that the required 'lint-check' status check context is satisfied for development, beta, and main PRs. --- .github/workflows/branch-protection.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/branch-protection.yml b/.github/workflows/branch-protection.yml index c58709ea5..0a42fb805 100644 --- a/.github/workflows/branch-protection.yml +++ b/.github/workflows/branch-protection.yml @@ -6,4 +6,22 @@ on: jobs: lint-check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint + + branch-check: uses: ConductionNL/.github/.github/workflows/branch-protection.yml@main