diff --git a/.github/workflows/deploy_docs_5x.yml b/.github/workflows/deploy_docs_5x.yml index 699f9cc3..90187f6e 100644 --- a/.github/workflows/deploy_docs_5x.yml +++ b/.github/workflows/deploy_docs_5x.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Cloning repo - uses: actions/checkout@v2 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -20,5 +20,6 @@ jobs: uses: dokku/github-action@master with: git_remote_url: 'ssh://dokku@apps.cakephp.org:22/elasticsearch-docs-5' + git_push_flags: '-f' ssh_private_key: ${{ secrets.DOKKU_SSH_PRIVATE_KEY }} branch: '5.x' diff --git a/.github/workflows/docs-validation.yml b/.github/workflows/docs-validation.yml new file mode 100644 index 00000000..439a52c1 --- /dev/null +++ b/.github/workflows/docs-validation.yml @@ -0,0 +1,27 @@ +name: Documentation Validation + +on: + push: + branches: + - 5.x + paths: + - 'docs/**' + - '.github/**' + pull_request: + paths: + - 'docs/**' + - '.github/**' + +jobs: + validate: + uses: cakephp/.github/.github/workflows/docs-validation.yml@5.x + with: + docs-path: 'docs' + vitepress-path: 'docs/.vitepress' + enable-config-js-check: true + enable-json-lint: true + enable-toc-check: true + enable-spell-check: true + enable-markdown-lint: true + enable-link-check: true + tools-ref: '5.x' diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..d1f12952 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +# ---------------------- +# 1. Build stage +# ---------------------- +FROM node:24-alpine AS builder + +# Git is required because docs/package.json pulls a dependency from GitHub. +RUN apk add --no-cache git openssh-client + +WORKDIR /app/docs + +# Copy dependency manifests first to preserve Docker layer caching. +COPY docs/ ./ +RUN npm ci + +# Increase max-old-space-size to avoid memory issues during build +#ENV NODE_OPTIONS="--max-old-space-size=8192" + +# Build the site. +RUN npm run docs:build + +# ---------------------- +# 2. Runtime stage (angie) +# ---------------------- +FROM docker.angie.software/angie:latest AS runner + +# Copy built files +COPY --from=builder /app/docs/.vitepress/dist /usr/share/angie/html + +# Expose port +EXPOSE 80 + +# Health check (optional) +HEALTHCHECK CMD wget --quiet --tries=1 --spider http://localhost:80/ || exit 1 + +# Start angie +CMD ["angie", "-g", "daemon off;"] diff --git a/docs.Dockerfile b/docs.Dockerfile deleted file mode 100644 index f6345917..00000000 --- a/docs.Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -# Generate the HTML output. -FROM ghcr.io/cakephp/docs-builder as builder - -COPY docs /data/docs -ENV LANGS="en ja" - -# Build docs with sphinx -RUN cd /data/docs-builder && \ - make website LANGS="$LANGS" SOURCE=/data/docs DEST=/data/website/ - - -# Build a small nginx container with just the static site in it. -FROM ghcr.io/cakephp/docs-builder:runtime as runtime - -# Configure search index script -ENV LANGS="en ja" -ENV SEARCH_SOURCE="/usr/share/nginx/html" -ENV SEARCH_URL_PREFIX="/elasticsearch/5" - -COPY --from=builder /data/docs /data/docs -COPY --from=builder /data/website /data/website -COPY --from=builder /data/docs-builder/nginx.conf /etc/nginx/conf.d/default.conf - -# Move build docs in place -RUN cp -R /data/website/html/* /usr/share/nginx/html \ - && rm -rf /data/website/ diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..974d64e4 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,4 @@ +node_modules +*/public/ +.vitepress/cache +.vitepress/dist diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js new file mode 100644 index 00000000..d1ee79cf --- /dev/null +++ b/docs/.vitepress/config.js @@ -0,0 +1,64 @@ +import baseConfig from '@cakephp/docs-skeleton/config' +import { createRequire } from 'module' + +const require = createRequire(import.meta.url) +const tocEn = require('./toc_en.json') +const tocJa = require('./toc_ja.json') + +const versions = { + text: '5.x', + items: [ + { text: '5.x (current)', link: 'https://book.cakephp.org/elasticsearch/5/', target: '_self' }, + { text: '4.x', link: 'https://book.cakephp.org/elasticsearch/4/', target: '_self' }, + { text: '3.x', link: 'https://book.cakephp.org/elasticsearch/3/', target: '_self' }, + ], +} + +export default { + extends: baseConfig, + srcDir: '.', + title: 'ElasticSearch', + description: 'CakePHP ElasticSearch Documentation', + base: '/elasticsearch/5/', + rewrites: { + 'en/:slug*': ':slug*', + 'ja/:slug*': 'ja/:slug*', + }, + sitemap: { + hostname: 'https://book.cakephp.org/elasticsearch/5/', + }, + themeConfig: { + siteTitle: false, + pluginName: 'ElasticSearch', + socialLinks: [ + { icon: 'github', link: 'https://github.com/cakephp/elastic-search' }, + ], + editLink: { + pattern: 'https://github.com/cakephp/elastic-search/edit/5.x/docs/:path', + text: 'Edit this page on GitHub', + }, + sidebar: tocEn, + nav: [ + { text: 'CakePHP', link: 'https://cakephp.org' }, + { text: 'API', link: 'https://api.cakephp.org/elasticsearch/' }, + { ...versions }, + ], + }, + locales: { + root: { + label: 'English', + lang: 'en', + themeConfig: { + sidebar: tocEn, + }, + }, + ja: { + label: 'Japanese', + lang: 'ja', + link: '/ja/', + themeConfig: { + sidebar: tocJa, + }, + }, + }, +} diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js new file mode 100644 index 00000000..e33e19ec --- /dev/null +++ b/docs/.vitepress/theme/index.js @@ -0,0 +1 @@ +export { default } from '@cakephp/docs-skeleton' diff --git a/docs/.vitepress/toc_en.json b/docs/.vitepress/toc_en.json new file mode 100644 index 00000000..3915b418 --- /dev/null +++ b/docs/.vitepress/toc_en.json @@ -0,0 +1,30 @@ +{ + "/": [ + { + "text": "Getting Started", + "collapsed": false, + "items": [ + { "text": "Installation and Configuration", "link": "/installation" }, + { "text": "Indexes and Documents", "link": "/indexes-and-documents" }, + { "text": "Searching and Saving", "link": "/searching-and-saving" } + ] + }, + { + "text": "Advanced Topics", + "collapsed": false, + "items": [ + { "text": "Embedded Documents", "link": "/embedded-documents" }, + { "text": "Testing", "link": "/testing" } + ] + }, + { + "text": "Migration", + "collapsed": false, + "items": [ + { "text": "5.0 Upgrade Guide", "link": "/5-0-upgrade-guide" }, + { "text": "4.0 Upgrade Guide", "link": "/4-0-upgrade-guide" }, + { "text": "3.0 Upgrade Guide", "link": "/3-0-upgrade-guide" } + ] + } + ] +} diff --git a/docs/.vitepress/toc_ja.json b/docs/.vitepress/toc_ja.json new file mode 100644 index 00000000..323b68d9 --- /dev/null +++ b/docs/.vitepress/toc_ja.json @@ -0,0 +1,11 @@ +{ + "/ja/": [ + { + "text": "ドキュメント", + "collapsed": false, + "items": [ + { "text": "ElasticSearch", "link": "/ja/" } + ] + } + ] +} diff --git a/docs/config/__init__.py b/docs/config/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/config/all.py b/docs/config/all.py deleted file mode 100644 index 7b931124..00000000 --- a/docs/config/all.py +++ /dev/null @@ -1,48 +0,0 @@ -# Global configuration information used across all the -# translations of documentation. -# -# Import the base theme configuration -from cakephpsphinx.config.all import * - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# - -# The full version, including alpha/beta/rc tags. -release = '5.x' - -# The search index version. -search_version = 'elasticsearch-5' - -# The marketing display name for the book. -version_name = '' - -# Project name shown in the black header bar -project = 'CakePHP ElasticSearch' - -# Other versions that display in the version picker menu. -version_list = [ - {'name': '5.x', 'number': '/elasticsearch/5', 'title': '5.x', 'current': True}, - {'name': '4.x', 'number': '/elasticsearch/4', 'title': '4.x'}, - {'name': '3.x', 'number': '/elasticsearch/3', 'title': '3.x'}, - {'name': '2.x', 'number': '/elasticsearch/2', 'title': '2.x'}, -] - -# Languages available. -languages = ['en', 'ja'] - -# The GitHub branch name for this version of the docs -# for edit links to point at. -branch = '5.x' - -# Current version being built -version = '5.x' - -show_root_link = True - -repository = 'cakephp/elastic-search' - -source_path = 'docs/' - -hide_page_contents = ('search', '404', 'contents') diff --git a/docs/en/3-0-upgrade-guide.md b/docs/en/3-0-upgrade-guide.md new file mode 100644 index 00000000..cb58fd2f --- /dev/null +++ b/docs/en/3-0-upgrade-guide.md @@ -0,0 +1,22 @@ +# 3.0 Upgrade Guide + +If you are upgrading from an earlier version of this plugin, this page collects the application changes you may need to make. + +## Types Renamed to Indexes + +Because of Elasticsearch 5 and 6 changes, the plugin no longer supports multiple types in the same index. All of your type classes therefore need to be renamed to indexes. + +For example: + +- `App\Model\Type\ArticlesType` becomes `App\Model\Index\ArticlesIndex` +- `Type` base classes become `Index` base classes + +Index classes assume that the mapping type uses the singularized index name. For example, the `articles` index uses the `article` mapping type. + +## Breaking Changes + +- `Index::entityClass()` was removed. Use `getEntityClass()` or `setEntityClass()` instead. +- `ResultSet::hasFacets()` was removed because Elastica no longer exposes this method. +- `ResultSet::getFacets()` was removed because Elastica no longer exposes this method. +- `Type` is now `Index`. +- `TypeRegistry` is now `IndexRegistry`. diff --git a/docs/en/3-0-upgrade-guide.rst b/docs/en/3-0-upgrade-guide.rst deleted file mode 100644 index 39fa7051..00000000 --- a/docs/en/3-0-upgrade-guide.rst +++ /dev/null @@ -1,31 +0,0 @@ -3.0 Upgrade Guide -################# - -If you are upgrading from an earlier version of this plugin, this page aims to -collect all the changes you may need to make to your application while -upgrading. - -Types Renamed to Indexes -======================== - -Because of the changes made in elasticsearch 5 and 6, this plugin no longer -supports multiple types in the same index. The impact of this is that all of -your type classes need to be renamed to indexes. For example -``App\Model\Type\ArticlesType`` needs to become -``App\Model\Index\ArticlesIndex``. Furthermore, Index classes assume that the -type mapping has the singular name of the index. For example the ``articles`` -index has a type mapping of ``article``. - -Breaking Changes -================ - -* ``Index::entityClass()`` was removed. Use ``getEntityClass()`` or - ``setEntityClass()`` instead. -* ``ResultSet::hasFacets()`` was removed as elastica no longer exposes this - method. -* ``ResultSet::getFacets()`` was removed as elastica no longer exposes this - method. -* ``ResultSet::getFacets()`` was removed as elastica no longer exposes this - method. -* The ``Type`` base class is now ``Index``. -* ``TypeRegistry`` is now ``IndexRegistry``. diff --git a/docs/en/4-0-upgrade-guide.md b/docs/en/4-0-upgrade-guide.md new file mode 100644 index 00000000..b811fc5a --- /dev/null +++ b/docs/en/4-0-upgrade-guide.md @@ -0,0 +1,31 @@ +# 4.0 Upgrade Guide + +::: warning Requirements +CakePHP ElasticSearch `4.x` requires CakePHP `5.x`. +::: + +## Breaking Changes + +- Native PHP types were added where possible. This improves type safety but may expose incompatibilities in application code. +- `Query` no longer uses `Cake\ORM\QueryTrait`, which removed several inherited methods that were never used by this plugin. +- `Query::isEagerLoaded()` and `Query::eagerLoaded()` were removed. + +## Indexes + +`IndexRegistry` was deprecated in `4.x`. + +Old code: + +```php +use Cake\ElasticSearch\IndexRegistry; + +$articles = IndexRegistry::get('Articles'); +``` + +New code: + +```php +use Cake\Datasource\FactoryLocator; + +$articles = FactoryLocator::get('ElasticSearch')->get('Articles'); +``` diff --git a/docs/en/4-0-upgrade-guide.rst b/docs/en/4-0-upgrade-guide.rst deleted file mode 100644 index e8535964..00000000 --- a/docs/en/4-0-upgrade-guide.rst +++ /dev/null @@ -1,35 +0,0 @@ -4.0 Upgrade Guide -################# - -.. warning:: - CakePHP ElasticSearch 4.x requires CakePHP 5.x. - -Breaking Changes -================ - -* All methods have had native types added where possible. This improves type - safety within this plugin but may cause errors with application code. -* ``Query`` no longer uses ``Cake\ORM\QueryTrait``. This has allowed several - unused methods to be removed. -* ``Query::isEagerLoaded()``, and ``Query::eagerLoaded()`` were removed. - Previously these methods were inherited from ``QueryTrait`` but served no - purpose here. - - -Indexes -====== - -IndexRegistry has been deprecated. - -Old code example:: - - use Cake\ElasticSearch\IndexRegistry; - - $articles = IndexRegistry::get('Articles'); - - -New code example:: - - use Cake\Datasource\FactoryLocator; - - $articles = FactoryLocator::get('ElasticSearch')->get('Articles'); diff --git a/docs/en/5-0-upgrade-guide.md b/docs/en/5-0-upgrade-guide.md new file mode 100644 index 00000000..d51a3b8a --- /dev/null +++ b/docs/en/5-0-upgrade-guide.md @@ -0,0 +1,198 @@ +# 5.0 Upgrade Guide + +::: warning Requirements +CakePHP ElasticSearch `5.x` requires CakePHP `5.2+`, Elasticsearch `9.x`, Elastica `9.x`, and PHP `8.1+`. +::: + +## Requirements + +- CakePHP `5.2+` +- Elasticsearch `9.x` +- Elastica `9.x` +- PHP `8.1+` + +## Breaking Changes + +Version `5.x` includes the breaking changes required for Elasticsearch `9.x`, the Elastica `9.x` upgrade, and current CakePHP `5.x` compatibility. + +- Connection configuration now prefers a `hosts` array. +- `IndexRegistry` has been removed after being deprecated since `3.4.3`. +- Several deprecated methods have been removed. +- Elastica API updates require modern client initialization. + +## Configuration Changes + +### Connection Configuration Format + +The recommended datasource format now uses `hosts` instead of separate `host` and `port` keys. + +Legacy configuration format: + +```php +'Datasources' => [ + 'elastic' => [ + 'className' => 'Cake\ElasticSearch\Datasource\Connection', + 'driver' => 'Cake\ElasticSearch\Datasource\Connection', + 'host' => '127.0.0.1', + 'port' => 9200, + ], +] +``` + +Recommended configuration format: + +```php +'Datasources' => [ + 'elastic' => [ + 'className' => 'Cake\ElasticSearch\Datasource\Connection', + 'driver' => 'Cake\ElasticSearch\Datasource\Connection', + 'hosts' => ['127.0.0.1:9200'], + ], +] +``` + +For HTTPS endpoints: + +```php +'hosts' => ['https://127.0.0.1:443'] +``` + +For clusters with multiple nodes: + +```php +'hosts' => [ + '127.0.0.1:9200', + '127.0.0.1:9201', + '127.0.0.1:9202', +] +``` + +## Deprecated Method Removals + +### QueryBuilder Methods + +- `QueryBuilder::and_()` -> `QueryBuilder::and()` +- `QueryBuilder::or_()` -> `QueryBuilder::or()` + +Old code: + +```php +$query->where(function ($builder) { + return $builder->and_( + $builder->gt('views', 99), + $builder->term('author.name', 'sally') + ); +}); +``` + +New code: + +```php +$query->where(function ($builder) { + return $builder->and( + $builder->gt('views', 99), + $builder->term('author.name', 'sally') + ); +}); +``` + +### Embedded Association Methods + +- `Embedded::property()` -> `setProperty()` / `getProperty()` +- `Embedded::entityClass()` -> `setEntityClass()` / `getEntityClass()` +- `Embedded::indexClass()` -> `setIndexClass()` / `getIndexClass()` + +Old code: + +```php +$association->property('comments'); +$class = $association->entityClass(); +$association->entityClass('App\Model\Document\Comment'); +``` + +New code: + +```php +$association->setProperty('comments'); +$class = $association->getEntityClass(); +$association->setEntityClass('App\Model\Document\Comment'); +``` + +### Query Methods + +- `Query::repository()` -> `setRepository()` + +Old code: + +```php +$query->repository($index); +``` + +New code: + +```php +$query->setRepository($index); +``` + +::: tip Ordering +`Query::order()` still exists because it is part of `Cake\Datasource\QueryInterface`, but `orderBy()` is preferred. +::: + +## IndexRegistry Removal + +Use `IndexLocator`, `IndexLocatorAwareTrait`, or `FactoryLocator` instead of `IndexRegistry`. + +Old approach: + +```php +use Cake\ElasticSearch\IndexRegistry; + +$articles = IndexRegistry::get('Articles'); +IndexRegistry::flush(); +``` + +New approach with `IndexLocator`: + +```php +use Cake\ElasticSearch\Datasource\IndexLocator; + +$locator = new IndexLocator(); +$articles = $locator->get('Articles'); +$locator->clear(); +``` + +New approach with `IndexLocatorAwareTrait`: + +```php +use Cake\ElasticSearch\Datasource\IndexLocatorAwareTrait; + +class MyController extends Controller +{ + use IndexLocatorAwareTrait; + + public function index() + { + $articles = $this->fetchIndex('Articles'); + } +} +``` + +Alternative with `FactoryLocator`: + +```php +use Cake\Datasource\FactoryLocator; + +$articles = FactoryLocator::get('ElasticSearch')->get('Articles'); +``` + +## Migration Steps + +1. Update dependencies to `cakephp/elastic-search:^5.0`. +2. Move datasource configuration to the `hosts` array format if you still use `host` and `port`. +3. Replace removed APIs such as `IndexRegistry`, `and_()`, `or_()`, embedded association setters/getters, and `repository()`. +4. Verify your Elasticsearch cluster is running `9.x`. +5. Run your application test suite. + +```bash +vendor/bin/phpunit +``` diff --git a/docs/en/5-0-upgrade-guide.rst b/docs/en/5-0-upgrade-guide.rst deleted file mode 100644 index 4de304d3..00000000 --- a/docs/en/5-0-upgrade-guide.rst +++ /dev/null @@ -1,215 +0,0 @@ -5.0 Upgrade Guide -################# - -.. warning:: - CakePHP ElasticSearch 5.x requires CakePHP 5.2+, ElasticSearch 9.x, and PHP 8.1+. - -Requirements -============ - -* CakePHP 5.2+ -* ElasticSearch 9.x -* Elastica 9.x (via ruflin/elastica) -* PHP 8.1+ - -Breaking Changes -================ - -Version 5.x includes several breaking changes to support ElasticSearch 9.x via -the Elastica 9.x library upgrade, along with compatibility updates for the latest -CakePHP 5.x features. - -* Connection configuration format has changed to use ``hosts`` array. -* ``IndexRegistry`` class has been removed (was deprecated since 3.4.3). -* Several deprecated methods have been removed. -* Elastica API changes require updated client initialization. - -Configuration Changes -===================== - -Connection Configuration Format --------------------------------- - -The recommended configuration format now uses a ``hosts`` array instead -of separate ``host`` and ``port`` keys. This aligns with Elastica 9.x -and provides better support for clustering. - -Legacy configuration format (still supported):: - - // in config/app.php - 'Datasources' => [ - 'elastic' => [ - 'className' => 'Cake\ElasticSearch\Datasource\Connection', - 'driver' => 'Cake\ElasticSearch\Datasource\Connection', - 'host' => '127.0.0.1', - 'port' => 9200, - ], - ] - -New configuration format (recommended):: - - // in config/app.php - 'Datasources' => [ - 'elastic' => [ - 'className' => 'Cake\ElasticSearch\Datasource\Connection', - 'driver' => 'Cake\ElasticSearch\Datasource\Connection', - 'hosts' => ['127.0.0.1:9200'], - ], - ] - -For HTTPS endpoints:: - - 'hosts' => ['https://127.0.0.1:443'] - -Multiple hosts can be specified in the array:: - - 'hosts' => [ - '127.0.0.1:9200', - '127.0.0.1:9201', - '127.0.0.1:9202', - ] - -Deprecated Method Removals -=========================== - -Several deprecated methods have been removed in this version. Update your code -to use the replacement methods. - -QueryBuilder Methods --------------------- - -* ``QueryBuilder::and_()`` → use ``QueryBuilder::and()`` -* ``QueryBuilder::or_()`` → use ``QueryBuilder::or()`` - -The underscore suffix is no longer needed as modern PHP allows these method names. - -Old code:: - - $query->where(function ($builder) { - return $builder->and_( - $builder->gt('views', 99), - $builder->term('author.name', 'sally') - ); - }); - -New code:: - - $query->where(function ($builder) { - return $builder->and( - $builder->gt('views', 99), - $builder->term('author.name', 'sally') - ); - }); - -Embedded Association Methods ------------------------------ - -* ``Embedded::property()`` → use ``setProperty()`` / ``getProperty()`` -* ``Embedded::entityClass()`` → use ``setEntityClass()`` / ``getEntityClass()`` -* ``Embedded::indexClass()`` → use ``setIndexClass()`` / ``getIndexClass()`` - -Old code:: - - $association->property('comments'); - $class = $association->entityClass(); - $association->entityClass('App\Model\Document\Comment'); - -New code:: - - $association->setProperty('comments'); - $class = $association->getEntityClass(); - $association->setEntityClass('App\Model\Document\Comment'); - -Query Methods -------------- - -* ``Query::repository()`` → use ``setRepository()`` - -Old code:: - - $query->repository($index); - -New code:: - - $query->setRepository($index); - -.. note:: - ``Query::order()`` remains available as it's required by the - ``Cake\Datasource\QueryInterface`` contract, but ``orderBy()`` is preferred. - -IndexRegistry Removal -===================== - -The deprecated ``IndexRegistry`` class has been completely removed. Use ``IndexLocator`` -or ``IndexLocatorAwareTrait`` instead. - -Old approach (no longer available):: - - use Cake\ElasticSearch\IndexRegistry; - - $articles = IndexRegistry::get('Articles'); - IndexRegistry::flush(); - -New approach using IndexLocator directly:: - - use Cake\ElasticSearch\Datasource\IndexLocator; - - $locator = new IndexLocator(); - $articles = $locator->get('Articles'); - $locator->clear(); - -New approach using IndexLocatorAwareTrait:: - - use Cake\ElasticSearch\Datasource\IndexLocatorAwareTrait; - - class MyController extends Controller - { - use IndexLocatorAwareTrait; - - public function index() - { - $articles = $this->fetchIndex('Articles'); - } - } - -Alternative using FactoryLocator:: - - use Cake\Datasource\FactoryLocator; - - $articles = FactoryLocator::get('ElasticSearch')->get('Articles'); - -Migration Steps -=============== - -1. **Update Dependencies** - - Update your ``composer.json`` to require version 5.x:: - - composer require cakephp/elastic-search:^5.0 - -2. **Update Configuration (Optional but Recommended)** - - Consider updating your datasource configuration in ``config/app.php`` to use - the ``hosts`` array format for better clustering support. The old ``host`` - and ``port`` format still works for backward compatibility. - -3. **Update Code** - - Search your codebase for the deprecated methods and replace them: - - * Replace ``IndexRegistry`` usage with ``IndexLocator`` or ``IndexLocatorAwareTrait`` - * Replace ``and_()`` with ``and()`` - * Replace ``or_()`` with ``or()`` - * Replace getter/setter methods on embedded associations - * Replace ``repository()`` with ``setRepository()`` - -4. **Test ElasticSearch 9.x Compatibility** - - Ensure your ElasticSearch cluster is running version 9.x and test your - application thoroughly. - -5. **Run Tests** - - Run your test suite to catch any compatibility issues:: - - vendor/bin/phpunit diff --git a/docs/en/conf.py b/docs/en/conf.py deleted file mode 100644 index abf3d8d0..00000000 --- a/docs/en/conf.py +++ /dev/null @@ -1,10 +0,0 @@ -import sys, os - -# Append the top level directory of the docs, so we can import from the config dir. -sys.path.insert(0, os.path.abspath('..')) - -# Pull in all the configuration options defined in the global config file.. -from config.all import * - -# Language in use for this directory. -language = 'en' diff --git a/docs/en/contents.rst b/docs/en/contents.rst deleted file mode 100644 index 763631d7..00000000 --- a/docs/en/contents.rst +++ /dev/null @@ -1,11 +0,0 @@ -Contents -######## - -.. toctree:: - :maxdepth: 2 - :caption: CakePHP ElasticSearch - - /index - /3-0-upgrade-guide - /4-0-upgrade-guide - /5-0-upgrade-guide diff --git a/docs/en/embedded-documents.md b/docs/en/embedded-documents.md new file mode 100644 index 00000000..c654a45b --- /dev/null +++ b/docs/en/embedded-documents.md @@ -0,0 +1,40 @@ +# Embedded Documents + +Embedded documents let you attach custom document classes to nested properties in a parent document. + +For example, if articles contain embedded users and comments, you can define those relationships in the index: + +```php +namespace App\Model\Index; + +use Cake\ElasticSearch\Index; + +class ArticlesIndex extends Index +{ + public function initialize(): void + { + $this->embedOne('User'); + $this->embedMany('Comments', [ + 'entityClass' => 'MyComment', + ]); + } +} +``` + +In this example: + +- `embedOne('User')` maps the `user` property to `App\Model\Document\User`. +- `embedMany('Comments')` maps the `comments` property to an array of document objects. +- `entityClass` lets you use a class name that does not match the property name. + +After configuring embedded documents, `find()` and `get()` hydrate the nested properties into the configured document classes: + +```php +$article = $this->Articles->get($id); + +// Instance of App\Model\Document\User +$article->user; + +// Array of App\Model\Document\Comment instances +$article->comments; +``` diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 00000000..5c56f874 --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,14 @@ +# ElasticSearch + +The ElasticSearch plugin provides an ORM-like abstraction for working with [Elasticsearch](https://www.elastic.co/products/elasticsearch) indexes in CakePHP. It helps with indexing, searching, validation, embedded documents, and fixture-backed tests. + +Version `5.x` targets CakePHP `5.2+`, Elasticsearch `9.x`, Elastica `9.x`, and PHP `8.1+`. + +## Documentation Map + +- [Installation and Configuration](/installation) covers package installation, plugin loading, and datasource setup. +- [Indexes and Documents](/indexes-and-documents) explains index classes, document classes, connections, and fetching index instances. +- [Searching and Saving](/searching-and-saving) covers finding, validating, saving, updating, bulk operations, and deleting documents. +- [Embedded Documents](/embedded-documents) explains `embedOne()` and `embedMany()` for nested document objects. +- [Testing](/testing) shows how to load mappings and fixtures for integration tests. +- [3.0 Upgrade Guide](/3-0-upgrade-guide), [4.0 Upgrade Guide](/4-0-upgrade-guide), and [5.0 Upgrade Guide](/5-0-upgrade-guide) describe version-specific migration changes. diff --git a/docs/en/index.rst b/docs/en/index.rst deleted file mode 100644 index 81127792..00000000 --- a/docs/en/index.rst +++ /dev/null @@ -1,423 +0,0 @@ -ElasticSearch -############# - -The ElasticSearch plugin provides an ORM-like abstraction on top of -`elasticsearch `_. The plugin -provides features that make testing, indexing documents and searching your -indexes easier. - -Installation -============ - -To install the ElasticSearch plugin, you can use ``composer``. From your -application's ROOT directory (where ``composer.json`` file is located) run the -following:: - - $ composer require cakephp/elastic-search "^5.0" - -You will need to add the following line to your application's -**src/Application.php** file:: - - $this->addPlugin('Cake/ElasticSearch'); - -Additionally, you will need to configure the 'elastic' datasource connection in -your **config/app.php** file. An example configuration would be:: - - // in config/app.php - 'Datasources' => [ - // other datasources - 'elastic' => [ - 'className' => 'Cake\ElasticSearch\Datasource\Connection', - 'driver' => 'Cake\ElasticSearch\Datasource\Connection', - 'hosts' => ['127.0.0.1:9200'], - 'index' => 'my_apps_index', - ], - ] - -The legacy configuration format with separate ``host`` and ``port`` is still supported:: - - 'elastic' => [ - 'className' => 'Cake\ElasticSearch\Datasource\Connection', - 'driver' => 'Cake\ElasticSearch\Datasource\Connection', - 'host' => '127.0.0.1', - 'port' => 9200, - 'index' => 'my_apps_index', - ] - -If your endpoint requires https, use:: - - 'hosts' => ['https://127.0.0.1:443'] - -.. note:: - The ``hosts`` array format is recommended for new applications and provides - better support for multiple hosts and clustering. - -Overview -======== - -The ElasticSearch plugin makes it easier to interact with an elasticsearch index -and provides an interface similar to the `ORM -`__. To get started you should -create an ``Index`` object. ``Index`` objects are the "Repository" or table-like -class in elasticsearch:: - - // in src/Model/Type/ArticlesIndex.php - namespace App\Model\Index; - - use Cake\ElasticSearch\Index; - - class ArticlesIndex extends Index - { - } - -You can then use your index class in your controllers:: - - public function beforeFilter(Event $event) - { - parent::beforeFilter($event); - // Load the Index using the 'Elastic' provider. - $this->loadModel('Articles', 'Elastic'); - } - - public function add() - { - $article = $this->Articles->newEntity(); - if ($this->request->is('post')) { - $article = $this->Articles->patchEntity($article, $this->request->getData()); - if ($this->Articles->save($article)) { - $this->Flash->success('It saved'); - } - } - $this->set(compact('article')); - } - -We would also need to create a basic view for our indexed articles:: - - // in src/Template/Articles/add.ctp - Form->create($article) ?> - Form->control('title') ?> - Form->control('body') ?> - Form->button('Save') ?> - Form->end() ?> - -You should now be able to submit the form and have a new document added to -elasticsearch. - -Document Objects -================ - -Like the ORM, the Elasticsearch ODM uses ORM-like classes. The -base class you should inherit from is ``Cake\ElasticSearch\Document``. Document -classes are found in the ``Model\Document`` namespace in your application or -plugin:: - - namespace App\Model\Document; - - use Cake\ElasticSearch\Document; - - class Article extends Document - { - } - -Outside of constructor logic that makes Documents work with data from -elasticsearch, the interface and functionality provided by ``Document`` are the -same as those in `Entities -`__ - -Searching Indexed Documents -=========================== - -After you've indexed some documents you will want to search through them. The -ElasticSearch plugin provides a query builder that allows you to build search -queries:: - - $query = $this->Articles->find() - ->where([ - 'title' => 'special', - 'or' => [ - 'tags in' => ['cake', 'php'], - 'tags not in' => ['c#', 'java'] - ] - ]); - - foreach ($query as $article) { - echo $article->title; - } - -You can use the ``QueryBuilder`` to add filtering conditions:: - - $query->where(function ($builder) { - return $builder->and( - $builder->gt('views', 99), - $builder->term('author.name', 'sally') - ); - }); - -The `QueryBuilder source -`_ -has the complete list of methods with examples for many commonly used methods. - -Validating Data & Using Application Rules -========================================= - -Like the ORM, the ElasticSearch plugin lets you validate data when marshalling -documents. Validating request data, and applying application rules works the -same as it does with the relational ORM. See the `validating request data -`__ -and `Application Rules -`__ -sections for more information. - -.. Need information on nested validators. - -Saving New Documents -==================== - -When you're ready to index some data into elasticsearch, you'll first need to -convert your data into a ``Document`` that can be indexed:: - - $article = $this->Articles->newEntity($data); - if ($this->Articles->save($article)) { - // Document was indexed - } - -When marshalling a document, you can specify which embedded documents you wish -to marshall using the ``associated`` key:: - - $article = $this->Articles->newEntity($data, ['associated' => ['Comments']]); - -Saving a document will trigger the following events: - -* ``Model.beforeSave`` - Fired before the document is saved. You can prevent the - save operation from happening by stopping this event. -* ``Model.buildRules`` - Fired when the rules checker is built for the first - time. -* ``Model.afterSave`` - Fired after the document is saved. - -.. note:: - There are no events for embedded documents, as the parent document and all - of its embedded documents are saved as one operation. - -Updating Existing Documents -=========================== - -When you need to re-index data, you can patch existing entities and re-save -them:: - - $query = $this->Articles->find()->where(['user.name' => 'jill']); - foreach ($query as $doc) { - $doc->set($newProperties); - $this->Articles->save($doc); - } - -Additionally Elasticsearch ``refresh`` request can be triggered by passing -``'refresh' => true`` in the ``$options`` argument. A refresh makes recent -operations performed on one or more indices available for search:: - - $this->Articles->save($article, ['refresh' => true]); - -Saving Multiple Documents -========================= - -Using this method you can bulk save multiple documents:: - - $result = $this->Articles->saveMany($documents); - -Here ``$documents`` is an array of documents. The result will be ``true`` on success or ``false`` on failure. -``saveMany`` can have second argument with the same options as accepted by ``save()``. - - -Deleting Documents -================== - -After retrieving a document you can delete it:: - - $doc = $this->Articles->get($id); - $this->Articles->delete($doc); - -You can also delete documents matching specific conditions:: - - $this->Articles->deleteAll(['user.name' => 'bob']); - -Embedding Documents -=================== - -By defining embedded documents, you can attach entity classes to specific -property paths in your documents. This allows you to provide custom behavior to -the documents within a parent document. For example, you may want the comments -embedded in an article to have specific application specific methods. You can -use ``embedOne`` and ``embedMany`` to define embedded documents:: - - // in src/Model/Index/ArticlesIndex.php - namespace App\Model\Index; - - use Cake\ElasticSearch\Index; - - class ArticlesIndex extends Index - { - public function initialize() - { - $this->embedOne('User'); - $this->embedMany('Comments', [ - 'entityClass' => 'MyComment' - ]); - } - } - -The above would create two embedded documents on the ``Article`` document. The -``User`` embed will convert the ``user`` property to instances of -``App\Model\Document\User``. To get the Comments embed to use a class name -that does not match the property name, we can use the ``entityClass`` option to -configure a custom class name. - -Once we've setup our embedded documents, the results of ``find()`` and ``get()`` -will return objects with the correct embedded document classes:: - - $article = $this->Articles->get($id); - // Instance of App\Model\Document\User - $article->user; - - // Array of App\Model\Document\Comment instances - $article->comments; - -Configuring Connections -======================= - -By default all index instances use the ``elastic`` connection. If your -application uses multiple connections you will want to configure which -index use which connections. This is the ``defaultConnectionName()`` method:: - - namespace App\Model\Index; - - use Cake\ElasticSearch\Index; - - class ArticlesIndex extends Index - { - public static function defaultConnectionName() { - return 'replica_db'; - } - } - -.. note:: - - The ``defaultConnectionName()`` method **must** be static. - -Getting Index Instances -======================= - -Like the ORM, the ElasticSearch plugin provides a locator for getting -``Index`` instances:: - - use Cake\ElasticSearch\Datasource\IndexLocator; - - $locator = new IndexLocator(); - $articles = $locator->get('Articles'); - -Using IndexLocatorAwareTrait ------------------------------ - -For convenient access to the index locator, you can use the ``IndexLocatorAwareTrait`` -in your classes:: - - use Cake\ElasticSearch\Datasource\IndexLocatorAwareTrait; - - class MyController extends Controller - { - use IndexLocatorAwareTrait; - - public function index() - { - $articles = $this->fetchIndex('Articles'); - } - } - -Flushing the Locator ---------------------- - -During test cases you may want to flush the locator. Doing so is often useful -when you are using mock objects, or modifying an index's dependencies:: - - $locator->clear(); - -Test Fixtures -============= - -The ElasticSearch plugin provides a seamless test suite integration. Just like -database fixtures, you can create test schema and fixture data elasticsearch. -Much like database fixtures we load our Elasticsearch mappings during -``tests/bootstrap.php`` of our application:: - - // In tests/bootstrap.php - use Cake\Elasticsearch\TestSuite\Fixture\MappingGenerator; - - $generator = new MappingGenerator('tests/mappings.php', 'test_elastic'); - $generator->reload(); - -The above will create the indexes and mappings defined in ``tests/mapping.php`` -and insert them into the ``test_elastic`` connection. The mappings in your -``mappings.php`` should return a list of mappings to create:: - - // in tests/mappings.php - return [ - [ - // The name of the index and mapping. - 'name' => 'articles', - // The schema for the mapping. - 'mapping' => [ - 'id' => ['type' => 'integer'], - 'title' => ['type' => 'text'], - 'user_id' => ['type' => 'integer'], - 'body' => ['type' => 'text'], - 'created' => ['type' => 'date'], - ], - // Additional index settings. - 'settings' => [ - 'number_of_shards' => 2, - 'number_of_routing_shards' => 2, - ], - ], - // ... - ]; - -Mappings use the `native elasticsearch mapping format -`_. -You can safely omit the type name and top level ``properties`` key. With our -mappings loaded, we can define a test fixture for our Articles index with the -following:: - - namespace App\Test\Fixture; - - use Cake\ElasticSearch\TestSuite\TestFixture; - - /** - * Articles fixture - */ - class ArticlesFixture extends TestFixture - { - /** - * The table/index for this fixture. - * - * @var string - */ - public $table = 'articles'; - - public $records = [ - [ - 'user' => [ - 'username' => 'billy' - ], - 'title' => 'First Post', - 'body' => 'Some content' - ] - ]; - } - -.. versionchanged:: 3.4.0 - Prior to CakePHP 4.3.0 schema was defined on each fixture in the ``$schema`` - property. - - -Once your fixtures are created you can use them in your test cases by including -them in your test's ``fixtures`` properties:: - - public $fixtures = ['app.Articles']; diff --git a/docs/en/indexes-and-documents.md b/docs/en/indexes-and-documents.md new file mode 100644 index 00000000..a99ea56e --- /dev/null +++ b/docs/en/indexes-and-documents.md @@ -0,0 +1,106 @@ +# Indexes and Documents + +The ElasticSearch plugin uses repository-style `Index` classes and ORM-like `Document` classes. + +## Creating an Index Class + +Create an index class in your application's `Model/Index` namespace: + +```php +namespace App\Model\Index; + +use Cake\ElasticSearch\Index; + +class ArticlesIndex extends Index +{ +} +``` + +`Index` objects are similar to `ORM\Table` instances. Each index class maps to an Elasticsearch index, and by default the plugin infers the index name from the class name. + +If you need to define the Elasticsearch index name explicitly, override `getName()`: + +```php +namespace App\Model\Index; + +use Cake\ElasticSearch\Index; + +class CommentsIndex extends Index +{ + public function getName(): string + { + return 'comments'; + } +} +``` + +## Creating a Document Class + +Document classes belong in `Model/Document`: + +```php +namespace App\Model\Document; + +use Cake\ElasticSearch\Document; + +class Article extends Document +{ +} +``` + +Aside from constructor logic needed to hydrate Elasticsearch data, `Document` behaves like a CakePHP entity. The entity documentation for CakePHP is therefore a good reference for getters, setters, hidden fields, virtual fields, and mass-assignment rules. + +## Fetching Index Instances + +Use `IndexLocatorAwareTrait` when you want convenient access in controllers, commands, and services: + +```php +use Cake\ElasticSearch\Datasource\IndexLocatorAwareTrait; + +class MyController extends Controller +{ + use IndexLocatorAwareTrait; + + public function index() + { + $articles = $this->fetchIndex('Articles'); + } +} +``` + +You can also work with `IndexLocator` directly: + +```php +use Cake\ElasticSearch\Datasource\IndexLocator; + +$locator = new IndexLocator(); +$articles = $locator->get('Articles'); +``` + +Clear the locator during tests when you need fresh instances or mocked dependencies: + +```php +$locator->clear(); +``` + +## Configuring the Connection Per Index + +All indexes use the `elastic` connection by default. Override `defaultConnectionName()` if an index should use a different datasource: + +```php +namespace App\Model\Index; + +use Cake\ElasticSearch\Index; + +class ArticlesIndex extends Index +{ + public static function defaultConnectionName(): string + { + return 'replica_db'; + } +} +``` + +::: warning Static method required +`defaultConnectionName()` must be declared `static`. +::: diff --git a/docs/en/installation.md b/docs/en/installation.md new file mode 100644 index 00000000..55ec6afe --- /dev/null +++ b/docs/en/installation.md @@ -0,0 +1,92 @@ +# Installation and Configuration + +Install the plugin with Composer from your application's root directory: + +```bash +composer require cakephp/elastic-search:^5.0 +``` + +Load the plugin in your application bootstrap: + +```php +use Cake\ElasticSearch\Plugin as ElasticSearchPlugin; + +class Application extends BaseApplication +{ + public function bootstrap(): void + { + parent::bootstrap(); + $this->addPlugin(ElasticSearchPlugin::class); + + // To disable the automatic model provider and FormHelper wiring: + // $this->addPlugin(ElasticSearchPlugin::class, ['bootstrap' => false]); + } +} +``` + +## Datasource Configuration + +Configure an `elastic` datasource in `config/app.php`: + +```php +// in config/app.php +'Datasources' => [ + // other datasources + 'elastic' => [ + 'className' => 'Cake\ElasticSearch\Datasource\Connection', + 'driver' => 'Cake\ElasticSearch\Datasource\Connection', + 'hosts' => ['127.0.0.1:9200'], + 'index' => 'my_apps_index', + ], +] +``` + +The legacy `host` and `port` keys are still supported: + +```php +'elastic' => [ + 'className' => 'Cake\ElasticSearch\Datasource\Connection', + 'driver' => 'Cake\ElasticSearch\Datasource\Connection', + 'host' => '127.0.0.1', + 'port' => 9200, + 'index' => 'my_apps_index', +] +``` + +For HTTPS endpoints, use full URLs in the `hosts` array: + +```php +'hosts' => ['https://127.0.0.1:443'] +``` + +For multi-node clusters, provide multiple hosts: + +```php +'hosts' => [ + '127.0.0.1:9200', + '127.0.0.1:9201', + '127.0.0.1:9202', +] +``` + +You can also configure the connection with a URL, which is useful when values come from environment variables: + +```php +'Datasources' => [ + 'elastic' => [ + 'url' => env('ELASTIC_URL', null), + ], +] +``` + +```text +ELASTIC_URL="Cake\ElasticSearch\Datasource\Connection://127.0.0.1:9200?driver=Cake\ElasticSearch\Datasource\Connection" +``` + +::: tip Recommended format +The `hosts` array is the recommended format for new applications because it aligns with Elastica `9.x` and works better with multiple hosts. +::: + +## Query Logging + +Enable request logging by setting `log` to `true` on the datasource configuration. By default the `debug` log profile is used. You can define an `elasticsearch` log profile in `Cake\Log\Log` if you want query logs written elsewhere. diff --git a/docs/en/searching-and-saving.md b/docs/en/searching-and-saving.md new file mode 100644 index 00000000..aad9609e --- /dev/null +++ b/docs/en/searching-and-saving.md @@ -0,0 +1,111 @@ +# Searching and Saving + +Once you have an index class, you can build queries, validate data, and persist documents through it. + +## Searching Indexed Documents + +Use `find()` to create a query: + +```php +$query = $this->Articles->find() + ->where([ + 'title' => 'special', + 'or' => [ + 'tags in' => ['cake', 'php'], + 'tags not in' => ['c#', 'java'], + ], + ]); + +foreach ($query as $article) { + echo $article->title; +} +``` + +Use the query builder callback for more expressive conditions: + +```php +$query->where(function ($builder) { + return $builder->and( + $builder->gt('views', 99), + $builder->term('author.name', 'sally') + ); +}); +``` + +Refer to the `QueryBuilder` API for the full set of supported methods. + +## Validating and Applying Rules + +Validation and application rules work the same way they do in the relational ORM. Validation runs during marshalling, and rule checks run before a document is persisted. + +## Saving New Documents + +Create and save new documents with `newEntity()` and `save()`: + +```php +$article = $this->Articles->newEntity($data); +if ($this->Articles->save($article)) { + // Document was indexed +} +``` + +To marshal embedded documents, pass the `associated` option: + +```php +$article = $this->Articles->newEntity($data, [ + 'associated' => ['Comments'], +]); +``` + +Saving a document triggers these events: + +- `Model.beforeSave` fires before the document is saved. Stop this event to prevent the save. +- `Model.buildRules` fires when the rules checker is built for the first time. +- `Model.afterSave` fires after the document is saved. + +::: info Embedded documents +Embedded documents do not emit their own save events because the parent document and its embedded records are persisted as one operation. +::: + +## Updating Existing Documents + +Patch or modify documents and save them again: + +```php +$query = $this->Articles->find()->where(['user.name' => 'jill']); +foreach ($query as $doc) { + $doc->set($newProperties); + $this->Articles->save($doc); +} +``` + +Pass `refresh => true` if you need Elasticsearch to refresh the index after a write: + +```php +$this->Articles->save($article, ['refresh' => true]); +``` + +## Saving Multiple Documents + +Use `saveMany()` to bulk save documents: + +```php +$result = $this->Articles->saveMany($documents); +``` + +`$documents` must be an array of document objects. `saveMany()` returns `true` on success or `false` on failure, and it accepts the same options as `save()`. + +## Deleting Documents + +Delete a single document after retrieving it: + +```php +$doc = $this->Articles->get($id); +$this->Articles->delete($doc); +``` + +Delete matching documents in bulk with `deleteAll()`: + +```php +$this->Articles->deleteAll(['user.name' => 'bob']); +``` diff --git a/docs/en/testing.md b/docs/en/testing.md new file mode 100644 index 00000000..19a4bb08 --- /dev/null +++ b/docs/en/testing.md @@ -0,0 +1,74 @@ +# Testing + +The ElasticSearch plugin includes test fixture support for loading mappings and records into a dedicated test connection. + +## Loading Mappings + +Generate and reload test mappings in `tests/bootstrap.php`: + +```php +use Cake\ElasticSearch\TestSuite\Fixture\MappingGenerator; + +$generator = new MappingGenerator('tests/mappings.php', 'test_elastic'); +$generator->reload(); +``` + +This creates the indexes and mappings defined in `tests/mappings.php` on the `test_elastic` connection. + +Your mappings file should return an array of mappings: + +```php +return [ + [ + 'name' => 'articles', + 'mapping' => [ + 'id' => ['type' => 'integer'], + 'title' => ['type' => 'text'], + 'user_id' => ['type' => 'integer'], + 'body' => ['type' => 'text'], + 'created' => ['type' => 'date'], + ], + 'settings' => [ + 'number_of_shards' => 2, + 'number_of_routing_shards' => 2, + ], + ], +]; +``` + +Mappings use the native Elasticsearch mapping format. You can omit the type name and the top-level `properties` key. + +## Defining Fixtures + +Create fixtures by extending `Cake\ElasticSearch\TestSuite\TestFixture`: + +```php +namespace App\Test\Fixture; + +use Cake\ElasticSearch\TestSuite\TestFixture; + +class ArticlesFixture extends TestFixture +{ + public string $table = 'articles'; + + public array $records = [ + [ + 'user' => [ + 'username' => 'billy', + ], + 'title' => 'First Post', + 'body' => 'Some content', + ], + ]; +} +``` + +Use those fixtures in your test case as usual: + +```php +public array $fixtures = ['app.Articles']; +``` + +::: info Schema handling +Before plugin `3.4.0`, schema was typically defined on each fixture using the `$schema` property. Current versions load mappings separately through `MappingGenerator`. +::: diff --git a/docs/ja/conf.py b/docs/ja/conf.py deleted file mode 100644 index 57004407..00000000 --- a/docs/ja/conf.py +++ /dev/null @@ -1,10 +0,0 @@ -import sys, os - -# Append the top level directory of the docs, so we can import from the config dir. -sys.path.insert(0, os.path.abspath('..')) - -# Pull in all the configuration options defined in the global config file.. -from config.all import * - -# Language in use for this directory. -language = 'ja' diff --git a/docs/ja/contents.rst b/docs/ja/contents.rst deleted file mode 100644 index 59461d1f..00000000 --- a/docs/ja/contents.rst +++ /dev/null @@ -1,8 +0,0 @@ -Contents -######## - -.. toctree:: - :maxdepth: 2 - :caption: CakePHP ElasticSearch - - /index diff --git a/docs/ja/index.md b/docs/ja/index.md new file mode 100644 index 00000000..b1fae161 --- /dev/null +++ b/docs/ja/index.md @@ -0,0 +1,224 @@ +# ElasticSearch + +ElasticSearch プラグインは、[Elasticsearch](https://www.elastic.co/products/elasticsearch) の上に ORM のような抽象化を提供します。テストの作成、ドキュメントのインデックス作成、インデックスの検索を CakePHP から扱いやすくします。 + +## インストール + +アプリケーションのルートディレクトリーで Composer を使ってインストールします。 + +```bash +composer require cakephp/elastic-search:^5.0 +``` + +`src/Application.php` でプラグインを読み込みます。 + +```php +use Cake\ElasticSearch\Plugin as ElasticSearchPlugin; + +class Application extends BaseApplication +{ + public function bootstrap(): void + { + parent::bootstrap(); + $this->addPlugin(ElasticSearchPlugin::class); + } +} +``` + +`config/app.php` に `elastic` データソースを追加します。 + +```php +'Datasources' => [ + 'elastic' => [ + 'className' => 'Cake\ElasticSearch\Datasource\Connection', + 'driver' => 'Cake\ElasticSearch\Datasource\Connection', + 'hosts' => ['127.0.0.1:9200'], + 'index' => 'my_apps_index', + ], +] +``` + +以前の `host` と `port` を使う形式も引き続き利用できます。 + +## 概要 + +ElasticSearch プラグインは Elasticsearch のインデックスを CakePHP ORM に近い感覚で扱えるようにします。まず `Index` クラスを作成します。 + +```php +namespace App\Model\Index; + +use Cake\ElasticSearch\Index; + +class ArticlesIndex extends Index +{ +} +``` + +次に `Document` クラスを作成します。 + +```php +namespace App\Model\Document; + +use Cake\ElasticSearch\Document; + +class Article extends Document +{ +} +``` + +## インデックス付きドキュメントの検索 + +`find()` と `where()` で検索クエリーを構築できます。 + +```php +$query = $this->Articles->find() + ->where([ + 'title' => 'special', + 'or' => [ + 'tags in' => ['cake', 'php'], + 'tags not in' => ['c#', 'java'], + ], + ]); + +foreach ($query as $article) { + echo $article->title; +} +``` + +`QueryBuilder` を使えばより細かい条件も組み立てられます。 + +```php +$query->where(function ($builder) { + return $builder->and( + $builder->gt('views', 99), + $builder->term('author.name', 'sally') + ); +}); +``` + +## 新しいドキュメントの保存 + +```php +$article = $this->Articles->newEntity($data); +if ($this->Articles->save($article)) { + // Document はインデックスされました +} +``` + +埋め込みドキュメントをマーシャリングする場合は `associated` を指定します。 + +```php +$article = $this->Articles->newEntity($data, [ + 'associated' => ['Comments'], +]); +``` + +保存時には以下のイベントが発生します。 + +- `Model.beforeSave` +- `Model.buildRules` +- `Model.afterSave` + +## 既存ドキュメントの更新と削除 + +```php +$query = $this->Articles->find()->where(['user.name' => 'jill']); +foreach ($query as $doc) { + $doc->set($newProperties); + $this->Articles->save($doc); +} +``` + +```php +$doc = $this->Articles->get($id); +$this->Articles->delete($doc); +``` + +条件に一致するドキュメントをまとめて削除することもできます。 + +```php +$this->Articles->deleteAll(['user.name' => 'bob']); +``` + +## 埋め込みドキュメント + +`embedOne()` と `embedMany()` を使うと、ネストしたプロパティーに独自のドキュメントクラスを割り当てられます。 + +```php +namespace App\Model\Index; + +use Cake\ElasticSearch\Index; + +class ArticlesIndex extends Index +{ + public function initialize(): void + { + $this->embedOne('User'); + $this->embedMany('Comments', [ + 'entityClass' => 'MyComment', + ]); + } +} +``` + +`find()` や `get()` の結果は、設定した埋め込みドキュメントクラスに変換されます。 + +## インデックスインスタンスの取得 + +`IndexLocatorAwareTrait` を使うとインデックスを簡単に取得できます。 + +```php +use Cake\ElasticSearch\Datasource\IndexLocatorAwareTrait; + +class MyController extends Controller +{ + use IndexLocatorAwareTrait; + + public function index() + { + $articles = $this->fetchIndex('Articles'); + } +} +``` + +または `IndexLocator` を直接使えます。 + +```php +use Cake\ElasticSearch\Datasource\IndexLocator; + +$locator = new IndexLocator(); +$articles = $locator->get('Articles'); +$locator->clear(); +``` + +## テストフィクスチャー + +`MappingGenerator` を使ってテスト用のマッピングを読み込めます。 + +```php +use Cake\ElasticSearch\TestSuite\Fixture\MappingGenerator; + +$generator = new MappingGenerator('tests/mappings.php', 'test_elastic'); +$generator->reload(); +``` + +フィクスチャーは `Cake\ElasticSearch\TestSuite\TestFixture` を継承して作成します。 + +```php +namespace App\Test\Fixture; + +use Cake\ElasticSearch\TestSuite\TestFixture; + +class ArticlesFixture extends TestFixture +{ + public string $table = 'articles'; + + public array $records = [ + [ + 'user' => ['username' => 'billy'], + 'title' => 'First Post', + 'body' => 'Some content', + ], + ]; +} +``` diff --git a/docs/ja/index.rst b/docs/ja/index.rst deleted file mode 100644 index 4ed0b6c1..00000000 --- a/docs/ja/index.rst +++ /dev/null @@ -1,314 +0,0 @@ -ElasticSearch -############# - -ElasticSearch プラグインは、`elasticsearch `_ -の上に ORM のような抽象化を提供します。そのプラグインは、テストの作成、 -ドキュメントのインデックス作成、インデックスをより簡単に検索などの機能を提供します。 - -インストール -============ - -ElasticSearch プラグインをインストールするには、 ``composer`` が利用できます。(composer.json -ファイルが置かれている) アプリケーションの ROOT ディレクトリーから次のコマンドを実行します。 :: - - php composer.phar require cakephp/elastic-search "@stable" - -以下の1行をあなたのアプリケーションの **src/Application.php** ファイルに追加する必要があります。 :: - - $this->addPlugin('Cake/ElasticSearch', ['bootstrap' => true]); - - // 3.6.0 より前は、Plugin::load() を使用する必要があります - -追加で 'elastic' のデータソースの接続を **config/app.php** ファイルに設定する必要があります。 -設定例は以下のようになります。 :: - - // config/app.php の中で - 'Datasources' => [ - // 他のデータソース - 'elastic' => [ - 'className' => 'Cake\ElasticSearch\Datasource\Connection', - 'driver' => 'Cake\ElasticSearch\Datasource\Connection', - 'host' => '127.0.0.1', - 'port' => 9200, - 'index' => 'my_apps_index', - ], - ] - -概要 -==== - -ElasticSearch プラグインは elasticsearch インデックスと作用することを簡単にし、 -`ORM `__ に似たインターフェイスを提供します。まず最初に ``Type`` オブジェクトを -作成しなければいけません。 ``Type`` オブジェクトは elasticsearch 内では "Repository" -もしくは Table のようなクラスです。 :: - - // src/Model/Type/ArticlesType.php の中で - namespace App\Model\Type; - - use Cake\ElasticSearch\Type; - - class ArticlesType extends Type - { - } - -コントローラーで Type クラスを利用できます。 :: - - public function beforeFilter(Event $event) - { - parent::beforeFilter($event); - // 'Elastic' プロバイダーを利用して Type を読み込む - $this->loadModel('Articles', 'Elastic'); - } - - public function add() - { - $article = $this->Articles->newEntity(); - if ($this->request->is('post')) { - $article = $this->Articles->patchEntity($article, $this->request->getData()); - if ($this->Articles->save($article)) { - $this->Flash->success('It saved'); - } - } - $this->set(compact('article')); - } - -インデックスされた articles の基本的なビューを作成する必要があります。 :: - - // src/Template/Articles/add.ctp の中で - Form->create($article) ?> - Form->control('title') ?> - Form->control('body') ?> - Form->button('Save') ?> - Form->end() ?> - -これで、フォームの送信が可能になり、新しいドキュメントが elasticsearch に追加されました。 - -Document オブジェクト -===================== - -ORM と同様に、Elasticsearch ODM は `エンティティー `__ のようなクラスを使用しています。 -継承しなければならない基底クラスは ``Cake\ElasticSearch\Document`` です。 -Document クラスは、アプリケーションやプラグイン内の ``Model\Document`` 名前空間に配置します。 :: - - namespace App\Model\Document; - - use Cake\ElasticSearch\Document; - - class Article extends Document - { - } - -elasticsearch からのデータで Document を動作させるコンストラクターロジックの外側、 -インターフェイスと ``Document`` によって提供される機能は、 `Entities -`__ -内にあるものと同じです。 - -インデックス付きドキュメントの検索 -================================== - -いくつかのドキュメントをインデックスに登録した後、あなたはそれらを検索したいと思うでしょう。 -ElasticSearch プラグインを使用すると、検索クエリーを構築するためのクエリービルダーを提供します。 :: - - $query = $this->Articles->find() - ->where([ - 'title' => 'special', - 'or' => [ - 'tags in' => ['cake', 'php'], - 'tags not in' => ['c#', 'java'] - ] - ]); - - foreach ($query as $article) { - echo $article->title; - } - -フィルタリング条件を追加するために ``QueryBuilder`` を使用することができます。 :: - - $query->where(function ($builder) { - return $builder->and( - $builder->gt('views', 99), - $builder->term('author.name', 'sally') - ); - }); - -`QueryBuilder のソース -`_ -は、多くの一般的に使用されるメソッドの例となるメソッドの完全なリストを持っています。 - -データのバリデーションとアプリケーションルールの使用 -==================================================== - -ORMと同様に、ElasticSearch プラグインは、ドキュメントをマーシャリングするときに -データを検証することができます。リクエストデータのバリデート、およびアプリケーションルールの -適用は、リレーショナルORMと同じ動作をします。詳細については、 `エンティティー構築前のデータ検証 `__ と -`条件付き/動的なエラーメッセージ `__ のセクションをご覧ください。 - -.. ネストされたバリデータに関する情報を必要としています。 - -新しいドキュメントの保存 -======================== - -elasticsearch にいくつかのデータをインデックスする準備ができたら、最初にインデックスが付けられる -``Document`` にデータを変換する必要があります。 :: - - $article = $this->Articles->newEntity($data); - if ($this->Articles->save($article)) { - // Document はインデックスされました - } - -ドキュメントをマーシャリングするとき、 ``associated`` キーを使用してマーシャリングしたい -埋め込みドキュメントを指定することができます。 :: - - $article = $this->Articles->newEntity($data, ['associated' => ['Comments']]); - -ドキュメントを保存すると、次のイベントがトリガーされます: - -* ``Model.beforeSave`` - ドキュメントが保存される前に発生します。 - このイベントを停止することによって保存操作を防ぐことができます。 -* ``Model.buildRules`` - ルールチェッカーが最初に構築されているときに発生します。 -* ``Model.afterSave`` - ドキュメントが保存された後に発生します。 - -.. note:: - 親ドキュメントとすべての埋め込みドキュメントを1つの操作で保存するため、 - 埋め込みドキュメントのためのイベントはありません。 - -既存ドキュメントの更新 -====================== - -データの再インデックスが必要な場合、既存のエンティティーにパッチを適用すると再保存できます。 :: - - $query = $this->Articles->find()->where(['user.name' => 'jill']); - foreach ($query as $doc) { - $doc->set($newProperties); - $this->Articles->save($doc); - } - -ドキュメントの削除 -================== - -ドキュメントを検索した後、それを削除することができます。 :: - - $doc = $this->Articles->get($id); - $this->Articles->delete($doc); - -また、特定の条件に一致するドキュメントを削除することができます。 :: - - $this->Articles->deleteAll(['user.name' => 'bob']); - -埋め込みドキュメント -==================== - -埋め込みドキュメントを定義することで、ドキュメント内の特定のプロパティーのパスに -エンティティークラスを添付することができます。これは、親ドキュメント内のドキュメントに -独自の振る舞いを提供することができます。たとえば、あなたが記事に埋め込まれたコメントは、 -特定のアプリケーション固有のメソッドを持っている場合があります。あなたが埋め込みドキュメントを -定義するために ``embedOne`` と ``embedMany`` を使用することができます。 :: - - // in src/Model/Type/ArticlesType.php - namespace App\Model\Type; - - use Cake\ElasticSearch\Type; - - class ArticlesType extends Type - { - public function initialize() - { - $this->embedOne('User'); - $this->embedMany('Comments', [ - 'entityClass' => 'MyComment' - ]); - } - } - -上記の ``Article`` ドキュメント上の2つの埋め込みドキュメントを作成します。 -``User`` 埋め込みは ``App\Model\Document\User`` のインスタンスに ``user`` プロパティーを変換します。 -プロパティー名と一致していないクラス名を使用する埋め込みコメントを得るためには、カスタムクラス名を -設定するための ``entityClass`` オプションを使用することができます。 - -埋め込みドキュメントをセットアップしたら、 ``find()`` と ``get`` の結果は -正しい埋め込みドキュメントクラスのオブジェクトを返します。 :: - - $article = $this->Articles->get($id); - // App\Model\Document\User のインスタンス - $article->user; - - // App\Model\Document\Comment インスタンスの配列 - $article->comments; - -Type インスタンスの取得 -======================= - -ORM と同様に、ElasticSearch プラグインは ``Type`` のインスタンスを取得するための -ファクトリー/レジストリーを提供します。 :: - - use Cake\ElasticSearch\TypeRegistry; - - $articles = TypeRegistry::get('Articles'); - -レジストリーのフラッシュ ------------------------- - -テストケースの中で、レジストリーをフラッシュすることができます。 -そうすることでモックオブジェクトを使用したり、Type の依存関係を変更する際に便利です。 :: - - TypeRegistry::flush(); - -テストフィクスチャー -==================== - -ElasticSearch プラグインは、シームレスなテストスイートの統合を提供します。ちょうどデータベースの -フィクスチャーのように、elasticsearch のためのテストフィクスチャーを作成することができます。 -次のように Articles タイプのテストフィクスチャーを定義することができます。 :: - - namespace App\Test\Fixture; - - use Cake\ElasticSearch\TestSuite\TestFixture; - - /** - * Articles fixture - */ - class ArticlesFixture extends TestFixture - { - /** - * The table/type for this fixture. - * - * @var string - */ - public $table = 'articles'; - - /** - * The mapping data. - * - * @var array - */ - public $schema = [ - 'id' => ['type' => 'integer'], - 'user' => [ - 'type' => 'nested', - 'properties' => [ - 'username' => ['type' => 'string'], - ] - ], - 'title' => ['type' => 'string'], - 'body' => ['type' => 'string'], - ]; - - public $records = [ - [ - 'user' => [ - 'username' => 'billy' - ], - 'title' => 'First Post', - 'body' => 'Some content' - ] - ]; - } - -``schema`` プロパティーは `ネイティブ elasticsearch マッピングフォーマット -`_ を使用します。 -安全にタイプ名およびトップレベルの ``properties`` キーを省略することができます。 -フィクスチャーが作成されたら、あなたのテストの ``fixtures`` プロパティーに含めることによって、 -あなたのテストケースで使用することができます。 :: - - public $fixtures = ['app.Articles']; - diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 00000000..7a760b05 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,2104 @@ +{ + "name": "docs", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "docs", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@cakephp/docs-skeleton": "git+ssh://git@github.com:cakephp/docs-skeleton.git#node-package", + "vitepress": "^2.0.0-alpha.16" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cakephp/docs-skeleton": { + "version": "1.0.0", + "resolved": "git+ssh://git@github.com/cakephp/docs-skeleton.git#9501c6d47a80645604fceb31328368eeb6a21bc6", + "bin": { + "cakedocs": "bin/cakedocs.js" + }, + "peerDependencies": { + "vitepress": "^2.0.0-alpha.15" + } + }, + "node_modules/@docsearch/css": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.6.2.tgz", + "integrity": "sha512-fH/cn8BjEEdM2nJdjNMHIvOVYupG6AIDtFVDgIZrNzdCSj4KXr9kd+hsehqsNGYjpUjObeKYKvgy/IwCb1jZYQ==", + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-4.6.2.tgz", + "integrity": "sha512-qj1yoxl3y4GKoK7+VM6fq/rQqPnvUmg3IKzJ9x0VzN14QVzdB/SG/J6VfV1BWT5RcPUFxIcVwoY1fwHM2fSRRw==", + "license": "MIT" + }, + "node_modules/@docsearch/sidepanel-js": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/sidepanel-js/-/sidepanel-js-4.6.2.tgz", + "integrity": "sha512-Pni85AP/GwRj7fFg8cBJp0U04tzbueBvWSd3gysgnOsVnQVSZwSYncfErUScLE1CAtR+qocPDFjmYR9AMRNJtQ==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.76", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.76.tgz", + "integrity": "sha512-lLRlA8yaf+1L5VCPRvR9lynoSklsddKHEylchmZJKdj/q2xVQ1ZAEJ8SCQlv9cbgtMefnlyM98U+8Si2aoFZPA==", + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", + "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", + "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", + "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.4" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.23.0.tgz", + "integrity": "sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.23.0", + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz", + "integrity": "sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg==", + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.32.tgz", + "integrity": "sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.2", + "@vue/shared": "3.5.32", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.32.tgz", + "integrity": "sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.32", + "@vue/shared": "3.5.32" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.32.tgz", + "integrity": "sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.2", + "@vue/compiler-core": "3.5.32", + "@vue/compiler-dom": "3.5.32", + "@vue/compiler-ssr": "3.5.32", + "@vue/shared": "3.5.32", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.8", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.32.tgz", + "integrity": "sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.32", + "@vue/shared": "3.5.32" + } + }, + "node_modules/@vue/devtools-api": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.1.1.tgz", + "integrity": "sha512-bsDMJ07b3GN1puVwJb/fyFnj/U2imyswK5UQVLZwVl7O05jDrt6BHxeG5XffmOOdasOj/bOmIjxJvGPxU7pcqw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.1.1" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.1.1.tgz", + "integrity": "sha512-gVBaBv++i+adg4JpH71k9ppl4soyR7Y2McEqO5YNgv0BI1kMZ7BDX5gnwkZ5COYgiCyhejZG+yGNrBAjj6Coqg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.1.1", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "perfect-debounce": "^2.0.0" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.1.1.tgz", + "integrity": "sha512-+h4ttmJYl/txpxHKaoZcaKpC+pvckgLzIDiSQlaQ7kKthKh8KuwoLW2D8hPJEnqKzXOvu15UHEoGyngAXCz0EQ==", + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.32.tgz", + "integrity": "sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.32" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.32.tgz", + "integrity": "sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.32", + "@vue/shared": "3.5.32" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.32.tgz", + "integrity": "sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.32", + "@vue/runtime-core": "3.5.32", + "@vue/shared": "3.5.32", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.32.tgz", + "integrity": "sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.32", + "@vue/shared": "3.5.32" + }, + "peerDependencies": { + "vue": "3.5.32" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.32.tgz", + "integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==", + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.2.1.tgz", + "integrity": "sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "14.2.1", + "@vueuse/shared": "14.2.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/integrations": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-14.2.1.tgz", + "integrity": "sha512-2LIUpBi/67PoXJGqSDQUF0pgQWpNHh7beiA+KG2AbybcNm+pTGWT6oPGlBgUoDWmYwfeQqM/uzOHqcILpKL7nA==", + "license": "MIT", + "dependencies": { + "@vueuse/core": "14.2.1", + "@vueuse/shared": "14.2.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7 || ^8", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7 || ^8", + "vue": "^3.5.0" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-14.2.1.tgz", + "integrity": "sha512-1ButlVtj5Sb/HDtIy1HFr1VqCP4G6Ypqt5MAo0lCgjokrk2mvQKsK2uuy0vqu/Ks+sHfuHo0B9Y9jn9xKdjZsw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.2.1.tgz", + "integrity": "sha512-shTJncjV9JTI4oVNyF1FQonetYAiTBd+Qj7cY89SWbXSkx7gyhrgtEdF2ZAVWS1S3SHlaROO6F2IesJxQEkZBw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/focus-trap": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-8.0.1.tgz", + "integrity": "sha512-9ptSG6z51YQOstI/oN4XuVGP/03u2nh0g//qz7L6zX0i6PZiPnkcf3GenXq7N2hZnASXaMxTPpbKwdI+PFvxlw==", + "license": "MIT", + "peer": true, + "dependencies": { + "tabbable": "^6.4.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "license": "MIT" + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oniguruma-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", + "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.5.tgz", + "integrity": "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.1", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/shiki": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", + "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.23.0", + "@shikijs/engine-javascript": "3.23.0", + "@shikijs/engine-oniguruma": "3.23.0", + "@shikijs/langs": "3.23.0", + "@shikijs/themes": "3.23.0", + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "2.0.0-alpha.17", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-2.0.0-alpha.17.tgz", + "integrity": "sha512-Z3VPUpwk/bHYqt1uMVOOK1/4xFiWQov1GNc2FvMdz6kvje4JRXEOngVI9C+bi5jeedMSHiA4dwKkff1NCvbZ9Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@docsearch/css": "^4.5.3", + "@docsearch/js": "^4.5.3", + "@docsearch/sidepanel-js": "^4.5.3", + "@iconify-json/simple-icons": "^1.2.69", + "@shikijs/core": "^3.22.0", + "@shikijs/transformers": "^3.22.0", + "@shikijs/types": "^3.22.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^6.0.4", + "@vue/devtools-api": "^8.0.5", + "@vue/shared": "^3.5.27", + "@vueuse/core": "^14.2.0", + "@vueuse/integrations": "^14.2.0", + "focus-trap": "^8.0.0", + "mark.js": "8.11.1", + "minisearch": "^7.2.0", + "shiki": "^3.22.0", + "vite": "^7.3.1", + "vue": "^3.5.27" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "oxc-minify": "*", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "oxc-minify": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.32.tgz", + "integrity": "sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.5.32", + "@vue/compiler-sfc": "3.5.32", + "@vue/runtime-dom": "3.5.32", + "@vue/server-renderer": "3.5.32", + "@vue/shared": "3.5.32" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..f1407730 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,19 @@ +{ + "name": "docs", + "version": "1.0.0", + "description": "", + "main": "config.js", + "scripts": { + "docs:dev": "vitepress dev", + "docs:build": "vitepress build", + "docs:preview": "vitepress preview" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "dependencies": { + "@cakephp/docs-skeleton": "git+ssh://git@github.com:cakephp/docs-skeleton.git#node-package", + "vitepress": "^2.0.0-alpha.16" + } +} diff --git a/docs/public/favicon/apple-touch-icon.png b/docs/public/favicon/apple-touch-icon.png new file mode 100644 index 00000000..c6d073d7 Binary files /dev/null and b/docs/public/favicon/apple-touch-icon.png differ diff --git a/docs/public/favicon/favicon-96x96.png b/docs/public/favicon/favicon-96x96.png new file mode 100644 index 00000000..6642e0cd Binary files /dev/null and b/docs/public/favicon/favicon-96x96.png differ diff --git a/docs/public/favicon/favicon.ico b/docs/public/favicon/favicon.ico new file mode 100644 index 00000000..405aa94c Binary files /dev/null and b/docs/public/favicon/favicon.ico differ diff --git a/docs/public/favicon/favicon.svg b/docs/public/favicon/favicon.svg new file mode 100644 index 00000000..805ef4b8 --- /dev/null +++ b/docs/public/favicon/favicon.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/public/favicon/site.webmanifest b/docs/public/favicon/site.webmanifest new file mode 100644 index 00000000..4f23fb31 --- /dev/null +++ b/docs/public/favicon/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "CakePHP", + "short_name": "CakePHP", + "icons": [ + { + "src": "/favicon/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/favicon/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/docs/public/favicon/web-app-manifest-192x192.png b/docs/public/favicon/web-app-manifest-192x192.png new file mode 100644 index 00000000..b5df2990 Binary files /dev/null and b/docs/public/favicon/web-app-manifest-192x192.png differ diff --git a/docs/public/favicon/web-app-manifest-512x512.png b/docs/public/favicon/web-app-manifest-512x512.png new file mode 100644 index 00000000..6a522de3 Binary files /dev/null and b/docs/public/favicon/web-app-manifest-512x512.png differ diff --git a/docs/public/fonts/cakedingbats-webfont.eot b/docs/public/fonts/cakedingbats-webfont.eot new file mode 100644 index 00000000..0800d1e7 Binary files /dev/null and b/docs/public/fonts/cakedingbats-webfont.eot differ diff --git a/docs/public/fonts/cakedingbats-webfont.svg b/docs/public/fonts/cakedingbats-webfont.svg new file mode 100644 index 00000000..d2afda5e --- /dev/null +++ b/docs/public/fonts/cakedingbats-webfont.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/public/fonts/cakedingbats-webfont.ttf b/docs/public/fonts/cakedingbats-webfont.ttf new file mode 100644 index 00000000..78ad6c88 Binary files /dev/null and b/docs/public/fonts/cakedingbats-webfont.ttf differ diff --git a/docs/public/fonts/cakedingbats-webfont.woff b/docs/public/fonts/cakedingbats-webfont.woff new file mode 100644 index 00000000..a95e1b38 Binary files /dev/null and b/docs/public/fonts/cakedingbats-webfont.woff differ diff --git a/docs/public/fonts/cakedingbats-webfont.woff2 b/docs/public/fonts/cakedingbats-webfont.woff2 new file mode 100644 index 00000000..2cd9fdd0 Binary files /dev/null and b/docs/public/fonts/cakedingbats-webfont.woff2 differ diff --git a/docs/public/logo.svg b/docs/public/logo.svg new file mode 100644 index 00000000..829c8e98 --- /dev/null +++ b/docs/public/logo.svg @@ -0,0 +1,27 @@ + + + +