diff --git a/docs/code-review-checklist.md b/.agents/code-review-checklist.md similarity index 76% rename from docs/code-review-checklist.md rename to .agents/code-review-checklist.md index 2f42e3963..24f919692 100644 --- a/docs/code-review-checklist.md +++ b/.agents/code-review-checklist.md @@ -4,17 +4,17 @@ When performing code review, double check all of the items below: - [ ] Confirm that the changes solve the issues the PR is trying to solve partially or fully. - [ ] Review the code in terms of the [OWASP top 10 security issues](https://owasp.org/Top10/). -- [ ] Verify that the code follows the [Code style guide](contributing.md#code-style-guide). +- [ ] Verify that the code follows the [Code style guide](). - [ ] Avoid using `any` at all costs. If there is no way to circumvent using it, document the reason carefully and consider using `@ts-expect-error` instead. - [ ] There is no code that is repeated within the PR or elsewhere in the repo. - [ ] All new components, functions and other entities are documented - [ ] The repo documentation markdown files are updated if the changes touch upon those. - [ ] If the change adds functions available to the user, tracking events are enabled with new ones defined if needed. -- [ ] Any new Svelte components that have been created, follow the [Svelte component guidelines](contributing.md#svelte-components). +- [ ] Any new Svelte components that have been created follow the [Svelte component guidelines](). - [ ] Errors are handled properly and logged in the code. - [ ] Troubleshoot any failing checks in the PR. - [ ] Check that parts of the application that share dependencies with the PR but are not included in it are not unduly affected. - [ ] The changes pass the [WGAC A and AA requirements for accessibility](https://usability.yale.edu/web-accessibility/articles/wcag2-checklist). - [ ] The changed parts of the app are fully usable with keyboard navigation and screen-reading. - [ ] Documentation is added wherever necessary. -- [ ] The commit history is clean and linear, and the commits follow the [commit guidelines](contributing.md#commit-your-update) +- [ ] The commit history is clean and linear, and the commits follow the [commit guidelines]() diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000..10842a650 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": ["Bash(yarn test:*)", "Bash(find:*)"], + "deny": [], + "ask": [] + } +} diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index f71f74de1..000000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "permissions": { - "allow": ["Bash(fi:*)", "Bash(done)", "Bash(yarn test:unit:*)"], - "deny": [], - "ask": [] - } -} diff --git a/.env.example b/.env.example index 6daafa44b..cb5a51d71 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,12 @@ -# Strapi dependencies +################################################################ +# Backend: Strapi configuration +################################################################ + STRAPI_HOST=0.0.0.0 STRAPI_PORT=1337 APP_KEYS="toBeModified1,toBeModified2" API_TOKEN_SALT=tobemodified -# ADMIN_JWT_SECRET and JWT_SECRET has to be different +# ADMIN_JWT_SECRET and JWT_SECRET have to be different ADMIN_JWT_SECRET=tobemodified JWT_SECRET=tobemodified2 DATABASE_HOST=postgres @@ -14,10 +17,18 @@ DATABASE_PASSWORD=strapi DATABASE_SCHEMA=public DATABASE_SSL_SELF=false +# See also PUBLIC_BROWSER_FRONTEND_URL below + +################################################################ +# Backend: AWS LocalStack (development only) +################################################################ + ## AWS LocalStack development endpoint LOCALSTACK_ENDPOINT=http://127.0.0.1:4566 -## AWS SES Settings +################################################################ +# Backend: AWS SES email, used by Strapi +################################################################ ## These settings correspond to LocalStack system defaults ## https://docs.localstack.cloud/references/configuration/ @@ -30,9 +41,9 @@ MAIL_FROM="no-reply@openvaa.org" MAIL_FROM_NAME="Voting Advice Application" MAIL_REPLY_TO="contact@openvaa.org" -## AWS S3 settings - -AWS_S3_BUCKET=static.openvaa.org +################################################################ +# Backend: AWS S3 storage, used by Strapi +################################################################ ## These settings correspond to LocalStack system defaults ## https://docs.localstack.cloud/references/configuration/ @@ -40,14 +51,18 @@ AWS_S3_ACCESS_KEY_ID="test" AWS_S3_ACCESS_SECRET="test" AWS_S3_REGION=us-east-1 +## S3 bucket name used by Strapi to store static content (e.g. images uploaded via Strapi's UI) +AWS_S3_BUCKET=static.openvaa.org + ## The base URL is used to access static content uploaded via Strapi's UI to AWS S3: ## - on production it uses a dedicated subdomain which is linked to an eponymous AWS S3 bucket via a CNAME DNS record ## - in development it points directly to LocalStack host and is appended by the S3 bucket name in Strapi's `plugin.ts` STATIC_CONTENT_BASE_URL=http://localhost:4566 STATIC_MEDIA_CONTENT_PATH=public/media -# Point to a folder (relative to /backend/vaa-strapi) if you want to load data on initialise. This will only take place if the database contains no Election -LOAD_DATA_ON_INITIALISE_FOLDER="" +################################################################ +# Backend: Mock data generation in Strapi +################################################################ # Set to true to enable mock data on an empty database GENERATE_MOCK_DATA_ON_INITIALISE=true @@ -55,8 +70,9 @@ GENERATE_MOCK_DATA_ON_INITIALISE=true # Used only in development builds GENERATE_MOCK_DATA_ON_RESTART=false -# LLM -LLM_OPENAI_API_KEY="" +################################################################ +# Frontend: configuration +################################################################ # Frontend dependencies # NB! If adding more such urls with different hosts on the server and client, please check whether /frontend/src/routes/[[lang=locale]]/api/cache/+server.ts needs to be updated accordingly @@ -64,16 +80,48 @@ LLM_OPENAI_API_KEY="" PUBLIC_BROWSER_BACKEND_URL=http://localhost:1337 # Used to reach frontend instance from a server (differs from `PUBLIC_BROWSER_BACKEND_URL` when using Docker) PUBLIC_SERVER_BACKEND_URL=http://strapi:1337 -# Used to reach frontend instance from a browser +# Used to reach frontend instance from a browser. NB. This variable is used by Strapi PUBLIC_BROWSER_FRONTEND_URL=http://localhost:5173 # Used to reach frontend instance from a server (differs from `PUBLIC_BROWSER_FRONTEND_URL` when using Docker) PUBLIC_SERVER_FRONTEND_URL=http://frontend:5173 +# Make sure this martches the urls above FRONTEND_PORT=5173 +################################################################ +# Frontend: cache settings +################################################################ + +# FlatCache disk cache settings + +# Set to 'true' to enable the cache. The variable must be public because the universal data adapter uses it. +PUBLIC_CACHE_ENABLED=false +# An absolute path to the cache location +CACHE_DIR=/var/data/cache +# "Time-To-Live" (TTL) means each cached item will be removed after {env.CACHE_TTL} seconds, even if it's still needed. +# 86400000 seconds = 24 hours +CACHE_TTL=86400000 +# "Least Recently Used" (LRU) means the cache will only keep the most recent {env.CACHE_LRU_SIZE} items. +CACHE_LRU_SIZE=1000 +# The cache checks for expired items every {env.CACHE_EXPIRATION_INTERVAL} seconds and removes them. +# 3600000 seconds = 1 hour +CACHE_EXPIRATION_INTERVAL=3600000 + +################################################################ +# Frontend: local data adapter settings +################################################################ + # An absolute path to the local data location when using the local data adapter LOCAL_DATA_DIR=/var/data/local +################################################################ +# Frontend: pregistration settings +################################################################ + +# Source: {PUBLIC_BROWSER_BACKEND_URL}/admin/settings/api-tokens +# Minimal permissions: Users-Permissions > Candidate > Preregister +BACKEND_API_TOKEN=api_token + # IdP (Signicat) # Source: https://openvaa.sandbox.signicat.com/auth/open/.well-known/openid-configuration @@ -94,24 +142,15 @@ IDENTITY_PROVIDER_CLIENT_SECRET=client_secret # Select "Encryption", keep the private part of the key pair. IDENTITY_PROVIDER_DECRYPTION_JWKS='[{"kty":"RSA","kid":"{key_id}","use":"enc","alg":"RSA-OAEP","e":"{secret}","n":"{secret}","d":"{secret}","p":"{secret}","q":"{secret}","dp":"{secret}","dq":"{secret}","qi":"{secret}"}]' -# Source: {PUBLIC_BROWSER_BACKEND_URL}/admin/settings/api-tokens -# Minimal permissions: Users-Permissions > Candidate > Preregister -BACKEND_API_TOKEN=api_token +################################################################ +# Frontend: LLM settings, used by the Admin App +################################################################ -# If 'true', frontend debug messages will be logged even in production -PUBLIC_DEBUG=false +LLM_OPENAI_API_KEY="" -# FlatCache disk cache settings +################################################################ +# Debugging +################################################################ -# Set to 'true' to enable the cache. The variable must be public because the universal data adapter uses it. -PUBLIC_CACHE_ENABLED=false -# An absolute path to the cache location -CACHE_DIR=/var/data/cache -# "Time-To-Live" (TTL) means each cached item will be removed after {env.CACHE_TTL} seconds, even if it's still needed. -# 86400000 seconds = 24 hours -CACHE_TTL=86400000 -# "Least Recently Used" (LRU) means the cache will only keep the most recent {env.CACHE_LRU_SIZE} items. -CACHE_LRU_SIZE=1000 -# The cache checks for expired items every {env.CACHE_EXPIRATION_INTERVAL} seconds and removes them. -# 3600000 seconds = 1 hour -CACHE_EXPIRATION_INTERVAL=3600000 +# If 'true', frontend debug messages will be logged even in production +PUBLIC_DEBUG=false diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index ff0242087..9c3897fca 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -52,7 +52,7 @@ jobs: Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR. - Make sure to check all the items in the [Code Review Checklist](/docs/code-review-checklist.md). + Make sure to check all the items in the [Code Review Checklist](/.agents/code-review-checklist.md). # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md # or https://docs.claude.com/en/docs/claude-code/cli-reference for available options diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..20eca7e32 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,73 @@ +name: Deploy Documentation + +on: + push: + branches: + - deploy-docs + paths: + - 'docs/**' + - 'packages/**' + - 'frontend/src/lib/**' + - 'scripts/generate-docs/**' + - '.github/workflows/docs.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'yarn' + + - name: Install dependencies + run: | + yarn install --immutable + cd docs + yarn install --immutable + + - name: Build shared packages + run: yarn build:shared + + - name: Generate documentation + run: | + cd docs + yarn generate:docs + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Build docs site + run: | + cd docs + yarn build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./docs/build + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 35ae3bd9e..4b408d84b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ !.env.example node_modules +# Claude Code local settings +.claude/*.local.json + # Yarn 4 without Zero-installs .pnp.* .yarn/* @@ -11,4 +14,4 @@ node_modules !.yarn/plugins !.yarn/releases !.yarn/sdks -!.yarn/versions \ No newline at end of file +!.yarn/versions diff --git a/.lintstagedrc.json b/.lintstagedrc.json index 93a1279f5..448447abb 100644 --- a/.lintstagedrc.json +++ b/.lintstagedrc.json @@ -2,7 +2,7 @@ "*.{html,js,jsx,cjs,mjssvelte,ts,tsx,cts,mts,xml,yaml,yml}": [ "yarn build:app-shared", "prettier --write", - "eslint --fix --flag unstable_config_lookup_from_file" + "eslint --fix --flag v10_config_lookup_from_file" ], "*.{css,json,md}": ["yarn build:app-shared", "prettier --write"] } diff --git a/.prettierignore b/.prettierignore index f416acc3f..c7272210c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -28,4 +28,7 @@ frontend/package frontend/tailwind.config.cjs # Ignores copied from /tests/.prettierignore tests/test-results/ -tests/playwright*/ \ No newline at end of file +tests/playwright*/ +# Ignores docs because it must be formatted separately due to different Svelte version +# TODO[Svelte 5]: Remove this limitation +docs diff --git a/CLAUDE.md b/CLAUDE.md index a0f394d36..7e32e5445 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -309,3 +309,7 @@ Recent costs (2024-2025): $80-350/month depending on traffic and instance sizes. **2025 H2**: Documentation site, AI features, application manager UI, first production release **2026**: Plugins/customization, multi-tenant model, migration from Strapi to Supabase, Svelte 5 upgrade + +## Code Review + +When performing code review or developing new features, make sure to check all the items in the [Code Review Checklist](/.agents/code-review-checklist.md). diff --git a/README.md b/README.md index a5733427c..505d0890a 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,18 @@ # OpenVAA Voting Advice Application -A software framework for creating [Voting Advice Applications](https://en.wikipedia.org/wiki/Voting_advice_application) (VAA), also known as Election Compasses. In short, VAAs are applications that help voters find candidates or parties in elections based on how much they agree about political or value statements. They’re currently most popular in the Nordic countries, the Netherlands and the German-speaking part of Europe. +

Read the docs at openvaa.org.

-This repository is a monorepo containing the frontend and backend, as well as other modules. In addition to the voters’ frontend, there is a similar UI for candidates that they can use to input their answers. +OpenVAA is a comprehensive open-source framework for building configurable [Voting Advice Applications](https://openvaa.org/publishers-guide/what-are-vaas/intro) (VAAs). It's designed to work in any election context and is fully localisable, accessible, modular and free to use. -The project is coordinated by the Finnish non-profit association [OpenVAA](https://openvaa.org/en) and funded by [Sitra](https://www.sitra.fi/en/) – the Finnish innovation fund. +OpenVAA was built for two reasons: to offer a transparent alternative for proprietary VAAs, and to kickstart development of more and better VAAs. OpenVAA is built by developers and researchers and maintained by a [Finnish non-profit](https://openvaa.org/about/association) of the same name. -## 💡 Features +## Version `0.1 Shiba` is out now! -- 🔎 Transparent -- 💸 Free to use -- 🌍 Fully localisable -- 🗳 Use in any election -- 🤲 Accessible -- 🧩 Modular, customisable and extendable -- 🕶️ Privacy-preserving -- 🎓 Informed by research and research-friendly - -See full list of [features](/docs/features/). - -## 🔨 Use cases - -- Collect candidates’ or parties’ answers and publish a VAA for voters -- Use previously collected answers to publish a VAA -- Rapidly prototype new VAA designs or functions -- Collect VAA usage data for research - -## 🥅 Project goals - -- Offer a fully transparent alternative to closed-source VAAs -- Make it easier to develop new VAA features and designs -- Expand the use of VAAs to elections where they’re not currently used -- Facilitate research on VAAs - -## 🚧 Project status: Alpha - -> Our latest VAA release is the [2025 Finnish Local Elections Election Compass for Youth](https://nuortenvaalikone.openvaa.org). - -> To stay up to speed, please ⭐️ star the repo or [📧 subscribe to our newsletter](https://openvaa.org/newsletter). - -The project is currently in an alpha phase with the onus being on refactoring the existing code base for greater robustness and a better developer experience, as well as implementing some missing features. - -We released two pilot VAAs for the 2024 EU Elections, which you can try out to see the software in action. - -| Video-based VAA |  Traditional VAA | -| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -|  The Election Compass for Young People – EE 2024 | EE 2024 Election Compass | -|  Finnish, Swedish and English |  Finnish only | -| Features custom video content | Select questions by theme | -| User survey and extended analytics | No analytics | -|  Data stored in Strapi backend | Data stored in local JSON files | -|  Postgres server and two Docker containers running on Render | Single Docker container running on Render | -| nuortenvaalikone.openvaa.org | vaalikone.openvaa.org | - -## 🎢 Roadmap - -**2026** - Added features for developer-friendliness - -- Enable plugins or easier customisation of pages and main components -- Multi-tenant model - -## 🪢 Collaborate - -In addition to developers and designers willing to contribute to the codebase (see below), we’re also looking for researchers to collaborate with and organisations willing to publish their own VAAs. Reach us at info@openvaa.org if you’re interested. - -## 🍭 Contributing - -We’re very happy to accept any help in coding, design, testing, translating and so on. If you want to help, drop a line at info@openvaa.org. - -See [the contribution guide](/docs/README.md#contributing) for further info about contributing to the project. - ---- - -## Getting Started - -See [Developer Guide](/docs/README.md) - -## Deploying - -See [Deployment guide](/docs/README.md#deployment). - -## Troubleshooting - -See [Troubleshooting](/docs/README.md#troubleshooting) for solutions to some common issues. +
+ A Shiba inu dog, the mascot of the OpenVAA 0.1 release + +
diff --git a/backend/vaa-strapi/Dockerfile b/backend/vaa-strapi/Dockerfile index 38ba5f76e..0897eb42a 100644 --- a/backend/vaa-strapi/Dockerfile +++ b/backend/vaa-strapi/Dockerfile @@ -15,6 +15,7 @@ COPY package.json yarn.lock .yarnrc.yml ./ COPY .yarn ./.yarn # To be able to use --immutable, we need to copy all the packages in the workspace COPY backend backend +COPY docs docs COPY frontend frontend COPY packages packages RUN yarn install --immutable diff --git a/backend/vaa-strapi/docker-compose.dev.yml b/backend/vaa-strapi/docker-compose.dev.yml index 74b79120d..0151bd8b1 100644 --- a/backend/vaa-strapi/docker-compose.dev.yml +++ b/backend/vaa-strapi/docker-compose.dev.yml @@ -3,7 +3,7 @@ services: awslocal: image: localstack/localstack ports: - - '127.0.0.1:4566:4566' + - "127.0.0.1:4566:4566" environment: DEBUG: 1 SERVICES: s3,ses @@ -21,7 +21,7 @@ services: - ./localstack-s3-cors-policy.json:/etc/localstack/s3-cors-policy.json # Mounting the Docker socket /var/run/docker.sock as a volume is required for some services # that use Docker to provide the emulation, such as AWS Lambda. - - '/var/run/docker.sock:/var/run/docker.sock' + - "/var/run/docker.sock:/var/run/docker.sock" strapi: build: # Context includes the root of the repository so that we can expose shared/ module. @@ -52,7 +52,6 @@ services: DATABASE_SSL_SELF: ${DATABASE_SSL_SELF} GENERATE_MOCK_DATA_ON_INITIALISE: ${GENERATE_MOCK_DATA_ON_INITIALISE} GENERATE_MOCK_DATA_ON_RESTART: ${GENERATE_MOCK_DATA_ON_RESTART} - LOAD_DATA_ON_INITIALISE_FOLDER: ${LOAD_DATA_ON_INITIALISE_FOLDER} AWS_SES_ACCESS_KEY_ID: ${AWS_SES_ACCESS_KEY_ID} AWS_SES_SECRET_ACCESS_KEY: ${AWS_SES_SECRET_ACCESS_KEY} AWS_SES_REGION: ${AWS_SES_REGION} @@ -69,7 +68,7 @@ services: PUBLIC_BROWSER_FRONTEND_URL: ${PUBLIC_BROWSER_FRONTEND_URL} volumes: # Creating this volume will make Docker update changes from local automatically - # Note that this only applies to src/, where all the source code is anyway, except the config. + # Note that this only applies to src/, where all the source code is anyway, except the config. # If you make changes to it, you need to rebuild the container. # - ./src:/opt/backend/vaa-strapi/src - strapi-uploads:/opt/backend/vaa-strapi/public/uploads @@ -88,10 +87,11 @@ services: volumes: - postgres:/var/lib/postgresql/data ports: - - '5432:5432' + - "5432:5432" restart: always healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USERNAME} -d ${DATABASE_NAME}"] + test: + ["CMD-SHELL", "pg_isready -U ${DATABASE_USERNAME} -d ${DATABASE_NAME}"] interval: 30s timeout: 10s retries: 5 @@ -100,7 +100,7 @@ services: image: adminer restart: always ports: - - '4567:8080' + - "4567:8080" volumes: postgres: diff --git a/backend/vaa-strapi/package.json b/backend/vaa-strapi/package.json index 77eee12cd..ff9a75955 100644 --- a/backend/vaa-strapi/package.json +++ b/backend/vaa-strapi/package.json @@ -53,7 +53,7 @@ "@types/react": "^18.3.18", "@types/react-is": "^19.0.0", "@typescript-eslint/parser": "^8.19.1", - "eslint": "~9.14.0", + "eslint": "^9.39.2", "globals": "^15.14.0", "jest": "^29.7.0", "sqlite3": "^5.1.7", diff --git a/backend/vaa-strapi/src/constants.ts b/backend/vaa-strapi/src/constants.ts index 08f1ada80..e38c6b87a 100644 --- a/backend/vaa-strapi/src/constants.ts +++ b/backend/vaa-strapi/src/constants.ts @@ -19,11 +19,6 @@ export const generateMockDataOnInitialise = process.env.GENERATE_MOCK_DATA_ON_IN export const generateMockDataOnRestart = (process.env.GENERATE_MOCK_DATA_ON_RESTART === 'true' && nodeEnv === 'development') || false; -/** - * If available, data will be loaded from this folder on initialise, if the database contains no Election objects. This will override mock data generation - */ -export const loadDataFolder = process.env.LOAD_DATA_ON_INITIALISE_FOLDER ?? ''; - export const mailFrom = process.env.MAIL_FROM ?? ''; export const mailFromName = process.env.MAIL_FROM_NAME ?? ''; export const mailReplyTo = process.env.MAIL_REPLY_TO ?? ''; diff --git a/backend/vaa-strapi/src/extensions/users-permissions/controllers/candidate.ts b/backend/vaa-strapi/src/extensions/users-permissions/controllers/candidate.ts index 9ac4b67cb..cb38aef0a 100644 --- a/backend/vaa-strapi/src/extensions/users-permissions/controllers/candidate.ts +++ b/backend/vaa-strapi/src/extensions/users-permissions/controllers/candidate.ts @@ -105,7 +105,7 @@ async function register(ctx: Context): Promise<{ type: 'success' }> { /** * Preregister a Candidate. - * @access Authorization: Bearer {BACKEND_API_TOKEN} + * Access: Authorization: Bearer {BACKEND_API_TOKEN} * @returns { success: true } */ async function preregister(ctx: Context): Promise<{ type: 'success' }> { diff --git a/backend/vaa-strapi/src/functions/generateMockData.ts b/backend/vaa-strapi/src/functions/generateMockData.ts index 09c316d7c..bb8fd9bc7 100644 --- a/backend/vaa-strapi/src/functions/generateMockData.ts +++ b/backend/vaa-strapi/src/functions/generateMockData.ts @@ -662,7 +662,7 @@ async function createAlliances(numParties: Array) { /** * Create questions - * @param options.electionPctg The fraction of opinion questions categories that will have their `election` relation set to a random election. + * @param options.electionPctg - The fraction of opinion questions categories that will have their `election` relation set to a random election. */ async function createQuestionCategories({ electionPctg = 0.2 }: { electionPctg?: number } = {}) { const elections = await strapi.documents('api::election.election').findMany({}); @@ -721,7 +721,7 @@ async function createQuestionTypes() { /** * Create questions - * @param options.constituencyPctg The fraction of Likert questions that will + * @param options.constituencyPctg - The fraction of Likert questions that will * have their `constituency` relation set to a random constituency. */ async function createQuestions({ constituencyPctg = 0.1 }: { constituencyPctg?: number } = {}) { @@ -903,8 +903,8 @@ function fakeTranslate>( /** * Uses faker to create a localized string json with the callback - * @param callback The function to call for each translation - * @param template Optional template object to which translations will only be added + * @param callback - The function to call for each translation + * @param template - Optional template object to which translations will only be added * if they don't already exist in it */ function fakeLocalized( @@ -916,8 +916,8 @@ function fakeLocalized( /** * Converts a localized string json to an abbreviation of its values - * @values The localized string json to translate - * @options Optional settings for abbreviation + * @param values - The localized string json to translate + * @param options - Optional settings for abbreviation */ function abbreviate(values: LocalizedString, options: AbbreviationOptions = { type: 'acronym' }): LocalizedString { return Object.fromEntries( diff --git a/backend/vaa-strapi/src/functions/loadData.ts b/backend/vaa-strapi/src/functions/loadData.ts deleted file mode 100644 index 719aa416e..000000000 --- a/backend/vaa-strapi/src/functions/loadData.ts +++ /dev/null @@ -1,277 +0,0 @@ -/** - * A quick-and-dirty tool for loading data into Strapi. - * NB. This should be refactored and packaged into a function that can be invoked from the Strapi admin UI instead of on restart. - */ - -import fs from 'fs'; -import mime from 'mime-types'; -import Path from 'path'; -import { deleteMedia, dropAllCollections, getAllMedia } from '../util/drop'; -import type { UID } from '@strapi/strapi'; - -/** - * Load data from `folder`, if `AppSettings` do not exist or their `allowOverwrite` property is true. Warning! This will delete all existing data, including media files. - * The data folder must contain separate json files for each data type: `answers.json`, `appSettings.json`, `candidates.json`, `constituencies.json`, `elections.json`, `infoQuestions.json`, `locales.json`, `nominations.json`, `opinionQuestions.json`, `parties.json`, `questionCategories.json`, `questionTypes.json`. - * Possible images should be placed in the same folder and referenced by a relative path. - * The data folder is set by the env variable `LOAD_DATA_ON_INITIALISE_FOLDER` by default. - * @param folder The folder to load data from - * @param force Load and clear data regardless of `AppSettings.allowOverwrite` - */ -export async function loadData(folder: string, force = false) { - console.warn('[loadData] This function has not been tested on Strapi 5 yet!'); - - console.info('[loadData] ##########################################\n[loadData] Starting data loading...'); - - folder = Path.resolve('.', folder); - console.info(`[loadData] - Path resolved to: ${folder}`); - - if (force) { - console.info('[loadData] - The force argument is true. Continuing with data loading...'); - } else { - console.info('[loadData] Getting current AppSettings...'); - const currentSettings = await strapi.documents('api::app-setting.app-setting').findMany(); - if (!currentSettings?.length) { - console.info('[loadData] - No AppSettings found. Continuing with data loading...'); - } else if (currentSettings.length > 1) { - console.info( - `[loadData] - Found more than one AppSettings (${currentSettings.length}). Please remove all but one. Data loading aborted.\n[loadData] ##########################################` - ); - return false; - } else if (!currentSettings[0].allowOverwrite) { - console.info( - '[loadData] - AppSettings.allowOverwrite is false. Data loading aborted.\n[loadData] ##########################################' - ); - return false; - } else { - console.info('[loadData] - AppSettings.allowOverwrite is true. Continuing with data loading...'); - } - } - - await strapi.db.transaction(async ({ rollback, commit }) => { - console.info('[loadData] Starting transaction...'); - - // Get a list of all media files in the media library which will be deleted at the end of the transaction - const mediaFiles = await getAllMedia(); - - try { - // Due to a bug in Strapi, we need to ensure this. See: https://github.com/strapi/strapi/issues/16071#issuecomment-1613334063 - console.info('[loadData] Ensuring we have an upload folder...'); - if (!(await strapi.plugin('upload').service('api-upload-folder').getAPIUploadFolder())) throw new Error(); - - console.info('[loadData] Warning! Deleting all existing collections...'); - await dropAllCollections(); - - // TODO: We probably don't need this anymore - console.info('[loadData] Creating Locales...'); - await createLocales((await loadFile(folder, 'locales')) as Array<{ name: string; code: string }>); - - console.info('[loadData] Creating App Settings...'); - if (!(await createFromFile(folder, 'appSettings', 'api::app-setting.app-setting'))) throw new Error(); - - console.info('[loadData] Creating App Customization...'); - if ( - !(await createFromFile(folder, 'appSettings', 'api::app-customization.app-customization', [ - 'candPoster', - 'candPosterDark', - 'publisherLogo', - 'publisherLogoDark', - 'poster', - 'posterDark' - ])) - ) - throw new Error(); - - console.info('[loadData] Creating Constituencies...'); - if (!(await createFromFile(folder, 'constituencies', 'api::constituency.constituency'))) throw new Error(); - - console.info('[loadData] Creating Constituency Groups...'); - if (!(await createFromFile(folder, 'constituencyGroups', 'api::constituency-group.constituency-group'))) - throw new Error(); - - console.info('[loadData] Creating Elections...'); - if (!(await createFromFile(folder, 'elections', 'api::election.election'))) throw new Error(); - - console.info('[loadData] Creating Question Categories...'); - if (!(await createFromFile(folder, 'questionCategories', 'api::question-category.question-category'))) - throw new Error(); - - console.info('[loadData] Creating Question Types...'); - if (!(await createFromFile(folder, 'questionTypes', 'api::question-type.question-type'))) throw new Error(); - - console.info('[loadData] Creating Info Questions...'); - if (!(await createFromFile(folder, 'infoQuestions', 'api::question.question'))) throw new Error(); - - console.info('[loadData] Creating Opinion Questions...'); - if (!(await createFromFile(folder, 'opinionQuestions', 'api::question.question'))) throw new Error(); - - console.info('[loadData] Creating Parties...'); - if (!(await createFromFile(folder, 'parties', 'api::party.party', ['image']))) throw new Error(); - - console.info('[loadData] Creating Candidates...'); - if (!(await createFromFile(folder, 'candidates', 'api::candidate.candidate', ['image']))) throw new Error(); - - console.info('[loadData] Creating Nominations...'); - if (!(await createFromFile(folder, 'nominations', 'api::nomination.nomination'))) throw new Error(); - - // console.info('[loadData] Creating Answers...'); - // if (!(await createFromFile(folder, 'answers', API.Answer))) throw new Error(); - } catch { - console.info('[loadData] - There was an error. Rolling back transaction...'); - await rollback(); - console.info('[loadData] Data loading aborted'); - return; - } - - console.info('[loadData] - Committing transaction...'); - commit(); - - console.info('[loadData] Warning! Deleting all media files that existed before loading data...'); - await deleteMedia(mediaFiles); - - console.info('[loadData] Data loading done!'); - }); - - console.info('[loadData] ##########################################'); -} - -//////////////////////////////////////////////////////////////////////// -// FUNCTIONS -//////////////////////////////////////////////////////////////////////// - -async function createLocales(locales: Array<{ code: string; name: string }>) { - const strapiLocales = await strapi.plugins.i18n.services.locales.find(); - for (const { code, name } of locales) { - if (strapiLocales.find((l) => l.code === code)) return; - await strapi.plugins.i18n.services.locales.create({ code, name }); - console.info(`[loadData] Created locale '${code}'`); - } -} - -/** - * Load a JSON file from disk and create Strapi objects from it. - * @param folder The import folder - * @param name The name of the file without extension - * @param api The api name - * @param mediaFields The names of the fields that contain media files. These are defined as paths (relative to the import folder) to the corresponding media file. - * @param publish If `true` the objects will be published with the current time - * @returns The created Strapi objects if succesfull - * @throws Never - */ -async function createFromFile( - folder: string, - name: string, - api: UID.ContentType, - mediaFields?: Array, - publish = true -): Promise> { - return new Promise((resolve) => { - loadFile(folder, name) - .catch(() => { - console.error(`[loadData] [createFromFile] Error creating '${api}' from file ${name} when reading file`); - resolve(); - }) - .then((data) => { - if (!data) { - resolve(undefined); - return; - } - create(folder, api, data as Array, mediaFields, publish) - .catch((e) => { - console.error( - `[loadData] [createFromFile] Error creating '${api}' from file ${name} when creating objects`, - e - ); - resolve(undefined); - }) - .then((result) => resolve(result)); - }); - }); -} - -/** - * Load a JSON file from disk. - * @param folder The import folder - * @param name The name of the file without extension - * @returns The JSON data - * @throws Error - */ -async function loadFile(folder: string, name: string): Promise> { - return new Promise((resolve) => { - const fp = Path.resolve(folder, `${name}.json`); - if (!fs.existsSync(fp)) { - console.error(`[loadData] [loadFile] No file such exists: ${fp}`); - resolve(undefined); - return; - } - try { - const data = fs.readFileSync(fp); - resolve(JSON.parse(data.toString()) as Array); - } catch (e) { - console.error(`[loadData] [loadFile] Error loading file: ${fp}`, e); - resolve(undefined); - } - }); -} - -/** - * Create Strapi objects from JSON data. - * @param folder The import folder - * @param api The api name - * @param data An array of JSON objects - * @param mediaFields The names of the fields that contain media files. These are defined as paths (relative to the import folder) to the corresponding media file. - * @param publish If `true` the objects will be published with the current time - * @returns The created objects - * @throws Error - */ -async function create( - folder, - api: UID.ContentType, - data: Array, - mediaFields?: Array, - publish = true -): Promise> { - const res = []; - for (const item of data) { - // Make a copy of item data for file upload purposes - const itemData = { ...item }; - const files = {} as Record; - if (mediaFields?.length) { - for (const key of mediaFields) { - if (itemData[key]) { - try { - const path = `${itemData[key]}`; - const name = Path.parse(path).name; - const fullPath = Path.resolve(folder, path); - const size = fs.statSync(fullPath).size; - const type = mime.lookup(fullPath); - files[key] = { name, path: fullPath, size, type }; - } catch (e) { - console.error(`[loadData] [create] Error reading media file '${itemData[key]}'`, e); - throw e; - } - } - delete itemData[key]; - } - } - // Create the object - const obj = await strapi - .documents(api) - .create({ - data: { ...itemData, publishedAt: publish ? new Date() : null } as object, - files - }) - .catch((e) => { - console.error(`[loadData] [create] Error creating '${api}'`, ...(e.details?.errors ?? []), itemData); - throw e; - }); - res.push(obj); - } - return res; -} - -interface FileUploadProps { - name: string; - path: string; - size: number; - type: string; -} diff --git a/backend/vaa-strapi/src/index.ts b/backend/vaa-strapi/src/index.ts index 0793cb4b9..96b1b34f5 100644 --- a/backend/vaa-strapi/src/index.ts +++ b/backend/vaa-strapi/src/index.ts @@ -1,6 +1,5 @@ -import { generateMockDataOnInitialise, generateMockDataOnRestart, loadDataFolder } from './constants'; +import { generateMockDataOnInitialise, generateMockDataOnRestart } from './constants'; import { generateMockData } from './functions/generateMockData'; -import { loadData } from './functions/loadData'; import { loadDefaultAppSettings } from './functions/loadDefaultAppSettings'; import { loadDefaultData } from './functions/loadDefaultData'; import { loadDynamicTranslations } from './functions/loadDynamicTranslations'; @@ -28,10 +27,7 @@ module.exports = { bootstrap(/*{ strapi }*/) { // 1. Default, mock or loaded data try { - // Due to ENV variable handling, we'll bypass some falsy values - if (loadDataFolder && !['""', "''", '-', 'false', 'FALSE', '0'].includes(loadDataFolder)) { - loadData(loadDataFolder); - } else if (generateMockDataOnInitialise || generateMockDataOnRestart) { + if (generateMockDataOnInitialise || generateMockDataOnRestart) { generateMockData(); } else { loadDefaultAppSettings(); diff --git a/backend/vaa-strapi/src/util/getUsersCandidate.ts b/backend/vaa-strapi/src/util/getUsersCandidate.ts index 0be5c83a2..e6de6e851 100644 --- a/backend/vaa-strapi/src/util/getUsersCandidate.ts +++ b/backend/vaa-strapi/src/util/getUsersCandidate.ts @@ -2,7 +2,7 @@ import type { Data } from '@strapi/strapi'; /** * Get the `Candidate` associated with a given `userId`. - * @param userId The `id` (not `documentId`) of the `User`. + * @param userId - The `id` (not `documentId`) of the `User`. * @returns The `Candidate` associated with the given `userId` or `null` if no `Candidate` is found. * @throws If multiple `Candidate`s are found for the same `User`. */ diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index e1b761bac..6c7b29f76 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -67,7 +67,6 @@ services: GENERATE_MOCK_DATA_ON_INITIALISE: ${GENERATE_MOCK_DATA_ON_INITIALISE} GENERATE_MOCK_DATA_ON_RESTART: ${GENERATE_MOCK_DATA_ON_RESTART} LLM_OPENAI_API_KEY: ${LLM_OPENAI_API_KEY} - LOAD_DATA_ON_INITIALISE_FOLDER: ${LOAD_DATA_ON_INITIALISE_FOLDER} AWS_SES_ACCESS_KEY_ID: ${AWS_SES_ACCESS_KEY_ID} AWS_SES_SECRET_ACCESS_KEY: ${AWS_SES_SECRET_ACCESS_KEY} AWS_SES_REGION: ${AWS_SES_REGION} @@ -96,4 +95,4 @@ volumes: strapi-uploads: awslocal: cache: - driver: local \ No newline at end of file + driver: local diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..bff793d5e --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,24 @@ +test-results +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/docs/.npmrc b/docs/.npmrc new file mode 100644 index 000000000..b6f27f135 --- /dev/null +++ b/docs/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/docs/.prettierignore b/docs/.prettierignore new file mode 100644 index 000000000..7d74fe246 --- /dev/null +++ b/docs/.prettierignore @@ -0,0 +1,9 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock +bun.lock +bun.lockb + +# Miscellaneous +/static/ diff --git a/docs/README.md b/docs/README.md index 708fc4191..518d8b809 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,200 +1,114 @@ -# Developer Guide - -Instructions for developers and contributors. - -## Table of Contents - -- [Quick start](quick-start.md) - Get up and running quickly with Docker -- [App and repo structure](app-and-repo-structure.md) - Overview of the monorepo structure -- [Development](development.md) - Development environment setup and workflows -- [Testing](testing.md) - Unit and E2E testing -- [Deployment](deployment.md) - Deploying the application to production -- [Contributing](contributing.md) - Guidelines for contributing to the project -- [Backend](backend.md) - Backend (Strapi) documentation -- [Frontend](frontend.md) - Frontend (SvelteKit) documentation -- [Localization](localization.md) - Internationalization and localization -- [App Settings](app-settings.md) - Application configuration -- [App Customization](app-customization.md) - Customizing the application -- [Candidate User Management](candidate-user-management.md) - Managing candidate users -- [LLM features](llm-features.md) - Experimental LLM features -- [Troubleshooting](troubleshooting.md) - Common issues and solutions -- [Code Review Checklist](code-review-checklist.md) - Checklist for code reviews - -## Full Table of Contents - -### [Quick start](quick-start.md) - -(No subsections) - -### [App and repo structure](app-and-repo-structure.md) - -(No subsections) - -### [Development](development.md) - -- [Requirements](development.md#requirements) -- [Running the Development Environment](development.md#running-the-development-environment) - - [Hot Reloading the Backend](development.md#hot-reloading-the-backend) -- [Monorepo](development.md#monorepo) - - [Module resolution](development.md#module-resolution) -- [Roadmap](development.md#roadmap) - -### [Testing](testing.md) - -- [Unit tests](testing.md#unit-tests) -- [E2E tests](testing.md#e2e-tests) - -### [Deployment](deployment.md) - -- [Costs](deployment.md#costs) -- [Setup with Render and AWS](deployment.md#setup-with-render-and-aws) - - [1. Fork](deployment.md#1-fork) - - [2. Configure AWS](deployment.md#2-configure-aws) - - [3. Create Render project](deployment.md#3-create-render-project) - - [4. Create Postgres database](deployment.md#4-create-postgres-database) - - [5. Create the Backend web service](deployment.md#5-create-the-backend-web-service) - - [6. Create the Frontend web service](deployment.md#6-create-the-frontend-web-service) - - [7. Create a Strapi Admin](deployment.md#7-create-a-strapi-admin) - - [8. Fill in further `env` variables](deployment.md#8-fill-in-further-env-variables) - - [9. Use your own domain name for the frontend](deployment.md#9-use-your-own-domain-name-for-the-frontend) -- [Manually Creating a Production Build](deployment.md#manually-creating-a-production-build) - - [Build from Dockerimage](deployment.md#build-from-dockerimage) - - [Building the frontend separately](deployment.md#building-the-frontend-separately) - - [Building the backend separately](deployment.md#building-the-backend-separately) - -### [Contributing](contributing.md) - -- [Recommended IDE settings (Code)](contributing.md#recommended-ide-settings-code) -- [AI agents](contributing.md#ai-agents) -- [Issues](contributing.md#issues) - - [Create a new issue](contributing.md#create-a-new-issue) - - [Search for an issue](contributing.md#search-for-an-issue) - - [Issue labels](contributing.md#issue-labels) -- [Contribute](contributing.md#contribute) - - [Commit your update](contributing.md#commit-your-update) -- [Workflows](contributing.md#workflows) -- [Pull Request](contributing.md#pull-request) -- [Your PR is ready to be merged!](contributing.md#your-pr-is-ready-to-be-merged) -- [Self-review](contributing.md#self-review) -- [Code style guide](contributing.md#code-style-guide) - - [Principles](contributing.md#principles) - - [Comments](contributing.md#comments) - - [TypeScript](contributing.md#typescript) - - [CSS](contributing.md#css) - - [Svelte components](contributing.md#svelte-components) - -### [Backend](backend.md) - -- [Preparing backend dependencies](backend.md#preparing-backend-dependencies) -- [Plugins](backend.md#plugins) -- [Running the backend separately](backend.md#running-the-backend-separately) -- [Re-generating types](backend.md#re-generating-types) -- [Customized behaviour](backend.md#customized-behaviour) -- [Default data loading](backend.md#default-data-loading) -- [Mock data generation](backend.md#mock-data-generation) - - [Mock users](backend.md#mock-users) -- [Authentication](backend.md#authentication) - - [Adding new content types](backend.md#adding-new-content-types) -- [Security](backend.md#security) - - [filter-by-candidate](backend.md#filter-by-candidate) - - [owned-by-candidate](backend.md#owned-by-candidate) - - [restrictPopulate](backend.md#restrictpopulate) - - [restrictFilters](backend.md#restrictfilters) - - [restrictFields](backend.md#restrictfields) - - [restrictBody](backend.md#restrictbody) - - [restrictResourceOwnedByCandidate](backend.md#restrictresourceownedbycandidate) - - [Preset](backend.md#preset) -- [OpenVAA admin tools plugin for Strapi](backend.md#openvaa-admin-tools-plugin-for-strapi) - - [Status: preliminary](backend.md#status-preliminary) - - [Installation](backend.md#installation) - - [Developing](backend.md#developing) - - [Usage](backend.md#usage) - - [Functions](backend.md#functions) - - [Access control](backend.md#access-control) - -### [Frontend](frontend.md) - -- [Components](frontend.md#components) - - [Dynamic and static components](frontend.md#dynamic-and-static-components) -- [Contexts](frontend.md#contexts) - - [Contexts vs global stores](frontend.md#contexts-vs-global-stores) - - [Example of Context Use](frontend.md#example-of-context-use) -- [Accessing data and state management](frontend.md#accessing-data-and-state-management) - - [Example: loading cascade for the `/(voter)/(located)/questions` route](frontend.md#example-loading-cascade-for-the--voter-located-questions-route) -- [Data API](frontend.md#data-api) - - [Cache](frontend.md#cache) - - [Folder structure](frontend.md#folder-structure) - - [Classes and interfaces](frontend.md#classes-and-interfaces) -- [Environmental variables](frontend.md#environmental-variables) -- [Routing](frontend.md#routing) - - [Building routes](frontend.md#building-routes) -- [Styling](frontend.md#styling) - - [Tailwind classes](frontend.md#tailwind-classes) - - [Colors](frontend.md#colors) - - [Z-index](frontend.md#z-index) - - [Default styling](frontend.md#default-styling) -- [Setting up the application for local development using Docker](frontend.md#setting-up-the-application-for-local-development-using-docker) - -### [Localization](localization.md) - -- [Localization in the frontend](localization.md#localization-in-the-frontend) - - [Value interpolation](localization.md#value-interpolation) - - [Localized default values in components](localization.md#localized-default-values-in-components) - - [Localized texts included in dynamically loaded data](localization.md#localized-texts-included-in-dynamically-loaded-data) -- [Storing multi-locale data](localization.md#storing-multi-locale-data) -- [Localization in Strapi](localization.md#localization-in-strapi) -- [Local translations](localization.md#local-translations) - - [The `TranslationKey` type](localization.md#the-translationkey-type) -- [Locale routes](localization.md#locale-routes) - - [The `getRoute` helper](localization.md#the-getroute-helper) -- [Locale selection step-by-step](localization.md#locale-selection-step-by-step) -- [Supported locales](localization.md#supported-locales) - - [Adding new locales](localization.md#adding-new-locales) - -### [App Settings](app-settings.md) - -- [Adding New Settings](app-settings.md#adding-new-settings) - -### [App Customization](app-customization.md) - -- [Adding New Customization Options](app-customization.md#adding-new-customization-options) - -### [Candidate User Management](candidate-user-management.md) - -- [Mock Data](candidate-user-management.md#mock-data) -- [Creating a New Candidate](candidate-user-management.md#creating-a-new-candidate) - - [Using Registration Key](candidate-user-management.md#using-registration-key) - - [Manually](candidate-user-management.md#manually) - - [Pre-Registration](candidate-user-management.md#pre-registration) -- [Resetting the Password](candidate-user-management.md#resetting-the-password) -- [Registration Process in Strapi](candidate-user-management.md#registration-process-in-strapi) - - [Email Templates](candidate-user-management.md#email-templates) - - [Templates](candidate-user-management.md#templates) -- [Password Validation](candidate-user-management.md#password-validation) - - [Frontend](candidate-user-management.md#frontend) - - [Backend](candidate-user-management.md#backend) - - [Password requirements](candidate-user-management.md#password-requirements) - -### [LLM features](llm-features.md) - -(No subsections) - -### [Troubleshooting](troubleshooting.md) - -- [Commit error: 'Husky not found'](troubleshooting.md#commit-error-husky-not-found) -- [Commit error: 'TypeError: Cannot read properties of undefined (reading 'font')' error when running `/generateTranslationKeyType.ts`](troubleshooting.md#commit-error-typeerror-cannot-read-properties-of-undefined-reading-font-error-when-running--generatetranslationkeytypets) -- [Docker error related to `frozen lockfile` when running `yarn dev`](troubleshooting.md#docker-error-related-to-frozen-lockfile-when-running-yarn-dev) -- [Docker error: Load metadata for docker.io/library/node:foo](troubleshooting.md#docker-error-load-metadata-for-dockerio-library-nodefoo) -- [Docker error: 'No space left on device' error](troubleshooting.md#docker-error-no-space-left-on-device-error) -- [Docker error: Service "foo" can't be used with `extends` as it declare `depends_on`](troubleshooting.md#docker-error-service-foo-cant-be-used-with-extends-as-it-declare-dependson) -- [Frontend: Candidate registration fails with 'Bad Request' error](troubleshooting.md#frontend-candidate-registration-fails-with-bad-request-error) -- [Frontend: Changes to the content in Strapi not updated in the frontend](troubleshooting.md#frontend-changes-to-the-content-in-strapi-not-updated-in-the-frontend) -- [Frontend: Server error when trying to access frontend](troubleshooting.md#frontend-server-error-when-trying-to-access-frontend) -- [Frontend: Strapi relations are not populated](troubleshooting.md#frontend-strapi-relations-are-not-populated) -- [Playwright: `TimeoutError` when locating elements and running the tests locally](troubleshooting.md#playwright-timeouterror-when-locating-elements-and-running-the-tests-locally) -- [Strapi: Content model is reset after restart](troubleshooting.md#strapi-content-model-is-reset-after-restart) -- [Strapi error: 'Relation already exists' error on restart after editing the content model](troubleshooting.md#strapi-error-relation-already-exists-error-on-restart-after-editing-the-content-model) - -### [Code Review Checklist](code-review-checklist.md) - -(No subsections) +# OpenVAA Documentation Site + +This directory contains the OpenVAA documentation website, which combines auto-generated API documentation with hand-written guides. + +## Overview + +The documentation system consists of: + +1. **Generated Documentation** (`/docs/generated/`) - Auto-generated from source code + - TypeDoc API references from TypeScript packages + - Component docs from Svelte `@component` docstrings + - Route map from SvelteKit routes + +2. **Documentation Site** (`/docs/`) - SvelteKit-based static site + - Hand-written guides and tutorials + - Automatic navigation + - CSS-based styling with dark mode support + - Serves generated documentation + +## Architecture + +``` +docs/ +├── scripts/ # Documentation generation scripts +│ ├── generate-docs.ts # Main orchestrator +│ ├── generate-component-docs.ts # Svelte component extraction +│ ├── generate-route-map.ts # Route map generation +│ ├── copy-generated.ts # Copy docs to static site +│ └── config/ +│ ├── typedoc.json # TypeDoc config for packages +│ └── typedoc.frontend.json # TypeDoc config for frontend +├── src/ +│ ├── routes/ +│ │ ├── +page.md # Home page +│ │ ├── +layout.svelte # Main layout with navigation +│ │ ├── guides/ # Hand-written guides +│ │ │ ├── quick-start/+page.md +│ │ │ ├── development/+page.md +│ │ │ └── architecture/+page.md +│ │ ├── api/ # API reference pages +│ │ │ ├── packages/+page.md +│ │ │ └── frontend/+page.md +│ │ ├── components/+page.md # Component docs +│ │ └── routes/+page.md # Route map +│ ├── lib/ +│ │ ├── layouts/ +│ │ │ └── MdLayout.svelte # Markdown layout +│ │ └── components/ # Doc-specific components +│ ├── app.css # Global styles +│ └── app.html # HTML template +├── generated/ # Auto-generated docs (gitignored) +├── static/ +│ └── generated/ # Copied generated docs +├── build/ # Built static site +├── package.json +├── svelte.config.js +└── vite.config.js +``` + +## Development + +### Local Development + +```bash +# Generate documentation from source +yarn generate:docs + +# Start the docs site +yarn dev +``` + +The site will be available at http://localhost:5173 + +### Building for Production + +```bash +# Generate documentation +yarn generate:docs + +# Build the docs site +yarn build + +# Preview the built site +yarn preview +``` + +## Deployment + +The documentation is automatically deployed to GitHub Pages via GitHub Actions. + +### GitHub Pages Setup + +1. **Enable GitHub Pages**: + - Go to repository Settings → Pages + - Source: GitHub Actions + - No branch selection needed + +2. **Workflow** (`.github/workflows/docs.yml`): + - Triggers on push to `main` branch + - Generates documentation from source + - Builds the SvelteKit site + - Deploys to GitHub Pages + +### Manual Deployment + +```bash +# Generate and build +yarn generate:docs +cd docs +yarn build + +# The static site is in docs/build/ +# Deploy these files to any static host +``` diff --git a/docs/app-and-repo-structure.md b/docs/app-and-repo-structure.md deleted file mode 100644 index 83a636125..000000000 --- a/docs/app-and-repo-structure.md +++ /dev/null @@ -1,20 +0,0 @@ -# App and repo structure - -The project is a monorepo and it consists of several yarn workspaces (each is a separate NPM module). See the READMEs in each for more information. - -- Abstract logic - - [`@openvaa/core`](/packages/core/) - - [`@openvaa/data`](/packages/data/) - - [`@openvaa/filters`](/packages/filters/) - - [`@openvaa/matching`](/packages/matching/) -- Application - - [`@openvaa/app-shared`](/packages/app-shared/) - - [`@openvaa/strapi`](/backend/vaa-strapi/) - - [`@openvaa/strapi-admin-tools`](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/) - - [`@openvaa/frontend`](/frontend/) -- Experimental LLM features - - [`@openvaa/argument-condensation`](/packages/argument-condensation/) - - [`@openvaa/llm`](/packages/llm/) - - [`@openvaa/question-info`](/packages/question-info/) -- Development - - [`@openvaa/shared-config`](/packages/shared-config/) diff --git a/docs/app-customization.md b/docs/app-customization.md deleted file mode 100644 index f3ec78b00..000000000 --- a/docs/app-customization.md +++ /dev/null @@ -1,15 +0,0 @@ -# App Customization - -App customization includes publisher logo, front page image, translations and frequently asked questions of the candidate app. App customization has been currently implemented only in Strapi. - -Translations from `dynamic.json` are loaded into Strapi if the app customization collection is empty. In addition to changing the dynamic translations, any other translation can be overridden. - -## Adding New Customization Options - -1. Add the new option to `AppCustomization` type in [global.d.ts](frontend/src/lib/types/global.d.ts). -2. Add the new option to the `App Customization` content type in Strapi. -3. If the new setting is a relation, media field or a component: - 1. Edit the populate restrictions for the [app-customization route](/backend/vaa-strapi/src/api/app-customization/routes/app-customization.ts). - 2. Add the necessary `populate` query params to the `getAppCustomization` method in [strapiDataProvider.ts](/frontend/src/lib/api/adapters/strapi/dataProvider/strapiDataProvider.ts). -4. Possibly edit how the data is preprocessed by the [app-customization route](/backend/vaa-strapi/src/api/app-customization/routes/app-customization.ts). -5. Update the Strapi data types for `StrapiAppCustomizationData` in [strapiData.type.ts](/frontend/src/lib/api/adapters/strapi/strapiData.type.ts) diff --git a/docs/app-settings.md b/docs/app-settings.md deleted file mode 100644 index de5bb382a..000000000 --- a/docs/app-settings.md +++ /dev/null @@ -1,31 +0,0 @@ -# App Settings - -App settings are located in [`@openvaa/app-shared`](/packages/app-shared/src/settings/) module. Settings are separated into static and dynamic settings. - -Static settings can be changed only by modifying [staticSettings.ts](/packages/app-shared/src/settings/staticSettings.ts). - -Dynamic settings can be changed by modifying [dynamicSettings.ts](/packages/app-shared/src/settings/dynamicSettings.ts). In addition, dynamic settings can also be changed in the backend. This has been currently implemented only in Strapi. Settings from `dynamicSettings.ts` are loaded into Strapi if the app settings collection is empty. - -Settings from `dynamicSettings.ts`, `staticSettings.ts` and from the DataProvider are merged together into [`settings` store](/frontend/src/lib/stores/stores.ts). Settings from `dynamicSettings.ts` are overwritten by dynamic settings from the DataProvider. Settings from `staticSettings.ts` are merged last to prevent overwriting them. - -## Adding New Settings - -In case of static settings: - -1. Add the type and documentation for the new setting to the `StaticSettings` type in [staticSettings.type.ts](/packages/app-shared/src/settings/staticSettings.type.ts). -2. Add the default value for the setting to [staticSettings.ts](/packages/app-shared/src/settings/staticSettings.ts). - -In case of dynamic settings: - -1. Add the type and documentation for the new setting to the `DynamicSettings` type in [dynamicSettings.type.ts](/packages/app-shared/src/settings/dynamicSettings.type.ts). -2. Add the default value for the setting to [dynamicSettings.ts](/packages/app-shared/src/settings/dynamicSettings.ts). -3. Edit the settings components in Strapi: - 1. If the new setting is a top-level one, create a new component for the setting and add it to the `App Settings` content-type. - 2. If the new setting is a subsetting of a top-level item, edit that setting. -4. Possibly update the [`app-settings` route controller](/backend/vaa-strapi/src/api/app-setting/controllers/app-setting.ts) or the utilities it uses, e.g., for [`cardContents`](/backend/vaa-strapi/src/functions/utils/appSettings.ts). -5. Add the necessary `populate` query params to the `getAppSettings` method in [strapiDataProvider.ts](/frontend/src/lib/api/adapters/strapi/dataProvider/strapiDataProvider.ts), because components need to be explicitly populated. Note that if the components have subcomponents, you need to explicitly populate all the way down. -6. If the data type for the setting does not match the one in the `DynamicSettings` type: - 1. Update the Strapi data types for `StrapiAppSettingsData` in [strapiData.type.ts](/frontend/src/lib/api/adapters/strapi/strapiData.type.ts). - 2. Edit the `getAppSettings` method in [strapiDataProvider.ts](/frontend/src/lib/api/adapters/strapi/dataProvider/strapiDataProvider.ts) so that it returns the setting in the correct format. - 3. Edit the [loadDefaultAppSettings](/backend/vaa-strapi/src/functions/loadDefaultAppSettings.ts) utility so that it converts the default settings to a format suitable for Strapi. -7. Repeat applicable steps for all other `DataProvider` implementations that support `getAppSettings`. diff --git a/docs/backend.md b/docs/backend.md deleted file mode 100644 index e372e3edd..000000000 --- a/docs/backend.md +++ /dev/null @@ -1,471 +0,0 @@ -# Backend - -> The current backend uses the Strapi headless CMS. It is scheduled to be migrated to Supabase in H1/2026. - -## Preparing backend dependencies - -The backend module depends on `@openvaa/app-shared` and you need to build it prior to using `@openvaa/strapi` directly (no need if you use it via Docker): - -```bash -yarn workspace @openvaa/app-shared install -yarn workspace @openvaa/app-shared build -``` - -## Plugins - -- Email uses AWS SES, see [Candidate App documentation](candidate-user-management.md#candidate-user-management) -- Upload uses AWS S3, see [plugins.ts](/backend/vaa-strapi/config/plugins.ts) -- [OpenVAA Strapi Admin Tools plugin](#openvaa-admin-tools-plugin-for-strapi) (local plugin) - -## Running the backend separately - -0. You should be running Strapi with the Node version specified under `engines` in the root [package.json](/package.json). Use of nvm is encouraged. **Additionally, you need Docker!** -1. Install dependencies by running `yarn install`. -2. Copy or rename the `.env.example` to `.env` before running any of the commands. -3. Run `docker compose -f docker-compose.dev.yml up postgres` to start Postgres container. -4. Run `yarn dev` or `yarn start` to run the Strapi server directly. - -## Re-generating types - -Run `yarn strapi ts:generate-types` to re-generate `types` folder. - -## Customized behaviour - -The Strapi backend has been customized in many ways to cater to VAA needs. The current implementation is split between direct edits to Strapi code and some functions implemented in the [OpenVAA Admin Tools plugin](#openvaa-admin-tools-plugin-for-strapi). Most of the customizations should be migrated to the plugin in the future. - -## Default data loading - -Some data is automatically loaded when Strapi is initialized. The data include: - -- [Question Types](/backend/vaa-strapi/src/functions/loadDefaultData.ts) -- [App Settings](/backend/vaa-strapi/src/functions/loadDefaultAppSettings.ts) -- [Translation overrides](/backend/vaa-strapi/src/functions/loadDynamicTranslations.ts) (under the `dynamic` key) - -API permissions are also set by defaul by [setDefaultApiPermissions.ts](/backend/vaa-strapi/src/functions/setDefaultApiPermissions.ts). - -> Note that some of the defaults are **not** loaded if mock data generations is enabled. - -## Mock data generation - -> NB! This feature must only be used for local development and testing (not on production) - -The database can be seeded with generated mock data using Faker.js. - -If enabled, the data is generated by the rather messy [generateMockData](/backend/vaa-strapi/src/functions/generateMockData.ts) function. See the implementation for details and constants you can edit in-code to control the amount of data. - -Mock data can be seeded only once on initialising the backend DB or on each restart of the Strapi backend. Mock data generation is controlled by these `.env` variables: - -```dotenv -# Set to true to enable mock data on an empty database -GENERATE_MOCK_DATA_ON_INITIALISE=true - -# Used only in development builds -GENERATE_MOCK_DATA_ON_RESTART=false -``` - -To enable mock data generation, set the `GENERATE_MOCK_DATA_ON_INITIALISE` variable as true. This will create mock data if the database is empty or give a warning if database is not empty and thus mock data could not be generated. - -You can also set `GENERATE_MOCK_DATA_ON_RESTART` as true. This will generate new mock data every time the Strapi instance is restarted. - -**Please keep in mind that setting this variable as true will clear the database contents of existing candidates, parties, elections, and so on and should only be used for debugging purposes.** -Setting `GENERATE_MOCK_DATA_ON_RESTART` as true will override `GENERATE_MOCK_DATA_ON_INITIALISE` setting. - -**Note:** you need to modify these variables in the relevant `.env` file (located either in the project's root directory or in `backend/vaa-strapi`) depending on how you choose to run the backend service locally. - -### Mock users - -By default, mock data includes the following Users (for up-to-date details, see [mockUsers](/backend/vaa-strapi/src/functions/mockData/mockUsers.json)): - -| User type | User role | Email | Password | Remarks | -| ----------- | ------------- | ------------------------------ | ------------ | ----------------------------- | -| Candidate 1 | Authenticated | `mock.candidate@openvaa.org` | `Password1!` | Use to test the Candidate App | -| Candidate 2 | Authenticated | `mock.candidate.2@openvaa.org` | `Password1!` | Use to test the Candidate App | -| Admin | Admin | `mock.admin@openvaa.org` | `Password1!` | Use to test the Admin App | - -Note that the mock admin is different from the default Strapi Admin, generated along with mock data. - -| User type | Username | Email | Password | Remarks | -| ------------ | -------- | ------------------------ | -------- | ---------------------- | -| Strapi Admin | `admin` | `mock.admin@openvaa.org` | `admin` | Use to login to Strapi | - -## Authentication - -Standard read calls require no authentication and are included in the default permissions, which are customized in the [Users’ permissions plugin](/src/extensions/users-permissions/strapi-server.ts). - -Furthermore, all API routes are configured - -Write calls require authentication: - -- For registered Candidates, this is handled by creating a user. Read more in the [Candidate App documentation](candidate-user-management.md#candidate-user-management). -- For pre-registration, an API token with the `users-permissions.candidate.preregister` priviledge is required, which must be saved in the `BACKEND_API_TOKEN` env variable. Read more on creating the token in the [Strapi documenation](https://docs.strapi.io/user-docs/settings/API-tokens#creating-a-new-api-token). - -### Adding new content types - -If you add new content types that should be accessible, make sure: - -1. Edit the `CONTENT_API` list in [api.ts](/backend/vaa-strapi/src/util/api.ts) to grant read rights to the public -2. Add the permission in the [Users’ permissions plugin](/backend/vaa-strapi/src/extensions/users-permissions/strapi-server.ts) so that registered users are granted access sa well -3. Also make sure that the route config includes the default restrictions: - -```ts -// /src/api//routes/.ts -export default factories.createCoreRouter('api::.', { - only: ['find', 'findOne'], // Explicitly disabled create, update, delete - config: { - find: { - policies: ['global::restrict-populate'] - }, - findOne: { - policies: ['global::restrict-populate'] - } - } -}); -``` - -## Security - -> NB. The Security chapter may be partly outdated. - -By default, all content types inside Strapi are assumed to be safe to be publicly exposed. For reading, all the fields except any fields marked with the `private` keyword in their schema definition are returned per how Strapi works. For writing, the majority of the create, update, and delete endpoints are disabled by default unless explicitly used by the candidate application, and otherwise restricted to only resources that belong to the logged in candidate to prevent unauthorized modification of the data. - -The restrictions are enforced using policies for each content type's route, usually found in the `backend/vaa-strapi/src/api/[schema]/routes/[schema].ts` file. To simplify this, there are a couple of helper functions available in [acl.ts](`/backend/vaa-strapi/src/util/acl.ts`) and [policies](`/backend/vaa-strapi/src/policies/`). Please see [Strapi's documentation on policies](https://docs.strapi.io/dev-docs/backend-customization/policies#usage) on how to use them. - -The default permissions unauthenticated and authenticated users are managed in the [strapi-server.js](/backend/vaa-strapi/src/extensions/users-permissions/strapi-server.js) file, in the `defaultPermissions` array. - -### filter-by-candidate - -Enforces that all the returned content belongs to the currently authenticated candidate by filtering the `candidate` field. This is primarily meant only for the find and findOne endpoints so that they only return resources belonging to the authenticated candidate in case the content type shouldn't be public for everyone. - -Example usage: - -```ts -export default factories.createCoreRouter('...', { - ... - config: { - find: { - policies: [ - 'global::filter-by-candidate', - ], - }, - }, -}); -``` - -### owned-by-candidate - -Enforces that the request applies the `candidate` field to the currently authenticated candidate. This is primarily meant only for the create and update endpoints so that the created and updated resources body will always have the candidate set to the authenticated candidate to prevent impersonation. - -Example usage: - -```ts -export default factories.createCoreRouter('...', { - ... - config: { - create: { - policies: [ - 'global::owned-by-candidate', - ], - }, - }, -}); -``` - -### restrictPopulate - -Enforces that only the allowed populate fields are set to prevent leaking content types from relationships. Strapi does not implement a convenient way to restrict populates, so this would need to be enforced for every content type that is able to eventually return the content type even if there is no direct relationship. For example, if there were the `shop -> pizza -> ingredient` relationship where `ingredient` should not be exposed to the public, it is still possible to use populate to get `ingredient` through the `shop` endpoint using the `shop -> pizza` and `pizza -> ingredient` relationship. - -Example usage: - -```ts -export default factories.createCoreRouter('...', { - ... - config: { - find: { - policies: [ - restrictPopulate([ - 'pizza', // ?populate[pizza]=true - 'pizza.populate.shop' // ?populate[pizza][populate][shop]=true - ]), - ], - }, - }, -}); -``` - -### restrictFilters - -Enforces that only the allowed filters are set to prevent leaking content types from relationships. This is useful in scenarios where a specific relationship should not be returned, which filter would allow querying for even though the value is not directly retrievable. For example, user would be able to filter relationship's value by checking the prefix, which would eventually give an oracle where if the character is correct, the content type is returned, or if it's wrong, the content type isn't returned. Repeating this would then allow recovering field values that they shouldn't be able to get. - -Example usage: - -```ts -export default factories.createCoreRouter('...', { - ... - config: { - find: { - policies: [ - restrictFilters([ - 'candidate.id.$eq', // ?filters[candidate][id][$eq]=1 - ]), - ], - }, - }, -}); -``` - -### restrictFields - -Enforces that only the allowed fields are returned from the content type. If no fields are explicitly provided (using the `?fields=...` syntax in the request), it will default to only providing the allowed fields. This is intended for all the request endpoints as they all return the content type the action is performed on. Note that you should use the `private` field in the content type schema first for increased security (making this redundant), but if that isn't possible then this is an alternative option. This also has same caveats as `restrictPopulate` where the fields will not apply to relationships returned, and the field that shouldn't be returned will still be returned through populate if not carefully restricted. - -Example usage: - -```ts -export default factories.createCoreRouter('...', { - ... - config: { - find: { - policies: [ - restrictFields(['id', 'name']), // will allow returning id, name, or a subset of those - ], - }, - }, -}); -``` - -### restrictBody - -Enforces that only the allowed fields are allowed in the body. This is primarily meant only for the create and update endpoints to prevent modifying fields that should not be modified in the content type. - -Example usage: - -```ts -export default factories.createCoreRouter('...', { - ... - config: { - update: { - policies: [ - restrictBody(['name']), // will only setting the name field in the body - ], - }, - }, -}); -``` - -### restrictResourceOwnedByCandidate - -Enforces that the accessed resource belongs to the currently authenticated user by verifying that the accessed resource has `candidate` relationship to the authenticated user. This is primarily meant only for the findOne, create, update, and delete endpoints to prevent modification to resources not owned by that candidate. Note that one needs to be careful and make sure that the currently authenticated user is unable to modify another user's resource and set their candidate field to be themselves before performing modifications. - -Example usage: - -```ts -export default factories.createCoreRouter('api::my-content', { - ... - config: { - update: { - policies: [ - restrictResourceOwnedByCandidate('api::my-content'), // the content type name is needed for the checks - ], - }, - }, -}); -``` - -### Preset - -Here is a default preset one can use for new content-type that aims to be secure by default: - -```ts -export default factories.createCoreRouter('...', { - only: ['find', 'findOne', 'create', 'update', 'delete'], - config: { - find: { - policies: [ - // Disable populate by default to avoid accidentally leaking data through relations - restrictPopulate([]), - // Disable filters by default to avoid accidentally leaking data of relations - restrictFilters([]) - ] - }, - findOne: { - policies: [ - // Disable populate by default to avoid accidentally leaking data through relations - restrictPopulate([]), - // Disable filters by default to avoid accidentally leaking data of relations - restrictFilters([]) - ] - }, - create: { - policies: [ - // Enforce ownership to always belong to the candidate - 'global::owned-by-candidate', - // Disable populate by default to avoid accidentally leaking data through relations - restrictPopulate([]), - // Disable filters by default to avoid accidentally leaking data of relations - restrictFilters([]) - ] - }, - update: { - policies: [ - // Allow only updating candidate's own resource - restrictResourceOwnedByCandidate('...'), - // Enforce ownership to always belong to the candidate - 'global::owned-by-candidate', - // Disable populate by default to avoid accidentally leaking data through relations - restrictPopulate([]), - // Disable filters by default to avoid accidentally leaking data of relations - restrictFilters([]) - ] - }, - delete: { - policies: [ - // Allow only deleting candidate's own resource - restrictResourceOwnedByCandidate('api::answer.answer'), - // Disable populate by default to avoid accidentally leaking data through relations - restrictPopulate([]), - // Disable filters by default to avoid accidentally leaking data of relations - restrictFilters([]) - ] - } - } -}); -``` - -Note that if you do not need the create, update, and delete endpoints, one should disable them by removing them from the `only` array. - -## OpenVAA admin tools plugin for Strapi - -> NB! The name of the plugin in Strapi is `openvaa-admin-tools`. - -The plugin combines Strapi admin functions needed by the OpenVAA voting advice application. - -### Status: preliminary - -Only some of the custom functions are currently contained in the plugin. Functions that should be migrated from the `@openvaa/strapi` include: - -- Mock data generation -- Loading default settings and customization -- Candidate registration -- Candidate preregistration - -### Installation - -The plugin is a local plugin and not currently published as an NPM package. - -It is enabled by default in `@openvaa/strapi`’s [plugin config](/backend/vaa-strapi/config/plugins.ts): - -```ts -export default ({ env }) => { - return { - // Other plugins - 'openvaa-admin-tools': { - enabled: true, - resolve: 'src/plugins/openvaa-admin-tools' - } - }; -}; -``` - -### Developing - -1. Enable [hot-reloading](#hot-reloading-the-backend) in Strapi. -2. Watch plugin source for edits by running `yarn workspace @openvaa/strapi-admin-tools watch`. - -> NB! Before merging it’s safest to also try the plugin in the production environment as well, because some (unstable) Strapi function may not work there. To do that set `services.strapi.build.target=production` in [docker-compose](/backend/vaa-strapi/docker-compose.dev.yml). - -### Usage - -The plugin functions are accessed via the Strapi Admin UI. There are currenlty two types of functions: - -1. Registration email functions which appear on the right-hand column in the Content manager Candidate list view and single Candidate view. -2. Data import and deletion which are accessed via the admin tool page, which is opened with the jigsaw icon in the main navigation. - -The services can also be invoked like any other plugins services with, e.g., `strapi.plugin('openvaa-admin-tools').service('data').import(data)`. - -### Functions - -#### Registration email - -##### Send the registration email to a single Candidate - -- UI: Content manager > Candidates > Edit view -- Component: [RegistrationEmailToOne](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/admin/src/components/RegistrationEmailToOne.tsx) -- Service: [`email.sendEmail`](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/email.ts) -- API: `/openvaa-admin-tools/send-email` - -Sends a registration link email with customizable content and subject to the Candidate. - -Throws if the content does not include the link placeholder. - -##### Send the registration email to all unregistered Candidates - -- UI: Content manager > Candidates > List view -- Component: [RegistrationEmailToAll](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/adminsrc/components/RegistrationEmailToAll.tsx) -- Service: [`email.sendEmailToUnregistered`](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/email.ts) -- API: `/openvaa-admin-tools//send-email-to-unregistered` -- Required permission: `plugin::openvaa-admin-tools.send-email` - -Sends a registration link email with customizable content and subject to all unregisterd Candidates. - -Throws if the content does not include the link placeholder. - -#### Import and delete data - -The `externalId` is a private field that all collection types have which is used to store a stable id which can be referenced instead of the `documentId` in the import and delete functions. - -##### Import any data in JSON format - -- UI: OpenVAA Admin Tools page -- Component: [ImportData](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/adminsrc/components/ImportData.tsx) -- Service: [`data.import`](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/data.ts) -- API: `/openvaa-admin-tools/import-data` -- Required permission: `plugin::openvaa-admin-tools.import-data` - -Import data into Strapi or update existing data based on `externalId` or `documentId` if provided. The data is supplied as `Array`s of `ImportData` objects collected by their collection name. - -See the instructions in the component for further info. - -##### Delete any data by its `externalId` - -- UI: OpenVAA Admin Tools page -- Component: [DeleteData](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/adminsrc/components/DeleteData.tsx) -- Service: [`data.delete`](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/data.ts) -- API: `/openvaa-admin-tools/delete-data` -- Required permission: `plugin::openvaa-admin-tools.import-data` - -Delete data based on `externalId`s. Objects whose `externalId`s (specified by collection) start with the provided, case-sensitive prefixes will be deleted. Mostly useful for deleting mock data which is generated with `externalId`s starting with `mock-`. - -See the instructions in the component for further info. - -##### Find any data using `filters` - -- UI: OpenVAA Admin Tools page -- Component: [FindData](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/adminsrc/components/FindData.tsx) -- Service: [`data.find`](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/data.ts) -- API: `/openvaa-admin-tools/find-data` -- Required permission: `plugin::openvaa-admin-tools.import-data` - -Find any data accessible by the data tools by providing arbitrary filters. You can also select the relations to populate. - -See the instructions in the component for further info. - -##### Send email to selected Candidates - -- UI: OpenVAA Admin Tools page -- Component: [SendEmail](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/adminsrc/components/SendEmail.tsx) -- Services: - - [`data.findCandidates`](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/data.ts) - - [`email.sendEmail`](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/email.ts) -- APIs: - - `/openvaa-admin-tools/find-candidates` - - `/openvaa-admin-tools/send-email` -- Required permissions: - - `plugin::openvaa-admin-tools.import-data` - - `plugin::openvaa-admin-tools.send-email` - -A tool that combines searching for Candidates to build an editable list to whom to send emails. - -See the instructions in the component for further info. - -### Access control - -In addition to the specified permissions, all routes require `admin::isAuthenticatedAdmin`, see [route policies](/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/routes/admin/index.ts). diff --git a/docs/candidate-user-management.md b/docs/candidate-user-management.md deleted file mode 100644 index 9fa53053e..000000000 --- a/docs/candidate-user-management.md +++ /dev/null @@ -1,195 +0,0 @@ -# Candidate User Management - -## Mock Data - -See [Mock users](#mock-users) for automatically generated users you can use to test the Candidate App. - -## Creating a New Candidate - -A candidate can log in to the candidate app either by having the admin give them a registration key, registering an account for them, or using pre-registration. - -### Using Registration Key - -To use the registration code, you need to navigate to the Strapi's admin area (http://localhost:1337), select the "Content Manager" tab, and choose the "Candidates" tab from the dropdown. If a candidate you want to log in as doesn't exist yet, you can use the "Create new entry" button to create one. - -Afterward, edit the desired candidate and choose a secure and random value for the registrationKey field. Then, save the change by clicking the "Save" button. The chosen registrationKey should be shared, and then the candidate can navigate to http://localhost:5173/candidate/register to create their account. - -### Manually - -To create the user manually, you need to navigate to the Strapi's admin area (http://localhost:1337), select the "Content Manager" tab, and choose the "User" tab from the dropdown. - -You can create a new user using the "Create new entry" button. The following fields should be set: - -- username (any desired username) -- email (any desired email, it'll be used for logging in) -- password (any strong password) -- confirmed => True -- blocked => False -- role => Authenticated -- candidate => the desired candidate the user is linked to - -Then, use the "Save" button and you should be able to log in as the candidate at http://localhost:5173/candidate. - -### Pre-Registration - -In the pre-registration process, anyone can use their bank identity to register as a candidate. When the Voter App is published, those registered users who can be matched to actual candidates in the election can be confirmed to be shown in the app. - -This requires a subscription to an identity service provider to work. It is enabled with the `preRegistration.enabled` static setting. - -The feature is partially supported. - -The documentation is TBA but one can read the notes from the two PRs in which was built: - -- [Initial PR 687](https://github.com/OpenVAA/voting-advice-application/pull/687) -- [Enhancement PR 703](https://github.com/OpenVAA/voting-advice-application/pull/703) - -## Resetting the Password - -The user gets an email with a link to reset their password using the forgot password functionality on the login page. The frontend URL in the emails is configured in `.env` with the PUBLIC_BROWSER_FRONTEND_URL variable, and the email service (AWS SES) can be configured using the following variables: - -- `AWS_SES_ACCESS_KEY_ID`: AWS SES user access key -- `AWS_SES_SECRET_ACCESS_KEY`: AWS SES user secret access key -- `AWS_SES_REGION`: AWS SES region -- `MAIL_FROM`: the email address the emails are sent from -- `MAIL_FROM_NAME`: the name of the sender -- `MAIL_REPLY_TO`: the email address replies should be sent to - -The emails are sent by `user-permissions` Strapi plugin and can be configured separately via Strapi UI in `Settings > Users & Permissions plugin > Email Templates`. - -You can use a local instance of AWS SES via [LocalStack](https://docs.localstack.cloud/user-guide/aws/ses/) for development. To enforce the use of LocalStack set `LOCALSTACK_ENDPOINT` to `http://localhost.localstack.cloud:4566` in `.env` file. You could use the project's Docker compose setup to spin up `awslocal` service or install and run it [yourself](https://docs.localstack.cloud/getting-started/installation/). The LocalStack's AWS SES mailbox can be checked at [http://localhost:4566/\_aws/ses](http://localhost:4566/_aws/ses), where you'll find any emails sent by Strapi. - -## Registration Process in Strapi - -The user registration process is primarily handled by the `users-permissions` plugin bundled by Strapi so that we can rely on Strapi's existing security mechanisms (rate-limiting and being battle-tested). The only exception is that the plugin is extended with registration code support (POST /api/auth/local/register endpoint) that is implemented in the [`candidate.ts`](/backend/vaa-strapi/src/extensions/users-permissions/controllers/candidate.ts) file. The user is also not logged in automatically by this endpoint to prevent bypassing 2FA in case we want to implement it in the future, in scenarios where the user already exists but is not explicitly linked to a candidate yet. - -The existing content-type of `User` is used to identify the users that can log in, but extended with the `candidate` field so a logged-in user could be associated to a specific candidate. Similarly, the candidate schema also has a belong-to relation back to the user if any exists. This makes it possible to rely on the logic provided by `users-permissions` plugin instead of implementing all the login logic manually. You can find the schema definition for user in the [`schema.json`](/backend/vaa-strapi/src/extensions/users-permissions/content-types/user/schema.json). - -See [password-validation.md](#password-validation) on additional information on how password validation is handled. - -For logging in and logging out, the frontend stores the session JWT token returned by Strapi in the local storage of the browser. The primary logic for this is handled in [`authenticationStore.ts`](/frontend/src/lib/utils/authenticationStore.ts). Log out is handled simply by resetting the state to being logged out and discarding the saved JWT token inside local storage. - -### Email Templates - -You can modify the default email templates in the [email-templates](/backend/vaa-strapi/config/email-templates) folder. - -#### Using Variables - -Strapi uses [EJS](https://ejs.co/) as its template engine, thus the email templates support the functionality EJS provides. - -Though the most important functionality you'll need is variables. They can be used by wrapping the variable with `<%=` and `%>`, e.g. `<%= URL %>` would be replaced with the URL variable's contents in the email when it gets sent. - -### Templates - -#### Reset Password - -The reset password (reset-password.html) template supports the following variables (https://github.com/strapi/strapi/blob/2a2faea1d49c0d84077f66a57b3b73021a4c3ba7/packages/plugins/users-permissions/server/controllers/auth.js#L230-L236): - -- URL: The public frontend URL to the password reset path (e.g. `http://localhost:5173/candidate/password-reset`) -- SERVER_URL: The Strapi's base URL -- ADMIN_URL: The Strapi's admin area URL -- USER: The user object the email is being sent to, specifically having the user's content-type properties that can be accessed like `USER.PROPERTY_NAME` (e.g. `USER.email`) -- TOKEN: The password reset token needed to reset the user's password - -## Password Validation - -### Frontend - -Setting the password is done in three components: - -- [`PasswordSetPage.svelte`](/frontend/src/routes/candidate/register/PasswordSetPage.svelte) (new user) -- [`PasswordResetPage.svelte`](/frontend/src/routes/candidate/password-reset/PasswordResetPage.svelte) (forgotten password) -- [`settings/+page.svelte`]() (update existing password) - -The password is validated using [`passwordValidation.ts`](/packages/app-shared/utils/passwordValidation.ts). - -The file provides two main functions for validating a password - -- `validatePassword` - Returns a boolean indicating whether the password is valid -- `validatePasswordDetails` - Returns both the password validity status boolean as well as details and state for each password requirement. These details are used for the validation UI. - -On all password set pages, a password validation UI is shown to the user. -This functionality is provided by the [`PasswordValidator.svelte`](/frontend/src/lib/components/passwordValidator/PasswordValidator.svelte) component. -The component provides real-time password validation checks and shows the state of each password requirement. -More details on how the different requirements are shown to the user can be found in the documentation of the component. - -The UI validation is done using debounced validation, i.e. the validation status is checked and updated once the user stops typing for a moment. - -The password validation UI provides a variable indicating the password validation status, which can be used to enable/disable the password submit button. This way frontend allows the password to be submitted only if it is valid. - -However, due to the use of debounced validation, the validity of the password is also checked by the password set page itself before sending the request to the backend. - -If the validity check passes, a POST request is sent to the backend to either - -- `api/auth/candidate/register` -- `api/auth/reset-password` - -### Backend - -- `/api/auth/candidate/register` endpoint is implemented by [`candidate.ts`](/backend/vaa-strapi/src/extensions/users-permissions/controllers/candidate.ts) using the registration key - - the backend also validates the password using the same `validatePassword` function -- `/api/auth/reset-password` is fully handled by Strapi's users-permissions plugin - -### Password requirements - -The password requirements are defined in the [`passwordValidation.ts`](/packages/app-shared/utils/passwordValidation.ts) file. - -Each requirement is defined by the `ValidationDetail` interface: - -```ts -export interface ValidationDetail { - status: boolean; - message: string; - negative?: boolean; - enforced?: boolean; -} -``` - -- status: - - indicates the state of the requirement, i.e. if it is valid -- message: - - describes the requirement - - shown to the user in the validation UI - - due to localization, the message string contains the key to the `i18n` localization file, where the actual texts are located -- negative: - - requirements are either positive or negative - - positive rules are the main requirements that are always enforced and must be met for the password to be valid - - negative rules are rules that are used to prevent bad password practises - - in the validation UI, negative rules are shown if they are violated -- enforced: - - negative rules can be either enforced or non-enforced - - enforced requirements need to be valid for a password to be valid - -Currently, all requirements are done without hardcoded lists of allowed characters and support languages that have separate uppercase and lowercase letters. Localization to other languages may need new validation rules. - -The file also contains help functions for checking different aspects of the password string. - -#### Changing and creating new requirements - -All validation rules are defined in the `passwordValidation` function. New requirements can be added and existing ones changed by modifying `ValidationDetail` objects of the result. - -The order of one group of requirements (positive, negative enforced, negative non-enforced) is the same order they are shown to the user in the validation UI. -Below are examples of both types of requirements that can be used as a guide to creating new requirements. If necessary, new help functions can be defined in the file to help with validating a specific aspect of the password. - -Example 1. -Positive requirement that checks that the length of the password is at least the required length - -```ts -length: { - status: password.length >= minPasswordLength, - message: 'candidateApp.passwordValidation.length' -} -``` - -Example 2. -Negative requirement that checks if the password contains repetition using the `checkRepetition` function. This function returns `true` if a string contains repetition and therefore the status of the requirement is the negative of that as the requirement passes if it does not contain repetition. -As the requirement is negative, it has the `negative` property set to true, and as the `enforced` property is missing, this negative requirement is not enforced. - -```ts -repetition: { - status: !checkRepetition(password), - message: 'candidateApp.passwordValidation.repetition', - negative: true -} -``` diff --git a/docs/contributing.md b/docs/contributing.md deleted file mode 100644 index 8b11f6531..000000000 --- a/docs/contributing.md +++ /dev/null @@ -1,390 +0,0 @@ -# Contributing - -## Recommended IDE settings (Code) - -Plugins: - -- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) -- [Pretter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) -- [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) -- [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - -We also recommend Git Graph for easier branch management: - -- [Git Graph v3](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - -Settings: - -```json -"editor.defaultFormatter": "esbenp.prettier-vscode", -"editor.formatOnSave": true, -"editor.codeActionsOnSave": { - "source.fixAll": "explicit", -}, -``` - -## AI agents - -Agent information is currently only provided for Claude in [CLAUDE.md](../CLAUDE.md), which can be used as a base for other agents’ instructions. - -There are Github workflows for code review, issue solving and generic tasks for Claude. These are triggered by commenting on PRs or issues with `@claude, review`, `@claude, solve` or `@claude [generic prompt]`. The author of the issue or comment must be a repository member with write access. - -## Issues - -### Create a new issue - -If you detect a problem with the application or have a new request, search first in the list of issues to see whether or -not a similar issue has already been reported by someone else. If the issue doesn't exist, you can open a new one by -following these steps. - -1. Add a descriptive title. -2. Add a descriptive description. -3. Assign the issue to the Voting Advice Application project. -4. [Add labels](#issue-labels). - -### Search for an issue - -Scan through our [existing issues](https://github.com/OpenVAA/voting-advice-application/issues). You can -narrow down the search using `labels` as filters. See [Labels](#issue-labels) for more information. -If you find an issue to work on, you are welcome to open a PR with a fix. - -### Issue labels - -Labels can help you find an issue you'd like to help with. On the other hand, labels allow us to manage the project and categorize the information in a more meaningful way. - -Currently, the labels divide into four categories: - -- Category: [CATEGORY] => This category lets us know what category the issue belongs to. Fox example, if the issue contains only security related changes, the label will be `category: security`. -- Status: [STATE of ISSUE] => This category lets us know what state the issue is in right now. -- Type: [TYPE OF ISSUE] => This category lets us know what type of issue this is. For example, if the issue is a bug, the label will be `type: bug`, or if the issue is a new feature, the label will be `type: feature`. -- Scope: [SCOPE] => This category describes the scope of this issue. For example, suppose that the issue involves the voter side of the app. Then the appropriate label would be `scope: voter`. - -Do not add more than one label from each category in each issue or pull request. - -It is imperative to add labels to the new issues. If you are not sure which labels to add, you can always ask for help from the team. - -Additionally, please use Milestones to indicate which release target and scope contribution belongs to (for example: Alpha version, Beta version, Post-launch bug fixing patch, etc.). - -## Contribute - -If you want to make changes to the project, you must follow the following steps. - -1. Clone the repository -2. Create a new branch with a descriptive yet short name. For example, `fix-404-page` or `add-privacy-policy-page`. -3. Once you start adding changes, make sure you split your work into small, meaningful, manageable commits. - -### Commit your update - -Commit the changes once you are happy with them. Try to keep commits small and to not mix unrelated changes in one commit. - -Don't add any editor config files, such as the `.vscode` folder, to your commit. These are not included in the project's `.gitignore` file but you can [add them to a global `.gitignore`](https://blog.martinhujer.cz/dont-put-idea-vscode-directories-to-projects-gitignore/) on your own machine. - -The commit message should follow the [conventional commits conventions](https://www.conventionalcommits.org/en/v1.0.0/). Use the `refactor:` prefix for changes that only affect styling. - -For commits that affect packages other than the frontend, add the package name (without the `@openvaa/` scope) to the commit prefix in brackets, e.g.: - -- `refactor[data]: doo foo` -- `refactor[q-info]: doo bar` (you can use the abbreviations `q-info` and `arg-cond` for `question-info` and `argument-condensation`) - -On top of that, the commit message should follow the following rules: - -- Commit messages must have a subject line and may have a body. A blank line must separate the subject line and body. -- If possible, the subject line must not exceed 50 characters -- The subject line must not end in a period -- The body copy must be wrapped at 72 columns -- The body copy must only explain _what_ and _why_, never _how_. The latter belongs in documentation and implementation. - -After you're satisfied with your commits, clean up the commit history so that the commits make sense for others. The best way to accomplish this is to use the [fixup workflow](https://dev.to/koffeinfrei/the-git-fixup-workflow-386d) so that the commit history will contain only one commit for some feature instead of multiple ones with cumulative fixes, i.e., your PR’s commit history should finally look like: - -- `feat: NewComponent` - -Instead of: - -- `feat: NewComponent` -- `fix: something in NewComponent` -- `fix: something else in NewComponent` - -Once your changes are ready, make sure you have followed all the steps in the [PR Review Checklist](#self-review). - -## Workflows - -The project uses GitHub Actions among other things to verify each commit passes unit tests, is able to build the app successfully and adheres to the [coding conventions used by the project](#code-style-guide). If a commit fails the verification, please check your changes from the logs and fix changes before submitting a review request. - -## Pull Request - -When you're done with the changes, create a pull request known as a PR. - -- Make sure that your commits pass the validation workflows, are able to run the [tests](testing.md#testing), and build the application. -- Make sure you have followed all the steps in the [PR Review Checklist](#self-review). -- Fill in the pull requested template. Mark your PR as a draft if you're still working on it. -- Don't forget to [link PR to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one. -- When you're satisfied with the PR, mark it as ready for review, and a team member will review it. The team may ask questions or request changes to your PR. Either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. -- As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations). -- While the review process is ongoing, do not force push changes to the branch but make the changes in new [fixup](https://dev.to/koffeinfrei/the-git-fixup-workflow-386d) commits. Only when the PR is otherwise approved, squash the commits and `push --force-with-lease`. - -## Your PR is ready to be merged! - -Once all the changes have been approved, the reviewers may still ask you to clean the git history before merging the changes into the main branch of the project. - -## Self-review - -You should always review your own PR first before asking someone to review it. Below you can find a checklist of things you should check before submitting your PR. - -- [ ] Confirm that the changes solve the issues the PR is trying to solve partially or fully. -- [ ] Review the code in terms of the [OWASP top 10 security issues](https://owasp.org/Top10/). -- [ ] Verify that the code follows the [Code style guide](contributing.md#code-style-guide). -- [ ] Avoid using `any` at all costs. If there is no way to circumvent using it, document the reason carefully and consider using `@ts-expect-error` instead. -- [ ] There is no code that is repeated within the PR or elsewhere in the repo. -- [ ] All new components, functions and other entities are documented -- [ ] The repo documentation markdown files are updated if the changes touch upon those. -- [ ] If the change adds functions available to the user, tracking events are enabled with new ones defined if needed. -- [ ] Any new Svelte components that have been created, follow the [Svelte component guidelines](contributing.md#svelte-components). -- [ ] Errors are handled properly and logged in the code. -- [ ] Run the unit tests successfully. -- [ ] Run the e2e tests successfully. -- [ ] Troubleshoot any failing checks in the PR. -- [ ] Test the change thoroughly on your own device, including parts that may have been affected via shared code. -- [ ] Check that parts of the application that share dependencies with the PR but are not included in it are not unduly affected. -- [ ] Test the changes using the [WAVE extension](https://wave.webaim.org/extension/) for accessibility. -- [ ] The changes pass the [WGAC A and AA requirements for accessibility](https://usability.yale.edu/web-accessibility/articles/wcag2-checklist). -- [ ] Test the changes using keyboard navigation and screen-reading. -- [ ] Documentation is added wherever necessary. -- [ ] The commit history is clean and linear, and the commits follow the [commit guidelines](contributing.md#commit-your-update) - -## Code style guide - -In general, Prettier formats the code on the surface in a nice way, but there are other requirements that you must take care of manually. - -### Principles - -#### Don't Repeat Yourself - -Check that no code is repeated if the same functionality is already implemented elsewhere. Be careful to check at least the following paths and modules for possible general use components, classes and utilities: - -- `$lib/api` -- `$lib/components` -- `$lib/dynamic-components` -- `$lib/contexts` -- `$lib/utils` -- `@openvaa/app-shared` -- `@openvaa/core` -- `@openvaa/data` -- `@openvaa/filters` -- `@openvaa/matching` - -If some existing code doesn’t do exactly the thing you want, consider extending the existing code instead of copy-pasting more than a few lines of code. - -#### Top-Down Organization - -Check that files with hierarchical functions are organized top-down such that the main (exported) function is first and its subfunctions come after. Preferably even split the functions into their own files and import them into the main function. - -Thus, a complex file should read like this (or with `foo` and `bar` imported from their own files): - -```ts -export function longProcess() { - const result = foo(); - if (!result) return undefined; - return bar(result); -} - -function foo() { - // Code here -} - -function bar(result) { - // Code here -} -``` - -### Comments - -Add comments to all exported variables as well as the properties of exported object, methods of classes and items of union types. - -Try to make the code understandable by itself, but if you suspect the program logic might be unclear to others, rather add comments than leave them out. - -Do not manually break comments into lines of a certain length unless separating paragraphs. This enables developers to use line-wrapping based on their own preference without adding unneccessary lines to the code. - -#### TSDoc - -In Typescript, use [TSDoc comments](https://tsdoc.org/) for all documentation unless you're only adding remarks concering the program flow, e.g.: - -```ts -/** - * Sum the inputs. - * @param a - The first addend. - * @param b - The second addend. - * @returns the sum of the addends. - */ -export function sum(a: number, b: number): number { - // Add the numbers together - return a + b; -} -``` - -### TypeScript - -We follow the conventions of [TypeScript Style Guide](https://mkosir.github.io/typescript-style-guide/) with the following exception: - -- [The naming conventions](https://mkosir.github.io/typescript-style-guide/#variables-1) for booleans are optional but if possible should be adhered to. - -Common errors, which will be flagged, include: - -- `Array` must be used instead of `Foo[]` -- Type parameters cannot be single letters: `type Foo = ...` instead of `type Foo`. - -#### `any` and `unknown` - -Avoid using `any` at all costs. If there is no way to circumvent using it, document the reason carefully and consider using `@ts-expect-error` instead. - -Also avoid `unknown` unless it is genuinely appropriate for the context like, e.g., in callback functions whose return values have no effect on the caller. - -#### Function parameters - -> This requirement is not flagged by automatic checks. - -To avoid bugs, try to always use named parameters to functions and methods, when there is any risk of confusion, i.e., in most cases where the functions expects more than one parameter. - -```ts -// NOT like this -function confused(foo: string, bar: string, baz = 'BAZ') { - // Do smth -} -// YES like this -function unConfused({ foo, bar, baz = 'BAZ' }: { foo: string; bar: string; baz?: string }) { - // Do smth -} -``` - -To make things smooth, try to use the same names for parameter across the board, so they can be destructured and passed as is, e.g. - -```typescript -const { foo } = getFoo(); -const { bar } = getBar(); -foobar({ foo, bar }); // Instead of foobar({ foo: foo, bar: bar }) -function foobar({ foo, bar }: { foo: string; bar: string }) { - // Do smthx -} -``` - -#### File organization - -Try to separate pure type files from the functional ones and keep them next to each other, as well as tests. Do not usually collect these into separate folders. E.g. - -- `foo.ts`: The file to compile -- `foo.type.ts`: Related types and types only -- `foo.test.ts`: The unit tests - -### CSS - -Use Tailwind for styling. - -See the [frontend styling guide](frontend.md#frontend) for information about using Tailwind classes. - -### Svelte components - -> The frontend currently uses Svelte 4. An update to Svelte 5 is scheduled for H1/2026. - -#### File structure - -Put each component in its own folder in `$lib/components`, or `$lib/dynamic-components` in case of [dynamic components](#dynamic-and-static-components). Multiple components that are integrally tied together may be included in the same folder (but see note below on exports). Separate the type definitions in a `.type.ts` file and provide an `index.ts` for easy imports. Thus, the `$lib/components/myComponent` folder would have the files: - -- `MyComponent.svelte`: the component itself -- `MyComponent.type.ts`: the type definitions for the component's properties -- `index.ts`: provides shortcuts to imports: - ```ts - export {default as MyComponent} from './MyComponent.svelte; - export * from './MyComponent.type; - ``` - -**NB.** All components exported from the `index.ts` file, will be loaded even when only one of them imported in the application, so place multiple components in the same folder tree only when it's absolutely necessary. - -#### Component properties - -Currently, most components use attribute forwarding with [Svelte's `$$restProps` variable](https://svelte.dev/docs/basic-markup#attributes-and-props). This means that any HTML or SVG attributes that the main element of the component accepts can be passed as the components properties – or, in case of a component derived from another Svelte component, the parent components properties. This is most commonly used for passing extra classes to the element. - -For example, in the `HeroEmoji` component additional CSS classes as well as any arbitraty properties of the `
` element can be passed to the `
` surrounding the emoji. - -```ts -// HeroEmoji.type.ts -import type { SvelteHTMLElements } from 'svelte/elements'; -export type HeroEmojiProps = SvelteHTMLElements['div'] & { - /** - * The emoji to use. Note that all non-emoji characters will be removed. If `undefined` the component will not be rendered at all. @default `undefined` - */ - emoji?: string; -}; -``` - -```tsx -// HeroEmoji.svelte - - -{#if emoji} - -{/if} -``` - -Also see the other existing components for more details on how this is done. - -##### Default values for properties included `$$restProps` - -In most cases, default values for properties included in `$$restProps`, such as `aria-hidden` can be just added as attributes in the relevant element or component. The only thing to keep in mind is that they must precede `$$restProps`, otherwise they will override the values in it. For example: - -```tsx -
- ... -
-``` - -However, if you want to concatenate values with properties in `$$restProps`, such as concatenating a default `class` string with one possibly defined in `$$restProps`, this should be added after `{...$$restProps}`. To make this easier, a `concatClass` helper function is provided in [`$lib/utils/components`](/frontend/src/lib/utils/components.ts). For example: - -```tsx -
...
-``` - -##### Aria attributes and the `class` attribute - -Note that you most Aria attributes cannot be exposed with `let export foo` because their names contain dashes, which also applies to the HTML `class` attribute. In order to access these, either use the `$restProps` object or specify them in the properties type the component uses, i.e., the one assigned to `type $$Props` and access them via `$$props`. For example: - -```tsx -// Foo.type.ts -export type FooProps = SvelteHTMLElements['p'] & { - 'aria-roledescription'?: string | null; - class?: string | null; -}; - -// Foo.svelte: + +
— {@render children()}
diff --git a/docs/src/lib/components/Footer.svelte b/docs/src/lib/components/Footer.svelte new file mode 100644 index 000000000..c841627ca --- /dev/null +++ b/docs/src/lib/components/Footer.svelte @@ -0,0 +1,23 @@ + + +
+
    +
  • + {#each navigation as section} + {@const firstChild = getFirstChild(section)} + {#if firstChild} +
  • + + {section.title} + +
  • + {/if} + {/each} +
  • +
+
diff --git a/docs/src/lib/components/GithubIcon.svelte b/docs/src/lib/components/GithubIcon.svelte new file mode 100644 index 000000000..f28cbe06b --- /dev/null +++ b/docs/src/lib/components/GithubIcon.svelte @@ -0,0 +1,13 @@ + + + + + GitHub + diff --git a/docs/src/lib/components/Header.svelte b/docs/src/lib/components/Header.svelte new file mode 100644 index 000000000..c5a0972e4 --- /dev/null +++ b/docs/src/lib/components/Header.svelte @@ -0,0 +1,65 @@ + + +
+ +
+ + diff --git a/docs/src/lib/components/Navigation.svelte b/docs/src/lib/components/Navigation.svelte new file mode 100644 index 000000000..6c5ffe8ef --- /dev/null +++ b/docs/src/lib/components/Navigation.svelte @@ -0,0 +1,56 @@ + + +{#if activeSection} + +{/if} + + diff --git a/docs/src/lib/components/PeerNavigation.svelte b/docs/src/lib/components/PeerNavigation.svelte new file mode 100644 index 000000000..101334b30 --- /dev/null +++ b/docs/src/lib/components/PeerNavigation.svelte @@ -0,0 +1,70 @@ + + +{#if peerNav.prev || peerNav.next} + +{/if} + + diff --git a/docs/src/lib/components/ReferenceList.svelte b/docs/src/lib/components/ReferenceList.svelte new file mode 100644 index 000000000..1102649f7 --- /dev/null +++ b/docs/src/lib/components/ReferenceList.svelte @@ -0,0 +1,18 @@ + + +{#if references.length} +
+
References
+
    + {#each references as reference} +
  • {reference}
  • + {/each} +
+
+{/if} diff --git a/docs/src/lib/components/ResearchQuote.svelte b/docs/src/lib/components/ResearchQuote.svelte new file mode 100644 index 000000000..e01b0f6bf --- /dev/null +++ b/docs/src/lib/components/ResearchQuote.svelte @@ -0,0 +1,86 @@ + + +{#snippet hgroup()} +
+

🔬 Research perspective

+

{title}

+
+{/snippet} + +
+
+ {#if collapsible} + + {:else} + {@render hgroup()} + {/if} + + {@render children()} + + {#if author} + {author} + {/if} + + {#if references.length} + + {/if} +
+
diff --git a/docs/src/lib/components/TableOfContents.svelte b/docs/src/lib/components/TableOfContents.svelte new file mode 100644 index 000000000..4ac9f8a31 --- /dev/null +++ b/docs/src/lib/components/TableOfContents.svelte @@ -0,0 +1,188 @@ + + +{#if filteredHeadings.length > 0} + +{/if} + + diff --git a/docs/src/lib/components/openVAALogo/OpenVAALogo.svelte b/docs/src/lib/components/openVAALogo/OpenVAALogo.svelte new file mode 100644 index 000000000..3af034dbb --- /dev/null +++ b/docs/src/lib/components/openVAALogo/OpenVAALogo.svelte @@ -0,0 +1,87 @@ + + + + + + {#if title} + {title} + {/if} + + + + + + + + + diff --git a/docs/src/lib/components/openVAALogo/OpenVAALogo.type.ts b/docs/src/lib/components/openVAALogo/OpenVAALogo.type.ts new file mode 100644 index 000000000..3e515baf8 --- /dev/null +++ b/docs/src/lib/components/openVAALogo/OpenVAALogo.type.ts @@ -0,0 +1,25 @@ +import type { SvelteHTMLElements } from 'svelte/elements'; + +export type OpenVAALogoProps = SvelteHTMLElements['svg'] & { + /** + * The `` of the SVG logo. Functions much the same way as the `alt`` + * attribute of an `<img>`. + * + * @default 'OpenVAA' + */ + title?: string; + + /** + * The size of the logo as one of the predefined sizes 'sm', 'md' or 'lg'. + * + * @default 'md' + */ + size?: 'xs' | 'sm' | 'md' | 'lg' | null; + + /** + * The color of the logo as one of the predefined colours. + * + * @default 'neutral' + */ + color?: 'primary' | 'secondary' | 'neutral' | null; +}; diff --git a/docs/src/lib/components/openVAALogo/index.ts b/docs/src/lib/components/openVAALogo/index.ts new file mode 100644 index 000000000..dcc4db4fb --- /dev/null +++ b/docs/src/lib/components/openVAALogo/index.ts @@ -0,0 +1,2 @@ +export { default as OpenVAALogo } from './OpenVAALogo.svelte'; +export * from './OpenVAALogo.type'; diff --git a/docs/src/lib/consts.ts b/docs/src/lib/consts.ts new file mode 100644 index 000000000..11d135fbd --- /dev/null +++ b/docs/src/lib/consts.ts @@ -0,0 +1 @@ +export const OPENVAA_REPO_URL = 'https://github.com/OpenVAA/voting-advice-application'; diff --git a/docs/src/lib/layouts/MdLayout.svelte b/docs/src/lib/layouts/MdLayout.svelte new file mode 100644 index 000000000..b1d3d1ccd --- /dev/null +++ b/docs/src/lib/layouts/MdLayout.svelte @@ -0,0 +1,32 @@ +<script lang="ts"> + import TableOfContents from '$lib/components/TableOfContents.svelte'; + import type { Snippet } from 'svelte'; + import PeerNavigation from '$lib/components/PeerNavigation.svelte'; + import './prism-vs.css'; + + interface Props { + children: Snippet; + } + + let { children }: Props = $props(); +</script> + +<div class="col-start-1 col-end-3 grid-cols-subgrid md:grid"> + <aside class="toc-sidebar col-start-2 row-span-full hidden p-lg ps-0 md:p-xl lg:block"> + <TableOfContents contentId="prose-content" maxLevel={3} /> + </aside> + <article + id="prose-content" + class="col-span-full col-start-1 max-w-[60rem] overflow-x-auto p-lg md:p-xl lg:col-span-1"> + {@render children()} + </article> + <div class="col-span-full p-lg md:p-xl lg:col-span-1"> + <PeerNavigation /> + </div> +</div> + +<style> + .toc-sidebar { + position: relative; + } +</style> diff --git a/docs/src/lib/layouts/prism-vs.css b/docs/src/lib/layouts/prism-vs.css new file mode 100644 index 000000000..5f7bdfa2b --- /dev/null +++ b/docs/src/lib/layouts/prism-vs.css @@ -0,0 +1,173 @@ +/** + * VS theme by Andrew Lock (https://andrewlock.net) + * Inspired by Visual Studio syntax coloring + * EDIT: Commented out colours which are set in main layout.css + */ + +code[class*='language-'], +pre[class*='language-'] { + /* color: #393a34; + font-family: 'Consolas', 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; */ + direction: ltr; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + font-size: 0.9em; + line-height: 1.2em; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre > code[class*='language-'] { + font-size: 1em; +} + +pre[class*='language-']::-moz-selection, +pre[class*='language-'] ::-moz-selection, +code[class*='language-']::-moz-selection, +code[class*='language-'] ::-moz-selection { + background: #c1def1; +} + +pre[class*='language-']::selection, +pre[class*='language-'] ::selection, +code[class*='language-']::selection, +code[class*='language-'] ::selection { + background: #c1def1; +} + +/* Code blocks */ +pre[class*='language-'] { + padding: 1em; + margin: 0.5em 0; + overflow: auto; + /* border: 1px solid #dddddd; + background-color: white; */ +} + +/* Inline code */ +:not(pre) > code[class*='language-'] { + padding: 0.2em; + padding-top: 1px; + padding-bottom: 1px; + /* background: #f8f8f8; + border: 1px solid #dddddd; */ +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #008000; + font-style: italic; +} + +.token.namespace { + opacity: 0.7; +} + +.token.string { + color: #a31515; +} + +.token.punctuation, +.token.operator { + color: #393a34; /* no highlight */ +} + +.token.url, +.token.symbol, +.token.number, +.token.boolean, +.token.variable, +.token.constant, +.token.inserted { + color: #36acaa; +} + +.token.atrule, +.token.keyword, +.token.attr-value, +.language-autohotkey .token.selector, +.language-json .token.boolean, +.language-json .token.number, +code[class*='language-css'] { + color: #0000ff; +} + +.token.function { + color: #393a34; +} + +.token.deleted, +.language-autohotkey .token.tag { + color: #9a050f; +} + +.token.selector, +.language-autohotkey .token.keyword { + color: #00009f; +} + +.token.important { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.class-name, +.language-json .token.property { + color: #2b91af; +} + +.token.tag, +.token.selector { + color: #800000; +} + +.token.attr-name, +.token.property, +.token.regex, +.token.entity { + color: #ff0000; +} + +.token.directive.tag .tag { + background: #ffff00; + color: #393a34; +} + +/* overrides color-values for the Line Numbers plugin + * http://prismjs.com/plugins/line-numbers/ + */ +.line-numbers.line-numbers .line-numbers-rows { + border-right-color: #a5a5a5; +} + +.line-numbers .line-numbers-rows > span:before { + color: #2b91af; +} + +/* overrides color-values for the Line Highlight plugin +* http://prismjs.com/plugins/line-highlight/ +*/ +.line-highlight.line-highlight { + background: rgba(193, 222, 241, 0.2); + background: -webkit-linear-gradient(left, rgba(193, 222, 241, 0.2) 70%, rgba(221, 222, 241, 0)); + background: linear-gradient(to right, rgba(193, 222, 241, 0.2) 70%, rgba(221, 222, 241, 0)); +} diff --git a/docs/src/lib/navigation.config.ts b/docs/src/lib/navigation.config.ts new file mode 100644 index 000000000..35a7a830f --- /dev/null +++ b/docs/src/lib/navigation.config.ts @@ -0,0 +1,471 @@ +/** + * Automatically updated navigation configuration + * Check and prepare update with scripts/generate-navigation-config.ts + * To mark fix sections titles, set `fixedTitle: true` for them, otherwise the titles are updated automatically. + * + * NB. Any comments or content other than the navigation object will be removed! + */ + +import type { Navigation } from './navigation.type'; + +export const navigation: Navigation = [ + { + title: 'About', + route: '/about', + children: [ + { + title: 'About OpenVAA', + route: '/about/intro' + }, + { + title: 'Features', + route: '/about/features' + }, + { + title: 'OpenVAA Association', + route: '/about/association' + }, + { + title: 'OpenVAA ry Association rules', + route: '/about/rules' + }, + { + title: 'The Initial Project', + route: '/about/project', + fixedTitle: true + }, + { + title: 'Newsletter', + route: '/about/newsletter' + }, + { + title: 'Roadmap', + route: '/about/roadmap' + } + ] + }, + { + title: 'Developers’ Guide', + route: '/developers-guide', + fixedTitle: true, + children: [ + { + title: 'Quick start', + route: '/developers-guide/quick-start' + }, + { + title: 'App and repo structure', + route: '/developers-guide/app-and-repo-structure' + }, + { + title: 'Configuration', + route: '/developers-guide/configuration', + children: [ + { + title: 'Configuration', + route: '/developers-guide/configuration/intro' + }, + { + title: 'Environmental variables', + route: '/developers-guide/configuration/environmental-variables' + }, + { + title: 'Static settings', + route: '/developers-guide/configuration/static-settings' + }, + { + title: 'App Settings', + route: '/developers-guide/configuration/app-settings' + }, + { + title: 'App Customization', + route: '/developers-guide/configuration/app-customization' + } + ] + }, + { + title: 'Development', + route: '/developers-guide/development', + children: [ + { + title: 'Docker', + route: '/developers-guide/development/intro' + }, + { + title: 'Requirements', + route: '/developers-guide/development/requirements' + }, + { + title: 'Monorepo', + route: '/developers-guide/development/monorepo' + }, + { + title: 'Running the Development Environment', + route: '/developers-guide/development/running-the-development-environment' + }, + { + title: 'Testing', + route: '/developers-guide/development/testing' + } + ] + }, + { + title: 'Deployment', + route: '/developers-guide/deployment' + }, + { + title: 'Contributing', + route: '/developers-guide/contributing', + children: [ + { + title: 'Contribute', + route: '/developers-guide/contributing/contribute' + }, + { + title: 'Issues', + route: '/developers-guide/contributing/issues' + }, + { + title: 'Pull Request', + route: '/developers-guide/contributing/pull-request' + }, + { + title: 'Recommended IDE settings (Code)', + route: '/developers-guide/contributing/recommended-ide-settings-code' + }, + { + title: 'Code style guide', + route: '/developers-guide/contributing/code-style-guide' + }, + { + title: 'Workflows', + route: '/developers-guide/contributing/workflows' + }, + { + title: 'AI agents', + route: '/developers-guide/contributing/ai-agents' + } + ] + }, + { + title: 'Backend', + route: '/developers-guide/backend', + children: [ + { + title: 'Strapi', + route: '/developers-guide/backend/intro' + }, + { + title: 'Customized behaviour', + route: '/developers-guide/backend/customized-behaviour' + }, + { + title: 'Plugins', + route: '/developers-guide/backend/plugins' + }, + { + title: 'OpenVAA admin tools plugin for Strapi', + route: '/developers-guide/backend/openvaa-admin-tools-plugin-for-strapi' + }, + { + title: 'Default data loading', + route: '/developers-guide/backend/default-data-loading' + }, + { + title: 'Mock data generation', + route: '/developers-guide/backend/mock-data-generation' + }, + { + title: 'Authentication', + route: '/developers-guide/backend/authentication' + }, + { + title: 'Security', + route: '/developers-guide/backend/security' + }, + { + title: 'Preparing backend dependencies', + route: '/developers-guide/backend/preparing-backend-dependencies' + }, + { + title: 'Running the backend separately', + route: '/developers-guide/backend/running-the-backend-separately' + }, + { + title: 'Re-generating types', + route: '/developers-guide/backend/re-generating-types' + } + ] + }, + { + title: 'Frontend', + route: '/developers-guide/frontend', + children: [ + { + title: 'Frontend', + route: '/developers-guide/frontend/intro' + }, + { + title: 'Components', + route: '/developers-guide/frontend/components' + }, + { + title: 'Routing', + route: '/developers-guide/frontend/routing' + }, + { + title: 'Accessing data and state management', + route: '/developers-guide/frontend/accessing-data-and-state-management' + }, + { + title: 'Data API', + route: '/developers-guide/frontend/data-api' + }, + { + title: 'Contexts', + route: '/developers-guide/frontend/contexts' + }, + { + title: 'Environmental variables', + route: '/developers-guide/frontend/environmental-variables' + }, + { + title: 'Styling', + route: '/developers-guide/frontend/styling' + } + ] + }, + { + title: 'Localization', + route: '/developers-guide/localization', + children: [ + { + title: 'Localization', + route: '/developers-guide/localization/intro' + }, + { + title: 'Local translations', + route: '/developers-guide/localization/local-translations' + }, + { + title: 'Locale routes', + route: '/developers-guide/localization/locale-routes' + }, + { + title: 'Locale selection step-by-step', + route: '/developers-guide/localization/locale-selection-step-by-step' + }, + { + title: 'Localization in Strapi', + route: '/developers-guide/localization/localization-in-strapi' + }, + { + title: 'Localization in the frontend', + route: '/developers-guide/localization/localization-in-the-frontend' + }, + { + title: 'Storing multi-locale data', + route: '/developers-guide/localization/storing-multi-locale-data' + }, + { + title: 'Supported locales', + route: '/developers-guide/localization/supported-locales' + } + ] + }, + { + title: 'Candidate user management', + route: '/developers-guide/candidate-user-management', + children: [ + { + title: 'Creating a New Candidate', + route: '/developers-guide/candidate-user-management/creating-a-new-candidate' + }, + { + title: 'Mock Data', + route: '/developers-guide/candidate-user-management/mock-data' + }, + { + title: 'Password Validation', + route: '/developers-guide/candidate-user-management/password-validation' + }, + { + title: 'Registration Process in Strapi', + route: '/developers-guide/candidate-user-management/registration-process-in-strapi' + }, + { + title: 'Resetting the Password', + route: '/developers-guide/candidate-user-management/resetting-the-password' + } + ] + }, + { + title: 'LLM features', + route: '/developers-guide/llm-features' + }, + { + title: 'Automatic Documentation Generation', + route: '/developers-guide/auto-documentation' + }, + { + title: 'Troubleshooting', + route: '/developers-guide/troubleshooting' + } + ] + }, + { + title: 'Publishers’ Guide', + route: '/publishers-guide', + fixedTitle: true, + children: [ + { + title: 'Introduction', + route: '/publishers-guide/intro' + }, + { + title: 'What are VAAs?', + route: '/publishers-guide/what-are-vaas', + fixedTitle: true, + children: [ + { + title: 'What are VAAs and why are they useful?', + route: '/publishers-guide/what-are-vaas/intro' + }, + { + title: 'Which kind of elections are VAAs used in?', + route: '/publishers-guide/what-are-vaas/vaas-used' + } + ] + }, + { + title: 'How to use OpenVAA?', + route: '/publishers-guide/publish-with-openvaa', + fixedTitle: true + }, + { + title: 'Preparing for publishing a VAA', + route: '/publishers-guide/preparing', + fixedTitle: true, + children: [ + { + title: 'Designing a VAA', + route: '/publishers-guide/preparing/intro' + }, + { + title: 'Timeline', + route: '/publishers-guide/preparing/timeline' + }, + { + title: 'Who is the target group of the VAA?', + route: '/publishers-guide/preparing/who-is-the-target-group' + }, + { + title: 'Which languages will the VAA be published in?', + route: '/publishers-guide/preparing/languages-will-the-vaa-be' + }, + { + title: 'What are the specifics of the elections?', + route: '/publishers-guide/preparing/the-specifics-of-the-elections' + }, + { + title: 'How should candidates’ and parties’ data be collected?', + route: '/publishers-guide/preparing/candidates-and-parties-data-be' + }, + { + title: 'What are the statements or questions posed?', + route: '/publishers-guide/preparing/the-statements-or-questions-posed' + }, + { + title: 'What other information is collected from candidates and parties?', + route: '/publishers-guide/preparing/what-other-information-is-collected' + }, + { + title: 'How should the recommendations be computed?', + route: '/publishers-guide/preparing/matching' + }, + { + title: 'What should the voter see when using the VAA?', + route: '/publishers-guide/preparing/the-voter-see-when-using' + }, + { + title: 'What should the VAA look and feel like?', + route: '/publishers-guide/preparing/the-vaa-look-and-feel' + }, + { + title: 'How should the application be hosted?', + route: '/publishers-guide/preparing/the-application-be-hosted' + }, + { + title: 'What data should be collected from voters’ use of the VAA?', + route: '/publishers-guide/preparing/what-data-should-be-collected' + }, + { + title: 'Do you want to ask voters to give feedback about the VAA?', + route: '/publishers-guide/preparing/to-ask-voters-to-give' + }, + { + title: 'Do you want to offer a survey for voters to fill?', + route: '/publishers-guide/preparing/to-offer-a-survey-for' + } + ] + }, + { + title: 'Data collection', + route: '/publishers-guide/data-collection', + children: [ + { + title: 'Steps for collecting data', + route: '/publishers-guide/data-collection/intro' + }, + { + title: 'Initial data', + route: '/publishers-guide/data-collection/initial-data' + }, + { + title: 'Candidates’ or parties answers', + route: '/publishers-guide/data-collection/candidates-or-parties-answers' + }, + { + title: 'Additional data for the voter application', + route: '/publishers-guide/data-collection/additional-data-for-the-voter' + }, + { + title: 'Data from final election lists', + route: '/publishers-guide/data-collection/data-from-final-election-lists' + }, + { + title: 'Moderation of candidate answers', + route: '/publishers-guide/data-collection/moderation-of-candidate-answers' + } + ] + }, + { + title: 'After publishing', + route: '/publishers-guide/after-publishing-the-vaa', + fixedTitle: true, + children: [ + { + title: 'After publishing the VAA', + route: '/publishers-guide/after-publishing-the-vaa/intro' + }, + { + title: 'User support', + route: '/publishers-guide/after-publishing-the-vaa/user-support' + }, + { + title: 'Marketing', + route: '/publishers-guide/after-publishing-the-vaa/marketing' + } + ] + }, + { + title: 'Application settings and features', + route: '/publishers-guide/app-settings' + }, + { + title: 'Other information sources', + route: '/publishers-guide/other-information-sources' + } + ] + } +]; diff --git a/docs/src/lib/navigation.type.ts b/docs/src/lib/navigation.type.ts new file mode 100644 index 000000000..1c41df142 --- /dev/null +++ b/docs/src/lib/navigation.type.ts @@ -0,0 +1,34 @@ +export type Navigation = Array<NavigationSection>; + +export type NavigationSection = TitleOptions & { + /** + * The route path segment that this section matches. Not used as a link, only used to check if the section is active. + */ + route: string; + /** + * The child items of this section. + */ + children: Array<NavigationSection | NavigationItem>; +}; + +export type NavigationItem = TitleOptions & { + /** + * The route path to this item. Also used to check if the item is active. + */ + route: string; + /** + * Whether this is a secondary item (shown differently in navigation). + */ + isSecondary?: boolean; +}; + +export type TitleOptions = { + /** + * The title of the item to show in navigation. + */ + title: string; + /** + * If set to true, the title won't be updated automatically based on the content when the navigation is generated. + */ + fixedTitle?: boolean; +}; diff --git a/docs/src/lib/screenshots.ts b/docs/src/lib/screenshots.ts new file mode 100644 index 000000000..8ee7ed1fb --- /dev/null +++ b/docs/src/lib/screenshots.ts @@ -0,0 +1,106 @@ +export type Screenshot = { + filename: string; + caption: string; + vaa: string; +}; +export const SCREENSHOT_BASE_PATH = '/images/screenshots/'; +export const SCREENSHOTS: Array<Screenshot> = [ + { + filename: 'nuortenvaalikone.fi-videoversio-ilman-kannykkaa-1.png', + caption: 'Support for video content in introductory views.', + vaa: '2024 EU Elections in Finland by Nuorisoala (Youth Sector)' + }, + { + filename: 'eu-elections-2024-vaa-screenshot-1.png', + caption: 'Customizable opening view.', + vaa: '2024 EU Elections in Finland by OpenVAA' + }, + { + filename: 'nuorisoala-finnish-local-elections-2025-vaa-1.png', + caption: 'Fully fledged app for candidates for entering their stances and profiles.', + vaa: '2025 Finnish Local Elections by Nuorisoala (Youth Sector)' + }, + { + filename: 'luxembourg-vaa-eu-elections-2024-1.png', + caption: + 'Optional real-time top matches while answering statements and optional statement weighting with configurable options.', + vaa: 'Research VAA for 2024 EU Elections in Luxembourg by PLDP' + }, + { + filename: 'nuorisoala-finnish-local-elections-2025-vaa-educational-mode-1.png', + caption: 'Optional educational mode with configurable tasks for users: task introduction.', + vaa: 'Educational mode of 2025 Finnish Local Elections by Nuorisoala (Youth Sector)' + }, + { + filename: 'nuortenvaalikone.fi-videoversio-ilman-kannykkaa-7.png', + caption: 'Optional display of top candidates withing party results view.', + vaa: '2024 EU Elections in Finland by Nuorisoala (Youth Sector)' + }, + { + filename: 'eu-elections-2024-vaa-screenshot-8.png', + caption: 'Configurable contents for results cards, such as category matches and featured answers.', + vaa: '2024 EU Elections in Finland by OpenVAA' + }, + { + filename: 'nuortenvaalikone.fi-videoversio-ilman-kannykkaa-4.png', + caption: 'Support for video content for statement introductions.', + vaa: '2024 EU Elections in Finland by Nuorisoala (Youth Sector)' + }, + { + filename: 'eu-elections-2024-vaa-screenshot-3.png', + caption: 'Optional statement category selection.', + vaa: '2024 EU Elections in Finland by OpenVAA' + }, + { + filename: 'nuorisoala-finnish-local-elections-2025-vaa-2.png', + caption: + 'Fully fledged app for candidates for entering their stances and profiles with support for optional, multilingual explanations for stances.', + vaa: '2025 Finnish Local Elections by Nuorisoala (Youth Sector)' + }, + { + filename: 'eu-elections-2024-vaa-screenshot-6.png', + caption: 'Expandable background information for statements.', + vaa: '2024 EU Elections in Finland by OpenVAA' + }, + { + filename: 'nuorisoala-finnish-local-elections-2025-vaa-educational-mode-2.png', + caption: 'Optional educational mode with configurable tasks for users: results view.', + vaa: 'Educational mode of 2025 Finnish Local Elections by Nuorisoala (Youth Sector)' + }, + { + filename: 'eu-elections-2024-vaa-screenshot-7.png', + caption: 'Configurable results tabs for candidates and parties.', + vaa: '2024 EU Elections in Finland by OpenVAA' + }, + { + filename: 'nuortenvaalikone.fi-videoversio-ilman-kannykkaa-5.png', + caption: 'Seamless switching between video and text content.', + vaa: '2024 EU Elections in Finland by Nuorisoala (Youth Sector)' + }, + { + filename: 'eu-elections-2024-vaa-screenshot-4.png', + caption: 'Optional statement category introductions.', + vaa: '2024 EU Elections in Finland by OpenVAA' + }, + { + filename: 'nuortenvaalikone.fi-videoversio-ilman-kannykkaa-9.png', + caption: 'Comparison of candidate or party stances to those of the voter and optional explanations for stances.', + vaa: '2024 EU Elections in Finland by Nuorisoala (Youth Sector)' + }, + { + filename: 'eu-elections-2024-vaa-screenshot-10.png', + caption: + 'Configurable tabs in candidate or party profiles for basic information, stances and candidates for parties.', + vaa: '2024 EU Elections in Finland by OpenVAA' + }, + { + filename: 'nuortenvaalikone.fi-videoversio-ilman-kannykkaa-6.png', + caption: 'Configurable content for results introduction: video or text.', + vaa: '2024 EU Elections in Finland by Nuorisoala (Youth Sector)' + }, + { + filename: 'nuortenvaalikone.fi-videoversio-ilman-kannykkaa-10.png', + caption: 'Optional prompts for participating in surveys.', + vaa: '2024 EU Elections in Finland by Nuorisoala (Youth Sector)' + } +]; diff --git a/docs/src/lib/utils/navigation.ts b/docs/src/lib/utils/navigation.ts new file mode 100644 index 000000000..41454107e --- /dev/null +++ b/docs/src/lib/utils/navigation.ts @@ -0,0 +1,75 @@ +import { navigation } from '../navigation.config'; +import type { NavigationItem, NavigationSection } from '$lib/navigation.type'; + +export function findActiveSection(url: URL): NavigationSection | null { + for (const section of navigation) { + if (url.pathname.startsWith(section.route)) { + return section; + } + } + return null; +} + +export function isActive(route: string, url: URL): boolean { + return url.pathname === route || url.pathname.startsWith(route + '/'); +} + +export function hasChildren(item: NavigationSection | NavigationItem): item is NavigationSection { + return 'children' in item && item.children.length > 0; +} + +/** + * Flattens the navigation tree into a list of all navigable items (leaf nodes only) + */ +function flattenNavigation(items: Array<NavigationSection | NavigationItem>): Array<NavigationItem> { + const result: Array<NavigationItem> = []; + + for (const item of items) { + if (hasChildren(item)) { + // Recursively flatten children + result.push(...flattenNavigation(item.children)); + } else { + // This is a leaf node (NavigationItem) + result.push(item); + } + } + + return result; +} + +/** + * Returns the previous and next navigation items relative to the current URL + */ +export function getPeerNavigation(url: URL): { + prev: NavigationItem | null; + next: NavigationItem | null; +} { + const activeSection = findActiveSection(url); + if (!activeSection) { + return { prev: null, next: null }; + } + + // Flatten all items in the active section + const allItems = flattenNavigation(activeSection.children); + + // Find the current item index + const currentIndex = allItems.findIndex((item) => isActive(item.route, url)); + + if (currentIndex === -1) { + return { prev: null, next: null }; + } + + return { + prev: currentIndex > 0 ? allItems[currentIndex - 1] : null, + next: currentIndex < allItems.length - 1 ? allItems[currentIndex + 1] : null + }; +} + +/** + * Get the first child route of a navigation section for displaying a working link to a page. + */ +export function getFirstChild(section: NavigationSection) { + if (section.children.length === 0) return null; + const firstChild = section.children[0]; + return 'children' in firstChild ? firstChild.route : firstChild.route; +} diff --git a/docs/src/routes/(content)/+layout.svelte b/docs/src/routes/(content)/+layout.svelte new file mode 100644 index 000000000..63463aa82 --- /dev/null +++ b/docs/src/routes/(content)/+layout.svelte @@ -0,0 +1,15 @@ +<script lang="ts"> + import Navigation from '$lib/components/Navigation.svelte'; + + let { children } = $props(); +</script> + +<div class="flex min-h-[calc(100vh-var(--spacing-headerHeight))] w-full max-w-full grid-cols-[20rem_1fr_20rem] md:grid"> + <div class="row-span-full hidden md:grid"> + <Navigation /> + </div> + + <main class="col-span-full col-start-2 prose grid-cols-subgrid md:grid"> + {@render children()} + </main> +</div> diff --git a/docs/src/routes/(content)/about/association/+page.md b/docs/src/routes/(content)/about/association/+page.md new file mode 100644 index 000000000..c7c5dcd58 --- /dev/null +++ b/docs/src/routes/(content)/about/association/+page.md @@ -0,0 +1,29 @@ +# OpenVAA Association + +OpenVAA ry is a non-profit association incorporated in Finland. It's purpose is to develop open digital tools for democracy with a special emphasis on VAAs or voting advice applications. + +Between 2023 and 2025 our focus was on building an open-source platform for VAAs. [The project](/about/project), called likewise OpenVAA, was graciously funded by [Sitra – the Finnish innovation fund](https://www.sitra.fi/en/). + +The project was accompanied by a research project called **YouthVAA** at the Helsinki University, which studied how VAAs are used and produced a VAA targeted at young, first-time voters. The research project was funded by [Kone Foundation](https://koneensaatio.fi/en/). + +### Contact + +President<br> +Kalle Järvenpää<br> ++358 50 357 2769<br> +kalle.jarvenpaa@openvaa.org + +[Association rules](/about/rules) (in Finnish) + +| Contacts | | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| Name | OpenVAA ry | +| Legal Status | Registered association (ry) | +| Business ID | [3313750-5](https://tietopalvelu.ytj.fi/yritystiedot.aspx?yavain=3075212&tarkiste=505C9E3BD81FF8631AAF4880405C20F9EDB17FA0) | +| Email | [info@openvaa.org](mailto:info@openvaa.org) | +| Phone | <a href="tel:+35850357 2769">+358 50 357 2769</a> (Kalle Järvenpää) | +| Address | ℅ Järvenpää, Kasarmikatu 12 A 3, 00130 Helsinki | +| Bank Account | FI32 5780 3820 1984 70 • OKOYFIHH | +|  E-invoicing Address | 003733137505 • OKOYFIHH | +| Board | Kalle Järvenpää, puheenjohtaja<br>Veikko Isotalo, Vice President<br>Theodora Helimäki, secretary<br>Jan-Erik (Janne) Berg, member | +| Auditors | Tuuli Tomperi<br>Eero Ekebom, Deputy Auditor | diff --git a/docs/features/README.md b/docs/src/routes/(content)/about/features/+page.md similarity index 79% rename from docs/features/README.md rename to docs/src/routes/(content)/about/features/+page.md index 2f2cd4440..aac8e7a16 100644 --- a/docs/features/README.md +++ b/docs/src/routes/(content)/about/features/+page.md @@ -2,7 +2,9 @@ A non-exhaustive list of the application features. -## Localization +> See also [Application settings and features](/publishers-guide/app-settings). + +### Localization - All texts used in the application are localizable - All text properties of the data are localizable @@ -10,18 +12,19 @@ A non-exhaustive list of the application features. - Translations can be easily overriden term by term - Automatic serving of requested locale to users if available - Currently available in + - Danish - English - Finnish - Swedish -## Accessibility +### Accessibility - WGAC 2.1 AA compliant - Automatic color contrast tuning for user-specified colors - Mobile first design - Light and dark mode enabled -## Customization +### Customization - Fonts and colors - Logos @@ -32,16 +35,16 @@ A non-exhaustive list of the application features. - Numerous other settings which can be edited in real time via the backend - Futher customization available by editing the source code -## Data Input +### Data Input - Using Strapi Admin UI - Using a json import tool -- Using a custom Admin UI (TBA) +- Using a custom Admin UI (to be added in [`0.2 Akita`](/about/roadmap)) - Support for a local version without Strapi - Data provided as flat json files - Does not support the Candidate App -## Elections and Constituencies +### Elections and Constituencies - Multiple simultaneous elections - Users may be given the option to select which elections they want to get recommendations for or all can be automatically included @@ -50,16 +53,16 @@ A non-exhaustive list of the application features. - Hierarchical constituencies, e.g. regions containing municipalities - When users select their constituency, the hierarchies are used to automatically select parent constituencies -## Statements or Questions +### Statements or Questions - Support for separate info and opinion questions - Opinion questions are used in calculating the matches - Info questions are shown in candidate or party profiles and can be used for filtering - Opinion question types - - Ordinal questions, e.g. Likert with full configurability (number of options and their labels) + - Ordinal questions, e.g. Likert with full configurability (number of options, their labels and distances) - Categorical questions - - Preference order (TBA) - - Number (TBA) + - Preference order (to be added in [`0.2 Akita`](/about/roadmap)) + - Number (to be added in [`0.2 Akita`](/about/roadmap)) - Info question types - Text - Number @@ -68,7 +71,7 @@ A non-exhaustive list of the application features. - Categorical with multiple selections - Date - Website link - - Multiple-item text (TBA) + - Multiple-item text (to be added in [`0.2 Akita`](/about/roadmap)) - Image (partial support) - Candidate portrait or Party logo - Question categories @@ -79,17 +82,17 @@ A non-exhaustive list of the application features. - Rich text - Can be separated into multiple sections - Video content - - Term definitions (WIP) - - AI-generated information (WIP) + - Term definitions + - AI-generated information (Experimental) -## Entities +### Entities - Candidates - Parties, i.e., Organizations - Electoral Alliances - Party Factions (partial support), e.g. those in _ley de lemas_ systems -## Matching algorithm +### Matching algorithm - Separated in an independent module - Based on a generic mathematical model (multidimensional space) and fully extensible within its confines @@ -98,15 +101,16 @@ A non-exhaustive list of the application features. - Directional - Euclidean - Sub-category matching, e.g., by Question category -- Configurable method for imputing missing answers +- Optional real-time display of top results while answering statements +- Optional statement weighting with configurable weights -## Candidate application +### Candidate application - Full application for candidates with which they can enter their data and opinions - Support for multi-lingual input - Optional support for self-registration using bank authentication -## Feedback and analytics +### Feedback and analytics - Built-in feedback form - Configurable user survey prompt for research purposes diff --git a/docs/src/routes/(content)/about/intro/+page.md b/docs/src/routes/(content)/about/intro/+page.md new file mode 100644 index 000000000..1f19e14fb --- /dev/null +++ b/docs/src/routes/(content)/about/intro/+page.md @@ -0,0 +1,5 @@ +# About OpenVAA + +OpenVAA is an open-source [Voting Advice Application](/publishers-guide/what-are-vaas/intro) platform. It is also the name of the non-profit association incorporated in Finland, tasked with developing and maintaining the platform. + +OpenVAA is also part of the [VoteMatch International](https://www.votematchinternational.org/) network. diff --git a/docs/src/routes/(content)/about/newsletter/+page.svelte b/docs/src/routes/(content)/about/newsletter/+page.svelte new file mode 100644 index 000000000..2717a232e --- /dev/null +++ b/docs/src/routes/(content)/about/newsletter/+page.svelte @@ -0,0 +1,81 @@ +<script lang="ts"></script> + +<div class="max-w-[40rem] p-xl"> + <p> + To be the first to know about our latest releases, to give feedback or to contribute otherwise, subscribe to our + newsletter below. + </p> + + <!-- Begin Mailchimp Signup Form --> + <div class="wrapper"> + <section id="mc_embed_signup" class="boxedSection"> + <form + action="https://openvaa.us9.list-manage.com/subscribe/post?u=093975a2cb721989c2e37def7&id=c06a61ed8a&f_id=008612e1f0" + method="post" + id="mc-embedded-subscribe-form" + name="mc-embedded-subscribe-form" + class="validate" + target="_self"> + <div id="mc_embed_signup_scroll"> + <h3>Do You Want to Shape the Future of VAAs?</h3> + <p> + Subscribe to our newsletter to receive calls for feedback and be among the first to know about our releases. + 💯 no spam. + </p> + <div class="inputAndButton"> + <input + type="email" + value="" + name="EMAIL" + class="required email input" + id="mce-EMAIL" + required + placeholder="Your email" /> + <input + type="submit" + value="Subscribe" + name="subscribe" + id="mc-embedded-subscribe" + class=" btn btn-primary" /> + <input type="hidden" name="group[53005]" id="mce-group[53005]" value="2" /> + </div> + <div id="mce-responses" class="clear foot"> + <div class="response" id="mce-error-response" style="display:none"></div> + <div class="response" id="mce-success-response" style="display:none"></div> + </div> + <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups--> + <div style="position: absolute; left: -5000px;" aria-hidden="true"> + <input type="text" name="b_093975a2cb721989c2e37def7_c06a61ed8a" tabindex="-1" value="" /> + </div> + </div> + </form> + </section> + </div> + <!--End mc_embed_signup--> +</div> + +<style> + .inputAndButton { + display: flex; + flex-direction: row; + align-content: stretch; + justify-content: center; + flex-wrap: wrap; + + > input, + > button { + margin: 0 var(--margin-XS); + } + + @media screen and (max-width: 600px) { + flex-direction: column; + + > input, + > button { + &:not(:first-child) { + margin-top: var(--margin-M); + } + } + } + } +</style> diff --git a/docs/src/routes/(content)/about/project/+page.md b/docs/src/routes/(content)/about/project/+page.md new file mode 100644 index 000000000..00a4464cf --- /dev/null +++ b/docs/src/routes/(content)/about/project/+page.md @@ -0,0 +1,49 @@ +# The initial OpenVAA project 2023–2025 + +Between 2023 and 2025 our was on building an open-source platform for VAAs. The project, called likewise OpenVAA, was graciously funded by [Sitra – the Finnish innovation fund](https://www.sitra.fi/en/). + +The project was accompanied by a research project called **YouthVAA** at the Helsinki University, which studied how VAAs are used and produced a VAA targeted at young, first-time voters. The research project was funded by [Kone Foundation](https://koneensaatio.fi/en/). + +## Motivation + +<p class="ingress">Voting advice applications (VAAs), or election compasses, are outstanding tools for assisting with candidate choice. In the last 25 years, they've become a staple of democracy in Finland and other, mainly European countries. Over two thirds of the electorate make use of them here, and young voters in particular rely on them. For parties and candidates they offer visibility centered on political issues and not constrained by campaign budgets.</p> + +For each national election, the largest media outlets publish VAAs that reach millions of electors. In addition to these, some smaller orgnasations release their own VAAs focused on specific viewpoints. All of these applications are, however, strikingly similar both to each other and from one election to the next. Their operational logic has remained mostly unchanged for at least a decade. _Have voting advice applications thus reached the apex of their development?_ + +We believe otherwise. Due to their vast popularity, VAAs have much potential to advance democracy even further: Their functionality and usability could be much improved. There could be greater variety in them, offering diverse user experiences and political perspectives. Most importantly, they could be more transparent; all Finnish VAAs, as well as most of those abroad, are closed systems with no insight to their matching algorithms, for example. + +Our solution to this challenge is the creation of an open-source framework for VAAs. Thus, we can enable the publishing of VAAs to a much wider spectrum of organisations than now. Furthermore, when the basic foundations are freely available, VAA publishers can concentrate their resources on improving them if they so choose. Nor is the scope of the VAA framework limited to Finland or national elections only. We ensure its compatibility with different electoral systems and languages from the onset, vastly expanding its possible applications to, for example, internal elections of non-governmental organisations. + +Due to the great influence of VAAs, it's very important that the framework along with the VAAs built using it function reliably and do not repeat errors made in the past. To this end, we also compose a methodological guide based on existing research literature and that conducted as part of the project. The guide is integrally tied to the framework and, naturally, open-sourced as well. It will cover the whole process of VAA creation from formulation of the questionnaire statements to the choice of matching algorithm. + +The three-year OpenVAA project started in 2023. The first VAAs created using it will be published for the European Parliament elections in April 2024. The project is funded by [Sitra – the Finnish innovation fund](https://www.sitra.fi/en/topics/digital-power-and-democracy/). Abreast with it, a separate research project called YouthVAA is conducted at the Helsinki University, which is tasked with building a VAA for young, first-time voters. YouthVAA is in turn funded by [Kone Foundation](https://koneensaatio.fi/en/). + +<p class="addendum" markdown="1"> + The projects are coordinated by designer [Kalle Järvenpää](http://kaljarv.com/) and VAA researcher [Veikko Isotalo](https://veikkoisotalo.fi/). Veikko has expansively explored the design of VAAs and theoretical issues associated with them. Kalle has focussed on the user experience of VAAs and published an innovative [VAA prototype](https://ehdokaskartta.fi/) for the 2021 Finnish municipal elections. +</p> + +## Project Vision + +The vision statement for the project is: + +> A widely used and openly developing framework. Diverse VAAs built with it help connect various voters with candidates and improve understanding about different views and values present in the society. + +## Publications + +Ín the preparatory phase of the project, OpenVAA published reports on VAAs, electoral systems and requirements for VAA development. + +### [Principles for the OpenVAA framework](https://docs.google.com/document/d/19pQ6ZEcThT7Hy_Mdds40zBuv6ketp8YozgV-liV48gQ/edit?usp=sharing) + +12 Jan 2024 v1.0 (Finnish) — The memo describes the foundational principles for building the OpenVAA framework. + +### [Report on VAA stakeholder consultations](https://docs.google.com/document/d/1cS0Q3A-N-RlA7bmyjSUQjN_mPu1J0YzZRQ_C2qBxbs0/edit?usp=sharing) + +12 Jan 2024 v1.0 (Finnish) – The report collects the main findings from consultations with and surveys sent to different VAA stakeholders. The consultations form the basis for designing the OpenVAA framework as well as provide novel ideas for future development. + +### [Comparison of electoral systems](https://docs.google.com/document/d/1VRDrFgvuKpch9295V8YjzTlA0mLofBuA1Mad5QfLKis/edit?usp=sharing) + +12 Jan 2024 v1.0 (Finnish) – The memo compares different electoral systems from the pespective of requirements for the OpenVAA framework. + +### [Current state of VAA technology](https://docs.google.com/document/d/1LpqkvmfL8aDxWGoAEy1Ub6l5OJdxGsVLNjaAWk3CTO8/edit?usp=sharing) + +12 Jan 2024 v1.0 (Finnish) – The report describes the most relevant VAAs in use in Finland as well as the most prominent international ones. It also details Finnish providers of VAA technology and open-source VAAs. diff --git a/docs/src/routes/(content)/about/roadmap/+page.md b/docs/src/routes/(content)/about/roadmap/+page.md new file mode 100644 index 000000000..7ea88f801 --- /dev/null +++ b/docs/src/routes/(content)/about/roadmap/+page.md @@ -0,0 +1,12 @@ +# Roadmap + +### Release `0.2 Akita` (2026/H1) + +- Update to Svelte 5 +- Migrate backend from Strapi to Supabase +- Update monorepo structure + +### Future releases + +- Enable plugins or easier customisation of pages and main components +- Multi-tenant model diff --git a/docs/src/routes/(content)/about/rules/+page.md b/docs/src/routes/(content)/about/rules/+page.md new file mode 100644 index 000000000..f4e1a13ec --- /dev/null +++ b/docs/src/routes/(content)/about/rules/+page.md @@ -0,0 +1,95 @@ +# OpenVAA ry Association rules + +## OpenVAA ry:n säännöt + +_Vahvistettu 23.9.2022_ + +## 1. Yhdistyksen nimi ja kotipaikka + +Yhdistyksen nimi on OpenVAA ry ja sen kotipaikka on Helsinki. + +## 2. Tarkoitus ja toiminnan laatu + +Yhdistyksen tarkoituksena on edistää demokratiaa edistävien digitaalisten palvelujen ja työkalujen kehittämistä. Sen erityisenä tavoitteena on avointen vaalikoneiden kehittäminen. + +Tarkoituksensa toteuttamiseksi yhdistys: + +- voi kerätä ja jakaa tietoa sekä harjoittaa tutkimustoimintaa +- voi tehdä esityksiä, aloitteita ja selvityksiä sekä antaa lausuntoja muille yhteisöille ja viranomaisille +- toteuttaa ja tukee tarkoitustaan tukevia hankkeita +- voi jakaa stipendejä, apurahoja, kunniamainintoja ja -kirjoja +- voi harjoittaa julkaisutoimintaa +- voi kuulua jäsenenä kotimaisiin ja ulkomaisiin yhdistyksiin ja yhteisöihin, joiden tarkoitus vastaa yhdistyksen tarkoitusta tai on sitä lähellä +- voi järjestää seminaareja, kursseja ja muita toiminnan tarkoitusta tukevia tilaisuuksia +- voi ottaa itselleen toiminnanjohtajan, jonka hallitus nimittää ja joka hoitaa yhdistyksen juoksevaa hallintoa ja muita asioita hallituksen ohjeiden ja määräysten mukaisesti + +Toimintansa tukemiseksi yhdistys voi, hankittuaan tarvittaessa asianomaisen luvan: + +- järjestää rahankeräyksiä ja arpajaisia +- järjestää maksullisia tilaisuuksia +- ottaa vastaan avustuksia, lahjoituksia ja testamentteja +- omistaa toimintansa kannalta tarpeellista irtainta ja kiinteää omaisuutta + +## 3. Jäsenet + +Yhdistykseen varsinaiseksi jäseneksi voidaan hyväksyä henkilö, joka hyväksyy yhdistyksen tarkoituksen. Kannattavaksi jäseneksi voidaan hyväksyä yksityinen henkilö tai oikeuskelpoinen yhteisö, joka haluaa tukea yhdistyksen tarkoitusta ja toimintaa. Varsinaiset jäsenet ja kannattavat jäsenet hyväksyy hakemuksesta yhdistyksen hallitus. Kunniapuheenjohtajaksi tai kunniajäseneksi voidaan hallituksen esityksestä yhdistyksen kokouksessa kutsua henkilö, joka on huomattavasti edistänyt ja tukenut yhdistyksen toimintaa. + +## 4. Liittymis- ja jäsenmaksu + +Varsinaisilta jäseniltä ja kannattavilta jäseniltä perittävän liittymismaksun ja vuotuisen jäsenmaksun suuruudesta erikseen kummallekin jäsenryhmälle päättää vuosikokous. Kunniapuheenjohtaja ja kunniajäsenet eivät suorita jäsenmaksuja. + +## 5. Hallitus + +Yhdistyksen asioita hoitaa hallitus, johon kuuluu vuosikokouksessa valitut puheenjohtaja ja 2 - 8 muuta varsinaista jäsentä. + +Hallituksen toimikausi on valintakokousten välinen aika. + +Hallitus valitsee keskuudestaan varapuheenjohtajan sekä ottaa keskuudestaan tai ulkopuoleltaan sihteerin, rahastonhoitajan ja muut tarvittavat toimihenkilöt. Hallitus kokoontuu puheenjohtajan tai hänen estyneenä ollessaan varapuheenjohtajan kutsusta, kun he katsovat siihen olevan aihetta tai kun vähintään puolet hallituksen jäsenistä sitä vaatii. Hallitus on päätösvaltainen, kun vähintään puolet sen jäsenistä, puheenjohtaja tai varapuheenjohtaja mukaanluettuna on läsnä. Äänestykset ratkaistaan ehdottomalla ääntenenemmistöllä. Äänten mennessä tasan ratkaisee puheenjohtajan ääni, vaaleissa kuitenkin arpa. + +## 6. Yhdistyksen nimen kirjoittaminen + +Yhdistyksen nimen kirjoittaa hallituksen puheenjohtaja, varapuheenjohtaja, sihteeri, rahastonhoitaja, hallituksen oikeuttama henkilö, kukin yksin. + +## 7. Tilikausi + +Yhdistyksen tilikausi on kalenterivuosi. + +## 8. Yhdistyksen kokoukset + +Yhdistyksen kokoukseen voidaan osallistua hallituksen tai yhdistyksen kokouksen niin päättäessä myös postitse taikka tietoliikenneyhteyden tai muun teknisen apuvälineen avulla kokouksen aikana tai ennen kokousta. Yhdistyksen kokous hyväksyy äänestys- ja vaalijärjestyksen. + +Yhdistyksen vuosikokous pidetään vuosittain hallituksen määräämänä päivänä tammi-toukokuussa. + +Yhdistyksen kokouksissa on jokaisella varsinaisella jäsenellä, kunniapuheenjohtajalla ja kunniajäsenellä yksi ääni. Kannattavalla jäsenellä on kokouksessa läsnäolo- ja puheoikeus. Yhdistyksen kokouksen päätökseksi tulee, ellei säännöissä ole toisin määrätty, se mielipide, jota on kannattanut yli puolet annetuista äänistä. + +Äänten mennessä tasan ratkaisee kokouksen puheenjohtajan ääni, vaaleissa kuitenkin arpa. + +## 9. Yhdistyksen kokousten koollekutsuminen + +Hallituksen on kutsuttava yhdistyksen kokoukset koolle vähintään 7 vuorokautta ennen kokousta. + +Kokouskutsu on lähetettävä jäsenille: + +- kirjeitse tai sähköpostitse +- yhdistyksen verkkosivulla tai yhdistyksen käyttämän sosiaalisen median ryhmässä + +## 10. Vuosikokous + +Yhdistyksen vuosikokouksessa käsitellään seuraavat asiat: + +1. kokouksen avaus +2. valitaan kokouksen puheenjohtaja, sihteeri, kaksi pöytäkirjantarkastajaa ja tarvittaessa kaksi ääntenlaskijaa +3. todetaan kokouksen laillisuus ja päätösvaltaisuus +4. hyväksytään kokouksen työjärjestys +5. esitetään tilinpäätös, vuosikertomus ja toiminnantarkastajien/tilintarkastajien lausunto +6. päätetään tilinpäätöksen vahvistamisesta ja vastuuvapauden myöntämisestä hallitukselle ja muille vastuuvelvollisille +7. vahvistetaan toimintasuunnitelma, tulo- ja menoarvio sekä liittymis- ja jäsenmaksujen suuruudet +8. valitaan hallituksen puheenjohtaja ja muut jäsenet +9. valitaan yksi tai kaksi toiminnantarkastajaa ja varatoiminnantarkastajaa taikka yksi tai kaksi tilintarkastajaa ja varatilintarkastajaa +10. käsitellään muut kokouskutsussa mainitut asiat. + +Mikäli yhdistyksen jäsen haluaa saada jonkin asian yhdistyksen vuosikokouksen käsiteltäväksi, on hänen ilmoitettava siitä kirjallisesti hallitukselle niin hyvissä ajoin, että asia voidaan sisällyttää kokouskutsuun. + +## 11. Sääntöjen muuttaminen ja yhdistyksen purkaminen + +Päätös sääntöjen muuttamisesta ja yhdistyksen purkamisesta on tehtävä yhdistyksen kokouksessa vähintään kolmen neljäsosan (3/4) enemmistöllä annetuista äänistä. Kokouskutsussa on mainittava sääntöjen muuttamisesta tai yhdistyksen purkamisesta. Yhdistyksen purkautuessa käytetään yhdistyksen varat yhdistyksen tarkoituksen edistämiseen purkamisesta päättävän kokouksen määräämällä tavalla. Yhdistyksen tullessa lakkautetuksi käytetään sen varat samaan tarkoitukseen. diff --git a/docs/src/routes/(content)/developers-guide/app-and-repo-structure/+page.md b/docs/src/routes/(content)/developers-guide/app-and-repo-structure/+page.md new file mode 100644 index 000000000..a3d3bb049 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/app-and-repo-structure/+page.md @@ -0,0 +1,22 @@ +# App and repo structure + +The project is a monorepo and it consists of several yarn workspaces (each is a separate NPM module). See the READMEs in each for more information. + +- Abstract logic + - [`@openvaa/core`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/core/) + - [`@openvaa/data`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/data/) + - [`@openvaa/filters`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/filters/) + - [`@openvaa/matching`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/matching/) +- Application + - [`@openvaa/app-shared`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/) + - [`@openvaa/strapi`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/) + - [`@openvaa/strapi-admin-tools`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/) + - [`@openvaa/frontend`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/) +- Experimental LLM features + - [`@openvaa/argument-condensation`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/argument-condensation/) + - [`@openvaa/llm`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/llm/) + - [`@openvaa/question-info`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/question-info/) +- Development + - [`@openvaa/shared-config`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/shared-config/) +- Documentation (this site) + - [`@openvaa/docs`](https://github.com/OpenVAA/voting-advice-application/blob/main/docs/) diff --git a/docs/src/routes/(content)/developers-guide/auto-documentation/+page.md b/docs/src/routes/(content)/developers-guide/auto-documentation/+page.md new file mode 100644 index 000000000..3c9e517b0 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/auto-documentation/+page.md @@ -0,0 +1,7 @@ +# Automatic Documentation Generation + +The documentation for the repository, i.e. this very site, is partly automatically generated. + +The site, its contents and scripts used in the generation are contained in the [`docs` package](https://github.com/OpenVAA/voting-advice-application/tree/main/docs) of the monorepo. + +See the README file in the package for more information. diff --git a/docs/src/routes/(content)/developers-guide/backend/authentication/+page.md b/docs/src/routes/(content)/developers-guide/backend/authentication/+page.md new file mode 100644 index 000000000..cb155e93b --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/authentication/+page.md @@ -0,0 +1,33 @@ +# Authentication + +Standard read calls require no authentication and are included in the default permissions, which are customized in the [Users’ permissions plugin](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/extensions/users-permissions/strapi-server.ts). + +Furthermore, all API routes are configured + +Write calls require authentication: + +- For registered Candidates, this is handled by creating a user. Read more in the [Candidate App documentation](/developers-guide/candidate-user-management/creating-a-new-candidate). +- For pre-registration, an API token with the `users-permissions.candidate.preregister` priviledge is required, which must be saved in the `BACKEND_API_TOKEN` env variable. Read more on creating the token in the [Strapi documenation](https://docs.strapi.io/user-docs/settings/API-tokens#creating-a-new-api-token). + +### Adding new content types + +If you add new content types that should be accessible, make sure: + +1. Edit the `CONTENT_API` list in [api.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/util/api.ts) to grant read rights to the public +2. Add the permission in the [Users’ permissions plugin](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/extensions/users-permissions/strapi-server.ts) so that registered users are granted access sa well +3. Also make sure that the route config includes the default restrictions: + +```ts +// /src/api/<COLLECTION>/routes/<COLLECTION>.ts +export default factories.createCoreRouter('api::<COLLECTION>.<COLLECTION>', { + only: ['find', 'findOne'], // Explicitly disabled create, update, delete + config: { + find: { + policies: ['global::restrict-populate'] + }, + findOne: { + policies: ['global::restrict-populate'] + } + } +}); +``` diff --git a/docs/src/routes/(content)/developers-guide/backend/customized-behaviour/+page.md b/docs/src/routes/(content)/developers-guide/backend/customized-behaviour/+page.md new file mode 100644 index 000000000..456f5bbe4 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/customized-behaviour/+page.md @@ -0,0 +1,3 @@ +# Customized behaviour + +The Strapi backend has been customized in many ways to cater to VAA needs. The current implementation is split between direct edits to Strapi code and some functions implemented in the [OpenVAA Admin Tools plugin](/developers-guide/backend/openvaa-admin-tools-plugin-for-strapi). Most of the customizations should be migrated to the plugin in the future. diff --git a/docs/src/routes/(content)/developers-guide/backend/default-data-loading/+page.md b/docs/src/routes/(content)/developers-guide/backend/default-data-loading/+page.md new file mode 100644 index 000000000..2dbd19db2 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/default-data-loading/+page.md @@ -0,0 +1,11 @@ +# Default data loading + +Some data is automatically loaded when Strapi is initialized. The data include: + +- [Question Types](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/functions/loadDefaultData.ts) +- [App Settings](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/functions/loadDefaultAppSettings.ts) +- [Translation overrides](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/functions/loadDynamicTranslations.ts) (under the `dynamic` key) + +API permissions are also set by defaul by [setDefaultApiPermissions.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/functions/setDefaultApiPermissions.ts). + +> Note that some of the defaults are **not** loaded if mock data generations is enabled. diff --git a/docs/src/routes/(content)/developers-guide/backend/intro/+page.md b/docs/src/routes/(content)/developers-guide/backend/intro/+page.md new file mode 100644 index 000000000..eae2d9fab --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/intro/+page.md @@ -0,0 +1,3 @@ +# Strapi + +> The current backend uses the Strapi headless CMS. It is scheduled to be migrated to Supabase in H1/2026. diff --git a/docs/src/routes/(content)/developers-guide/backend/mock-data-generation/+page.md b/docs/src/routes/(content)/developers-guide/backend/mock-data-generation/+page.md new file mode 100644 index 000000000..3af11e71e --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/mock-data-generation/+page.md @@ -0,0 +1,42 @@ +# Mock data generation + +> NB! This feature must only be used for local development and testing (not on production) + +The database can be seeded with generated mock data using Faker.js. + +If enabled, the data is generated by the rather messy [generateMockData](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/functions/generateMockData.ts) function. See the implementation for details and constants you can edit in-code to control the amount of data. + +Mock data can be seeded only once on initialising the backend DB or on each restart of the Strapi backend. Mock data generation is controlled by these `.env` variables: + +```dotenv +# Set to true to enable mock data on an empty database +GENERATE_MOCK_DATA_ON_INITIALISE=true + +# Used only in development builds +GENERATE_MOCK_DATA_ON_RESTART=false +``` + +To enable mock data generation, set the `GENERATE_MOCK_DATA_ON_INITIALISE` variable as true. This will create mock data if the database is empty or give a warning if database is not empty and thus mock data could not be generated. + +You can also set `GENERATE_MOCK_DATA_ON_RESTART` as true. This will generate new mock data every time the Strapi instance is restarted. + +**Please keep in mind that setting this variable as true will clear the database contents of existing candidates, parties, elections, and so on and should only be used for debugging purposes.** +Setting `GENERATE_MOCK_DATA_ON_RESTART` as true will override `GENERATE_MOCK_DATA_ON_INITIALISE` setting. + +**Note:** you need to modify these variables in the relevant `.env` file (located either in the project's root directory or in `backend/vaa-strapi`) depending on how you choose to run the backend service locally. + +### Mock users + +By default, mock data includes the following Users (for up-to-date details, see [mockUsers](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/functions/mockData/mockUsers.json)): + +| User type | User role | Email | Password | Remarks | +| ----------- | ------------- | ------------------------------ | ------------ | ----------------------------- | +| Candidate 1 | Authenticated | `mock.candidate@openvaa.org` | `Password1!` | Use to test the Candidate App | +| Candidate 2 | Authenticated | `mock.candidate.2@openvaa.org` | `Password1!` | Use to test the Candidate App | +| Admin | Admin | `mock.admin@openvaa.org` | `Password1!` | Use to test the Admin App | + +Note that the mock admin is different from the default Strapi Admin, generated along with mock data. + +| User type | Username | Email | Password | Remarks | +| ------------ | -------- | ------------------------ | -------- | ---------------------- | +| Strapi Admin | `admin` | `mock.admin@openvaa.org` | `admin` | Use to login to Strapi | diff --git a/docs/src/routes/(content)/developers-guide/backend/openvaa-admin-tools-plugin-for-strapi/+page.md b/docs/src/routes/(content)/developers-guide/backend/openvaa-admin-tools-plugin-for-strapi/+page.md new file mode 100644 index 000000000..683fd6f7f --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/openvaa-admin-tools-plugin-for-strapi/+page.md @@ -0,0 +1,137 @@ +# OpenVAA admin tools plugin for Strapi + +> NB! The name of the plugin in Strapi is `openvaa-admin-tools`. + +The plugin combines Strapi admin functions needed by the OpenVAA voting advice application. + +### Status: preliminary + +Only some of the custom functions are currently contained in the plugin. Functions that should be migrated from the `@openvaa/strapi` include: + +- Mock data generation +- Loading default settings and customization +- Candidate registration +- Candidate preregistration + +### Installation + +The plugin is a local plugin and not currently published as an NPM package. + +It is enabled by default in `@openvaa/strapi`’s [plugin config](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/config/plugins.ts): + +```ts +export default ({ env }) => { + return { + // Other plugins + 'openvaa-admin-tools': { + enabled: true, + resolve: 'src/plugins/openvaa-admin-tools' + } + }; +}; +``` + +### Developing + +1. Enable [hot-reloading](/developers-guide/development/running-the-development-environment#hot-reloading-the-backend) in Strapi. +2. Watch plugin source for edits by running `yarn workspace @openvaa/strapi-admin-tools watch`. + +> NB! Before merging it’s safest to also try the plugin in the production environment as well, because some (unstable) Strapi function may not work there. To do that set `services.strapi.build.target=production` in [docker-compose](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/docker-compose.dev.yml). + +### Usage + +The plugin functions are accessed via the Strapi Admin UI. There are currenlty two types of functions: + +1. Registration email functions which appear on the right-hand column in the Content manager Candidate list view and single Candidate view. +2. Data import and deletion which are accessed via the admin tool page, which is opened with the jigsaw icon in the main navigation. + +The services can also be invoked like any other plugins services with, e.g., `strapi.plugin('openvaa-admin-tools').service('data').import(data)`. + +### Functions + +#### Registration email + +##### Send the registration email to a single Candidate + +- UI: Content manager > Candidates > Edit view +- Component: [RegistrationEmailToOne](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/admin/src/components/RegistrationEmailToOne.tsx) +- Service: [`email.sendEmail`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/email.ts) +- API: `/openvaa-admin-tools/send-email` + +Sends a registration link email with customizable content and subject to the Candidate. + +Throws if the content does not include the link placeholder. + +##### Send the registration email to all unregistered Candidates + +- UI: Content manager > Candidates > List view +- Component: [RegistrationEmailToAll](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/adminsrc/components/RegistrationEmailToAll.tsx) +- Service: [`email.sendEmailToUnregistered`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/email.ts) +- API: `/openvaa-admin-tools//send-email-to-unregistered` +- Required permission: `plugin::openvaa-admin-tools.send-email` + +Sends a registration link email with customizable content and subject to all unregisterd Candidates. + +Throws if the content does not include the link placeholder. + +#### Import and delete data + +The `externalId` is a private field that all collection types have which is used to store a stable id which can be referenced instead of the `documentId` in the import and delete functions. + +##### Import any data in JSON format + +- UI: OpenVAA Admin Tools page +- Component: [ImportData](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/adminsrc/components/ImportData.tsx) +- Service: [`data.import`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/data.ts) +- API: `/openvaa-admin-tools/import-data` +- Required permission: `plugin::openvaa-admin-tools.import-data` + +Import data into Strapi or update existing data based on `externalId` or `documentId` if provided. The data is supplied as `Array`s of `ImportData` objects collected by their collection name. + +See the instructions in the component for further info. + +##### Delete any data by its `externalId` + +- UI: OpenVAA Admin Tools page +- Component: [DeleteData](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/adminsrc/components/DeleteData.tsx) +- Service: [`data.delete`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/data.ts) +- API: `/openvaa-admin-tools/delete-data` +- Required permission: `plugin::openvaa-admin-tools.import-data` + +Delete data based on `externalId`s. Objects whose `externalId`s (specified by collection) start with the provided, case-sensitive prefixes will be deleted. Mostly useful for deleting mock data which is generated with `externalId`s starting with `mock-`. + +See the instructions in the component for further info. + +##### Find any data using `filters` + +- UI: OpenVAA Admin Tools page +- Component: [FindData](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/adminsrc/components/FindData.tsx) +- Service: [`data.find`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/data.ts) +- API: `/openvaa-admin-tools/find-data` +- Required permission: `plugin::openvaa-admin-tools.import-data` + +Find any data accessible by the data tools by providing arbitrary filters. You can also select the relations to populate. + +See the instructions in the component for further info. + +##### Send email to selected Candidates + +- UI: OpenVAA Admin Tools page +- Component: [SendEmail](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/adminsrc/components/SendEmail.tsx) +- Services: + - [`data.findCandidates`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/data.ts) + - [`email.sendEmail`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/services/email.ts) +- APIs: + - `/openvaa-admin-tools/find-candidates` + - `/openvaa-admin-tools/send-email` +- Required permissions: + - `plugin::openvaa-admin-tools.import-data` + - `plugin::openvaa-admin-tools.send-email` + +A tool that combines searching for Candidates to build an editable list to whom to send emails. + +See the instructions in the component for further info. + +### Access control + +In addition to the specified permissions, all routes require `admin::isAuthenticatedAdmin`, see [route policies](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/plugins/openvaa-admin-tools/server/src/routes/admin/index.ts). diff --git a/docs/src/routes/(content)/developers-guide/backend/plugins/+page.md b/docs/src/routes/(content)/developers-guide/backend/plugins/+page.md new file mode 100644 index 000000000..aad51ce80 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/plugins/+page.md @@ -0,0 +1,5 @@ +# Plugins + +- Email uses AWS SES, see [Candidate App documentation](/developers-guide/candidate-user-management/resetting-the-password) +- Upload uses AWS S3, see [plugins.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/config/plugins.ts) +- [OpenVAA Strapi Admin Tools plugin](/developers-guide/backend/openvaa-admin-tools-plugin-for-strapi) (local plugin) diff --git a/docs/src/routes/(content)/developers-guide/backend/preparing-backend-dependencies/+page.md b/docs/src/routes/(content)/developers-guide/backend/preparing-backend-dependencies/+page.md new file mode 100644 index 000000000..30d11cd0a --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/preparing-backend-dependencies/+page.md @@ -0,0 +1,8 @@ +# Preparing backend dependencies + +The backend module depends on `@openvaa/app-shared` and you need to build it prior to using `@openvaa/strapi` directly (no need if you use it via Docker): + +```bash +yarn workspace @openvaa/app-shared install +yarn workspace @openvaa/app-shared build +``` diff --git a/docs/src/routes/(content)/developers-guide/backend/re-generating-types/+page.md b/docs/src/routes/(content)/developers-guide/backend/re-generating-types/+page.md new file mode 100644 index 000000000..c50f0d169 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/re-generating-types/+page.md @@ -0,0 +1,3 @@ +# Re-generating types + +Run `yarn strapi ts:generate-types` to re-generate `types` folder. diff --git a/docs/src/routes/(content)/developers-guide/backend/running-the-backend-separately/+page.md b/docs/src/routes/(content)/developers-guide/backend/running-the-backend-separately/+page.md new file mode 100644 index 000000000..27c904716 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/running-the-backend-separately/+page.md @@ -0,0 +1,7 @@ +# Running the backend separately + +0. You should be running Strapi with the Node version specified under `engines` in the root [package.json](https://github.com/OpenVAA/voting-advice-application/blob/main/package.json). Use of nvm is encouraged. **Additionally, you need Docker!** +1. Install dependencies by running `yarn install`. +2. Copy or rename the `.env.example` to `.env` before running any of the commands. +3. Run `docker compose -f docker-compose.dev.yml up postgres` to start Postgres container. +4. Run `yarn dev` or `yarn start` to run the Strapi server directly. diff --git a/docs/src/routes/(content)/developers-guide/backend/security/+page.md b/docs/src/routes/(content)/developers-guide/backend/security/+page.md new file mode 100644 index 000000000..b977a8093 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/backend/security/+page.md @@ -0,0 +1,209 @@ +# Security + +> NB. The Security chapter may be partly outdated. + +By default, all content types inside Strapi are assumed to be safe to be publicly exposed. For reading, all the fields except any fields marked with the `private` keyword in their schema definition are returned per how Strapi works. For writing, the majority of the create, update, and delete endpoints are disabled by default unless explicitly used by the candidate application, and otherwise restricted to only resources that belong to the logged in candidate to prevent unauthorized modification of the data. + +The restrictions are enforced using policies for each content type's route, usually found in the `backend/vaa-strapi/src/api/[schema]/routes/[schema].ts` file. To simplify this, there are a couple of helper functions available in [acl.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/util/acl.ts) and [policies](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/policies/). Please see [Strapi's documentation on policies](https://docs.strapi.io/dev-docs/backend-customization/policies#usage) on how to use them. + +The default permissions unauthenticated and authenticated users are managed in the [strapi-server.js](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/extensions/users-permissions/strapi-server.js) file, in the `defaultPermissions` array. + +### filter-by-candidate + +Enforces that all the returned content belongs to the currently authenticated candidate by filtering the `candidate` field. This is primarily meant only for the find and findOne endpoints so that they only return resources belonging to the authenticated candidate in case the content type shouldn't be public for everyone. + +Example usage: + +```ts +export default factories.createCoreRouter('...', { + ... + config: { + find: { + policies: [ + 'global::filter-by-candidate', + ], + }, + }, +}); +``` + +### owned-by-candidate + +Enforces that the request applies the `candidate` field to the currently authenticated candidate. This is primarily meant only for the create and update endpoints so that the created and updated resources body will always have the candidate set to the authenticated candidate to prevent impersonation. + +Example usage: + +```ts +export default factories.createCoreRouter('...', { + ... + config: { + create: { + policies: [ + 'global::owned-by-candidate', + ], + }, + }, +}); +``` + +### restrictPopulate + +Enforces that only the allowed populate fields are set to prevent leaking content types from relationships. Strapi does not implement a convenient way to restrict populates, so this would need to be enforced for every content type that is able to eventually return the content type even if there is no direct relationship. For example, if there were the `shop -> pizza -> ingredient` relationship where `ingredient` should not be exposed to the public, it is still possible to use populate to get `ingredient` through the `shop` endpoint using the `shop -> pizza` and `pizza -> ingredient` relationship. + +Example usage: + +```ts +export default factories.createCoreRouter('...', { + ... + config: { + find: { + policies: [ + restrictPopulate([ + 'pizza', // ?populate[pizza]=true + 'pizza.populate.shop' // ?populate[pizza][populate][shop]=true + ]), + ], + }, + }, +}); +``` + +### restrictFilters + +Enforces that only the allowed filters are set to prevent leaking content types from relationships. This is useful in scenarios where a specific relationship should not be returned, which filter would allow querying for even though the value is not directly retrievable. For example, user would be able to filter relationship's value by checking the prefix, which would eventually give an oracle where if the character is correct, the content type is returned, or if it's wrong, the content type isn't returned. Repeating this would then allow recovering field values that they shouldn't be able to get. + +Example usage: + +```ts +export default factories.createCoreRouter('...', { + ... + config: { + find: { + policies: [ + restrictFilters([ + 'candidate.id.$eq', // ?filters[candidate][id][$eq]=1 + ]), + ], + }, + }, +}); +``` + +### restrictFields + +Enforces that only the allowed fields are returned from the content type. If no fields are explicitly provided (using the `?fields=...` syntax in the request), it will default to only providing the allowed fields. This is intended for all the request endpoints as they all return the content type the action is performed on. Note that you should use the `private` field in the content type schema first for increased security (making this redundant), but if that isn't possible then this is an alternative option. This also has same caveats as `restrictPopulate` where the fields will not apply to relationships returned, and the field that shouldn't be returned will still be returned through populate if not carefully restricted. + +Example usage: + +```ts +export default factories.createCoreRouter('...', { + ... + config: { + find: { + policies: [ + restrictFields(['id', 'name']), // will allow returning id, name, or a subset of those + ], + }, + }, +}); +``` + +### restrictBody + +Enforces that only the allowed fields are allowed in the body. This is primarily meant only for the create and update endpoints to prevent modifying fields that should not be modified in the content type. + +Example usage: + +```ts +export default factories.createCoreRouter('...', { + ... + config: { + update: { + policies: [ + restrictBody(['name']), // will only setting the name field in the body + ], + }, + }, +}); +``` + +### restrictResourceOwnedByCandidate + +Enforces that the accessed resource belongs to the currently authenticated user by verifying that the accessed resource has `candidate` relationship to the authenticated user. This is primarily meant only for the findOne, create, update, and delete endpoints to prevent modification to resources not owned by that candidate. Note that one needs to be careful and make sure that the currently authenticated user is unable to modify another user's resource and set their candidate field to be themselves before performing modifications. + +Example usage: + +```ts +export default factories.createCoreRouter('api::my-content', { + ... + config: { + update: { + policies: [ + restrictResourceOwnedByCandidate('api::my-content'), // the content type name is needed for the checks + ], + }, + }, +}); +``` + +### Preset + +Here is a default preset one can use for new content-type that aims to be secure by default: + +```ts +export default factories.createCoreRouter('...', { + only: ['find', 'findOne', 'create', 'update', 'delete'], + config: { + find: { + policies: [ + // Disable populate by default to avoid accidentally leaking data through relations + restrictPopulate([]), + // Disable filters by default to avoid accidentally leaking data of relations + restrictFilters([]) + ] + }, + findOne: { + policies: [ + // Disable populate by default to avoid accidentally leaking data through relations + restrictPopulate([]), + // Disable filters by default to avoid accidentally leaking data of relations + restrictFilters([]) + ] + }, + create: { + policies: [ + // Enforce ownership to always belong to the candidate + 'global::owned-by-candidate', + // Disable populate by default to avoid accidentally leaking data through relations + restrictPopulate([]), + // Disable filters by default to avoid accidentally leaking data of relations + restrictFilters([]) + ] + }, + update: { + policies: [ + // Allow only updating candidate's own resource + restrictResourceOwnedByCandidate('...'), + // Enforce ownership to always belong to the candidate + 'global::owned-by-candidate', + // Disable populate by default to avoid accidentally leaking data through relations + restrictPopulate([]), + // Disable filters by default to avoid accidentally leaking data of relations + restrictFilters([]) + ] + }, + delete: { + policies: [ + // Allow only deleting candidate's own resource + restrictResourceOwnedByCandidate('api::answer.answer'), + // Disable populate by default to avoid accidentally leaking data through relations + restrictPopulate([]), + // Disable filters by default to avoid accidentally leaking data of relations + restrictFilters([]) + ] + } + } +}); +``` + +Note that if you do not need the create, update, and delete endpoints, one should disable them by removing them from the `only` array. diff --git a/docs/src/routes/(content)/developers-guide/candidate-user-management/creating-a-new-candidate/+page.md b/docs/src/routes/(content)/developers-guide/candidate-user-management/creating-a-new-candidate/+page.md new file mode 100644 index 000000000..7ab96b5ad --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/candidate-user-management/creating-a-new-candidate/+page.md @@ -0,0 +1,38 @@ +# Creating a New Candidate + +A candidate can log in to the candidate app either by having the admin give them a registration key, registering an account for them, or using pre-registration. + +### Using Registration Key + +To use the registration code, you need to navigate to the Strapi's admin area (http://localhost:1337), select the "Content Manager" tab, and choose the "Candidates" tab from the dropdown. If a candidate you want to log in as doesn't exist yet, you can use the "Create new entry" button to create one. + +Afterward, edit the desired candidate and choose a secure and random value for the registrationKey field. Then, save the change by clicking the "Save" button. The chosen registrationKey should be shared, and then the candidate can navigate to http://localhost:5173/candidate/register to create their account. + +### Manually + +To create the user manually, you need to navigate to the Strapi's admin area (http://localhost:1337), select the "Content Manager" tab, and choose the "User" tab from the dropdown. + +You can create a new user using the "Create new entry" button. The following fields should be set: + +- username (any desired username) +- email (any desired email, it'll be used for logging in) +- password (any strong password) +- confirmed => True +- blocked => False +- role => Authenticated +- candidate => the desired candidate the user is linked to + +Then, use the "Save" button and you should be able to log in as the candidate at http://localhost:5173/candidate. + +### Pre-Registration + +In the pre-registration process, anyone can use their bank identity to register as a candidate. When the Voter App is published, those registered users who can be matched to actual candidates in the election can be confirmed to be shown in the app. + +This requires a subscription to an identity service provider to work. It is enabled with the `preRegistration.enabled` static setting. + +The feature is partially supported. + +The documentation is TBA but one can read the notes from the two PRs in which was built: + +- [Initial PR 687](https://github.com/OpenVAA/voting-advice-application/pull/687) +- [Enhancement PR 703](https://github.com/OpenVAA/voting-advice-application/pull/703) diff --git a/docs/src/routes/(content)/developers-guide/candidate-user-management/mock-data/+page.md b/docs/src/routes/(content)/developers-guide/candidate-user-management/mock-data/+page.md new file mode 100644 index 000000000..9990e41b3 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/candidate-user-management/mock-data/+page.md @@ -0,0 +1,3 @@ +# Mock Data + +See [Mock users](/developers-guide/backend/mock-data-generation/#mock-users) for automatically generated users you can use to test the Candidate App. diff --git a/docs/src/routes/(content)/developers-guide/candidate-user-management/password-validation/+page.md b/docs/src/routes/(content)/developers-guide/candidate-user-management/password-validation/+page.md new file mode 100644 index 000000000..c949a7e6b --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/candidate-user-management/password-validation/+page.md @@ -0,0 +1,103 @@ +# Password Validation + +### Frontend + +Setting the password is done in three components: + +- [`PasswordSetPage.svelte`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/routes/candidate/register/PasswordSetPage.svelte) (new user) +- [`PasswordResetPage.svelte`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/routes/candidate/password-reset/PasswordResetPage.svelte) (forgotten password) +- [`settings/+page.svelte`](<https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/routes/candidate/(protected)/settings/>) (update existing password) + +The password is validated using [`passwordValidation.ts`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/utils/passwordValidation.ts). + +The file provides two main functions for validating a password + +- `validatePassword` + Returns a boolean indicating whether the password is valid +- `validatePasswordDetails` + Returns both the password validity status boolean as well as details and state for each password requirement. These details are used for the validation UI. + +On all password set pages, a password validation UI is shown to the user. +This functionality is provided by the [`PasswordValidator.svelte`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/passwordValidator/PasswordValidator.svelte) component. +The component provides real-time password validation checks and shows the state of each password requirement. +More details on how the different requirements are shown to the user can be found in the documentation of the component. + +The UI validation is done using debounced validation, i.e. the validation status is checked and updated once the user stops typing for a moment. + +The password validation UI provides a variable indicating the password validation status, which can be used to enable/disable the password submit button. This way frontend allows the password to be submitted only if it is valid. + +However, due to the use of debounced validation, the validity of the password is also checked by the password set page itself before sending the request to the backend. + +If the validity check passes, a POST request is sent to the backend to either + +- `api/auth/candidate/register` +- `api/auth/reset-password` + +### Backend + +- `/api/auth/candidate/register` endpoint is implemented by [`candidate.ts`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/extensions/users-permissions/controllers/candidate.ts) using the registration key + - the backend also validates the password using the same `validatePassword` function +- `/api/auth/reset-password` is fully handled by Strapi's users-permissions plugin + +### Password requirements + +The password requirements are defined in the [`passwordValidation.ts`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/utils/passwordValidation.ts) file. + +Each requirement is defined by the `ValidationDetail` interface: + +```ts +export interface ValidationDetail { + status: boolean; + message: string; + negative?: boolean; + enforced?: boolean; +} +``` + +- status: + - indicates the state of the requirement, i.e. if it is valid +- message: + - describes the requirement + - shown to the user in the validation UI + - due to localization, the message string contains the key to the `i18n` localization file, where the actual texts are located +- negative: + - requirements are either positive or negative + - positive rules are the main requirements that are always enforced and must be met for the password to be valid + - negative rules are rules that are used to prevent bad password practises + - in the validation UI, negative rules are shown if they are violated +- enforced: + - negative rules can be either enforced or non-enforced + - enforced requirements need to be valid for a password to be valid + +Currently, all requirements are done without hardcoded lists of allowed characters and support languages that have separate uppercase and lowercase letters. Localization to other languages may need new validation rules. + +The file also contains help functions for checking different aspects of the password string. + +#### Changing and creating new requirements + +All validation rules are defined in the `passwordValidation` function. New requirements can be added and existing ones changed by modifying `ValidationDetail` objects of the result. + +The order of one group of requirements (positive, negative enforced, negative non-enforced) is the same order they are shown to the user in the validation UI. +Below are examples of both types of requirements that can be used as a guide to creating new requirements. If necessary, new help functions can be defined in the file to help with validating a specific aspect of the password. + +Example 1. +Positive requirement that checks that the length of the password is at least the required length + +```ts +length: { + status: password.length >= minPasswordLength, + message: 'candidateApp.passwordValidation.length' +} +``` + +Example 2. +Negative requirement that checks if the password contains repetition using the `checkRepetition` function. This function returns `true` if a string contains repetition and therefore the status of the requirement is the negative of that as the requirement passes if it does not contain repetition. +As the requirement is negative, it has the `negative` property set to true, and as the `enforced` property is missing, this negative requirement is not enforced. + +```ts +repetition: { + status: !checkRepetition(password), + message: 'candidateApp.passwordValidation.repetition', + negative: true +} +``` diff --git a/docs/src/routes/(content)/developers-guide/candidate-user-management/registration-process-in-strapi/+page.md b/docs/src/routes/(content)/developers-guide/candidate-user-management/registration-process-in-strapi/+page.md new file mode 100644 index 000000000..55f761d29 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/candidate-user-management/registration-process-in-strapi/+page.md @@ -0,0 +1,31 @@ +# Registration Process in Strapi + +The user registration process is primarily handled by the `users-permissions` plugin bundled by Strapi so that we can rely on Strapi's existing security mechanisms (rate-limiting and being battle-tested). The only exception is that the plugin is extended with registration code support (POST /api/auth/local/register endpoint) that is implemented in the [`candidate.ts`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/extensions/users-permissions/controllers/candidate.ts) file. The user is also not logged in automatically by this endpoint to prevent bypassing 2FA in case we want to implement it in the future, in scenarios where the user already exists but is not explicitly linked to a candidate yet. + +The existing content-type of `User` is used to identify the users that can log in, but extended with the `candidate` field so a logged-in user could be associated to a specific candidate. Similarly, the candidate schema also has a belong-to relation back to the user if any exists. This makes it possible to rely on the logic provided by `users-permissions` plugin instead of implementing all the login logic manually. You can find the schema definition for user in the [`schema.json`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/extensions/users-permissions/content-types/user/schema.json). + +See [password-validation.md](/developers-guide/candidate-user-management/password-validation) on additional information on how password validation is handled. + +For logging in and logging out, the frontend stores the session JWT token returned by Strapi in the local storage of the browser. The primary logic for this is handled in [`authenticationStore.ts`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/utils/authenticationStore.ts). Log out is handled simply by resetting the state to being logged out and discarding the saved JWT token inside local storage. + +### Email Templates + +You can modify the default email templates in the [email-templates](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/config/email-templates) folder. + +#### Using Variables + +Strapi uses [EJS](https://ejs.co/) as its template engine, thus the email templates support the functionality EJS provides. + +Though the most important functionality you'll need is variables. They can be used by wrapping the variable with `<%=` and `%>`, e.g. `<%= URL %>` would be replaced with the URL variable's contents in the email when it gets sent. + +### Templates + +#### Reset Password + +The reset password (reset-password.html) template supports the following variables (https://github.com/strapi/strapi/blob/2a2faea1d49c0d84077f66a57b3b73021a4c3ba7/packages/plugins/users-permissions/server/controllers/auth.js#L230-L236): + +- URL: The public frontend URL to the password reset path (e.g. `http://localhost:5173/candidate/password-reset`) +- SERVER_URL: The Strapi's base URL +- ADMIN_URL: The Strapi's admin area URL +- USER: The user object the email is being sent to, specifically having the user's content-type properties that can be accessed like `USER.PROPERTY_NAME` (e.g. `USER.email`) +- TOKEN: The password reset token needed to reset the user's password diff --git a/docs/src/routes/(content)/developers-guide/candidate-user-management/resetting-the-password/+page.md b/docs/src/routes/(content)/developers-guide/candidate-user-management/resetting-the-password/+page.md new file mode 100644 index 000000000..166454742 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/candidate-user-management/resetting-the-password/+page.md @@ -0,0 +1,14 @@ +# Resetting the Password + +The user gets an email with a link to reset their password using the forgot password functionality on the login page. The frontend URL in the emails is configured in `.env` with the PUBLIC_BROWSER_FRONTEND_URL variable, and the email service (AWS SES) can be configured using the following variables: + +- `AWS_SES_ACCESS_KEY_ID`: AWS SES user access key +- `AWS_SES_SECRET_ACCESS_KEY`: AWS SES user secret access key +- `AWS_SES_REGION`: AWS SES region +- `MAIL_FROM`: the email address the emails are sent from +- `MAIL_FROM_NAME`: the name of the sender +- `MAIL_REPLY_TO`: the email address replies should be sent to + +The emails are sent by `user-permissions` Strapi plugin and can be configured separately via Strapi UI in `Settings > Users & Permissions plugin > Email Templates`. + +You can use a local instance of AWS SES via [LocalStack](https://docs.localstack.cloud/user-guide/aws/ses/) for development. To enforce the use of LocalStack set `LOCALSTACK_ENDPOINT` to `http://localhost.localstack.cloud:4566` in `.env` file. You could use the project's Docker compose setup to spin up `awslocal` service or install and run it [yourself](https://docs.localstack.cloud/getting-started/installation/). The LocalStack's AWS SES mailbox can be checked at [http://localhost:4566/\_aws/ses](http://localhost:4566/_aws/ses), where you'll find any emails sent by Strapi. diff --git a/docs/src/routes/(content)/developers-guide/configuration/app-customization/+page.md b/docs/src/routes/(content)/developers-guide/configuration/app-customization/+page.md new file mode 100644 index 000000000..7e00de187 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/configuration/app-customization/+page.md @@ -0,0 +1,15 @@ +# App Customization + +App customization includes settings that do not affect the functionality of the app, such as, publisher logo, front page image, translation overrides and frequently asked questions of the candidate app. App customization has been currently implemented only in Strapi. + +Translations from `dynamic.json` are preloaded into Strapi if the app customization collection is empty. In addition to changing the dynamic translations, any other translation can be overridden. + +## Adding New Customization Options + +1. Add the new option to `AppCustomization` type in [global.d.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/types/global.d.ts). +2. Add the new option to the `App Customization` content type in Strapi. +3. If the new setting is a relation, media field or a component: + 1. Edit the populate restrictions for the [app-customization route](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/api/app-customization/routes/app-customization.ts). + 2. Add the necessary `populate` query params to the `getAppCustomization` method in [strapiDataProvider.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/adapters/strapi/dataProvider/strapiDataProvider.ts). +4. Possibly edit how the data is preprocessed by the [app-customization route](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/api/app-customization/routes/app-customization.ts). +5. Update the Strapi data types for `StrapiAppCustomizationData` in [strapiData.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/adapters/strapi/strapiData.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/configuration/app-settings/+page.md b/docs/src/routes/(content)/developers-guide/configuration/app-settings/+page.md new file mode 100644 index 000000000..33802d5a5 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/configuration/app-settings/+page.md @@ -0,0 +1,35 @@ +# App Settings + +> This section deals with adding new App Settings. For information about existing ones, see the [Publishers’ Guide](/publishers-guide/app-settings). + +App settings are located in [`@openvaa/app-shared`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/) module. Settings are separated into static and dynamic settings. + +Static settings can be changed only by modifying [staticSettings.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.ts). + +Dynamic settings can be changed by modifying [dynamicSettings.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/dynamicSettings.ts). In addition, dynamic settings can also be changed in the backend. This has been currently implemented only in Strapi. Settings from `dynamicSettings.ts` are loaded into Strapi if the app settings collection is empty. + +Because the settings files are imported from the `app-shared` module, make sure to [watch it for changes and reload the frontend](/developers-guide/development/running-the-development-environment) when developing. + +Settings from `dynamicSettings.ts`, `staticSettings.ts` and from the DataProvider are merged together into [`settings` store](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/stores/stores.ts). Settings from `dynamicSettings.ts` are overwritten by dynamic settings from the DataProvider. Settings from `staticSettings.ts` are merged last to prevent overwriting them. + +## Adding New Settings + +In case of static settings: + +1. Add the type and documentation for the new setting to the `StaticSettings` type in [staticSettings.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.type.ts). +2. Add the default value for the setting to [staticSettings.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.ts). + +In case of dynamic settings: + +1. Add the type and documentation for the new setting to the `DynamicSettings` type in [dynamicSettings.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/dynamicSettings.type.ts). +2. Add the default value for the setting to [dynamicSettings.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/dynamicSettings.ts). +3. Edit the settings components in Strapi: + 1. If the new setting is a top-level one, create a new component for the setting and add it to the `App Settings` content-type. + 2. If the new setting is a subsetting of a top-level item, edit that setting. +4. Possibly update the [`app-settings` route controller](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/api/app-setting/controllers/app-setting.ts) or the utilities it uses, e.g., for [`cardContents`](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/functions/utils/appSettings.ts). +5. Add the necessary `populate` query params to the `getAppSettings` method in [strapiDataProvider.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/adapters/strapi/dataProvider/strapiDataProvider.ts), because components need to be explicitly populated. Note that if the components have subcomponents, you need to explicitly populate all the way down. +6. If the data type for the setting does not match the one in the `DynamicSettings` type: + 1. Update the Strapi data types for `StrapiAppSettingsData` in [strapiData.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/adapters/strapi/strapiData.type.ts). + 2. Edit the `getAppSettings` method in [strapiDataProvider.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/adapters/strapi/dataProvider/strapiDataProvider.ts) so that it returns the setting in the correct format. + 3. Edit the [loadDefaultAppSettings](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/src/functions/loadDefaultAppSettings.ts) utility so that it converts the default settings to a format suitable for Strapi. +7. Repeat applicable steps for all other `DataProvider` implementations that support `getAppSettings`. diff --git a/docs/src/routes/(content)/developers-guide/configuration/environmental-variables/+page.md b/docs/src/routes/(content)/developers-guide/configuration/environmental-variables/+page.md new file mode 100644 index 000000000..b5b970008 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/configuration/environmental-variables/+page.md @@ -0,0 +1,18 @@ +# Environmental variables + +In addition to basic configuration, some application functions are controlled by env variables. They affect: + +- Backend + - Strapi configuration + - AWS LocalStack (development only) + - AWS SES email + - AWS S3 storage + - [Mock data generation](/developers-guide/backend/mock-data-generation) +- Frontend configuration + - Disk cache + - Local data adapter + - Pregistration + - LLM API keys, used by the Admin App +- Debugging + +For a full list of all the variables and their explanations see [.env.example](https://github.com/OpenVAA/voting-advice-application/blob/main/.env.example). diff --git a/docs/src/routes/(content)/developers-guide/configuration/intro/+page.md b/docs/src/routes/(content)/developers-guide/configuration/intro/+page.md new file mode 100644 index 000000000..32bf40038 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/configuration/intro/+page.md @@ -0,0 +1,8 @@ +# Configuration + +The application configuration is split into three parts: + +- [Environmental variables](/developers-guide/configuration/environmental-variables) +- [Static settings](/developers-guide/configuration/static-settings) +- [Dynamic settings](/developers-guide/configuration/app-settings) +- [App Customization](/developers-guide/configuration/app-customization) diff --git a/docs/src/routes/(content)/developers-guide/configuration/static-settings/+page.md b/docs/src/routes/(content)/developers-guide/configuration/static-settings/+page.md new file mode 100644 index 000000000..296d5c496 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/configuration/static-settings/+page.md @@ -0,0 +1,16 @@ +# Static settings + +> Some of the static settings, especially fonts and colors, are planned to be moved to dynamic settings and it’s possible that the rest will be merged with `env` variables thereafter. + +Statics settings are set by editing [staticSettings.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.ts) and they cannot be changed after initialisation. They contain: + +- Admin settings, including admin email +- Settings related to the version of the app, handling of saved user data and linking to the source code +- Settings defining the data adapters to use, which may be a database interface or one using local files +- The main colors used by the application, defined separately for both the light and dark themes +- The main font used in the application +- The locales supported by the application +- Settings related to data collection and other research or analytics use +- Settings related to Candidate App pre-registration + +For a full list of all the settings, see [staticSettings.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.type.ts). diff --git a/docs/src/routes/(content)/developers-guide/contributing/ai-agents/+page.md b/docs/src/routes/(content)/developers-guide/contributing/ai-agents/+page.md new file mode 100644 index 000000000..4b48ae488 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/contributing/ai-agents/+page.md @@ -0,0 +1,5 @@ +# AI agents + +Agent information is currently only provided for Claude in [CLAUDE.md](https://github.com/OpenVAA/voting-advice-application/blob/main/CLAUDE.md), which can be used as a base for other agents’ instructions. + +There are Github workflows for code review, issue solving and generic tasks for Claude. These are triggered by commenting on PRs or issues with `@claude, review`, `@claude, solve` or `@claude [generic prompt]`. The author of the issue or comment must be a repository member with write access. diff --git a/docs/src/routes/(content)/developers-guide/contributing/code-style-guide/+page.md b/docs/src/routes/(content)/developers-guide/contributing/code-style-guide/+page.md new file mode 100644 index 000000000..27b99082f --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/contributing/code-style-guide/+page.md @@ -0,0 +1,244 @@ +# Code style guide + +In general, Prettier formats the code on the surface in a nice way, but there are other requirements that you must take care of manually. + +### Principles + +#### Don't Repeat Yourself + +Check that no code is repeated if the same functionality is already implemented elsewhere. Be careful to check at least the following paths and modules for possible general use components, classes and utilities: + +- `$lib/api` +- `$lib/components` +- `$lib/dynamic-components` +- `$lib/contexts` +- `$lib/utils` +- `@openvaa/app-shared` +- `@openvaa/core` +- `@openvaa/data` +- `@openvaa/filters` +- `@openvaa/matching` + +If some existing code doesn’t do exactly the thing you want, consider extending the existing code instead of copy-pasting more than a few lines of code. + +#### Top-Down Organization + +Check that files with hierarchical functions are organized top-down such that the main (exported) function is first and its subfunctions come after. Preferably even split the functions into their own files and import them into the main function. + +Thus, a complex file should read like this (or with `foo` and `bar` imported from their own files): + +```ts +export function longProcess() { + const result = foo(); + if (!result) return undefined; + return bar(result); +} + +function foo() { + // Code here +} + +function bar(result) { + // Code here +} +``` + +### Comments + +Add comments to all exported variables as well as the properties of exported object, methods of classes and items of union types. + +Try to make the code understandable by itself, but if you suspect the program logic might be unclear to others, rather add comments than leave them out. + +Do not manually break comments into lines of a certain length unless separating paragraphs. This enables developers to use line-wrapping based on their own preference without adding unneccessary lines to the code. + +#### TSDoc + +In Typescript, use [TSDoc comments](https://tsdoc.org/) for all documentation unless you're only adding remarks concering the program flow, e.g.: + +```ts +/** + * Sum the inputs. + * @param a - The first addend. + * @param b - The second addend. + * @returns the sum of the addends. + */ +export function sum(a: number, b: number): number { + // Add the numbers together + return a + b; +} +``` + +### TypeScript + +We follow the conventions of [TypeScript Style Guide](https://mkosir.github.io/typescript-style-guide/) with the following exception: + +- [The naming conventions](https://mkosir.github.io/typescript-style-guide/#variables-1) for booleans are optional but if possible should be adhered to. + +Common errors, which will be flagged, include: + +- `Array<Foo>` must be used instead of `Foo[]` +- Type parameters cannot be single letters: `type Foo<TBar> = ...` instead of `type Foo<T>`. + +#### `any` and `unknown` + +Avoid using `any` at all costs. If there is no way to circumvent using it, document the reason carefully and consider using `@ts-expect-error` instead. + +Also avoid `unknown` unless it is genuinely appropriate for the context like, e.g., in callback functions whose return values have no effect on the caller. + +#### Function parameters + +> This requirement is not flagged by automatic checks. + +To avoid bugs, try to always use named parameters to functions and methods, when there is any risk of confusion, i.e., in most cases where the functions expects more than one parameter. + +```ts +// NOT like this +function confused(foo: string, bar: string, baz = 'BAZ') { + // Do smth +} +// YES like this +function unConfused({ foo, bar, baz = 'BAZ' }: { foo: string; bar: string; baz?: string }) { + // Do smth +} +``` + +To make things smooth, try to use the same names for parameter across the board, so they can be destructured and passed as is, e.g. + +```typescript +const { foo } = getFoo(); +const { bar } = getBar(); +foobar({ foo, bar }); // Instead of foobar({ foo: foo, bar: bar }) +function foobar({ foo, bar }: { foo: string; bar: string }) { + // Do smthx +} +``` + +#### File organization + +Try to separate pure type files from the functional ones and keep them next to each other, as well as tests. Do not usually collect these into separate folders. E.g. + +- `foo.ts`: The file to compile +- `foo.type.ts`: Related types and types only +- `foo.test.ts`: The unit tests + +### CSS + +Use Tailwind for styling. + +See the [frontend styling guide](/developers-guide/frontend/styling) for information about using Tailwind classes. + +### Svelte components + +> The frontend currently uses Svelte 4. An update to Svelte 5 is scheduled for H1/2026. + +#### File structure + +Put each component in its own folder in `$lib/components`, or `$lib/dynamic-components` in case of [dynamic components](/developers-guide/frontend/components). Multiple components that are integrally tied together may be included in the same folder (but see note below on exports). Separate the type definitions in a `.type.ts` file and provide an `index.ts` for easy imports. Thus, the `$lib/components/myComponent` folder would have the files: + +- `MyComponent.svelte`: the component itself +- `MyComponent.type.ts`: the type definitions for the component's properties +- `index.ts`: provides shortcuts to imports: + ```ts + export {default as MyComponent} from './MyComponent.svelte; + export * from './MyComponent.type; + ``` + +**NB.** All components exported from the `index.ts` file, will be loaded even when only one of them imported in the application, so place multiple components in the same folder tree only when it's absolutely necessary. + +#### Component properties + +Currently, most components use attribute forwarding with [Svelte's `$$restProps` variable](https://svelte.dev/docs/basic-markup#attributes-and-props). This means that any HTML or SVG attributes that the main element of the component accepts can be passed as the components properties – or, in case of a component derived from another Svelte component, the parent components properties. This is most commonly used for passing extra classes to the element. + +For example, in the `HeroEmoji` component additional CSS classes as well as any arbitraty properties of the `<div>` element can be passed to the `<div>` surrounding the emoji. + +```ts +// HeroEmoji.type.ts +import type { SvelteHTMLElements } from 'svelte/elements'; +export type HeroEmojiProps = SvelteHTMLElements['div'] & { + /** + * The emoji to use. Note that all non-emoji characters will be removed. If `undefined` the component will not be rendered at all. @default `undefined` + */ + emoji?: string; +}; +``` + +```tsx +// HeroEmoji.svelte +<script lang="ts"> + import { concatClass } from '$lib/utils/components'; + import type { HeroEmojiProps } from './HeroEmoji.type'; + + type $$Props = HeroEmojiProps; + + export let emoji: $$Props['emoji'] = undefined; +</script> + +{#if emoji} + <div + aria-hidden="true" + role="img" + style="font-variant-emoji: emoji;" + {...concatClass( + $$restProps, + 'whitespace-nowrap truncate text-clip text-center font-emoji text-[6.5rem] leading-[1.1]' + )}> + {emoji} + </div> +{/if} +``` + +Also see the other existing components for more details on how this is done. + +##### Default values for properties included `$$restProps` + +In most cases, default values for properties included in `$$restProps`, such as `aria-hidden` can be just added as attributes in the relevant element or component. The only thing to keep in mind is that they must precede `$$restProps`, otherwise they will override the values in it. For example: + +```tsx +<div aria-label="Default label" {...$$restProps}> + ... +</div> +``` + +However, if you want to concatenate values with properties in `$$restProps`, such as concatenating a default `class` string with one possibly defined in `$$restProps`, this should be added after `{...$$restProps}`. To make this easier, a `concatClass` helper function is provided in [`$lib/utils/components`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/utils/components.ts). For example: + +```tsx +<div {...concatClass($$restProps, 'default-class')}>...</div> +``` + +##### Aria attributes and the `class` attribute + +Note that you most Aria attributes cannot be exposed with `let export foo` because their names contain dashes, which also applies to the HTML `class` attribute. In order to access these, either use the `$restProps` object or specify them in the properties type the component uses, i.e., the one assigned to `type $$Props` and access them via `$$props`. For example: + +```tsx +// Foo.type.ts +export type FooProps = SvelteHTMLElements['p'] & { + 'aria-roledescription'?: string | null; + class?: string | null; +}; + +// Foo.svelte: <script> +type $$Props = FooProps; +let ariaDesc: $$Props['aria-roledescription'] = $$props['aria-roledescription']; +let className: $$Props['class'] = $$props['class']; +``` + +#### Component documentation + +Follow Svelte's [guidelines for component documentation](https://svelte.dev/docs/faq#how-do-i-document-my-components). For an example, see [`IconBase`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/icon/base/IconBase.svelte) component and its associated [type definition](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/icon/base/IconBase.type.ts). + +Place the Svelte docstring at the top of the file, before the `<script>` block. The documentation must consist of: + +- general description +- 'Properties' (see below) +- 'Slots' detailing all slots and their uses (if applicable) +- 'Usage' showing a concise code block of the component’s use + +The type file defining the properties is the prime source of truth for the properties’ descriptions. Make sure the "Properties" section of the component doc string matches that. + +Add documentation for pages, layouts and other non-reusable components, detailing their main purpose. Include in their documentation under separate subheadings: + +- `Settings` affecting the page +- Route and query `Params` affecting the behaviour +- `Tracking events` initiated by the page + +It is not necessary to duplicate the documentation of the individual properties in the doc string of the `.svelte` file, because the properties should have their explanations directly in the interface definition in the `.type.ts` file (using [`/** ... */` TSDoc comments](https://tsdoc.org/)). The component's possible slots should, however, be included in the doc string. diff --git a/docs/src/routes/(content)/developers-guide/contributing/contribute/+page.md b/docs/src/routes/(content)/developers-guide/contributing/contribute/+page.md new file mode 100644 index 000000000..a97c61b2b --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/contributing/contribute/+page.md @@ -0,0 +1,40 @@ +# Contribute + +If you want to make changes to the project, you must follow the following steps. + +1. Clone the repository +2. Create a new branch with a descriptive yet short name. For example, `fix-404-page` or `add-privacy-policy-page`. +3. Once you start adding changes, make sure you split your work into small, meaningful, manageable commits. + +### Commit your update + +Commit the changes once you are happy with them. Try to keep commits small and to not mix unrelated changes in one commit. + +Don't add any editor config files, such as the `.vscode` folder, to your commit. These are not included in the project's `.gitignore` file but you can [add them to a global `.gitignore`](https://blog.martinhujer.cz/dont-put-idea-vscode-directories-to-projects-gitignore/) on your own machine. + +The commit message should follow the [conventional commits conventions](https://www.conventionalcommits.org/en/v1.0.0/). Use the `refactor:` prefix for changes that only affect styling. + +For commits that affect packages other than the frontend, add the package name (without the `@openvaa/` scope) to the commit prefix in brackets, e.g.: + +- `refactor[data]: doo foo` +- `refactor[q-info]: doo bar` (you can use the abbreviations `q-info` and `arg-cond` for `question-info` and `argument-condensation`) + +On top of that, the commit message should follow the following rules: + +- Commit messages must have a subject line and may have a body. A blank line must separate the subject line and body. +- If possible, the subject line must not exceed 50 characters +- The subject line must not end in a period +- The body copy must be wrapped at 72 columns +- The body copy must only explain _what_ and _why_, never _how_. The latter belongs in documentation and implementation. + +After you're satisfied with your commits, clean up the commit history so that the commits make sense for others. The best way to accomplish this is to use the [fixup workflow](https://dev.to/koffeinfrei/the-git-fixup-workflow-386d) so that the commit history will contain only one commit for some feature instead of multiple ones with cumulative fixes, i.e., your PR’s commit history should finally look like: + +- `feat: NewComponent` + +Instead of: + +- `feat: NewComponent` +- `fix: something in NewComponent` +- `fix: something else in NewComponent` + +Once your changes are ready, make sure you have followed all the steps in the [PR Review Checklist](/developers-guide/contributing/pull-request/#self-review). diff --git a/docs/src/routes/(content)/developers-guide/contributing/issues/+page.md b/docs/src/routes/(content)/developers-guide/contributing/issues/+page.md new file mode 100644 index 000000000..9e4d5e545 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/contributing/issues/+page.md @@ -0,0 +1,36 @@ +# Issues + +### Create a new issue + +If you detect a problem with the application or have a new request, search first in the list of issues to see whether or +not a similar issue has already been reported by someone else. If the issue doesn't exist, you can open a new one by +following these steps. + +1. Add a descriptive title. +2. Add a descriptive description. +3. Assign the issue to the Voting Advice Application project. +4. [Add labels](#issue-labels). + +### Search for an issue + +Scan through our [existing issues](https://github.com/OpenVAA/voting-advice-application/issues). You can +narrow down the search using `labels` as filters. + +If you find an issue to work on, you are welcome to open a PR with a fix. + +### Issue labels + +Labels can help you find an issue you'd like to help with. On the other hand, labels allow us to manage the project and categorize the information in a more meaningful way. + +Currently, the labels divide into four categories: + +- Category: [CATEGORY] => This category lets us know what category the issue belongs to. Fox example, if the issue contains only security related changes, the label will be `category: security`. +- Status: [STATE of ISSUE] => This category lets us know what state the issue is in right now. +- Type: [TYPE OF ISSUE] => This category lets us know what type of issue this is. For example, if the issue is a bug, the label will be `type: bug`, or if the issue is a new feature, the label will be `type: feature`. +- Scope: [SCOPE] => This category describes the scope of this issue. For example, suppose that the issue involves the voter side of the app. Then the appropriate label would be `scope: voter`. + +Do not add more than one label from each category in each issue or pull request. + +It is imperative to add labels to the new issues. If you are not sure which labels to add, you can always ask for help from the team. + +Additionally, please use Milestones to indicate which release target and scope contribution belongs to (for example: Alpha version, Beta version, Post-launch bug fixing patch, etc.). diff --git a/docs/src/routes/(content)/developers-guide/contributing/pull-request/+page.md b/docs/src/routes/(content)/developers-guide/contributing/pull-request/+page.md new file mode 100644 index 000000000..724129c70 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/contributing/pull-request/+page.md @@ -0,0 +1,40 @@ +# Pull Request + +When you're done with the changes, create a pull request known as a PR. + +- Make sure that your commits pass the validation workflows, are able to run the [tests](/developers-guide/development/testing), and build the application. +- Make sure you have followed all the steps in the [PR Review Checklist](/developers-guide/contributing/pull-request/#self-review). +- Fill in the pull requested template. Mark your PR as a draft if you're still working on it. +- Don't forget to [link PR to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one. +- When you're satisfied with the PR, mark it as ready for review, and a team member will review it. The team may ask questions or request changes to your PR. Either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. +- As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations). +- While the review process is ongoing, do not force push changes to the branch but make the changes in new [fixup](https://dev.to/koffeinfrei/the-git-fixup-workflow-386d) commits. Only when the PR is otherwise approved, squash the commits and `push --force-with-lease`. + +### Your PR is ready to be merged! + +Once all the changes have been approved, the reviewers may still ask you to clean the git history before merging the changes into the main branch of the project. + +### Self-review + +You should always review your own PR first before asking someone to review it. Below you can find a checklist of things you should check before submitting your PR. + +- [ ] Confirm that the changes solve the issues the PR is trying to solve partially or fully. +- [ ] Review the code in terms of the [OWASP top 10 security issues](https://owasp.org/Top10/). +- [ ] Verify that the code follows the [Code style guide](/developers-guide/contributing/code-style-guide). +- [ ] Avoid using `any` at all costs. If there is no way to circumvent using it, document the reason carefully and consider using `@ts-expect-error` instead. +- [ ] There is no code that is repeated within the PR or elsewhere in the repo. +- [ ] All new components, functions and other entities are documented +- [ ] The repo documentation markdown files are updated if the changes touch upon those. +- [ ] If the change adds functions available to the user, tracking events are enabled with new ones defined if needed. +- [ ] Any new Svelte components that have been created, follow the [Svelte component guidelines](/developers-guide/contributing/code-style-guide/#svelte-components). +- [ ] Errors are handled properly and logged in the code. +- [ ] Run the unit tests successfully. +- [ ] Run the e2e tests successfully. +- [ ] Troubleshoot any failing checks in the PR. +- [ ] Test the change thoroughly on your own device, including parts that may have been affected via shared code. +- [ ] Check that parts of the application that share dependencies with the PR but are not included in it are not unduly affected. +- [ ] Test the changes using the [WAVE extension](https://wave.webaim.org/extension/) for accessibility. +- [ ] The changes pass the [WGAC A and AA requirements for accessibility](https://usability.yale.edu/web-accessibility/articles/wcag2-checklist). +- [ ] Test the changes using keyboard navigation and screen-reading. +- [ ] Documentation is added wherever necessary. +- [ ] The commit history is clean and linear, and the commits follow the [commit guidelines](/developers-guide/contributing/contribute/#commit-your-update) diff --git a/docs/src/routes/(content)/developers-guide/contributing/recommended-ide-settings-code/+page.md b/docs/src/routes/(content)/developers-guide/contributing/recommended-ide-settings-code/+page.md new file mode 100644 index 000000000..b38306c21 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/contributing/recommended-ide-settings-code/+page.md @@ -0,0 +1,22 @@ +# Recommended IDE settings (Code) + +Plugins: + +- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) +- [Pretter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) +- [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) +- [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) + +We also recommend Git Graph for easier branch management: + +- [Git Graph v3](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + +Settings: + +```json +"editor.defaultFormatter": "esbenp.prettier-vscode", +"editor.formatOnSave": true, +"editor.codeActionsOnSave": { + "source.fixAll": "explicit", +}, +``` diff --git a/docs/src/routes/(content)/developers-guide/contributing/workflows/+page.md b/docs/src/routes/(content)/developers-guide/contributing/workflows/+page.md new file mode 100644 index 000000000..65307b6d8 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/contributing/workflows/+page.md @@ -0,0 +1,3 @@ +# Workflows + +The project uses GitHub Actions among other things to verify each commit passes unit tests, is able to build the app successfully and adheres to the [coding conventions used by the project](/developers-guide/contributing/code-style-guide). If a commit fails the verification, please check your changes from the logs and fix changes before submitting a review request. diff --git a/docs/deployment.md b/docs/src/routes/(content)/developers-guide/deployment/+page.md similarity index 95% rename from docs/deployment.md rename to docs/src/routes/(content)/developers-guide/deployment/+page.md index c71ddeccb..e098a69e3 100644 --- a/docs/deployment.md +++ b/docs/src/routes/(content)/developers-guide/deployment/+page.md @@ -22,12 +22,12 @@ The instructions below detail how the application is deployed using [Render](htt Fork the repo and make any changes you need to the source code. You’ll most likely need to edit at least: -- [StaticSettings](/packages/app-shared/src/settings/staticSettings.ts) -- For local development, copy [.env.example](/.env.example) to `.env` and edit the variables therein. +- [StaticSettings](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.ts) +- For local development, copy `.env.example` to `.env` and edit the variables therein. ### 2. Configure AWS -The backend uses AWS by default for media storage (S3) and email (SES). If you do not wish to use AWS, you will need to edit the [Strapi plugin config](/backend/vaa-strapi/config/plugins.ts). +The backend uses AWS by default for media storage (S3) and email (SES). If you do not wish to use AWS, you will need to edit the [Strapi plugin config](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/config/plugins.ts). You will need to set the following `env` variables for AWS to work. You can collect the variables in an `.env` file for easier import into Render services, which are set up below. @@ -64,7 +64,7 @@ STATIC_MEDIA_CONTENT_PATH=public/media Login to Render or create an account. -You can either use the [Render Blueprints](https://render.com/docs/infrastructure-as-code) feature by modifying the [render.example.yaml](/render.example.yaml) or create the services manually. +You can either use the [Render Blueprints](https://render.com/docs/infrastructure-as-code) feature by modifying the [render.example.yaml](https://github.com/OpenVAA/voting-advice-application/blob/main/render.example.yaml) or create the services manually. If you choose the blueprints option, you can skip to [Step 9](#9-use-your-own-domain-name-for-the-frontend). diff --git a/docs/src/routes/(content)/developers-guide/development/intro/+page.md b/docs/src/routes/(content)/developers-guide/development/intro/+page.md new file mode 100644 index 000000000..0d8b0227a --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/development/intro/+page.md @@ -0,0 +1,3 @@ +# Docker + +You can run the whole application in Docker containers (frontend, backend, postgres database,and AWS local stack for developing), or run the frontend or backend separately depending on your preferences. Using the Docker image is recommended and the quickest way to set up the application. This guide assumes you’re using Docker. diff --git a/docs/src/routes/(content)/developers-guide/development/monorepo/+page.md b/docs/src/routes/(content)/developers-guide/development/monorepo/+page.md new file mode 100644 index 000000000..74cdcb5fb --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/development/monorepo/+page.md @@ -0,0 +1,54 @@ +# Monorepo + +All workspaces share a single `yarn.lock` file located at the project root but contain their own `tsconfig.json` and `package.json` files. + +The workspaces can be addressed by yarn from any directory as follows: + +```bash +yarn workspace [module-name] [script-name]. +``` + +E.g., the `app-shared` module can be built by running: + +```bash +yarn workspace @openvaa/app-shared build +``` + +In order to install dependencies for all modules and build all modules (although, you’d rarely want to this) run: + +```bash +yarn install +yarn workspaces foreach -A build +``` + +When adding interdependencies between the modules, use yarn’s `workspace:` syntax: + +```json + "dependencies": { + "@openvaa/core": "workspace:^" + } +``` + +Also add a reference to the package’s `tsconfig.json` file (see more in [Module resolution](#module-resolution)): + +```json + "references": [{ "path": "../core/tsconfig.json" }] +``` + +The root [`package.json`](https://github.com/OpenVAA/voting-advice-application/blob/main/package.json) contains scipts for many repo-wide tasks. + +### Module resolution + +#### IDE + +In order to resolve cross `import`s between the monorepo modules Code uses TypeScript references, which are defined in the `tsconfig.json` files of the corresponding modules. + +In other words, you DO NOT have to build the **dependee** modules in order for the IDE to resolve their `import`s within a **dependent** module or to pick up changes you make in the **dependee’s** `.ts` sources. + +#### NPM/Node + +When you use Yarn and during runtime NPM/Node module resolution mechanism is used instead. It relies on various pointers defined in `package.json` files of the corresponding modules (e.g. `main`, `module` or `exports`). These pointers usually refer to `build`/`dist` directory containing already transpiled TS sources of a given module (`.js` files). This directory subsequently gets symlinked by `yarn install` in a `node_modules` directory of a **dependent** module. + +In other words, you DO have to build the **dependee** modules prior to running a **dependent** module or using Yarn on it, so that NPM/Node can find the transpiled `.js` sources and pick up changes you make in the original `.ts` code. + +The `yarn dev` script automatically watches the packages for changes. If there are some, they will be rerebuilt and the frontend restarted to reflect the changes. diff --git a/docs/src/routes/(content)/developers-guide/development/requirements/+page.md b/docs/src/routes/(content)/developers-guide/development/requirements/+page.md new file mode 100644 index 000000000..995195633 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/development/requirements/+page.md @@ -0,0 +1,7 @@ +# Requirements + +- Yarn 4 +- Docker (unless you plan to run the app outside of Docker) +- Node.js. Use the version specified under `engines` in the root [package.json](https://github.com/OpenVAA/voting-advice-application/blob/main/package.json). Use of nvm is encouraged for Node version management +- Ports 1337, 5173 and 5432 should be free for the application if using default settings + - These ports can be changed in the `.env` file if desired. diff --git a/docs/src/routes/(content)/developers-guide/development/running-the-development-environment/+page.md b/docs/src/routes/(content)/developers-guide/development/running-the-development-environment/+page.md new file mode 100644 index 000000000..384ca089e --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/development/running-the-development-environment/+page.md @@ -0,0 +1,32 @@ +# Running the Development Environment + +Make sure that you’re using the recommended Node version (see [Requirements](/developers-guide/development/requirements)) by running `node -v`. If needed set the correct version with `nvm use <VERSION>`. + +First, install dependencies for all workspaces: + +```bash +yarn install +``` + +To build and run development Docker images for the entire stack (frontend, backend and DB), in the project's root directory: + +- Make a copy of the `.env.example` file and rename the copy as `.env` +- Run `yarn dev` +- If you run into errors, try checking the tips related to Docker in [Troubleshooting](/developers-guide/troubleshooting). + +The `yarn dev` script will automatically build all the shared packages and start watching them for changes. If these change, they will be rerebuilt and the frontend restarted to reflect the changes. + +To bring down the Docker stack properly (delete all containers, images and named volumes which include backend DB volume with potentially seeded mock data) run: + +```bash +yarn dev:down +``` + +**When running the project in Docker, only use the `.env` file in project root. You usually +don't have to touch the separate .env files for frontend and backend.** + +If you want to seed backend DB with mock data (e.g. for demonstration, development or testing purposes purposes), please follow the instructions [here](/developers-guide/backend/mock-data-generation). + +### Hot Reloading the Backend + +Development Docker images will listen to changes in the files and allow hot reloading, meaning the Docker images don't need to be re-generated after making changes to the codebase. Hot reloading is enabled by default in the frontend, but for backend this can be enabled by adding the volume `- ./:/opt` as a mounted point in [docker-compose.dev.yml](https://github.com/OpenVAA/voting-advice-application/blob/main/backend/vaa-strapi/docker-compose.dev.yml) and re-building the Docker container. However, this can make the development process slow at times, so it is not recommended to keep that on unless doing direct development on the backend source code. diff --git a/docs/testing.md b/docs/src/routes/(content)/developers-guide/development/testing/+page.md similarity index 82% rename from docs/testing.md rename to docs/src/routes/(content)/developers-guide/development/testing/+page.md index a87507659..6aba5d621 100644 --- a/docs/testing.md +++ b/docs/src/routes/(content)/developers-guide/development/testing/+page.md @@ -18,7 +18,7 @@ yarn test:unit:watch The project uses Playwright for E2E testing. The tests rely on generated data which the local PosgresDB is being seeded with. If you need to run the tests repeatedly it's useful to set the `GENERATE_MOCK_DATA_ON_RESTART` to `true` and restart the backend between test runs. -All of the E2E tests are collected in the [`tests`](/tests) folder. To run the E2E tests start all the services locally: +All of the E2E tests are collected in the `/tests` folder. To run the E2E tests start all the services locally: ```bash yarn dev @@ -31,7 +31,7 @@ yarn playwright install yarn test:e2e ``` -If you encounter any unexpected issues with the E2E tests, make sure to bring down the Docker stack properly to reseed the DB with the original mock data (more on mock data [here](backend.md#mock-data-generation)). +If you encounter any unexpected issues with the E2E tests, make sure to bring down the Docker stack properly to reseed the DB with the original mock data (more on mock data [here](/developers-guide/backend/mock-data-generation)). To bring down the Docker stack properly (delete all containers, images and named volumes which include backend DB volume with potentially seeded mock data) run: diff --git a/docs/src/routes/(content)/developers-guide/frontend/accessing-data-and-state-management/+page.md b/docs/src/routes/(content)/developers-guide/frontend/accessing-data-and-state-management/+page.md new file mode 100644 index 000000000..7a435a5ef --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/accessing-data-and-state-management/+page.md @@ -0,0 +1,190 @@ +# Accessing data and state management + +The overall model for loading and writing data and managing the application state is as follows. + +1. External data is loaded (and written) using the [Data API](/developers-guide/frontend/data-api). The API is accessed by universal `load` functions in `+layout.ts` files, the `Context`s or some API routes. + - Depending on [settings](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.type.ts), either a Strapi backend is accessed or data is read from local `json` files. +2. The loaded data is passed to the `dataRoot` store accessible via the `DataContext` and converted into functional objects using the [`@openvaa/data` model](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/data/). + - All pages, some other [contexts](/developers-guide/frontend/contexts) and [dynamic components](/developers-guide/frontend/components) can access the `DataContext`. + - Some data in the Candidate App is contained in a `UserDataStore` instead of the `dataRoot` store. +3. All other shared stores are contained in [contexts](/developers-guide/frontend/contexts). + - Some contexts are globally available and some to only certain parts of the application. + +### Example: loading cascade for the `/(voter)/(located)/questions` route + +Below is description of the data loading, provision and context initialisation cascade for the `/(voter)/(located)/questions/+page.svelte` page, which displays an introduction to the questions and possible `QuestionCategory` selection. See the individual files for more details. + +- The `(located)` virtual route folder contains a layout that loads the `Entity`, `Nomination` and `QuestionData` that are relevant to the selected `electionId`s and `constituencyId`s, which are either stores in search parameters or implied, e.g. when there’s only on `Election`. + +The basic paradigm is (from top to bottom): + +- `+page.svelte` files access VAA data via the `dataRoot: Readable<DataRoot>` store contained in `DataContext` or derived stores using it. +- `+layout.svelte` files await for the data loaded by universal `load` functions and provide it to the `dataRoot` store. `DataRoot` converts the data into fully-fletched data objects with methods etc. +- `+layout.ts` universal loaders import a `DataProvider` from `$lib/api/dataProvider` and use it to get data as promises. +- `$lib/api/dataProvider` exports the correct `DataProvider` implementation based on the configuration. +- The specific `DataProvider` implementations may either + - directly access the database, or + - if they can only run on the server circulate the calls via the generic `ApiRouteDataProvider`—`/routes/api/data/[collection]/+server.ts`—`$lib/server/_api/serverDataProvider` chain, the last part of which exports the correct `ServerDataProvider` implementation. +- If the `PUBLIC_CACHE_ENABLED` env variable is set, the either adapters `fetch` requests are rerouted via the cache route (`/routes/api/cache/server.ts`). This is handled by the [`UniversalAdapter.fetch`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/base/universalAdapter.ts), which the providers use internally. + +The process is described in the flowchart below. + +```mermaid +--- +title: Data loading cascade for the /(voter)/(located)/questions route +--- +flowchart TD + +%% Block Definitions + +PAGE["Questions Intro Page +/routes/[lang]/(voters)/(located)/+page.svelte +Displays an introduction to the questions and possible QuestionCategory selection"]:::svelte + +LAYOUT_SV_LOC["Located Layout +/routes/[lang]/(voters)/(located)/+layout.svelte +Receives the promised data and provides it to dataRoot. +Shows an error if data is not available."]:::svelte + +LAYOUT_TS_LOC["Located Layout Loader +/routes/[lang]/(voters)/(located)/+layout.ts +Universally loads questionData and nominationData for the constituencyIds and electionIds in the search params. +If the params are not set or cannot be implied, redirects to the relevant selection pages."] + +LAYOUT_SV_VOT["Voter Layout +/routes/[lang]/(voters)/+layout.svelte +Receives the promised data and provides it to dataRoot. +Shows an error if data is not available. +Initiates the App and VoterContexts."]:::svelte + +LAYOUT_TS_VOT["Voter Layout Loader +/routes/[lang]/(voters)/+layout.ts +Universally loads electionData, constituencyData, appSettingsData and appCustomizationData."] + +LAYOUT_SV_ROOT["Outermost Layout +/routes/[lang]/+layout.svelte +Initializes the I18n, Data and ComponentContexts."]:::svelte + +CTX_VOTER["VoterContext +$lib/api/contexts/voter"]:::ctx + +CTX_APP["AppContext +$lib/api/contexts/app"]:::ctx + +CTX_COMP["ComponentContext +$lib/api/contexts/component"]:::ctx + +CTX_VAA["DataContext +$lib/api/contexts/vaaData"]:::ctx + +CTX_I18N["I18nContext +$lib/api/contexts/i18n"]:::ctx + +DP["DataProvider +$lib/api/dataProvider.ts +Imports the correct DataProvider implementation"] + +DP_STRAPI["$lib/api/adapters/strapi/provider/strapiDataProvider.ts +A specific implementation to connect to a Strapi backend"] + +DP_API["$lib/api/adapters/apiRoute/provider/apiRouteDataProvider.ts +A generic wrapper for all DataProvider implementations +that rely on the server and are, thus, accessible via the API routes"] + +UDA_STRAPI["UniversalDataProvider.fetch() +$lib/api/base/universalDataAdapter.ts +Wraps the fetch method used for requests, +possibly redirecting requests to the cache."] + +CACHE_STRAPI["/routes/api/cache/+server.ts"]:::ssr + +UDA_API["UniversalDataProvider.fetch() +$lib/api/base/universalDataAdapter.ts +Wraps the fetch method used for requests, +possibly redirecting requests to the cache."] + +CACHE_API["/routes/api/cache/+server.ts"]:::ssr + +DATA_API_ROUTE["/routes/api/data/[collection]/+server.ts +A generic API route (GET) request handler, +which loads the data using DataProvider<'server'>"]:::ssr + +%% Connections + +PAGE---|"getVoterContext"|CTX_VOTER +PAGE-.-|"In <slot> of"| LAYOUT_SV_LOC + +subgraph Layouts +LAYOUT_SV_LOC---|"export let data"| LAYOUT_TS_LOC +LAYOUT_SV_LOC-.-|"In <slot> of"| LAYOUT_SV_VOT +LAYOUT_SV_VOT---|"export let data"| LAYOUT_TS_VOT +LAYOUT_SV_VOT-.-|"In <slot> of"| LAYOUT_SV_ROOT +end + +subgraph Contexts +CTX_VOTER---|"includes"| CTX_APP +CTX_APP---|"includes"| CTX_VAA +CTX_APP---|"includes"| CTX_COMP +CTX_COMP---|"includes"| CTX_I18N +end + +%% CTX_VOTER-.-|"Initiated by"| LAYOUT_SV_VOT +LAYOUT_SV_VOT---|"$dataRoot.provideElectionData +$dataRoot.provideConstituencyData"| CTX_VAA +LAYOUT_SV_LOC---|"$dataRoot.provideQuestionData +$dataRoot.provideNominationData"| CTX_VAA +CTX_APP---|"Gets appSettingsData and appCustomizationData via $page.data from"| LAYOUT_TS_VOT +%% CTX_APP-.-|"Initiated by"| LAYOUT_SV_VOT +%% CTX_COMP-.-|"Initiated by"| LAYOUT_SV_ROOT +%% CTX_I18N-.-|"Initiated by"| LAYOUT_SV_ROOT + +LAYOUT_TS_LOC---|"getQuestionData +getNominationData"| DP +LAYOUT_TS_VOT---|"getAppSettingsData +getAppCustomizationData +getElectionData +getConstituencyData"| DP + +%% Data Provider roots + +subgraph DataProviders +DP +---|"import depending on appSettings"|DP_STRAPI +---|"_getElectionData"|UDA_STRAPI +---|"fetch()"|STRAPI["Strapi backend"]:::ssr; + +UDA_STRAPI +---|"if cache is enabled"|CACHE_STRAPI +---|"fetch() and cache"|STRAPI["Strapi backend"]:::ssr; + +DP +---|"import depending on appSettings"|DP_API +---|"_getElectionData"|UDA_API +---|"fetch()"|DATA_API_ROUTE +---|"getElectionData etc."|DP_SERVER["$lib/server/api/dataProvider.ts +Imports the correct DataProvider<'server'> implementation"]:::ssr +---|"import depending on appSettings"|DP_SERVER_LOCAL["$lib/server/api/adapters/local/provider/localServerDataProvider.ts +A specific implementation to read locally saved json files."]:::ssr +---|"_getCandidatesData etc. → read()"|JSON["${LOCAL_DATA_DIR}/candidates.json"]:::ssr; + +UDA_API +---|"if cache is enabled"|CACHE_API +---|"fetch() and cache"|DATA_API_ROUTE + + +end + + +subgraph Legend +L1["Svelte component"]:::svelte +L2["Context"]:::ctx +L3["SSR-only"]:::ssr +L4["Other module"] +end + +classDef ctx fill:#afa +classDef svelte fill:#aaf +classDef ssr fill:#faa +``` + +\* The AppContext and its associated data will be loaded by the outermost layout and in the future when the Candidate App is refactored. diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/+page.md new file mode 100644 index 000000000..3fcce2233 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/+page.md @@ -0,0 +1,11 @@ +# Components + +For information about component properties, typing and documentation, see [Svelte components](/developers-guide/contributing/code-style-guide/#svelte-components) in the Contributors' guide. + +## Dynamic and static components + +The components used in the app are divided into dynamic and static ones, contained in [$lib/components](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components) and [$lib/dynamic-components](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components), respectively. + +## Available components + +See the [auto-generated component documentation](/developers-guide/frontend/components/generated). diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/+page.md new file mode 100644 index 000000000..f33bce81f --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/+page.md @@ -0,0 +1,407 @@ +# Component Documentation + +This documentation is automatically generated from the `@component` docstrings in Svelte files. + +## Components by Category + +### Static Components + +- [AccordionSelect](/developers-guide/frontend/components/generated/components/accordionSelect/AccordionSelect) + + `import { AccordionSelect } from '$lib/components/accordionSelect';` + +- [Alert](/developers-guide/frontend/components/generated/components/alert/Alert) + + `import { Alert } from '$lib/components/alert';` + +- [Avatar](/developers-guide/frontend/components/generated/components/avatar/Avatar) + + `import { Avatar } from '$lib/components/avatar';` + +- [Button](/developers-guide/frontend/components/generated/components/button/Button) + + `import { Button } from '$lib/components/button';` + +- [ButtonWithConfirmation](/developers-guide/frontend/components/generated/components/buttonWithConfirmation/ButtonWithConfirmation) + + `import { ButtonWithConfirmation } from '$lib/components/buttonWithConfirmation';` + +- [CategoryTag](/developers-guide/frontend/components/generated/components/categoryTag/CategoryTag) + + `import { CategoryTag } from '$lib/components/categoryTag';` + +- [ConfirmationModal](/developers-guide/frontend/components/generated/components/modal/confirmation/ConfirmationModal) + + `import { ConfirmationModal } from '$lib/components/modal/confirmation';` + +- [ConstituencySelector](/developers-guide/frontend/components/generated/components/constituencySelector/ConstituencySelector) + + `import { ConstituencySelector } from '$lib/components/constituencySelector';` + +- [Drawer](/developers-guide/frontend/components/generated/components/modal/drawer/Drawer) + + `import { Drawer } from '$lib/components/modal/drawer';` + +- [ElectionSelector](/developers-guide/frontend/components/generated/components/electionSelector/ElectionSelector) + + `import { ElectionSelector } from '$lib/components/electionSelector';` + +- [ElectionSymbol](/developers-guide/frontend/components/generated/components/electionSymbol/ElectionSymbol) + + `import { ElectionSymbol } from '$lib/components/electionSymbol';` + +- [ElectionTag](/developers-guide/frontend/components/generated/components/electionTag/ElectionTag) + + `import { ElectionTag } from '$lib/components/electionTag';` + +- [EntityFilters](/developers-guide/frontend/components/generated/components/entityFilters/EntityFilters) + + `import { EntityFilters } from '$lib/components/entityFilters';` + +- [EntityTag](/developers-guide/frontend/components/generated/components/entityTag/EntityTag) + + `import { EntityTag } from '$lib/components/entityTag';` + +- [EnumeratedEntityFilter](/developers-guide/frontend/components/generated/components/entityFilters/enumerated/EnumeratedEntityFilter) + + `import { EnumeratedEntityFilter } from '$lib/components/entityFilters/enumerated';` + +- [ErrorMessage](/developers-guide/frontend/components/generated/components/errorMessage/ErrorMessage) + + `import { ErrorMessage } from '$lib/components/errorMessage';` + +- [Expander](/developers-guide/frontend/components/generated/components/expander/Expander) + + `import { Expander } from '$lib/components/expander';` + +- [HeadingGroup](/developers-guide/frontend/components/generated/components/headingGroup/HeadingGroup) + + `import { HeadingGroup } from '$lib/components/headingGroup';` + +- [Hero](/developers-guide/frontend/components/generated/components/hero/Hero) + + `import { Hero } from '$lib/components/hero';` + +- [HeroEmoji](/developers-guide/frontend/components/generated/components/heroEmoji/HeroEmoji) + + `import { HeroEmoji } from '$lib/components/heroEmoji';` + +- [Icon](/developers-guide/frontend/components/generated/components/icon/Icon) + + `import { Icon } from '$lib/components/icon';` + +- [Image](/developers-guide/frontend/components/generated/components/image/Image) + + `import { Image } from '$lib/components/image';` + +- [InfoAnswer](/developers-guide/frontend/components/generated/components/infoAnswer/InfoAnswer) + + `import { InfoAnswer } from '$lib/components/infoAnswer';` + +- [InfoBadge](/developers-guide/frontend/components/generated/components/infoBadge/InfoBadge) + + `import { InfoBadge } from '$lib/components/infoBadge';` + +- [InfoMessages](/developers-guide/frontend/components/generated/components/controller/InfoMessages) + + `import { InfoMessages } from '$lib/components/controller';` + +- [Input](/developers-guide/frontend/components/generated/components/input/Input) + + `import { Input } from '$lib/components/input';` + +- [InputGroup](/developers-guide/frontend/components/generated/components/input/InputGroup) + + `import { InputGroup } from '$lib/components/input';` + +- [Loading](/developers-guide/frontend/components/generated/components/loading/Loading) + + `import { Loading } from '$lib/components/loading';` + +- [MatchScore](/developers-guide/frontend/components/generated/components/matchScore/MatchScore) + + `import { MatchScore } from '$lib/components/matchScore';` + +- [Modal](/developers-guide/frontend/components/generated/components/modal/Modal) + + `import { Modal } from '$lib/components/modal';` + +- [ModalContainer](/developers-guide/frontend/components/generated/components/modal/ModalContainer) + + `import { ModalContainer } from '$lib/components/modal';` + +- [Notification](/developers-guide/frontend/components/generated/components/notification/Notification) + + `import { Notification } from '$lib/components/notification';` + +- [NumericEntityFilter](/developers-guide/frontend/components/generated/components/entityFilters/numeric/NumericEntityFilter) + + `import { NumericEntityFilter } from '$lib/components/entityFilters/numeric';` + +- [OpenVAALogo](/developers-guide/frontend/components/generated/components/openVAALogo/OpenVAALogo) + + `import { OpenVAALogo } from '$lib/components/openVAALogo';` + +- [OpinionQuestionInput](/developers-guide/frontend/components/generated/components/questions/OpinionQuestionInput) + + `import { OpinionQuestionInput } from '$lib/components/questions';` + +- [PreHeading](/developers-guide/frontend/components/generated/components/headingGroup/PreHeading) + + `import { PreHeading } from '$lib/components/headingGroup';` + +- [PreventNavigation](/developers-guide/frontend/components/generated/components/preventNavigation/PreventNavigation) + + `import { PreventNavigation } from '$lib/components/preventNavigation';` + +- [PreviewAllIcons](/developers-guide/frontend/components/generated/components/icon/PreviewAllIcons) + + `import { PreviewAllIcons } from '$lib/components/icon';` + +- [PreviewAllInputs](/developers-guide/frontend/components/generated/components/input/PreviewAllInputs) + + `import { PreviewAllInputs } from '$lib/components/input';` + +- [ProgressBar](/developers-guide/frontend/components/generated/components/controller/ProgressBar) + + `import { ProgressBar } from '$lib/components/controller';` + +- [QuestionActions](/developers-guide/frontend/components/generated/components/questions/QuestionActions) + + `import { QuestionActions } from '$lib/components/questions';` + +- [QuestionBasicInfo](/developers-guide/frontend/components/generated/components/questions/QuestionBasicInfo) + + `import { QuestionBasicInfo } from '$lib/components/questions';` + +- [QuestionChoices](/developers-guide/frontend/components/generated/components/questions/QuestionChoices) + + `import { QuestionChoices } from '$lib/components/questions';` + +- [QuestionExtendedInfo](/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfo) + + `import { QuestionExtendedInfo } from '$lib/components/questions';` + +- [QuestionExtendedInfoButton](/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfoButton) + + `import { QuestionExtendedInfoButton } from '$lib/components/questions';` + +- [QuestionExtendedInfoDrawer](/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfoDrawer) + + `import { QuestionExtendedInfoDrawer } from '$lib/components/questions';` + +- [QuestionInput](/developers-guide/frontend/components/generated/components/input/QuestionInput) + + `import { QuestionInput } from '$lib/components/input';` + +- [QuestionOpenAnswer](/developers-guide/frontend/components/generated/components/questions/QuestionOpenAnswer) + + `import { QuestionOpenAnswer } from '$lib/components/questions';` + +- [ScoreGauge](/developers-guide/frontend/components/generated/components/scoreGauge/ScoreGauge) + + `import { ScoreGauge } from '$lib/components/scoreGauge';` + +- [Select](/developers-guide/frontend/components/generated/components/select/Select) + + `import { Select } from '$lib/components/select';` + +- [SingleGroupConstituencySelector](/developers-guide/frontend/components/generated/components/constituencySelector/SingleGroupConstituencySelector) + + `import { SingleGroupConstituencySelector } from '$lib/components/constituencySelector';` + +- [SubMatches](/developers-guide/frontend/components/generated/components/subMatches/SubMatches) + + `import { SubMatches } from '$lib/components/subMatches';` + +- [SuccessMessage](/developers-guide/frontend/components/generated/components/successMessage/SuccessMessage) + + `import { SuccessMessage } from '$lib/components/successMessage';` + +- [Tabs](/developers-guide/frontend/components/generated/components/tabs/Tabs) + + `import { Tabs } from '$lib/components/tabs';` + +- [Term](/developers-guide/frontend/components/generated/components/term/Term) + + `import { Term } from '$lib/components/term';` + +- [TextEntityFilter](/developers-guide/frontend/components/generated/components/entityFilters/text/TextEntityFilter) + + `import { TextEntityFilter } from '$lib/components/entityFilters/text';` + +- [TimedModal](/developers-guide/frontend/components/generated/components/modal/timed/TimedModal) + + `import { TimedModal } from '$lib/components/modal/timed';` + +- [Toggle](/developers-guide/frontend/components/generated/components/toggle/Toggle) + + `import { Toggle } from '$lib/components/toggle';` + +- [Video](/developers-guide/frontend/components/generated/components/video/Video) + + `import { Video } from '$lib/components/video';` + +- [Warning](/developers-guide/frontend/components/generated/components/warning/Warning) + + `import { Warning } from '$lib/components/warning';` + +- [WarningMessages](/developers-guide/frontend/components/generated/components/controller/WarningMessages) + + `import { WarningMessages } from '$lib/components/controller';` + +### Dynamic Components + +- [AdminNav](/developers-guide/frontend/components/generated/dynamic-components/navigation/admin/AdminNav) + + `import { AdminNav } from '$lib/dynamic-components/navigation/admin';` + +- [AppLogo](/developers-guide/frontend/components/generated/dynamic-components/appLogo/AppLogo) + + `import { AppLogo } from '$lib/dynamic-components/appLogo';` + +- [CandidateNav](/developers-guide/frontend/components/generated/dynamic-components/navigation/candidate/CandidateNav) + + `import { CandidateNav } from '$lib/dynamic-components/navigation/candidate';` + +- [DataConsent](/developers-guide/frontend/components/generated/dynamic-components/dataConsent/DataConsent) + + `import { DataConsent } from '$lib/dynamic-components/dataConsent';` + +- [DataConsentInfoButton](/developers-guide/frontend/components/generated/dynamic-components/dataConsent/DataConsentInfoButton) + + `import { DataConsentInfoButton } from '$lib/dynamic-components/dataConsent';` + +- [DataConsentPopup](/developers-guide/frontend/components/generated/dynamic-components/dataConsent/popup/DataConsentPopup) + + `import { DataConsentPopup } from '$lib/dynamic-components/dataConsent/popup';` + +- [EntityCard](/developers-guide/frontend/components/generated/dynamic-components/entityCard/EntityCard) + + `import { EntityCard } from '$lib/dynamic-components/entityCard';` + +- [EntityCardAction](/developers-guide/frontend/components/generated/dynamic-components/entityCard/EntityCardAction) + + `import { EntityCardAction } from '$lib/dynamic-components/entityCard';` + +- [EntityChildren](/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityChildren) + + `import { EntityChildren } from '$lib/dynamic-components/entityDetails';` + +- [EntityDetails](/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityDetails) + + `import { EntityDetails } from '$lib/dynamic-components/entityDetails';` + +- [EntityDetailsDrawer](/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityDetailsDrawer) + + `import { EntityDetailsDrawer } from '$lib/dynamic-components/entityDetails';` + +- [EntityInfo](/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityInfo) + + `import { EntityInfo } from '$lib/dynamic-components/entityDetails';` + +- [EntityList](/developers-guide/frontend/components/generated/dynamic-components/entityList/EntityList) + + `import { EntityList } from '$lib/dynamic-components/entityList';` + +- [EntityListControls](/developers-guide/frontend/components/generated/dynamic-components/entityList/EntityListControls) + + `import { EntityListControls } from '$lib/dynamic-components/entityList';` + +- [EntityOpinions](/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityOpinions) + + `import { EntityOpinions } from '$lib/dynamic-components/entityDetails';` + +- [Feedback](/developers-guide/frontend/components/generated/dynamic-components/feedback/Feedback) + + `import { Feedback } from '$lib/dynamic-components/feedback';` + +- [FeedbackModal](/developers-guide/frontend/components/generated/dynamic-components/feedback/modal/FeedbackModal) + + `import { FeedbackModal } from '$lib/dynamic-components/feedback/modal';` + +- [FeedbackPopup](/developers-guide/frontend/components/generated/dynamic-components/feedback/popup/FeedbackPopup) + + `import { FeedbackPopup } from '$lib/dynamic-components/feedback/popup';` + +- [Footer](/developers-guide/frontend/components/generated/dynamic-components/footer/Footer) + + `import { Footer } from '$lib/dynamic-components/footer';` + +- [InfoItem](/developers-guide/frontend/components/generated/dynamic-components/entityDetails/InfoItem) + + `import { InfoItem } from '$lib/dynamic-components/entityDetails';` + +- [LanguageSelection](/developers-guide/frontend/components/generated/dynamic-components/navigation/languages/LanguageSelection) + + `import { LanguageSelection } from '$lib/dynamic-components/navigation/languages';` + +- [LogoutButton](/developers-guide/frontend/components/generated/dynamic-components/logoutButton/LogoutButton) + + `import { LogoutButton } from '$lib/dynamic-components/logoutButton';` + +- [NavGroup](/developers-guide/frontend/components/generated/dynamic-components/navigation/NavGroup) + + `import { NavGroup } from '$lib/dynamic-components/navigation';` + +- [Navigation](/developers-guide/frontend/components/generated/dynamic-components/navigation/Navigation) + + `import { Navigation } from '$lib/dynamic-components/navigation';` + +- [NavItem](/developers-guide/frontend/components/generated/dynamic-components/navigation/NavItem) + + `import { NavItem } from '$lib/dynamic-components/navigation';` + +- [QuestionHeading](/developers-guide/frontend/components/generated/dynamic-components/questionHeading/QuestionHeading) + + `import { QuestionHeading } from '$lib/dynamic-components/questionHeading';` + +- [SurveyBanner](/developers-guide/frontend/components/generated/dynamic-components/survey/banner/SurveyBanner) + + `import { SurveyBanner } from '$lib/dynamic-components/survey/banner';` + +- [SurveyButton](/developers-guide/frontend/components/generated/dynamic-components/survey/SurveyButton) + + `import { SurveyButton } from '$lib/dynamic-components/survey';` + +- [SurveyPopup](/developers-guide/frontend/components/generated/dynamic-components/survey/popup/SurveyPopup) + + `import { SurveyPopup } from '$lib/dynamic-components/survey/popup';` + +- [VoterNav](/developers-guide/frontend/components/generated/dynamic-components/navigation/voter/VoterNav) + + `import { VoterNav } from '$lib/dynamic-components/navigation/voter';` + +### Candidate App Components + +- [LogoutButton](/developers-guide/frontend/components/generated/candidate/components/logoutButton/LogoutButton) + + `import { LogoutButton } from '$candidate/components/logoutButton';` + +- [PasswordField](/developers-guide/frontend/components/generated/candidate/components/passwordField/PasswordField) + + `import { PasswordField } from '$candidate/components/passwordField';` + +- [PasswordSetter](/developers-guide/frontend/components/generated/candidate/components/passwordSetter/PasswordSetter) + + `import { PasswordSetter } from '$candidate/components/passwordSetter';` + +- [PasswordValidator](/developers-guide/frontend/components/generated/candidate/components/passwordValidator/PasswordValidator) + + `import { PasswordValidator } from '$candidate/components/passwordValidator';` + +- [PreregisteredNotification](/developers-guide/frontend/components/generated/candidate/components/preregisteredNotification/PreregisteredNotification) + + `import { PreregisteredNotification } from '$candidate/components/preregisteredNotification';` + +- [TermsOfUse](/developers-guide/frontend/components/generated/candidate/components/termsOfUse/TermsOfUse) + + `import { TermsOfUse } from '$candidate/components/termsOfUse';` + +- [TermsOfUseForm](/developers-guide/frontend/components/generated/candidate/components/termsOfUse/TermsOfUseForm) + + `import { TermsOfUseForm } from '$candidate/components/termsOfUse';` + +--- + +Total: 98 components diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/logoutButton/LogoutButton/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/logoutButton/LogoutButton/+page.md new file mode 100644 index 000000000..534beb308 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/logoutButton/LogoutButton/+page.md @@ -0,0 +1,29 @@ +# LogoutButton + +Allows user to log out. Displays modal notification if the user hasn't filled all the data. + +### Dynamic component + +Accesses `CandidateContext`. + +### Properties + +- `logoutModalTimer`: The duration in seconds a logout modal will wait before automatically logging the user out. Default: `30` +- `stayOnPage`: Whether pressing the button takes the user to the login page or not. Default: `false` +- Any valid properties of a `Button` component + +### Settings + +- `entities.hideIfMissingAnswers.candidate`: Affects message shown. + +### Usage + +```tsx +<LogoutButton /> +``` + +## Source + +[frontend/src/lib/candidate/components/logoutButton/LogoutButton.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/logoutButton/LogoutButton.svelte) + +[frontend/src/lib/candidate/components/logoutButton/LogoutButton.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/logoutButton/LogoutButton.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/passwordField/PasswordField/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/passwordField/PasswordField/+page.md new file mode 100644 index 000000000..e710957d4 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/passwordField/PasswordField/+page.md @@ -0,0 +1,25 @@ +# PasswordField + +PasswordField is an input box for password that comes with a button to reveal and hide the password + +### Properties + +- `id`: Optional id for the input. +- `password`: Bindable: The password value. +- `autocomplete`: The autocomplete value for password input. Default: `''` +- `label`: The label for the password field. +- `externalLabel`: Whether the label is outside the component and should not be rendered inside. Default: `false` +- `focus`: Bindable: Function to set focus to the password input. +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<PasswordField bind:password={passwordOfContext} autocomplete="current-password" /> +``` + +## Source + +[frontend/src/lib/candidate/components/passwordField/PasswordField.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/passwordField/PasswordField.svelte) + +[frontend/src/lib/candidate/components/passwordField/PasswordField.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/passwordField/PasswordField.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/passwordSetter/PasswordSetter/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/passwordSetter/PasswordSetter/+page.md new file mode 100644 index 000000000..6ffef4cec --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/passwordSetter/PasswordSetter/+page.md @@ -0,0 +1,28 @@ +# PasswordSetter + +Contains a password validator, a password input field and a second confirmation password input field. + +### Dynamic component + +Contains the dynamic `PasswordValidator` component. + +### Properties + +- `password`: Bindable: The password value. +- `autocomplete`: The autocomplete attribute for the password input field. Default: `'new-password'` +- `errorMessage`: Bindable: Error message if the password is invalid or doesn't match the confirmation password. +- `valid`: Bindable: Whether the password is valid and the confirmation password matches. +- `reset`: Bindable: Function to clear the form. +- Any valid attributes of a `<form>` element + +### Usage + +```tsx +<PasswordSetter bind:password={password} bind:valid={canSubmit} /> +``` + +## Source + +[frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.svelte) + +[frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/passwordValidator/PasswordValidator/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/passwordValidator/PasswordValidator/+page.md new file mode 100644 index 000000000..038384a18 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/passwordValidator/PasswordValidator/+page.md @@ -0,0 +1,44 @@ +# PasswordValidator + +Component for real-time password validation UI. +Password is validated against rules defined in `passwordValidation.ts`. + +A progress bar is shown that indicates the number of completed rules. +The progress bar is colored based on the validation state: + +There are two types of rules: + +- Positive rules: These are basic requirements that must be met for the password to be valid. + These are always enforced and their status is shown in the UI. + Completed positive rules are shown in a different color with a checkmark. +- Negative rules: These are rules that are used to prevent bad password practises. + These can be either enforced or non-enforced. Negative rules are shown if they are violated. + +Due to the use of debounced validation, the component will only update after a delay when the user stops typing. +Therefore, the validity should be also checked on form submit as well and on the server side for security reasons. + +### Dynamic component + +Accesses validation functions from `@openvaa/app-shared`. + +### Properties + +- `password`: The password to validate. +- `username`: The username used to prevent the password from being too similar. Default: `''` +- `validPassword`: Bindable: Whether the password is valid. +- Any valid attributes of a `<div>` element + +### Usage + +When using this component, the `validPassword` property should be bound to a boolean variable that is used to enable/disable the submit button. +`password` and `username` should be given as props. + +```tsx +<PasswordValidator bind:validPassword={validPassword} password={password} username={username} /> +``` + +## Source + +[frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.svelte) + +[frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/preregisteredNotification/PreregisteredNotification/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/preregisteredNotification/PreregisteredNotification/+page.md new file mode 100644 index 000000000..d554fb6d7 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/preregisteredNotification/PreregisteredNotification/+page.md @@ -0,0 +1,21 @@ +# PreregisteredNotification + +Show a notification prompting the user to login instead of preregistering again. + +### Properties + +- Any valid properties of an `Alert` component + +### Usage + +```tsx +popupQueue.push({ + component: PreregisteredNotification +}); +``` + +## Source + +[frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.svelte) + +[frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/termsOfUse/TermsOfUse/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/termsOfUse/TermsOfUse/+page.md new file mode 100644 index 000000000..2f42faabe --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/termsOfUse/TermsOfUse/+page.md @@ -0,0 +1,13 @@ +# TermsOfUse + +A utility component for displaying candidate app terms of use and privacy statement. + +### Usage + +```tsx +<TermsOfUse /> +``` + +## Source + +[frontend/src/lib/candidate/components/termsOfUse/TermsOfUse.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/termsOfUse/TermsOfUse.svelte) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/termsOfUse/TermsOfUseForm/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/termsOfUse/TermsOfUseForm/+page.md new file mode 100644 index 000000000..cc7057ae7 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/candidate/components/termsOfUse/TermsOfUseForm/+page.md @@ -0,0 +1,28 @@ +# TermsOfUseForm + +Show the terms of use along with a checkbox for accepting them. + +### Dynamic component + +Accesses `CandidateContext`. + +### Properties + +- `termsAccepted`: Bindable: Whether the terms are accepted. Default: `false` +- Any valid attributes of a `<section>` element + +### Usage + +```tsx +<script lang="ts"> + let termsAccepted: boolean; + $: console.info('termsAccepted:', termsAccepted); +</script> +<TermsOfUseForm bind:termsAccepted/> +``` + +## Source + +[frontend/src/lib/candidate/components/termsOfUse/TermsOfUseForm.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/termsOfUse/TermsOfUseForm.svelte) + +[frontend/src/lib/candidate/components/termsOfUse/TermsOfUseForm.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/candidate/components/termsOfUse/TermsOfUseForm.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/accordionSelect/AccordionSelect/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/accordionSelect/AccordionSelect/+page.md new file mode 100644 index 000000000..3be3b2391 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/accordionSelect/AccordionSelect/+page.md @@ -0,0 +1,25 @@ +# AccordionSelect + +Show a select widget which is expanded when no selection is made and collapsed when an option is selected. + +If there's only one option, it is automatically selected and no interactions are allowed. + +### Properties + +- `options`: The titles and other data related to the options. +- `activeIndex`: The index of the active option. Bind to this to change or read the active option. +- `labelGetter`: A callback used to get the label for each option. Default: `String` +- `onChange`: Callback for when the active option changes. The event `details` contains the active option as `option` as well as its `index`. +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<AccordionSelect bind:activeIndex options={['Basic Info', 'Opinions']} /> +``` + +## Source + +[frontend/src/lib/components/accordionSelect/AccordionSelect.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/accordionSelect/AccordionSelect.svelte) + +[frontend/src/lib/components/accordionSelect/AccordionSelect.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/accordionSelect/AccordionSelect.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/alert/Alert/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/alert/Alert/+page.md new file mode 100644 index 000000000..f3491bad2 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/alert/Alert/+page.md @@ -0,0 +1,53 @@ +# Alert + +Show a non-model alert or dialog that appears at the bottom of the screen. + +### Properties + +- `title`: The title of the alert. +- `icon`: Possible icon of the alert. +- `autoOpen`: Whether to open the alert automatically. Default: `true` +- `isOpen`: Bind to this to get the alert's open state. +- `onClose`: The callback triggered when the alert is closed. +- Any valid attributes of a `<dialog>` element + +### Bindable functions + +- `openAlert`: Opens the alert +- `closeAlert`: Closes the alert + +### Slots + +- `actions`: The action buttons to display. +- default: The content of the alert. + +### Events + +- `open`: Fired after the alert is opened. +- `close`: Fired when the alert is closed by any means. +- Neither event has any details. + +### Usage + +```tsx +<script lang="ts"> + let closeAlert: () => void; +</script> +<Alert + bind:closeAlert + title="Can we help you?" + icon="warning" + onClose={() => console.info('Alert closed')}> + Please tell us whether we can help you? + <div slot="actions" class="flex flex-col w-full max-w-md mx-auto"> + <Button on:click={() => {console.info('Yes'); closeAlert();}} text="Yes" variant="main"/> + <Button on:click={closeAlert} text="No"/> + </div> +</Alert> +``` + +## Source + +[frontend/src/lib/components/alert/Alert.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/alert/Alert.svelte) + +[frontend/src/lib/components/alert/Alert.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/alert/Alert.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/avatar/Avatar/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/avatar/Avatar/+page.md new file mode 100644 index 000000000..13ccb1d26 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/avatar/Avatar/+page.md @@ -0,0 +1,22 @@ +# Avatar + +Display either an image or a initials-based avatar for an entity. The color of the initials background is based on the entity's color or `'base-300'` by default. If the color is specified, it should be dark enough, because the `primary-content` color is used for the text. + +### Properties + +- `entity`: The entity for which to display the avatar. +- `size`: The size of the avatar. Default: `'md'` +- `linkFullImage`: Whether to link the thumbnail to the full image. Default: `false` +- Any valid attributes of a `<figure>` element + +### Usage + +```tsx +<Avatar entity={candidate} size="lg" linkFullImage /> +``` + +## Source + +[frontend/src/lib/components/avatar/Avatar.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/avatar/Avatar.svelte) + +[frontend/src/lib/components/avatar/Avatar.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/avatar/Avatar.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/button/Button/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/button/Button/+page.md new file mode 100644 index 000000000..43dc4eb97 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/button/Button/+page.md @@ -0,0 +1,55 @@ +# Button + +A component for buttons that mostly contain text and an icon. Use the `variant` prop to specify the button type. When using an `icon`, use `iconPos` to set the position of the icon relative to the text. + +- `main`: A large, prominent button that is used for the main action of the page. In general, there should only be one of these on a page. +- `prominent`: A large, quite prominent button. +- `floating-icon`: A button with a large icon and no text. This is usually used for a floating action button. +- `icon`: A button containing only an icon. Note that you still need to provide the `text` property, which will be used as the `aria-label` and `title` of the button. +- `responsive-icon`: A button rendered as icon only on small screens but which exposes the text label on large screens. Set the `iconPos` to `left` or `right` to control its location in the expanded view. +- `secondary`: A button with a smaller (uppercase) text and possibly an icon. +- `normal`: The default button type, which usually consists of an icon and text. + +Only `main` buttons have a backround color. The other variants use DaisyUI's `btn-ghost` class, i.e. they do not have a background color. + +The button is rendered as an `<a>` element if `href` is supplied. Otherwise a `<button>` element will be used. Be sure to provide an `on:click` event handler or other way of making the item interactive. + +### Properties + +- `text`: The required text of the button. If `variant` is `icon`, the text will be used as the `aria-label` and `title` for the button. You can override both by providing them as attributes, e.g. `aria-label="Another text"`. +- `icon`: The name of the icon to use in the button or `null` if no icon should be used. Default: `'next'` if `variant='main'`, otherwise `null` +- `color`: The color of the button or text. Default: `'primary'` +- `disabled`: Whether the button is disabled. This can also be used with buttons rendered as `<a>` elements. +- `variant`: Type of the button, which defines it's appearance. Default: `'normal'` +- `iconPos`: Position of the icon in the button. Only relevant if `icon` is not `null` and `variant` is not `icon` or `floating-icon`. Note that `top` and `bottom` are not supported if `variant='main'`. Default: `'right'` if `variant='main'`, otherwise `'left'` +- `loading`: Set to `true` to show a loading spinner instead of the possible icon and disable the button. Default: `false` +- `loadingText`: The text shown when `loading` is `true`. Default: `$t('common.loading')` +- `href`: The URL to navigate to. If this is not supplied be sure to provide an `on:click` event handler or other way of making the item interactive. +- Any valid attributes of either an `<a>` or `<button>` element depending whether `href` was defined or not, respectively. + +### Slots + +- `badge`: A slot for adding a badge to the button. + +### Reactivity + +Reactivity is not supported for the properties: `variant`, `iconPos`. + +### Usage + +```tsx +<Button on:click={next} variant="main" icon="next" +text="Continue"/> +<Button on:click={skip} icon="skip" iconPos="top" color="secondary" +text="Skip this question"/> +<Button on:click={addToList} variant="icon" icon="addToList" +text="Add to list"> + <InfoBadge text="5" slot="badge"/> +</Button> +``` + +## Source + +[frontend/src/lib/components/button/Button.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/button/Button.svelte) + +[frontend/src/lib/components/button/Button.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/button/Button.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/buttonWithConfirmation/ButtonWithConfirmation/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/buttonWithConfirmation/ButtonWithConfirmation/+page.md new file mode 100644 index 000000000..3e3d1dd4d --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/buttonWithConfirmation/ButtonWithConfirmation/+page.md @@ -0,0 +1,39 @@ +# ButtonWithConfirmation + +A button which will open a confirmation modal when clicked before the action is executed. + +### Properties + +- `modalTitle`: The title of the confirmation modal. +- `onConfirm`: Callback triggered when the user confirms the action. +- `onCancel`: Callback triggered when the user cancels the action. +- `cancelLabel`: The label for the cancel button in the modal. +- `confirmLabel`: The label for the confirm button in the modal. +- Any valid properties of a `<Button>` component except `href` and `on:click`. + +### Slots + +- `badge`: A slot for adding a badge to the button. + +### Reactivity + +Reactivity is not supported for the properties: `variant`, `iconPos`. + +### Usage + +```tsx +<Button on:click={next} variant="main" icon="next" +text="Continue"/> +<Button on:click={skip} icon="skip" iconPos="top" color="secondary" +text="Skip this question"/> +<Button on:click={addToList} variant="icon" icon="addToList" +text="Add to list"> + <InfoBadge text="5" slot="badge"/> +</Button> +``` + +## Source + +[frontend/src/lib/components/buttonWithConfirmation/ButtonWithConfirmation.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/buttonWithConfirmation/ButtonWithConfirmation.svelte) + +[frontend/src/lib/components/buttonWithConfirmation/ButtonWithConfirmation.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/buttonWithConfirmation/ButtonWithConfirmation.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/categoryTag/CategoryTag/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/categoryTag/CategoryTag/+page.md new file mode 100644 index 000000000..d21540bcb --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/categoryTag/CategoryTag/+page.md @@ -0,0 +1,23 @@ +# CategoryTag + +Used to display a question category tag with the category's color. + +### Properties + +- `category`: The `QuestionCategory` object. +- `variant`: Whether to use an abbreviation or the full name. Default: `'full'` +- `suffix`: An optional suffix to add after the category name, e.g. '1/3'. +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. Default: `false` +- Any valid attributes of a `<span>` element + +### Usage + +```tsx +<CategoryTag category={question.category} /> +``` + +## Source + +[frontend/src/lib/components/categoryTag/CategoryTag.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/categoryTag/CategoryTag.svelte) + +[frontend/src/lib/components/categoryTag/CategoryTag.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/categoryTag/CategoryTag.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/constituencySelector/ConstituencySelector/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/constituencySelector/ConstituencySelector/+page.md new file mode 100644 index 000000000..f0e950c49 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/constituencySelector/ConstituencySelector/+page.md @@ -0,0 +1,32 @@ +# ConstituencySelector + +Display constituency selection inputs for elections. + +If any of the `ConstituencyGroup`s for the `Election`s are shared, only a single selector will be shown for them. Also, if any `ConstituencyGroup`s are completely subsumed by another, only the selector for the child group will be shown and the selected parent will be implied. + +### Properties + +- `elections`: The `Election`s for which to show the `Constituency`s. +- `disableSorting`: If `true`, the `Constituency`s are not ordered alphabetically. Default: `false` +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. Default: `false` +- `selected`: Bindable value for the `Id`s of the selected `Constituency`s organized by `Election`. +- `useSingleGroup`: If specified, only this group is offered for selection and the `Constituency`s for the `Election`s are implied from this one. Only meaningful when there are multiple `Election`s whose `ConstituencyGroup` hierarchies overlap only partially. To be used when the `elections.startFromConstituencyGroup` setting is set. +- `selectionComplete`: A utility bindable value which is `true` when a selection has been made for each `Election` or for the single group if `useSingleGroup` is set. +- `onChange`: Callback triggered when the selection changes. +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<ConstituencySelector + elections={$dataRoot.elections} + bind:selected={$selectedConstituencies} + onChange={(sel) => console.info('Selected', sel)} +/> +``` + +## Source + +[frontend/src/lib/components/constituencySelector/ConstituencySelector.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/constituencySelector/ConstituencySelector.svelte) + +[frontend/src/lib/components/constituencySelector/ConstituencySelector.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/constituencySelector/ConstituencySelector.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/constituencySelector/SingleGroupConstituencySelector/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/constituencySelector/SingleGroupConstituencySelector/+page.md new file mode 100644 index 000000000..9eaf4ffc8 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/constituencySelector/SingleGroupConstituencySelector/+page.md @@ -0,0 +1,31 @@ +# SingleGroupConstituencySelector + +# Single group constituency selection component + +Display constituency selection input for just one `ConstituencyGroup` which is not necessarily tied to a specific `Election`. + +### Properties + +- `group`: The `ConstituencyGroup` to be show. +- `label`: The `aria-label` and placeholder text for the select input. Default `$t('components.constituencySelector.selectPrompt', { constituencyGroup: group.name })`. +- `disableSorting`: If `true`, the `Constituency`s are not ordered alphabetically. Default `false`. +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. @default false +- `selected`: Bindable value for the `Id` of the selected `Constituency`. +- `onChange`: Callback triggered when the selection changes. +- Any valid attributes of a `<select>` element. + +### Usage + +```tsx +<SingleGroupConstituencySelector + group={election.constituencyGroups[0]} + bind:selected={selectedId} + onChange={(id) => console.info('Selected constituency with id', id)} +/> +``` + +## Source + +[frontend/src/lib/components/constituencySelector/SingleGroupConstituencySelector.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/constituencySelector/SingleGroupConstituencySelector.svelte) + +[frontend/src/lib/components/constituencySelector/SingleGroupConstituencySelector.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/constituencySelector/SingleGroupConstituencySelector.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/controller/InfoMessages/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/controller/InfoMessages/+page.md new file mode 100644 index 000000000..760a91bdf --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/controller/InfoMessages/+page.md @@ -0,0 +1,20 @@ +# InfoMessages + +Reusable component for displaying informational messages with scrolling. + +### Properties + +- `messages`: Array of informational messages to display. Default: `[]` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<InfoMessages messages={jobMessages} /> +``` + +## Source + +[frontend/src/lib/components/controller/InfoMessages.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/controller/InfoMessages.svelte) + +[frontend/src/lib/components/controller/InfoMessages.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/controller/InfoMessages.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/controller/ProgressBar/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/controller/ProgressBar/+page.md new file mode 100644 index 000000000..0cded4ee0 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/controller/ProgressBar/+page.md @@ -0,0 +1,24 @@ +# ProgressBar + +Reusable progress bar component for displaying task progress. + +### Properties + +- `progress`: Progress value between 0 and 1. +- `label`: Label for the progress bar. Default: `t('adminApp.jobs.progress')` +- `showPercentage`: Whether to show the percentage. Default: `true` +- `color`: Color theme for the progress bar. Default: `'primary'` +- `size`: Size of the progress bar. Default: `'md'` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<ProgressBar progress={0.75} label="Processing" /> +``` + +## Source + +[frontend/src/lib/components/controller/ProgressBar.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/controller/ProgressBar.svelte) + +[frontend/src/lib/components/controller/ProgressBar.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/controller/ProgressBar.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/controller/WarningMessages/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/controller/WarningMessages/+page.md new file mode 100644 index 000000000..4b5dd3bcc --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/controller/WarningMessages/+page.md @@ -0,0 +1,21 @@ +# WarningMessages + +Reusable component for displaying warning and error messages with scrolling. + +### Properties + +- `warnings`: Array of warning messages to display. Default: `[]` +- `errors`: Array of error messages to display. Default: `[]` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<WarningMessages warnings={warningMessages} errors={errorMessages} /> +``` + +## Source + +[frontend/src/lib/components/controller/WarningMessages.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/controller/WarningMessages.svelte) + +[frontend/src/lib/components/controller/WarningMessages.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/controller/WarningMessages.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/electionSelector/ElectionSelector/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/electionSelector/ElectionSelector/+page.md new file mode 100644 index 000000000..49213a75b --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/electionSelector/ElectionSelector/+page.md @@ -0,0 +1,30 @@ +# ElectionSelector + +# Election selection component + +Display constituency selection inputs for elections. + +If there’s only one option, it is automatically selected and no interactions are allowed. + +### Properties + +- `elections`: The `Election`s to show. +- `selected`: Bindable value for the `Id`s of the selected elections. +- `onChange`: Callback triggered when the selection changes. +- Any valid attributes of a `<div>` element. + +### Usage + +```tsx +<ElectionSelector + elections={$dataRoot.elections} + bind:selected={$selectedElectionIds} + onChange={(ids) => console.info('Selected', ids)} +/> +``` + +## Source + +[frontend/src/lib/components/electionSelector/ElectionSelector.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/electionSelector/ElectionSelector.svelte) + +[frontend/src/lib/components/electionSelector/ElectionSelector.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/electionSelector/ElectionSelector.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/electionSymbol/ElectionSymbol/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/electionSymbol/ElectionSymbol/+page.md new file mode 100644 index 000000000..e64b09d2d --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/electionSymbol/ElectionSymbol/+page.md @@ -0,0 +1,22 @@ +# ElectionSymbol + +Display an entity's election symbol, which is usually a number but may also be an image, e.g. in Pakistan. + +### Properties + +- `text`: The text of the symbol, e.g. '15' or 'A'. If the symbol is an image, `text` will be used as its `alt` attribute. +- `image`: The `src` of an image election symbol. +- Any valid attributes of a `<span>` element + +### Usage + +```tsx +<ElectionSymbol>46</ElectionSymbol> +<ElectionSymbol><img src="arrow.png" alt="Arrow"/></ElectionSymbol> +``` + +## Source + +[frontend/src/lib/components/electionSymbol/ElectionSymbol.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/electionSymbol/ElectionSymbol.svelte) + +[frontend/src/lib/components/electionSymbol/ElectionSymbol.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/electionSymbol/ElectionSymbol.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/electionTag/ElectionTag/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/electionTag/ElectionTag/+page.md new file mode 100644 index 000000000..e110d5a2b --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/electionTag/ElectionTag/+page.md @@ -0,0 +1,24 @@ +# ElectionTag + +Used to display an election tag with the election's color. + +Used when the application has multiple elections and question may apply to only some of them. + +### Properties + +- `election`: The `Election` object. +- `variant`: Whether to use an abbreviation or the full name. Default: `'short'` +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. Default: `false` +- Any valid attributes of a `<span>` element. + +### Usage + +```tsx +<ElectionTag {election}/> +``` + +## Source + +[frontend/src/lib/components/electionTag/ElectionTag.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/electionTag/ElectionTag.svelte) + +[frontend/src/lib/components/electionTag/ElectionTag.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/electionTag/ElectionTag.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/EntityFilters/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/EntityFilters/+page.md new file mode 100644 index 000000000..efc49e482 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/EntityFilters/+page.md @@ -0,0 +1,21 @@ +# EntityFilters + +Show filters for entities. This component and the individual filter components only display the UI for the filters and handle their rule updates. To access the results of the filters, you have to apply the filters to the targets. + +### Properties + +- `filterGroup`: The filters applied to the contents. +- `targets`: The target entitiess of the filter objects. Note that these will only be used to get value options, not for actual filtering. +- Any valid attributes of a `<div>` element. + +### Usage + +```tsx +<EntityFilters {filters} targets={candidates}/> +``` + +## Source + +[frontend/src/lib/components/entityFilters/EntityFilters.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/entityFilters/EntityFilters.svelte) + +[frontend/src/lib/components/entityFilters/EntityFilters.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/entityFilters/EntityFilters.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/enumerated/EnumeratedEntityFilter/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/enumerated/EnumeratedEntityFilter/+page.md new file mode 100644 index 000000000..1e3bac3f2 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/enumerated/EnumeratedEntityFilter/+page.md @@ -0,0 +1,21 @@ +# EnumeratedEntityFilter + +Render an enumerated filter for entities that displays a list of values to include in the results. These can be, for example, parties or answers to enumerated questions, like gender or language. The filter works for both single and multiple selection questions. + +### Properties + +- `filter`: The filter object +- `targets`: An array of target entities or rankings +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<EnumeratedEntityFilter {filter} targets={candidates}/> +``` + +## Source + +[frontend/src/lib/components/entityFilters/enumerated/EnumeratedEntityFilter.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/entityFilters/enumerated/EnumeratedEntityFilter.svelte) + +[frontend/src/lib/components/entityFilters/enumerated/EnumeratedEntityFilter.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/entityFilters/enumerated/EnumeratedEntityFilter.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/numeric/NumericEntityFilter/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/numeric/NumericEntityFilter/+page.md new file mode 100644 index 000000000..cd2b36b2f --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/numeric/NumericEntityFilter/+page.md @@ -0,0 +1,21 @@ +# NumericEntityFilter + +Render a numeric filter for entities. + +### Properties + +- `filter`: The filter object +- `targets`: An array of target entities or rankings +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<NumericEntityFilter {filter} targets={candidates}/> +``` + +## Source + +[frontend/src/lib/components/entityFilters/numeric/NumericEntityFilter.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/entityFilters/numeric/NumericEntityFilter.svelte) + +[frontend/src/lib/components/entityFilters/numeric/NumericEntityFilter.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/entityFilters/numeric/NumericEntityFilter.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/text/TextEntityFilter/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/text/TextEntityFilter/+page.md new file mode 100644 index 000000000..a4ddf729c --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityFilters/text/TextEntityFilter/+page.md @@ -0,0 +1,22 @@ +# TextEntityFilter + +Render a text filter for entities. + +### Properties + +- `filter`: The text filter object. +- `placeholder`: The placeholder text. Default: `$t('components.entityFilters.text.placeholder')` +- `variant`: The styling variant for the text field. Default: `'default'` +- Any valid attributes of a `<div>` element. + +### Usage + +```tsx +<TextEntityFilter {filter}/> +``` + +## Source + +[frontend/src/lib/components/entityFilters/text/TextEntityFilter.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/entityFilters/text/TextEntityFilter.svelte) + +[frontend/src/lib/components/entityFilters/text/TextEntityFilter.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/entityFilters/text/TextEntityFilter.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityTag/EntityTag/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityTag/EntityTag/+page.md new file mode 100644 index 000000000..b25cdd5c6 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/entityTag/EntityTag/+page.md @@ -0,0 +1,23 @@ +# EntityTag + +Used to display an `Entity` as small tag including an icon. + +### Properties + +- `entity`: A possibly wrapped entity, e.g. candidate or a party. +- `variant`: Whether to use an abbreviation or the full name. Default: `'default'` +- `hideParent`: Whether to hide the possible parent nomination. Default: `false` +- Any valid attributes of a `<div>` element. + +### Usage + +```tsx +<EntityTag entity={organization}/> +<EntityTag entity={nomination.parentNomination} variant="short"/> +``` + +## Source + +[frontend/src/lib/components/entityTag/EntityTag.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/entityTag/EntityTag.svelte) + +[frontend/src/lib/components/entityTag/EntityTag.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/entityTag/EntityTag.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/errorMessage/ErrorMessage/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/errorMessage/ErrorMessage/+page.md new file mode 100644 index 000000000..72de7acdd --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/errorMessage/ErrorMessage/+page.md @@ -0,0 +1,22 @@ +# ErrorMessage + +Used to display an error message. Also logs the error to the console. + +### Properties + +- `inline`: Whether to show an inline version of the message. By default the message tries to center itself in the available area and displays a large emoji. Default: `false` +- `message`: The message to display. Default: `$t('error.default')` +- `logMessage`: The message to log in the console in development mode. Default: value of `message` +- Any valid attributes of a `<div>` element. + +### Usage + +```tsx +<ErrorMessage /> +``` + +## Source + +[frontend/src/lib/components/errorMessage/ErrorMessage.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/errorMessage/ErrorMessage.svelte) + +[frontend/src/lib/components/errorMessage/ErrorMessage.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/errorMessage/ErrorMessage.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/expander/Expander/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/expander/Expander/+page.md new file mode 100644 index 000000000..e44e8271a --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/expander/Expander/+page.md @@ -0,0 +1,49 @@ +# Expander + +A component for expanders that contain a title and some content. Use the +`variant` prop to specify the expander type. + +- `read-more`: the default style of the expander. Used, for example, for getting + more information about a question. +- `question`: a more prominent style of the expander. Used in question listings + to display a question that can be expanded to reveal further information. +- `category`: the most prominent style of the expander. Used for collapsible + categories of items, such as questions. +- `question-help`: used to display questions and answers in the style of the help page. + +### Properties + +- `title`: Title is seen as the text in the expander's visible part, and it is mandatory. Title will also be used as a 'aria-label' for a checkbow on which the expander operates on. +- `iconColor`: The color of the next-icon that is used in the expander. Default: `'primary'` +- `iconPos`: The position of the next-icon that is used in the expander. Default: `'text'` +- `titleClass`: Variable with which to configure the expanders title if no variants are in use. +- `contentClass`: Variable with which to configure the expanders content if no variants are in use. +- `defaultExpanded`: Variable used to define if the expander is expanded or not by default. +- `variant`: Variable used to define a variant for the expander. +- Any valid attributes of a `<div>` element. + +You should not try to use a variant and customize at the same time. + +### Events + +- `expand`: Fired when the expander is expanded. +- `collapse`: Fired when the expander is collapsed. + +### Usage + +```tsx +<Expander title="Example title"> + <p>Example content<p/> +</Expander> + +<Expander title="Example title" variant="category" iconColor="primary" + titleClass="bg-base-100 text-primary" contentClass="bg-base-300 text-info font-bold"> + <p>Example content<p/> +</Expander> +``` + +## Source + +[frontend/src/lib/components/expander/Expander.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/expander/Expander.svelte) + +[frontend/src/lib/components/expander/Expander.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/expander/Expander.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/headingGroup/HeadingGroup/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/headingGroup/HeadingGroup/+page.md new file mode 100644 index 000000000..4e256cfe1 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/headingGroup/HeadingGroup/+page.md @@ -0,0 +1,29 @@ +# HeadingGroup + +Used to group the page's main headings, such as a pre-heading (kicker) +and the main title. + +### Properties + +- `aria-roledescription`: The Aria role description of the `<hgroup>` element. Default: `$t('aria.headingGroup')` +- `role`: The Aria role of the `<hgroup>` element. Default: `'group'` +- Any valid attributes of a `<hgroup>` element. + +### Slots + +- default: the contents of the heading group. + +### Usage + +```tsx +<HeadingGroup> + <PreHeading class="text-accent">{$t('categories.environment')}</PreHeading> + <h1>{$t('titles.environment')}</h1> +</HeadingGroup> +``` + +## Source + +[frontend/src/lib/components/headingGroup/HeadingGroup.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/headingGroup/HeadingGroup.svelte) + +[frontend/src/lib/components/headingGroup/HeadingGroup.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/headingGroup/HeadingGroup.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/headingGroup/PreHeading/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/headingGroup/PreHeading/+page.md new file mode 100644 index 000000000..8b9a5240c --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/headingGroup/PreHeading/+page.md @@ -0,0 +1,24 @@ +# PreHeading + +Used for a pre-title, or kicker, above the main title of a page within a `HeadingGroup`. + +### Properties + +- `aria-roledescription`: The Aria role description of the `<p>` element representing the pre-title. Default: `$t('aria.preHeading')` +- Any valid attributes of a `<p>` element. + +### Slots + +- default: the contents of pre-title + +### Usage + +```tsx +<PreHeading class="text-accent">{$t('categories.environment')}</PreHeading> +``` + +## Source + +[frontend/src/lib/components/headingGroup/PreHeading.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/headingGroup/PreHeading.svelte) + +[frontend/src/lib/components/headingGroup/PreHeading.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/headingGroup/PreHeading.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/hero/Hero/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/hero/Hero/+page.md new file mode 100644 index 000000000..0178ac401 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/hero/Hero/+page.md @@ -0,0 +1,20 @@ +# Hero + +Display a hero illustration. + +### Properties + +- `content`: The content to display. +- Any valid attributes of a `<div>` element. + +### Usage + +```tsx +<Hero content={{ emoji: '🎃' }} /> +``` + +## Source + +[frontend/src/lib/components/hero/Hero.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/hero/Hero.svelte) + +[frontend/src/lib/components/hero/Hero.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/hero/Hero.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/heroEmoji/HeroEmoji/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/heroEmoji/HeroEmoji/+page.md new file mode 100644 index 000000000..9474698d2 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/heroEmoji/HeroEmoji/+page.md @@ -0,0 +1,28 @@ +# HeroEmoji + +Used for large emojis acting as decorative illustrations. + +The content is hidden from screen readers by default, because the +intended use is for [decorative purposes](https://www.w3.org/WAI/tutorials/images/decorative/). +To override, add `aria-hidden="false"` to the tag and also consider +adding an `aria-label`. + +To change the size of the emoji, add a `text-[size]` utility class +using the `class` attribute, e.g. `class="text-[10rem]"`. + +### Properties + +- `emoji`: The emoji to use. Note that all non-emoji characters will be removed. If `undefined` the component will not be rendered at all. Default: `undefined` +- Any valid attributes of a `<div>` element. + +### Usage + +```tsx +<HeroEmoji emoji="🚀" /> +``` + +## Source + +[frontend/src/lib/components/heroEmoji/HeroEmoji.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/heroEmoji/HeroEmoji.svelte) + +[frontend/src/lib/components/heroEmoji/HeroEmoji.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/heroEmoji/HeroEmoji.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/icon/Icon/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/icon/Icon/+page.md new file mode 100644 index 000000000..30b3f583e --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/icon/Icon/+page.md @@ -0,0 +1,30 @@ +# Icon + +An icon component, where the `name` property defines which icon to use. + +Use the other properties to set the size and color of the icon. The icon +is `aria-hidden` by default, but that can overriden. You can also pass +any valid attributes of the `<svg>` element. + +### Properties + +- `name`: The name of the icon to use. +- `size`: The size of the icon as one of the predefined sizes 'sm', 'md' or 'lg'. For arbitrary values, you can supply a `class` property, such as `h-[3.15rem] w-[3.15rem]`. Default: `'md'` +- `color`: The color of the icon as one of the predefined colours. For arbitrary values, use the `customColor` and `customColorDark` properties. Default: `'current'` +- `customColor`: A custom color string to use for the icon, e.g. in case of parties, which will override the `color` property. Make sure to define both `customColor` and `customColorDark` together. +- `customColorDark`: A custom color string to use for the icon in dark mode, which will override the `color` property. +- Any valid attributes of a `<svg>` element. + +### Usage + +```tsx +<Icon name="addToList"/> +<Icon name="opinion" color="primary" size="lg" + aria-hidden="false" aria-label="Add candidate to your list"/> +``` + +## Source + +[frontend/src/lib/components/icon/Icon.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/icon/Icon.svelte) + +[frontend/src/lib/components/icon/Icon.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/icon/Icon.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/icon/PreviewAllIcons/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/icon/PreviewAllIcons/+page.md new file mode 100644 index 000000000..c72ac7ca5 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/icon/PreviewAllIcons/+page.md @@ -0,0 +1,16 @@ +# PreviewAllIcons + +A utility component to see all the available icons at a glance. You can supply +any valid props to the icons. + +### Usage + +```tsx +<PreviewAllIcons size="lg" /> +``` + +## Source + +[frontend/src/lib/components/icon/PreviewAllIcons.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/icon/PreviewAllIcons.svelte) + +[frontend/src/lib/components/icon/PreviewAllIcons.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/icon/PreviewAllIcons.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/image/Image/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/image/Image/+page.md new file mode 100644 index 000000000..f35c5e7d9 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/image/Image/+page.md @@ -0,0 +1,21 @@ +# Image + +Display an `@openvaa/data: Image` object, automatically switching between dark and normal variants if available. + +### Properties + +- `image`: The `Image` object to display. +- `format`: The preferred format of the image. The default one will be used if the format is not defined or not available. +- Any valid attributes of a `<img>` element + +### Usage + +```tsx +<Image image={candidate.image} format="thumbnail" on:load={() => console.info('Loaded!')} /> +``` + +## Source + +[frontend/src/lib/components/image/Image.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/image/Image.svelte) + +[frontend/src/lib/components/image/Image.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/image/Image.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/infoAnswer/InfoAnswer/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/infoAnswer/InfoAnswer/+page.md new file mode 100644 index 000000000..5d2084d68 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/infoAnswer/InfoAnswer/+page.md @@ -0,0 +1,24 @@ +# InfoAnswer + +Used to display a possibly wrapped entity's answer to an info question. Depending on the question type it is rendered as a `<span>`, `<ol>` or `<a>` element. + +### Properties + +- `answer`: The possibly missing answer to the question. +- `question`: The info question object. +- `format`: How to format the answer. Default: `'default'` + - `default`: use the same format as in `<EntityDetails>`. + - `tag`: format the answers as a pill or tag. Nb. links are always rendered as tags. +- Any valid common attributes of an HTML element. + +### Usage + +```tsx +<InfoAnswer answer={candidate.getAnswer(question)} {question}/> +``` + +## Source + +[frontend/src/lib/components/infoAnswer/InfoAnswer.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/infoAnswer/InfoAnswer.svelte) + +[frontend/src/lib/components/infoAnswer/InfoAnswer.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/infoAnswer/InfoAnswer.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/infoBadge/InfoBadge/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/infoBadge/InfoBadge/+page.md new file mode 100644 index 000000000..a64b969d1 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/infoBadge/InfoBadge/+page.md @@ -0,0 +1,21 @@ +# InfoBadge + +Small badge component used to display information next to other components. + +### Properties + +- `text`: Text to be displayed in the badge. +- `classes`: Custom css classes. +- `disabled`: Whether the badge is disabled and rendered as grey. + +### Usage + +```tsx +<InfoBadge text="example" classes="p-10" /> +``` + +## Source + +[frontend/src/lib/components/infoBadge/InfoBadge.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/infoBadge/InfoBadge.svelte) + +[frontend/src/lib/components/infoBadge/InfoBadge.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/infoBadge/InfoBadge.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/Input/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/Input/+page.md new file mode 100644 index 000000000..5b9e90883 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/Input/+page.md @@ -0,0 +1,57 @@ +# Input + +Display any data input, its associated label and possible info. The HTML element used to for the input is defined by the `type` property. + +The input itself is wrapped in multiple container elements, the outermost of which can be passed the `containerProps` prop. + +### Properties + +- `type`: The type of input element to use. This also defines the type of the `value` prop, which of the other properties are allowed or required, and the HTML element rendered. + - `boolean`: A boolean toggle.render + - `date`: A date input. + - `image`: An image file input. + - `number`: A numeric input. + - `select`: A select dropdown. + - `select-multiple`: A select dropdown from which multiple options can be selected. See also the `ordered` prop. + - `text`: A single-line text input. + - `text-multilingual`: A multilingual single-line text input. + - `textarea`: A multi-line text input. + - `textarea-multilingual`: A multilingual multi-line text input. +- `label`: The label to show for the input or group of inputs if `multilingual`. +- `containerProps`: Any additional props to be passed to the container element of the input. +- `id`: The id of the input. If not provided, a unique id will be generated. +- `info`: Additional info displayed below the input. +- `disabled`: Works the same way as a normal `input`'s `disabled` attribute. +- `locked`: If `locked` the input will be disabled and a lock icon is displayed. +- `required`: If `true`, a badge will be displayed next to the input when its value is empty. @default false +- `value`: Bindable: the value of the input. Depends on the `type` prop. +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. @default false +- `options`: The options to show for a `select` or `select-multiple` input. +- `ordered`: If `true`, enables ordering of the values of a `select-multiple` input. @default false +- `maxFilesize`: The maximum file size for `image` inputs. @default `20 * 1024**2` (20MB) +- `multilingualInfo`: Additional info displayed below the input for multilingual input together with possible `info`. @default $t('components.input.multilingualInfo') +- Any valid attributes of the HTML element (`input`, `select` or `textarea`) used for the input, except in the case of `image` whose input is hidden. + +### Callbacks + +- `onChange`: Event handler triggered when the value changes with the new `value`. + +### Usage + +```tsx +<Input type="text" label="Name" placeholder="Enter your name" onChange={(v) => console.info(v)} /> + +<Input type="select-multiple" label="Favourite colours" ordered value={['c3', 'c1']} options={[ + { id: 'c1', label: 'Red' }, + { id: 'c2', label: 'Blue' }, + { id: 'c3', label: 'Green' }, + ]} + onChange={(v) => console.info(v)} + info="Select any number of colours in the order you prefer them." /> +``` + +## Source + +[frontend/src/lib/components/input/Input.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/input/Input.svelte) + +[frontend/src/lib/components/input/Input.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/input/Input.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/InputGroup/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/InputGroup/+page.md new file mode 100644 index 000000000..7be7b8498 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/InputGroup/+page.md @@ -0,0 +1,30 @@ +# InputGroup + +A componend used to group `Input`-components together. + +NB. Only single-row `Input`s are joined and they should not have the `info` property set. + +### Properties + +- `title`: Optional title for the group. +- `info`: Optional info text for the group. + +### Slots + +- default: The `Input` components to group. + +### Usage + +```tsx +<InputGroup title="Nominations" info="These are placeholders."> + <Input type="text" label="First nomination" /> + <Input type="text" label="Second nomination" /> + <Input type="text" label="Third nomination" /> +</InputGroup> +``` + +## Source + +[frontend/src/lib/components/input/InputGroup.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/input/InputGroup.svelte) + +[frontend/src/lib/components/input/InputGroup.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/input/InputGroup.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/PreviewAllInputs/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/PreviewAllInputs/+page.md new file mode 100644 index 000000000..946c2ecb4 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/PreviewAllInputs/+page.md @@ -0,0 +1,21 @@ +# PreviewAllInputs + +Preview component displaying all available input types. + +### Properties + +- `info`: Optional info text to display with inputs. +- `locked`: Whether inputs are locked. Default: `false` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<PreviewAllInputs info="Example info" locked={false} /> +``` + +## Source + +[frontend/src/lib/components/input/PreviewAllInputs.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/input/PreviewAllInputs.svelte) + +[frontend/src/lib/components/input/PreviewAllInputs.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/input/PreviewAllInputs.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/QuestionInput/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/QuestionInput/+page.md new file mode 100644 index 000000000..3c0d15de0 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/input/QuestionInput/+page.md @@ -0,0 +1,28 @@ +# QuestionInput + +A convenience wrapper for `Input` which fills in the necessary properties based on the info `Question` and possible `Answer` passed. + +NB. To show opinion `Question`s, use the `OpinionQuestionInput` component in `$lib/components/questions`. + +### Properties + +- `question`: The `Question` for which to show the input. Not reactive. +- `answer`: The `Answer` object to the question. Not reactive. +- `disableMultilingual`: If `true`, text inputs will not be multilingual. @default `false` +- Any properties of `Input`, except `choices`, `ordered` and `type`. Note that `label`, `id` and `info` are prefilled but may be overridden. + +### Callbacks + +- `onChange`: Event handler triggered when the value changes with the new `value`. + +### Usage + +```tsx +<QuestionInput {question} onChange={(v) => console.info(v)} /> +``` + +## Source + +[frontend/src/lib/components/input/QuestionInput.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/input/QuestionInput.svelte) + +[frontend/src/lib/components/input/QuestionInput.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/input/QuestionInput.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/loading/Loading/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/loading/Loading/+page.md new file mode 100644 index 000000000..90235d7b2 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/loading/Loading/+page.md @@ -0,0 +1,25 @@ +# Loading + +Used to display a loading spinner with an optionally visible text label. + +### Properties + +- `inline`: Whether to show an inline version of the spinner. By default the spinner tries to center itself in the available area. Default: `false` +- `label`: The label text. Default: `$t('common.loading')` +- `showLabel`: Whether to show the text label. The label will always be shown to screen readers. Default: `false` +- `size`: The size of the loading spinner. Default: `'lg'` +- Any valid attributes of a `<div>` element. + +### Usage + +```tsx +<Loading/> +<Loading size="md"/> +<Loading showLabel label="Loading custom stuff…"/> +``` + +## Source + +[frontend/src/lib/components/loading/Loading.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/loading/Loading.svelte) + +[frontend/src/lib/components/loading/Loading.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/loading/Loading.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/matchScore/MatchScore/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/matchScore/MatchScore/+page.md new file mode 100644 index 000000000..456dce85a --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/matchScore/MatchScore/+page.md @@ -0,0 +1,22 @@ +# MatchScore + +Display an entity's match score. + +### Properties + +- `score`: The match score as a `string` or a `number`. Note that `$t('components.matchScore.label')` will be used display the score. +- `label`: The label to display under the score. Default: `$t('components.matchScore.label')` +- `showLabel`: Whether to show the label. Default: `true` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<MatchScore score="25%" /> +``` + +## Source + +[frontend/src/lib/components/matchScore/MatchScore.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/matchScore/MatchScore.svelte) + +[frontend/src/lib/components/matchScore/MatchScore.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/matchScore/MatchScore.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/Modal/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/Modal/+page.md new file mode 100644 index 000000000..512f5fcf0 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/Modal/+page.md @@ -0,0 +1,67 @@ +# Modal + +A modal dialog. +See `<ModalContainer>` component for more information. + +### Slots + +- `actions`: The action buttons to display. +- default: The content of the modal. + +### Properties + +- `title`: The title of the modal +- `boxClass`: Optional classes to add to the dialog box itself. Note that the basic `class` property is applied to the `<dialog>` element, which is rarely needed. +- Any valid properties of a `<ModalContainer>` component. + +### Callbacks + +- `onClose`: Callback for when the modal closes. Note that the modal may still be transitioning to `hidden`. +- `onOpen`: Callback for when the modal opens. Note that the modal may still be transitioning from `hidden`. + +### Bindable functions + +- `openModal`: Opens the modal +- `closeModal`: Closes the modal + +### Accessibility + +See the `<ModalContainer>` component documentation for more information. + +### Usage + +```tsx +<script lang="ts"> + let openModal: () => void; + let closeModal: () => void; + let answer = '?'; + + // Will only set answer if it's not been set yet, because this will fire even when we close the modal ourselves + function setAnswer(a: string) { + if (answer == '?') answer = a; + } +</script> + +<Button on:click={openModal}>Open modal</Button> + +<h2>Answer: {answer}</h2> + +<Modal + bind:closeModal + bind:openModal + title="What's your answer?" + onOpen={() => answer = '?'} + onClose={() => setAnswer('No')}> + <p>Click below or hit ESC to exit.</p> + <div slot="actions" class="flex flex-col w-full max-w-md mx-auto"> + <Button on:click={() => {setAnswer('Yes'); closeModal();}} text="Yes" variant="main"/> + <Button on:click={closeModal} text="No"/> + </div> +</Modal> +``` + +## Source + +[frontend/src/lib/components/modal/Modal.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/Modal.svelte) + +[frontend/src/lib/components/modal/Modal.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/Modal.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/ModalContainer/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/ModalContainer/+page.md new file mode 100644 index 000000000..89a607e5b --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/ModalContainer/+page.md @@ -0,0 +1,54 @@ +# ModalContainer + +A modal dialog. + +### Slots + +- default: The modal. + +### Properties + +- `title`: The title of the modal +- `autofocusId`: Optional id of the element to autofocus when the dialog has opened. Note that this must be a focusable element. By default, the first focusable descendant will be focused. +- `closeOnBackdropClick`: Whether to allow closing the modal by clicking outside of it. @default `true` +- Any valid properties of a `<dialog>` element. + +### Callbacks + +- `onClose`: Callback for when the modal closes. Note that the modal may still be transitioning to `hidden`. +- `onOpen`: Callback for when the modal opens. Note that the modal may still be transitioning from `hidden`. + +### Bindable functions + +- `openModal`: Opens the modal +- `closeModal`: Closes the modal + +### Accessibility + +- The modal can be closed by pressing the `Escape` key. +- When opened, either the element defined by `autofocusId` or the first focusable descendant will be focused on. Note that if the contents of the moadl are long, it's recommended to use the `autofocusId` property and select an element that appears at the start of the dialog to focus. If this is not an interactive element, set `tabindex="-1"` for it. +- For more accessibility information, see [ARIA Dialog Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/#keyboardinteraction) + +### Usage + +```tsx +<script lang="ts"> + export let title; + export let isOpen; + export let closeModal; + export let openModal; +</script> + +<ModalContainer {...$$restProps} {title} bind:isOpen bind:closeModal bind:openModal> + <div class="modal-box"> + <h2 class="mb-lg text-center">{title}</h2> + <slot /> + </div> +</ModalContainer> +``` + +## Source + +[frontend/src/lib/components/modal/ModalContainer.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/ModalContainer.svelte) + +[frontend/src/lib/components/modal/ModalContainer.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/ModalContainer.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/confirmation/ConfirmationModal/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/confirmation/ConfirmationModal/+page.md new file mode 100644 index 000000000..86f6749a6 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/confirmation/ConfirmationModal/+page.md @@ -0,0 +1,50 @@ +# ConfirmationModal + +A modal dialog that will automatically close after a set amount of time. + +### Slots + +- `actions`: The action buttons to display. +- default: The content of the modal. + +### Properties + +- `title`: The title of the modal +- `timerDuration`: Logout timer duration in seconds. @default `30` +- `timeLeft`: Bind to this to get time left in seconds +- Any valid properties of a `<Modal>` component. + +### Callbacks + +- `onClose`: Callback for when the modal closes. Note that the modal may still be transitioning to `hidden`. +- `onOpen`: Callback for when the modal opens. Note that the modal may still be transitioning from `hidden`. +- `timeout`: Callback triggered right before the modal is closed due to a timeout. Note that the `onClose` callback will be triggered after this. + +### Bindable functions + +- `openModal`: Opens the modal +- `closeModal`: Closes the modal + +### Accessibility + +See the `<Modal>` component documentation for more information. + +### Usage + +```tsx +<TimedModal + bind:closeModal + title="Timout modal" + onOpen={() => console.info('Opened')} + onClose={() => console.info('Closed')} + onTimeout={() => console.info('Timeout!')}> + <p>Wait for it…</p> + <Button slot="actions" on:click={closeModal} text="Close" variant="main" /> +</TimedModal> +``` + +## Source + +[frontend/src/lib/components/modal/confirmation/ConfirmationModal.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/confirmation/ConfirmationModal.svelte) + +[frontend/src/lib/components/modal/confirmation/ConfirmationModal.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/confirmation/ConfirmationModal.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/drawer/Drawer/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/drawer/Drawer/+page.md new file mode 100644 index 000000000..5988a2126 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/drawer/Drawer/+page.md @@ -0,0 +1,42 @@ +# Drawer + +A modal dialog that looks like a drawer. + +### Slots + +- default: The content of the modal. + +### Properties + +- `title`: The title of the modal +- `isOpen`: A bindable property which is `true` when the drawer is open +- `showFloatingCloseButton`: Whether to show the floating close button. @default true +- Any valid properties of a `<ModalContainer>` component. + +### Callbacks + +- `onClose`: Callback for when the modal closes. Note that the modal may still be transitioning to `hidden`. +- `onOpen`: Callback for when the modal opens. Note that the modal may still be transitioning from `hidden`. + +### Bindable functions + +- `openModal`: Opens the modal +- `closeModal`: Closes the modal + +### Accessibility + +See the `<ModalContainer>` component documentation for more information. + +### Usage + +```tsx +<Drawer title="Drawer"> + <p>Drawer content</p> +</Drawer> +``` + +## Source + +[frontend/src/lib/components/modal/drawer/Drawer.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/drawer/Drawer.svelte) + +[frontend/src/lib/components/modal/drawer/Drawer.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/drawer/Drawer.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/timed/TimedModal/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/timed/TimedModal/+page.md new file mode 100644 index 000000000..5f5e73855 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/modal/timed/TimedModal/+page.md @@ -0,0 +1,50 @@ +# TimedModal + +A modal dialog that will automatically close after a set amount of time. + +### Slots + +- `actions`: The action buttons to display. +- default: The content of the modal. + +### Properties + +- `title`: The title of the modal +- `timerDuration`: Logout timer duration in seconds. @default `30` +- `timeLeft`: Bind to this to get time left in seconds +- Any valid properties of a `<Modal>` component. + +### Callbacks + +- `onClose`: Callback for when the modal closes. Note that the modal may still be transitioning to `hidden`. +- `onOpen`: Callback for when the modal opens. Note that the modal may still be transitioning from `hidden`. +- `timeout`: Callback triggered right before the modal is closed due to a timeout. Note that the `onClose` callback will be triggered after this. + +### Bindable functions + +- `openModal`: Opens the modal +- `closeModal`: Closes the modal + +### Accessibility + +See the `<Modal>` component documentation for more information. + +### Usage + +```tsx +<TimedModal + bind:closeModal + title="Timout modal" + onOpen={() => console.info('Opened')} + onClose={() => console.info('Closed')} + onTimeout={() => console.info('Timeout!')}> + <p>Wait for it…</p> + <Button slot="actions" on:click={closeModal} text="Close" variant="main" /> +</TimedModal> +``` + +## Source + +[frontend/src/lib/components/modal/timed/TimedModal.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/timed/TimedModal.svelte) + +[frontend/src/lib/components/modal/timed/TimedModal.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/timed/TimedModal.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/notification/Notification/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/notification/Notification/+page.md new file mode 100644 index 000000000..6d8f942aa --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/notification/Notification/+page.md @@ -0,0 +1,20 @@ +# Notification + +Show a notification popup to the user. + +### Properties + +- `data`: The data for the notification to show. +- Any valid properties of an `<Alert>` component. + +### Usage + +```tsx +<Notification data={$appSettings.notifications.voterApp} /> +``` + +## Source + +[frontend/src/lib/components/notification/Notification.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/notification/Notification.svelte) + +[frontend/src/lib/components/notification/Notification.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/notification/Notification.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/openVAALogo/OpenVAALogo/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/openVAALogo/OpenVAALogo/+page.md new file mode 100644 index 000000000..a865c9475 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/openVAALogo/OpenVAALogo/+page.md @@ -0,0 +1,27 @@ +# OpenVAALogo + +Display the OpenVAA logo. You can define the `color` and `size` of the logo using +predefined values. + +The logo is rendered as a `<svg>` element, and you can also pass any valid +attributes of one. + +### Properties + +- `title`: The `<title>` of the SVG logo. Functions much the same way as the `alt` attribute of an `<img>`. Default: `'OpenVAA'` +- `size`: The size of the logo as one of the predefined sizes 'sm', 'md' or 'lg'. For arbitrary values, you can supply a `class` property, such as `h-[3.15rem] w-[3.15rem]`. Default: `'md'` +- `color`: The color of the logo as one of the predefined colours. For arbitrary values, you can supply a `class` property, such as `fill-[#123456]`. Default: `'neutral'` +- Any valid attributes of a `<svg>` element. + +### Usage + +```tsx +<OpenVAALogo/> +<OpenVAALogo color="secondary" size="sm"/> +``` + +## Source + +[frontend/src/lib/components/openVAALogo/OpenVAALogo.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/openVAALogo/OpenVAALogo.svelte) + +[frontend/src/lib/components/openVAALogo/OpenVAALogo.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/openVAALogo/OpenVAALogo.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/preventNavigation/PreventNavigation/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/preventNavigation/PreventNavigation/+page.md new file mode 100644 index 000000000..dc503d89b --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/preventNavigation/PreventNavigation/+page.md @@ -0,0 +1,21 @@ +# PreventNavigation + +Functional component used to block user nagivation. + +### Properties + +- `active`: Whether the navigation should be prevented or not. This can also be callback to cater for changes that would required re-rendering the component. +- `onCancel`: A callback function that is called when the navigation is about to be cancelled. +- `onConfirm`: A callback function that is called when the navigation is about to be confirmed. + +### Usage + +```tsx +<PreventNavigation active={true} onCancel={resetAnswers} /> +``` + +## Source + +[frontend/src/lib/components/preventNavigation/PreventNavigation.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/preventNavigation/PreventNavigation.svelte) + +[frontend/src/lib/components/preventNavigation/PreventNavigation.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/preventNavigation/PreventNavigation.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/OpinionQuestionInput/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/OpinionQuestionInput/+page.md new file mode 100644 index 000000000..da8d73f36 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/OpinionQuestionInput/+page.md @@ -0,0 +1,35 @@ +# OpinionQuestionInput + +Display an opinion `Question`'s answering input. Shows an error if the question is of an unsupported type. + +NB. The layout differs from the `QuestionInput` component, which is used for info questions. + +### Properties + +- `question`: The opinion `Question` for which to show the input. Not reactive. +- `answer`: The `Answer` object to the question. Not reactive. +- `mode`: The same component can be used both for answering the questions and displaying answers. @default `'answer'` +- `otherAnswer`:The `Answer` of the other entity in `display` mode. @default undefined +- `otherLabel`: The label for the entity's answer. Be sure to supply this if `otherSelected` is supplied. +- Any properties of `QuestionInput`. + +### Usage + +```tsx +<OpinionQuestionInput + {question} + answer={$voterAnswers[question.id]} + onChange={answerQuestion} /> +<OpinionQuestionInput + {question} + mode="display" + answer={$voterAnswers[question.id]} + otherAnswer={candidate.getAnswer(question)} + otherLabel={$t('candidateApp.common.candidateAnswerLabel')} /> +``` + +## Source + +[frontend/src/lib/components/questions/OpinionQuestionInput.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/OpinionQuestionInput.svelte) + +[frontend/src/lib/components/questions/OpinionQuestionInput.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/OpinionQuestionInput.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionActions/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionActions/+page.md new file mode 100644 index 000000000..20c56f015 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionActions/+page.md @@ -0,0 +1,43 @@ +# QuestionActions + +Display a question's secondary actions, such as skip. + +### Properties + +- `answered`: Set to `true` if the question has already been answered. This controls which actions are shown. @default `false` +- `disabled`: Whether to disable all the actions. @default `false` +- `disablePrevious`: Whether to disable the previous button. @default `false` +- `variant`: Use to switch between looser and tighter layouts. @default `'default'` +- `separateSkip`: Whether to separate `skip` and `next` actions both as events and button symbols. @default `false` +- `nextLabel`: The text label for the `next` button. @default `$t('questions.next')` or `$t('questions.skip')` +- `previousLabel`: The text label for the `previous` button. @default `$t('questions.previous')` +- Any valid properties of a `<div>` element + +### Callbacks + +If `separateSkip` is set to `true`, the `onNext` callback is switched to a `onSkip` callback if the user has not yet answered the question. Otherwise, only `onNext` callbacks will be triggered. + +- `onDelete`: Triggered when the user has clicked on the delete answer button. This is only available if `answered` is `true`. +- `onNext`: Triggered when the user has clicked on the next button. This is only available if `answered` is `true` or `separateSkip` is `true`. +- `onPrevious`: Triggered when the user has clicked on the previous button. +- `onSkip`: Triggered when user has clicked on the skip button. This is only available if `answered` is `false` and `separateSkip` is `true`. + +### Usage + +```tsx +<QuestionActions + answered={voterAnswer != null} + separateSkip={true} + variant="tight" + onPrevious={gotoPreviousQuestion} + onDelete={deleteAnswer} + onNext={gotoNextQuestion} + onSkip={skipQuestion} +/> +``` + +## Source + +[frontend/src/lib/components/questions/QuestionActions.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionActions.svelte) + +[frontend/src/lib/components/questions/QuestionActions.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionActions.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionBasicInfo/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionBasicInfo/+page.md new file mode 100644 index 000000000..4e1401314 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionBasicInfo/+page.md @@ -0,0 +1,22 @@ +# QuestionBasicInfo + +Display the question's expandable information content. + +### Properties + +- `info`: The info content to show as a plain or HTML string. +- `onCollapse`: A callback triggered when the info content is collapsed. Mostly used for tracking. +- `onExpand`: A callback triggered when the info content is expanded. Mostly used for tracking. +- Any valid properties of an `<Expander>` component + +### Usage + +```tsx +<QuestionInfo {info}/> +``` + +## Source + +[frontend/src/lib/components/questions/QuestionBasicInfo.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionBasicInfo.svelte) + +[frontend/src/lib/components/questions/QuestionBasicInfo.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionBasicInfo.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionChoices/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionChoices/+page.md new file mode 100644 index 000000000..876c2b4d0 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionChoices/+page.md @@ -0,0 +1,65 @@ +# QuestionChoices + +Display the buttons used for answering Likert and other single choice questions. + +The buttons are rendered as `<input type="radio">` elements contained inside a `<fieldset>`. Consider passing an `aria-labelledby` pointing to the question or an `aria-label`. + +The buttons for ordinal questions are by default displayed horizontally and with a line connecting them, while categorical ones are displayed vertically using a larger text size and without a line. These can be overriden by setting the relevant properties. The vertical layout should always be used for choices with long labels. + +The radio buttons' behaviour is as follows when using a pointer or touch device: + +1. Selecting an option triggers the `onChange` callback +2. Clicking on the selected radio button triggers the `onReselect` callback + +Keyboard navigation works in the following way: + +1. `Tab` focuses the whole radio group +2. The arrow keys change the focused radio button and _select_ it at the same time +3. When the keyboard focus leaves the radio group, either of the callbacks is triggered, depending on whether value has been changed or not +4. The event is also dispatched when the user presses the `Space` or `Enter` key + +### Display mode + +The same component can also be used to display the answers of the voter and another entity by setting `mode` to `'display'` and supplying `otherSelected` and `otherLabel`. In this case the buttons cannot be selected. + +### Properties + +- `name`: The `name` of the radio group. Usually the question's id +- `choices`: The `key`-`label` pairs of the radio buttons +- `disabled`: Whether to disable all the buttons. @default `false` +- `mode`: The same component can be used both for answering the questions and displaying answers. @default `'answer'` +- `selectedId`: The initially selected key of the radio group. +- `otherSelected`: The answer key of the entity in display mode. +- `otherLabel`: The label for the entity's answer. Be sure to supply this if `otherSelected` is supplied. +- `showLine`: Whether to show a line connecting the choices. @default `true` for ordinal questions, and `false` for categorical questions +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. @default `false` +- `variant`: Defines the layout variant of the buttons. The `vertical` variant can be used for questions with longer labels. @default `'horizontal'` for ordinal questions, and `'vertical'` for categorical questions. +- Any valid attributes of a `<fieldset>` element + +### Callbacks + +- `onReselect`: Triggered when user has clicked on the same radio button that was initially selected. +- `onChange`: Triggered when the user has clicked on a different radio button than which was initially selected or there was no selected value initially. + +### Usage + +```tsx +<QuestionChoices + {question} + selectedId={$voterAnswers[question.id]} + onChange={answerQuestion} + onReselect={doFoo} /> + +<QuestionChoices + {question} + mode="display" + selectedId={$voterAnswers[question.id]} + otherSelected={candidateAnswer} + otherLabel={$t('candidateApp.common.candidateAnswerLabel')} /> +``` + +## Source + +[frontend/src/lib/components/questions/QuestionChoices.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionChoices.svelte) + +[frontend/src/lib/components/questions/QuestionChoices.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionChoices.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfo/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfo/+page.md new file mode 100644 index 000000000..57216d3a6 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfo/+page.md @@ -0,0 +1,27 @@ +# QuestionExtendedInfo + +Display the question's expandable information content. + +### Properties + +- `title`: The title for the info, usually the question text. +- `info`: The info content to show as a plain or HTML string. +- `infoSections`: An array of objects with `title` and `content` properties to show as expandable sections. +- Any valid properties of a `<div>` element + +### Callback properties + +- `onSectionCollapse`: A callback triggered when an info section is collapsed. Mostly used for tracking. +- `onSectionExpand`: A callback triggered when an info section is expanded. Mostly used for tracking. + +### Usage + +```tsx +<QuestionExtendedInfo info={question.info} infoSections={customData.infoSections} /> +``` + +## Source + +[frontend/src/lib/components/questions/QuestionExtendedInfo.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionExtendedInfo.svelte) + +[frontend/src/lib/components/questions/QuestionExtendedInfo.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionExtendedInfo.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfoButton/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfoButton/+page.md new file mode 100644 index 000000000..ef1879a67 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfoButton/+page.md @@ -0,0 +1,26 @@ +# QuestionExtendedInfoButton + +A button that will display the question's extended information content in a `Drawer`. + +### Properties + +- `question`: The question whose expanded info to show. +- Any valid properties of a `<Button>` component + +### Callback properties + +- `onOpen`: A callback function to be executed when the drawer is opened, mostly for tracking. +- `onSectionCollapse`: A callback triggered when an info section is collapsed. Mostly used for tracking. +- `onSectionExpand`: A callback triggered when an info section is expanded. Mostly used for tracking. + +### Usage + +```tsx +<QuestionExtendedInfoButton {question} /> +``` + +## Source + +[frontend/src/lib/components/questions/QuestionExtendedInfoButton.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionExtendedInfoButton.svelte) + +[frontend/src/lib/components/questions/QuestionExtendedInfoButton.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionExtendedInfoButton.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfoDrawer/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfoDrawer/+page.md new file mode 100644 index 000000000..51ff4633d --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionExtendedInfoDrawer/+page.md @@ -0,0 +1,25 @@ +# QuestionExtendedInfoDrawer + +A `Drawer` that displays the question's extended information. + +### Properties + +- `question`: The question whose expanded info to show. +- Any valid properties of a `<Drawer>` component + +### Callback properties + +- `onSectionCollapse`: A callback triggered when an info section is collapsed. Mostly used for tracking. +- `onSectionExpand`: A callback triggered when an info section is expanded. Mostly used for tracking. + +### Usage + +```tsx +<QuestionExtendedInfoDrawer {question} onClose={() => console.info('Closed!')}/> +``` + +## Source + +[frontend/src/lib/components/questions/QuestionExtendedInfoDrawer.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionExtendedInfoDrawer.svelte) + +[frontend/src/lib/components/questions/QuestionExtendedInfoDrawer.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionExtendedInfoDrawer.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionOpenAnswer/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionOpenAnswer/+page.md new file mode 100644 index 000000000..e9b099142 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/questions/QuestionOpenAnswer/+page.md @@ -0,0 +1,19 @@ +# QuestionOpenAnswer + +Display an `Entity`’s open answer to a question. If the content is empty, nothing will be rendered. + +### Properties + +- Any valid properties of an `<Expander>` component + +### Usage + +```tsx +<QuestionOpenAnswer content={openAnswer} /> +``` + +## Source + +[frontend/src/lib/components/questions/QuestionOpenAnswer.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionOpenAnswer.svelte) + +[frontend/src/lib/components/questions/QuestionOpenAnswer.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/questions/QuestionOpenAnswer.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/scoreGauge/ScoreGauge/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/scoreGauge/ScoreGauge/+page.md new file mode 100644 index 000000000..de4959d17 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/scoreGauge/ScoreGauge/+page.md @@ -0,0 +1,27 @@ +# ScoreGauge + +Show a radial or a linear score gauge for a sub-match. + +### Properties + +- `score`: The score of the gauge in the range from 0 to `max`, usually 100. +- `max`: The maximum value of the gauge. @default 100 +- `label`: The text label for the gauge, e.g. the name of the category. +- `variant`: The format of the gauge. @default 'linear' +- `showScore`: Whether to also show the score as numbers. @default true +- `unit`: The string to add to the score if it's shown, e.g. '%'. @default '' +- `colors`: The colors of the gauge. @default 'oklch(var(--n))' i.e. the `neutral` color. +- Any valid attributes of a `<div>` element + +```tsx +<ScoreGauge score={23} label={category.name} + color={category.color} colorDark={category.colorDark} + variant="radial"/> +<ScoreGauge score={23} label={category.name}/> +``` + +## Source + +[frontend/src/lib/components/scoreGauge/ScoreGauge.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/scoreGauge/ScoreGauge.svelte) + +[frontend/src/lib/components/scoreGauge/ScoreGauge.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/scoreGauge/ScoreGauge.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/select/Select/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/select/Select/+page.md new file mode 100644 index 000000000..0be60fdf4 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/select/Select/+page.md @@ -0,0 +1,40 @@ +# Select + +# Select component with list autocomplete support + +Displays a select input with optional autocomplete support. + +If there’s only one option available, the selected value will be set automatically and a non-interactive 'input' will be displayed. + +### Properties + +- `label`: The `aria-label` and `placeholder` text for the select input. +- `options`: The list of selectable options. You can provide an array of objects with `id` and `label` properties, or an array of strings in which case the ids will be the same as the labels. +- `selected`: A bindable value for the id of the selected option. @default the only option if there’s only one, `undefined` otherwise. +- `onChange`: A callback function triggered when the selection changes. +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. @default `false` +- `autocomplete`: Controls autocomplete behavior; supported values: `on` or `off`. @default `off` +- Any valid attributes of a `<select>` element (although when `autocomplete` is set to `on`, the props will be passed to a similarly styled `<input>` element). + +### Accessiblity + +In the `autocomplete` mode, the input can be controlled using the keyboard: + +- Move between options using the `Up` and `Down` arrow keys. If `Shift` is pressed, the focus moves by 10. The focused option is scrolled into view and outlined. +- Select an option using the `Enter` key +- The focused option is automatically selected when defocusing the input by `Tab` +- Defocus without selecting with `Esc` + +The component follows the [WGAI Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-both/). + +### Usage + +```tsx +<Select label="Select option" options={[{ id: '1', label: 'Label' }]} bind:selected autocomplete="on" /> +``` + +## Source + +[frontend/src/lib/components/select/Select.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/select/Select.svelte) + +[frontend/src/lib/components/select/Select.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/select/Select.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/subMatches/SubMatches/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/subMatches/SubMatches/+page.md new file mode 100644 index 000000000..519312fd8 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/subMatches/SubMatches/+page.md @@ -0,0 +1,21 @@ +# SubMatches + +Display an entity's sub-matches. + +### Properties + +- `matches`: The `SubMatch`es of a `Match`. +- `variant`: Variant layout, controlling the spacing of gauges. @default `'tight'` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<SubMatches matches={ranking.subMatches} /> +``` + +## Source + +[frontend/src/lib/components/subMatches/SubMatches.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/subMatches/SubMatches.svelte) + +[frontend/src/lib/components/subMatches/SubMatches.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/subMatches/SubMatches.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/successMessage/SuccessMessage/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/successMessage/SuccessMessage/+page.md new file mode 100644 index 000000000..5f3b2a78f --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/successMessage/SuccessMessage/+page.md @@ -0,0 +1,22 @@ +# SuccessMessage + +Used to display a message when an action succeeds. + +### Properties + +- `inline`: Whether to show an inline version of the message. By default the message tries to center itself in the available area and displays a large emoji. Default: `false` +- `message`: The message to display. Default: `$t('common.success')` +- Any valid attributes of a `<div>` element. + +### Usage + +```tsx +<SuccessMessage /> +<SuccessMessage inline message="Password saved!" /> +``` + +## Source + +[frontend/src/lib/components/successMessage/SuccessMessage.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/successMessage/SuccessMessage.svelte) + +[frontend/src/lib/components/successMessage/SuccessMessage.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/successMessage/SuccessMessage.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/tabs/Tabs/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/tabs/Tabs/+page.md new file mode 100644 index 000000000..3dff742cd --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/tabs/Tabs/+page.md @@ -0,0 +1,30 @@ +# Tabs + +Show a tab title bar that can be used to switch between different tabs. + +### Properties + +- `tabs`: The titles of the tabs. +- `activeIndex`: The index of the active tab. Bind to this to change or read the active tab. @default 0 +- Any valid attributes of a `<ul>` element + +### Callbacks + +- `onChange`: Callback for when the active tab changes. The event `details` contains the active tab as `tab` as well as its `index`. Note, it's preferable to just bind to the `activeTab` property instead. + +### Accessibility + +- The tab can be activated by pressing `Enter` or `Space`. +- TODO[a11y]: Add support for keyboard navigation with the arrow keys. + +### Usage + +```tsx +<Tabs bind:activeIndex tabs={['Basic Info', 'Opinions']} /> +``` + +## Source + +[frontend/src/lib/components/tabs/Tabs.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/tabs/Tabs.svelte) + +[frontend/src/lib/components/tabs/Tabs.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/tabs/Tabs.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/term/Term/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/term/Term/+page.md new file mode 100644 index 000000000..7f03203c1 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/term/Term/+page.md @@ -0,0 +1,27 @@ +# Term + +Show a definition popup when hovering over a term. + +### Properties + +- `definition`: The text to show in the definition popup. +- `position`: Position of the tooltip relative to the term. Default: `'bottom'` +- `showUnderline`: Whether to show the underline styling. Default: `true` +- `forceShow`: Whether to force show the tooltip. Default: `false` +- Any valid attributes of a `span` element. + +### Accessibility + +Uses the `term` and `definition` roles. + +### Usage + +```tsx +<Term definition="Hovering is an act of levitation">Hover over me</Term> +``` + +## Source + +[frontend/src/lib/components/term/Term.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/term/Term.svelte) + +[frontend/src/lib/components/term/Term.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/term/Term.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/toggle/Toggle/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/toggle/Toggle/+page.md new file mode 100644 index 000000000..283f69ffc --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/toggle/Toggle/+page.md @@ -0,0 +1,57 @@ +# Toggle + +Display a short list of options as toggleable text or icon buttons from which one can be selected. Semantically a `<fieldset>` with radio buttons. + +### Properties + +- `label`: The aria label for the toggle. +- `options`: The options for the toggle. Each must contain a `key` and a `label` property. If an `icon` property is provided, the option will be rendered as an icon button. The `label` is still required and will be used for a screen-reader-only label. +- Any valid attributes of a `<fieldset>` element. + +### Bindable properties + +- `selected`: The currently selected option `key` of the toggle. Bind to this to get the currently selected value. + +### Usage + +```tsx +<script lang="ts"> + // Text toggle + const options = [ + { + label: 'Text', + key: 'text' + }, + { + label: 'Video', + key: 'video' + } + ]; + let selected: string; +</script> +<Toggle bind:selected label="Switch between video and text display" {options}/> + +<script lang="ts"> + // Icon toggle + const iconOptions = [ + { + label: 'Text', + icon: 'text', + key: 'text' + }, + { + label: 'Video', + icon: 'video', + key: 'video' + } + ]; + let selected: string; +</script> +<Toggle bind:selected label="Switch between video and text display" {options}/> +``` + +## Source + +[frontend/src/lib/components/toggle/Toggle.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/toggle/Toggle.svelte) + +[frontend/src/lib/components/toggle/Toggle.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/toggle/Toggle.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/video/Video/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/video/Video/+page.md new file mode 100644 index 000000000..f0816b47f --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/video/Video/+page.md @@ -0,0 +1,80 @@ +# Video + +A video player that also includes a switcher between the video and a text transcript. The player also supports a variety of controls that mimic social media video controls. + +The player can be initialized without providing any content in which case it will be hidden until the content is provided using the `load` function. + +It's best to supply a number of sources, such as `mp4` and `webm` to support different browsers and devices. You also need to supply a `poster` image and VTT `captions` for accessibility reasons. `aspectRatio` is required for sizing the player correctly. A text `transcript` is recommended to be supplied, but if it's missing, one will be created from the `captions`. + +You can hide some of the controls using the `hideControls` property, in which case you should implement the functionality otherwise by binding to the control functions (see below). + +The player will try to unmute the video when the user first interacts with it. You can disable this by setting `autoUnmute` to `false`. + +User choices are stored in the `videoPreferences` store so that they persist across page loads. The preferences included are `muted`, `textTracksHidden` and `transcriptVisible`. + +### Content properties + +If not provided, the `video` element will be hidden until these properties are provided using the `load` function. + +- `title`: The title of the video for labelling. +- `sources`: The source URLs of the video. +- `captions`: The source URL for the video's captions. +- `poster`: The poster image URL for the video. +- `aspectRatio`: The aspect ratio of the video. This is needed so that the component can be sized correctly even before the data is loaded. Default: `1/1` +- `transcript`: Optional transcript text for the video as a HTML string. If empty, `captions` will be used instead. + +### Other properties + +- `hideControls`: The controls to hide. All are shown if the list is not defined. Default: `undefined` +- `autoPlay`: Whether to autoPlay the video (when content is provided). Default: `true` +- `autoUnmute`: Whether to automatically try to unmute the video when the user interacts with it. Default: `true` +- `showCaptions`: Whether to show captions by default. Default: `true` +- `showTranscript`: Whether to show the transcript instead of the video by default. Default: `false` +- `skipByCue`: Whether to skip using the captions' cues. Default: `true` +- `skipAmount`: The amount in seconds to skip if not using cues. Default: `10` + +### Callbacks + +- `onTrack`: A callback triggered when the video tracking event should be sent. Send to `TrackingService.startEvent` or `track` to track. +- `onEnded`: Forwarded from the `<video>` element. + +### Bindable properties + +- `atEnd`: Bindable: Whether the video is at the end (with a small margin) +- `mode`: Bindable: Whether the video or the transcript is visible. + +### Bindable functions + +- `togglePlay`: Toggle video playback or replay. +- `toggleSound`: Toggle sound +- `toggleCaptions`: Show or hide captions. +- `toggleTranscript`: Toggle transcript visibility. +- `jump`: Skip the video a number of steps based on text track cues or `skipAmount` if cues are not available. If the video is in the end, a `steps` of `-1` will be skip to the beginning of the last cue. If `steps` would result in a negative index or one greater than the number of cues, the video will be scrolled to the beginning or the end. +- `gotoAndPlay`: Scroll the video to the given time and play. +- `load`: Change the video contents, i.e. sources, captions, poster and transcript, and optionally other properties. + +### Tracking events + +- `video`: The video player creates an analytics event for each video viewed which combines a number of properties. See the `VideoTrackingEventData` in `Video.type.ts` for a complete description. The event is started and submitted when: + - the component is created/destroyed + - when the video shown is changed with `reload` + - when the page's visibility changes to `hidden`. + +### Usage + +```tsx +<Video + title="Video title" + sources={['https://openvaa.org/video.webm', 'https://openvaa.org/video.mp4']} + captions="https://openvaa.org/video.vtt" + poster="https://openvaa.org/video.jpg" + aspectRatio={4 / 5} + transcript="<p>Transcript text</p>" +/> +``` + +## Source + +[frontend/src/lib/components/video/Video.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/video/Video.svelte) + +[frontend/src/lib/components/video/Video.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/video/Video.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/warning/Warning/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/warning/Warning/+page.md new file mode 100644 index 000000000..6aa7168fd --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/components/warning/Warning/+page.md @@ -0,0 +1,19 @@ +# Warning + +A simple warning component to be used with BasicPage + +### Properties + +- Any valid attributes of a `div` element. + +### Usage + +```tsx +<Warning>Caveat emptor!</Warning> +``` + +## Source + +[frontend/src/lib/components/warning/Warning.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/warning/Warning.svelte) + +[frontend/src/lib/components/warning/Warning.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/warning/Warning.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/appLogo/AppLogo/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/appLogo/AppLogo/+page.md new file mode 100644 index 000000000..cec2a443f --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/appLogo/AppLogo/+page.md @@ -0,0 +1,31 @@ +# AppLogo + +A template part that is used to show the application's logo. The logo colour changes dynamically based on whether the light or dark mode is active. + +Logo files for use on a light and a dark background can be defined. If the latter is not supplied an `invert` filter will be applied. If no logo files are supplied, the OpenVAA logo will be used. + +### Dynamic component + +- Access `AppContext` to get `appCustomization`. + +### Properties + +- `alt`: The `alt` text for the logo image. If missing, the publisher name or 'OpenVAA' will be used, depending on the logo shown. +- `inverse`: If `true`, the light and dark versions of the logo will be reversed. Set to `true` if using the logo on a dark background. Default: `false` +- `size`: The size of the logo as one of the predefined sizes 'sm', 'md' or 'lg'. For arbitrary values, you can supply a `class` attribute, such as `class="h-[3.5rem]"`. Default: `'md'` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<AppLogo size="lg"/> +<div class="bg-primary"> + <AppLogo inverse/> +</div> +``` + +## Source + +[frontend/src/lib/dynamic-components/appLogo/AppLogo.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/appLogo/AppLogo.svelte) + +[frontend/src/lib/dynamic-components/appLogo/AppLogo.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/appLogo/AppLogo.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/dataConsent/DataConsent/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/dataConsent/DataConsent/+page.md new file mode 100644 index 000000000..490d07adc --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/dataConsent/DataConsent/+page.md @@ -0,0 +1,33 @@ +# DataConsent + +Show buttons opting in or out of data collection and possibly information about data collection. + +### Dynamic component + +Accesses `AppContext` to set and read `userPreferences`. + +### Properties + +- `description`: Whether and how to show the data consent description. Default: `'modal'` + - `'none'`: Don't show the description. + - `'inline'`: Show the consent description above the buttons. + - `'modal'`: Show a button that opens the description in a modal. +- Any valid attributes of a `<div>` element. + +### Events + +- `change`: Fired when the user changes their data collection consent. The event `detail` cóntains: + - `consent`: the new consent value. + +### Usage + +```tsx +<DataConsent/> +<DataConsent description="none"/> +``` + +## Source + +[frontend/src/lib/dynamic-components/dataConsent/DataConsent.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/dataConsent/DataConsent.svelte) + +[frontend/src/lib/dynamic-components/dataConsent/DataConsent.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/dataConsent/DataConsent.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/dataConsent/DataConsentInfoButton/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/dataConsent/DataConsentInfoButton/+page.md new file mode 100644 index 000000000..77e7018d0 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/dataConsent/DataConsentInfoButton/+page.md @@ -0,0 +1,24 @@ +# DataConsentInfoButton + +Show a button that opens a modal describing the data the app collects. + +### Dynamic component + +Accesses `AppContext` to read `appSettings`. + +### Properties + +- Any valid properties of a `<Button>` component. + +### Usage + +```tsx +<DataConsentInfoButton/> +<DataConsentInfoButton class="!inline"/> +``` + +## Source + +[frontend/src/lib/dynamic-components/dataConsent/DataConsentInfoButton.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/dataConsent/DataConsentInfoButton.svelte) + +[frontend/src/lib/dynamic-components/dataConsent/DataConsentInfoButton.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/dataConsent/DataConsentInfoButton.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/dataConsent/popup/DataConsentPopup/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/dataConsent/popup/DataConsentPopup/+page.md new file mode 100644 index 000000000..83b0c1f48 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/dataConsent/popup/DataConsentPopup/+page.md @@ -0,0 +1,19 @@ +# DataConsentPopup + +Show a popup with a data consent form, if data consent has not been given yet. + +### Properties + +- Any valid properties of an `<Alert>` component. + +### Usage + +```tsx +<DataConsentPopup /> +``` + +## Source + +[frontend/src/lib/dynamic-components/dataConsent/popup/DataConsentPopup.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/dataConsent/popup/DataConsentPopup.svelte) + +[frontend/src/lib/dynamic-components/dataConsent/popup/DataConsentPopup.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/dataConsent/popup/DataConsentPopup.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityCard/EntityCard/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityCard/EntityCard/+page.md new file mode 100644 index 000000000..66350ed50 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityCard/EntityCard/+page.md @@ -0,0 +1,47 @@ +# EntityCard + +A card for displaying a possibly wrapped `Entity`, i.e. a candidate or a party, nomination thereof, or a match, in a list or as part of an `Entity`'s details, possibly including a matching score, sub-matches, nested `EntityCard`s and answers to specified questions. + +In nested cards, the layout and rendering of contents varies from that of a parent card to make the layout more compact. + +- Some elements are smaller and the title is rendered as `<h4>` instead of `<h3>`. +- The election symbol is shown next to the title. +- Nested nakedEntity cards are not rendered. + +### Dynamic component + +This is a dynamic component, because it accesses the `dataRoot` and other properties of the `AppContext` as well as the `VoterContext` if used within the `voter` app. + +### Properties + +- `action`: Custom action to take when the card is clicked, defaults to a link to the entity's `ResultEntity` route. If the card has subentites, the action will only be triggered by clicking the content above them. +- `entity`: A possibly ranked entity, e.g. candidate or a party. +- `variant`: The context-dependend layout variant. Usually set automatically. Default: `'list'` + - `'list'`: In a list of entities. + - `'details'`: As part of the header of `EntityDetails`. + - `'subcard'`: In a list of nested entity cards, e.g., the candidates for a party. +- `maxSubcards`: The maximum number of sub-entities to show. If there are more a button will be shown for displaying the remaining ones. Default: `3` +- `showElection`: Whether to show the possible nomination's election and constituency. Default: `false` +- Any valid attributes of an `<article>` element. + +### Tracking events + +- `entityCard_expandSubcards`: Fired when the list of sub-entities is expanded. + +### Accessibility + +- Currently, keyboard navigation is non-hierarchical even when subcards are present. In the future, this should be expanded into a more elaborate system where arrow keys or such can be used to navigate within a card with subcards. + +### Usage + +```tsx +<EntityCard action={$getRoute({route: 'ResultsCandidate', entityId: candidate.id})} + content={candidate}> +<EntityCard content={party} variant="details"> +``` + +## Source + +[frontend/src/lib/dynamic-components/entityCard/EntityCard.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityCard/EntityCard.svelte) + +[frontend/src/lib/dynamic-components/entityCard/EntityCard.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityCard/EntityCard.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityCard/EntityCardAction/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityCard/EntityCardAction/+page.md new file mode 100644 index 000000000..36c0970ff --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityCard/EntityCardAction/+page.md @@ -0,0 +1,29 @@ +# EntityCardAction + +A simple utility component for possibly wrapping content in an action handler. + +TODO[Svelte 5]: Maybe convert into `$snippet`. + +### Properties + +- `action`: The action to take when the part or card is clicked. +- `shadeOnHover`: Whether to shade the element on hover. Use when applying to subcards or their parent card's header. Default: `false` +- Any valid attributes common to HTML elements. Note that these will only be applied if `<EntityCardAction>` is rendered. + +### Slots + +– default: The contents to wrap. + +### Usage + +```tsx +<EntityCardAction action={$getRoute({ route: 'ResultsCandidate', entityId: candidate.id })}> + Content here +</EntityCardAction> +``` + +## Source + +[frontend/src/lib/dynamic-components/entityCard/EntityCardAction.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityCard/EntityCardAction.svelte) + +[frontend/src/lib/dynamic-components/entityCard/EntityCardAction.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityCard/EntityCardAction.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityChildren/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityChildren/+page.md new file mode 100644 index 000000000..ea090cf01 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityChildren/+page.md @@ -0,0 +1,22 @@ +# EntityChildren + +Used to show an entity's children in an `EntityDetails` component. + +### Properties + +- `entities`: An array of possibly ranked entities, e.g. a party's candidates. +- `entityType`: The type of the entities being displayed. Used to pick correct translations. +- `action`: An optional callback for building the card actions for the child possible entities. If nullish, the default action filled in by `EntityCard` will be used. If `false`, no actions will be added. +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<EntityChildren entities={organizationNomination.nominatedCandidates} /> +``` + +## Source + +[frontend/src/lib/dynamic-components/entityDetails/EntityChildren.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/EntityChildren.svelte) + +[frontend/src/lib/dynamic-components/entityDetails/EntityChildren.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/EntityChildren.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityDetails/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityDetails/+page.md new file mode 100644 index 000000000..e90754742 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityDetails/+page.md @@ -0,0 +1,36 @@ +# EntityDetails + +Used to show an entity's details, possibly including their answers to `info` questions, `opinion` questions and their child nominations. You can supply either a naked entity or a ranking containing an entity. + +If the provided entity is a (possibly matched) nomination, the questions to include will be those applicable to the election and constiuency of the nomination. + +If `AppContext.$appType` is `voter`, the voter’s possible answers will included in the `opinions` tab. + +### Dynamic component + +This is a dynamic component, because it accesses the `dataRoot` and other properties of the `AppContext` as well as the `VoterContext` if used within the `voter` app. + +### Properties + +- `entity`: A possibly ranked entity, e.g. candidate or a party. +- Any valid attributes of an `<article>` element. + +### Tracking events + +- `entityDetails_changeTab`: Fired when the user changes the active tab. Has a `section` property with the name of the tab. + +### Usage + +```tsx +<EntityDetails + entity={matchedCandidate}/> +<EntityDetails + entity={matchedOrganization} + tabs={$appSettings.entityDetails.contents.organization}/> +``` + +## Source + +[frontend/src/lib/dynamic-components/entityDetails/EntityDetails.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/EntityDetails.svelte) + +[frontend/src/lib/dynamic-components/entityDetails/EntityDetails.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/EntityDetails.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityDetailsDrawer/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityDetailsDrawer/+page.md new file mode 100644 index 000000000..d07d2f18c --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityDetailsDrawer/+page.md @@ -0,0 +1,20 @@ +# EntityDetailsDrawer + +A `Drawer` that displays `EntityDetails`. + +### Properties + +- `entity`: A possibly ranked entity, e.g. candidate or a party. +- Any valid properties of a `<Drawer>` component + +### Usage + +```tsx +<EntityDetailsDrawer {entity} onClose={() => console.info('Closed!')}/> +``` + +## Source + +[frontend/src/lib/dynamic-components/entityDetails/EntityDetailsDrawer.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/EntityDetailsDrawer.svelte) + +[frontend/src/lib/dynamic-components/entityDetails/EntityDetailsDrawer.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/EntityDetailsDrawer.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityInfo/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityInfo/+page.md new file mode 100644 index 000000000..992e18b76 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityInfo/+page.md @@ -0,0 +1,30 @@ +# EntityInfo + +Used to show an entity's basic info and their answers to `info` questions in an `EntityDetails` component. + +### Dynamic component + +This is a dynamic component, because it accesses `appSettings` and `dataRoot` from `AppContext`. + +### Properties + +- `entity`: A possibly ranked entity, e.g. candidate or a party. +- `questions`: An array of `info` questions. +- Any valid attributes of a `<div>` element + +### Settings + +- `results.sections: 'organization'`: Whether to show a link to the nominating organization. +- `survey.showIn: 'entityDetails'`: Whether to show the survey banner. + +### Usage + +```tsx +<EntityInfo entity={candidate} questions={$infoQuestions} /> +``` + +## Source + +[frontend/src/lib/dynamic-components/entityDetails/EntityInfo.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/EntityInfo.svelte) + +[frontend/src/lib/dynamic-components/entityDetails/EntityInfo.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/EntityInfo.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityOpinions/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityOpinions/+page.md new file mode 100644 index 000000000..2e913f9ac --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/EntityOpinions/+page.md @@ -0,0 +1,22 @@ +# EntityOpinions + +Used to show an entity's answers to `opinion` questions and possibly those of the voter, too, in an `EntityDetails` component. + +### Properties + +- `entity`: A possibly ranked entity, e.g. candidate or a party. +- `questions`: An array of `opinion` questions. +- `answers`: An optional `AnswerStore` with the Voter's answers to the questions. +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<EntityOpinions entity={candidate} questions={$opinionQuestions} /> +``` + +## Source + +[frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.svelte) + +[frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/InfoItem/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/InfoItem/+page.md new file mode 100644 index 000000000..ec24b45a2 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityDetails/InfoItem/+page.md @@ -0,0 +1,25 @@ +# InfoItem + +Used to show a label-content pair in a Candidate's basic information. + +### Properties + +- `label`: The label of the information. +- `vertical`: Layout mode for the item. Default: `false` +- Any valid attributes of a `<div>` element + +### Slots + +- default: the information contents. + +### Usage + +```tsx +<InfoItem label={$t('candidateApp.common.firstNameLabel')}>{candidate.firstName}</InfoItem> +``` + +## Source + +[frontend/src/lib/dynamic-components/entityDetails/InfoItem.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/InfoItem.svelte) + +[frontend/src/lib/dynamic-components/entityDetails/InfoItem.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityDetails/InfoItem.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityList/EntityList/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityList/EntityList/+page.md new file mode 100644 index 000000000..8c27c55ad --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityList/EntityList/+page.md @@ -0,0 +1,39 @@ +# EntityList + +Show a list of possibly wrapped entities with pagination and defined actions. + +### Dynamic component + +This is a dynamic component, because it renders the dynamic `EntityCard` component. + +### Properties + +- `cards`: The properties for the `EntityCard`s to show. +- `itemsPerPage`: The number of entities to display on each page of the list. @default `50` +- `itemsTolerance`: The fraction of `itemsPerPage` that can be exceeded on the last page to prevent showing a short last page. @default `0.2` +- `scrollIntoView`: Whether to scroll loaded items into view. This may results in glitches when the list is contained in a modal. @default `true` +- Any valid attributes of a `<div>` element. + +### Bindable properties + +- `itemsShown`: The number of items currently shown in the list. + +### Accessibility + +- Loading more items happens using a basic `<button>`, which becomes invisible to when clicked but remains in the DOM. + +### Usage + +```tsx +<h2>{itemsShown} candidates of {candidates.length}</h2> +<EntityList + bind:itemsShown + contents={candidates} + actionCallBack={({id}) => $getRoute({route: ROUTE.Candidate, id})}/> +``` + +## Source + +[frontend/src/lib/dynamic-components/entityList/EntityList.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityList/EntityList.svelte) + +[frontend/src/lib/dynamic-components/entityList/EntityList.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityList/EntityList.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityList/EntityListControls/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityList/EntityListControls/+page.md new file mode 100644 index 000000000..65bd75021 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/entityList/EntityListControls/+page.md @@ -0,0 +1,30 @@ +# EntityListControls + +Show filter, sorting (TBA) and search tools for an associated `<EntityList>`. + +TODO: Consider moving the tracking events away from the component and just adding callbacks that the consumer can use to trigger tracking events. + +### Properties + +- `entities`: A list of possibly ranked entities, e.g. candidates or a parties. +- `filterGroup`: The filters applied to the contents. +- `searchProperty`: The property used for the search tool. Default: `'name'` +- `onUpdate`: Callback for when the filters are applied. +- Any valid attributes of a `<div>` element. + +### Tracking events + +- `filters_reset` +- `filters_active` with `activeFilters` listing the (localized) names of the currently active filters + +### Usage + +```tsx +<EntityListControls entities={allParties} bind:output={filteredParties} /> +``` + +## Source + +[frontend/src/lib/dynamic-components/entityList/EntityListControls.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityList/EntityListControls.svelte) + +[frontend/src/lib/dynamic-components/entityList/EntityListControls.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/entityList/EntityListControls.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/feedback/Feedback/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/feedback/Feedback/+page.md new file mode 100644 index 000000000..f095ff081 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/feedback/Feedback/+page.md @@ -0,0 +1,50 @@ +# Feedback + +Show a form for sending feedback. + +### Dynamic component + +Accesses the `AppContext` and the `FeedbackWriter` api. + +### Properties + +- `showActions`: Whether to show the standard action buttons below the feedback form. Default: `true` +- `variant`: The layout variant of the feedback form. Default: `'default'` +- Any valid attributes of a `<form>` element. + +### Bindable properties + +- `canSubmit`: Bind to this to know whether the feedback can be submitted, i.e. the user has entered something. Default: `false` +- `status`: Bind to this to access the status of the feedback form. Default: `'default'` +- `submit`: Submit the feedback or close the modal if it's already been submitted. +- `reset`: Reset the form so that if the user opens it again, they can fill new feedback. You should call this when closing any modal containing the feedback. + +### Events + +- `cancel`: Fired when the user clicks the cancel button or the submit button again after submitting or an error, indicating that the form should close. +- `error`: Fired when there is an error sending the feedback. +- `sent`: Fired when the feedback is successfully sent. + +### Tracking events + +- `feedback_sent`: Feedback is succesfully sent. Contains `rating` and `description` properties. +- `feedback_error`: There was an error sending the feedback. Contains `rating` and `description` properties. + +### Usage + +```tsx +<script lang="ts"> + let reset: () => void; + function close() { + // Also hide the feedback somehow + reset(); + } +</script> +<Feedback bind:reset on:cancel={close} on:sent={close}/> +``` + +## Source + +[frontend/src/lib/dynamic-components/feedback/Feedback.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/feedback/Feedback.svelte) + +[frontend/src/lib/dynamic-components/feedback/Feedback.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/feedback/Feedback.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/feedback/modal/FeedbackModal/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/feedback/modal/FeedbackModal/+page.md new file mode 100644 index 000000000..07c649ca1 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/feedback/modal/FeedbackModal/+page.md @@ -0,0 +1,24 @@ +# FeedbackModal + +Show a modal dialog for sending feedback. + +### Properties + +- `title`: Optional title for the modal. Defaults to `{$t('feedback.title')}` +- Any valid properties of a `<Modal>` component. + +### Usage + +```tsx +<script lang="ts"> + let openFeedback: () => void; +</script> +<FeedbackModal bind:openFeedback> +<Button on:click={openFeedback} text="Open feedback"/> +``` + +## Source + +[frontend/src/lib/dynamic-components/feedback/modal/FeedbackModal.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/feedback/modal/FeedbackModal.svelte) + +[frontend/src/lib/dynamic-components/feedback/modal/FeedbackModal.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/feedback/modal/FeedbackModal.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/feedback/popup/FeedbackPopup/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/feedback/popup/FeedbackPopup/+page.md new file mode 100644 index 000000000..a90a1273a --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/feedback/popup/FeedbackPopup/+page.md @@ -0,0 +1,19 @@ +# FeedbackPopup + +Show a popup asking for user feedback. + +### Properties + +- Any valid properties of an `<Alert>` component. + +### Usage + +```tsx +<FeedbackPopup /> +``` + +## Source + +[frontend/src/lib/dynamic-components/feedback/popup/FeedbackPopup.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/feedback/popup/FeedbackPopup.svelte) + +[frontend/src/lib/dynamic-components/feedback/popup/FeedbackPopup.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/feedback/popup/FeedbackPopup.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/footer/Footer/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/footer/Footer/+page.md new file mode 100644 index 000000000..0f256a8ff --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/footer/Footer/+page.md @@ -0,0 +1,23 @@ +# Footer + +A template part that is used to show the application's common footer, shown on some pages. + +### Dynamic component + +- Accesses `appCustomization` from `AppContext`. + +### Properties + +- Any valid attributes of the `<footer>` element. + +### Usage + +```tsx +<Footer /> +``` + +## Source + +[frontend/src/lib/dynamic-components/footer/Footer.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/footer/Footer.svelte) + +[frontend/src/lib/dynamic-components/footer/Footer.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/footer/Footer.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/logoutButton/LogoutButton/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/logoutButton/LogoutButton/+page.md new file mode 100644 index 000000000..31815888e --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/logoutButton/LogoutButton/+page.md @@ -0,0 +1,24 @@ +# LogoutButton + +Allows user to log out. + +### Dynamic component + +Accesses `AuthContext` and `AppContext`. + +### Properties + +- `redirectTo`: The route to redirect to after logging out. Default: `'Home'` +- Any valid properties of a `<Button>` component. + +### Usage + +```tsx +<LogoutButton /> +``` + +## Source + +[frontend/src/lib/dynamic-components/logoutButton/LogoutButton.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/logoutButton/LogoutButton.svelte) + +[frontend/src/lib/dynamic-components/logoutButton/LogoutButton.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/logoutButton/LogoutButton.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/NavGroup/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/NavGroup/+page.md new file mode 100644 index 000000000..feb00c8e4 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/NavGroup/+page.md @@ -0,0 +1,30 @@ +# NavGroup + +Use to group `NavItem` components. Displays a faint line above the group. + +### Properties + +- `title`: Optional title for the navigation group. +- Any valid attributes of a `<ul>` element. + +### Slots + +- default: The contents of the navigation group. Should be mostly `<NavItem>` components. + +### Usage + +```tsx +<NavGroup> + <NavItem href={$getRoute(ROUTE.Info)} icon="info"> + Show info + </NavItem> + <NavItem on:click={(e) => foo(e)}>Do foo</NavItem> + <div>Some other content</div> +</NavGroup> +``` + +## Source + +[frontend/src/lib/dynamic-components/navigation/NavGroup.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/NavGroup.svelte) + +[frontend/src/lib/dynamic-components/navigation/NavGroup.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/NavGroup.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/NavItem/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/NavItem/+page.md new file mode 100644 index 000000000..e36bf7578 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/NavItem/+page.md @@ -0,0 +1,30 @@ +# NavItem + +Outputs a navigation item for use inside a `<NavGroup>` which in turn is used within a `<Navigation>` component. + +The item is rendered as an `<a>` element if `href` is supplied. Otherwise a `<button>` element will be used. Be sure to provde an `on:click` event handler or other way of making the item interactive. + +### Dynamic component + +Accesses `LayoutContext`. + +### Properties + +- `icon`: The optional name of the icon to use with the navigation item. See the `Icon` component for more details. +- `text`: The text to display in the navigation item. +- `disabled`: Whether the button is disabled. This can also be used with items rendered as `<a>` elements. +- `autoCloseNav`: Whether the menu available from the page context should be closed when the item is clicked. Default: `true` +- Any valid attributes of either an `<a>` or `<button>` element depending whether `href` was defined or not, respectively. + +### Usage + +```tsx +<NavItem href={$getRoute(ROUTE.Info)} icon="info" text="Show info"/> +<NavItem on:click={(e) => foo(e)} text="Do foo"/> +``` + +## Source + +[frontend/src/lib/dynamic-components/navigation/NavItem.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/NavItem.svelte) + +[frontend/src/lib/dynamic-components/navigation/NavItem.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/NavItem.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/Navigation/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/Navigation/+page.md new file mode 100644 index 000000000..e3b1de292 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/Navigation/+page.md @@ -0,0 +1,43 @@ +# Navigation + +Create navigation menus for the application in a predefined style. + +### Properties + +- `hidden`: Set to `true` to whenever the navigation is hidden. Default: `false` +- Any valid attributes of a `<nav>` element. + +### Slots + +- default: The content of the navigation menu. It should mainly consist of `<NavGroup>` components containing `<NavItem>` components. + +### Events + +- `keyboardFocusOut`: Emitted when the component loses a keyboard user's + focus. This can be used to automatically close a drawer menu this is + contained in. + +### Usage + +```tsx +<Navigation aria-label="Main navigation" on:keyboardFocusOut={closeDrawer}> + <NavGroup> + <NavItem href={$getRoute(ROUTE.Info)} icon="info"> + Show info + </NavItem> + <NavItem on:click={(e) => foo(e)}>Do foo</NavItem> + <div>Some other content</div> + </NavGroup> + <NavGroup> + <NavItem href={$getRoute(ROUTE.Help)} icon="help"> + Show help + </NavItem> + </NavGroup> +</Navigation> +``` + +## Source + +[frontend/src/lib/dynamic-components/navigation/Navigation.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/Navigation.svelte) + +[frontend/src/lib/dynamic-components/navigation/Navigation.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/Navigation.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/admin/AdminNav/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/admin/AdminNav/+page.md new file mode 100644 index 000000000..76a09474b --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/admin/AdminNav/+page.md @@ -0,0 +1,25 @@ +# AdminNav + +A template part that outputs the navigation menu for the Admin App for use in `Layout`. + +### Dynamic component + +- Accesses the `AdminContext` in the future. + +### Properties + +- Any valid properties of a `Navigation` component. + +### Usage + +```tsx +<AdminNav> + <NavItem slot="close" on:click={closeMenu} icon="close" text="Close" /> +</AdminNav> +``` + +## Source + +[frontend/src/lib/dynamic-components/navigation/admin/AdminNav.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/admin/AdminNav.svelte) + +[frontend/src/lib/dynamic-components/navigation/admin/AdminNav.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/admin/AdminNav.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/candidate/CandidateNav/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/candidate/CandidateNav/+page.md new file mode 100644 index 000000000..8ea5ee795 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/candidate/CandidateNav/+page.md @@ -0,0 +1,25 @@ +# CandidateNav + +A template part that outputs the navigation menu for the Candidate App for use in `Layout`. + +### Dynamic component + +- Accesses the `CandidateContext`. + +### Properties + +- Any valid properties of a `Navigation` component + +### Usage + +```tsx +<CandidateNav> + <NavItem slot="close" on:click={closeMenu} icon="close" text="Close" /> +</CandidateNav> +``` + +## Source + +[frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.svelte) + +[frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/languages/LanguageSelection/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/languages/LanguageSelection/+page.md new file mode 100644 index 000000000..bad2bd5df --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/languages/LanguageSelection/+page.md @@ -0,0 +1,17 @@ +# LanguageSelection + +A template part that language selection options for the navigation menu if these are available. + +### Dynamic component + +- Accesses `getRoute` from `AppContext`. + +### Usage + +```tsx +<LanguageSelection /> +``` + +## Source + +[frontend/src/lib/dynamic-components/navigation/languages/LanguageSelection.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/languages/LanguageSelection.svelte) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/voter/VoterNav/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/voter/VoterNav/+page.md new file mode 100644 index 000000000..cc83b5eb5 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/navigation/voter/VoterNav/+page.md @@ -0,0 +1,31 @@ +# VoterNav + +A template part that outputs the navigation menu for the Voter App for use in `Layout`. + +### Dynamic component + +- Accesses the `VoterContext`. + +### Properties + +- Any valid properties of a `Navigation` component. + +### Settings + +- `elections.disallowSelection`: Affects whether the select elections item is shown. +- `elections.startFromConstituencyGroup`: Affects the order of the items shown and under which conditions they are disabled. +- `entities.showAllNominations`: Affects whether the 'All nominations' route is shown. + +### Usage + +```tsx +<VoterNav> + <NavItem href={$getRoute('Home')} icon="home" text={$t('common.home')} /> +</VoterNav> +``` + +## Source + +[frontend/src/lib/dynamic-components/navigation/voter/VoterNav.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/voter/VoterNav.svelte) + +[frontend/src/lib/dynamic-components/navigation/voter/VoterNav.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/navigation/voter/VoterNav.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/questionHeading/QuestionHeading/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/questionHeading/QuestionHeading/+page.md new file mode 100644 index 000000000..38b5b4ad9 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/questionHeading/QuestionHeading/+page.md @@ -0,0 +1,33 @@ +# QuestionHeading + +Show a `Question`’s text and metadata, such as category and applicable elections. + +### Dynamic component + +This is a dynamic component, because it accesses the settings via `AppContext` and selected elections from `VoterContext` or `CandidateContext`. + +### Properties + +- `question`: The `Question` whose text and metadata to show. +- `questionBlocks`: The `QuestionBlocks` containing the question. +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. @default false +- Any valid properties of a `HeadingGroup` component. + +### Settings + +- `questions.showCategoryTags`: Whether to show the category tags. + +### Usage + +```tsx +<QuestionHeading + id="{question.id}-heading" + {question} + questionBlocks={$selectedQuestionBlocks}/> +``` + +## Source + +[frontend/src/lib/dynamic-components/questionHeading/QuestionHeading.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/questionHeading/QuestionHeading.svelte) + +[frontend/src/lib/dynamic-components/questionHeading/QuestionHeading.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/questionHeading/QuestionHeading.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/survey/SurveyButton/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/survey/SurveyButton/+page.md new file mode 100644 index 000000000..7502fb6f8 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/survey/SurveyButton/+page.md @@ -0,0 +1,36 @@ +# SurveyButton + +Display a button for filling out a user survey. + +### Dynamic component + +Accesses `AppContext` to set and read the current survey status and link. + +### Properties + +- Any valid common properties of a `<Button>` component. + +### Bindable properties + +- `clicked`: Whether the button has been clicked. + +### Events + +- `click`: Dispatched when the button is clicked. The event has no details. + +### Tracking events + +- `survey_opened`: Dispatched when the survey link is opened. + +### Usage + +```tsx +<SurveyButton bind:clicked on:click={() => console.info('Clicked!')}/> + <SurveyButton variant="main"/> +``` + +## Source + +[frontend/src/lib/dynamic-components/survey/SurveyButton.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/survey/SurveyButton.svelte) + +[frontend/src/lib/dynamic-components/survey/SurveyButton.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/survey/SurveyButton.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/survey/banner/SurveyBanner/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/survey/banner/SurveyBanner/+page.md new file mode 100644 index 000000000..c0b05b139 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/survey/banner/SurveyBanner/+page.md @@ -0,0 +1,25 @@ +# SurveyBanner + +Display a prompt for filling out a user survey if the setting is enabled and the user has not answered the survey yet. Otherwise, nothing will be rendered. + +### Dynamic component + +Accesses `AppContext` to get `appSettings` and `userPreferences`. + +### Properties + +- `variant`: The layout variant of the banner. Can be either `default` or `compact`. @default `default` +- Any valid common attributes of a `<div>` element. + +### Usage + +```tsx +<SurveyBanner/> +<SurveyBanner variant="compact"/> +``` + +## Source + +[frontend/src/lib/dynamic-components/survey/banner/SurveyBanner.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/survey/banner/SurveyBanner.svelte) + +[frontend/src/lib/dynamic-components/survey/banner/SurveyBanner.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/survey/banner/SurveyBanner.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/survey/popup/SurveyPopup/+page.md b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/survey/popup/SurveyPopup/+page.md new file mode 100644 index 000000000..c1af7e5d0 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/components/generated/dynamic-components/survey/popup/SurveyPopup/+page.md @@ -0,0 +1,19 @@ +# SurveyPopup + +Show a popup asking for user feedback. + +### Properties + +- Any valid properties of an `<Alert>` component. + +### Usage + +```tsx +<FeedbackPopup /> +``` + +## Source + +[frontend/src/lib/dynamic-components/survey/popup/SurveyPopup.svelte](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/survey/popup/SurveyPopup.svelte) + +[frontend/src/lib/dynamic-components/survey/popup/SurveyPopup.type.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/dynamic-components/survey/popup/SurveyPopup.type.ts) diff --git a/docs/src/routes/(content)/developers-guide/frontend/contexts/+page.md b/docs/src/routes/(content)/developers-guide/frontend/contexts/+page.md new file mode 100644 index 000000000..fc0cfb6ab --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/contexts/+page.md @@ -0,0 +1,48 @@ +# Contexts + +All of the data, shared stores and other state variables used by the app and components are collected in [Svelte contexts](https://svelte.dev/docs/svelte#setcontext). They are defined in the [$lib/api/contexts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts) folder. + +Contexts must be initialized before they can be used by calling the `initFooContext()` function. Initialisation is performed by the ancestor `+layout.svelte` files of the routes on which the contexts are available. Afterwards the contexts are accessed by `getFooContext()`. + +For ease of use, most contexts contain the properties provided by lower-level contexts. The [`VoterContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/voter), for example, contains the [`AppContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/app/appContext.type.ts), which in turn contains the [`I18nContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/i18n/i18nContext.type.ts) and [`DataContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/data/dataContext.type.ts). + +> See also an [example of the data loading cascade](#example-loading-cascade-for-the-voterlocatedquestions-route-example). + +### Contexts vs global stores + +Contexts are used instead of global stores, because they are explicitly initialised and restricted to components and their children. Tracking the use and dependency-loading of directly imported global stores is hard, because they’re initialized immediately when imported. + +### Example of Context Use + +On the `/(voters)/elections/` route where the Voter can select which elections to get results for we have: + +```tsx +import { getVoterContext } from '$lib/contexts/voter'; +const { dataRoot, getRoute, selectedElections, t } = getVoterContext(); +``` + +The properties accessed are: + +- `dataRoot`: A store providing access to all of the VAA data which has been provided to it by `load` functions. Inherited from `DataContext`. +- `getRoute`: A store containing a function with which links to internal routes are build. Inherited from `AppContext`. +- `selectedElections`: A store containing the currently selected `Election` objects, derived from the `electionId` search parameters and `dataRoot`. +- `t`: A store containing the translation function. Inherited from `I18nContext`. + +#### Available contexts + +> For complete descriptions of the contexts’ contents, see their associated type files. + +| Context | Description |  Consumer | Includes | Own contents (non-exhaustive) | Initialized by | +| ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| [`I18nContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/i18n/i18nContext.type.ts) | All localization functions | Other contexts | — | — `t`<br> — `locale`<br> — `locales` from `$lib/i18n` | `/[lang]` | +| [`ComponentContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/component/componentContext.type.ts) | Functions available to all components | Any component | `I18n` | — `darkMode: Readable<boolean>` | `/[lang]` | +| [`DataContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/data/dataContext.type.ts) | All VAA data (using the `@openvaa/data` model) | Other contexts | `I18n`\* | — `dataRoot: Readable<dataRoot>` | `/[lang]` | +| [`AppContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/app/appContext.type.ts) | All functions shared by the Voter and Candidate Apps | Any page, layout or dynamic component | `I18n`, `VaaData`, `Component` | — `appType: Writable<AppType>`<br> — `appSettings: SettingsStore`<br> — `userPreferences: Writable<UserPreferences>`<br> — `getRoute: Readable<(options: RouteOptions) => string>`<br> — `sendFeedback: (data: FeedbackData) => Promise<Response>`<br> — contents of `TrackinService`<br> — popup and modal dialog handling<br> — handling data consent and user surveys | `/[lang]` | +| ` | +| [`AuthContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/auth/authContext.type.ts) | Functions for logging in and out as well password editing | Any page or layout | — | – `authToken`<br> — `logout()`<br> — `requestForgotPasswordEmail(opts)`<br> — `resetPassword(opts)`<br> — `setPassword(opts)` | `/[lang]` | +| [`LayoutContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/layout/layoutContext.type.ts) | Functions for subpages to affect the outer application layout | Any page or layout | — | — `topBarSettings: StackedStore<TopBarSettings, DeepPartial<TopBarSettings>>`<br> — `pageStyles: StackedStore<PageStyles, DeepPartial<PageStyles>>`<br> — `progress: Progress`<br> — `navigation: Navigation` | `/[lang]` | +| [`VoterContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/voter/voterContext.type.ts) | All functions exclusive to the Voter App | Any part of the Voter App or dynamic components (conditionally) | `App` | — `answers: AnswerStore`<br> — `matches: Readable<MatchTree>`<br> — `entityFilters: Readable<FilterTree>`<br> — `infoQuestions: Readable<Array<AnyQuestionVariant>>`<br> — `opinionQuestions: Readable<Array<AnyQuestionVariant>>`<br> — `selectedConstituencies: Readable<Array<Constituency>>`<br> — `selectedElections: Readable<Array<Election>>`<br> — handling question category selection and question ordering<br> — other selection-related functions | `/[lang]/(voter)` | +| [`CandidateContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/candidate/candidateContext.type.ts) | All functions exclusive to the Candidate App | Any part of the Candidate App or dynamic components (conditionally) | `App` | — `userData: UserDataStore`<br> — `preregistrationElectionIds: Readable<Array<Id>>` and other preregisration stores<br> — `selectedElections: Readable<Array<Election>>` and other stores matching those in `VoterContext`<br> — `checkRegistrationKey(opts)`<br> — `register(opts)`<br> — `logout()`<br> — `requestForgotPasswordEmail(opts)`<br> — `resetPassword(opts)`<br> — `setPassword(opts)`<br> — `exchangeCodeForIdToken(opts)`<br> — `preregister(opts)`<br> — `clearIdToken()`<br> — `answersLocked: Readable<boolean>`<br> — `requiredInfoQuestions: Readable<Array<AnyQuestionVariant>>` and other utility derived stores | `/[lang]/candidate` | +| [`AdminContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/admin/adminContext.type.ts) | All functions exclusive to the Admin App | Any part of the Admin App or dynamic components (conditionally) | `App`, `Auth` | TBA | `/[lang]/admin` | + +\* The `DataContext` accesses the `I18nContext` because it needs `locale` but it doesn’t export its contents. diff --git a/docs/src/routes/(content)/developers-guide/frontend/data-api/+page.md b/docs/src/routes/(content)/developers-guide/frontend/data-api/+page.md new file mode 100644 index 000000000..bc7a04e72 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/data-api/+page.md @@ -0,0 +1,241 @@ +# Data API + +The Data API is the interface between the frontend and the backend. It handles reading and writing of data in such a way that the frontend can remain agnostic to the actual implementation. + +The Data API is composed of three services: + +1. `DataProvider`: Reads of public data used by the Voter App from the backend. +2. `FeedbackWriter`: Writes feedback items from either the Voter or Candidate App to the backend. +3. `DataWriter`: Writes data from the Candidate App to the backend and reads some data requiring authentication. + +> See also an [example of the data loading cascade](/developers-guide/frontend/accessing-data-and-state-management). + +### Cache + +A simple [disk cache](https://github.com/jaredwray/cacheable#readme) may also be opted in by setting the `PUBLIC_CACHE_ENABLED` env variable to `true` (and configuring the other `CACHE_` variables). If enabled, non-authenticated `GET` requests are cached by default. Caching is handled by the [UniversalAdapter.fetch](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/base/universalAdapter.ts) method, using the [/api/cache/+server] route. + +Note that the cache can be enabled also when the `local` data adapter is used. This may, however, not improve performance by much, as the only overhead saved is the application of query filters to the locally stored json data. + +### Folder structure + +- [frontend/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend) + - [src/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src) + - [lib/api/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api) — All universally available Data API implementations. + - [adapters/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/adapters) — Specific Data API implemenations. + - [apiRoute/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/adapters/apiRoute) — Generic `ApiRouteDataProvider` and `ApiRouteFeedbackWriter` implementations through which all server-run implementations are accessed. Redirects calls to local API routes (see below). + - [strapi/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/adapters/strapi) – Specific `StrapiDataProvider`, `StrapiFeedbackWriter` and `StrapiDataWriter` implementations for use with the Strapi backend. + - [base/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/base) — Common types and interfaces as well as the `UniversalDataProvider`, `UniversalFeedbackWriter` and `UniversalDataWriter` classes which the specific implementations extend. All common data processing, such as color contrast checking, is handled by these classes. + - [utils/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/utils) — Utilities related to the Data API. + - [dataProvider.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/dataProvider.ts) — The main entry point for the `load` functions via `import { dataProvider } from '$lib/api/dataProvider'`. The implementation specified in the settings is returned (as a Promise). + - [dataWriter.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/dataWriter.ts) — The main entry point for the `CandidateContext` methods. + - [feedbackWriter.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/feedbackWriter.ts) — The main entry point for the `sendFeedback` method of `AppContext`. + - [server/api/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/server/api) – All Data API implementations that must run on the server. + - [adapters/local/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/server/api/adapters/local) — Specific `LocalServerDataProvider` and `LocalServerFeedbackWriter` implementations that read and write local `json` files in the [data](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/data) folder. + - [dataProvider.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/server/api/dataProvider.ts) — The main entry point for the `GET` function of the `/api/data/[collection]/+server.ts` API route. + - [feedbackWriter.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/server/api/feedbackWriter.ts) — The main entry point for the `POST` function of the `/api/feedback/+server.ts` API route. + - `routes/[lang=locale]/` + - `api/` – Contains the API routes. + - `cache/+server.ts` – Implements simple disk caching using [`flat-cache`](https://github.com/jaredwray/cacheable#readme) + - `candidate/logout/+server.ts` – Clear the strict cookie containing the authentication token (does not actually access`DataWriter`). + - `candidate/preregister/+server.ts` – Access to `DataWriter.preregisterWithApiToken`. + - `data/[collection]/+server.ts` – Access to the server-run `ServerDataProvider` implementations. + - `feedback/+server.ts` – Access to the server-run `ServerFeedbackWriter` implementations. + - `candidate/login/+page.server.ts` - Access to `DataWriter.login` and set the authentication cookie. + - [data/](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/data) — For data used by the `LocalServerDataProvider` and `LocalServerFeedbackWriter`. + +### Classes and interfaces + +```mermaid +--- +title: Data API classes and interfaces +--- +classDiagram +direction TD + +%% CLASS DEFINITIONS + +namespace Universal { + + class DPReturnType:::interface { + <<Type>> + Contains the return types for all DataProvider + getter methods, e.g.: + Partial~DynamicSettings~ +appSettings + Array~ElectionData~ +elections + } + + class DataProvider_AdapterType_:::interface { + <<Interface>> + Ensures that all the getters are implemented + both universally and on the server + with the return type defined by ~AdapterType~, + which is either 'universal' or 'server' + +getFooData(options) Promise~DPReturnType['foo'] | Response~ + } + + class DataWriter_AdapterType_:::interface { + <<Interface>> + Ensures that all the writers are implemented universally + Contains methods for + loggin in and out + setting and resetting passwords + registration + pre-registration + getting user data + setting user data + } + + class FeedbackWriter:::interface { + <<Interface>> + Ensures a matching interface for posting feedback + both universally and on the server + +postFeedback(data) Promise~Response~ + } + + class UniversalAdapter:::abstract { + <<Abstract>> + Implements common fetch for Data API services that handles possible disk caching, + must be initialized by providing the fetch function before use. + +init(fetch) void + +fetch(opts) Promise~Response~ + } + + class UniversalDataProvider:::abstract { + <<Abstract>> + Implements all the data getter methods + Processes all data returned, e.g., ensuring colors + Requires subclasses to implement protected methods + +getFooData(options) Promise~DPReturnType['foo']~ + #_getFooData(options)* Promise~DPReturnType['foo']~ + } + + class UniversalDataWriter:::abstract { + <<Abstract>> + Implements all the DataWriter methods, + Requires subclasses to implement protected methods, + except for methods using universal API routes + +getFooData(options) Promise~DPReturnType['foo']~ + #_getFooData(options)* Promise~DPReturnType['foo']~ + } + + class UniversalFeedbackWriter:::abstract { + <<Abstract>> + Implements postFeedback method + Processes the data by adding possibly missing fields + Requires subclasses to implement the protected method + +postFeedback(data) Promise~Response~ + #_postFeedback(data)* Promise~Response~ + } + + class StrapiAdapter:::abstract { + <<Abstract/Mixin>> + Implements methods for accessing the Strapi API + +apiFetch(opts) Promise~Response~ + +apiGet(opts) Promise~StrapiReturnType['foo']~ + +apiPost(opts) Promise~Response~ + +apiPut(opts) Promise~Response~ + } + + class StrapiDataProvider { + Implements the actual _getter methods, + which use StrapiAdapter.apiGet and convert + the Strapi data into DPReturnType['foo'] + #_getFooData(options) Promise~Array~FooData~~ + } + + class StrapiDataWriter { + Implements the actual _foo methods, + mandated by UniversalDataWriter + } + + class StrapiFeedbackWriter { + Implements the _postFeedback method + #_postFeedback(data) Promise~Response~ + } + + class ApiRouteAdapter:::abstract { + <<Abstract/Mixin>> + Implements methods for accessing the API routes + +apiFetch(opts) Promise~Response~ + +apiGet(opts) Promise~DPReturnType['foo']~ + +apiPost(opts) Promise~Response~ + } + + class ApiRouteDataProvider { + Implements the actual _getter methods by fetching + from the API route /api/data/[collection] + #_getFooData() Promise~DPReturnType['foo']~ + } + + class ApiRouteFeedbackWriter { + Implements the _postFeedback method by fetching + from the API route /api/feedback + #_postFeedback() Promise~Response~ + } +} + +namespace Server { + + class LocalServerAdapter:::abstract { + <<Abstract>> + Implements methods for accessing local files + in the /data folder + +exist(endpoint) Promise~boolean~ + +read(endpoint) Response + +create(endpoint, data) Promise~Response~ + } + + class LocalServerDataProvider { + Implements the actual getter methods + by reading from disk + getFooData(options) Promise~Response~ + } + + class LocalServerFeedbackWriter { + Implements the postFeedback method + by writing to disk + postFeedback(data) Promise~Response~ + } + +} + +%% CONNECTIONS + +DPReturnType ..> DataProvider_AdapterType_ : defined return types +DataProvider_AdapterType_ <.. UniversalDataProvider : implements (universal) +DataWriter_AdapterType_ <.. UniversalDataWriter : implements (universal) +FeedbackWriter <.. UniversalFeedbackWriter : implements (universal) +UniversalAdapter <|-- UniversalDataProvider +UniversalAdapter <|-- UniversalDataWriter +UniversalAdapter <|-- UniversalFeedbackWriter + +StrapiAdapter <|-- StrapiDataProvider : mixin +StrapiAdapter <|-- StrapiDataWriter : mixin +StrapiAdapter <|-- StrapiFeedbackWriter : mixin +UniversalDataProvider <|-- StrapiDataProvider +UniversalDataWriter <|-- StrapiDataWriter +UniversalFeedbackWriter <|-- StrapiFeedbackWriter + +ApiRouteAdapter <|-- ApiRouteDataProvider : mixin +ApiRouteAdapter <|-- ApiRouteFeedbackWriter : mixin +UniversalDataProvider <|-- ApiRouteDataProvider +UniversalFeedbackWriter <|-- ApiRouteFeedbackWriter +ApiRouteDataProvider ..> LocalServerDataProvider : accesses via GET /api/data/[collection]?... +ApiRouteFeedbackWriter ..> LocalServerFeedbackWriter : accesses via POST /api/feedback + +DataProvider_AdapterType_ <.. LocalServerDataProvider : implements (server) +FeedbackWriter <.. LocalServerFeedbackWriter : implements (server) +LocalServerAdapter <|-- LocalServerDataProvider +LocalServerAdapter <|-- LocalServerFeedbackWriter + +%% STYLING + +namespace Legend { + class Interface:::interface + class Abstract:::abstract + class Concrete +} + +classDef interface fill:#afa +classDef abstract fill:#aaf +``` diff --git a/docs/src/routes/(content)/developers-guide/frontend/environmental-variables/+page.md b/docs/src/routes/(content)/developers-guide/frontend/environmental-variables/+page.md new file mode 100644 index 000000000..f2196f2ed --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/environmental-variables/+page.md @@ -0,0 +1,13 @@ +# Environmental variables + +Environmental variables are never accessed directly in the frontend (i.e. from `$env/static/public` etc.), but instead via two utility modules: + +- [$lib/utils/constants](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/utils/constants.ts) which contains the publicly accessible variables, always prefixed with `PUBLIC_`. +- [$lib/server/constants](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/server/constants.ts) which contains the private variables only accessible on the server. + +Furthermore, the variables are not imported directly from these modules due to production compilation intricacies. They are imported wholesale instead: + +```ts +import { constants } from '$lib/utils/constants'; +const urlRoot = constants.PUBLIC_BROWSER_BACKEND_URL; +``` diff --git a/docs/src/routes/(content)/developers-guide/frontend/intro/+page.md b/docs/src/routes/(content)/developers-guide/frontend/intro/+page.md new file mode 100644 index 000000000..c68745e0b --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/intro/+page.md @@ -0,0 +1,7 @@ +# Frontend + +> The frontend currently uses Svelte 4. An update to Svelte 5 is scheduled for H1/2026. + +See also: + +- In-code documentation for all components, pages and layouts diff --git a/docs/src/routes/(content)/developers-guide/frontend/routing/+page.md b/docs/src/routes/(content)/developers-guide/frontend/routing/+page.md new file mode 100644 index 000000000..02bf04ab0 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/routing/+page.md @@ -0,0 +1,24 @@ +# Routing + +Routing is based on either route or search parameters: + +- Route parameters are used when the parameters are required, such as the `entityType` and `entityId` parameters on the route displaying individual `Entity`s. +- Search parameters are used when the parameters are always or sometimes optional, such as the `electionId` parameter which is optional if there data only has one `Election` or the `elections.disallowSelection` setting is `true`. + +For a list of the parameters in use, see [params.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/utils/route/params.ts). Some of the parameters (`ArrayParam`s) support multiple values but always accept single values as well. + +For implying optional parameters, the utilities in [impliedParams.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/utils/route/impliedParams.ts) are used. + +### Building routes + +Routes are constructed using the [`buildRoute`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/utils/route/buildRoute.ts) function, which takes as arguments the name of the [`Route`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/utils/route/route.ts), parameter values and values of the current route. + +In most cases, the dynamic function contained in the [`getRoute`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/app/getRoute.ts) store of [`AppContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/app/appContext.type.ts) is used. It automatically supplies the current route to the route builder, so that already effective parameters need not be explicitly supplied. + +Calling `$getRoute('Results')`, for example, will build a route to the Results page taking into account the currently selected `electionId`s, `constituencyId`s and `lang`. They can also be set explicitly, such as on the election selection page, with `$getRoute({ route: 'Results', electionId: ['e1', 'e2'] })`. + +When passing parameters to [`buildRoute`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/utils/route/buildRoute.ts) or [`$getRoute`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/app/getRoute.ts), search and route parameters need not be treated differently. The function will take care of rendering them correctly. + +### App Routes + +See the [auto-generated route map](/developers-guide/frontend/routing/generated). diff --git a/docs/src/routes/(content)/developers-guide/frontend/routing/generated/+page.md b/docs/src/routes/(content)/developers-guide/frontend/routing/generated/+page.md new file mode 100644 index 000000000..89f3d2b22 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/routing/generated/+page.md @@ -0,0 +1,76 @@ +# Route Map + +This is an automatically generated map of the SvelteKit application routes. + +``` +└── routes/ + └── [[lang=locale]]/ + ├── (voters)/ + │ ├── (located)/ + │ │ ├── questions/ + │ │ │ ├── category/ + │ │ │ │ └── [categoryId]/ + │ │ │ └── [questionId]/ + │ │ └── results/ + │ │ ├── statistics/ + │ │ └── [entityType]/ + │ │ └── [entityId]/ + │ ├── about/ + │ ├── constituencies/ + │ ├── elections/ + │ ├── info/ + │ ├── intro/ + │ ├── nominations/ + │ └── privacy/ + ├── admin/ + │ ├── (protected)/ + │ │ ├── argument-condensation/ + │ │ ├── jobs/ + │ │ └── question-info/ + │ └── login/ + ├── api/ + │ ├── admin/ + │ │ └── jobs/ + │ │ ├── abort-all/ + │ │ ├── active/ + │ │ ├── past/ + │ │ ├── single/ + │ │ │ └── [jobId]/ + │ │ │ ├── abort/ + │ │ │ └── progress/ + │ │ └── start/ + │ ├── auth/ + │ │ ├── login/ + │ │ └── logout/ + │ ├── cache/ + │ ├── candidate/ + │ │ └── preregister/ + │ ├── data/ + │ │ └── [collection]/ + │ ├── feedback/ + │ └── oidc/ + │ └── token/ + └── candidate/ + ├── (protected)/ + │ ├── preview/ + │ ├── profile/ + │ ├── questions/ + │ │ └── [questionId]/ + │ └── settings/ + ├── forgot-password/ + ├── help/ + ├── login/ + ├── password-reset/ + ├── preregister/ + │ ├── (authenticated)/ + │ │ ├── constituencies/ + │ │ ├── elections/ + │ │ └── email/ + │ ├── signicat/ + │ │ └── oidc/ + │ │ └── callback/ + │ └── status/ + ├── privacy/ + └── register/ + └── password/ +``` diff --git a/docs/src/routes/(content)/developers-guide/frontend/styling/+page.md b/docs/src/routes/(content)/developers-guide/frontend/styling/+page.md new file mode 100644 index 000000000..e5dda45c9 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/frontend/styling/+page.md @@ -0,0 +1,62 @@ +# Styling + +The frontend uses [Tailwind](https://tailwindcss.com/docs) and the [DaisyUI plugin](https://daisyui.com/components/) for styling. See below for more information on how these are used in the project. + +### Tailwind classes + +We use only a limited set of some of the [Tailwind utility classes](https://tailwindcss.com/docs/) for, e.g., spacing and font sizes, as well as the colours DaisyUI uses. For a list of the classes in use, see `tailwind.config.cjs` or use autocomplete. + +The reason for limiting Tailwind classes is that this way adherence to the design system is easier, because Tailwind gives nice autocomplete for the available options. For most classes names, such as `md` and `lg` are used. For spacing numeric values are also available, such as `w-40`, with the named ones, such as `gap-md`, reserved the most commonly used values. + +Note that you can still use [arbitrary values](https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values) with any Tailwind utility class with the bracket notation, e.g. `w-[21.35px]`. Bear in mind, however, that you should not construct these (or any other Tailwind classes) in code, unless the whole final string for the class is spelled out in code, such as `class = 'w-' + size === 'lg' ? 'lg' : 'md'`, because Tailwind only compiles classes it can spot in the source code using a naïve search. + +#### Passing classes to components + +The components in the app allow passing any attributes of the underlying element as component properties. This is most commonly used to add extra classes to those defined by the component (see [Component properties](/developers-guide/contributing/code-style-guide/#component-properties) in the Contributors’ guide). However, note that due to styling compartmentalization, **you should only pass Tailwind or global classes, not any classes defined locally**. + +### Colors + +For DaisyUI, all the [basic colours](https://daisyui.com/docs/colors/) are defined for both the default and the dark theme, which is used in dark mode. The colours are applicable to Tailwind utility classes (e.g. `text-primary`, `bg-base-300`) and DaisyUI component classes (e.g. `btn-primary`). You can see all the colours in `tailwind.config.cjs` but the most common ones are listed below. + +| | Name | Use | +| --------------------------------------------------------------------------------------------------------------------- | --------------- | --------------------------------------------------------------------- | +| <div style="background: #333333; width: 1.5rem; height: 1.5rem;"/> | `neutral` | Default colour for text | +| <div style="background: #2546a8; width: 1.5rem; height: 1.5rem;"/> | `primary` | For actions and links | +|  <div style="background: #666666; width: 1.5rem; height: 1.5rem;"/> | `secondary` | For secondary text and disabled buttons | +|  <div style="background: #a82525; width: 1.5rem; height: 1.5rem;"/> | `warning` | For warnings and actions demanding caution | +|  <div style="background: #a82525; width: 1.5rem; height: 1.5rem;"/> | `error` | For errors | +|  <div style="background: #ffffff; outline: 1px solid #666666; outline-offset: -1px; width: 1.5rem; height: 1.5rem;"/> | `base-100` | Default background | +|  <div style="background: #e8f5f6; width: 1.5rem; height: 1.5rem;"/> | `base-200` | Slightly less prominent shaded background | +|  <div style="background: #d1ebee; width: 1.5rem; height: 1.5rem;"/> | `base-300` | Default prominent background | +|  <div style="background: #ffffff; outline: 1px solid #2546a8; outline-offset: -1px; width: 1.5rem; height: 1.5rem;"/> | primary-content | Text on `bg-primary`. Each colour has its associated `content` colour | + +#### Color contrast + +In order to fulfil the application's accessibility requirements, a [WGAC AA level color contrast](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) has to be reached. The colors have been defined to ensure this as long as: + +- If the background is any of the base colors, e.g. `bg-base-100`, `bg-base-200` or `bg-base-300`, you can use any of the basic text colors on it. +- If background is any other color, such as `bg-primary`, always use the matching `content` color for text on it, e.g. `text-primary-content`. + +#### Testing the colors + +To test all of the application's colors, copy-paste the contents of [`color-test.txt`](/developers-guide/frontend/styling/color-test.txt) in a `+page.svelte` file somewhere, navigate to it and use the Wave browser extension to check the colors. Remember to do this for both the light and dark modes. + +The file is not included anywhere as a ready Svelte file, because otherwise all of the color classes in it would unnecessarily be compiled into the application by Tailwind. + +### Z-index + +For basic content, avoid using `z-index` and prefer layerying using the element order. + +For elements that absolutely need their `z-index` set, the following Tailwind classes are used: + +- `z-10`: Navigation drawer menu, page header +- `z-20`: Buttons overlaid on the header by the [`<Video>`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/video/Video.svelte) component +- `z-30`: The [`<Alert>`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/alert/Alert.svelte) component +- `z-40`: Not used currently +- `z-50`: Not used currently + +Note that the dialog created by the [`<Modal>`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/components/modal/Modal.svelte) component, is placed in a ’Top Layer’ placed in front of any content, regardless of their `z-index`. + +### Default styling + +See `app.css` for some styling defaults that are set throughout the app. diff --git a/docs/color-test.txt b/docs/src/routes/(content)/developers-guide/frontend/styling/color-test.txt similarity index 100% rename from docs/color-test.txt rename to docs/src/routes/(content)/developers-guide/frontend/styling/color-test.txt diff --git a/docs/llm-features.md b/docs/src/routes/(content)/developers-guide/llm-features/+page.md similarity index 100% rename from docs/llm-features.md rename to docs/src/routes/(content)/developers-guide/llm-features/+page.md diff --git a/docs/src/routes/(content)/developers-guide/localization/intro/+page.md b/docs/src/routes/(content)/developers-guide/localization/intro/+page.md new file mode 100644 index 000000000..ab0e83085 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/localization/intro/+page.md @@ -0,0 +1,14 @@ +# Localization + +Localization uses: + +- [`sveltekit-i18n`](https://github.com/sveltekit-i18n/lib) +- [`@sveltekit-i18n/parser-icu`](https://github.com/sveltekit-i18n/parsers/tree/master/parser-icu) which enables the [ICU message format](https://formatjs.io/docs/intl-messageformat/) + +> When updating to Svelte 5, its built-in localization features can probably be used instead. + +In short: + +- The locale is set exclusively by an optional `lang` route parameter at the start of the route. +- In the frontend, all translations are accessed with the same `$t('foo.bar')` function regardless of source. +- Localization uses soft locale matching whenever possible, i.e. `en-UK` matches both `en` and `en-US` if an exact match is not available. diff --git a/docs/src/routes/(content)/developers-guide/localization/local-translations/+page.md b/docs/src/routes/(content)/developers-guide/localization/local-translations/+page.md new file mode 100644 index 000000000..e8975ad06 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/localization/local-translations/+page.md @@ -0,0 +1,17 @@ +# Local translations + +Local translations are stored in `$lib/i18n/translations` and organized by language and key. The logic by which they're separated into different files is not absolutely rigid, but the following principles should be followed: + +1. Create a new file for each Voter App page or [dynamic component](/developers-guide/frontend/components), named `<pageOrComponentName>.json`. +2. Create a new file for each Candidate App page, named `candidateApp.<pageName>.json`. +3. For translations needed by [static components](/developers-guide/frontend/components), create a new subkey in the `components.json` file. + +Whenever adding translations, be sure to create them for all supported languages. + +### The [`TranslationKey`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/types/generated/translationKey.ts) type + +The available translation keys are defined by the [`TranslationKey`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/types/generated/translationKey.ts) type. These can be automatically generated by running `yarn workspace @openvaa/frontend generate:translation-key-type`. + +If you need to use a dynamically constructed translation key that is not recognized by the linter, use the [assertTranslationKey](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/i18n/utils/assertTranslationKey.ts) utility. + +When committing any changes the `pre-commit` hook will check that the type matches the translations. diff --git a/docs/src/routes/(content)/developers-guide/localization/locale-routes/+page.md b/docs/src/routes/(content)/developers-guide/localization/locale-routes/+page.md new file mode 100644 index 000000000..6de86db30 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/localization/locale-routes/+page.md @@ -0,0 +1,15 @@ +# Locale routes + +All routes start with an optional `locale` route parameter. The parameter matches any supported locale defined in the [`StaticSettings`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.ts) and their soft matches (using [`$lib/i18n/utils/matchLocale`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/i18n/utils/matchLocale.ts)). Thus, if `en`, `fi` and `es-CO` are supported and `en` is marked as `isDefault`, routes behave as follows: + +- `/foo` redirects to the default English version unless the user has a supported locale listed in `Request.accept-language`, in which case the user is redirected to `/fi/foo` (if `fi` is preferred) +- `/en/foo` shows the English version +- `/es-CO/foo` shows the Spanish version +- `/fi/foo` shows the Finnish version +- `/en-UK/foo` redirects to `/en/foo`, `/es/foo` to `/es-CO/foo` and so on for all soft locale matches + +Switching between locales happens by only changing the language parameter in the route. + +### The `getRoute` helper + +To facilitate locale switching, a `getRoute` helper function is provided as a store by the [`I18nContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/i18n/i18nContext.type.ts). It is passed a `Route` name, possible parameters and optionally a `locale` from which it constructs the proper url to go to. It can also be used to just switch the locale of the current page, by calling `$getRoute({locale: 'foo'})`. diff --git a/docs/src/routes/(content)/developers-guide/localization/locale-selection-step-by-step/+page.md b/docs/src/routes/(content)/developers-guide/localization/locale-selection-step-by-step/+page.md new file mode 100644 index 000000000..2022c58a7 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/localization/locale-selection-step-by-step/+page.md @@ -0,0 +1,31 @@ +# Locale selection step-by-step + +The locale selection process works as follows. + +[`$lib/i18n`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/i18n/init.ts) is initialized: + +1. Supported `locales` and the `defautlLocale` are loaded from [`StaticSettings`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.ts) + +[`hooks.server.ts: handle`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/hooks.server.ts) parses the route and `Request.accept-language`: + +1. Supported `locales` are loaded from `$lib/i18n` +2. The locales listed in `Request.accept-language` are iterated and the first one found (using a soft match) in `locales` is saved as `preferredLocale` +3. We check if there is a `lang` route parameter and it is included in `locales` +4. Depending on these, one of the following happens: + +| `lang` route param | `preferredLocale` | Action | +| ------------------ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| not defined | not supported | Redirect to `/${defautlLocale}/${route}` | +| not defined | supported | Redirect to `/${preferredLocale}/${route}` | +| soft match | N/A | Redirect to `/${supportedLocale}/${route}` where `supportedLocale` is the soft-matched locale, e.g. `'en'` for `lang = 'en-UK'` | +| supported | N/A | Serve content in `lang` (and show notification in the front end if `preferredLocale` is supported and `!= lang`) | + +1. Both `preferredLocale` and `currentLocale` (in which the content is served) are passed further in `locals`. + +[`+layout.ts`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/routes/[[lang=locale]]/+layout.ts) loads translations from the local source and the database: + +1. Load `DataProvider.getAppCustomization(•).translationOverrides` as dynamic translations for use with `i18n`. +2. Load local translations with `$lib/i18n: loadTranslations` +3. Add the `translationOverrides` by `$lib/i18n: addTranslations` +4. Set the locale to `params.lang` +5. Call `$lib/i18n: setRoute('')` which is required for the translations to be available. diff --git a/docs/src/routes/(content)/developers-guide/localization/localization-in-strapi/+page.md b/docs/src/routes/(content)/developers-guide/localization/localization-in-strapi/+page.md new file mode 100644 index 000000000..6233acfae --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/localization/localization-in-strapi/+page.md @@ -0,0 +1,13 @@ +# Localization in Strapi + +Strapi's built-in i18n plugin is not used because it creates objects with different ids for each locale. Thus, a proprietary `json`-based format is used for translated strings instead of regular text fields. (This format is defined in [`@openvaa/app-shared`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/data/localized.type.ts) as `LocalizedString`.) The example below also demonstrates the use of [ICU message format](https://formatjs.io/docs/intl-messageformat/) value interpolation. + +```json +{ + "en": "You have {testValue, plural, =0 {no photos.} =1 {one photo.} other {# photos.}}", + "fi": "Sinulla {testValue, plural, =0 {ei ole valokuvia.} =1 {on yksi valokuva.} other {on # valokuvaa.}}", + "es-CO": "Tienes {testValue, plural, =0 {no photos.} =1 {un photo.} other {# photos.}}" +} +``` + +The methods offered by [`DataProvider`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/api/base/dataProvider.type.ts) handle the translations based on an optional `locale` parameter accepted by all of them. This defaults to the current locale. The functions pick the requested language (or the best match) from the `json` fields and return them as regular `string`s. diff --git a/docs/src/routes/(content)/developers-guide/localization/localization-in-the-frontend/+page.md b/docs/src/routes/(content)/developers-guide/localization/localization-in-the-frontend/+page.md new file mode 100644 index 000000000..16c486873 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/localization/localization-in-the-frontend/+page.md @@ -0,0 +1,40 @@ +# Localization in the frontend + +All localized strings are fetched using the same `$t('foo.bar', {numBar: 5})` provided by the [`I18nContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/i18n/i18nContext.type.ts) and, for convenience, all other contexts including it, such as the [`ComponentContext`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/component/componentContext.type.ts). This is agnostic to both: + +1. Whether the translations are local (i.e. from `json` files) or fetched from the database. Local fallbacks are overwritten by those from the database. +2. Whether SSR or CSR rendering is used. + +> Never import any stores directly from `$lib/i18n`. Use the imports provided by the contexts instead. You can safely use the utilities in [`$lib/i18n/utils`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/i18n/utils), however. + +### Value interpolation + +For value interpolation in already translated strings, such as those contained in database objects, the same [ICU message format](https://formatjs.io/docs/intl-messageformat/) value interpolation is provided with a `parse('Foo is {value}', {value: 'bar'})` function also provided by `$lib/i18n`. + +Some commonly used values are automatically provided for interpolation. See [`updateDefaultPayload`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/i18n/init.ts). + +> If any interpolated values are missing, the string will not be translated and its key will be displayed. + +### Localized default values in components + +When providing localized default values to component properties, be sure to assign these reactively or they won't be updated when the locale is changed, i.e. + +```tsx +// This will NOT update +export let label = $t('someLabel'); +// On the page +<label>{label}</label>; + +// This will update +export let label = undefined; +// On the page +<label>{label ?? $t('someLabel')}</label>; +``` + +### Localized texts included in dynamically loaded data + +Specific care must be taken with any localized content loaded dynamically so that language changes are propagated everywhere where the data is used. + +The data loaded by the Data API (settings, app customization and anything contained in the [`dataRoot`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/contexts/data/dataContext.type.ts) store or its descendants) is always returned already translated. Therefore, if any such data is used in the frontend, it should be reactive. + +This is usually automatically the case, because the contexts hold such data in stores, but when reading store values make sure not to use outdated local copies. diff --git a/docs/src/routes/(content)/developers-guide/localization/storing-multi-locale-data/+page.md b/docs/src/routes/(content)/developers-guide/localization/storing-multi-locale-data/+page.md new file mode 100644 index 000000000..9282f0983 --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/localization/storing-multi-locale-data/+page.md @@ -0,0 +1,5 @@ +# Storing multi-locale data + +The data model expects single-locale data to be displayed by most frontend components and when transferred between modules. The backend as well as Admin and Candidate Apps, however, deal with multi-locale data. + +Some common such data types are defined in the [`@openvaa/app-shared`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/data/localized.type.ts) module. The types prefixed with `Localized` are multi-locale versions of the basic data types. diff --git a/docs/src/routes/(content)/developers-guide/localization/supported-locales/+page.md b/docs/src/routes/(content)/developers-guide/localization/supported-locales/+page.md new file mode 100644 index 000000000..76440d51f --- /dev/null +++ b/docs/src/routes/(content)/developers-guide/localization/supported-locales/+page.md @@ -0,0 +1,10 @@ +# Supported locales + +Supported locales are defined app-wide in [`StaticSettings`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.ts). + +### Adding new locales + +1. Add the locale to [`locales`](https://github.com/OpenVAA/voting-advice-application/blob/main/frontend/src/lib/i18n/translations/index.ts) +2. Create versions of all the translation files in the new locale and place them in `frontend/src/lib/i18n/translations/<LOCALE>` +3. Copy the `dynamic.json` translation file to `../backend/vaa-strapi/src/util/translations/<LOCALE>/dynamic.json` +4. Make the locale available in [`StaticSettings`](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.ts). diff --git a/docs/quick-start.md b/docs/src/routes/(content)/developers-guide/quick-start/+page.md similarity index 81% rename from docs/quick-start.md rename to docs/src/routes/(content)/developers-guide/quick-start/+page.md index 9078e3dc0..3bffa27d4 100644 --- a/docs/quick-start.md +++ b/docs/src/routes/(content)/developers-guide/quick-start/+page.md @@ -16,4 +16,4 @@ - Username `admin` - Password `admin` -- If you run into errors, try checking the tips related to Docker in [Troubleshooting](troubleshooting.md#troubleshooting). +> If you run into errors, try checking the tips related to Docker in [Troubleshooting](/developers-guide/troubleshooting#troubleshooting). diff --git a/docs/troubleshooting.md b/docs/src/routes/(content)/developers-guide/troubleshooting/+page.md similarity index 90% rename from docs/troubleshooting.md rename to docs/src/routes/(content)/developers-guide/troubleshooting/+page.md index c86bf6228..6c584ee0e 100644 --- a/docs/troubleshooting.md +++ b/docs/src/routes/(content)/developers-guide/troubleshooting/+page.md @@ -1,5 +1,7 @@ # Troubleshooting +If you can't find an answer to your problem below, feel free to reach us via Github [Discussions](https://github.com/OpenVAA/voting-advice-application/discussions). + ## Commit error: ’Husky not found’ Try running `npx husky install`. @@ -21,7 +23,7 @@ Try running `yarn build:app-shared` first. Try deleting `/yarn.lock` and rerunning the command. You may also: - check that you’re using the correct Node version (see [Docker Setup: Requirements](https://github.com/OpenVAA/voting-advice-application/blob/main/docs/docker-setup-guide.md#requirements)). -- follow the steps in [Docker error: ”No space left on device” error](#docker-no-space-left-on-device-error) below. +- follow the steps in [Docker error: ”No space left on device” error](#docker-error-no-space-left-on-device-errordocker-no-space-left-on-device-error) below. ## Docker error: Load metadata for docker.io/library/node:foo @@ -68,15 +70,15 @@ If that's not the issue, open Docker and check the `frontend` and `strapi` conta The REST api query syntax can be a bit tricky, notably `*` only goes one-level deep. -Another possible cause is that the access control policy does not allow populating the relations. The policy is defined for each API route in [`backend/vaa-strapi/src/api/<schema>/routes/<schema>.ts`](/backend/vaa-strapi/src/api). For more information, see [Security](backend.md#security). +Another possible cause is that the access control policy does not allow populating the relations. The policy is defined for each API route in `backend/vaa-strapi/src/api/<schema>/routes/<schema>.ts`. For more information, see [Security](/developers-guide/backend/security). ## Playwright: `TimeoutError` when locating elements and running the tests locally -Elements are currently located mostly by their translated labels with hardcoded locales, which match those in the mock data. If, however, the `supportedLocales` you have set in [staticSettings.ts](/packages/app-shared/src/settings/staticSettings.ts) differ from the ones used by the tests, many of them will fail. +Elements are currently located mostly by their translated labels with hardcoded locales, which match those in the mock data. If, however, the `supportedLocales` you have set in [staticSettings.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/staticSettings.ts) differ from the ones used by the tests, many of them will fail. ## Strapi: Content model is reset after restart -Any changes to the content model are not reflected on local files by default. If you can't see any changes in your local files when editing the content types using Strapi's web UI, check that you have [hot reloading enabled](#hot-reloading-the-backend). +Any changes to the content model are not reflected on local files by default. If you can't see any changes in your local files when editing the content types using Strapi's web UI, check that you have [hot reloading enabled](/developers-guide/development/running-the-development-environment). ## Strapi error: ’Relation already exists’ error on restart after editing the content model diff --git a/docs/src/routes/(content)/publishers-guide/after-publishing-the-vaa/intro/+page.md b/docs/src/routes/(content)/publishers-guide/after-publishing-the-vaa/intro/+page.md new file mode 100644 index 000000000..8e00e06e1 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/after-publishing-the-vaa/intro/+page.md @@ -0,0 +1,5 @@ +# After publishing the VAA + +Most of the hard work is done when you’ve published the candidate application or the actual voters’ VAA. + +The time to rest on your laurels has not, alas, come quite yet. diff --git a/docs/src/routes/(content)/publishers-guide/after-publishing-the-vaa/marketing/+page.md b/docs/src/routes/(content)/publishers-guide/after-publishing-the-vaa/marketing/+page.md new file mode 100644 index 000000000..3e77cb1e0 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/after-publishing-the-vaa/marketing/+page.md @@ -0,0 +1,7 @@ +# Marketing + +Building a great VAA and getting candidates and parties to answer it is, unfortunately, not enough for it to have the intended effect. + +For getting users to your VAA, one option is to collaborate with a media outlet. + +You can also market the VAA in social media or otherwise, but it’s best to check well in advance if promoting the VAA will fall foul of the platform’s possible limitations on political advertising. diff --git a/docs/src/routes/(content)/publishers-guide/after-publishing-the-vaa/user-support/+page.md b/docs/src/routes/(content)/publishers-guide/after-publishing-the-vaa/user-support/+page.md new file mode 100644 index 000000000..31d1c6e6d --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/after-publishing-the-vaa/user-support/+page.md @@ -0,0 +1,9 @@ +# User support + +During the time the candidate application is open, you will probably receive a number of requests for support. + +As described earlier, it’s best to have a plan for or preferably a dedicated person responding to these requests. + +With voters, there’s less need for such support but you’d be well advised to monitor the feedback sent by users to find out if they’re having any issues with the VAA. + +The developer you’re working with should also keep an eye on the application logs and performance. diff --git a/docs/src/routes/(content)/publishers-guide/app-settings/+page.md b/docs/src/routes/(content)/publishers-guide/app-settings/+page.md new file mode 100644 index 000000000..277796c6d --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/app-settings/+page.md @@ -0,0 +1,147 @@ +# Application settings and features + +> This section deals with configuring App Settings. For adding new ones, see [Adding new settings](/developers-guide/configuration/app-settings). + +The application’s functionality is affected by different types of settings: + +- App Settings, which can be edited in the Strapi dashboard at any time and, hence, also called dynamic settings +- Environmental variables, which can only be edited in the source code or the hosting platform +- Static settings, which can only be edited in the source code +- Built-in features but which cannot be easily enabled via settings + +In addition to these, the application’s text contents and appearance can be changed in the [Customization](#customization) options. + +Below, the dynamic App Settings are explained in detail as well as some features that must enabled otherwise. For more information about the environmental variables and static settings, see [Configuration](/developers-guide/configuration/intro) in the Developers’ Guide. + +## App Settings + +App Settings can be edited in the Strapi dashboard. When the application is created, they’re initialized with the default values set in the [dynamicSettings.ts](https://github.com/OpenVAA/voting-advice-application/blob/main/packages/app-shared/src/settings/dynamicSettings.ts) file. + +The settings control the following features. + +### `survey` + +Settings related to a user survey. If not defined, no survey will be shown. + +- `linkTemplate`: The link to the survey. This is passed to the translation function, which will replace `{sessionId}` with the URL-encoded session id if available or an empty string otherwise. +- `showIn`: Where the survey prompt should be shown. The `resultsPopup` option means that the survey will be shown in a popup after a timeout starting when the user reaches the results page. Use `results.showSurveyPopup` to set the delay. + +### `entityDetails` + +Settings related to the entity details view, i.e. the pages for individual candidates and parties. + +- `contents`: Which content tabs to show. + - `candidate`: The content tabs to show for candidates. Possible values: + - `info`: Basic information. + - `opinions`: Answers to opinion questions. + - `organization`: The content tabs to show for parties. Possible values: + - `candidates`: The party’s candidates. + - `info` + - `opinions` +- `showMissingElectionSymbol`: Whether to show a marker for missing election symbol in entity details, e.g. 'Election Symbol: --', or hide missing items completely. The marker, if shown, is defined in the translations. +- `showMissingAnswers`: Whether to show a marker for missing answers in entity details as, e.g. 'Age: --', or hide missing items completely. The marker, if shown, is defined in the translations. This only applies to non-opinion questions. + +### `header` + +Settings related to the actions in the app header. + +- `showFeedback`: Whether to show the feedback icon by default in the header. +- `showHelp`: Whether to show the help icon by default in the header. + +### `headerStyle` + +> These will be moved to App customization in the future. + +Settings related to app header styling. + +- `dark`: Background colors for the header in dark mode. + - `bgColor`: Default background color of the header. + - `overImgBgColor`: Background color of the header when it’s over an image. +- `light`: Background colors for the header in light mode. + - `bgColor`: Default background color of the header. + - `overImgBgColor`: Background color of the header when it’s over an image. +- `imgSize`: The size of the background image in the header. E.g. `cover`, `contain`, or specific sizes like `100% 50%`. +- `imgPosition`: The positioning of the background image in the header. E.g. `center`, `top`, `bottom`, `left`, `right`, or specific positions like `50% 25%`. + +### `entities` + +Settings controlling which entities are shown in the app. + +- `hideIfMissingAnswers`: Settings controlling whether entites with missing answers should be shown. This is currently only supported for candidates. + - `candidate`: Whether to hide candidates with missing answers in the app. +- `showAllNominations`: Whether to show the `/nominations` route on which all nominations in the app are shown. + +### `matching` + +Settings related to the matching algorithm. + +- `minimumAnswers`: The minimum number of voter answers needed before matching results are available. +- `organizationMatching`: The method with which parties are matched. The options are: + - `none`: no party matching is done + - `answersOnly`: matching is only performed on the parties explicit answers + - `impute`: missing party answers are substituted with an anwswer imputed from the party's candidates' answers. + +### `questions` + +Settings related to the question view. + +- `categoryIntros`: Settings related to the optional category intro pages. + - `allowSkip`: Whether to allow the user to skip the whole category. + - `show`: Whether to show category intro pages before the first question of each category. +- `interactiveInfo`: Settings related to the interactive info view for each question. + - `enabled`: Default `false`. +- `questionsIntro`: Settings related to the optional questions intro page, shown before going to questions. + - `allowCategorySelection`: Whether to allow the user to select which categories to answer if there are more than one. NB. If the app has multiple elections with different question applicable to each, category selection may result in cases where the user does not select enough questions to get any results for one or more elections, regardless of the minimum number of answers required. In such cases, consider setting this to `false`. + - `show`: Whether to show the questions intro page. +- `showCategoryTags`: Whether to show the category tag along the question text. +- `showResultsLink`: Whether to the link to results in the header when answering questions if enough answers are provided. + +### `results` + +Settings related to the results view. + +- `cardContents`: Settings related to the contents of the entity cards in the results list and entity details. + - `candidate`: The additional contents of candidate cards. NB. the order of the items has currently no effect. Possible values: + - `submatches`: Show the entity's answer to a specific question. Only applies to the results list. + - Question answer, defined by three properties: + - `question`: The question's id. + - `hideLabel`: Whether to hide the question label in the card. + - `format`: How to format the answer. Possible values: + - `default`: use the same format as in entity details. + - `tag`: format the answers as a pill or tag. +- `organization`: The additional contents of party cards. NB. the order of the items has currently no effect. Possible values: + - `candidates`: List the party's the top 3 candidates within it's card. Only applies to the results list. + - `submatches` + - Question answer +- `sections`: Which entity types to show in the results view. There must be at least one. +- `showFeedbackPopup`: If defined, a feedback popup will be shown on the next page load, when the user has reached the results section and the number of seconds given by this value has passed. The popup will not be shown, if the user has already given some feedback. +- `showSurveyPopup`: The delay in seconds after which a survey popup will be shown on the next page load, when the user has reached the results section. The popup will only be shown if the relevant `analytics.survey` settings are defined and if the user has not already opened the survey. + +### `elections` + +Settings related to election and constituency selection in VAAs with multiple elections. These have no effect if there is just one election. + +- `disallowSelection`: If `true` all elections are selected by default. +- `showElectionTags`: Whether to show the election tags along the question text. +- `startFromConstituencyGroup`: If `true` and there are multiple elections, the constituency selection page with this `ConstituencyGroup` as the only option will be shown first and the possible election selection only afterwards. Only those elections that are applicable to the selected constituency or its ancestors are shown. Election selection will be bypassed the same way as normally. + +### `access` + +Settings related to access to the applications. + +- `candidateApp`: If `true`, the Candidate App can be accessed. +- `answersLocked`: If `true`, candidates can no longer edit their answers. +- `voterApp`: If `true`, the Voter App can be accessed. +- `adminApp`: If `true`, the Admin App can be accessed. +- `underMaintenance`: If `true`, an under maintenance error page will be shown when attempting to access any part of the app. + +### `notifications` + +Settings related to important notifications shown to users. + +- `candidateApp`: The notification shown to users of the Candidate App. Defined by the properties: + - `show`: If `true`, the notification will be shown the next time the user loads the app. + - `title`: The title of the notification. + - `content`: The content of the notification. + - `icon`: The `Icon.name` to display in the notification. +- `voterApp`: The notification shown to users of the Voter App. diff --git a/docs/src/routes/(content)/publishers-guide/data-collection/additional-data-for-the-voter/+page.md b/docs/src/routes/(content)/publishers-guide/data-collection/additional-data-for-the-voter/+page.md new file mode 100644 index 000000000..4e7d3c2d3 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/data-collection/additional-data-for-the-voter/+page.md @@ -0,0 +1,7 @@ +# Additional data for the voter application + +Some of the application data is not needed for the candidate application and can, thus, be provided a bit later. These include: + +- [ ] Content for the introduction views in the voter application +- [ ] Additional supporting information for statements, such as video +- [ ] Text content only visible in the voter application diff --git a/docs/src/routes/(content)/publishers-guide/data-collection/candidates-or-parties-answers/+page.md b/docs/src/routes/(content)/publishers-guide/data-collection/candidates-or-parties-answers/+page.md new file mode 100644 index 000000000..4b633d69f --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/data-collection/candidates-or-parties-answers/+page.md @@ -0,0 +1,3 @@ +# Candidates’ or parties answers + +Depending on the method of data collection you’ve decided on, you can either collect these manually or have the candidates use the candidate application to enter their own data. diff --git a/docs/src/routes/(content)/publishers-guide/data-collection/data-from-final-election-lists/+page.md b/docs/src/routes/(content)/publishers-guide/data-collection/data-from-final-election-lists/+page.md new file mode 100644 index 000000000..88e56e350 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/data-collection/data-from-final-election-lists/+page.md @@ -0,0 +1,11 @@ +# Data from final election lists + +After the election lists have been confirmed, you will need to corroborate that the data received from the candidates and parties is accurate. + +In addition, some data will only be available at this time, such as: + +- [ ] The election numbers of symbols +- [ ] Electoral alliances between parties in each constituency +- [ ] The party or constituency association nominating each candidate in each constituency + +If you haven’t asked parties to provide their data beforehand, you may also add at this point logos, brand colors and standard abbreviations to the parties’ data. diff --git a/docs/src/routes/(content)/publishers-guide/data-collection/initial-data/+page.md b/docs/src/routes/(content)/publishers-guide/data-collection/initial-data/+page.md new file mode 100644 index 000000000..df0a8728b --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/data-collection/initial-data/+page.md @@ -0,0 +1,15 @@ +# Initial data + +Before publishing the candidate application, you will need to the data below. The developer you’re working with will probably provide you spreadsheet templates to fill. + +- [ ] Details of the election or elections +- [ ] Details of constituencies for each election +- [ ] Statement categories +- [ ] Statements +- [ ] Statements’ supporting information +- [ ] Non-statement questions +- [ ] Candidate lists if using the candidate app and not using bank authentication +- [ ] Information content about the elections +- [ ] Privacy statements +- [ ] Any other customized text content +- [ ] Assets for visual customization diff --git a/docs/src/routes/(content)/publishers-guide/data-collection/intro/+page.md b/docs/src/routes/(content)/publishers-guide/data-collection/intro/+page.md new file mode 100644 index 000000000..b33027e04 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/data-collection/intro/+page.md @@ -0,0 +1,11 @@ +# Steps for collecting data + +After designing your VAA and preparing the content for the statements and their supporting information, you will need to collect all of the data needed for publication. + +The steps for data collection can be divided into: + +1. initial data, including everything needed before publishing the possible candidate application +2. candidates’ or parties’ answers, which can be collected manually or using the candidate application +3. additional data for the voter application +4. final elections lists +5. moderation of candidate answers. diff --git a/docs/src/routes/(content)/publishers-guide/data-collection/moderation-of-candidate-answers/+page.md b/docs/src/routes/(content)/publishers-guide/data-collection/moderation-of-candidate-answers/+page.md new file mode 100644 index 000000000..031bc9720 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/data-collection/moderation-of-candidate-answers/+page.md @@ -0,0 +1,5 @@ +# Moderation of candidate answers + +If the candidate app is available, candidates can enter free-form explanation to accompany their replies to the statements. + +You may want to perform some kind of moderation on these to remove offensive language before publishing the application to voters. diff --git a/docs/src/routes/(content)/publishers-guide/intro/+page.md b/docs/src/routes/(content)/publishers-guide/intro/+page.md new file mode 100644 index 000000000..fbfb6d761 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/intro/+page.md @@ -0,0 +1,7 @@ +# Introduction + +If you want to publish a Voting Advice Application (VAA for short) using OpenVAA, this guide is for you. + +The guide covers all of the steps of creating a VAA from initial scoping to data collection, deployment and beyond. + +If you don't know what a VAA is, start from [What are VAAs](/publishers-guide/what-are-vaas/intro). diff --git a/docs/src/routes/(content)/publishers-guide/other-information-sources/+page.md b/docs/src/routes/(content)/publishers-guide/other-information-sources/+page.md new file mode 100644 index 000000000..7ff9f03e6 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/other-information-sources/+page.md @@ -0,0 +1,6 @@ +# Other information sources + +For further reading on VAAs, we recommend: + +- Sivistävän vaalikoneen käsikirja, 2026. A handbook for publishing educational VAAs, published by Sitra. +- [The Policy Positioning Tool for Political Parties: A Facilitator’s Guide](https://www.idea.int/publications/catalogue/policy-positioning-tool-political-parties-facilitators-guide), 2016. Published by Idea International. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/candidates-and-parties-data-be/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/candidates-and-parties-data-be/+page.md new file mode 100644 index 000000000..5cc52b969 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/candidates-and-parties-data-be/+page.md @@ -0,0 +1,57 @@ +# How should candidates’ and parties’ data be collected? + +The complexity of collecting data for your VAA will depend greatly on whether it will be based on parties or candidates – and how many of these will there be. + +For each, you will need to collect: + +- their basic information such as name +- possibly a candidate portrait or party logo +- their answers to the statements with possible accompanying free-form explanations +- which election and which constituency they’re standing in +- for candidates, the party nominating them +- for parties, the possible electoral alliance they belong to (in each constituency) +- their possible election number or symbol + +If there is only a small number of parties, you may be able to collect these data manually with online forms or by sending spreadsheets to fill. + +In candidate-based settings, this is, however, seldom feasible and you will need to enable the platform’s candidate application so that they can enter their data themselves. + +## How should candidates register for the VAA? + +For candidate registration, there are two options. + +### 1. Email-based registration + +This is the simpler choice, but you will need to collect a list of the candidates’ emails. Because the electoral authorities do not usually publish such lists at all or early enough, you will usually need to contact the parties standing in the elections to get these lists. + +Once you have the lists, the emails can be added to the VAA after which the candidates will be able to enter their data by signing up with their email. They can also be sent reminders to sign up if they haven’t done so. + +### 2. Bank-authentication-based preregistration + +In some cases, however, procuring the candidates lists from the parties may be hard or impossible. + +With bank-authentication-based preregistration anyone can sign up as a preliminary candidate and enter their data in the application. + +Their full name and birth date will be stored, and these can be used to select only officially confirmed candidates to be included in the VAA when it’s published to voters. At the same time, the parties nominating them and any other information from the lists can be automatically added to their data. + +The downside of using the bank-authentication method is that it will require a subscription to an authentication provider, which may cost up to a few thousands of euros. It will also require a somewhat more complex setup than the simple method. + +## Where can official election lists be found? + +In order to corroborate the data candidates give out – such as which party they stand for and in which constituency – it is best to rely on the official electoral lists published by the body organising the elections. And if you’re using bank-authentication-based preregistration, these will be required. + +## How long should the application be open for candidates? + +We recommend keeping the application open for candidates for at least one or 1.5 months but preferably a bit longer. + +It’s usually advisable to also close it for candidates before the VAA is published to voters so that the results they receive do not change (and that you have time to moderate the answers if necessary). + +## How will you offer technical support for candidates? + +It is very often the case that candidates will require assistance when registering for or using the candidate application. + +In our experience up to 10% or even 20% of candidates may contact support, so it’s best to have a clear plan for dealing with such requests. + +A spike in such attempts will probably take place right before the deadline for candidates to submit their answers – or within a week _thereafter_ as those who missed the deadline still want to enter their information. (The application can be reopened if you so wish.) + +The candidate application also has a Frequently asked questions page onto which answers to common problems can be added. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/intro/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/intro/+page.md new file mode 100644 index 000000000..08f9fdbb8 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/intro/+page.md @@ -0,0 +1,30 @@ +<script lang="ts"> + import ResearchQuote from '$lib/components/ResearchQuote.svelte'; +</script> + +# Designing a VAA + +When preparing to publish a VAA follow the steps in this section. Not all of them are relevant in all cases, but it’s worthwhile to check each. + +<ResearchQuote title="Tenets of VAA design" author="Veikko Isotalo" references={[ +'Garzia, D., & Marschall, S. (2014). The Lausanne Declaration on Voting Advice Applications. In D. Garzia and S. Marschall (Eds.), Matching Voters with Parties and Candidates: Voting Advice Applications in a Comparative Perspective (pp. 227–228). Colchester, UK: ECPR Press.', +'​Isotalo, V. (2021). Improving candidate-based voting advice application design: The case of Finland. Informaatiotutkimus, 40(3), 85-109.' +]}> + +There is no universal VAA design that would fit in all contexts. VAA designs are highly dependent on the election type and the electoral system. It also matters for the VAA if the election is a local one instead of a national election. Sometimes multiple elections can be held at the same time, in these cases having separate VAAs for each election might not be optimal for user fatigue. + +For determining an optimal VAA design in a specific setting one has to consider the following questions: + +- Are there multiple elections at the same time? +- How many electoral districts are there? +- Are there important local issues in different districts? +- Do voters vote for a party or a candidate? +- If voters cast a personal vote, how many candidates are running for the same party? +- Do parties rank candidates in their lists or are candidate positions determined by their personal votes? + +Even though there is no universal VAA design which would be suitable for all contexts, a short list of design principles has been suggested by VAA researchers. + +According to the Lausanne declaration VAAs should be “open, transparent, impartial and methodologically sound” (Garzia and Marschall 2014, 227–228). Openness refers to accessibility for both users and political actors. Transparency relates to transparency of intentions and funding regarding the VAA development. More broadly, users should be able to know how parties and candidates were positioned on the VAA. Additionally, transparency entails the matching algorithm, which should be understandable to the users. According to the impartiality requirement, all parties should be included in the VAA, and the VAA design should not favor any party in a systematic manner (Garzia and Marschall 2014, 227–228). + +Lausanne declaration’s guidelines can be seen as the minimum standards for VAAs. In addition, VAA designers could consider more ambitious design goals. For instance, VAAs could be designed in such a way that they empower their users to modify them to suit their own needs (interactivity and customizability) or that promote honest answering behavior of candidates (see more in Isotalo 2021). To reach the wanted design goals, VAA designers will have to make specific design choices regarding VAA elements such as statements, answering scales, user interface, matching algorithm, and how voting advice is displayed (Isotalo 2021). It is important for VAA designers and developers to keep these design goals in mind and reflect how their design choices achieve the set out goals. The challenge in VAA development stems from the fact that not all design choices work well together. This guide is made to help you navigate through these challenges. +</ResearchQuote> diff --git a/docs/src/routes/(content)/publishers-guide/preparing/languages-will-the-vaa-be/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/languages-will-the-vaa-be/+page.md new file mode 100644 index 000000000..795609b87 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/languages-will-the-vaa-be/+page.md @@ -0,0 +1,11 @@ +# Which languages will the VAA be published in? + +The VAA can be published in any one language or combination thereof. + +The language affects all of the application’s content. Candidates or parties can also provide their answers in any of the languages you select for the app. + +## Are all the languages available? + +The currently available languages can be seen in the [translations folder](https://github.com/OpenVAA/voting-advice-application/tree/main/frontend/src/lib/i18n/translations). + +If the language you would like to use is not available, it can be added by creating [new translation files](/developers-guide/localization/supported-locales). This process is a bit arduous and is best started using an LLM. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/matching/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/matching/+page.md new file mode 100644 index 000000000..9f64b016f --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/matching/+page.md @@ -0,0 +1,134 @@ +<script lang="ts"> + import ResearchQuote from '$lib/components/ResearchQuote.svelte'; +</script> + +# How should the recommendations be computed? + +The platform offers a robust matching algorithm that can be configured in a few ways. + +## What is the minimum number of answers needed for matching? + +<ResearchQuote +title="Minimum number of answers" +author="Veikko Isotalo"> + +For the VAA to function it is necessary that a user answers at least one statement/question for the VAA to calculate a recommendation. However, many VAAs also display users’ positions on ideological scales based on their answers to the VAA statements. In this case, it should be notified to the user that not all features of the VAA will be available, if they do not answer a required set of statements. We recommend that the user should not be limited in their way they want to use the VAA, as it could be that the user is a single issue voter and they only care about one VAA question. There is no need to place an arbitrary number of minimum answers (higher than one), as this could potentially hamper the usefulness of the VAA advice for the user. Minimum number of answers can be set higher only in cases, when the VAA cannot technically provide a recommendation. + +Many VAAs have also enabled a viewer-mode, where users can inspect candidate or party profiles without answering any statements. This is a useful feature for users that might have already done the VAA once before and want to simply go back to inspect candidate/party profiles. + +</ResearchQuote> + +This dictates how many statements the voters must answer before the matches are computed. They can still view the candidate and party lists but no scores will be given. + +This number can be anything from 1 up, but if the number is low, the results are not reliable. + +## What metric should be used for distance calculation in matching? + +<ResearchQuote +title="Algorithmic choices and distance calculation methods" +author="Veikko Isotalo" +references={[ +'Downs, A. (1957). An Economic Theory of Democracy. New York: Harper & Row Publishers', +'Isotalo, V. (2020). Designing Voting Advice Applications: The Finnish Case. Master’s thesis. http://urn.fi/URN:NBN:fi:aalto-2020112918207', +'Katakis, I., Tsapatsoulis, N., Mendez, F., Triga, V., & Djouvas, C. (2014). Social Voting Advice Applications-Definitions, Challenges, Datasets and Evaluation. IEEE Transactions on Cybernetics, 44(7), 1039–1052. https://doi.org/10.1109/TCYB.2013.2279019', +'Mendez, F. (2017). Modeling proximity and directional decisional logic: What can we learn from applying statistical learning techniques to VAA-generated data? Journal of Elections, Public Opinion and Parties, 27(1), 31–55. https://doi.org/10.1080/17457289.2016.1269113', +'Merrill, S., & Grofman, B. (1999). A Unified Theory of Voting: Directional and Proximity Spatial Models. Cambridge: Cambridge University Press.', +'Rabinowitz, G., & Macdonald, S. (1989). A Directional Theory of Issue Voting. The American Political Science Review, 83(1), 93–121. https://doi.org/10.2307/1956436', +'Romero Moreno, G., Padilla, J., & Chueca, E. (2020). Learning VAA: A new method for matching users to parties in voting advice applications. Journal of Elections, Public Opinion and Parties, 1–19. https://doi.org/10.1080/17457289.2020.1760282' +]}> + +VAAs can use different matching algorithms to determine the best suitable candidate/party. Isotalo (2020, 33-38) notes that there have been different types of general approaches for matching algorithms. The most prevalent algorithm is based on issue distance between the user and the candidate/party, which follows the issue voting paradigm. There have been attempts to include additional factors to the issue distance model (e.g., candidate social media posts, candidate incumbency status). However, these external sources can introduce bias to the recommendations. Additionally, there have been attempts to include community-based recommendations to VAAs (see Katakis et al. 2014) or use machine learning to calibrate distance matrices of statements (Romero Moreno et al. 2020). Here, we will focus on the most common variants of the issue distance algorithms. + +According to Isotalo (2020, 39) there are five design choices that the VAA designer has to make regarding the matching algorithm: + +1. “choosing the number of dimensions of the modelling space, +2. “choosing how questions are combined to form the dimensions, +3. “choosing a distance matrix for an appropriate spatial model, +4. “choosing a method to aggregate the overall distance scores between a user and a candidate in the modelling space and +5. “choosing whether questions can be weighted.” + +For an in depth explanation, we recommend the reader to consult Isotalo (2020, 39-43). Here, we will introduce the most popular models of issue voting and distance metrics that will get you started. + +#### Issue voting: Proximity vs. directional models + +How to determine which party to vote for based on stances on political issues alone? This question piqued the interest of political scientists since the 1950s. Downs (1957) suggested that rational utility maximizing voters vote for parties that are the most proximate to their own ideological positions. Alternative view, introduced by Rabinowitz and Macdonald (1989), suggests that voters prefer parties that have directional (clear) political stances on issues (see more Isotalo 2020, 24-30). Other models have also been introduced (see Merrill & Grofman 1999), but these models have not been widely implemented in the context of VAAs. + +Both proximity and directional models can be easily implemented in VAAs (see Figure 1). Values in the figure represent distance scores associated with combinations of user and candidate answers on five-point Likert scale ranging from completely agree (CA) to completely disagree (CD). Total agreement gets a positive score of +1, whereas a total disagreement is shown as -1. It is also possible to combine the two theories into a hybrid matrix (Panel C). We recommend an implementation, where the user can choose the issue voting model that they prefer. + +<figure> +<img src="/images/guides/distance-matrices.png" + alt="Distance matrices in Isotalo (2020, 41)" class="w-full"/> +<figcaption>Figure 1. Distance matrices in Isotalo (2020, 41). First matrix from the left shows the proximity model (panel A), the middle matrix (panel B) displays the directional model, and the right one (panel C) displays the combination of the two. All panels use Manhattan distance metric.</figcaption> +</figure> + +#### Distance metrics + +According to Isotalo (2020, 41) there are three main metrics that can be used to aggregate distance scores in multi-dimensional space: + +1. Manhattan distance, +2. Euclidean distance and +3. Mahalanobis distance. + +The decision, which distance metric one should use is dependent on the dimensional structure of the VAA. If the VAA is providing its recommendations in a low-dimensional space, then Euclidean distance is a valid choice. In high-dimensional spaces, Manhattan distance is a preferable choice. The Euclidean distance metric is not an optimal choice for aggregating distances in high-dimensional space, as the metric uses list-wise deletion to deal with missing values (Mendez 2017, 50-51). + +Both Euclidean and Manhattan distance metrics ignore possible correlations of VAA items assuming their independence. Mahalanobis distance metric takes into consideration these correlations (Katakis et al. 2014, 8). However, using Mahalanobis distance is more demanding as it requires data (either candidate or pre-user data) to form the covariance matrix which establishes the relations of issue variables Isotalo (2020, 42). + +</ResearchQuote> + +The distance calculation metric can also be changed from the commonly used Manhattan or ‘city block’ metric to something else. + +If you’re unsure, it’s best not to change the metric. + +## Should matches be calculated for statement categories? + +<ResearchQuote +title="Sub-category matching" +author="Veikko Isotalo"> + +Sub-category matching is possible to implement if VAA statements are labelled into different themes, e.g., economy, environment, etc. Sub-category matching means a way to show the user their match with a party/candidate in separate issue categories. Users can find it helpful to identify candidates that match with them the best on a specific policy theme that they care about. + +The same principle can be applied when the VAA calculates ideological positions based on multiple statements. In this case, the user could then be shown which candidates/parties match the highest on particular ideological dimensions (e.g., the Left–Right dimension). + +<figure> +<img src="/images/guides/subcategory-matches.png" + alt="Screenshot of a VAA built with OpenVAA showing subcategory matches for candidates." class="h-[40rem] max-h-[80vh]"/> +<figcaption>Figure 1: Subcategory matches for candidates for the four categories whose statements the voter has answered.</figcaption> +</figure> + +</ResearchQuote> + +Voters can also be shown results for each candidate or party in each of the categories you have divided the statements into. + +This can offer richer information in the results list in addition to the simplifying matching score. + +## How to treat missing candidate or party answers? + +<ResearchQuote +title="Imputing missing answers" +author="Veikko Isotalo" +references={[ +'Isotalo, V. (2021). Improving candidate-based voting advice application design: The case of Finland. Informaatiotutkimus, 40(3), 85-109.', +'Mendez, F. (2017). Modeling proximity and directional decisional logic: What can we learn from applying statistical learning techniques to VAA-generated data? Journal of Elections, Public Opinion and Parties, 27(1), 31–55. https://doi.org/10.1080/17457289.2016.1269113', +]}> + +Let’s imagine a scenario where a user answers to all 30 VAA statements and a candidate happens to only answer to one statement in the whole VAA. The user and the candidate are in complete agreement in that one statement. Should the candidate receive a matching score of 100%? Imagine that this candidate is recommended to the user over another candidate that received 87% matching score in all 30 statements. In our opinion, this is not how the VAA should function. There are alternative strategies to avoid this situation. + +Mendez (2017, 50–51) suggests using recommendation scores that range from -100 to +100. This operationalization of the matching score has the benefit that missing party/candidate responses can be incorporated to the matching, so that they make the matching scores gravitate towards the middle point of the scale (zero). Mendez (2017) suggests that one divides the sum of matching scores with the candidate by the number of statements to which the user has responded. For example, if the user has all 30 statements, but the candidate has responded to only 15 statements, and in those 15 statements they have responded exactly the same way (sum of matching scores: 15/15) the agreement would be 15/30 = +50 in the range of -100 to +100. + +Alternative solution is to perform imputation of missing values. It is a common practice that VAAs assign the value of total disagreement between the user and the candidate, if the candidate has not provided any answer to the statement (and the user answered the statement). In a five-point Likert scale this would mean the difference of answering “completely agree” and “completely disagree”. + +In the case of ideological matching (see Isotalo 2021), some methods (e.g., confirmatory factor analysis) allow calculation of factor scores with missing values. Missing values increase the confidence intervals of the point estimates. + +We recommend using the method recommended by Mendez (2017), if it is applicable with the algorithmic design of the VAA. In other cases, imputation can be performed with the value of total disagreement. However, this needs to be communicated with the candidates/parties that they know the associated cost of not answering. + +> Note. The Mendez method is currently not implemented in OpenVAA, but we’re working on adding it. + +</ResearchQuote> + +The default option is that only candidates or parties who have answered all statements are included in the VAA. + +It is also possible to allow them to leave some answers empty, in which case they may be penalised in the results calculation by treating empty answers as maximally distant from the voter’s answer. + +## How should party scores be computed from candidate answers? + +If candidates answer your VAA but you also want to show party scores, these can be computed from their candidates’ answers. Note that the best way would be to get the answers directly from the parties, because their candidates (especially those having answered the VAA) may not have followed the party line in their answers. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/the-application-be-hosted/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/the-application-be-hosted/+page.md new file mode 100644 index 000000000..06b57517f --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/the-application-be-hosted/+page.md @@ -0,0 +1,5 @@ +# How should the application be hosted? + +Hosting means publishing the application to the public and will mostly be the concern of the software developer working with you. + +The main decision affecting hosting is whether you will have need of the candidate application with which candidates can enter their answers to the statements and other questions themselves. If this is not needed, you may opt for a static website with no separate database. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/the-specifics-of-the-elections/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/the-specifics-of-the-elections/+page.md new file mode 100644 index 000000000..58a1af3d4 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/the-specifics-of-the-elections/+page.md @@ -0,0 +1,123 @@ +<script lang="ts"> + import ResearchQuote from '$lib/components/ResearchQuote.svelte'; +</script> + +# What are the specifics of the elections? + +You will need to gather some information about the structure of the elections your VAA deals with. + +You’ll probably also need to compile a list of official information sources about the elections that the developer can use. Read more about that in [Data Collection](/publishers-guide/data-collection/intro) + +## One or many elections? + +It is surprisingly often the case that multiple elections are conducted at the same time. + +In such situations, it is usually helpful to combine all elections in the same VAA, because some of the questions (i.e. statements) can often be used for multiple concurrent elections. + +### Can the voters choose only some of the elections? + +<ResearchQuote +title="Should the voters be able to choose which elections to show?" +author="Veikko Isotalo" +references={['Dehdari, S. H., Meriläinen, J., & Oskarsson, S. (2021). Selective abstention in simultaneous elections: Understanding the turnout gap. Electoral Studies, 71, 102302.']}> + +Organizing multiple elections (e.g., parliamentary and local elections) at the same time typically strives to lower the costs for organizing elections and also to increase voter turnout. The idea is to increase voter turnout in the less salient election to match turnout of the more popular one. However, turnout gap might not close completely as some voters still choose to abstain in one election but vote in the other one (see e.g., Dehdari et al. 2021). + +When multiple types of elections are combined VAA designers have two options: a) develop distinct VAAs for each election, b) provide only a single VAA in which voters receive recommendations for multiple elections. First option is simpler to implement, but it might create repetition for the users if they need to provide answers to similar statements in both VAAs. Our recommendation would be to implement a combined design, if the number of simultaneous elections is small (2–3). In this way the VAA design aligns with the goal why the elections were organized jointly in the first place, i.e. to encourage voters to cast votes in all election types. + +Voters could be allowed to choose in the beginning of the VAA for which election they are looking for advice. This would be beneficial in cases when the voter is only interested in one of the elections or they might have already decided how to vote in the other election. Allowing voters to customize the VAA to their own needs is also a desirable VAA design goal. From another perspective, allowing voters to choose only a single election for the VAA might work against the intended goal of increasing turnout. In this case limiting user autonomy can potentially increase turnout as users receive recommendations for multiple elections. This example illustrates how VAA design choices are not value neutral and it is left for the VAA designer to choose, which values they deem more important for the VAA. +</ResearchQuote> + +If your VAA has many elections, the voters can be allowed to select only one of them for answering. + +This choice can, however, be disabled, which may be preferred especially in situations where the other election is less well-known or interesting to the voters. + +## Which are the constituencies in the elections? + +Elections are usually split into constituencies (also called districts, ridings and wards), but in country-wide elections there is only one constituency. + +You will need to find a data source for a list of the constituencies (with their names in all the languages you want to use). + +If your VAA has multiple elections with different constituencies, you will also need to check if some of them are subparts of the others. This is the case, for example, in combined regional and municipal elections, where each municipality is contained in a region. + +This connection is useful, because then the user can only select their municipality and the region can be deduced from that. + +## Who can be voted for? + +<ResearchQuote +id="open-and-closed-lists" +title="Open and closed lists" +author="Veikko Isotalo" +references={[ +'André, A., Depauw, S., Shugart, M. S., & Chytilek, R. (2017). Party nomination strategies in flexible-list systems: Do preference votes matter? Party Politics, 23(5), 589-600.', +'Crisp, B. F., Olivella, S., Malecki, M., & Sher, M. (2013). Vote-earning strategies in flexible list systems: Seats at the price of unity. Electoral Studies, 32(4), 658-669.', +'Isotalo, V. (2020). Designing Voting Advice Applications: The Finnish Case. Master’s thesis. http://urn.fi/URN:NBN:fi:aalto-2020112918207', +'Gemenis, K., & van Ham, C. (2014). Comparing Methods for Estimating Parties’ Positions in Voting Advice Applications. In D. Garzia and S. Marschall (Eds.), Matching Voters with Parties and Candidates: Voting Advice Applications in a Comparative Perspective (pp. 33–48). Colchester, UK: ECPR Press', +'Elklit, J., Pade, A. B., & Miller, N. N. (2011). The parliamentary electoral system in denmark: Guide to the Danish Electoral System. Ministry of the Interior and Health and The Danish Parliament. Copenhagen.', +'Taylor, S. L., & Shugart, M. S. (2017). Electoral systems in context: Colombia. In Erik S. Herron, Robert J. Pekkanen, and Matthew S. Shugart (Eds.), The Oxford Handbook of Electoral Systems, Oxford Handbooks.' +]}> + +Open-list and closed-list proportional representation electoral systems are widely spread across the world. These systems pose different needs for VAAs. Closed lists mean that voters can only cast a party vote. Candidates within parties are ranked by their parties and this order cannot be influenced by voters. To name a few, closed lists are used in parliamentary elections in Israel, Spain and Portugal. VAAs in closed-list electoral systems should focus on parties’ stances on political issues, as parties are the ones that voters see on the ballot. VAAs that provide voting advice at the level of parties are called party-based VAAs. Parties can be either asked directly to input their answers to the VAA or one can use experts to evaluate party positions to minimize the risk of parties manipulating their stances (Gemenis and van Ham 2014). It is also possible to use the Kieskompas method, where parties’ self-placements and expert placements are iteratively updated until agreement between their positions are found (see Gemenis and van Ham 2014). Expert evaluation is a suitable method when the number of parties and candidates is small (e.g., n < 20). Expert evaluation is not a viable method when one deals with dozens of candidates, as the workload becomes easily too substantial for the evaluators and it becomes more likely that one cannot find source material to base the evaluations for all candidates’ issue positions. + +Table 1 shows the categorization of VAAs based on a cross-tabulation of the placement method and the level of recommendation. VAA recommendations can be provided at the party level, candidate level or on both levels (hybrid). Candidates and parties can be placed on issues based on self-placement or expert evaluation. VAA designers should select the appropriate form for the VAA depending on the electoral system and number of candidates/parties on the ballot. + +In open-list proportional representation electoral systems voters are allowed to cast preference votes to candidates and parties have no control over who gets elected within party lists. VAAs should reflect this and allow voters to compare candidate positions to their own positions. VAAs that provide advice on candidate level are referenced as candidate-based VAAs. However, it is not uncommon that candidate-based VAAs also provide party recommendations based on aggregate candidate answers. It is noteworthy that this can be sometimes problematic. For instance, if a party is internally split on a certain issue, determining the party position from candidate answers might not yield accurate results. Countries that employ open lists in parliamentary elections include Brazil, Finland, Poland, Switzerland and Luxembourg. One should keep in mind, when the number of candidates is high, expert evaluation as a placement method is not usually viable. Candidate-based VAAs are also best suited for single transferable vote (STV) electoral systems, where voters can rank multiple candidates on the ballot in their own preference order. Variants of STV are in use in Australia, Ireland and Malta. + +Note that in some countries, such as in Denmark and Colombia, parties can determine whether lists are open or closed in any given district (Elklit et al. 2011; Taylor & Shugart 2017). In these cases where both list types are present in the same election, one should follow the same procedure as in the flexible-list case. + +Table 1. Categorization of VAAs (modified from Isotalo 2020). + +<table> + <thead> + <tr> + <th></th> + <th colspan="3">Level of recommendation</th> + </tr> + </thead> + <tbody> + <tr> + <th rowspan="2">Placement method</th> + <td>Candidate &<br>Self-placement<br>(open lists)</td> + <td>Hybrid &<br>Self-placement<br>(flexible lists)</td> + <td>Party<br>&<br>Self-placement<br>(closed lists)</td> + </tr> + <tr> + <td>Candidate &<br>Expert evaluation*<br>(open lists)</td> + <td>Hybrid &<br>Expert evaluation<br>(flexible lists)</td> + <td>Party<br>&<br>Expert evaluation<br>(closed lists)</td> + </tr> + </tbody> + <tfoot> + <tr> + <td colspan="4">* Expert evaluation is often not viable, if there is a high number of candidates.</td> + </tr> + </tfoot> +</table> + +#### Flexible lists + +Electoral systems that employ flexible lists use a mixture of open-list and closed-list rules. In flexible-list proportional representation systems, voters can cast a vote either for a party list as a whole or for a specific candidate. Candidates are per default ranked by their parties, but this order can be overturned if a candidate receives more personal votes than the determined vote quota (Crisp et al. 2013). Flexible-list PR system is the most common electoral system in Europe and it is used in countries such as Austria, Belgium, Croatia, the Czech Republic, Estonia, the Netherlands, Norway, Slovakia and Sweden (André et al. 2017). + +In terms of VAA design, flexible lists pose a unique challenge, as potentially both parties’ and candidates’ issue positions matter for the voter. Some flexible-list PR systems (e.g., Norway and Sweden) resemble more closed-list systems, as voters rarely cast preference votes or the thresholds are set so high that candidates have little chances of changing the list order. However, in countries where casting preference votes is more common (e.g., Belgium and Czech Republic) and so-called “ballot jumpers” are commonly elected, VAAs should contain information on individual candidates’ policy preferences. + +Our suggestion is to collect and position both party and candidate responses to VAA statements. Voters should also receive voting advice on both party and candidate levels (hybrid level of recommendation in Table 1). Our suggested option would be to display voting advice so that parties are ranked by their congruence with the user and within these parties candidates are ranked by their respective matching score with the user (see Figure 1). + +<figure> +<img src="/images/guides/nested-candidate-matches-within-parties.png" + alt="Screenshot of a VAA built with OpenVAA showing candidate matches nested within party matches." class="h-[40rem] max-h-[80vh]"/> +<figcaption>Figure 1: Parties ranked by match score. Candidates are nested within and also ranked.</figcaption> +</figure> + +</ResearchQuote> + +The final detail about the elections themselves is who can the voters cast their ballot for. In most cases it is either a candidate or a party or both. + +Thus, if the elections follow a closed-list model in which the voters can only vote a party list, not an individual candidate, there’s usually no point in showing the candidates in the VAA either and there will be no use for the VAA’s Candidate Application. + +### Do candidates or parties have election symbols? + +In many elections, candidates or parties have numbers or symbols that the voters can use instead of the candidate’s name. Check if this is the case in your elections. + +## Are there electoral alliances? + +It’s also good to check beforehand whether parties can form electoral alliances with each other. These can be shown in the VAA or left out if the information is not easy to gather. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/the-statements-or-questions-posed/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/the-statements-or-questions-posed/+page.md new file mode 100644 index 000000000..6d35b1dde --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/the-statements-or-questions-posed/+page.md @@ -0,0 +1,266 @@ +<script lang="ts"> + import ResearchQuote from '$lib/components/ResearchQuote.svelte'; +</script> + +# What are the statements or questions posed? + +<ResearchQuote +title="Statements" +author="Veikko Isotalo" +references={[ +'Walgrave, S., Nuytemans, M., & Pepermans, K. (2009). Voting aid applications and the effect of statement selection. West European Politics, 32(6), 1161-1180.', +'Fossen, T., & Van den Brink, B. (2015). Electoral dioramas: on the problem of representation in voting advice applications. Representation, 51(3), 341-358.', +'Haukio, J., & Suojanen, M. (2004). Vaalikone poliittisena mediana. Politiikka, 46(2), 128–136.', +'Gemenis, K. (2013). Estimating parties’ policy positions through voting advice applications: Some methodological considerations. Acta politica, 48(3), 268-295.' +]}> + +Statements are the most integral part of the VAAs. If selected statements are not appropriate for the election then the overall usefulness of the VAA will be negligible. Walgrave et al. (2009) have shown that statement selection impacts the recommendations that the VAAs provide. This means that VAA designers have a potential influence on the election outcome, as they can frame what the election is about (Fossen & van den Brink 2015). This means that VAA designers need to consider the quality of the statements and the balance of the selected statements. + +First, we need to understand what properties do statements have. + +VAA statements that are related to politics typically have links to ideological dimensions, for instance the Left–Right or the Liberal–Conservative. There are also some exceptions, as some local or foreign policy statements might not map out on any dimension. + +If a VAA statement links to a dimension then it is also directional. For instance, a statement that says “Workers’ right to strike should be limited” is directional to the right in the Left-Right dimension, as agreement with the statement associated with right-wing politics. + +Statements vary by strength / political extremity. For instance a statement “The state should seize the means of production” is directional to the left and it can be labelled as extreme. This means that only those who belong to the extreme end of the ideological dimension will agree with the statement. The statement can differentiate center-left and far left proponents from each other. + +Statements can also vary by how abstract/concrete they are. VAA developers often make a distinction between value statements and issue statements. This distinction can be a bit misleading as both types of statements can still relate to underlying ideological dimensions. They only vary by the level of abstraction, as value statements measure general attitudes and issue statements focus on specific policies. Issue statements require more political knowledge from the respondent. An example of a value statement is “The interest of the environment should take precedence over economic growth and job creation if they conflict”, whereas an example of an issue statement would be “Fur farming should be banned in Finland”. Both statements relate to the Materialist–Post-materialist dimension of politics. Even though value statements are not about specific policies they still send a signal about the respondent’s preferences. Issue statements are more demanding as one might need to know what is the current state of affairs and what the implications of proposed policies would be. + +Now that we know about properties of VAA statements, we can focus on what makes a good VAA statement. Haukio and Suojanen (2004) have written that good VAA statements + +- are relevant for the upcoming election +- are easily comprehensible with comprehensible answer options +- present differences between and within parties +- relate to the tasks of the electoral representative body in the upcoming term +- raise strong emotions in voters or concern important economic issues + +This list is by no means exhaustive, but it serves as a good starting point for what one should expect from a VAA statement. To summarize, good VAA statements reveal the preferences of the respondents in terms of relevant political issues and they make differences between parties/candidates apparent. + +Gemenis (2013) has also suggested that VAA statements should avoid common issues identified in survey research, namely + +1. vague wording, +2. double-barreled structure +3. quantification, and +4. qualification. + +It is imperative that all respondents would interpret statements similarly. Double-barrelledness means that multiple statements have been combined together. This proves a challenge for respondents, as they might agree with parts of the statement but disagree with the other part. Then it becomes problematic to choose a single answer for the statement. Statements that include quantifications refer to situations when something should be increased or decreased. These types of statements are difficult to measure using Likert-scales, so it is often recommended that these should be avoided. Qualifications in statements mean that an addition to the core statement affects the interpretation of the whole statement (see Gemenis 2013, 273). + +Next, we will discuss the statement structure as a whole, as VAA designers are limited by the number of statements that they can include in their VAA. + +</ResearchQuote> + +Often the most time-consuming part of a VAA project is statement or question formulation. + +Hurrying in this part is also very dangerous, because the recommendations the VAA produces are dramatically affected by the choice of statements. + +## Overall process of statement formulation + +Statement formulation is usually an iterative process with some or all of these steps: + +1. Identification of topics and political dimensions +2. Draft statement formulation +3. Circulation of draft statements to target groups +4. Preparing statements _and_ supporting information to be sent out to candidates or parties +5. Confirming statement loading on political dimensions after answers are received, if applicable + +## What are the main topics in the elections? + +<ResearchQuote +id="statement-structure" +title="Statement structure and ideological dimensions" +author="Veikko Isotalo" +references={[ +'Lefevere, J., & Walgrave, S. (2014). A perfect match? The impact of statement selection on Voting Advice Applications’ ability to match voters and parties. Electoral Studies, 36, 252-262.', +'Isotalo, V. (2021). Improving candidate-based voting advice application design: The case of Finland. Informaatiotutkimus, 40(3), 85-109.', +'Paloheimo, H. (2008). Ideologiat ja ristiriitaulottuvuudet. In H. Paloheimo and T. Raunio (Eds.), Suomen puolueet ja puoluejärjestelmä (pp. 27–60). Helsinki: WSOY', +]}> + +Selected VAA statements form what we call a statement structure. Typically, VAAs consist of 20 to 30 statements in total. A good practice is to always include a few extra statements, as not all statements might not work as intended. VAA designers should ensure that the structure is in balance in terms of which issues and ideological dimensions are represented. Unbalanced structure would mean that certain political issues or dimensions would be overrepresented, whereas other dimensions would be underrepresented or completely absent. Statement structures that lean more heavily on certain ideological dimensions might benefit parties that distinguish themselves on these dimensions (see Lefevere and Walgrave 2014). However, it should be noted that there is no official guideline on what the balance should be. Our suggestion is to rely on previously identified ideological dimensions from the country where the elections are held (Isotalo 2021, 101). + +For optimal statement balance, one should benchmark which ideological dimensions have been found to be relevant in this national context and how those are relevant for the election at hand (e.g., EU dimension might not be relevant for local elections). If there is no research on ideological dimensions among the electorate or in the party system one could pay attention to the votes and debates that have taken place in the electoral body (e.g., the national parliament, local council) and use these as points of reference. One should also form ideological dimensions based on the response data from parties or candidates using dimension reduction techniques such as factor analysis. Comparing the data-driven and theoretical dimensions to each other is recommended as this can hint, whether the planned dimensional structure works as intended and whether individual statements belong to the dimensions as was expected by theory. + +For example, in the Finnish context Paloheimo (2008) has suggested that there are seven dimensions that mark the Finnish party politics: + +- Left–Right +- Liberal–Conservative +- Center–Periphery +- National–International +- Monolingual–Bilingual +- Ecological–Materialist +- Elite–People + +VAA designers can then reference back to these dimensions when they select their statements and keep count how many statements relate to each dimension. This is one way to ensure that the statement structure is in balance. However, one needs to also pay attention to the internal balance of the dimensions. For instance, having only statements in the Left–Right dimension that are directional to the right would not be desirable. Optimally there would be an equal number of statements that are directional to the left and to the right and these statements would vary in terms of strength so that the whole length of the ideological dimension can be accurately measured. + +</ResearchQuote> + +Statement formulation can be started from two directions, but it’s best to combine them both. + +The first is to identify the main political questions that will be relevant for the next session of the body whose members are being elected. + +At this point you may also want to consult the VAA’s target group as well as parties and candidates for topics that they deem important. + +The other avenue is to identify the main political dimensions in the political system, as described in [Statement structure](#statement-structure), above. + +## How will the statements be categorized? + +Before progressing into the actual statement formulation, it may be useful to form a preliminary categorization scheme for them. + +If your VAA has many elections, you can have separate categories for each election or allow them to mix. You can also have statements that deal with all elections. + +## How many statements will there be? + +The number of statements is usually best constrained a bit, because having too many of them will make application too arduous for voters to use – and may also turn off some candidates or parties from answering. On the other hand, having too few, may compromise the quality of the recommendations. + +In most VAAs the number of questions is 20–30 (see also [Statement structure](#statement-structure), above). You can, however, create a few extra statements when gathering party or candidate answers, because it may turn out later that some of them don’t produce answers that are distinctive enough. + +## Do you want constituency-specific statements? + +Statements can be specific to certain constituencies if you wish. + +## Statement selection + +<ResearchQuote +id="statement-selection" +title="Statement selection process" +author="Veikko Isotalo" +references={[ +'van der Linden, C., & Dufresne, Y. (2017). The curse of dimensionality in Voting Advice Applications: reliability and validity in algorithm design. Journal of Elections, Public Opinion and Parties, 27(1), 9–30. https://doi.org/10.1080/17457289.2016.1268144', +'Isotalo, V. (2021). Improving candidate-based voting advice application design: The case of Finland. Informaatiotutkimus, 40(3), 85-109.' +]}> + +VAA statements can be developed in multiple stages that might include input from various stakeholders. In the ideation phase, VAA designers can collect ideas for statements from voters, political actors and experts. These statements should then be internally refined to ensure that they meet the standards for acceptable VAA statements. We do also recommend that VAA developers would disclose where the VAA statements came from and which actors took part in the process. + +#### Expert consultation + +After the ideation phase, VAA statements should then go through internal testing. After the first round of testing, the statements can be sent for experts (e.g., political scientists), for further feedback and refinement. Expert feedback is typically based on theoretical knowledge and experience with surveys or previous VAAs. It is important to ask the experts to justify their recommendations, as this will be important for enhancing learning for the VAA designers and in the case if expert opinions conflict with each other. + +#### Voter consultation + +One should also use the possibility to gather ideas for statements and feedback on existing formulations from the wider public, if it is possible. For instance, newspapers can ask their readership to answer test statements and comment on them. This kind of pre-VAA answer data is useful although not likely to be representative of the whole VAA user base, as people who tend to leave a comment are more politically interested than most users. The benefit of these pre-VAA users is that they might be a proxy for how political candidates might answer the same statements. More expensive option to test the statements is to run a survey on a demographically representative sample. The main idea for this data is to validate statements and test that the statements work according to one’s initial expectations. + +For instance, statements that do not show any differences between users’ that identify with different parties might hint that there are issues with the statement formulation. Moreover, if pre-VAA users’ answers are centered to the neutral option it might indicate that the statement is not easily comprehensible. Importantly, this data can also be used to validate ideological dimensions, if one wants to show parties on ideological scales. This can be done by applying factor analysis on the pre-VAA users’ answer data or on early access VAA users (van der Linden & Dufresne 2017). This step is particularly important for party-based VAAs, as the number of parties is too small to overcome the curse of dimensionality (i.e., fewer responses than there are VAA statements), which prevents scale validation with typical methods. + +#### Candidate or party consultation + +Parties and candidates might be involved in the VAA statement development in earlier stages of the statement formulation. Here we want to highlight that one can also use candidate or party answers to the VAA (depending which type of VAA is in question) for testing the statement quality, similarly as in the case of users. In practice, this would mean that VAA developers would reserve time after collecting party/candidate answers to analyze the data and select the final set of statements after consulting the party/candidate data. In candidate-based VAAs, when the number of candidates is sufficiently high (n > 300) the candidate data itself can be used to validate ideological dimensions (see Isotalo 2021). This means that candidate-based VAAs do not necessarily need external user data to validate the VAA statements and ideological scales. + +To summarize, VAA statements should be updated iteratively after first being tested internally, then with experts, pre-users (or early users), and finally statements should be tested with candidate or party answers. Besides applying theoretical knowledge for selecting VAA statements we recommend the usage of data-driven methods to test the VAA statements. It is especially important to keep an eye on three metrics that might be indicative of an unsuccessful statement: + +1. high proportion of “I don’t know” answers or neutral answers, +2. lack of differences between parties/candidates, and +3. no statistical connection to any ideological dimensions. + +</ResearchQuote> + +## Statement formulation + +Once the framing above is ready, it’s time to start formulating the actual statements. + +For each statement, you will need the following. + +### Answer type + +<ResearchQuote +title="Question types and answering scales" +author="Veikko Isotalo" +references={[ +'Gemenis, K. (2013). Estimating parties’ policy positions through voting advice applications: Some methodological considerations. Acta politica, 48(3), 268-295.', +'Koljonen, J., Isotalo, V., Ahonen, P., & Mattila, M. (2022). Comparing computational and non-computational methods in party position estimation: Finland, 2003–2019. Party Politics, 28(2), 306-317.', +]}> + +Multiple types of questions have been used in VAAs. Most commonly VAAs use statements, which measure user agreement with the statement. Multiple choice questions have also been used, but these are much less common. The main challenge with multiple choice questions is in the scoring of the agreement between the user and the party. In the Finnish case, multiple choice questions were used in the early 2000s VAAs, after which their use was discontinued. Ranking questions have also been used in many modern VAAs. Their benefit is in the ability to survey users’ priorities. Direct self-placements on ideological dimensions have also made an appearance in VAAs, but these are typically used for visualization purposes. VAAs could also use numeric questions, where one has a predefined budget that one should allocate to various activities. Budgeting questions have not been widely used. + +Each question type can be paired with suitable answering scales. VAA statements are typically paired up with Likert scales. Gemenis (2013, 270) notes that “VAAs have adopted the summated rating scale approach to measurement”. In practice, this means VAAs have adopted Likert scales, in which, Gemenis (2013, 270) writes, “each individual item by itself is an imperfect indicator of the latent concept (in this case, party policy or ideology). Likert scales aim to fully capture the latent concept by combining responses from multiple items.” Likert scales are typically used to measure agreement with a statement. Most common variants are five-point and seven-point scales, where the answering options range from “strongly agree” to “strongly disagree” with the addition of a neutral answering option in the middle. + +Notably some VAA designers have implemented a special variant of a five-point Likert scale where they have removed the neutral answering option. This means that answers are coded “strongly disagree” = 1, “somewhat disagree” = 2, “somewhat agree” = 4, and “strongly agree” = 5. This decision has been justified by wanting political actors to take a stand on the issue and limiting the strategic positioning of political actors to the neutral stance. Another supporting argument has been that the neutral answering option is commonly misused by users as they often think of it as a synonym for “I don’t know”. This is not helped by some VAA developers as they often misuse Likert scale by renaming the central answering option as “I don’t know”. It is important to note that a neutral answer truly means a neutral stance on the issue, not an uninformed opinion. + +There are also other ways to measure positions of political actors on political issues. Gemenis (2013) notes that expert and mass surveys typically use interval scales, where parties and respondents are directly positioned on latent dimensions of interest (e.g., Left–Right, Pro-immigration vs. Anti-immigration). Party positioning on latent dimensions can also be achieved with computational methods that use party manifestos, however, these methods are not necessarily very accurate or reliable (see Koljonen et al. 2022). + +One of the main differences between the interval scales and Likert scales is that in Likert scales the answer options might not be equidistant (although this is often assumed). As is the case with ordinal scales, the order of the answering options can be established in Likert scales. In interval scales, distances between answering options stay constant (e.g., distance from 0 to 1 is the same as 1 to 2). Interval scales are typically longer than Likert scales. Common variants are 0–10 and 0–100 scales. Likert scales typically use radio buttons as UI elements, but interval scales can employ sliders. However, the main difference in using interval scales is that they require different types of questions, where the one assigns a value directly to their position on the latent dimension. + +</ResearchQuote> + +There are currently two main answer types available for matching: ordinal responses (e.g. Likert) and categorical responses. + +The most common type in VAAs is a 4–7-point Likert answer, ranging from ‘Fully disagree’ to ‘Fully agree’. + +You can decide the number of options and their labels but it’s usually best to adhere to existing standards. + +Another option is a categorical response in which you can offer any options to the user you deem suitable, e.g. + +> What is your favourite color? +> +> - Red +> - Green +> - Blue + +The difference with an ordinal response is that these cannot be ordered. In the example above, this means that ‘Red’ is no closer to ‘Green’ than it is to ‘Blue’. In Likert responses, on the other hand, ‘Partly disagree’ is closer to ‘Fully disagree’ than to ‘Fully agree’. + +The upshot is that categorical questions do not yield as much information for the matching calculation as ordinal ones do. + +### Statement text in all languages + +You will naturally need to provide the statement text in all languages your VAA uses. + +If you use response options that are not part of the existing translations, you will also need to create translations for each of them. + +### Supporting information in all languages + +<ResearchQuote +title="Background information for statements" +author="Veikko Isotalo" +references={[ +'Kamoen, N., & Holleman, B. (2017). I don’t get it: Response difficulties in answering political attitude questions in Voting Advice Applications. Survey Research Methods, 11(2), 125-140. https://doi.org/10.18148/srm/2017.v11i2.6728', +'Kamoen, N., & Liebrecht, C. (2022). I need a CAVAA: How conversational agent voting advice applications (CAVAAs) affect users’ political knowledge and tool experience. Frontiers in Artificial Intelligence, 5, 835505.', +]}> + +#### Introduction + +VAA statement design is not limited to formulating the statement itself, but one needs to also consider if there is a need to draft an introduction for the statement. In the introduction, it is important to highlight why the issue matters and what are the alternative viewpoints regarding the issue. Statement introductions are especially relevant for video-based VAAs, where each statement is presented along with their video introduction. + +#### Important concepts and current state of affairs + +VAA statements might be difficult to understand, which is why it is important for VAA designers to provide clarifying information regarding the VAA statements. This is especially important for VAA users that are new to politics or they have low levels of political sophistication. +VAA users that encounter difficulties in understanding certain statements engage often in satisficing behavior (Kamoen & Holleman 2017). Satisficing means that users do not seek additional information before answering the statement to clarify that they understand the statement, but instead they make assumptions about the meaning of the statement and provide an answer nonetheless (Kamoen & Holleman 2017). VAA user comprehension problems might also manifest as a disproportionately large share of neutral or “I don’t know” answers. In the worst case comprehension problems can lead to unreliable voting advice. + +According to Kamoen and Holleman (2017), comprehension problems while filling in the VAA can stem from two sources: 1) semantic problems (i.e., not understanding certain words in the statement), or 2) practical problems (i.e., not knowing the current state of affairs). Both of these problems can hampeter user’s ability to answer and form an opinion on the issue. + +To tackle the previously mentioned issues, it is suggested that VAA designers provide background information for statements that deal with concrete issue statements. This means that there should be an UI element (e.g., highlighted text or a separate button) that reveals explanations for key concepts in the statement. There should also be an UI element that explains the current state of affairs (e.g., if the statement deals with inheritance tax, the explanation should briefly explain how the tax is implemented currently). There are alternative versions of how the additional information on the statements could be provided. Instead of relying on users to actively seek this information it could be revealed for users per default. However, we do not recommend all additional information to be shown per default, as this can overload the user unnecessarily. + +Alternatively, VAAs can employ a chatbot which could contain pre-coded responses to help users overcome comprehension problems with the VAA statements. Research on Conversational Agent VAAs (CAVAAs) have shown that structured chatbots that have predefined action buttons are more highly valued by users than more open-ended chatbots, where users can formulate their questions freely (Kamoen & Liebrecht 2022). + +#### Pros & cons + +VAAs might also show what kind of arguments are presented to support or to oppose the statement. This is likely to influence users on how one should think about the issue. However, this can also be educational for users to learn new ways to think about the issue. The main challenge in formulating these pro and con lists has been that the process is labour intensive for the VAA developers (especially if there are many parties and candidates). Some VAAs have implemented solutions where they employ large language models to summarize arguments for and against the issue based on party answers. + +</ResearchQuote> + +It is often very useful to provide the user additional information about the statement. First, they may understand the statement differently from what you intended and, secondly, they may not know the full specifics affecting the issue. + +Supporting information can be in many different formats: + +- Plain text +- Text with infographics +- Video + +The platform also has WIP functionality for generating drafts of supporting information, such as term definitions and pros & cons, using LLMs. + +Whichever option you choose, make sure to also provide a text version of the information for accessibility. + +It is also important to have at least a text version of this information ready when the candidates or parties answer the statements to maximise the chance that they understand the statements the same way as the voters. + +## Order of statements + +<ResearchQuote +title="Order of statements" +author="Veikko Isotalo"> + +The impact of statement order has not been previously researched in the context of VAAs. However, from survey research it is known that respondents are prone to answering fatigue, meaning that respondents lose focus and interest to complete the survey, as the number of items in the survey increases. This is why the typical length of the VAA has settled around 30 statements (although some outliers with around 70 statements exist). + +It is relatively common to see VAAs place concrete issue statements first and more abstract value statements in the end of the VAA. The idea here is to start with more challenging statements first and then ease the difficulty in answering in the latter part of the VAA to encourage users to stick to the end. The downside of this approach is that users that become demoralized by the difficulty of the issue statements might quit the VAA early. Alternative order could be to start with easy statements that do not require a lot of knowledge on politics and then in the very end place the most difficult statements, when the users are close to finishing. + +VAAs might also thematically sort their statements into small segments, where statements related to certain policy themes (e.g., the economy, environment, immigration) are asked one after another. While thematic order of statements has its benefits such as structural coherence, it is not the most time-efficient way to survey the user. To maximize time-efficiency VAA statements could be dynamically ordered by their information value. However, this feature is not widely used, and is currently being developed for OpenVAA. + +As a general rule, we recommend placing the most engaging VAA statements to the beginning of the VAA. This way users can be persuaded to start the VAA. It is not well-established how large a share of VAA users drop out while answering the statements, but we expect this share not to be substantial. However, most likely the users that are in the risk of quitting the VAA prematurely are the ones that could benefit the most from the VAA advice, i.e., users with low political interest and no party identification. + +</ResearchQuote> diff --git a/docs/src/routes/(content)/publishers-guide/preparing/the-vaa-look-and-feel/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/the-vaa-look-and-feel/+page.md new file mode 100644 index 000000000..468ff9e66 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/the-vaa-look-and-feel/+page.md @@ -0,0 +1,15 @@ +# What should the VAA look and feel like? + +You can customize the look and the text content of the VAA to your liking. + +On the visual side this includes: + +- main logo +- front page banner image +- emojis or other illustrations for any page, statement or category +- colours, such as those of text, background, links and buttons +- the font used in the application + +In addition any text shown in the application can be changed. + +Furthermore, the application’s source code can be modified to make any edits not listed above. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/the-voter-see-when-using/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/the-voter-see-when-using/+page.md new file mode 100644 index 000000000..f88622b92 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/the-voter-see-when-using/+page.md @@ -0,0 +1,227 @@ +<script lang="ts"> + import ResearchQuote from '$lib/components/ResearchQuote.svelte'; +</script> + +# What should the voter see when using the VAA? + +There are a number of choices you can make that affect the way the VAA works for the voters. + +Most of these can be changed even when the VAA is already running in the [App Settings](/publishers-guide/app-settings), but it’s of course better to decide on them beforehand. + +## Overview of the voters’ VAA process + +When voters use the VAA, the process they go through includes the steps below, some of which are optional. + +1. Introductory + 1. Front page + 2. Optional introduction about the next steps in using the VAA + 3. Optional selection of elections if the VAA has multiple elections and you have allowed voters to choose from them + 4. Constituency selection if applicable +2. Answering statements + 1. Optional introduction to statements and statement categories with possible category selection + 2. Optional introduction to each statement category, possibly with option to skip the category + 3. View each statement with options to: + - view supporting information + - return to previous statement + - skip the statement + - answer the statement + - clear answer if already answered + - optional: jump directly to results if available + - optional: set statement weight + - optional: see real-time results for the top candidates or parties when answering +3. Results + 1. Optional introductory content at the start of the results page + 2. Selection of the election for which to show results if the VAA has multiple elections + 3. Selection between candidates and parties if both are available (for the selected election) + 4. Apply filters to the results if available +4. Details of a single candidate or party + 1. The user may click on any candidate or party to show their details (with regard to the selected election if there are many) which may include: + - answers to statements compared to the voter’s answers + - other details including name, possible election symbol and answers to non-statement questions + - in case of parties, a list of their candidates + +In addition to them they may use the application menu at any time to access other content or move back to previous steps: + +- Return to the front page (step 1 above) +- Return to answering the statements (step 2) +- Go to results or browse candidates and parties if the voter hasn’t answered enough statements (step 3) +- View information about the elections +- View information on how the VAA works +- View privacy information +- Send feedback +- Clear the answers they have given +- Change application language (if multiple are available) + +## Which intro screens should there be? + +Voters can be shown information about the elections or the VAA in various stages of the process, especially category intros which are shown to users when they answer the VAA statements. They are used to describe the theme of the upcoming VAA statements. This feature is by no means essential for the VAA user experience, but setting up these categories might help the user to navigate through the answering process. + +## Should the voter be able to select or skip categories? + +<ResearchQuote +title="Category selection" +author="Veikko Isotalo"> + +If category labels are assigned to VAA statements, then it is possible to allow the user to select which categories of statements they wish to answer. Category selection feature is usually shown to the user before they start answering the VAA statements. + +This is a potentially useful feature, if there is a high number of statements. On one hand, allowing users to choose which categories they wish to answer increases the utility of the VAA from the user perspective, as users are not shown statements that they do not care about. On the other hand, if the user removes multiple categories of statements, the VAA might not be able to function as intended. Presenting the categories in the beginning of the VAA might also create confusion among the users, as they might not be sure whether all statements are per default included to the VAA or if they have to select them to be included. + +Our recommendation is to disable the category selection feature, unless there are certain categories of statements that are seen as supplementary. In a typical case, where all VAA statements are perceived to be important for the user experience, there is no need for this feature, as users can always skip unwanted statements when they are shown. + +<figure> +<img src="/images/guides/category-selection.png" + alt="Screenshot of a VAA built with OpenVAA showing category selection." class="h-[40rem] max-h-[80vh]"/> +<figcaption>Figure 1: Category selection with three out of five categories selected.</figcaption> +</figure> + +</ResearchQuote> + +If the statements are divided into categories, the voter may be given the options to: + +- select which categories to answer to in the optional category introduction view +- skip a single category when shown its optional introduction + +## Should the voter be able to skip individual statements? + +<ResearchQuote +title="Skipping statements" +author="Veikko Isotalo"> + +Users should be able to skip a statement if they desire to do so. One should keep in mind that users that are unsure and wish to skip a Likert scale item, might often mistakenly choose the center of the Likert scale, which is a neutral answer option. Neutral answer is always part of the VAA’s matching calculation, whereas skipping the statement means that it has no influence on the VAA’s recommendation. Therefore, it is important to place the skip-button to a visible spot in the UI and to encourage its use. + +<figure> +<img src="/images/guides/skip-statement.png" + alt="Screenshot of a VAA built with OpenVAA showing the button for skipping statements." class="h-[40rem] max-h-[80vh]"/> +<figcaption>Figure 2: The skip button is by default placed below the answering options.</figcaption> +</figure> + +</ResearchQuote> + +Individual statements can be skipped by default. + +## Should the voter be able to set statement weights? + +<ResearchQuote +title="Statement weights" +author="Veikko Isotalo" +references={[ +'Gemenis, K. (2013). Estimating parties’ policy positions through voting advice applications: Some methodological considerations. Acta politica, 48(3), 268-295.' +]}> + +User weights, also known as salience weights, allow users to express how important of a role they want to give to any statement in the VAA’s recommendation. This is significant because Likert scales cannot measure salience (Gemenis 2013, 270). This means that it is possible to have an extreme position on a political issue but the voter might still deem the issue to have low importance (salience). + +A common variant of user weights is a three-level version: 0.5, 1.0, and 2.0. Here, a weight of 0.5 means that the user deems the statement less important and the statement’s impact on the VAA’s advice is halved. The weight of two means double the importance. Default weight of 1.0 means normal importance. Typically, users can perform weighting of a statement while they answer the statement. However, it might be easier for the user to perform weighting after they have seen all the statements, as then they can truly evaluate which ones they deem important. We recommend an implementation, where after answering the statements, users could see an overview of all their answers and adjust any statement’s weights. + +Before enabling user weights, it is important to verify that the VAA’s matching method is compatible with them. + +</ResearchQuote> + +Voters may be given the option to mark some statements more or less important than others so that a statement affects the results, for example, twice or half as much as the others. + +Currently, however, this will mean that answering the statements is a little bit slower for the user, so enabling this option must be considered with that in mind. + +## Should the voter be able to jump into results? + +<ResearchQuote +title="Shortcut to results" +author="Veikko Isotalo"> + +A shortcut to results can be displayed to the users while they are filling the VAA statements. This feature increases user’s ability to modify the VAA experience to their own needs. However, allowing users to proceed to results before answering most of the statements might make the VAA not work as intended and it can have a negative effect on the statement structure balance. These factors should be kept in mind when including this feature. + +<figure> +<img src="/images/guides/results-link.png" + alt="Screenshot of a VAA built with OpenVAA showing the shortcut to results." class="h-[40rem] max-h-[80vh]"/> +<figcaption>Figure 1: The shortcut to results is in the top-right corner of the screen.</figcaption> +</figure> + +</ResearchQuote> + +Voters may be given the option to jump directly to the results when they have answered the minimum number of statements for results to be computed. If enabled, they will see a discrete jump to results button in the application header. + +## Should the voter see real-time results when they answer statements? + +<ResearchQuote +title="Quick results" +author="Veikko Isotalo" +references={[ +'Isotalo, V. (2021). Improving candidate-based voting advice application design: The case of Finland. Informaatiotutkimus, 40(3), 85-109.' +]}> + +Quick results, also known as “live match tracking” of the results, is a feature that shows the real time matching scores of most congruent parties/candidates to the user, while they are still answering the VAA statements (see Isotalo 2021). Usually this feature is implemented by showing maximally five party logos or candidate photos along with their matching scores in the upper corner of the UI. Users are prompted to notice how the top matches and their matching percentages change based on their statement responses. + +To this date, there is no research on the potentially biasing effects of this feature. However, there is some anecdotal evidence that this feature can change user’s response patterns, as users might want to mimic their most preferred candidate’s responses. Our recommendation is to use the quick results feature as an optional feature that is per default hidden to the users and they can enable/disable it, if they wish to do so. + +<figure> +<img src="/images/guides/quick-results.png" + alt="Screenshot of a VAA built with OpenVAA showing quick results." class="h-[40rem] max-h-[80vh]"/> +<figcaption>Figure 3: Top 7 live matches at the top of the screen in an experimental VAA.</figcaption> +</figure> + +</ResearchQuote> + +A list of the top 5 or so candidates or parties can be shown to the user when they are answering the statements. This may heighten user engagement but may also have an adverse effect on how the VAA is used. + +This is an experimental feature and only available if the VAA has a single election. + +## Should the voter see candidates, parties or both in the results and in which order? + +<ResearchQuote +title="Section order" +author="Veikko Isotalo"> + +In displaying the results, the order of VAA recommendations becomes relevant, when the VAA provides advice on multiple levels (e.g., candidates and parties). This means that the VAA in question is a hybrid VAA. This type of VAA is suitable for electoral systems that use flexible lists. In open-list systems, party recommendations can also be provided based on aggregate candidate responses (e.g., the party median). When both candidates and parties are recommended by the VAA, which ones should be displayed first? + +This is not a trivial question, as some VAA users might be limited by the time they are willing to spend investigating the VAA. These users might look at the VAA’s results only once before leaving the site. Therefore, it is important to consider which advice is shown to them first. + +For open-list systems, we recommend showing the candidate advice first, especially if the party positions are candidate response aggregates, which might not be reflective of official party positions. As mentioned earlier in [Open and closed lists](/publishers-guide/preparing/the-specifics-of-the-elections#open-and-closed-lists), we recommend VAAs in flexible-list systems to display voting advice on parties within which candidates are nested and ordered by their respective matching score with the user. + +</ResearchQuote> + +In the results (or browse) view, the voter can be given the choice of viewing either candidates or parties, if applicable. + +Even in elections in which the voters are casting their ballot directly for a candidate, it is often meaningful to list the parties as well. + +You can decide which views to show and which one of them is shown first. In VAAs with multiple elections, the same setting applies to all elections. + +## What info about the candidates and parties should be shown in the results list? + +<ResearchQuote +title="Candidate card contents" +author="Veikko Isotalo"> + +Candidate card contents can include all sorts of collected information regarding the candidate (e.g., demographic information) or their congruence on various political issues with the user. One option is to display the same information that will be displayed by party lists in the election booth. In the Finnish case, this would mean displaying candidate number, party name, candidate occupation or education level. Commonly, candidate cards include a photo of the candidate. + +#### Featured answers + +Featured answers is an option to provide a fast glance at a couple of candidate answers. These answers can consist of predefined statements or they can be selected automatically, so that they display statements where the candidate and the user disagree/agree the most. From the user perspective, it is probably the most beneficial to see what are their main sources of disagreement with each candidate without opening the candidate profiles. + +</ResearchQuote> + +For candidates, their name, possible portrait, possible election symbol, nominating party and matching score are shown by default in the results list. + +For voters to be able to better distinguish candidates from each other (before opening their details), on the list can also be shown: + +- the matching scores for each statement category (see [How should the recommendations be computed?](/publishers-guide/preparing/matching)) +- their answers to any question or statement, such as their election manifesto. + +<ResearchQuote +title="Party card contents" +author="Veikko Isotalo"> + +Party cards can display the list of candidates in the order of their list ranking in flexible-list and closed-list electoral systems. Alternatively, party cards can display candidates based on issue congruence, if VAA candidate data exists. It is left for the VAA designer to choose how many top candidates are shown in the party card. + +</ResearchQuote> + +For parties, the list may also include the top 3 candidates from that party along with their matching scores. + +## What details of the candidates and parties should be shown when they’re viewed individually? + +When the voter clicks on a candidate or party in the results view, the target’s details are shown in a popup. + +These details may include any of the following divided into tabs: + +- answers to statements compared to the voter’s answers +- other details including name, possible election symbol and answers to non-statement questions +- in case of parties, a list of their candidates + +If parties haven’t answered the statements directly and you’re relying on answers approximated from their candidates’ answers, showing these as the party’s answers may be misleading. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/timeline/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/timeline/+page.md new file mode 100644 index 000000000..9935db76e --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/timeline/+page.md @@ -0,0 +1,16 @@ +# Timeline + +The time required for publishing a VAA may vary a lot but in our experience the following is a somewhat safe schedule. + +| Step | Duration | +| ------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| Statement formulation and creating supporting content | 2–3 months | +| Collecting other data and brand assets | 2 weeks | +| Testing the application before publishing the candidate application | 2 weeks | +| Candidate application open for | 1.5 months | +| Moderation of candidates’ answers (this period can also serve as an extension for answering for candidates who missed the initial deadline) | 1 week | +| Application open for voters for | 1 month | + +Thus, counting back from the election day, it’s best to start the VAA project 6–7 months before that. + +If you will not be using the candidate application, the time reserved for that will be used to otherwise collect answers to the statements from the candidates or parties. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/to-ask-voters-to-give/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/to-ask-voters-to-give/+page.md new file mode 100644 index 000000000..77f98cdd9 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/to-ask-voters-to-give/+page.md @@ -0,0 +1,5 @@ +# Do you want to ask voters to give feedback about the VAA? + +The voters can always use the ‘Give feedback’ option in the application menu to give feedback but you can also actively ask for feedback from them. + +If enabled, voters will be asked to give a star rating and optional other comments after having reached the results view (with a delay that can be configured, e.g. 60 seconds). diff --git a/docs/src/routes/(content)/publishers-guide/preparing/to-offer-a-survey-for/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/to-offer-a-survey-for/+page.md new file mode 100644 index 000000000..df1bc901f --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/to-offer-a-survey-for/+page.md @@ -0,0 +1,7 @@ +# Do you want to offer a survey for voters to fill? + +In addition to simple feedback, you can also ask voters to fill in a more comprehensive survey. + +If enabled, voters will be shown a request to fill in the survey after having reached the results view as well as in other parts of the app. + +You will need to provide an external link for the survey and voters will be pointed to that if they choose to take the survey. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/what-data-should-be-collected/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/what-data-should-be-collected/+page.md new file mode 100644 index 000000000..1f3e8a058 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/what-data-should-be-collected/+page.md @@ -0,0 +1,7 @@ +# What data should be collected from voters’ use of the VAA? + +No data are collected from voters by default, but if you wish to collect data about how they use the app, you can enable this. + +If data collection is enabled, voters will be shown a banner asking for permission for data collection when they start the VAA. Only if they grant this, will their data be collected. + +For data collection, you will also need to use an external service to store the data. Currently, the platform has a built-in adapter for doing this with [Umami](https://umami.is/). diff --git a/docs/src/routes/(content)/publishers-guide/preparing/what-other-information-is-collected/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/what-other-information-is-collected/+page.md new file mode 100644 index 000000000..d336845b2 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/what-other-information-is-collected/+page.md @@ -0,0 +1,43 @@ +<script lang="ts"> + import ResearchQuote from '$lib/components/ResearchQuote.svelte'; +</script> + +# What other information is collected from candidates and parties? + +<ResearchQuote +title="Use case: recommendation filters " +author="Veikko Isotalo" +references={[ +'Isotalo, V. (2021). Improving candidate-based voting advice application design: The case of Finland. Informaatiotutkimus, 40(3), 85-109.', +'Shugart, M. S., Valdini, M. E., & Suominen, K. (2005). Looking for locals: Voter information demands and personal vote‐earning attributes of legislators under proportional representation. American Journal of political science, 49(2), 437-449.', +'Giger, N., Holli, A. M., Lefkofridi, Z., & Wass, H. (2014). The gender gap in same-gender voting: The role of context. Electoral Studies, 35, 303-314.', +'Arceneaux, K., & Vander Wielen, R. J. (2023). Do voters prefer educated candidates? How candidate education influences vote choice in congressional elections. Electoral Studies, 82, 102596.' +]}> + +It has been suggested by Isotalo (2021) that VAAs should make use of filters to improve the VAA user experience. To make the VAA recommendation relevant for the user, one should be able to incorporate other types of information to the VAA recommendation that reflects issue congruence. Voters rarely rely on a single source of information, when they decide which party/candidate to vote for. + +When voting for candidates voters deem candidates’ demographic attributes also important. Candidate gender and age are especially important, as it is common for voters to limit their search for viable candidates based on these factors. Same-gender voting is a well-established pattern among voters (Giger et al. 2014). Moreover, voters also prefer voting for local candidates that come from the same area as the voter (Shugart et al. 2005). It has also been established that voters select candidates with higher education levels (Arceneaux & Vander Wielen 2023). Our suggestion is to provide the VAA users options to filter the list of recommended candidates/parties in the results page based on relevant candidate/party information that can be useful for their voting decision. + +<figure> +<img src="/images/guides/results-filters.png" + alt="Screenshot of a VAA built with OpenVAA showing filters used in candidate results." class="h-[40rem] max-h-[80vh]"/> +<figcaption>Figure 1: Filtering candidates by age and education.</figcaption> +</figure> + +</ResearchQuote> + +In addition to their replies to the statements used in matching, other information is usually collected as well. + +These pieces of information are useful for voters to get a fuller picture of the candidate or party, not captured by the formulaic statements. Popular options are: + +- Date of birth +- Photo +- Election manifesto +- Gender +- Occupation +- Education +- Links to social media sites + +Additionally, voters can use these as filtering criteria when they view their results to see, e.g., candidates of a certain age. + +The answer to any of these questions can also be shown along the matching scores in the results list. diff --git a/docs/src/routes/(content)/publishers-guide/preparing/who-is-the-target-group/+page.md b/docs/src/routes/(content)/publishers-guide/preparing/who-is-the-target-group/+page.md new file mode 100644 index 000000000..e012e112e --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/preparing/who-is-the-target-group/+page.md @@ -0,0 +1,5 @@ +# Who is the target group of the VAA? + +Start by deciding whom the VAA is for. This may well be the whole electorate, but in some cases your target group may be more definite. + +Target group selection will affect both what issues the VAA should cover, what kind of supporting material to provide the users and in some cases the features to choose. diff --git a/docs/src/routes/(content)/publishers-guide/publish-with-openvaa/+page.md b/docs/src/routes/(content)/publishers-guide/publish-with-openvaa/+page.md new file mode 100644 index 000000000..749f79188 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/publish-with-openvaa/+page.md @@ -0,0 +1,7 @@ +# How to publish a VAA with OpenVAA? + +The whole OpenVAA platform is open source and free to use, so one option is to make a copy, called ‘fork’, of the code and base your VAA on it. For the time being, this will require quite a bit of experience in software development, but you should get a running start from the [Developers’ Guide](/developers-guide/quick-start). + +If technical development is not your forte or you don’t know a developer to hire for the work, you can also [contact us](/about/association#contact) and we may be able to help you with your project. + +We also partner with the [VoteMatch International](https://www.votematchinternational.org/) network, which offers support for introducing VAAs to new democratic contexts. diff --git a/docs/src/routes/(content)/publishers-guide/what-are-vaas/intro/+page.md b/docs/src/routes/(content)/publishers-guide/what-are-vaas/intro/+page.md new file mode 100644 index 000000000..997adb921 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/what-are-vaas/intro/+page.md @@ -0,0 +1,53 @@ +<script lang="ts"> + import ReferenceList from '$lib/components/ReferenceList.svelte'; + import Author from '$lib/components/Author.svelte'; +</script> + +# What are VAAs and why are they useful? + +## What are they + +Voting advice applications, also known as VAAs, are online applications that help voters to choose a political entity (a party or a candidate) to vote for (Marschall and Garzia 2014, 1). VAAs are typically employed in the context of elections, however, VAAs can also be used in a non-electoral setting. The history of VAAs dates back to the 1980s, when the central operating principle of the VAA was introduced in non-digital form in the Netherlands (Marschall and Garzia 2014, 2). World’s first VAA was introduced in Finland in 1996 by the Finnish National Broadcasting Company for the European parliament election (Vihtonen 2007). During the last three decades, VAAs have become an integral part of election campaigns in many democracies around the world. These tools attract millions of users at the time of elections, especially in countries such as Finland, Denmark, the Netherlands, Germany and Switzerland (see, e.g., Statistics Finland 2023; Hansen and Stubager 2019; van de Pol et al. 2019; BPD 2025). + +The basic operating principle of a VAA is to compare users’ preferences on political issues to the positions of those entities that are on the ballot. Issue positions are collected by completing a web-survey that allows comparison of user’s answers to answer profiles of political actors. After algorithmic matching the application produces an output which ranks the political entities (e.g., parties and/or candidates) by the level of agreement with the voter. In the results, VAAs might also display graphs that show the user’s position in policy dimensions in respect to parties. (Marschall and Garzia 2014, 1) + +VAAs typically encourage users to vote for their most proximate “match” in terms of issue positions. Nearly all VAAs operate with this principle that Fossen and Anderson (2014) call “the matching VAA”. The matching model perceives democracy through the lens of social choice (i.e., preference aggregation), where citizens are assumed to be politically knowledgeable policy shoppers. In this model, the central challenge that VAAs seek to alleviate is the citizens’ ignorance of political actors’ policy positions (Fossen and Anderson 2014). Other models for VAAs have been theorized (e.g., a deliberative VAA), but they have not been widely employed. + +VAAs operate based on relatively strong assumptions that its users have clear preferences on the surveyed issues. In reality, not all VAA users have high political knowledge or belief in their capability to understand politics. Moreover, voters can encounter comprehension problems while filling a VAA (Kamoen and Holleman 2017). Despite these shortcomings, VAAs have been an international success story, as their usage has been associated with positive effects for democracy. + +## Why are they useful + +The main benefit of VAAs is that they lower the cost of voting (Gemenis and Rosema 2014). VAAs serve as information shortcuts for voters, as they provide easily accessible information to voters on which voters can base their vote choice. VAAs can also be exceptionally helpful in various information settings. In the context of low-salience elections, VAAs can inform the voters what the election is about. This educational role of VAAs should not be overlooked. On the other hand, VAAs are also suitable for information intensive environments, where voters are facing an abundance of political information (e.g., many candidates and multiple salient election themes). In information-intensive settings, VAAs can enable voters to shorten the list of viable electoral options. As long as VAAs reduce information acquisition costs and they provide advice that improve the accuracy of one’s vote (in terms of voting according to one’s own preferences), using VAAs can be seen as ecologically rational (Tromborg & Albertsen 2023). + +> “VAAs highlight the role of issues in politics. VAAs also help keep politicians accountable.” + +From a normative standpoint, issue voting is only among one of many ways one can vote. However, basing one’s vote on politicians' actual issue stances is still preferable to a situation where voters are guessing what the politicians stand for. Making politicians’ stances on issues public can also improve political accountability, as voters and journalists can compare politicians’ actions to their VAA answers. + +## What effects do they have + +Research on voting advice applications has found that its use has been linked to higher voter turnout, changes in vote choice and a potential increase in political knowledge (Munzert and Ramirez Ruiz 2021). However, it should be noted that many of the earlier VAA studies have been impacted by methodological issues regarding self-selected survey samples (Pianzola 2014). In recent experimental studies, VAA recommendations have been found to affect their users’ party choices, so that many voters change their party choice if it is not congruent with their preferences (Germann et al. 2023; Tromborg & Albertsen 2023). + +## Who uses them + +VAA users have been predominantly young people and those who have high education level and high political knowledge (Marschall 2014; Albertsen 2022). However, VAA usage has increased in many countries in the last two decades, so that the group of users has become more heterogeneous. This also poses challenges for VAA designers as different VAA user groups might have different needs for the application. In terms of VAA usage intentions, VAA users have been categorized into three groups: checkers, seekers and doubters (van de Pol et al. 2014). Checkers use VAAs merely to confirm their vote choice, or they use VAAs for entertainment purposes. These users are politically knowledgeable and they are not typically impacted by the VAA’s recommendation. Seekers and doubters use VAAs to decide who to vote for, but they vary by the degree of vote certainty. (van de Pol et al. 2014) + +<Author>Veikko Isotalo</Author> + +<ReferenceList references={[ +'Albertsen, A. (2022). How do the characteristics of voting advice application users change over time? Evidence from the German election studies. German Politics, 31(3), 399-419.', +'Bundeszentrale für politische Bildung [BPD]. (2025). Bundestagswahl 2025 | bpb, bpb.de. Retrieved from https://www.bpb.de/themen/wahl-o-mat/bundestagswahl-2025/', +'Fossen, T., & Anderson, J. (2014). What’s the point of voting advice applications? Competing perspectives on democracy and citizenship. Electoral Studies, 36, 244–251. https://doi.org/10.1016/j.electstud.2014.04.001', +'Gemenis, K., & Rosema, M. (2014). Voting Advice Applications and electoral turnout. Electoral Studies, 36, 281–289. https://doi.org/10.1016/j.electstud.2014.06.010', +'Germann, M., Mendez, F., & Gemenis, K. (2023). Do voting advice applications affect party preferences? Evidence from field experiments in five European countries. Political Communication, 40(5), 596-614.', +'Hansen, K. M., & Stubager, R. (2019). ‘Den Danske Valgundersøgelse 2019’, Retrieved from https://www.sa.dk/da/forskning-rigsarkivet/benyt-surveydata/valgundersoegelsen/', +'Kamoen, N., & Holleman, B. (2017). I don’t get it: Response difficulties in answering political attitude questions in Voting Advice Applications. Survey Research Methods, 11(2), 125-140. https://doi.org/10.18148/srm/2017.v11i2.6728', +'Marschall, S. (2014). Profiling Users. In D. Garzia and S. Marschall (Eds.), Matching Voters with Parties and Candidates: Voting Advice Applications in Comparative Perspective (pp. 93–104). Colchester, UK: ECPR Press.', +'Marschall, S., & Garzia, D. (2014). Voting advice applications in a comparative perspective: an introduction. In D. Garzia and S. Marschall (Eds.), Matching voters with parties and candidates: Voting advice applications in comparative perspective (pp. 1–10). Colchester, UK: ECPR Press', +'Munzert, S., & Ramirez-Ruiz, S. (2021). Meta-analysis of the effects of voting advice applications. Political Communication, 38(6), 691-706.', +'Pianzola, J. (2014). Selection biases in voting advice application research. Electoral Studies, 36, 272-280.', +'Tromborg, M. W., & Albertsen, A. (2023). Candidates, voters, and voting advice applications. European Political Science Review, 15(4), 582-599.', +'Väestön tieto- ja viestintätekniikan käyttö [SVT]. (2023). ISSN=2341-8699. Helsinki: Tilastokeskus. Retrieved December 29, 2025 from https://stat.fi/julkaisu/clpwcpboal1760bvvs7ufutru', +'van de Pol, J., Holleman, B.C., Kamoen, N., Krouwel, A.P.M., & De Vreese, C.H. (2014). Beyond young, highly educated males: A typology of VAA users. Journal of Information Technology & Politics, 11, 397-411. https://doi.org/10.1080/19331681.2014.958794', +'van de Pol, J., Kamoen, N., Krouwel, A., De Vreese, C., & Holleman, B. (2019). Same but different: A typology of Voting Advice Application users in first-and second-order elections. Acta Politica, 54(2), 225-244.', +'Vihtonen, E. (2007). Vaalikoneen synty ja kehitys yleisradiossa 1996–2006. In M. Suojanen & J. Talponen (Eds.), Vallaton vaalikone (pp. 29–38). SoPhi, 103.' +]}/> diff --git a/docs/src/routes/(content)/publishers-guide/what-are-vaas/vaas-used/+page.md b/docs/src/routes/(content)/publishers-guide/what-are-vaas/vaas-used/+page.md new file mode 100644 index 000000000..7c21300a1 --- /dev/null +++ b/docs/src/routes/(content)/publishers-guide/what-are-vaas/vaas-used/+page.md @@ -0,0 +1,7 @@ +# Which kind of elections are VAAs used in? + +VAAs are predominantly used in governmental elections, but they may also be used in organizations’ internal elections. + +Because they help voters choose their preferred candidates based on their policy opinions, it can be argued that VAAs are even more useful in such organisations where the electorate is not familiar with the candidates beforehand. + +Examples of NGOs using VAAs in their internal elections are the Finnish Lutheran church and OP, a Finnish banking cooperative. diff --git a/docs/src/routes/+layout.svelte b/docs/src/routes/+layout.svelte new file mode 100644 index 000000000..783df8a53 --- /dev/null +++ b/docs/src/routes/+layout.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import './layout.css'; + import Header from '$lib/components/Header.svelte'; + import Footer from '$lib/components/Footer.svelte'; + + let { children } = $props(); +</script> + +<svelte:head><link rel="icon" href="/favicon.png" /></svelte:head> + +<Header /> + +<div class="mt-headerHeight"> + {@render children()} +</div> + +<Footer /> diff --git a/docs/src/routes/+page.svelte b/docs/src/routes/+page.svelte new file mode 100644 index 000000000..5fcd53971 --- /dev/null +++ b/docs/src/routes/+page.svelte @@ -0,0 +1,218 @@ +<script lang="ts"> + import { SCREENSHOTS, SCREENSHOT_BASE_PATH } from '$lib/screenshots'; + import type { Screenshot } from '$lib/screenshots'; + import { OPENVAA_REPO_URL } from '$lib/consts'; + + let content: HTMLDivElement; + let firstItem = $state(0); + let numItems = $state(getNumItems()); + let autoScroll = $state(true); + let selectedScreenshot = $state<string | null>(null); + let touchStartX = $state(0); + let touchEndX = $state(0); + + /** + * Screenshots padded on both ends to allow infinite scrolling effect. + */ + let screenshots = $derived.by(() => { + const partialPageLength = SCREENSHOTS.length % numItems; + const prefix = SCREENSHOTS.slice(-numItems); + const suffix = SCREENSHOTS.slice(0, partialPageLength + numItems); + return [...prefix, ...SCREENSHOTS, ...suffix]; + }); + + /** + * Total number of scrollable items without the padding (except that to fill the last page) + */ + let numScrollable = $derived(screenshots.length - 2 * numItems); + + // Update numItems based on screen width + $effect(() => { + const updateNumItems = () => (numItems = getNumItems()); + updateNumItems(); + window.addEventListener('resize', updateNumItems); + return () => window.removeEventListener('resize', updateNumItems); + }); + + // Auto scroll screenshots + $effect(() => { + const interval = setInterval(() => { + if (autoScroll) scrollItems('next'); + }, 4000); + return () => clearInterval(interval); + }); + + function getNumItems(): number { + if (typeof window === 'undefined') return 4; + const width = window.innerWidth; + if (width < 500) return 1; + if (width < 768) return 2; + if (width < 1024) return 3; + if (width < 1280) return 4; + if (width < 1480) return 5; + return 6; + } + + function scrollManually(direction: 'prev' | 'next') { + autoScroll = false; + scrollItems(direction); + } + + function handleTouchStart(e: TouchEvent) { + touchStartX = e.touches[0].clientX; + } + + function handleTouchEnd(e: TouchEvent) { + touchEndX = e.changedTouches[0].clientX; + const swipeDistance = touchStartX - touchEndX; + const minSwipeDistance = 50; + if (Math.abs(swipeDistance) > minSwipeDistance) { + scrollManually(swipeDistance > 0 ? 'next' : 'prev'); + } + } + + function scrollItems(direction: 'prev' | 'next') { + firstItem = (firstItem + (direction === 'prev' ? -numItems : numItems)) % numScrollable; + if (firstItem < 0) { + firstItem += numScrollable; + } + } + + function toggleSelectedScreenshot(screenshot: Screenshot) { + autoScroll = false; + if (selectedScreenshot === screenshot.filename) { + selectedScreenshot = null; + } else { + selectedScreenshot = screenshot.filename; + } + } +</script> + +<section + class="relative flex w-full flex-col items-center overflow-hidden bg-base-300" + style:--firstItem={firstItem} + style:--numItems={numItems} + style:--itemWidth="calc((100vw - (var(--numItems) - 1) * var(--itemGap) - 2 * var(--sideGap)) / var(--numItems))" + style:--sideGap="4rem" + style:--itemGap="2rem" + ontouchstart={handleTouchStart} + ontouchend={handleTouchEnd}> + <button class="absolute top-0 bottom-0 left-0 z-10 h-full w-(--sideGap)" onclick={() => scrollManually('prev')} + ><span class="sr-only">Show previous</span></button> + <div + class="flex w-max translate-x-[calc(-1*(var(--firstItem)+var(--numItems))*var(--itemWidth)-(var(--firstItem)+var(--numItems))*var(--itemGap))] gap-(--itemGap) place-self-start px-(--sideGap) + pb-[calc(2*var(--spacing-xl))] transition-transform md:pt-xl"> + {#each screenshots as screenshot} + <button + class="group relative aspect-[0.4884488449] w-(--itemWidth) overflow-hidden rounded-md shadow-2xl transition-all + odd:translate-y-xl hover:scale-[1.025]" + onclick={() => toggleSelectedScreenshot(screenshot)}> + <img + src="{SCREENSHOT_BASE_PATH}{screenshot.filename}" + alt="{screenshot.caption} • VAA: {screenshot.vaa}" + class="h-full w-full object-cover" /> + <div + class="absolute right-0 bottom-0 left-0 translate-y-full bg-base-200 p-md text-center + text-sm text-base-content group-hover:translate-y-0 {selectedScreenshot === screenshot.filename + ? 'translate-y-0!' + : ''} transition-transform"> + {screenshot.caption} <span class="text-secondary">• VAA: {screenshot.vaa}</span> + </div> + </button> + {/each} + </div> + <button class="absolute top-0 right-0 bottom-0 z-10 h-full w-(--sideGap)" onclick={() => scrollManually('next')} + ><span class="sr-only">Show next</span></button> + + <button + class="btn mx-auto mb-lg rounded-lg bg-primary px-xl py-md text-center text-lg text-primary-content" + onclick={() => content.scrollIntoView({ behavior: 'smooth' })}>Version <code>0.1 Shiba</code> out now!</button> + <img + src="/images/shiba-inu-sitting.png" + alt="A Shiba inu dog, the mascot of the OpenVAA 0.1 release" + class="pointer-events-none absolute right-xl bottom-0 h-[20rem] max-h-[min(30dvh,50dvw)] translate-x-[calc(50%+var(--spacing-xl))] select-none lg:translate-x-0" /> +</section> + +<div class="mx-auto flex max-w-[50rem] flex-col gap-lg px-lg py-xl text-lg leading-[1.6] md:px-xl" bind:this={content}> + <p> + OpenVAA is a comprehensive open-source framework for building configurable <a + class="link" + href="/publishers-guide/what-are-vaas/intro">Voting Advice Applications</a> (VAAs). It’s designed to work in any election + context and is fully localisable, accessible, modular and free to use. + </p> + <p> + OpenVAA was built for two reasons: to offer a transparent alternative for proprietary VAAs, and to kickstart + development of more and better VAAs. OpenVAA is built by developers and researchers and maintained by a <a + class="link" + href="/about/association">Finnish non-profit</a> of the same name. + </p> + + <div class="flex flex-col-reverse items-end gap-md rounded-lg bg-base-300 p-lg md:flex-row"> + <img + src="/images/shiba-inu-facing-front.png" + alt="A Shiba inu dog, the mascot of the OpenVAA 0.1 release" + class="-mb-lg max-w-[5rem] md:max-w-[7rem]" /> + <div> + <h4 class="mb-md font-bold">Version <code>0.1 Shiba</code> is out now!</h4> + <ul class="arrow-list text-primary"> + <li><a href="/developers-guide/quick-start" class="link">Quick start for developers</a></li> + <li> + <a href="/publishers-guide/intro" class="link">Guide for publishers of VAAs</a> + </li> + <li><a href={OPENVAA_REPO_URL} class="link">View source on GitHub</a></li> + </ul> + </div> + </div> + + <h2 class="mt-lg text-xl font-bold">Main Features</h2> + + <div class="grid gap-md sm:grid-cols-3"> + <span>🔎 Transparent</span> + <span>💸 Free to use</span> + <span>🌍 Fully localisable</span> + <span>🗳 Use in any elections</span> + <span>🤲 Accessible</span> + <span>🎓 Research-friendly</span> + <span>🧩 Modular and fully editable</span> + <span>🧮 Configurable and extensible matching algorithm</span> + <span>🧑‍💼 Includes an app for candidates</span> + <ul class="arrow-list col-span-full place-self-end"> + <li><a href="/about/features" class="link text-primary">See expanded list of features</a></li> + </ul> + </div> + + <h2 class="mt-lg text-xl font-bold">See OpenVAA in Action</h2> + + <p> + The following VAAs are currently online. Note that you may experience slow loading and related issues because the + instances are hosted on low-cost tiers. + </p> + + <div class="grid gap-lg sm:grid-cols-3"> + {#snippet vaaExample({ filename, href, name }: { filename: string; href: string; name: string })} + <div class="text-md text-center"> + <a {href} class="group flex flex-col items-center gap-sm"> + <img + src="{SCREENSHOT_BASE_PATH}{filename}" + alt="Screenshot of {name}" + class="m-md aspect-auto max-h-[60dvh] w-auto rounded-md shadow-lg transition-transform group-hover:scale-[1.025]" /> + <span class="link text-base">{name}</span></a> + </div> + {/snippet} + {@render vaaExample({ + filename: 'nuorisoala-finnish-local-elections-2025-vaa-3.png', + href: 'https://nuortenvaalikone.openvaa.org/', + name: 'Election Compass for Young Voters, 2025 Finnish local elections' + })} + {@render vaaExample({ + filename: 'nuorisoala-finnish-local-elections-2025-vaa-educational-mode-1.png', + href: 'https://voting-advice-application-qu5v.onrender.com', + name: 'Educational ‘Game’ Mode VAA, 2025 Finnish local elections' + })} + {@render vaaExample({ + filename: 'eu-elections-2024-vaa-screenshot-1.png', + href: 'https://vaalikone.openvaa.org/', + name: 'OpenVAA Election Compass, 2024 European elections, Finland' + })} + </div> +</div> diff --git a/docs/src/routes/layout.css b/docs/src/routes/layout.css new file mode 100644 index 000000000..7b21e6bb7 --- /dev/null +++ b/docs/src/routes/layout.css @@ -0,0 +1,154 @@ +@import 'tailwindcss'; + +@plugin 'daisyui'; + +@plugin '@tailwindcss/typography'; + +@theme { + --spacing: calc(1rem / 16); + --spacing-px: 1px; + --spacing-xs: calc(4rem / 16); + --spacing-sm: calc(8rem / 16); + --spacing-md: calc(10rem / 16); + --spacing-lg: calc(20rem / 16); + --spacing-xl: calc(40rem / 16); + --spacing-xxl: calc(60rem / 16); + --spacing-safel: env(safe-area-inset-left, 0px); + --spacing-safer: env(safe-area-inset-right, 0px); + --spacing-safet: env(safe-area-inset-top, 0px); + --spacing-safeb: env(safe-area-inset-bottom, 0px); + --spacing-safemdl: calc(env(safe-area-inset-left, 0px) + 10rem / 16); + --spacing-safemdr: calc(env(safe-area-inset-right, 0px) + 10rem / 16); + --spacing-safemdt: calc(env(safe-area-inset-top, 0px) + 10rem / 16); + --spacing-safemdb: calc(env(safe-area-inset-bottom, 0px) + 10rem / 16); + --spacing-safelgl: calc(env(safe-area-inset-left, 0px) + 20rem / 16); + --spacing-safelgr: calc(env(safe-area-inset-right, 0px) + 20rem / 16); + --spacing-safelgt: calc(env(safe-area-inset-top, 0px) + 20rem / 16); + --spacing-safelgb: calc(env(safe-area-inset-bottom, 0px) + 20rem / 16); + --spacing-safenavt: calc(env(safe-area-inset-top, 0px) + 16rem / 16); + --spacing-headerHeight: 4.5rem; +} + +@plugin 'daisyui/theme' { + name: 'light'; + default: true; + prefersdark: false; + color-scheme: 'light'; + --color-base-100: oklch(100% 0 0); + --color-base-200: #e8f5f6; + --color-base-300: #d1ebee; + --color-base-content: #333333; + --color-primary: #2546a8; + --color-primary-content: #ffffff; + --color-secondary: #666666; + --color-secondary-content: oklch(100% 0 0); + --color-accent: #0a716b; + --color-accent-content: #ffffff; + --color-neutral: #333333; + --color-neutral-content: oklch(100% 0 0); + --color-info: #0a716b; + --color-info-content: #ffffff; + --color-success: #2546a8; + --color-success-content: oklch(100% 0 0); + --color-warning: #a82525; + --color-warning-content: oklch(100% 0 0); + --color-error: #a82525; + --color-error-content: oklch(100% 0 0); + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 0; + --noise: 0; +} + +@plugin 'daisyui/theme' { + name: 'dark'; + default: false; + prefersdark: true; + color-scheme: 'dark'; + --color-base-100: #000000; + --color-base-200: #101212; + --color-base-300: #1f2324; + --color-base-content: #cccccc; + --color-primary: #6887e3; + --color-primary-content: #000000; + --color-secondary: #8c8c8c; + --color-secondary-content: #000000; + --color-accent: #11a8a0; + --color-accent-content: #000000; + --color-neutral: #cccccc; + --color-neutral-content: oklch(100% 0 0); + --color-info: #11a8a0; + --color-info-content: #000000; + --color-success: #6887e3; + --color-success-content: #000000; + --color-warning: #e16060; + --color-warning-content: #000000; + --color-error: #e16060; + --color-error-content: #000000; + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 0; + --noise: 0; +} + +@utility prose { + blockquote { + font-style: normal; + /* Disable adding quote marks to block-quotes: https://github.com/tailwindlabs/tailwindcss-typography/issues/66 */ + p { + &:first-of-type::before, + &:last-of-type::after { + content: none; + } + } + } + + pre[class*='language-'], + :not(pre) > code[class*='language-'] { + @apply border-1 border-base-300 bg-base-200 text-neutral; + } +} + +@layer utilities { + .arrow-list { + @apply flex flex-col gap-xs; + } + .arrow-list > li { + @apply relative ps-lg before:absolute before:top-0 before:left-0 before:content-['→']; + } +} + +body { + font-family: + 'Inter', + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + 'Helvetica Neue', + Arial, + sans-serif; + line-height: 1.6; + color: var(--color-text); + background-color: var(--color-bg); +} + +figure > img { + @apply mx-auto rounded-md; +} + +figure > figcaption { + @apply text-center; +} + +pre { + @apply w-full overflow-x-auto; +} diff --git a/docs/static/favicon.png b/docs/static/favicon.png new file mode 100644 index 000000000..a2e175d7b Binary files /dev/null and b/docs/static/favicon.png differ diff --git a/docs/static/images/guides/category-selection.png b/docs/static/images/guides/category-selection.png new file mode 100644 index 000000000..3ced6ad02 Binary files /dev/null and b/docs/static/images/guides/category-selection.png differ diff --git a/docs/static/images/guides/distance-matrices.png b/docs/static/images/guides/distance-matrices.png new file mode 100644 index 000000000..a34a928a7 Binary files /dev/null and b/docs/static/images/guides/distance-matrices.png differ diff --git a/docs/static/images/guides/highlight-template.psd b/docs/static/images/guides/highlight-template.psd new file mode 100644 index 000000000..38f6e8ec0 Binary files /dev/null and b/docs/static/images/guides/highlight-template.psd differ diff --git a/docs/static/images/guides/nested-candidate-matches-within-parties.png b/docs/static/images/guides/nested-candidate-matches-within-parties.png new file mode 100644 index 000000000..aa06d1dcc Binary files /dev/null and b/docs/static/images/guides/nested-candidate-matches-within-parties.png differ diff --git a/docs/static/images/guides/quick-results.png b/docs/static/images/guides/quick-results.png new file mode 100644 index 000000000..482ef1158 Binary files /dev/null and b/docs/static/images/guides/quick-results.png differ diff --git a/docs/static/images/guides/results-filters.png b/docs/static/images/guides/results-filters.png new file mode 100644 index 000000000..50bcbcb2e Binary files /dev/null and b/docs/static/images/guides/results-filters.png differ diff --git a/docs/static/images/guides/results-link.png b/docs/static/images/guides/results-link.png new file mode 100644 index 000000000..f03f1e5d5 Binary files /dev/null and b/docs/static/images/guides/results-link.png differ diff --git a/docs/static/images/guides/skip-statement.png b/docs/static/images/guides/skip-statement.png new file mode 100644 index 000000000..173116b0b Binary files /dev/null and b/docs/static/images/guides/skip-statement.png differ diff --git a/docs/static/images/guides/subcategory-matches.png b/docs/static/images/guides/subcategory-matches.png new file mode 100644 index 000000000..342c74443 Binary files /dev/null and b/docs/static/images/guides/subcategory-matches.png differ diff --git a/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-1.png b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-1.png new file mode 100644 index 000000000..e1d7bb96e Binary files /dev/null and b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-1.png differ diff --git a/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-10.png b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-10.png new file mode 100644 index 000000000..628909701 Binary files /dev/null and b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-10.png differ diff --git a/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-11.png b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-11.png new file mode 100644 index 000000000..1dea7fe20 Binary files /dev/null and b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-11.png differ diff --git a/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-3.png b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-3.png new file mode 100644 index 000000000..3ced6ad02 Binary files /dev/null and b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-3.png differ diff --git a/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-4.png b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-4.png new file mode 100644 index 000000000..2ed9b4c3f Binary files /dev/null and b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-4.png differ diff --git a/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-6.png b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-6.png new file mode 100644 index 000000000..e0ddb6a6d Binary files /dev/null and b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-6.png differ diff --git a/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-7.png b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-7.png new file mode 100644 index 000000000..f5d9e152d Binary files /dev/null and b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-7.png differ diff --git a/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-8.png b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-8.png new file mode 100644 index 000000000..342c74443 Binary files /dev/null and b/docs/static/images/screenshots/eu-elections-2024-vaa-screenshot-8.png differ diff --git a/docs/static/images/screenshots/luxembourg-vaa-eu-elections-2024-1.png b/docs/static/images/screenshots/luxembourg-vaa-eu-elections-2024-1.png new file mode 100644 index 000000000..1b01c8d0d Binary files /dev/null and b/docs/static/images/screenshots/luxembourg-vaa-eu-elections-2024-1.png differ diff --git a/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-1.png b/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-1.png new file mode 100644 index 000000000..44001092c Binary files /dev/null and b/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-1.png differ diff --git a/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-2.png b/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-2.png new file mode 100644 index 000000000..34bf788c4 Binary files /dev/null and b/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-2.png differ diff --git a/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-3.png b/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-3.png new file mode 100644 index 000000000..3365fdc1b Binary files /dev/null and b/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-3.png differ diff --git a/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-educational-mode-1.png b/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-educational-mode-1.png new file mode 100644 index 000000000..8947a1b12 Binary files /dev/null and b/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-educational-mode-1.png differ diff --git a/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-educational-mode-2.png b/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-educational-mode-2.png new file mode 100644 index 000000000..722d7301a Binary files /dev/null and b/docs/static/images/screenshots/nuorisoala-finnish-local-elections-2025-vaa-educational-mode-2.png differ diff --git a/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-1.png b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-1.png new file mode 100644 index 000000000..29c169154 Binary files /dev/null and b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-1.png differ diff --git a/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-10.png b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-10.png new file mode 100644 index 000000000..326b21434 Binary files /dev/null and b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-10.png differ diff --git a/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-4.png b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-4.png new file mode 100644 index 000000000..c36ed2740 Binary files /dev/null and b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-4.png differ diff --git a/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-5.png b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-5.png new file mode 100644 index 000000000..6d998e38e Binary files /dev/null and b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-5.png differ diff --git a/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-6.png b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-6.png new file mode 100644 index 000000000..35c18cf06 Binary files /dev/null and b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-6.png differ diff --git a/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-7.png b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-7.png new file mode 100644 index 000000000..aa06d1dcc Binary files /dev/null and b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-7.png differ diff --git a/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-9.png b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-9.png new file mode 100644 index 000000000..904383e76 Binary files /dev/null and b/docs/static/images/screenshots/nuortenvaalikone.fi-videoversio-ilman-kannykkaa-9.png differ diff --git a/docs/static/images/shiba-inu-facing-front.png b/docs/static/images/shiba-inu-facing-front.png new file mode 100644 index 000000000..070dc2089 Binary files /dev/null and b/docs/static/images/shiba-inu-facing-front.png differ diff --git a/docs/static/images/shiba-inu-sitting.png b/docs/static/images/shiba-inu-sitting.png new file mode 100644 index 000000000..51ef3b92e Binary files /dev/null and b/docs/static/images/shiba-inu-sitting.png differ diff --git a/docs/svelte.config.js b/docs/svelte.config.js new file mode 100644 index 000000000..d8b415efb --- /dev/null +++ b/docs/svelte.config.js @@ -0,0 +1,35 @@ +import adapter from '@sveltejs/adapter-static'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +import { mdsvex } from 'mdsvex'; +import rehypeSlug from 'rehype-slug'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: [ + vitePreprocess(), + mdsvex({ + extensions: ['.md', '.svx'], + layout: { + _: './src/lib/layouts/MdLayout.svelte' + }, + rehypePlugins: [rehypeSlug], + smartypants: true + }) + ], + kit: { + alias: { + // Needed for mdsvex imports to work both on the server and client + './src/lib/*': './src/lib/*' + }, + adapter: adapter({ + pages: 'build', + assets: 'build', + fallback: '404.html', + precompress: false, + strict: true + }) + }, + extensions: ['.svelte', '.svx', '.md'] +}; + +export default config; diff --git a/docs/tailwind.config.mjs b/docs/tailwind.config.mjs new file mode 100644 index 000000000..fd1a020c5 --- /dev/null +++ b/docs/tailwind.config.mjs @@ -0,0 +1,283 @@ +import { staticSettings } from '@openvaa/app-shared'; +import daisyui from 'daisyui'; + +// Utility for getting a color from the settings with a backup value +function getColor(name, defaultValue, theme = 'light') { + return staticSettings.colors?.[theme]?.[name] ?? defaultValue; +} + +// Other DaisyUI variables: https://daisyui.com/docs/themes/ +// We define them here, so they can be used in both the light and the dark themes +const themeCSSVars = { + '--rounded-box': 'var(--rounded-md)', // border radius rounded-box utility class, used in card and other large boxes + '--rounded-btn': 'var(--rounded-lg)', // border radius rounded-btn utility class, used in buttons and similar element + '--rounded-badge': 'var(--rounded-lg)', // border radius rounded-badge utility class, used in badges and similar + '--animation-btn': 'var(--duration-sm)', // duration of animation when you click on button + '--animation-input': 'var(--duration-sm)', // duration of animation for inputs like checkbox, toggle, radio, etc + '--btn-focus-scale': '0.95', // scale transform of button when you focus on it + '--border-btn': '0px', // border width of buttons + '--tab-border': '0px', // border width of tabs + '--tab-radius': 'var(--rounded-sm)' // border radius of tabs +}; + +// We apply this fix to (min/max) height to cater for iOS Safari's address bar. +// This can be removed when Tailwind changes it's default behaviour to match this. +const fixedScreenHeight = ['100vh', '-webkit-fill-available', '100dvh']; + +// This defines the minimum touch target size +const touchTargetSize = `${44 / 16}rem`; + +// We'll use this below to generate color classes to safelist +// Make sure to check that this matches the `Color` type in +// `./src/lib/components/color/color.type.ts` +// as well as the color definitions in the DaisyUI themes futher below. +const colorNames = [ + 'current', + 'primary', + 'secondary', + 'accent', + 'neutral', + 'base-100', + 'base-200', + 'base-300', + 'info', + 'success', + 'warning', + 'error', + 'base-content', + 'primary-content', + 'secondary-content', + 'accent-content', + 'info-content', + 'success-content', + 'warning-content', + 'error-content', + 'white' +]; + +// The emoji font-family +const emojiFonts = ['"Apple Color Emoji"', '"Segoe UI Emoji"', '"Segoe UI Symbol"', '"Noto Color Emoji"']; + +// The fallback fonts for different font styles. Note that the emoji fonts will be appended to all of these. +const fontFallbacks = { + sans: [ + 'system-ui', + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + '"Noto Sans"', + 'sans-serif' + ], + serif: ['ui-serif', 'Georgia', 'Cambria', '"Times New Roman"', 'Times', 'serif'] +}; + +export default { + content: ['./src/**/*.{html,js,svelte,ts}'], + // We need to safelist these color utility classes so that we can freely use + // the DaisyUI color classes as variables like `fill-${color}`. See: + // https://tailwindcss.com/docs/content-configuration#dynamic-class-names and + // https://tailwindcss.com/docs/content-configuration#safelisting-classes + safelist: [ + ...colorNames.map((c) => `btn-${c}`), + ...colorNames.map((c) => `bg-${c}`), + ...colorNames.map((c) => `fill-${c}`), + ...colorNames.map((c) => `dark:fill-${c}`), + ...colorNames.map((c) => `text-${c}`) + ], + theme: { + borderRadius: { + none: '0px', + sm: `${2 / 16}rem`, // Tab + DEFAULT: `${2 / 16}rem`, + md: `${4 / 16}rem`, // Card + lg: `${8 / 16}rem`, // Button, input, speech bubble + full: '9999px' + }, + borderWidth: { + 0: '0px', + md: '1px', + DEFAULT: '1px', + lg: '2px', + xl: '4px' + }, + lineHeight: { + none: '1', + sm: '1.21', + md: '1.35', + lg: '1.65' + }, + fontFamily: { + base: [ + staticSettings.font?.name ?? 'Inter', + ...(fontFallbacks[staticSettings.font?.style ?? 'sans'] ?? fontFallbacks.sans), + ...emojiFonts + ], + emoji: emojiFonts + }, + fontSize: ({ theme }) => ({ + xs: [`${11.5 / 16}rem`, { lineHeight: theme('lineHeight.sm') }], // label in all caps + sm: [`${13 / 16}rem`, { lineHeight: theme('lineHeight.md') }], // non-important info + md: [`${15 / 16}rem`, { lineHeight: theme('lineHeight.md') }], // base, body text + base: [`${15 / 16}rem`, { lineHeight: theme('lineHeight.md') }], // base, body text + lg: [`${17 / 16}rem`, { lineHeight: theme('lineHeight.sm') }], // h3: card title + xl: [`${20 / 16}rem`, { lineHeight: theme('lineHeight.sm') }], // h2: section title + '2xl': [`${23 / 16}rem`, { lineHeight: theme('lineHeight.sm') }], // h1: page title + '3xl': [`${28 / 16}rem`, { lineHeight: theme('lineHeight.sm') }] // h1.appTitle + }), + fontWeight: { + normal: '400', + bold: '700' + }, + spacing: { + px: '1px', + 0: '0px', + // 1: `${1/16}rem`, + 2: `${2 / 16}rem`, + 4: `${4 / 16}rem`, + xs: `${4 / 16}rem`, // Gap bw icon and label + 6: `${6 / 16}rem`, + 8: `${8 / 16}rem`, + sm: `${8 / 16}rem`, // Gap bw cards + 10: `${10 / 16}rem`, + md: `${10 / 16}rem`, // Gap default + 12: `${12 / 16}rem`, + 14: `${14 / 16}rem`, + 16: `${16 / 16}rem`, + 18: `${18 / 16}rem`, + 20: `${20 / 16}rem`, + lg: `${20 / 16}rem`, // Page margin + 24: `${24 / 16}rem`, + 32: `${32 / 16}rem`, // Likert buttons + 40: `${40 / 16}rem`, + xl: `${40 / 16}rem`, // Main big gap + 44: `${44 / 16}rem`, + 48: `${48 / 16}rem`, + 60: `${60 / 16}rem`, + xxl: `${60 / 16}rem`, // Portrait height + 100: `${100 / 16}rem`, + // We might want to use the plugin https://github.com/mvllow/tailwindcss-safe-area + safel: 'env(safe-area-inset-left, 0px)', + safer: 'env(safe-area-inset-right, 0px)', + safet: 'env(safe-area-inset-top, 0px)', + safeb: 'env(safe-area-inset-bottom, 0px)', + safemdl: `calc(env(safe-area-inset-left, 0px) + ${10 / 16}rem)`, + safemdr: `calc(env(safe-area-inset-right, 0px) + ${10 / 16}rem)`, + safemdt: `calc(env(safe-area-inset-top, 0px) + ${10 / 16}rem)`, + safemdb: `calc(env(safe-area-inset-bottom, 0px) + ${10 / 16}rem)`, + safelgl: `calc(env(safe-area-inset-left, 0px) + ${20 / 16}rem)`, + safelgr: `calc(env(safe-area-inset-right, 0px) + ${20 / 16}rem)`, + safelgt: `calc(env(safe-area-inset-top, 0px) + ${20 / 16}rem)`, + safelgb: `calc(env(safe-area-inset-bottom, 0px) + ${20 / 16}rem)`, + safenavt: `calc(env(safe-area-inset-top, 0px) + ${16 / 16}rem)`, // For the top nav + touch: touchTargetSize + }, + transitionDuration: { + none: '0s', + sm: '225ms', + DEFAULT: '225ms', + md: '350ms', + lg: '500ms', + full: '675ms' + }, + extend: { + height: { + screen: fixedScreenHeight + }, + maxHeight: { + screen: fixedScreenHeight + }, + minHeight: { + screen: fixedScreenHeight, + touch: touchTargetSize + }, + minWidth: ({ theme }) => ({ + touch: touchTargetSize, + // Add the named maxWidth sizes for consistency + ...Object.fromEntries( + ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl'].map((size) => [ + size, + theme(`maxWidth.${size}`) + ]) + ) + }), + screens: { + xs: '320px', + // The 36rem should match the value for max-w-xl. We have to use + // a raw media query because an error will otherwise be generated + // when using units other than pixels. + 'match-w-xl': { raw: 'screen and (min-width: 36rem)' } + } + } + }, + // We may want to use this later for formatting info pages etc: require('@tailwindcss/typography') + plugins: [daisyui], + daisyui: { + themes: [ + { + light: { + // DaisyUI colors: https://daisyui.com/docs/colors/ + primary: getColor('primary', '#2546a8'), // = success + secondary: getColor('secondary', '#666666'), + accent: getColor('accent', '#0a716b'), // = info + neutral: getColor('neutral', '#333333'), + 'base-100': getColor('base-100', '#ffffff'), + 'base-200': getColor('base-200', '#e8f5f6'), // 50% tint of base-300 on base-100 + 'base-300': getColor('base-300', '#d1ebee'), + info: getColor('accent', '#0a716b'), // = accent (var() cannot be used in these) + success: getColor('primary', '#2546a8'), // = primary + warning: getColor('warning', '#a82525'), // = error + error: getColor('warning', '#a82525'), // = warning + 'base-content': getColor('neutral', '#333333'), // = neutral + 'primary-content': getColor('base-100', '#ffffff'), // = base-100 + 'secondary-content': getColor('base-100', '#ffffff'), // = base-100 + 'accent-content': getColor('base-100', '#ffffff'), // = base-100 + 'info-content': getColor('base-100', '#ffffff'), // = base-100 + 'success-content': getColor('base-100', '#ffffff'), // = base-100 + 'warning-content': getColor('base-100', '#ffffff'), // = base-100 + 'error-content': getColor('base-100', '#ffffff'), // = base-100 + + // Other DaisyUI variables + ...themeCSSVars, + + // Custom variables + '--line-color': getColor('line-color', '#d9d9d9'), + '--progress-color': 'oklch(var(--n))', + '--progress-label-color': 'oklch(var(--n))' + }, + dark: { + // DaisyUI colors: https://daisyui.com/docs/colors/ + primary: getColor('primary', '#6887e3', 'dark'), // = success + secondary: getColor('secondary', '#8c8c8c', 'dark'), + accent: getColor('accent', '#11a8a0', 'dark'), // = info + neutral: getColor('neutral', '#cccccc', 'dark'), + 'base-100': getColor('base-100', '#000000', 'dark'), + 'base-200': getColor('base-200', '#101212', 'dark'), // 50% tint of base-300 on base-100 + 'base-300': getColor('base-300', '#1f2324', 'dark'), + info: getColor('info', '#11a8a0', 'dark'), // = accent (var() cannot be used in these) + success: getColor('primary', '#6887e3', 'dark'), // = primary + warning: getColor('warning', '#e16060', 'dark'), // = error + error: getColor('warning', '#e16060', 'dark'), // = warning + 'base-content': getColor('neutral', '#cccccc', 'dark'), // = neutral + 'primary-content': getColor('base-100', '#000000', 'dark'), + 'secondary-content': getColor('base-100', '#000000', 'dark'), + 'accent-content': getColor('base-100', '#000000', 'dark'), + 'info-content': getColor('base-100', '#000000', 'dark'), + 'success-content': getColor('base-100', '#000000', 'dark'), + 'warning-content': getColor('base-100', '#000000', 'dark'), + 'error-content': getColor('base-100', '#000000', 'dark'), + + // Other DaisyUI variables + ...themeCSSVars, + + // Custom variables + '--line-color': getColor('line-color', '#262626', 'dark'), + '--progress-color': 'oklch(var(--n))', + '--progress-label-color': 'oklch(var(--n))' + } + } + ] + } +}; diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 000000000..6a9c3c91b --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": ["@openvaa/shared-config/ts", "./.svelte-kit/tsconfig.json"], + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + }, + "references": [{ "path": "../packages/app-shared/tsconfig.esm.json" }] +} diff --git a/docs/tsconfig.tsbuildinfo b/docs/tsconfig.tsbuildinfo new file mode 100644 index 000000000..384fdb5f8 --- /dev/null +++ b/docs/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","../node_modules/typescript/lib/typescript.d.ts","../node_modules/@typescript-eslint/types/dist/generated/ast-spec.d.ts","../node_modules/@typescript-eslint/types/dist/lib.d.ts","../node_modules/@typescript-eslint/types/dist/parser-options.d.ts","../node_modules/@typescript-eslint/types/dist/ts-estree.d.ts","../node_modules/@typescript-eslint/types/dist/index.d.ts","../node_modules/esrap/types/index.d.ts","./node_modules/svelte/node_modules/magic-string/dist/magic-string.es.d.mts","./node_modules/svelte/node_modules/@types/estree/index.d.ts","../node_modules/locate-character/types/index.d.ts","./node_modules/svelte/types/index.d.ts","./node_modules/vite/types/hmrpayload.d.ts","./node_modules/vite/types/customevent.d.ts","./node_modules/vite/types/hot.d.ts","./node_modules/vite/types/importglob.d.ts","./node_modules/vite/types/importmeta.d.ts","./node_modules/vite/client.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/events.d.ts","../node_modules/buffer/index.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/vite/dist/node/chunks/modulerunnertransport.d.ts","./node_modules/rollup/node_modules/@types/estree/index.d.ts","./node_modules/rollup/dist/rollup.d.ts","./node_modules/rollup/dist/parseast.d.ts","./node_modules/vite/dist/node/module-runner.d.ts","./node_modules/esbuild/lib/main.d.ts","../node_modules/@jridgewell/trace-mapping/dist/types/sourcemap-segment.d.ts","../node_modules/@jridgewell/trace-mapping/dist/types/types.d.ts","../node_modules/@jridgewell/trace-mapping/dist/types/any-map.d.ts","../node_modules/@jridgewell/trace-mapping/dist/types/trace-mapping.d.ts","../node_modules/@jridgewell/gen-mapping/dist/types/sourcemap-segment.d.ts","../node_modules/@jridgewell/gen-mapping/dist/types/types.d.ts","../node_modules/@jridgewell/gen-mapping/dist/types/gen-mapping.d.ts","../node_modules/@jridgewell/source-map/dist/types/source-map.d.ts","../node_modules/terser/tools/terser.d.ts","./node_modules/vite/types/internal/terseroptions.d.ts","../node_modules/source-map-js/source-map.d.ts","./node_modules/vite/node_modules/postcss/lib/previous-map.d.ts","./node_modules/vite/node_modules/postcss/lib/input.d.ts","./node_modules/vite/node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/vite/node_modules/postcss/lib/declaration.d.ts","./node_modules/vite/node_modules/postcss/lib/root.d.ts","./node_modules/vite/node_modules/postcss/lib/warning.d.ts","./node_modules/vite/node_modules/postcss/lib/lazy-result.d.ts","./node_modules/vite/node_modules/postcss/lib/no-work-result.d.ts","./node_modules/vite/node_modules/postcss/lib/processor.d.ts","./node_modules/vite/node_modules/postcss/lib/result.d.ts","./node_modules/vite/node_modules/postcss/lib/document.d.ts","./node_modules/vite/node_modules/postcss/lib/rule.d.ts","./node_modules/vite/node_modules/postcss/lib/node.d.ts","./node_modules/vite/node_modules/postcss/lib/comment.d.ts","./node_modules/vite/node_modules/postcss/lib/container.d.ts","./node_modules/vite/node_modules/postcss/lib/at-rule.d.ts","./node_modules/vite/node_modules/postcss/lib/list.d.ts","./node_modules/vite/node_modules/postcss/lib/postcss.d.ts","./node_modules/vite/node_modules/postcss/lib/postcss.d.mts","./node_modules/vite/types/internal/csspreprocessoroptions.d.ts","../node_modules/lightningcss/node/ast.d.ts","../node_modules/lightningcss/node/targets.d.ts","../node_modules/lightningcss/node/index.d.ts","./node_modules/vite/types/internal/lightningcssoptions.d.ts","./node_modules/vite/types/metadata.d.ts","./node_modules/vite/dist/node/index.d.ts","./node_modules/@sveltejs/vite-plugin-svelte-inspector/types/index.d.ts","./node_modules/@sveltejs/vite-plugin-svelte/types/index.d.ts","../node_modules/@standard-schema/spec/dist/index.d.ts","../node_modules/@opentelemetry/api/build/src/baggage/internal/symbol.d.ts","../node_modules/@opentelemetry/api/build/src/baggage/types.d.ts","../node_modules/@opentelemetry/api/build/src/baggage/utils.d.ts","../node_modules/@opentelemetry/api/build/src/common/exception.d.ts","../node_modules/@opentelemetry/api/build/src/common/time.d.ts","../node_modules/@opentelemetry/api/build/src/common/attributes.d.ts","../node_modules/@opentelemetry/api/build/src/context/types.d.ts","../node_modules/@opentelemetry/api/build/src/context/context.d.ts","../node_modules/@opentelemetry/api/build/src/api/context.d.ts","../node_modules/@opentelemetry/api/build/src/diag/types.d.ts","../node_modules/@opentelemetry/api/build/src/diag/consolelogger.d.ts","../node_modules/@opentelemetry/api/build/src/api/diag.d.ts","../node_modules/@opentelemetry/api/build/src/metrics/observableresult.d.ts","../node_modules/@opentelemetry/api/build/src/metrics/metric.d.ts","../node_modules/@opentelemetry/api/build/src/metrics/meter.d.ts","../node_modules/@opentelemetry/api/build/src/metrics/noopmeter.d.ts","../node_modules/@opentelemetry/api/build/src/metrics/meterprovider.d.ts","../node_modules/@opentelemetry/api/build/src/api/metrics.d.ts","../node_modules/@opentelemetry/api/build/src/propagation/textmappropagator.d.ts","../node_modules/@opentelemetry/api/build/src/baggage/context-helpers.d.ts","../node_modules/@opentelemetry/api/build/src/api/propagation.d.ts","../node_modules/@opentelemetry/api/build/src/trace/attributes.d.ts","../node_modules/@opentelemetry/api/build/src/trace/trace_state.d.ts","../node_modules/@opentelemetry/api/build/src/trace/span_context.d.ts","../node_modules/@opentelemetry/api/build/src/trace/link.d.ts","../node_modules/@opentelemetry/api/build/src/trace/status.d.ts","../node_modules/@opentelemetry/api/build/src/trace/span.d.ts","../node_modules/@opentelemetry/api/build/src/trace/span_kind.d.ts","../node_modules/@opentelemetry/api/build/src/trace/spanoptions.d.ts","../node_modules/@opentelemetry/api/build/src/trace/tracer.d.ts","../node_modules/@opentelemetry/api/build/src/trace/tracer_options.d.ts","../node_modules/@opentelemetry/api/build/src/trace/proxytracer.d.ts","../node_modules/@opentelemetry/api/build/src/trace/tracer_provider.d.ts","../node_modules/@opentelemetry/api/build/src/trace/proxytracerprovider.d.ts","../node_modules/@opentelemetry/api/build/src/trace/samplingresult.d.ts","../node_modules/@opentelemetry/api/build/src/trace/sampler.d.ts","../node_modules/@opentelemetry/api/build/src/trace/trace_flags.d.ts","../node_modules/@opentelemetry/api/build/src/trace/internal/utils.d.ts","../node_modules/@opentelemetry/api/build/src/trace/spancontext-utils.d.ts","../node_modules/@opentelemetry/api/build/src/trace/invalid-span-constants.d.ts","../node_modules/@opentelemetry/api/build/src/trace/context-utils.d.ts","../node_modules/@opentelemetry/api/build/src/api/trace.d.ts","../node_modules/@opentelemetry/api/build/src/context-api.d.ts","../node_modules/@opentelemetry/api/build/src/diag-api.d.ts","../node_modules/@opentelemetry/api/build/src/metrics-api.d.ts","../node_modules/@opentelemetry/api/build/src/propagation-api.d.ts","../node_modules/@opentelemetry/api/build/src/trace-api.d.ts","../node_modules/@opentelemetry/api/build/src/index.d.ts","../node_modules/@types/cookie/index.d.ts","./node_modules/@sveltejs/kit/types/index.d.ts","./.svelte-kit/ambient.d.ts","./.svelte-kit/non-ambient.d.ts","./.svelte-kit/types/src/routes/$types.d.ts","./.svelte-kit/types/src/routes/about/$types.d.ts","./.svelte-kit/types/src/routes/about/intro/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/apis/frontend/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/apis/frontend/generated/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/apis/packages/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/apis/packages/generated/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/app-and-repo-structure/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/app-customization/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/app-settings/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/auto-documentation/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/backend/authentication/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/backend/customized-behaviour/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/backend/default-data-loading/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/backend/mock-data-generation/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/backend/openvaa-admin-tools-plugin-for-strapi/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/backend/plugins/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/backend/preparing-backend-dependencies/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/backend/re-generating-types/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/backend/running-the-backend-separately/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/backend/security/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/candidate-user-management/creating-a-new-candidate/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/candidate-user-management/mock-data/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/candidate-user-management/password-validation/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/candidate-user-management/registration-process-in-strapi/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/candidate-user-management/resetting-the-password/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/configuration/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/contributing/ai-agents/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/contributing/code-style-guide/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/contributing/contribute/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/contributing/issues/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/contributing/pull-request/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/contributing/recommended-ide-settings-code/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/contributing/workflows/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/deployment/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/development/intro/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/development/monorepo/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/development/requirements/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/development/roadmap/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/development/running-the-development-environment/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/frontend/accessing-data-and-state-management/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/frontend/components/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/frontend/contexts/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/frontend/data-api/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/frontend/environmental-variables/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/frontend/intro/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/frontend/routing/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/frontend/routing/generated/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/frontend/setting-up-the-application-for-local-development-using-docker/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/frontend/styling/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/llm-features/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/localization/intro/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/localization/local-translations/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/localization/locale-routes/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/localization/locale-selection-step-by-step/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/localization/localization-in-strapi/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/localization/localization-in-the-frontend/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/localization/storing-multi-locale-data/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/localization/supported-locales/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/quick-start/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/testing/$types.d.ts","./.svelte-kit/types/src/routes/developers-guide/troubleshooting/$types.d.ts","./.svelte-kit/types/src/routes/publishers-guide/$types.d.ts","./.svelte-kit/types/src/routes/publishers-guide/app-settings/$types.d.ts","./node_modules/vite-plugin-devtools-json/dist/index.d.ts","./node_modules/@tailwindcss/vite/dist/index.d.mts","../node_modules/@vitest/utils/node_modules/@vitest/pretty-format/dist/index.d.ts","../node_modules/@vitest/utils/dist/display.d.ts","../node_modules/@vitest/utils/dist/types.d.ts","../node_modules/@vitest/utils/dist/helpers.d.ts","../node_modules/@vitest/utils/dist/timers.d.ts","../node_modules/@vitest/utils/dist/index.d.ts","./node_modules/@vitest/runner/dist/tasks.d-xu8vapgy.d.ts","../node_modules/@vitest/utils/dist/types.d-bcelap-c.d.ts","../node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/@vitest/runner/dist/types.d.ts","./node_modules/@vitest/runner/dist/index.d.ts","./node_modules/@vitest/spy/dist/index.d.ts","../node_modules/tinyrainbow/dist/index.d.ts","../node_modules/@types/deep-eql/index.d.ts","../node_modules/assertion-error/index.d.ts","../node_modules/@types/chai/index.d.ts","./node_modules/@vitest/expect/dist/index.d.ts","./node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/@vitest/snapshot/dist/environment.d-dhdq1csl.d.ts","./node_modules/@vitest/snapshot/dist/rawsnapshot.d-lfsmjfud.d.ts","./node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/vitest/dist/chunks/traces.d.402v_yfi.d.ts","./node_modules/vitest/dist/chunks/rpc.d.rh3apgef.d.ts","./node_modules/vitest/dist/chunks/config.d.czijkicf.d.ts","./node_modules/vitest/dist/chunks/environment.d.crsxczp1.d.ts","./node_modules/vitest/dist/chunks/worker.d.b4a26qg6.d.ts","./node_modules/vitest/dist/chunks/browser.d.dbzuq_na.d.ts","./node_modules/@vitest/mocker/dist/types.d-b8cckmht.d.ts","./node_modules/@vitest/mocker/dist/index.d-c-slyzi-.d.ts","./node_modules/@vitest/mocker/dist/index.d.ts","../node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/@vitest/runner/dist/utils.d.ts","../node_modules/tinybench/dist/index.d.ts","./node_modules/vitest/dist/chunks/benchmark.d.daahlpsq.d.ts","./node_modules/vitest/dist/chunks/global.d.b15mdlcr.d.ts","./node_modules/vitest/dist/chunks/suite.d.bjwk38hb.d.ts","./node_modules/vitest/dist/chunks/evaluatedmodules.d.bxj5omdx.d.ts","./node_modules/expect-type/dist/utils.d.ts","./node_modules/expect-type/dist/overloads.d.ts","./node_modules/expect-type/dist/branding.d.ts","./node_modules/expect-type/dist/messages.d.ts","./node_modules/expect-type/dist/index.d.ts","./node_modules/vitest/dist/index.d.ts","./node_modules/vitest/dist/chunks/coverage.d.bztk59wp.d.ts","../node_modules/@vitest/utils/dist/serialize.d.ts","../node_modules/@vitest/utils/dist/error.d.ts","./node_modules/vitest/dist/browser.d.ts","./node_modules/@vitest/browser/aria-role.d.ts","./node_modules/@vitest/browser/jest-dom.d.ts","./node_modules/@vitest/browser/matchers.d.ts","./node_modules/@vitest/browser/context.d.ts","./node_modules/@vitest/browser-playwright/context.d.ts","./node_modules/vitest/browser/context.d.ts","./node_modules/vitest/optional-types.d.ts","./node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/vitest/dist/chunks/reporters.d.oxek7y4s.d.ts","./node_modules/vitest/dist/chunks/plugin.d.cy7cujf-.d.ts","./node_modules/vitest/dist/config.d.ts","./node_modules/vitest/config.d.ts","../node_modules/obug/dist/core.d.ts","../node_modules/obug/dist/node.d.ts","./node_modules/vitest/dist/node.d.ts","./node_modules/@vitest/browser/dist/index.d.ts","../node_modules/playwright/node_modules/playwright-core/types/protocol.d.ts","../node_modules/playwright/node_modules/playwright-core/types/structs.d.ts","../node_modules/playwright/node_modules/playwright-core/types/types.d.ts","../node_modules/playwright/node_modules/playwright-core/index.d.ts","../node_modules/playwright/index.d.ts","./node_modules/@vitest/browser-playwright/dist/index.d.ts","./vite.config.ts","./src/app.d.ts","./src/lib/navigation.type.ts","./src/lib/navigation.config.ts","../node_modules/clsx/clsx.d.mts","./node_modules/svelte/elements.d.ts","./src/lib/components/openvaalogo/openvaalogo.type.ts","./src/lib/components/openvaalogo/index.ts","./src/lib/utils/navigation.ts","../node_modules/@types/accepts/index.d.ts","../node_modules/@types/argparse/index.d.ts","../node_modules/@types/aria-query/index.d.ts","../node_modules/@babel/types/lib/index.d.ts","../node_modules/@types/babel__generator/index.d.ts","../node_modules/@babel/parser/typings/babel-parser.d.ts","../node_modules/@types/babel__template/index.d.ts","../node_modules/@types/babel__traverse/index.d.ts","../node_modules/@types/babel__core/index.d.ts","../node_modules/@types/connect/index.d.ts","../node_modules/@types/body-parser/index.d.ts","../node_modules/keyv/src/index.d.ts","../node_modules/@types/http-cache-semantics/index.d.ts","../node_modules/@types/responselike/index.d.ts","../node_modules/@types/cacheable-request/index.d.ts","../node_modules/@types/cheerio/index.d.ts","../node_modules/@types/qs/index.d.ts","../node_modules/@types/co-body/index.d.ts","../node_modules/@types/content-disposition/index.d.ts","../node_modules/@types/mime/index.d.ts","../node_modules/@types/send/index.d.ts","../node_modules/@types/range-parser/index.d.ts","../node_modules/@types/express-serve-static-core/index.d.ts","../node_modules/@types/http-errors/index.d.ts","../node_modules/@types/serve-static/index.d.ts","../node_modules/@types/express/index.d.ts","../node_modules/@types/keygrip/index.d.ts","../node_modules/@types/cookies/index.d.ts","../node_modules/@types/estree/index.d.ts","../node_modules/@types/json-schema/index.d.ts","../node_modules/@types/eslint/use-at-your-own-risk.d.ts","../node_modules/@types/eslint/index.d.ts","../node_modules/eslint/node_modules/@eslint/core/dist/cjs/types.d.cts","../node_modules/eslint/lib/types/use-at-your-own-risk.d.ts","../node_modules/eslint/lib/types/index.d.ts","../node_modules/@types/eslint-scope/index.d.ts","../node_modules/@types/fined/index.d.ts","../node_modules/@types/follow-redirects/index.d.ts","../node_modules/@types/formidable/formidable.d.ts","../node_modules/@types/formidable/parsers/index.d.ts","../node_modules/@types/formidable/persistentfile.d.ts","../node_modules/@types/formidable/volatilefile.d.ts","../node_modules/@types/formidable/formidableerror.d.ts","../node_modules/@types/formidable/index.d.ts","../node_modules/@types/fs-extra/index.d.ts","../node_modules/@types/minimatch/index.d.ts","../node_modules/@types/glob/index.d.ts","../node_modules/@types/graceful-fs/index.d.ts","../node_modules/@types/hast/node_modules/@types/unist/index.d.ts","../node_modules/@types/hast/index.d.ts","../node_modules/@types/hoist-non-react-statics/node_modules/@types/react/global.d.ts","../node_modules/csstype/index.d.ts","../node_modules/@types/hoist-non-react-statics/node_modules/@types/react/index.d.ts","../node_modules/@types/hoist-non-react-statics/index.d.ts","../node_modules/@types/html-minifier-terser/index.d.ts","../node_modules/@types/http-assert/index.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/subscription.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/types.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/subscriber.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/operator.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/iif.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/throwerror.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/subject.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/connectableobservable.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/operators/groupby.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/symbol/observable.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/behaviorsubject.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/replaysubject.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/asyncsubject.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/action.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/asyncscheduler.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/asyncaction.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/asapscheduler.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/asap.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/async.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/queuescheduler.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/queue.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/animationframescheduler.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/animationframe.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduler/virtualtimescheduler.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/notification.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/util/pipe.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/util/noop.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/util/identity.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/util/isobservable.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/util/argumentoutofrangeerror.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/util/emptyerror.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/util/objectunsubscribederror.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/util/unsubscriptionerror.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/util/timeouterror.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/bindcallback.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/bindnodecallback.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/innersubscriber.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/outersubscriber.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/combinelatest.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/concat.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/defer.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/empty.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/forkjoin.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/from.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/fromevent.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/fromeventpattern.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/generate.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/interval.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/merge.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/never.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/of.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/onerrorresumenext.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/pairs.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/partition.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/race.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/range.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/timer.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/using.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/observable/zip.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/scheduled/scheduled.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/internal/config.d.ts","../node_modules/@types/inquirer/node_modules/rxjs/index.d.ts","../node_modules/@types/through/index.d.ts","../node_modules/@types/inquirer/lib/objects/choice.d.ts","../node_modules/@types/inquirer/lib/objects/separator.d.ts","../node_modules/@types/inquirer/lib/objects/choices.d.ts","../node_modules/@types/inquirer/lib/utils/screen-manager.d.ts","../node_modules/@types/inquirer/lib/prompts/base.d.ts","../node_modules/@types/inquirer/lib/utils/paginator.d.ts","../node_modules/@types/inquirer/lib/prompts/checkbox.d.ts","../node_modules/@types/inquirer/lib/prompts/confirm.d.ts","../node_modules/@types/inquirer/lib/prompts/editor.d.ts","../node_modules/@types/inquirer/lib/prompts/expand.d.ts","../node_modules/@types/inquirer/lib/prompts/input.d.ts","../node_modules/@types/inquirer/lib/prompts/list.d.ts","../node_modules/@types/inquirer/lib/prompts/number.d.ts","../node_modules/@types/inquirer/lib/prompts/password.d.ts","../node_modules/@types/inquirer/lib/prompts/rawlist.d.ts","../node_modules/@types/inquirer/lib/ui/baseui.d.ts","../node_modules/@types/inquirer/lib/ui/bottom-bar.d.ts","../node_modules/@types/inquirer/lib/ui/prompt.d.ts","../node_modules/@types/inquirer/lib/utils/events.d.ts","../node_modules/@types/inquirer/lib/utils/readline.d.ts","../node_modules/@types/inquirer/lib/utils/utils.d.ts","../node_modules/@types/inquirer/index.d.ts","../node_modules/@types/is-hotkey/index.d.ts","../node_modules/@types/istanbul-lib-coverage/index.d.ts","../node_modules/@types/istanbul-lib-report/index.d.ts","../node_modules/@types/istanbul-reports/index.d.ts","../node_modules/@types/js-yaml/index.d.ts","../node_modules/@types/jsonwebtoken/index.d.ts","../node_modules/@types/keyv/index.d.ts","../node_modules/@types/koa-compose/index.d.ts","../node_modules/@types/koa/index.d.ts","../node_modules/@types/liftoff/index.d.ts","../node_modules/@types/lodash/common/common.d.ts","../node_modules/@types/lodash/common/array.d.ts","../node_modules/@types/lodash/common/collection.d.ts","../node_modules/@types/lodash/common/date.d.ts","../node_modules/@types/lodash/common/function.d.ts","../node_modules/@types/lodash/common/lang.d.ts","../node_modules/@types/lodash/common/math.d.ts","../node_modules/@types/lodash/common/number.d.ts","../node_modules/@types/lodash/common/object.d.ts","../node_modules/@types/lodash/common/seq.d.ts","../node_modules/@types/lodash/common/string.d.ts","../node_modules/@types/lodash/common/util.d.ts","../node_modules/@types/lodash/index.d.ts","../node_modules/iconv-lite/lib/index.d.ts","../node_modules/@types/mailparser/index.d.ts","../node_modules/@types/mdast/node_modules/@types/unist/index.d.ts","../node_modules/@types/mdast/index.d.ts","../node_modules/@types/node-fetch/node_modules/form-data/index.d.ts","../node_modules/@types/node-fetch/externals.d.ts","../node_modules/@types/node-fetch/index.d.ts","../node_modules/@types/nodemon/index.d.ts","../node_modules/@types/normalize-package-data/index.d.ts","../node_modules/@types/parse-json/index.d.ts","../node_modules/@types/progress-stream/index.d.ts","../node_modules/@types/prompts/node_modules/kleur/kleur.d.ts","../node_modules/@types/prompts/index.d.ts","../node_modules/@types/prop-types/index.d.ts","../node_modules/@types/pug/index.d.ts","../node_modules/@types/react/global.d.ts","../node_modules/@types/react/index.d.ts","../node_modules/@types/react-dom/index.d.ts","../node_modules/@types/react-is/node_modules/@types/react/index.d.ts","../node_modules/@types/react-is/index.d.ts","../node_modules/@types/react-transition-group/config.d.ts","../node_modules/@types/react-transition-group/transition.d.ts","../node_modules/@types/react-transition-group/csstransition.d.ts","../node_modules/@types/react-transition-group/switchtransition.d.ts","../node_modules/@types/react-transition-group/transitiongroup.d.ts","../node_modules/@types/react-transition-group/index.d.ts","../node_modules/@types/resolve/index.d.ts","../node_modules/@types/slice-ansi/index.d.ts","../node_modules/@types/stack-utils/index.d.ts","../node_modules/@types/stylis/index.d.ts","../node_modules/@types/triple-beam/index.d.ts","../node_modules/@types/trusted-types/lib/index.d.ts","../node_modules/@types/trusted-types/index.d.ts","../node_modules/@types/unist/index.d.ts","../node_modules/@types/use-sync-external-store/index.d.ts","../node_modules/@types/yargs-parser/index.d.ts","../node_modules/@types/yargs/index.d.ts","../node_modules/@types/react-is/node_modules/@types/react/global.d.ts"],"fileIdsList":[[105,152,296],[105,152,296,439],[93,105,152,296],[105,152,296,299],[93,99,105,152,166,243,245,246,294,295,296,298,422],[105,152,243,422],[93,105,152,243,244,422],[105,149,152],[105,151,152],[152],[105,152,157,185],[105,152,153,158,163,171,182,193],[105,152,153,154,163,171],[105,152],[100,101,102,105,152],[105,152,155,194],[105,152,156,157,164,172],[105,152,157,182,190],[105,152,158,160,163,171],[105,151,152,159],[105,152,160,161],[105,152,162,163],[105,151,152,163],[105,152,163,164,165,182,193],[105,152,163,164,165,178,182,185],[105,152,160,163,166,171,182,193],[105,152,163,164,166,167,171,182,190,193],[105,152,166,168,182,190,193],[103,104,105,106,107,108,109,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199],[105,152,163,169],[105,152,170,193,198],[105,152,160,163,171,182],[105,152,172],[105,152,173],[105,151,152,174],[105,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199],[105,152,176],[105,152,177],[105,152,163,178,179],[105,152,178,180,194,196],[105,152,163,182,183,185],[105,152,184,185],[105,152,182,183],[105,152,185],[105,152,186],[105,149,152,182,187],[105,152,163,188,189],[105,152,188,189],[105,152,157,171,182,190],[105,152,191],[105,152,171,192],[105,152,166,177,193],[105,152,157,194],[105,152,182,195],[105,152,170,196],[105,152,197],[105,147,152],[105,147,152,163,165,174,182,185,193,196,198],[105,152,182,199],[105,152,415],[105,152,417,426,427,432,433],[105,152,407,411,412,414],[105,152,415,426,427,433],[105,152,412,415],[105,152,407,413,414,415],[105,152,246,366,370,373,375,376,377,380,399],[105,152,392],[105,152,392,393],[105,152,370,371,373,374],[105,152,370],[105,152,370,371,373],[105,152,370,371],[105,152,365,383,384],[105,152,365,383],[105,152,402,403],[105,152,402,403,404,405],[105,152,402,404],[105,152,402],[105,152,203,242,243],[91,105,152,203],[93,105,152,438],[89,90,91,92,93,105,152,203],[105,119,123,152,193],[105,119,152,182,193],[105,114,152],[105,116,119,152,190,193],[105,152,171,190],[105,152,200],[105,114,152,200],[105,116,119,152,171,193],[105,111,112,115,118,152,163,182,193],[105,119,126,152],[105,111,117,152],[105,119,140,141,152],[105,115,119,152,185,193,200],[105,140,152,200],[105,113,114,152,200],[105,119,152],[105,113,114,115,116,117,118,119,120,121,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,141,142,143,144,145,146,152],[105,119,134,152],[105,119,126,127,152],[105,117,119,127,128,152],[105,118,152],[105,111,114,119,152],[105,119,123,127,128,152],[105,123,152],[105,117,119,122,152,193],[105,111,116,119,126,152],[105,152,182],[105,114,119,140,152,198,200],[98,105,152],[94,105,152],[94,95,97,105,152,163,164,166,167,168,171,182,190,193,199,200,201,203,204,205,206,216,236,237,241,242,243],[94,95,96,105,152,201],[105,152,232],[105,152,230,232],[105,152,221,229,230,231,233,235],[105,152,219],[105,152,222,227,232,235],[105,152,218,235],[105,152,222,223,226,227,228,235],[105,152,222,223,224,226,227,235],[105,152,219,220,221,222,223,227,228,229,231,232,233,235],[105,152,235],[105,152,217,219,220,221,222,223,224,226,227,228,229,230,231,232,233,234],[105,152,217,235],[105,152,222,224,225,227,228,235],[105,152,226,235],[105,152,227,228,232,235],[105,152,220,230],[95,105,152],[96,97,105,152],[105,152,240],[105,152,215],[105,152,203,243],[105,152,411,416],[105,152,381,399,422],[105,152,365,366,368,369,370,373,375,376,385,388,395,399,408,410],[105,152,375,396,397,399],[105,152,375,390,399],[105,152,365,373,375,385,399],[105,152,205],[105,152,365,375,381,385,387,398,399],[105,152,243,420,422],[105,152,155,164,182,243,365,370,373,375,381,385,386,387,388,390,391,394,395,398,399,408,417,418,419,422,433],[105,152,205,375,385,386,399],[105,152,375,396,397,398,399],[105,152,205,375,387,388,389,399],[105,152,155,164,182,205,243,365,370,373,375,381,385,386,387,388,389,390,391,394,395,396,397,398,399,408,417,418,419,420,421,422,433],[105,152,205,365,370,373,375,376,381,385,386,387,388,389,390,391,396,397,398,399,400,401,406],[105,152,155,164,166,182,205,243,365,370,373,375,381,385,386,387,388,389,390,391,394,395,396,397,398,399,408,417,418,419,420,421,422,425,433],[93,105,152,440],[105,152,298,439],[105,152,436],[105,152,436,437],[105,152,296,363,364,423,433],[105,152,446],[105,152,210,212],[105,152,211],[105,152,210,213],[105,152,208,210],[105,152,207,208,209],[105,152,207,210],[105,152,253],[105,152,256],[105,152,261,263],[105,152,249,253,265,266],[105,152,276,279,285,287],[105,152,248,253],[105,152,247],[105,152,248],[105,152,255],[105,152,258],[105,152,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,288,289,290,291,292,293],[105,152,264],[105,152,260],[105,152,261],[105,152,252,253,259],[105,152,260,261],[105,152,267],[105,152,288],[105,152,252],[105,152,253,270,273],[105,152,269],[105,152,270],[105,152,268,270],[105,152,253,273,275,276,277],[105,152,276,277,279],[105,152,253,268,271,274,281],[105,152,268,269],[105,152,250,251,268,270,271,272],[105,152,270,273],[105,152,251,268,271,274],[105,152,253,273,275],[105,152,276,277],[105,152,166,200],[105,152,446,447,448,449,450],[105,152,446,448],[105,152,166,200,452],[105,152,163,166,193,200,454,455,456],[105,152,378,379],[105,152,166,200,459],[105,152,166,200,452,468,469],[105,152,471,477],[105,152,471,472,473],[105,152,474],[105,152,163,166,200,459,463,464],[105,152,453,459,465,467],[105,152,166,168,182,193,200],[105,152,166,182,486],[105,152,182,200,481,482,483,484,485],[105,152,182,486],[105,152,163,486],[105,152,164,200],[105,152,163,164,200,488],[105,152,491],[105,152,495],[105,152,493,494],[105,152,178,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,580,581,582,583,584],[105,152,585],[105,152,564,565,585],[105,152,178,562,567,585],[105,152,178,568,569,585],[105,152,178,568,585],[105,152,178,562,568,585],[105,152,178,574,585],[105,152,178,585],[105,152,563,579,585],[105,152,562,579,585],[105,152,178,562],[105,152,567],[105,152,178],[105,152,562,585],[105,152,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,518,519,521,523,524,525,526,527,528,529,530,531,532,533,534,535,536,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561],[105,152,499,501,506],[105,152,501,538],[105,152,500,505],[105,152,499,500,501,502,503,504],[105,152,500,501,502,505,538],[105,152,499,501,505,506],[105,152,505],[105,152,505,545],[105,152,499,500,501,505],[105,152,500,501,502,505],[105,152,500,501],[105,152,499,500,501,505,506],[105,152,501,537],[105,152,499,500,501,506],[105,152,562],[105,152,499,500,514],[105,152,499,500,513],[105,152,522],[105,152,515,516],[105,152,517],[105,152,515],[105,152,499,500,514,515],[105,152,499,500,513,514,516],[105,152,520],[105,152,499,500,515,516],[105,152,499,500,501,502,505],[105,152,499,500],[105,152,500],[105,152,499,505],[105,152,587],[105,152,588],[105,152,157,200],[105,152,163,200],[105,152,594],[105,151,152,163,166,167,171,177,193,200,443,461,466,469,470,498,593],[105,152,163,200,479],[105,152,596,598,599,600,601,602,603,604,605,606,607,608],[105,152,596,597,599,600,601,602,603,604,605,606,607,608],[105,152,597,598,599,600,601,602,603,604,605,606,607,608],[105,152,596,597,598,600,601,602,603,604,605,606,607,608],[105,152,596,597,598,599,601,602,603,604,605,606,607,608],[105,152,596,597,598,599,600,602,603,604,605,606,607,608],[105,152,596,597,598,599,600,601,603,604,605,606,607,608],[105,152,596,597,598,599,600,601,602,604,605,606,607,608],[105,152,596,597,598,599,600,601,602,603,605,606,607,608],[105,152,596,597,598,599,600,601,602,603,604,606,607,608],[105,152,596,597,598,599,600,601,602,603,604,605,607,608],[105,152,596,597,598,599,600,601,602,603,604,605,606,608],[105,152,596,597,598,599,600,601,602,603,604,605,606,607],[105,152,182,200,609],[105,152,166,193,200,613,614],[105,152,166,182,200],[105,152,182,200],[105,152,182,200,620],[105,152,625],[105,152,494,646],[105,152,625,630],[105,152,629,630,631,632,633],[105,152,494,622,624],[105,152,164,182,200,462],[105,152,166,200,463,466],[105,152,640],[105,152,644],[83,105,152],[84,85,86,87,105,152],[83,85,105,152],[84,87,105,152],[105,152,365,372],[105,152,365],[105,152,365,372,409],[105,152,367],[105,152,365,366,367,368,369],[105,152,471,472,475,476],[105,152,477],[88,89,105,152],[105,152,163],[105,152,238,239],[105,152,194],[105,152,424],[105,152,431],[105,152,430],[105,152,153,164,182,428,429],[105,152,214]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"2ab096661c711e4a81cc464fa1e6feb929a54f5340b46b0a07ac6bbf857471f0","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"60037901da1a425516449b9a20073aa03386cce92f7a1fd902d7602be3a7c2e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"22adec94ef7047a6c9d1af3cb96be87a335908bf9ef386ae9fd50eeb37f44c47","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"73f78680d4c08509933daf80947902f6ff41b6230f94dd002ae372620adb0f60","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5239f5c01bcfa9cd32f37c496cf19c61d69d37e48be9de612b541aac915805b","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f209bee1a2bb686e42fc46301ca505be20b7cbf2dccff0f4406f1cfc10eebb8","impliedFormat":1},{"version":"6f006b3ccbf257ae375fcab5ca158c40338bf9c056b381c207e560d699db918f","impliedFormat":1},{"version":"cc512139c85c41ba2dc95076b1ce05786c98129bcfe875017946ba2c82607ef1","impliedFormat":1},{"version":"12bffdbf179bfe787334d1aa31393bac5b79a84d2285ad94bcf36c1cce9eed57","impliedFormat":1},{"version":"e81484fc62d5e6add90882339bb2cdba0c87b85ca4002add438d0771ce2fdfa7","impliedFormat":1},{"version":"92ebc3261b20037c4e078cd3d26bccedb719b3eec653925e103b6ced4a936c0d","impliedFormat":1},{"version":"31618d50f44952e197d84618673fa8966461cfac91795b5d8ae467e6e8b14b7b","impliedFormat":99},{"version":"2be2227c3810dfd84e46674fd33b8d09a4a28ad9cb633ed536effd411665ea1e","impliedFormat":99},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"ecd7b4429a2b12f50ef619043fca298eb99a9b7f9e8edabd60c9bbb7fa8f036b","impliedFormat":99},{"version":"aa374812a7941ec1c72df3ba5a6e8336810f1cbc7bba40fb9d43434b9e4b5a2c","affectsGlobalScope":true,"impliedFormat":99},{"version":"a7ca8df4f2931bef2aa4118078584d84a0b16539598eaadf7dce9104dfaa381c","impliedFormat":1},{"version":"72950913f4900b680f44d8cab6dd1ea0311698fc1eefb014eb9cdfc37ac4a734","impliedFormat":1},{"version":"36977c14a7f7bfc8c0426ae4343875689949fb699f3f84ecbe5b300ebf9a2c55","impliedFormat":1},{"version":"59f8dc89b9e724a6a667f52cdf4b90b6816ae6c9842ce176d38fcc973669009e","affectsGlobalScope":true,"impliedFormat":1},{"version":"474eba6689e97bf58edd28c90524e70f4fb11820df66752182a1ad1ff9970bb2","affectsGlobalScope":true,"impliedFormat":1},{"version":"1eef826bc4a19de22155487984e345a34c9cd511dd1170edc7a447cb8231dd4a","affectsGlobalScope":true,"impliedFormat":99},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"ba481bca06f37d3f2c137ce343c7d5937029b2468f8e26111f3c9d9963d6568d","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d9ef24f9a22a88e3e9b3b3d8c40ab1ddb0853f1bfbd5c843c37800138437b61","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"2cbe0621042e2a68c7cbce5dfed3906a1862a16a7d496010636cdbdb91341c0f","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2677634fe27e87348825bb041651e22d50a613e2fdf6a4a3ade971d71bac37e","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"8c0bcd6c6b67b4b503c11e91a1fb91522ed585900eab2ab1f61bba7d7caa9d6f","impliedFormat":1},{"version":"8cd19276b6590b3ebbeeb030ac271871b9ed0afc3074ac88a94ed2449174b776","affectsGlobalScope":true,"impliedFormat":1},{"version":"696eb8d28f5949b87d894b26dc97318ef944c794a9a4e4f62360cd1d1958014b","impliedFormat":1},{"version":"3f8fa3061bd7402970b399300880d55257953ee6d3cd408722cb9ac20126460c","impliedFormat":1},{"version":"35ec8b6760fd7138bbf5809b84551e31028fb2ba7b6dc91d95d098bf212ca8b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"68bd56c92c2bd7d2339457eb84d63e7de3bd56a69b25f3576e1568d21a162398","affectsGlobalScope":true,"impliedFormat":1},{"version":"3e93b123f7c2944969d291b35fed2af79a6e9e27fdd5faa99748a51c07c02d28","impliedFormat":1},{"version":"9d19808c8c291a9010a6c788e8532a2da70f811adb431c97520803e0ec649991","impliedFormat":1},{"version":"87aad3dd9752067dc875cfaa466fc44246451c0c560b820796bdd528e29bef40","impliedFormat":1},{"version":"4aacb0dd020eeaef65426153686cc639a78ec2885dc72ad220be1d25f1a439df","impliedFormat":1},{"version":"f0bd7e6d931657b59605c44112eaf8b980ba7f957a5051ed21cb93d978cf2f45","impliedFormat":1},{"version":"8db0ae9cb14d9955b14c214f34dae1b9ef2baee2fe4ce794a4cd3ac2531e3255","affectsGlobalScope":true,"impliedFormat":1},{"version":"15fc6f7512c86810273af28f224251a5a879e4261b4d4c7e532abfbfc3983134","impliedFormat":1},{"version":"58adba1a8ab2d10b54dc1dced4e41f4e7c9772cbbac40939c0dc8ce2cdb1d442","impliedFormat":1},{"version":"2fd4c143eff88dabb57701e6a40e02a4dbc36d5eb1362e7964d32028056a782b","impliedFormat":1},{"version":"714435130b9015fae551788df2a88038471a5a11eb471f27c4ede86552842bc9","impliedFormat":1},{"version":"855cd5f7eb396f5f1ab1bc0f8580339bff77b68a770f84c6b254e319bbfd1ac7","impliedFormat":1},{"version":"5650cf3dace09e7c25d384e3e6b818b938f68f4e8de96f52d9c5a1b3db068e86","impliedFormat":1},{"version":"1354ca5c38bd3fd3836a68e0f7c9f91f172582ba30ab15bb8c075891b91502b7","affectsGlobalScope":true,"impliedFormat":1},{"version":"27fdb0da0daf3b337c5530c5f266efe046a6ceb606e395b346974e4360c36419","impliedFormat":1},{"version":"2d2fcaab481b31a5882065c7951255703ddbe1c0e507af56ea42d79ac3911201","impliedFormat":1},{"version":"a192fe8ec33f75edbc8d8f3ed79f768dfae11ff5735e7fe52bfa69956e46d78d","impliedFormat":1},{"version":"ca867399f7db82df981d6915bcbb2d81131d7d1ef683bc782b59f71dda59bc85","affectsGlobalScope":true,"impliedFormat":1},{"version":"d9e971bba9cf977c7774abbd4d2e3413a231af8a06a2e8b16af2a606bc91ddd0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e043a1bc8fbf2a255bccf9bf27e0f1caf916c3b0518ea34aa72357c0afd42ec","impliedFormat":1},{"version":"b4f70ec656a11d570e1a9edce07d118cd58d9760239e2ece99306ee9dfe61d02","impliedFormat":1},{"version":"3bc2f1e2c95c04048212c569ed38e338873f6a8593930cf5a7ef24ffb38fc3b6","impliedFormat":1},{"version":"6e70e9570e98aae2b825b533aa6292b6abd542e8d9f6e9475e88e1d7ba17c866","impliedFormat":1},{"version":"f9d9d753d430ed050dc1bf2667a1bab711ccbb1c1507183d794cc195a5b085cc","impliedFormat":1},{"version":"9eece5e586312581ccd106d4853e861aaaa1a39f8e3ea672b8c3847eedd12f6e","impliedFormat":1},{"version":"47ab634529c5955b6ad793474ae188fce3e6163e3a3fb5edd7e0e48f14435333","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"0225ecb9ed86bdb7a2c7fd01f1556906902929377b44483dc4b83e03b3ef227d","affectsGlobalScope":true,"impliedFormat":1},{"version":"74cf591a0f63db318651e0e04cb55f8791385f86e987a67fd4d2eaab8191f730","impliedFormat":1},{"version":"5eab9b3dc9b34f185417342436ec3f106898da5f4801992d8ff38ab3aff346b5","impliedFormat":1},{"version":"12ed4559eba17cd977aa0db658d25c4047067444b51acfdcbf38470630642b23","affectsGlobalScope":true,"impliedFormat":1},{"version":"f3ffabc95802521e1e4bcba4c88d8615176dc6e09111d920c7a213bdda6e1d65","impliedFormat":1},{"version":"f9ab232778f2842ffd6955f88b1049982fa2ecb764d129ee4893cbc290f41977","impliedFormat":1},{"version":"ae56f65caf3be91108707bd8dfbccc2a57a91feb5daabf7165a06a945545ed26","impliedFormat":1},{"version":"a136d5de521da20f31631a0a96bf712370779d1c05b7015d7019a9b2a0446ca9","impliedFormat":1},{"version":"c3b41e74b9a84b88b1dca61ec39eee25c0dbc8e7d519ba11bb070918cfacf656","affectsGlobalScope":true,"impliedFormat":1},{"version":"4737a9dc24d0e68b734e6cfbcea0c15a2cfafeb493485e27905f7856988c6b29","affectsGlobalScope":true,"impliedFormat":1},{"version":"36d8d3e7506b631c9582c251a2c0b8a28855af3f76719b12b534c6edf952748d","impliedFormat":1},{"version":"1ca69210cc42729e7ca97d3a9ad48f2e9cb0042bada4075b588ae5387debd318","impliedFormat":1},{"version":"f5ebe66baaf7c552cfa59d75f2bfba679f329204847db3cec385acda245e574e","impliedFormat":1},{"version":"ed59add13139f84da271cafd32e2171876b0a0af2f798d0c663e8eeb867732cf","affectsGlobalScope":true,"impliedFormat":1},{"version":"05db535df8bdc30d9116fe754a3473d1b6479afbc14ae8eb18b605c62677d518","impliedFormat":1},{"version":"b1810689b76fd473bd12cc9ee219f8e62f54a7d08019a235d07424afbf074d25","impliedFormat":1},{"version":"10073cdcf56982064c5337787cc59b79586131e1b28c106ede5bff362f912b70","impliedFormat":99},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"4e741b9c88e80c9e4cedf07b5a698e8e3a3bd73cf649f664d6dd3f868c05c2f3","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"ff0a83c9a0489a627e264ffcb63f2264b935b20a502afa3a018848139e3d8575","impliedFormat":99},{"version":"161c8e0690c46021506e32fda85956d785b70f309ae97011fd27374c065cac9b","affectsGlobalScope":true,"impliedFormat":1},{"version":"971f12a5fc236419ced0b7b9f23a53c1758233713f565635bbf4b85e2b23f55a","impliedFormat":1},{"version":"9d670bb3be18ea59cea824e3bb07d576b55c9542f5bc24aacc2a3c1ebd889de6","impliedFormat":1},{"version":"695b586df2d8c78b78cdd7cc6943594f3f4bc52948f13b31cdedfa3ce8d97c31","impliedFormat":1},{"version":"0771a93ef5e3b2a29f929c20f7ad232829341a671c9d1e96e93ef3fc42ef7bc2","impliedFormat":1},{"version":"cadb68b67b80b14a9a5bb64cce3093168fb2bfe2c7b10096d230df5203218de1","impliedFormat":1},{"version":"0b3c75be13f930b46117e205d900ee9c4f2ad6c7317655bca5364958ba1e34f0","impliedFormat":1},{"version":"5af161220fdf46730477706e8c431ccbd1b4ff50223cb32450bc20513f50bfbd","impliedFormat":1},{"version":"1bfbc75967888d5e8854123482a2725a806208d9cee4a09937e566ea67c1df99","impliedFormat":1},{"version":"a6b62e405392a02cc9433194c8713ede9cdf45997a6745c377888dcb0ef0f5b7","impliedFormat":99},{"version":"f582b0fcbf1eea9b318ab92fb89ea9ab2ebb84f9b60af89328a91155e1afce72","impliedFormat":1},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"8885cf05f3e2abf117590bbb951dcf6359e3e5ac462af1c901cfd24c6a6472e2","impliedFormat":1},{"version":"333caa2bfff7f06017f114de738050dd99a765c7eb16571c6d25a38c0d5365dc","impliedFormat":1},{"version":"e61df3640a38d535fd4bc9f4a53aef17c296b58dc4b6394fd576b808dd2fe5e6","impliedFormat":1},{"version":"459920181700cec8cbdf2a5faca127f3f17fd8dd9d9e577ed3f5f3af5d12a2e4","impliedFormat":1},{"version":"4719c209b9c00b579553859407a7e5dcfaa1c472994bd62aa5dd3cc0757eb077","impliedFormat":1},{"version":"7ec359bbc29b69d4063fe7dad0baaf35f1856f914db16b3f4f6e3e1bca4099fa","impliedFormat":1},{"version":"70790a7f0040993ca66ab8a07a059a0f8256e7bb57d968ae945f696cbff4ac7a","impliedFormat":1},{"version":"d1b9a81e99a0050ca7f2d98d7eedc6cda768f0eb9fa90b602e7107433e64c04c","impliedFormat":1},{"version":"a022503e75d6953d0e82c2c564508a5c7f8556fad5d7f971372d2d40479e4034","impliedFormat":1},{"version":"b215c4f0096f108020f666ffcc1f072c81e9f2f95464e894a5d5f34c5ea2a8b1","impliedFormat":1},{"version":"644491cde678bd462bb922c1d0cfab8f17d626b195ccb7f008612dc31f445d2d","impliedFormat":1},{"version":"dfe54dab1fa4961a6bcfba68c4ca955f8b5bbeb5f2ab3c915aa7adaa2eabc03a","impliedFormat":1},{"version":"1251d53755b03cde02466064260bb88fd83c30006a46395b7d9167340bc59b73","impliedFormat":1},{"version":"47865c5e695a382a916b1eedda1b6523145426e48a2eae4647e96b3b5e52024f","impliedFormat":1},{"version":"4cdf27e29feae6c7826cdd5c91751cc35559125e8304f9e7aed8faef97dcf572","impliedFormat":1},{"version":"331b8f71bfae1df25d564f5ea9ee65a0d847c4a94baa45925b6f38c55c7039bf","impliedFormat":1},{"version":"2a771d907aebf9391ac1f50e4ad37952943515eeea0dcc7e78aa08f508294668","impliedFormat":1},{"version":"0146fd6262c3fd3da51cb0254bb6b9a4e42931eb2f56329edd4c199cb9aaf804","impliedFormat":1},{"version":"183f480885db5caa5a8acb833c2be04f98056bdcc5fb29e969ff86e07efe57ab","impliedFormat":99},{"version":"960bd764c62ac43edc24eaa2af958a4b4f1fa5d27df5237e176d0143b36a39c6","affectsGlobalScope":true,"impliedFormat":1},{"version":"f7eebe1b25040d805aefe8971310b805cd49b8602ec206d25b38dc48c542f165","impliedFormat":1},{"version":"a18642ddf216f162052a16cba0944892c4c4c977d3306a87cb673d46abbb0cbf","impliedFormat":1},{"version":"509f8efdfc5f9f6b52284170e8d7413552f02d79518d1db691ee15acc0088676","impliedFormat":1},{"version":"4ec16d7a4e366c06a4573d299e15fe6207fc080f41beac5da06f4af33ea9761e","impliedFormat":1},{"version":"e4af494f7a14b226bbe732e9c130d8811f8c7025911d7c58dd97121a85519715","impliedFormat":1},{"version":"c1d587d31636bf51527d349f4786a36472e5aa311add673073c833c9853493c8","impliedFormat":99},{"version":"439433b94f71b1e569fb4058ecaa7fde97bdd8aadf1c865298cd2c981b5fb691","impliedFormat":99},{"version":"e63d1bebae16b67829e3c956c33c82e5af7746dccabb149f7d261294d8f0286c","impliedFormat":99},{"version":"76af14c3cce62da183aaf30375e3a4613109d16c7f16d30702f16d625a95e62c","impliedFormat":99},{"version":"a4e9e0d92dcad2cb387a5f1bdffe621569052f2d80186e11973aa7080260d296","impliedFormat":1},{"version":"f6380cc36fc3efc70084d288d0a05d0a2e09da012ee3853f9d62431e7216f129","impliedFormat":1},{"version":"497c3e541b4acf6c5d5ba75b03569cfe5fe25c8a87e6c87f1af98da6a3e7b918","impliedFormat":1},{"version":"d9429b81edf2fb2abf1e81e9c2e92615f596ed3166673d9b69b84c369b15fdc0","impliedFormat":1},{"version":"7e22943ae4e474854ca0695ab750a8026f55bb94278331fda02a4fb42efce063","impliedFormat":1},{"version":"7da9ff3d9a7e62ddca6393a23e67296ab88f2fcb94ee5f7fb977fa8e478852ac","impliedFormat":1},{"version":"e1b45cc21ea200308cbc8abae2fb0cfd014cb5b0e1d1643bcc50afa5959b6d83","impliedFormat":1},{"version":"c9740b0ce7533ce6ba21a7d424e38d2736acdddeab2b1a814c00396e62cc2f10","impliedFormat":1},{"version":"b3c1f6a3fdbb04c6b244de6d5772ffdd9e962a2faea1440e410049c13e874b87","impliedFormat":1},{"version":"dcaa872d9b52b9409979170734bdfd38f846c32114d05b70640fd05140b171bb","impliedFormat":1},{"version":"6c434d20da381fcd2e8b924a3ec9b8653cf8bed8e0da648e91f4c984bd2a5a91","impliedFormat":1},{"version":"992419d044caf6b14946fa7b9463819ab2eeb7af7c04919cc2087ce354c92266","impliedFormat":1},{"version":"fa9815e9ce1330289a5c0192e2e91eb6178c0caa83c19fe0c6a9f67013fe795c","impliedFormat":1},{"version":"06384a1a73fcf4524952ecd0d6b63171c5d41dd23573907a91ef0a687ddb4a8c","impliedFormat":1},{"version":"34b1594ecf1c84bcc7a04d9f583afa6345a6fea27a52cf2685f802629219de45","impliedFormat":1},{"version":"d82c9ca830d7b94b7530a2c5819064d8255b93dfeddc5b2ebb8a09316f002c89","impliedFormat":1},{"version":"7e046b9634add57e512412a7881efbc14d44d1c65eadd35432412aa564537975","impliedFormat":1},{"version":"aac9079b9e2b5180036f27ab37cb3cf4fd19955be48ccc82eab3f092ee3d4026","impliedFormat":1},{"version":"3d9c38933bc69e0a885da20f019de441a3b5433ce041ba5b9d3a541db4b568cb","impliedFormat":1},{"version":"606aa2b74372221b0f79ca8ae3568629f444cc454aa59b032e4cb602308dec94","impliedFormat":1},{"version":"50474eaea72bfda85cc37ae6cd29f0556965c0849495d96c8c04c940ef3d2f44","impliedFormat":1},{"version":"b4874382f863cf7dc82b3d15aed1e1372ac3fede462065d5bfc8510c0d8f7b19","impliedFormat":1},{"version":"df10b4f781871afb72b2d648d497671190b16b679bf7533b744cc10b3c6bf7ea","impliedFormat":1},{"version":"1fdc28754c77e852c92087c789a1461aa6eed19c335dc92ce6b16a188e7ba305","impliedFormat":1},{"version":"a656dab1d502d4ddc845b66d8735c484bfebbf0b1eda5fb29729222675759884","impliedFormat":1},{"version":"465a79505258d251068dc0047a67a3605dd26e6b15e9ad2cec297442cbb58820","impliedFormat":1},{"version":"ddae22d9329db28ce3d80a2a53f99eaed66959c1c9cd719c9b744e5470579d2f","impliedFormat":1},{"version":"d0e25feadef054c6fc6a7f55ccc3b27b7216142106b9ff50f5e7b19d85c62ca7","impliedFormat":1},{"version":"111214009193320cacbae104e8281f6cb37788b52a6a84d259f9822c8c71f6ca","impliedFormat":1},{"version":"01c8e2c8984c96b9b48be20ee396bd3689a3a3e6add8d50fe8229a7d4e62ff45","impliedFormat":1},{"version":"a4a0800b592e533897b4967b00fb00f7cd48af9714d300767cc231271aa100af","impliedFormat":1},{"version":"20aa818c3e16e40586f2fa26327ea17242c8873fe3412a69ec68846017219314","impliedFormat":1},{"version":"f498532f53d54f831851990cb4bcd96063d73e302906fa07e2df24aa5935c7d1","impliedFormat":1},{"version":"5fd19dfde8de7a0b91df6a9bbdc44b648fd1f245cae9e8b8cf210d83ee06f106","impliedFormat":1},{"version":"3b8d6638c32e63ea0679eb26d1eb78534f4cc02c27b80f1c0a19f348774f5571","impliedFormat":1},{"version":"ce0da52e69bc3d82a7b5bc40da6baad08d3790de13ad35e89148a88055b46809","impliedFormat":1},{"version":"9e01233da81bfed887f8d9a70d1a26bf11b8ddff165806cc586c84980bf8fc24","impliedFormat":1},{"version":"214a6afbab8b285fc97eb3cece36cae65ea2fca3cbd0c017a96159b14050d202","impliedFormat":1},{"version":"14beeca2944b75b229c0549e0996dc4b7863e07257e0d359d63a7be49a6b86a4","impliedFormat":1},{"version":"f7bb9adb1daa749208b47d1313a46837e4d27687f85a3af7777fc1c9b3dc06b1","impliedFormat":1},{"version":"c549fe2f52101ffe47f58107c702af7cdcd42da8c80afd79f707d1c5d77d4b6e","impliedFormat":1},{"version":"3966ea9e1c1a5f6e636606785999734988e135541b79adc6b5d00abdc0f4bf05","impliedFormat":1},{"version":"0b60b69c957adb27f990fbc27ea4ac1064249400262d7c4c1b0a1687506b3406","impliedFormat":1},{"version":"12c26e5d1befc0ded725cee4c2316f276013e6f2eb545966562ae9a0c1931357","impliedFormat":1},{"version":"27b247363f1376c12310f73ebac6debcde009c0b95b65a8207e4fa90e132b30a","impliedFormat":1},{"version":"05bd302e2249da923048c09dc684d1d74cb205551a87f22fb8badc09ec532a08","impliedFormat":1},{"version":"fe930ec064571ab3b698b13bddf60a29abf9d2f36d51ab1ca0083b087b061f3a","impliedFormat":1},{"version":"6b85c4198e4b62b0056d55135ad95909adf1b95c9a86cdbed2c0f4cc1a902d53","impliedFormat":1},{"version":"1748c03e7a7d118f7f6648c709507971eb0d416f489958492c5ae625de445184","impliedFormat":1},{"version":"5e59d278f7ee979ca9becc2271fc618381ddc699511f94b0aa6f3c22f5ff156c","affectsGlobalScope":true,"impliedFormat":99},"40faee1dcf7c818e9906dce20945ac26eec2f1e013d5e8f1839286167c8ec80c","66fd09068b05d36c4f601b3f7c61cc3ff10b97f9a40e0153abcbde27edc6e859","55709caf07067d6f0887d296e0a805943705e8dae6c2b6f523d8d9a83683aa79","b88e00dd2f7dad0da3c01f84baff263968bbf10da5a294657b6d957886223f9e","8b55244c4d61d4974c1971469b33163a48eb0e2fead13d5b6af92c00fb607a12","423afdb7056546f3108564eb274f599056a8afb63b208fc05f4c0c520c11ccbe","4bbf0dbd97c9bb907f393f21439bf2552b9e7f9105219966280e3dd17282294d","0c226b2460645a356374e39b574be422615afee1275580c66232fd6b47539319","b9c17e89acbac40f5b9c2fd0498643ffb9a26e41caef74cc54c56cf39b60569f","ef83f9e5d26b447f4844b0794902e12b8960261f9001640da88f4dadd9f14b07","0d0ed81938ecec8535d283018f8c101c27aa14d4828e5f4ce92775f604c2696f","e204e13c6ac471b9f0cd45f9134f5eec5cf3c897b0fdd8b7fb4512d182954862","31a30be7dc34007369be1830771e3cbfde6912f025a414864c1f03692a562236","661617749b88cd495f3291469e2a3cc3e90b4079710c749c7a90208456158ffc","91e52e454446c7fcf9420150ab48307a3e5284961df1d25b5aa43536000a0b95","2e11a5ad7c5e72120841ea527cece42d8765ca28cebf6366b41e959bb1de7c4b","f8485b26882ee5a632d596aa41609d11ad008b76db8002fa2711c43969149bd5","9b4f97b274251e0410ea7d1afd1b5f0a45797eee96e0fc76828391712823f019","3b69e9a8b6a47831c9e826e7ed67e2d61ee9057325d2180a66d984d7272745c1","759ef23ad2992b4645e9794f92e2fb1d07e16bc9e577471b6f6481ae707b20e3","ee19b198a0ac82462331f121edf5059497243b590e591cd7625d386ae2b0900f","344e9babb8990462956126016d275c9441be238c9a529dd37d6f4b97b3801f98","d1339ec0390ca0b7d5bfebe4e732beaa8f49bbeb83cabef23170a24769ad221f","5aa5705b82a39dfb97c5598fa8995b3198dc921d8e714f5db9d84cb9ec1d2b0d","060b7a78f87c26037aff851447547a4f716e6faa801c576cd6abce8a68c3d7f9","94a7ffb22b5e95b2d5c12b663283c9e7bd798eff060d7655c0018edcc07542b9","fa67f00c2db1073979f00c7130f8ca8ad743074b4ecd3709121b12dd140e3ff0","02180e79590f7d444641d212d634fcebfdffb2d08295a5122da85594ceaffff9","360e0562b62a930cb3fa9e14ae25fb264195795bddc4f290707a63551ccf75e4","511819879ac4f8c38ef02d96a11ed444032834b354e3c932cd04bbdb45121b46","858d0892734765fb3ba6e2f82da463edd98b806c6dedec8fe19d66ae18b7df1c","d5b3c4576d1444d6efed71b5edcfde74fd2f0ab73e930d36feea2ad56eaadba0","76f797d6f6ad88f4568fb7776b3fde0ed3cff8822d55d88cdb0dc8bd6ab698ab","8a75fa2e8a09dd7f0edfb1c29693193ff2858f597565845b9bae13bce49c7284","00ff90e69f800fe6fc323fc2de963b8134bcfbcfae5a34b939d9bb8564b73bf8","43868b8310909e348336f59392b877c7ddde7b02327bde9b4b1b1895b0e4d0da","cb0ba5df3f6743d55b99a1823c1572b697992625631739734cf427616ac06b6f","b6fce5b57764ee00db8aed8ddd79a2fe83d8d252bcf38e3cbfb34c686a6dc89d","2c3bb73d625c1b11edbc534943d032ad35a7012a1d7b00aa5646428fcc39f9dc","6a7c94e105baff21be5ebc94b378e9b4015d7cf070475986d830c6113f364ea0","89ac205eae872aeea12b75e23cd7fc8b64b3c43ff8c61cd91d92fbf621d432d6","aa028415eb257f223bd162003db6df0b916f6e47b0b32ddeb85fa4d0c573040b","c3d2d4e5058c8eead07e48d2231313b310f8cb625d97125b6c2084074a8a8e9f","f391b87065b61a72646bd9fd43311be2de7cc11e1e8c15deeb5dcdd3adcded41","38dee35b06ab4ded08d1b58e0e407eab9e846e4bc2ee1698a499479b6b914fde","b509e175e55c023ce99c0a02f0368af188a541544f8f282981d82480d104b7aa","41e4a9d0885edc92678f5b4c13e49f819fb4261175e7d6aa2bf84740e6222184","1c38e7f1cf07ce1e85443bb5424f4b222aa25515fdc8273801035f40cc483793","2c7b221f26c1fa2867d365a0bcf57f906caa3fcecfb7e208db0ac315cd96f4f3","d57c8bb5f9d93e49f62c1b76cb263be45a52828e8ab31aa01d1e368ac62eb082","57dbfbb21d70a2ace4c9566498f5f386191d7c6a94a635abe1ac361597899d21","b05cda582bc27842006917754ea793dd6960f1015ac6149a5a12c3dfc6526ece","f22bb5609c3830a354841ac96b2d9c0d11fb7215155609b7225bb7df650e4a75","2ec754d8a64cb63b176022faedb83863aca1fe593aaf980d55ac55ac6e15933d","d9d98722225c60b17d6b0377dd9156c538de9200afc3dfcaac2d0861a8ab0bbc","e386ee134fe83a973fa3593201547dabc9de86b74a2edb38bc9b9855c781b01c","06d305f47bae315a12408545ade6225994647db5d253014192ccaa6edc910e9c","eb1ec97747edd3f8de9f4347f35c401ee6650a1dc8cac91a71ffad84346497ca","886ec1a4212d10e7f5ef9c813eebd6ac7113dcfe4dc9bb6f2ae24b8a1e34e3b3","a4820698382f909494fb31ae8c8173be7f933be7e19be7dac2f13e7e74f111bc","6e35dfbe9be96b83367b89af6d14ca51a846ceafca2bbd38b36b59fec65deda4","eee622519b1904ce5a72c7d24e32d7c85864f71664fa7cfcb11a44486128bef1","fc7ef77b0e55b121dfbf6d455655dac761a7da3e689dc771ab965b0fe84f61ea","f7102e3b5d007280a507d01224204b3ccf1f042e9f08b3170c43a7bf0ccf78c6","ac47bf3f9484d2a4aa26bde7ecd2373f37b1c5de0a301d15641a9a90863db5bc","d630a9bb6a2a5cce4b4dccfd56e89838d53de89ae79e23129921499b0c2c7d02",{"version":"2bde6df7f02d36ceb4650590ff091f5aa33a32f71ea09c8df9bdf51406dc38f3","impliedFormat":99},{"version":"7757c6ca7a8ad1992401c6aff33633d6a088515be5a39d7ee188b35bfc8e5f8e","impliedFormat":99},{"version":"acfb723d81eda39156251aed414c553294870bf53062429ebfcfba8a68cb4753","impliedFormat":99},{"version":"09124307d0bc873aba353b80027899599e794c2cf44dfe6315d73111d40e29f4","impliedFormat":99},{"version":"b5ce343886d23392be9c8280e9f24a87f1d7d3667f6672c2fe4aa61fa4ece7d4","impliedFormat":99},{"version":"57e9e1b0911874c62d743af24b5d56032759846533641d550b12a45ff404bf07","impliedFormat":99},{"version":"b0857bb28fd5236ace84280f79a25093f919fd0eff13e47cc26ea03de60a7294","impliedFormat":99},{"version":"5e43e0824f10cd8c48e7a8c5c673638488925a12c31f0f9e0957965c290eb14c","impliedFormat":99},{"version":"d024767b27121cd5a2cb2f7cb93718803e4ed1c86ebd1ee3bd8de008f0ecbf96","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"3b0a56d056d81a011e484b9c05d5e430711aaecd561a788bad1d0498aad782c7","impliedFormat":99},{"version":"d6300bb90d031832e5a62d7cad4cf00add5cce9f5d4f0ac514722f41b1af6f92","impliedFormat":99},{"version":"244c16ce21d66faeaca8296e9f4cf5dd79f2c64d9248d7ff06b7c5377684c7ac","impliedFormat":99},{"version":"31fd7c12f6e27154efb52a916b872509a771880f3b20f2dfd045785c13aa813f","impliedFormat":99},{"version":"b481de4ab5379bd481ca12fc0b255cdc47341629a22c240a89cdb4e209522be2","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"4e258d11c899cb9ff36b4b5c53df59cf4a5ccae9a9931529686e77431e0a3518","affectsGlobalScope":true,"impliedFormat":99},{"version":"acfb723d81eda39156251aed414c553294870bf53062429ebfcfba8a68cb4753","impliedFormat":99},{"version":"324ac98294dab54fbd580c7d0e707d94506d7b2c3d5efe981a8495f02cf9ad96","impliedFormat":99},{"version":"9ec72eb493ff209b470467e24264116b6a8616484bca438091433a545dfba17e","impliedFormat":99},{"version":"c35b8117804c639c53c87f2c23e0c786df61d552e513bd5179f5b88e29964838","impliedFormat":99},{"version":"ac3d263474022e9a14c43f588f485d549641d839b159ecc971978b90f34bdf6b","impliedFormat":99},{"version":"67acaedb46832d66c15f1b09fb7b6a0b7f41bdbf8eaa586ec70459b3e8896eb9","impliedFormat":99},{"version":"2c2aee81ffcfc4043d5cbe3f4e9cfc355702696daa0c1e048f28ebe238439888","impliedFormat":99},{"version":"bcbd3becd08b4515225880abea0dbfbbf0d1181ce3af8f18f72f61edbe4febfb","impliedFormat":99},{"version":"36cddcef8f1a4a573c9993c9d3cf3e0f86f948276c287c235b04cfac661c2f9f","impliedFormat":99},{"version":"4d9a1d2160e70b68e5a8038b1cbb8070417e8f8117a7f486ca533330a1bf58a2","impliedFormat":99},{"version":"213a00d511892898e9dad3c98efe3b1de230f171b9e91496faca3e40e27ef6a7","impliedFormat":99},{"version":"62486ec77ac020b82d5a65a270096bb7f2a1fd0627a89f29c5a5d3cbd6bd1f59","impliedFormat":99},{"version":"c637a793905f02d354b640fae41a6ae79395ed0d77fbb87c36d9664ecbd95ac1","impliedFormat":99},{"version":"437b7613a30a2fcde463f7b707c6d5567a8823fbc51de50b8641bf5b1d126fad","impliedFormat":99},{"version":"21dff8020ae0329f8620a144429971a0fd21f4b307e453955181381441904ac8","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"6987dfb4b0c4e02112cc4e548e7a77b3d9ddfeffa8c8a2db13ceac361a4567d9","impliedFormat":99},{"version":"a534e61c2f06a147d97aebad720db97dffd8066b7142212e46bcbcdcb640b81a","impliedFormat":99},{"version":"ddf569d04470a4d629090d43a16735185001f3fcf0ae036ead99f2ceab62be48","impliedFormat":99},{"version":"b413fbc6658fe2774f8bf9a15cf4c53e586fc38a2d5256b3b9647da242c14389","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"37159bf2f7c374599d2bae28d629929003869720bcd6df3ae850bbbca72f23a4","impliedFormat":99},{"version":"63ea959e28c110923f495576e614fb8b36c09b6828b467b2c7cd7f03b03ccf9f","impliedFormat":99},{"version":"1601a95dbb33059fc3d12638ed2a9aecff899e339c5c0f3a0b28768866d385b4","impliedFormat":99},{"version":"a8dd232837b1d83f76a47a5193c1afd9e17b9bf352cb84345f86f7759ee346d0","impliedFormat":99},{"version":"34a7b7fd9007a9caedb079bad794806cb79a4640e0ad199589c56097bd5d9c01","impliedFormat":99},{"version":"7cd92bc6808967f175063fce65f8d1d60a07d752b79ed20aa2eba9a2b8ed0820","impliedFormat":99},{"version":"6308e8e434fc631f436e512399dfcab54c37684bc51ddd2c85a2e9d05dd1e77b","impliedFormat":99},{"version":"84e977ab810a75dc103b38cad1e019d3ab8f4ec9042b8f09f117b7d18c4076f0","impliedFormat":99},{"version":"af33c9d3e293b9b584b0a00f247ea3e3be2fd740a06757e6315513dcfe26c930","impliedFormat":99},{"version":"2b1795d67cf9aa20167c72d795d4db6badea9cea646d9181869b5ca08c778479","impliedFormat":99},{"version":"45f770f2ae71acc1cacfac137f50911e1a004ccba52b2b55c4432c0d4bd97814","impliedFormat":99},{"version":"8124828a11be7db984fcdab052fd4ff756b18edcfa8d71118b55388176210923","impliedFormat":99},{"version":"b62006bbc815fe8190c7aee262aad6bff993e3f9ade70d7057dfceab6de79d2f","impliedFormat":99},{"version":"e2f43cbcdfa32da3bb01d55ab6b8c9587b866f6bc89dadb379c8ad2d455c2643","impliedFormat":99},{"version":"5f6ebe9fb69d3f0d499b0d3a43dbbf98685609e8b09827452ef524a68cef1549","impliedFormat":99},{"version":"218bff92c7f75571ff222bf186419c9b44bc1b712e20c085840b3fb14af824f9","impliedFormat":99},{"version":"7bbff6783e96c691a41a7cf12dd5486b8166a01b0c57d071dbcfca55c9525ec4","impliedFormat":99},{"version":"632711a108accd4533a327ac3b60d0518fa536dfdc2d9e146222c16aa348506e","impliedFormat":99},{"version":"81b5530dfc39e1946dd771dd5526b84710e11d6a371812b1dc2e8eef9e12aa61","impliedFormat":99},{"version":"a393049d61c350e46fcf77f7a55b1751ece77e01fc16044965dc1af2390dc98e","impliedFormat":99},{"version":"0450af186e70c7720f704b2705e11ea08dd7523fc7e3e752d775f6a5211b6682","impliedFormat":99},{"version":"b34209befaf07b7cc1932e5cc137ce121cbc9f892551126962d9e908be91adb4","impliedFormat":1},{"version":"32727845ab5bd8a9ef3e4844c567c09f6d418fcf0f90d381c00652a6f23e7f6e","impliedFormat":1},{"version":"2c0b5ace721ddf7314b622bbad664a9958cfd1068422dbed5cdb760cba1c7f0c","impliedFormat":1},{"version":"7a8ec10b0834eb7183e4bfcd929838ac77583828e343211bb73676d1e47f6f01","impliedFormat":1},{"version":"b07f64ff6ec710998a62b07377fbda0ab4c313ba1f0055bfe9faa22cffedd47c","impliedFormat":1},{"version":"a6be4724bb869268948891342e6dea556e71e6722ca1b1cecac4bee2d9fc6abb","impliedFormat":99},"6b76cec5925db78e8615bd3c8b2eb216389cc969ee2c333b2e4275e2ec589e46",{"version":"3d450fc2fc7b0f10b6a9493a682769aac34f8b612f0f5d1bc4845a7b0bfe9f0a","affectsGlobalScope":true},"c025522e23ed0c5db6b7bfb724fd75393bb15f15703d2d06543c17276f38eb9a","84d77d3757439e320b45bfea31cafc971bb3387e4647f351b7bcc1c5759c0419",{"version":"c57b441e0c0a9cbdfa7d850dae1f8a387d6f81cbffbc3cd0465d530084c2417d","impliedFormat":99},{"version":"4fb69178071e2eeb7c0383b37c0825f79a806afdf1ad33e371f82e3bb7a1cc33","impliedFormat":99},"ddae4030828246e47b124e5677121abea40bac701c0d1f49b5fc1132749ae09b","4ad16d32ec2acd0ead0b95e3ede7bb0e2ab9ce5348d3129a04a8b3b729bfff02","5a66bcc822874a3180da81ab3db3aec15750f424963716988ba458df80558917",{"version":"87f287f296f3ff07dbd14ea7853c2400d995dccd7bd83206196d6c0974774e96","impliedFormat":1},{"version":"dc3b172ee27054dbcedcf5007b78c256021db936f6313a9ce9a3ecbb503fd646","impliedFormat":1},{"version":"ae77d81a5541a8abb938a0efedf9ac4bea36fb3a24cc28cfa11c598863aba571","impliedFormat":1},{"version":"03f1d83d61696326ea29c8a1c15cbaccf61e92598d53f2ccae06078531f42448","impliedFormat":1},{"version":"2c8e55457aaf4902941dfdba4061935922e8ee6e120539c9801cd7b400fae050","impliedFormat":1},{"version":"3a9313fe5ace558b8b18e85f931da10b259e738775f411c061e5f15787b138eb","impliedFormat":1},{"version":"670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","impliedFormat":1},{"version":"9e0cf651e8e2c5b9bebbabdff2f7c6f8cedd91b1d9afcc0a854cdff053a88f1b","impliedFormat":1},{"version":"069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","impliedFormat":1},{"version":"104c67f0da1bdf0d94865419247e20eded83ce7f9911a1aa75fc675c077ca66e","impliedFormat":1},{"version":"cc0d0b339f31ce0ab3b7a5b714d8e578ce698f1e13d7f8c60bfb766baeb1d35c","impliedFormat":1},{"version":"42baf4ca38c38deaf411ea73f37bc39ff56c6e5c761a968b64ac1b25c92b5cd8","impliedFormat":1},{"version":"d7dbe0ad36bdca8a6ecf143422a48e72cc8927bab7b23a1a2485c2f78a7022c6","impliedFormat":1},{"version":"8718fa41d7cf4aa91de4e8f164c90f88e0bf343aa92a1b9b725a9c675c64e16b","impliedFormat":1},{"version":"f992cd6cc0bcbaa4e6c810468c90f2d8595f8c6c3cf050c806397d3de8585562","impliedFormat":1},{"version":"b8d8a69d95a2a0c585b6c0d4661d625d2449149525c22ff0bc1f58a8238f5bc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"f0c3a51c7314523b169d4756b2df6e3e59a3f0d9bc4848248362edaf75b5d315","impliedFormat":1},{"version":"7578ed901eec5f590609fc7a6ba9027be5735ad1aedef119aa56d53a22dfbe02","impliedFormat":1},{"version":"0504070e7eaba788f5d0d5926782ed177f1db01cee28363c488fae94950c0bbc","impliedFormat":1},{"version":"d3f2d715f57df3f04bf7b16dde01dec10366f64fce44503c92b8f78f614c1769","impliedFormat":1},{"version":"b78cd10245a90e27e62d0558564f5d9a16576294eee724a59ae21b91f9269e4a","impliedFormat":1},{"version":"2f5747b1508ccf83fad0c251ba1e5da2f5a30b78b09ffa1cfaf633045160afed","impliedFormat":1},{"version":"21e0b438a5e837907407bcb5bc9cd375b05e05fba21958d0eae50b66834a5c2d","affectsGlobalScope":true,"impliedFormat":1},{"version":"b71c603a539078a5e3a039b20f2b0a0d1708967530cf97dec8850a9ca45baa2b","impliedFormat":1},{"version":"0e13570a7e86c6d83dd92e81758a930f63747483e2cd34ef36fcdb47d1f9726a","impliedFormat":1},{"version":"5c45abf1e13e4463eacfd5dedda06855da8748a6a6cb3334f582b52e219acc04","impliedFormat":1},{"version":"6847334317c1bc1e6fc4b679b0095bbd2b6ee3b85fe3f26fc26bac462f68ef5e","impliedFormat":1},{"version":"2224f3072e3cc07906eeed5c71746779511fba2dd224addc5489bcdb489bdee5","impliedFormat":1},{"version":"785b9d575b49124ce01b46f5b9402157c7611e6532effa562ac6aebec0074dfc","impliedFormat":1},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"a4a39b5714adfcadd3bbea6698ca2e942606d833bde62ad5fb6ec55f5e438ff8","impliedFormat":1},{"version":"bbc1d029093135d7d9bfa4b38cbf8761db505026cc458b5e9c8b74f4000e5e75","impliedFormat":1},{"version":"dfcd866c260978314882d091e31a7b07773d9d726523112081e458fd2758a26b","impliedFormat":1},{"version":"9d4073b672a0fa8bad4de924b66e6610f2dd2206e3132d1a79f3cc6800d804a0","impliedFormat":1},{"version":"67e39a466cfb0e07507f1431c983f6dc1b6739436216f9a55c075ee82a68b130","impliedFormat":1},{"version":"1f68ab0e055994eb337b67aa87d2a15e0200951e9664959b3866ee6f6b11a0fe","impliedFormat":1},{"version":"037817934c90357f71eea18fb643b1fd7e6763fec011f5da1f0fb17acad09d62","impliedFormat":1},{"version":"fc235bce306cfc1b1a1a0848d551501709389ecd8fa12baa6bc156904763315a","impliedFormat":1},{"version":"55caa9dcb8c8d260e424e69a723bd40abc4ea25824f485b4e03651b2540f2f5d","impliedFormat":1},{"version":"49d41b881040e728bc28d463806cdff98b64c69e9da721adcf0ec34345f691b5","impliedFormat":1},{"version":"0623c302651761724ec920bb95a27d9d47ea71f7e6ef7e4d6f60bd05c86cf50c","impliedFormat":1},{"version":"b32b6bcb77993c29a12335096b2000dae9028a425e15e8fdc8ce4c24c67bd9a5","impliedFormat":1},{"version":"afc87a77703487af971af992374f59a6cc729411cd8498a492eb14cce49f092b","impliedFormat":1},{"version":"041717f80688c47a942e0275a387609f6d029709b8054a9cfc78a6d338fd6511","impliedFormat":1},{"version":"a13b9bb3e49bc162bb03870f3409474c58bb04a5e60618c305c7842f8a7b251c","impliedFormat":1},{"version":"963d59066dd6742da1918a6213a209bcc205b8ee53b1876ee2b4e6d80f97c85e","impliedFormat":1},{"version":"fd326577c62145816fe1acc306c734c2396487f76719d3785d4e825b34540b33","impliedFormat":1},{"version":"afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","impliedFormat":1},{"version":"89121c1bf2990f5219bfd802a3e7fc557de447c62058d6af68d6b6348d64499a","impliedFormat":1},{"version":"79b4369233a12c6fa4a07301ecb7085802c98f3a77cf9ab97eee27e1656f82e6","impliedFormat":1},{"version":"36a2e4c9a67439aca5f91bb304611d5ae6e20d420503e96c230cf8fcdc948d94","affectsGlobalScope":true,"impliedFormat":1},{"version":"8a8eb4ebffd85e589a1cc7c178e291626c359543403d58c9cd22b81fab5b1fb9","impliedFormat":1},{"version":"1d0efd1dd185c3dd81eac37f6a393b34bfbf569e55117f5d6c5006c91cb4b68f","impliedFormat":1},{"version":"b2d0630483bf337ef9dac326c3334a245aa4946e9f60f12baf7da5be44beafbb","impliedFormat":1},{"version":"ee65fe452abe1309389c5f50710f24114e08a302d40708101c4aa950a2a7d044","impliedFormat":1},{"version":"fbf802b3a028f5eb22ad406ee5fc7c368f0acfd3a2a6d0f805120766f5717ec8","impliedFormat":1},{"version":"6cb35d83d21a7e72bd00398c93302749bcd38349d0cc5e76ff3a90c6d1498a4d","impliedFormat":1},{"version":"369dd7668d0e6c91550bce0c325f37ce6402e5dd40ecfca66fbb5283e23e559d","affectsGlobalScope":true,"impliedFormat":1},{"version":"2632057d8b983ee33295566088c080384d7d69a492bc60b008d6a6dfd3508d6b","impliedFormat":1},{"version":"4bf71cf2a94492fc71e97800bdf2bcb0a9a0fa5fce921c8fe42c67060780cbfa","impliedFormat":1},{"version":"0996ff06f64cb05b6dac158a6ada2e16f8c2ccd20f9ff6f3c3e871f1ba5fb6d9","impliedFormat":1},{"version":"5c492d01a19fea5ebfff9d27e786bc533e5078909521ca17ae41236f16f9686a","impliedFormat":1},{"version":"a6ee930b81c65ec79aca49025b797817dde6f2d2e9b0e0106f0844e18e2cc819","impliedFormat":1},{"version":"84fce15473e993e6b656db9dd3c9196b80f545647458e6621675e840fd700d29","impliedFormat":1},{"version":"7d5336ee766aa72dffb1cc2a515f61d18a4fb61b7a2757cbccfb7b286b783dfb","impliedFormat":1},{"version":"63e96248ab63f6e7a86e31aa3e654ed6de1c3f99e3b668e04800df05874e8b77","impliedFormat":1},{"version":"80da0f61195385d22b666408f6cccbc261c066d401611a286f07dfddf7764017","impliedFormat":1},{"version":"06a20cc7d937074863861ea1159ac783ff97b13952b4b5d1811c7d8ab5c94776","impliedFormat":1},{"version":"ab6de4af0e293eae73b67dad251af097d7bcc0b8b62de84e3674e831514cb056","impliedFormat":1},{"version":"18cbd79079af97af66c9c07c61b481fce14a4e7282eca078c474b40c970ba1d0","impliedFormat":1},{"version":"e7b45405689d87e745a217b648d3646fb47a6aaba9c8d775204de90c7ea9ff35","impliedFormat":1},{"version":"669b754ec246dd7471e19b655b73bda6c2ca5bb7ccb1a4dff44a9ae45b6a716a","impliedFormat":1},{"version":"bcfaca4a8ff50f57fd36df91fba5d34056883f213baff7192cbfc4d3805d2084","impliedFormat":1},{"version":"76a564b360b267502219a89514953058494713ee0923a63b2024e542c18b40e5","impliedFormat":1},{"version":"8f62cbd3afbd6a07bb8c934294b6bfbe437021b89e53a4da7de2648ecfc7af25","impliedFormat":1},{"version":"a20629551ed7923f35f7556c4c15d0c8b2ebe7afaa68ceaab079a1707ba64be2","impliedFormat":1},{"version":"d6de66600c97cd499526ddecea6e12166ab1c0e8d9bf36fb2339fd39c8b3372a","impliedFormat":1},{"version":"8e7a5b8f867b99cc8763c0b024068fb58e09f7da2c4810c12833e1ca6eb11c4f","impliedFormat":1},{"version":"a8932876de2e3138a5a27f9426b225a4d27f0ba0a1e2764ba20930b4c3faf4b9","impliedFormat":1},{"version":"df877050b04c29b9f8409aa10278d586825f511f0841d1ec41b6554f8362092b","impliedFormat":1},{"version":"027d600e00c5f5e1816c207854285d736f2f5fa28276e2829db746d5d6811ba1","impliedFormat":1},{"version":"5443113a16ef378446e08d6500bb48b35de582426459abdb5c9704f5c7d327d9","impliedFormat":1},{"version":"0fb581ecb53304a3c95bb930160b4fa610537470cce850371cbaad5a458ca0d9","impliedFormat":1},{"version":"7da4e290c009d7967343a7f8c3f145a3d2c157c62483362183ba9f637a536489","impliedFormat":1},{"version":"eb21ddc3a8136a12e69176531197def71dc28ffaf357b74d4bf83407bd845991","impliedFormat":1},{"version":"914560d0c4c6aa947cfe7489fe970c94ba25383c414bbe0168b44fd20dbf0df4","impliedFormat":1},{"version":"4fb3405055b54566dea2135845c3a776339e7e170d692401d97fd41ad9a20e5d","impliedFormat":1},{"version":"8d607832a6ef0eac30657173441367dd76c96bf7800d77193428b922e060c3af","impliedFormat":1},{"version":"20ff7207f0bb5cdde5fee8e83315ade7e5b8100cfa2087d20d39069a3d7d06f4","impliedFormat":1},{"version":"7ca4c534eab7cff43d81327e369a23464bc37ef38ce5337ceff24a42c6c84eb2","impliedFormat":1},{"version":"5252dec18a34078398be4e321dee884dc7f47930e5225262543a799b591b36d2","impliedFormat":1},{"version":"23caed4dff98bd28157d2b798b43f1dfefe727f18641648c01ce4e0e929a1630","impliedFormat":1},{"version":"f67e013d5374826596d7c23dbae1cdb14375a27cd72e16c5fb46a4b445059329","impliedFormat":1},{"version":"ea3401b70e2302683bbf4c18b69ef2292b60f4d8f8e6d920413b81fb7bde0f65","impliedFormat":1},{"version":"71afe26642c0fb86b9f8b1af4af5deb5181b43b6542a3ff2314871b53d04c749","impliedFormat":1},{"version":"0d7f01634e6234d84cf0106508efdb8ae00e5ed126eff9606d37b031ac1de654","impliedFormat":1},{"version":"f8d209086bad78af6bd7fef063c1ed449c815e6f8d36058115f222d9f788b848","impliedFormat":1},{"version":"3ad003278d569d1953779e2f838f7798f02e793f6a1eceac8e0065f1a202669b","impliedFormat":1},{"version":"fb2c5eceffcd918dbb86332afa0199f5e7b6cf6ee42809e930a827b28ef25afe","impliedFormat":1},{"version":"f664aaff6a981eeca68f1ff2d9fd21b6664f47bf45f3ae19874df5a6683a8d8a","impliedFormat":1},{"version":"ce066f85d73e09e9adbd0049bcf6471c7eefbfc2ec4b5692b5bcef1e36babd2a","impliedFormat":1},{"version":"09d302513cacfbcc54b67088739bd8ac1c3c57917f83f510b2d1adcb99fd7d2a","impliedFormat":1},{"version":"3faa54e978b92a6f726440c13fe3ab35993dc74d697c7709681dc1764a25219f","impliedFormat":1},{"version":"2bd0489e968925eb0c4c0fb12ef090be5165c86bd088e1e803102c38d4a717d8","impliedFormat":1},{"version":"88924207132b9ba339c1adb1ed3ea07e47b3149ff8a2e21a3ea1f91cee68589d","impliedFormat":1},{"version":"b8800b93d8ab532f8915be73f8195b9d4ef06376d8a82e8cdc17c400553172d6","impliedFormat":1},{"version":"d7d469703b78beba76d511957f8c8b534c3bbb02bea7ab4705c65ef573532fb8","impliedFormat":1},{"version":"74c8c3057669c03264263d911d0f82e876cef50b05be21c54fef23c900de0420","impliedFormat":1},{"version":"b303eda2ff2d582a9c3c5ecb708fb57355cdc25e8c8197a9f66d4d1bf09fda19","impliedFormat":1},{"version":"4e5dc89fa22ff43da3dee1db97d5add0591ebaff9e4adef6c8b6f0b41f0f60f0","impliedFormat":1},{"version":"ec4e82cb42a902fe83dc13153c7a260bee95684541f8d7ef26cb0629a2f4ca31","impliedFormat":1},{"version":"5f36e24cd92b0ff3e2a243685a8a780c9413941c36739f04b428cc4e15de629d","impliedFormat":1},{"version":"40a26494e6ab10a91851791169582ab77fed4fbd799518968177e7eefe08c7a9","impliedFormat":1},{"version":"208e125b45bc561765a74f6f1019d88e44e94678769824cf93726e1bac457961","impliedFormat":1},{"version":"b3985971de086ef3aa698ef19009a53527b72e65851b782dc188ac341a1e1390","impliedFormat":1},{"version":"c81d421aabb6113cd98b9d4f11e9a03273b363b841f294b457f37c15d513151d","impliedFormat":1},{"version":"30063e3a184ff31254bbafa782c78a2d6636943dfe59e1a34f451827fd7a68dc","impliedFormat":1},{"version":"c05d4cae0bceed02c9d013360d3e65658297acb1b7a90252fe366f2bf4f9ccc9","impliedFormat":1},{"version":"6f14b92848889abba03a474e0750f7350cc91fc190c107408ca48679a03975ae","impliedFormat":1},{"version":"a588d0765b1d18bf00a498b75a83e095aef75a9300b6c1e91cbf39e408f2fe2f","impliedFormat":1},{"version":"08323a8971cb5b2632b532cba1636ad4ca0d76f9f7d0b8d1a0c706fdf5c77b45","impliedFormat":1},{"version":"5d2651c679f59706bf484e7d423f0ec2d9c79897e2e68c91a3f582f21328d193","impliedFormat":1},{"version":"30d49e69cb62f350ff0bc5dda1c557429c425014955c19c557f101c0de9272e7","impliedFormat":1},{"version":"d3747dbed45540212e9a906c2fb8b5beb691f2cd0861af58a66dc01871004f38","impliedFormat":1},{"version":"05a21cbb7cbe1ec502e7baca1f4846a4e860d96bad112f3e316b995ba99715b7","impliedFormat":1},{"version":"1eaee2b52f1c0e1848845a79050c1d06ae554d8050c35e3bf479f13d6ee19dd5","impliedFormat":1},{"version":"fd219904eea67c470dfebbaf44129b0db858207c3c3b55514bdc84de547b1687","impliedFormat":1},{"version":"4de232968f584b960b4101b4cdae593456aff149c5d0c70c2389248e9eb9fbac","impliedFormat":1},{"version":"933c42f6ed2768265dfb42faa817ce8d902710c57a21a1859a9c3fe5e985080e","impliedFormat":1},{"version":"c5430542eeebb207d651e8b00a08e4bb680c47ecb73dd388d8fa597a1fc5de5b","impliedFormat":1},{"version":"a6c5c9906262cf10549989c0061e5a44afdc1f61da77d5e09418a9ecea0018fe","impliedFormat":1},{"version":"bc6e433cb982bf63eaa523dbbbd30fe12960a09861b352d77baf77ad6dd8886d","impliedFormat":1},{"version":"9af64ab00918f552388252977c1569fe31890686ca1fdb8e20f58d3401c9a50c","impliedFormat":1},{"version":"3d3cc03b5c6e056c24aac76789f4bc67caee98a4f0774ab82bc8ba34d16be916","impliedFormat":1},{"version":"747ce36fa27a750a05096f3610e59c9b5a55e13defec545c01a75fd13d67b620","impliedFormat":1},{"version":"1a8f503c64bdb36308f245960d9e4acac4cf65d8b6bd0534f88230ebf0be7883","impliedFormat":1},{"version":"a2c1f4012459547d62116d724e7ec820bb2e6848da40ea0747bf160ffd99b283","impliedFormat":1},{"version":"0dc197e52512a7cbea4823cc33c23b0337af97bd59b38bf83be047f37cd8c9a8","impliedFormat":1},{"version":"492c93ade227fe4545fabb3035b9dd5d57d8b4fde322e5217fdaef20aa1b80a8","impliedFormat":1},{"version":"83c54a3b3e836d1773b8c23ff76ce6e0aae1a2209fc772b75e9de173fec9eac0","impliedFormat":1},{"version":"475e411f48f74c14b1f6e50cc244387a5cc8ce52340dddfae897c96e03f86527","impliedFormat":1},{"version":"5573ce7aa683a81c9a727294ffdb47d82d7715a148bfe9f4ddcf2f6cdfef1f0a","impliedFormat":1},{"version":"2cd9edbb4a6411a9f5258237dd73323db978d7aa9ebf1d1b0ac79771ac233e24","impliedFormat":1},{"version":"0112a7f3c11fc4792e70f5d0d5c9f80ee6a1c5c548723714433da6a03307e87b","impliedFormat":1},{"version":"035a5df183489c2e22f3cf59fc1ed2b043d27f357eecc0eb8d8e840059d44245","impliedFormat":1},{"version":"a4809f4d92317535e6b22b01019437030077a76fec1d93b9881c9ed4738fcc54","impliedFormat":1},{"version":"5f53fa0bd22096d2a78533f94e02c899143b8f0f9891a46965294ee8b91a9434","impliedFormat":1},{"version":"7a1dd1e9c8bf5e23129495b10718b280340c7500570e0cfe5cffcdee51e13e48","impliedFormat":1},{"version":"abd6ccdaae9905ea2ec85488fdce744930862327633eebd40d429511f6a1d5da","impliedFormat":1},{"version":"fec943fdb3275eb6e006b35e04a8e2e99e9adf3f4b969ddf15315ac7575a93e4","impliedFormat":1},{"version":"7e8d3f08435ad2cefe67f58182618bfc9a0a29db08cf2544b94cbcae754a9bd9","impliedFormat":1},{"version":"8cf9b9045a614f883b623c2f1a631ec6a93321747e933330b2eec0ee47164a34","impliedFormat":1},{"version":"492e71f8f8f44a968be333ea1bd4e761b020216a380b5b3b213b06a9aecdfbf4","impliedFormat":1},{"version":"ceeb65c57fe2a1300994f095b5e5c7c5eae440e9ce116d32a3b46184ab1630ec","impliedFormat":1},{"version":"f90d4c1ae3af9afb35920b984ba3e41bdd43f0dc7bae890b89fbd52b978f0cac","impliedFormat":1},{"version":"fcf79300e5257a23ed3bacaa6861d7c645139c6f7ece134d15e6669447e5e6db","impliedFormat":1},{"version":"187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","impliedFormat":1},{"version":"aa2c18a1b5a086bbcaae10a4efba409cc95ba7287d8cf8f2591b53704fea3dea","impliedFormat":1},{"version":"b88749bdb18fc1398370e33aa72bc4f88274118f4960e61ce26605f9b33c5ba2","impliedFormat":1},{"version":"0aaef8cded245bf5036a7a40b65622dd6c4da71f7a35343112edbe112b348a1e","impliedFormat":1},{"version":"00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","impliedFormat":1},{"version":"bdf0ed7d9ebae6175a5d1b4ec4065d07f8099379370a804b1faff05004dc387d","impliedFormat":1},{"version":"7c14ccd2eaa82619fffc1bfa877eb68a012e9fb723d07ee98db451fadb618906","impliedFormat":1},{"version":"288d992cd0d35fd4bb5a0f23df62114b8bfbc53e55b96a4ad00dde7e6fb72e31","impliedFormat":1},{"version":"df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9","impliedFormat":1},{"version":"4f6a12044ee6f458db11964153830abbc499e73d065c51c329ec97407f4b13dd","impliedFormat":1},{"version":"85d3aa95b0086752d2f7784d2bdaeb38f99c3cf6c35bee861702beb68556cb9e","impliedFormat":1},{"version":"a995fd088f0ad2dea9c0835584ac467c77a29db6dc77120d78b38400e1df1a36","impliedFormat":1},{"version":"89121c1bf2990f5219bfd802a3e7fc557de447c62058d6af68d6b6348d64499a","impliedFormat":1},{"version":"d4a22007b481fe2a2e6bfd3a42c00cd62d41edb36d30fc4697df2692e9891fc8","impliedFormat":1},{"version":"736097ddbb2903bef918bb3b5811ef1c9c5656f2a73bd39b22a91b9cc2525e50","impliedFormat":1},{"version":"4340936f4e937c452ae783514e7c7bbb7fc06d0c97993ff4865370d0962bb9cf","impliedFormat":1},{"version":"b70c7ea83a7d0de17a791d9b5283f664033a96362c42cc4d2b2e0bdaa65ef7d1","impliedFormat":1},{"version":"280f9c3734dd3fdcce909988dc312eff90bd7753a22e496f237a3bfb1b8be96c","impliedFormat":1},{"version":"22293bd6fa12747929f8dfca3ec1684a3fe08638aa18023dd286ab337e88a592","impliedFormat":1},{"version":"916be7d770b0ae0406be9486ac12eb9825f21514961dd050594c4b250617d5a8","impliedFormat":1},{"version":"e195b919d24087d178526e00fdb26e8a6060bc7820c021eb8777483c9163dcdc","impliedFormat":1},{"version":"6ab263df6465e2ed8f1d02922bae18bb5b407020767de021449a4c509859b22e","impliedFormat":1},{"version":"6805621d9f970cda51ab1516e051febe5f3ec0e45b371c7ad98ac2700d13d57c","impliedFormat":1},{"version":"65ff5a0aefd7817a03c1ad04fee85c9cdd3ec415cc3c9efec85d8008d4d5e4ee","impliedFormat":1},{"version":"37da3671586f0270f6b0772348f39a6e637a0ca9faf2a5dba0791df74ae8de6b","impliedFormat":1},{"version":"36a2e4c9a67439aca5f91bb304611d5ae6e20d420503e96c230cf8fcdc948d94","affectsGlobalScope":true,"impliedFormat":1},{"version":"b89c2ddec6bd955e8721d41e24ca667de06882338d88b183c2cdc1f41f4c5a34","affectsGlobalScope":true,"impliedFormat":1},{"version":"17ed71200119e86ccef2d96b73b02ce8854b76ad6bd21b5021d4269bec527b5f","impliedFormat":1},{"version":"1d0efd1dd185c3dd81eac37f6a393b34bfbf569e55117f5d6c5006c91cb4b68f","impliedFormat":1},{"version":"d93476b774279834b479a824430c296b5d2b913e534a9d163f2e20f6b5f7ae04","impliedFormat":1},{"version":"960a68ced7820108787135bdae5265d2cc4b511b7dcfd5b8f213432a8483daf1","impliedFormat":1},{"version":"7c52a6d05a6e68269e63bc63fad6e869368a141ad23a20e2350c831dc499c5f2","impliedFormat":1},{"version":"2e7ebdc7d8af978c263890bbde991e88d6aa31cc29d46735c9c5f45f0a41243b","impliedFormat":1},{"version":"b57fd1c0a680d220e714b76d83eff51a08670f56efcc5d68abc82f5a2684f0c0","impliedFormat":1},{"version":"8cf121e98669f724256d06bebafec912b92bb042a06d4944f7fb27a56c545109","impliedFormat":1},{"version":"1084565c68b2aed5d6d5cea394799bd688afdf4dc99f4e3615957857c15bb231","impliedFormat":1},{"version":"8baa5d0febc68db886c40bf341e5c90dc215a90cd64552e47e8184be6b7e3358","impliedFormat":1},{"version":"857f9cda624606f2bfb1cb758d47499edfbee66b2cf46804f54620bd430ea26f","impliedFormat":1},{"version":"ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","impliedFormat":1},{"version":"480ffa66827143d60025514f0d979f7bc790024821e5ecc12967ce13a7e3e08a","impliedFormat":1},{"version":"908217c4f2244ec402b73533ebfcc46d6dcd34fc1c807ff403d7f98702abb3bc","impliedFormat":1},{"version":"15fe687c59d62741b4494d5e623d497d55eb38966ecf5bea7f36e48fc3fbe15e","impliedFormat":1},{"version":"2c3b8be03577c98530ef9cb1a76e2c812636a871f367e9edf4c5f3ce702b77f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d09838b65c3c780513878793fc394ae29b8595d9e4729246d14ce69abc71140","impliedFormat":1},{"version":"61f41da9aaa809e5142b1d849d4e70f3e09913a5cb32c629bf6e61ef27967ff7","impliedFormat":1},{"version":"bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","impliedFormat":1},{"version":"26a770cec4bd2e7dbba95c6e536390fffe83c6268b78974a93727903b515c4e7","impliedFormat":1}],"root":[[297,362],[434,437],[440,442]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declarationMap":true,"esModuleInterop":true,"module":99,"rewriteRelativeImportExtensions":true,"skipLibCheck":true,"sourceMap":true,"strict":true,"target":99,"verbatimModuleSyntax":true},"referencedMap":[[297,1],[298,2],[299,3],[300,4],[301,4],[302,4],[303,4],[304,4],[305,4],[306,4],[307,4],[308,4],[309,4],[310,4],[311,4],[312,4],[313,4],[314,4],[315,4],[316,4],[317,4],[318,4],[319,4],[320,4],[321,4],[322,4],[323,4],[324,4],[325,4],[326,4],[327,4],[328,4],[329,4],[330,4],[331,4],[332,4],[333,4],[334,4],[335,4],[336,4],[337,4],[338,4],[339,4],[340,4],[341,4],[342,4],[343,4],[344,4],[345,4],[346,4],[347,4],[348,4],[349,4],[350,4],[351,4],[352,4],[353,4],[354,4],[355,4],[356,4],[357,4],[358,4],[359,4],[360,4],[361,4],[362,4],[296,5],[244,6],[245,7],[364,6],[149,8],[150,8],[151,9],[105,10],[152,11],[153,12],[154,13],[100,14],[103,15],[101,14],[102,14],[155,16],[156,17],[157,18],[158,19],[159,20],[160,21],[161,21],[162,22],[163,23],[164,24],[165,25],[106,14],[104,14],[166,26],[167,27],[168,28],[200,29],[169,30],[170,31],[171,32],[172,33],[173,34],[174,35],[175,36],[176,37],[177,38],[178,39],[179,39],[180,40],[181,14],[182,41],[184,42],[183,43],[185,44],[186,45],[187,46],[188,47],[189,48],[190,49],[191,50],[192,51],[193,52],[194,53],[195,54],[196,55],[197,56],[107,14],[108,14],[109,14],[148,57],[198,58],[199,59],[416,60],[433,61],[412,14],[415,62],[427,63],[413,64],[414,65],[381,66],[393,67],[394,68],[392,14],[382,14],[375,69],[371,70],[374,71],[396,72],[383,14],[385,73],[419,73],[384,74],[376,14],[206,14],[404,75],[406,76],[405,77],[403,78],[402,14],[204,79],[203,80],[202,14],[439,81],[91,14],[90,14],[93,82],[81,14],[82,14],[13,14],[14,14],[16,14],[15,14],[2,14],[17,14],[18,14],[19,14],[20,14],[21,14],[22,14],[23,14],[24,14],[3,14],[25,14],[26,14],[4,14],[27,14],[31,14],[28,14],[29,14],[30,14],[32,14],[33,14],[34,14],[5,14],[35,14],[36,14],[37,14],[38,14],[6,14],[42,14],[39,14],[40,14],[41,14],[43,14],[7,14],[44,14],[49,14],[50,14],[45,14],[46,14],[47,14],[48,14],[8,14],[54,14],[51,14],[52,14],[53,14],[55,14],[9,14],[56,14],[57,14],[58,14],[60,14],[59,14],[61,14],[62,14],[10,14],[63,14],[64,14],[65,14],[11,14],[66,14],[67,14],[68,14],[69,14],[70,14],[1,14],[71,14],[72,14],[12,14],[76,14],[74,14],[79,14],[78,14],[73,14],[77,14],[75,14],[80,14],[126,83],[136,84],[125,83],[146,85],[117,86],[116,87],[145,88],[139,89],[144,90],[119,91],[133,92],[118,93],[142,94],[114,95],[113,88],[143,96],[115,97],[120,98],[121,14],[124,98],[111,14],[147,99],[137,100],[128,101],[129,102],[131,103],[127,104],[130,105],[140,88],[122,106],[123,107],[132,108],[112,109],[135,100],[134,98],[138,14],[141,110],[363,6],[99,111],[201,112],[243,113],[205,114],[233,115],[231,116],[232,117],[220,118],[221,116],[228,119],[219,120],[224,121],[234,14],[225,122],[230,123],[236,124],[235,125],[218,126],[226,127],[227,128],[222,129],[229,115],[223,130],[95,112],[94,14],[96,131],[97,14],[98,132],[237,14],[241,133],[216,134],[242,135],[417,136],[423,137],[411,138],[398,139],[391,140],[388,141],[408,14],[389,70],[401,142],[399,143],[421,144],[420,145],[387,146],[400,147],[386,14],[390,148],[422,149],[407,150],[426,151],[418,14],[435,14],[441,152],[440,153],[437,154],[436,14],[442,155],[434,156],[448,157],[446,14],[213,158],[211,14],[212,159],[214,160],[209,161],[207,14],[210,162],[208,163],[255,164],[258,165],[264,166],[267,167],[288,168],[266,169],[247,14],[248,170],[249,171],[252,14],[250,14],[251,14],[289,172],[254,164],[253,14],[290,173],[257,165],[256,14],[294,174],[291,175],[261,176],[263,177],[260,178],[262,179],[259,176],[292,180],[265,164],[293,181],[268,182],[287,183],[284,184],[286,185],[271,186],[278,187],[280,188],[282,189],[281,190],[273,191],[270,184],[274,14],[285,192],[275,193],[272,14],[283,14],[269,14],[276,194],[277,14],[279,195],[246,14],[443,196],[444,14],[445,14],[451,197],[447,157],[449,198],[450,157],[453,199],[457,200],[380,201],[458,88],[460,202],[452,196],[461,14],[295,14],[470,203],[378,14],[478,204],[474,205],[473,206],[471,14],[465,207],[468,208],[479,14],[480,209],[481,210],[485,14],[486,211],[482,212],[483,213],[484,213],[487,214],[489,215],[490,214],[492,216],[491,14],[496,217],[493,14],[495,218],[497,14],[498,14],[455,14],[466,14],[585,219],[564,220],[566,221],[565,220],[568,222],[570,223],[571,224],[572,225],[573,223],[574,224],[575,223],[576,226],[577,224],[578,223],[579,227],[580,228],[581,229],[582,230],[569,231],[583,232],[567,232],[584,233],[562,234],[512,235],[510,235],[561,14],[537,236],[525,237],[505,238],[535,237],[536,237],[539,239],[540,237],[507,240],[541,237],[542,237],[543,237],[544,237],[545,241],[546,242],[547,237],[503,237],[548,237],[549,237],[550,241],[551,237],[552,237],[553,243],[554,237],[555,239],[556,237],[504,237],[557,237],[558,237],[559,244],[502,245],[508,246],[538,247],[511,248],[560,249],[513,250],[514,251],[523,252],[522,253],[518,254],[517,253],[519,255],[516,256],[515,257],[521,258],[520,255],[524,259],[506,260],[501,261],[499,262],[509,14],[500,263],[530,14],[531,14],[528,14],[529,241],[527,14],[532,14],[526,262],[534,14],[533,14],[586,14],[587,14],[588,264],[589,265],[590,14],[472,14],[591,266],[469,14],[592,267],[593,268],[594,269],[595,270],[597,271],[598,272],[596,273],[599,274],[600,275],[601,276],[602,277],[603,278],[604,279],[605,280],[606,281],[607,282],[608,283],[610,284],[612,216],[611,14],[462,14],[488,14],[614,14],[615,285],[613,286],[616,88],[617,14],[618,14],[619,287],[621,288],[620,14],[622,14],[623,14],[459,14],[464,14],[626,289],[628,217],[627,290],[629,14],[631,291],[634,292],[632,289],[630,289],[633,291],[624,14],[625,293],[635,14],[456,286],[463,294],[467,295],[636,14],[637,14],[638,14],[563,287],[639,14],[641,296],[640,14],[642,14],[643,14],[644,14],[645,297],[84,298],[88,299],[85,14],[86,300],[87,301],[373,302],[366,303],[410,304],[368,305],[370,306],[409,14],[395,305],[369,14],[372,303],[367,14],[365,14],[379,14],[110,14],[438,14],[494,14],[477,307],[476,308],[475,14],[89,309],[609,14],[454,310],[238,14],[240,311],[239,14],[92,14],[424,312],[425,313],[432,314],[431,315],[428,14],[429,315],[430,316],[217,14],[215,317],[397,14],[377,14],[83,14]],"affectedFilesPendingEmit":[[441,51],[440,51],[437,51],[436,51],[442,51],[434,51]],"emitSignatures":[434,436,437,440,441,442],"version":"5.9.3"} \ No newline at end of file diff --git a/docs/vite.config.ts b/docs/vite.config.ts new file mode 100644 index 000000000..25ad75937 --- /dev/null +++ b/docs/vite.config.ts @@ -0,0 +1,43 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import tailwindcss from '@tailwindcss/vite'; +import { playwright } from '@vitest/browser-playwright'; +import devtoolsJson from 'vite-plugin-devtools-json'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [tailwindcss(), sveltekit(), devtoolsJson()], + server: { port: 5174 }, + test: { + expect: { requireAssertions: true }, + + projects: [ + { + extends: './vite.config.ts', + + test: { + name: 'client', + + browser: { + enabled: true, + provider: playwright(), + instances: [{ browser: 'chromium', headless: true }] + }, + + include: ['src/**/*.svelte.{test,spec}.{js,ts}'], + exclude: ['src/lib/server/**'] + } + }, + + { + extends: './vite.config.ts', + + test: { + name: 'server', + environment: 'node', + include: ['src/**/*.{test,spec}.{js,ts}'], + exclude: ['src/**/*.svelte.{test,spec}.{js,ts}'] + } + } + ] + } +}); diff --git a/frontend/Dockerfile b/frontend/Dockerfile index a02699a6f..64c224e0d 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -15,6 +15,7 @@ COPY package.json yarn.lock .yarnrc.yml ./ COPY .yarn ./.yarn # To be able to use --immutable, we need to copy all the packages in the workspace COPY backend backend +COPY docs docs COPY frontend frontend COPY packages packages RUN yarn install --immutable diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs index 269db4448..196147bf8 100644 --- a/frontend/eslint.config.mjs +++ b/frontend/eslint.config.mjs @@ -42,13 +42,7 @@ export default [ globals: { ...globals.browser, ...globals.node, - ...globals.jest, - // TODO: Remove these when global types deprecated with @openvaa/data - ElectionProps: 'writable', - PartyProps: 'writable', - CandidateProps: 'writable', - QuestionProps: 'writable', - RankingProps: 'writable' + ...globals.jest }, parser: tsParser, diff --git a/frontend/package.json b/frontend/package.json index 4fec38067..f2a5cbf72 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -29,7 +29,7 @@ "@vitest/coverage-v8": "^2.1.8", "autoprefixer": "^10.4.20", "daisyui": "^4.12.23", - "eslint": "~9.14.0", + "eslint": "^9.39.2", "eslint-plugin-svelte": "^2.46.1", "globals": "^15.14.0", "jest": "^29.7.0", diff --git a/frontend/src/lib/admin/components/jobs/FeatureJobs.svelte b/frontend/src/lib/admin/components/jobs/FeatureJobs.svelte index 7eefffa4a..9a6395432 100644 --- a/frontend/src/lib/admin/components/jobs/FeatureJobs.svelte +++ b/frontend/src/lib/admin/components/jobs/FeatureJobs.svelte @@ -1,3 +1,20 @@ +<!-- +@component +Displays jobs for a specific admin feature, showing active and past job details. + +### Properties + +- `feature`: The admin feature to display jobs for. +- `showFeatureLink`: Whether to show the "Go to Feature" link. Default: `true` +- Any valid attributes of a `<section>` element + +### Usage + +```tsx +<FeatureJobs feature={ADMIN_FEATURE.FactorAnalysis} /> +``` +--> + <script lang="ts"> import { ADMIN_FEATURE } from '$lib/admin/features'; import { Button } from '$lib/components/button'; diff --git a/frontend/src/lib/admin/components/jobs/JobDetails.svelte b/frontend/src/lib/admin/components/jobs/JobDetails.svelte index 30af6caed..8fd489828 100644 --- a/frontend/src/lib/admin/components/jobs/JobDetails.svelte +++ b/frontend/src/lib/admin/components/jobs/JobDetails.svelte @@ -1,3 +1,19 @@ +<!-- +@component +Displays detailed information about a job, including its status, progress, and messages. + +### Properties + +- `job`: The job information to display. +- Any valid attributes of an `<article>` element + +### Usage + +```tsx +<JobDetails job={jobInfo} /> +``` +--> + <script lang="ts"> import { Button } from '$lib/components/button'; import ButtonWithConfirmation from '$lib/components/buttonWithConfirmation/ButtonWithConfirmation.svelte'; diff --git a/frontend/src/lib/admin/components/jobs/WithPolling.svelte b/frontend/src/lib/admin/components/jobs/WithPolling.svelte index 7a2578846..18fc4ab1b 100644 --- a/frontend/src/lib/admin/components/jobs/WithPolling.svelte +++ b/frontend/src/lib/admin/components/jobs/WithPolling.svelte @@ -1,8 +1,16 @@ -<!-- @component - +<!-- +@component A temporary utility component within which jobs are being polled. TODO[Svelte 5]: Count subscriptions to stores (or $states) and automatically start and stop polling. + +### Usage + +```tsx +<WithPolling> + <FeatureJobs feature={ADMIN_FEATURE.FactorAnalysis} /> +</WithPolling> +``` --> <script lang="ts"> diff --git a/frontend/src/lib/admin/components/languageFeatures/LanguageSelector.svelte b/frontend/src/lib/admin/components/languageFeatures/LanguageSelector.svelte index b1d89b0c3..1b7ae1e44 100644 --- a/frontend/src/lib/admin/components/languageFeatures/LanguageSelector.svelte +++ b/frontend/src/lib/admin/components/languageFeatures/LanguageSelector.svelte @@ -1,9 +1,20 @@ -<!--@component -# Language Selector +<!-- +@component +Reusable component for selecting target language for language-based features (question info, argument condensation, etc.). Automatically selected if only one language is available. -Reusable component for selecting target language for language-based features (question info, argument condensation, etc.). -Automatically selected if only one language is available. +### Properties +- `selected`: The selected language value. +- `name`: The name of the form element. Default: `'language'` +- `id`: The id of the select element. +- `options`: The available language options. +- Any valid attributes of a `Select` component + +### Usage + +```tsx +<LanguageSelector bind:selected={targetLanguage} /> +``` --> <script lang="ts"> diff --git a/frontend/src/lib/api/README.md b/frontend/src/lib/api/README.md new file mode 100644 index 000000000..ce0706827 --- /dev/null +++ b/frontend/src/lib/api/README.md @@ -0,0 +1,12 @@ +# Data API + +> See also the online doc [Data API](https://openvaa.org/developers-guide/frontend/data-api) (or [locally](/docs/src/routes/developers-guide/frontend/data-api/+page.md)) + +This directory contains the Data API implementation for the OpenVAA frontend, including: + +- Data providers for reading public data +- Feedback writers for submitting feedback +- Data writers for authenticated data operations +- Multiple implementations (Strapi, API routes, local server) + +See the documentation for detailed information on how the Data API works and how to use it. diff --git a/frontend/src/lib/api/base/dataWriter.type.ts b/frontend/src/lib/api/base/dataWriter.type.ts index 80275c3f5..ebf0c10db 100644 --- a/frontend/src/lib/api/base/dataWriter.type.ts +++ b/frontend/src/lib/api/base/dataWriter.type.ts @@ -42,7 +42,7 @@ export interface DataWriter<TType extends AdapterType = 'universal'> { * Create a candidate with a nomination or nominations and send a registration link. * @param email - Email. * @param nominations - Nominations. - * @access ID token. + * Access: ID token. * @returns A `Promise` resolving to an `DataApiActionResult` object or a `Response` containing one. */ preregisterWithIdToken: (opts: { @@ -64,7 +64,7 @@ export interface DataWriter<TType extends AdapterType = 'universal'> { * @param identifier - Personal identifier such as a birthdate. * @param email - Email. * @param nominations - Nominations. - * @access API token. + * Access: API token. * @returns A `Promise` resolving to an `DataApiActionResult` object or a `Response` containing one. */ preregisterWithApiToken: ( diff --git a/frontend/src/lib/api/utils/auth/getIdTokenClaims.ts b/frontend/src/lib/api/utils/auth/getIdTokenClaims.ts index ebe71a803..fe7c6b0d0 100644 --- a/frontend/src/lib/api/utils/auth/getIdTokenClaims.ts +++ b/frontend/src/lib/api/utils/auth/getIdTokenClaims.ts @@ -4,7 +4,7 @@ import { constants as publicConstants } from '$lib/utils/constants'; export const defaultOptions = { privateEncryptionJWKSet: JSON.parse(constants.IDENTITY_PROVIDER_DECRYPTION_JWKS || '[]'), - publicSignatureJWKSetUri: constants.IDENTITY_PROVIDER_JWKS_URI, + publicSignatureJWKSetUri: constants.IDENTITY_PROVIDER_JWKS_URI!, audience: publicConstants.PUBLIC_IDENTITY_PROVIDER_CLIENT_ID, issuer: constants.IDENTITY_PROVIDER_ISSUER } as const; diff --git a/frontend/src/lib/api/utils/isValidResult.ts b/frontend/src/lib/api/utils/isValidResult.ts index 149e02803..b560db4e1 100644 --- a/frontend/src/lib/api/utils/isValidResult.ts +++ b/frontend/src/lib/api/utils/isValidResult.ts @@ -3,7 +3,7 @@ import type { DPDataType } from '../base/dataTypes'; /** * Checks the result returned by a `DataProvider` get data method, logs the possible error and returning `false` if it is invalid. - * @param options.allowEmpty Whether to allow an empty array as result. Default is `false`. + * @param options.allowEmpty - Whether to allow an empty array as result. Default is `false`. */ export function isValidResult<TData extends keyof DPDataType>( result: DPDataType[TData] | Error | null | undefined, diff --git a/frontend/src/lib/candidate/components/README.md b/frontend/src/lib/candidate/components/README.md new file mode 100644 index 000000000..6aabf0da3 --- /dev/null +++ b/frontend/src/lib/candidate/components/README.md @@ -0,0 +1,7 @@ +# Candidate Components + +> See also the online doc [Components](https://openvaa.org/developers-guide/frontend/components) (or [locally](/docs/src/routes/developers-guide/frontend/components/+page.md)) + +This directory contains components specific to the candidate application used throughout the OpenVAA frontend. + +For auto-generated component documentation, see the [generated components documentation](https://openvaa.org/developers-guide/frontend/components/generated). diff --git a/frontend/src/lib/candidate/components/logoutButton/LogoutButton.svelte b/frontend/src/lib/candidate/components/logoutButton/LogoutButton.svelte index 9ec9c99db..cbd1568c9 100644 --- a/frontend/src/lib/candidate/components/logoutButton/LogoutButton.svelte +++ b/frontend/src/lib/candidate/components/logoutButton/LogoutButton.svelte @@ -8,15 +8,16 @@ Accesses `CandidateContext`. ### Properties -- `logoutModalTimer`:Time in seconds that the logout modal created by the button waits before automatically logging the user out. Defaults to 30 -- `stayOnPage`: boolean deciding if pressing the button takes the user to the login page or not. Defaults to true -- Any valid properties of a `<Button>` component. +- `logoutModalTimer`: The duration in seconds a logout modal will wait before automatically logging the user out. Default: `30` +- `stayOnPage`: Whether pressing the button takes the user to the login page or not. Default: `false` +- Any valid properties of a `Button` component ### Settings - `entities.hideIfMissingAnswers.candidate`: Affects message shown. ### Usage + ```tsx <LogoutButton /> ``` @@ -30,10 +31,10 @@ Accesses `CandidateContext`. import { logDebugError } from '$lib/utils/logger'; import type { LogoutButtonProps } from './LogoutButton.type'; - type $$props = LogoutButtonProps; + type $$Props = LogoutButtonProps; - export let stayOnPage: $$props['stayOnPage'] = false; - export let logoutModalTimer: $$props['logoutModalTimer'] = 30; + export let stayOnPage: $$Props['stayOnPage'] = false; + export let logoutModalTimer: $$Props['logoutModalTimer'] = 30; //////////////////////////////////////////////////////////////////// // Get contexts diff --git a/frontend/src/lib/candidate/components/logoutButton/LogoutButton.type.ts b/frontend/src/lib/candidate/components/logoutButton/LogoutButton.type.ts index 8a638924b..7b4c2bc97 100644 --- a/frontend/src/lib/candidate/components/logoutButton/LogoutButton.type.ts +++ b/frontend/src/lib/candidate/components/logoutButton/LogoutButton.type.ts @@ -6,7 +6,7 @@ export type LogoutButtonProps = Partial<ButtonProps> & { */ logoutModalTimer?: number; /** - * Whether pressing the button takes the user to the login page or not. @default true + * Whether pressing the button takes the user to the login page or not. @default false */ stayOnPage?: boolean; }; diff --git a/frontend/src/lib/candidate/components/logoutButton/index.ts b/frontend/src/lib/candidate/components/logoutButton/index.ts index c47c77e54..ecef0f03c 100644 --- a/frontend/src/lib/candidate/components/logoutButton/index.ts +++ b/frontend/src/lib/candidate/components/logoutButton/index.ts @@ -1 +1,2 @@ export { default as LogoutButton } from './LogoutButton.svelte'; +export * from './LogoutButton.type'; diff --git a/frontend/src/lib/candidate/components/passwordField/PasswordField.svelte b/frontend/src/lib/candidate/components/passwordField/PasswordField.svelte index e20eb8a34..f21cbc1f9 100644 --- a/frontend/src/lib/candidate/components/passwordField/PasswordField.svelte +++ b/frontend/src/lib/candidate/components/passwordField/PasswordField.svelte @@ -3,14 +3,17 @@ PasswordField is an input box for password that comes with a button to reveal and hide the password ### Properties -- `id` : optional id for the input -- `password` : value of the password, bindable -- `autoComplete` : autocomplete value for password input -- `label`: the label for the password field. Defaults to `$t('common.password')` -- `externalLabel` : whether the label is outside the component and should not be rendered inside -- `focus`: bindable function to set focus to the password input + +- `id`: Optional id for the input. +- `password`: Bindable: The password value. +- `autocomplete`: The autocomplete value for password input. Default: `''` +- `label`: The label for the password field. +- `externalLabel`: Whether the label is outside the component and should not be rendered inside. Default: `false` +- `focus`: Bindable: Function to set focus to the password input. +- Any valid attributes of a `<div>` element ### Usage + ```tsx <PasswordField bind:password={passwordOfContext} autocomplete="current-password" /> ``` @@ -20,12 +23,15 @@ PasswordField is an input box for password that comes with a button to reveal an import { Button } from '$lib/components/button'; import { getComponentContext } from '$lib/contexts/component'; import { getUUID } from '$lib/utils/components'; + import type { PasswordFieldProps } from './PasswordField.type'; + + type $$Props = PasswordFieldProps; - export let id: string | undefined = undefined; - export let password = ''; - export let autocomplete = ''; - export let label: string | undefined = undefined; - export let externalLabel = false; + export let id: $$Props['id'] = undefined; + export let password: $$Props['password'] = ''; + export let autocomplete: $$Props['autocomplete'] = ''; + export let label: $$Props['label'] = undefined; + export let externalLabel: $$Props['externalLabel'] = false; export function focus(): void { input?.focus(); } diff --git a/frontend/src/lib/candidate/components/passwordField/PasswordField.type.ts b/frontend/src/lib/candidate/components/passwordField/PasswordField.type.ts new file mode 100644 index 000000000..7071ffa14 --- /dev/null +++ b/frontend/src/lib/candidate/components/passwordField/PasswordField.type.ts @@ -0,0 +1,28 @@ +import type { SvelteHTMLElements } from 'svelte/elements'; + +export type PasswordFieldProps = SvelteHTMLElements['div'] & { + /** + * Optional id for the input. + */ + id?: string; + /** + * Bindable: The password value. + */ + password?: string; + /** + * The autocomplete value for password input. @default '' + */ + autocomplete?: string; + /** + * The label for the password field. + */ + label?: string; + /** + * Whether the label is outside the component and should not be rendered inside. @default false + */ + externalLabel?: boolean; + /** + * Bindable: Function to set focus to the password input. + */ + focus?: () => void; +}; diff --git a/frontend/src/lib/candidate/components/passwordField/index.ts b/frontend/src/lib/candidate/components/passwordField/index.ts index 07eaa96b6..a30ae8679 100644 --- a/frontend/src/lib/candidate/components/passwordField/index.ts +++ b/frontend/src/lib/candidate/components/passwordField/index.ts @@ -1 +1,2 @@ export { default as PasswordField } from './PasswordField.svelte'; +export * from './PasswordField.type'; diff --git a/frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.svelte b/frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.svelte index f2fd80752..415b6f57d 100644 --- a/frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.svelte +++ b/frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.svelte @@ -8,16 +8,16 @@ Contains the dynamic `PasswordValidator` component. ### Properties -- `password`: value for the password field -- `autocomplete`: autocomplete attribute for the password input field -- `errorMessage`: bindable error message if the password is invalid or doesn't match the confirmation password -- `valid`: bindable property which can be read to see if the password is valid and the confirmation password matches -- `reset`: bindable function with which to clear to form +- `password`: Bindable: The password value. +- `autocomplete`: The autocomplete attribute for the password input field. Default: `'new-password'` +- `errorMessage`: Bindable: Error message if the password is invalid or doesn't match the confirmation password. +- `valid`: Bindable: Whether the password is valid and the confirmation password matches. +- `reset`: Bindable: Function to clear the form. +- Any valid attributes of a `<form>` element ### Usage ```tsx - <PasswordSetter bind:password={password} bind:valid={canSubmit}/> @@ -29,11 +29,14 @@ Contains the dynamic `PasswordValidator` component. import { PasswordValidator } from '$candidate/components/passwordValidator'; import { getComponentContext } from '$lib/contexts/component'; import { getUUID } from '$lib/utils/components'; + import type { PasswordSetterProps } from './PasswordSetter.type'; + + type $$Props = PasswordSetterProps; - export let password = ''; - export let autocomplete = 'new-password'; - export let errorMessage: string | undefined = undefined; - export let valid = false; + export let password: $$Props['password'] = ''; + export let autocomplete: $$Props['autocomplete'] = 'new-password'; + export let errorMessage: $$Props['errorMessage'] = undefined; + export let valid: $$Props['valid'] = false; export function reset(): void { password = ''; passwordConfirmation = ''; diff --git a/frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.type.ts b/frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.type.ts new file mode 100644 index 000000000..aefe3abe4 --- /dev/null +++ b/frontend/src/lib/candidate/components/passwordSetter/PasswordSetter.type.ts @@ -0,0 +1,24 @@ +import type { SvelteHTMLElements } from 'svelte/elements'; + +export type PasswordSetterProps = SvelteHTMLElements['form'] & { + /** + * Bindable: The password value. + */ + password?: string; + /** + * The autocomplete attribute for the password input field. @default 'new-password' + */ + autocomplete?: string; + /** + * Bindable: Error message if the password is invalid or doesn't match the confirmation password. + */ + errorMessage?: string; + /** + * Bindable: Whether the password is valid and the confirmation password matches. + */ + valid?: boolean; + /** + * Bindable: Function to clear the form. + */ + reset?: () => void; +}; diff --git a/frontend/src/lib/candidate/components/passwordSetter/index.ts b/frontend/src/lib/candidate/components/passwordSetter/index.ts index e86d4d5d4..11d70715f 100644 --- a/frontend/src/lib/candidate/components/passwordSetter/index.ts +++ b/frontend/src/lib/candidate/components/passwordSetter/index.ts @@ -1 +1,2 @@ export { default as PasswordSetter } from './PasswordSetter.svelte'; +export * from './PasswordSetter.type'; diff --git a/frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.svelte b/frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.svelte index 28b5f1304..24ff46356 100644 --- a/frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.svelte +++ b/frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.svelte @@ -21,9 +21,11 @@ Therefore, the validity should be also checked on form submit as well and on the Accesses validation functions from `@openvaa/app-shared`. ### Properties -- `password`: The password to validate -- `username`: The username used to prevent the password from being too similar -- `validPassword`: A boolean that is set to true when the password is valid + +- `password`: The password to validate. +- `username`: The username used to prevent the password from being too similar. Default: `''` +- `validPassword`: Bindable: Whether the password is valid. +- Any valid attributes of a `<div>` element ### Usage @@ -42,10 +44,13 @@ When using this component, the `validPassword` property should be bound to a boo import { tweened } from 'svelte/motion'; import { getComponentContext } from '$lib/contexts/component'; import { assertTranslationKey } from '$lib/i18n/utils/assertTranslationKey'; + import type { PasswordValidatorProps } from './PasswordValidator.type'; + + type $$Props = PasswordValidatorProps; - export let password = ''; - export let username = ''; - export let validPassword = false; + export let password: $$Props['password'] = ''; + export let username: $$Props['username'] = ''; + export let validPassword: $$Props['validPassword'] = false; const { t } = getComponentContext(); diff --git a/frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.type.ts b/frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.type.ts new file mode 100644 index 000000000..9271b6762 --- /dev/null +++ b/frontend/src/lib/candidate/components/passwordValidator/PasswordValidator.type.ts @@ -0,0 +1,16 @@ +import type { SvelteHTMLElements } from 'svelte/elements'; + +export type PasswordValidatorProps = SvelteHTMLElements['div'] & { + /** + * The password to validate. + */ + password?: string; + /** + * The username used to prevent the password from being too similar. @default '' + */ + username?: string; + /** + * Bindable: Whether the password is valid. + */ + validPassword?: boolean; +}; diff --git a/frontend/src/lib/candidate/components/passwordValidator/index.ts b/frontend/src/lib/candidate/components/passwordValidator/index.ts index a9acb4e33..6a843feb3 100644 --- a/frontend/src/lib/candidate/components/passwordValidator/index.ts +++ b/frontend/src/lib/candidate/components/passwordValidator/index.ts @@ -1 +1,2 @@ export { default as PasswordValidator } from './PasswordValidator.svelte'; +export * from './PasswordValidator.type'; diff --git a/frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.svelte b/frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.svelte index ecd7e2e87..7372c8d47 100644 --- a/frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.svelte +++ b/frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.svelte @@ -2,29 +2,30 @@ @component Show a notification prompting the user to login instead of preregistering again. -### Props +### Properties -- Any valid props for the `Alert` component. +- Any valid properties of an `Alert` component ### Usage ```tsx -popupQueue.push({ - component: PreregisteredNotification, +popupQueue.push({ + component: PreregisteredNotification, }); ``` --> <script lang="ts"> import { goto } from '$app/navigation'; - import { Alert, type AlertProps } from '$lib/components/alert'; + import { Alert } from '$lib/components/alert'; import { Button } from '$lib/components/button'; import { getAppContext } from '$lib/contexts/app'; import { sanitizeHtml } from '$lib/utils/sanitize'; import type { Route } from '$lib/utils/route'; + import type { PreregisteredNotificationProps } from './PreregisteredNotification.type'; - /* eslint-disable @typescript-eslint/no-unused-vars */ - type $$Props = Partial<AlertProps>; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type $$Props = PreregisteredNotificationProps; const { getRoute, t } = getAppContext(); diff --git a/frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.type.ts b/frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.type.ts new file mode 100644 index 000000000..d7c39fbeb --- /dev/null +++ b/frontend/src/lib/candidate/components/preregisteredNotification/PreregisteredNotification.type.ts @@ -0,0 +1,3 @@ +import type { AlertProps } from '$lib/components/alert'; + +export type PreregisteredNotificationProps = Partial<AlertProps>; diff --git a/frontend/src/lib/candidate/components/preregisteredNotification/index.ts b/frontend/src/lib/candidate/components/preregisteredNotification/index.ts index c668eb674..3e4fcb97f 100644 --- a/frontend/src/lib/candidate/components/preregisteredNotification/index.ts +++ b/frontend/src/lib/candidate/components/preregisteredNotification/index.ts @@ -1 +1,2 @@ export { default as PreregisteredNotification } from './PreregisteredNotification.svelte'; +export * from './PreregisteredNotification.type'; diff --git a/frontend/src/lib/candidate/components/termsOfUse/TermsOfUse.svelte b/frontend/src/lib/candidate/components/termsOfUse/TermsOfUse.svelte index c782648ad..6c0bf12a4 100644 --- a/frontend/src/lib/candidate/components/termsOfUse/TermsOfUse.svelte +++ b/frontend/src/lib/candidate/components/termsOfUse/TermsOfUse.svelte @@ -1,10 +1,8 @@ -<!--@component - -# Candidate app terms of use - +<!-- +@component A utility component for displaying candidate app terms of use and privacy statement. -## Usage +### Usage ```tsx <TermsOfUse /> diff --git a/frontend/src/lib/candidate/components/termsOfUse/TermsOfUseForm.svelte b/frontend/src/lib/candidate/components/termsOfUse/TermsOfUseForm.svelte index c3adddb00..dec441d39 100644 --- a/frontend/src/lib/candidate/components/termsOfUse/TermsOfUseForm.svelte +++ b/frontend/src/lib/candidate/components/termsOfUse/TermsOfUseForm.svelte @@ -1,14 +1,17 @@ -<!--@component - -# Candidate app terms of use form - +<!-- +@component Show the terms of use along with a checkbox for accepting them. -## Dynamic component +### Dynamic component Accesses `CandidateContext`. -## Usage +### Properties + +- `termsAccepted`: Bindable: Whether the terms are accepted. Default: `false` +- Any valid attributes of a `<section>` element + +### Usage ```tsx <script lang="ts"> diff --git a/frontend/src/lib/components/README.md b/frontend/src/lib/components/README.md new file mode 100644 index 000000000..ffc30600e --- /dev/null +++ b/frontend/src/lib/components/README.md @@ -0,0 +1,7 @@ +# Components + +> See also the online doc [Components](https://openvaa.org/developers-guide/frontend/components) (or [locally](/docs/src/routes/developers-guide/frontend/components/+page.md)) + +This directory contains reusable static components used throughout the OpenVAA frontend application. + +For auto-generated component documentation, see the [generated components documentation](https://openvaa.org/developers-guide/frontend/components/generated). diff --git a/frontend/src/lib/components/accordionSelect/AccordionSelect.svelte b/frontend/src/lib/components/accordionSelect/AccordionSelect.svelte index 2359dc514..3718a3f99 100644 --- a/frontend/src/lib/components/accordionSelect/AccordionSelect.svelte +++ b/frontend/src/lib/components/accordionSelect/AccordionSelect.svelte @@ -1,18 +1,17 @@ -<!--@component +<!-- +@component Show a select widget which is expanded when no selection is made and collapsed when an option is selected. -If there’s only one option, it is automatically selected and no interactions are allowed. +If there's only one option, it is automatically selected and no interactions are allowed. ### Properties -- `options`: The titles of the options. -- `activeIndex`: The index of the active option. Bind to this to change or read the active option. @default 0 +- `options`: The titles and other data related to the options. +- `activeIndex`: The index of the active option. Bind to this to change or read the active option. +- `labelGetter`: A callback used to get the label for each option. Default: `String` +- `onChange`: Callback for when the active option changes. The event `details` contains the active option as `option` as well as its `index`. - Any valid attributes of a `<div>` element -### Callbacks - -- `onChange`: Callback for when the active option changes. The event `details` contains the active option as `option` as well as its `index`. Note, it's preferable to just bind to the `activeTab` property instead. - ### Usage ```tsx diff --git a/frontend/src/lib/components/alert/Alert.svelte b/frontend/src/lib/components/alert/Alert.svelte index 6c59be781..04defeaa6 100644 --- a/frontend/src/lib/components/alert/Alert.svelte +++ b/frontend/src/lib/components/alert/Alert.svelte @@ -2,24 +2,25 @@ @component Show a non-model alert or dialog that appears at the bottom of the screen. -### Slots - -- `actions`: The action buttons to display. -- default: The content of the alert. - ### Properties -- `title`: The title of the modal -- `icon`: The icon of the alert. @default `info` -- `autoOpen`: Whether to open the alert automatically. @default `true` +- `title`: The title of the alert. +- `icon`: Possible icon of the alert. +- `autoOpen`: Whether to open the alert automatically. Default: `true` +- `isOpen`: Bind to this to get the alert's open state. - `onClose`: The callback triggered when the alert is closed. -- Any valid properties of a `<div>` element. +- Any valid attributes of a `<dialog>` element ### Bindable functions - `openAlert`: Opens the alert - `closeAlert`: Closes the alert +### Slots + +- `actions`: The action buttons to display. +- default: The content of the alert. + ### Events - `open`: Fired after the alert is opened. @@ -32,7 +33,7 @@ Show a non-model alert or dialog that appears at the bottom of the screen. <script lang="ts"> let closeAlert: () => void; </script> -<Alert +<Alert bind:closeAlert title="Can we help you?" icon="warning" diff --git a/frontend/src/lib/components/avatar/Avatar.svelte b/frontend/src/lib/components/avatar/Avatar.svelte index 16d273afc..5f5ae20be 100644 --- a/frontend/src/lib/components/avatar/Avatar.svelte +++ b/frontend/src/lib/components/avatar/Avatar.svelte @@ -5,8 +5,8 @@ Display either an image or a initials-based avatar for an entity. The color of t ### Properties - `entity`: The entity for which to display the avatar. -- `size`: The size of the avatar. @default `'md'` -- `linkFullImage`: Whether to link the thumbnail to the full image. @default false +- `size`: The size of the avatar. Default: `'md'` +- `linkFullImage`: Whether to link the thumbnail to the full image. Default: `false` - Any valid attributes of a `<figure>` element ### Usage diff --git a/frontend/src/lib/components/button/Button.svelte b/frontend/src/lib/components/button/Button.svelte index 48a30c8a5..26f7b1283 100644 --- a/frontend/src/lib/components/button/Button.svelte +++ b/frontend/src/lib/components/button/Button.svelte @@ -16,15 +16,15 @@ The button is rendered as an `<a>` element if `href` is supplied. Otherwise a `< ### Properties -- `href`: The URL to navigate to. If this is not supplied be sure to provide an `on:click` event handler or other way of making the item interactive. - `text`: The required text of the button. If `variant` is `icon`, the text will be used as the `aria-label` and `title` for the button. You can override both by providing them as attributes, e.g. `aria-label="Another text"`. -- `variant`: The type of the button. -- `icon`: The name of the icon to display. -- `color`: The color of the button or text. -- `iconPos`: The position of the icon relative to the text. +- `icon`: The name of the icon to use in the button or `null` if no icon should be used. Default: `'next'` if `variant='main'`, otherwise `null` +- `color`: The color of the button or text. Default: `'primary'` - `disabled`: Whether the button is disabled. This can also be used with buttons rendered as `<a>` elements. -- `loading`: Set to `true` to show a loading spinner instead of the possible icon and disable the button. @default false -- `loadingText`: The text shown when `loading` is `true`. @default $t('common.loading') +- `variant`: Type of the button, which defines it's appearance. Default: `'normal'` +- `iconPos`: Position of the icon in the button. Only relevant if `icon` is not `null` and `variant` is not `icon` or `floating-icon`. Note that `top` and `bottom` are not supported if `variant='main'`. Default: `'right'` if `variant='main'`, otherwise `'left'` +- `loading`: Set to `true` to show a loading spinner instead of the possible icon and disable the button. Default: `false` +- `loadingText`: The text shown when `loading` is `true`. Default: `$t('common.loading')` +- `href`: The URL to navigate to. If this is not supplied be sure to provide an `on:click` event handler or other way of making the item interactive. - Any valid attributes of either an `<a>` or `<button>` element depending whether `href` was defined or not, respectively. ### Slots diff --git a/frontend/src/lib/components/buttonWithConfirmation/ButtonWithConfirmation.svelte b/frontend/src/lib/components/buttonWithConfirmation/ButtonWithConfirmation.svelte index 4dcbabd04..f8fc677bb 100644 --- a/frontend/src/lib/components/buttonWithConfirmation/ButtonWithConfirmation.svelte +++ b/frontend/src/lib/components/buttonWithConfirmation/ButtonWithConfirmation.svelte @@ -1,31 +1,15 @@ <!-- @component -A component for buttons that mostly contain text and an icon. Use the `variant` prop to specify the button type. When using an `icon`, use `iconPos` to set the position of the icon relative to the text. - -- `main`: A large, prominent button that is used for the main action of the page. In general, there should only be one of these on a page. -- `prominent`: A large, quite prominent button. -- `floating-icon`: A button with a large icon and no text. This is usually used for a floating action button. -- `icon`: A button containing only an icon. Note that you still need to provide the `text` property, which will be used as the `aria-label` and `title` of the button. -- `responsive-icon`: A button rendered as icon only on small screens but which exposes the text label on large screens. Set the `iconPos` to `left` or `right` to control its location in the expanded view. -- `secondary`: A button with a smaller (uppercase) text and possibly an icon. -- `normal`: The default button type, which usually consists of an icon and text. - -Only `main` buttons have a backround color. The other variants use DaisyUI's `btn-ghost` class, i.e. they do not have a background color. - -The button is rendered as an `<a>` element if `href` is supplied. Otherwise a `<button>` element will be used. Be sure to provide an `on:click` event handler or other way of making the item interactive. +A button which will open a confirmation modal when clicked before the action is executed. ### Properties -- `href`: The URL to navigate to. If this is not supplied be sure to provide an `on:click` event handler or other way of making the item interactive. -- `text`: The required text of the button. If `variant` is `icon`, the text will be used as the `aria-label` and `title` for the button. You can override both by providing them as attributes, e.g. `aria-label="Another text"`. -- `variant`: The type of the button. -- `icon`: The name of the icon to display. -- `color`: The color of the button or text. -- `iconPos`: The position of the icon relative to the text. -- `disabled`: Whether the button is disabled. This can also be used with buttons rendered as `<a>` elements. -- `loading`: Set to `true` to show a loading spinner instead of the possible icon and disable the button. @default false -- `loadingText`: The text shown when `loading` is `true`. @default $t('common.loading') -- Any valid attributes of either an `<a>` or `<button>` element depending whether `href` was defined or not, respectively. +- `modalTitle`: The title of the confirmation modal. +- `onConfirm`: Callback triggered when the user confirms the action. +- `onCancel`: Callback triggered when the user cancels the action. +- `cancelLabel`: The label for the cancel button in the modal. +- `confirmLabel`: The label for the confirm button in the modal. +- Any valid properties of a `<Button>` component except `href` and `on:click`. ### Slots diff --git a/frontend/src/lib/components/categoryTag/CategoryTag.svelte b/frontend/src/lib/components/categoryTag/CategoryTag.svelte index 258861152..07e23736f 100644 --- a/frontend/src/lib/components/categoryTag/CategoryTag.svelte +++ b/frontend/src/lib/components/categoryTag/CategoryTag.svelte @@ -5,10 +5,10 @@ Used to display a question category tag with the category's color. ### Properties - `category`: The `QuestionCategory` object. -- `variant`: Whether to use an abbreviation or the full name. @default `'full'` -- `suffix`: An optional suffix to add after the category name, e.g. '1/3'. @default undefined -- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. @default false -- Any valid attributes of a `<span>` element. +- `variant`: Whether to use an abbreviation or the full name. Default: `'full'` +- `suffix`: An optional suffix to add after the category name, e.g. '1/3'. +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. Default: `false` +- Any valid attributes of a `<span>` element ### Usage diff --git a/frontend/src/lib/components/constituencySelector/ConstituencySelector.svelte b/frontend/src/lib/components/constituencySelector/ConstituencySelector.svelte index 4c9437e35..8d9f4cc29 100644 --- a/frontend/src/lib/components/constituencySelector/ConstituencySelector.svelte +++ b/frontend/src/lib/components/constituencySelector/ConstituencySelector.svelte @@ -1,28 +1,26 @@ -<!--@component - -# Constituency selection component - +<!-- +@component Display constituency selection inputs for elections. -If any of the `ConstituencyGroup`s for the `Election`s are shared, only a single selectior will be shown for them. Also, if any `ConstituencyGroup`s are completely subsumed by another, only the selector for the child group will be shown and the selected parent will be implied. +If any of the `ConstituencyGroup`s for the `Election`s are shared, only a single selector will be shown for them. Also, if any `ConstituencyGroup`s are completely subsumed by another, only the selector for the child group will be shown and the selected parent will be implied. ### Properties - `elections`: The `Election`s for which to show the `Constituency`s. -- `disableSorting`: If `true`, the `Constituency`s are not ordered alphabetically. Default `false`. -- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. @default false +- `disableSorting`: If `true`, the `Constituency`s are not ordered alphabetically. Default: `false` +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. Default: `false` - `selected`: Bindable value for the `Id`s of the selected `Constituency`s organized by `Election`. - `useSingleGroup`: If specified, only this group is offered for selection and the `Constituency`s for the `Election`s are implied from this one. Only meaningful when there are multiple `Election`s whose `ConstituencyGroup` hierarchies overlap only partially. To be used when the `elections.startFromConstituencyGroup` setting is set. - `selectionComplete`: A utility bindable value which is `true` when a selection has been made for each `Election` or for the single group if `useSingleGroup` is set. - `onChange`: Callback triggered when the selection changes. -- Any valid attributes of a `<div>` element. +- Any valid attributes of a `<div>` element ### Usage ```tsx -<ConstituenctSelector - elections={$dataRoot.elections} - bind:selected={$selectedConstituencies} +<ConstituencySelector + elections={$dataRoot.elections} + bind:selected={$selectedConstituencies} onChange={(sel) => console.info('Selected', sel)} /> ``` --> diff --git a/frontend/src/lib/components/controller/InfoMessages.svelte b/frontend/src/lib/components/controller/InfoMessages.svelte index ca11ae001..6be6d59ce 100644 --- a/frontend/src/lib/components/controller/InfoMessages.svelte +++ b/frontend/src/lib/components/controller/InfoMessages.svelte @@ -1,20 +1,32 @@ -<!--@component -# Info Messages Component +<!-- +@component +Reusable component for displaying informational messages with scrolling. -Reusable component for displaying informational messages with scrolling +### Properties + +- `messages`: Array of informational messages to display. Default: `[]` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<InfoMessages messages={jobMessages} /> +``` --> <script lang="ts"> import { DEFAULT_MAX_MESSAGES, DEFAULT_MESSAGES_HEIGHT } from '$lib/admin/components/jobs/shared'; import { getAdminContext } from '$lib/contexts/admin'; - import type { JobMessage } from '$lib/server/admin/jobs/jobStore.type'; + import type { InfoMessagesProps } from './InfoMessages.type'; - const { t } = getAdminContext(); + type $$Props = InfoMessagesProps; + + export let messages: $$Props['messages'] = undefined; - export let messages: Array<JobMessage> = []; + const { t } = getAdminContext(); // Get the most recent messages and reverse order (latest first) - $: displayMessages = messages.slice(-DEFAULT_MAX_MESSAGES).reverse(); + $: displayMessages = messages?.slice(-DEFAULT_MAX_MESSAGES).reverse() ?? []; </script> <div class="p-3 rounded-lg bg-base-200 {DEFAULT_MESSAGES_HEIGHT} overflow-y-auto"> diff --git a/frontend/src/lib/components/controller/InfoMessages.type.ts b/frontend/src/lib/components/controller/InfoMessages.type.ts new file mode 100644 index 000000000..3af684d9b --- /dev/null +++ b/frontend/src/lib/components/controller/InfoMessages.type.ts @@ -0,0 +1,9 @@ +import type { SvelteHTMLElements } from 'svelte/elements'; +import type { JobMessage } from '$lib/server/admin/jobs/jobStore.type'; + +export type InfoMessagesProps = SvelteHTMLElements['div'] & { + /** + * Array of informational messages to display. @default [] + */ + messages?: Array<JobMessage>; +}; diff --git a/frontend/src/lib/components/controller/ProgressBar.svelte b/frontend/src/lib/components/controller/ProgressBar.svelte index e685282b7..b4cf7a718 100644 --- a/frontend/src/lib/components/controller/ProgressBar.svelte +++ b/frontend/src/lib/components/controller/ProgressBar.svelte @@ -1,15 +1,45 @@ -<!--@component -# Progress Bar Component +<!-- +@component +Reusable progress bar component for displaying task progress. -Reusable progress bar component for displaying task progress +### Properties + +- `progress`: Progress value between 0 and 1. +- `label`: Label for the progress bar. Default: `t('adminApp.jobs.progress')` +- `showPercentage`: Whether to show the percentage. Default: `true` +- `color`: Color theme for the progress bar. Default: `'primary'` +- `size`: Size of the progress bar. Default: `'md'` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<ProgressBar progress={0.75} label="Processing" /> +``` --> <script lang="ts"> - export let progress: number; // Value between 0 and 1 - export let label: string = 'Progress'; - export let showPercentage: boolean = true; - export let color: 'primary' | 'secondary' | 'accent' = 'primary'; - export let size: 'sm' | 'md' | 'lg' = 'md'; + import { getComponentContext } from '$lib/contexts/component'; + import type { ProgressBarProps } from './ProgressBar.type'; + + type $$Props = ProgressBarProps; + + export let progress: $$Props['progress']; + export let label: $$Props['label'] = undefined; + export let showPercentage: $$Props['showPercentage'] = true; + export let color: $$Props['color'] = undefined; + export let size: $$Props['size'] = undefined; + + const { t } = getComponentContext(); + + // Set defaults + $: size ||= 'md'; + $: color ||= 'primary'; + $: label ||= $t('adminApp.jobs.progress'); + + // Normalize progress value + let normalizedProgress: number; + let percentage: number; // Ensure progress is between 0 and 1 $: normalizedProgress = Math.max(0, Math.min(1, progress)); diff --git a/frontend/src/lib/components/controller/ProgressBar.type.ts b/frontend/src/lib/components/controller/ProgressBar.type.ts new file mode 100644 index 000000000..08a5beb96 --- /dev/null +++ b/frontend/src/lib/components/controller/ProgressBar.type.ts @@ -0,0 +1,24 @@ +import type { SvelteHTMLElements } from 'svelte/elements'; + +export type ProgressBarProps = SvelteHTMLElements['div'] & { + /** + * Progress value between 0 and 1. + */ + progress: number; + /** + * Label for the progress bar. @default t('adminApp.jobs.progress') + */ + label?: string; + /** + * Whether to show the percentage. @default true + */ + showPercentage?: boolean; + /** + * Color theme for the progress bar. @default 'primary' + */ + color?: 'primary' | 'secondary' | 'accent'; + /** + * Size of the progress bar. @default 'md' + */ + size?: 'sm' | 'md' | 'lg'; +}; diff --git a/frontend/src/lib/components/controller/WarningMessages.svelte b/frontend/src/lib/components/controller/WarningMessages.svelte index 75d7c0988..741899bde 100644 --- a/frontend/src/lib/components/controller/WarningMessages.svelte +++ b/frontend/src/lib/components/controller/WarningMessages.svelte @@ -1,21 +1,34 @@ -<!--@component -# Warning Messages Component +<!-- +@component +Reusable component for displaying warning and error messages with scrolling. -Reusable component for displaying warning and error messages with scrolling +### Properties + +- `warnings`: Array of warning messages to display. Default: `[]` +- `errors`: Array of error messages to display. Default: `[]` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<WarningMessages warnings={warningMessages} errors={errorMessages} /> +``` --> <script lang="ts"> import { DEFAULT_MAX_MESSAGES, DEFAULT_MESSAGES_HEIGHT } from '$lib/admin/components/jobs/shared'; import { getAdminContext } from '$lib/contexts/admin'; - import type { JobMessage } from '$lib/server/admin/jobs/jobStore.type'; + import type { WarningMessagesProps } from './WarningMessages.type'; - const { t } = getAdminContext(); + type $$Props = WarningMessagesProps; + + export let warnings: $$Props['warnings'] = []; + export let errors: $$Props['errors'] = []; - export let warnings: Array<JobMessage> = []; - export let errors: Array<JobMessage> = []; + const { t } = getAdminContext(); // Combine warnings and errors for display, limiting to maxMessages, and reverse order (latest first) - $: allMessages = [...warnings, ...errors].slice(-DEFAULT_MAX_MESSAGES).reverse(); + $: allMessages = [...(warnings ?? []), ...(errors ?? [])].slice(-DEFAULT_MAX_MESSAGES).reverse(); </script> <div class="p-3 rounded-lg bg-base-200 {DEFAULT_MESSAGES_HEIGHT} overflow-y-auto"> diff --git a/frontend/src/lib/components/controller/WarningMessages.type.ts b/frontend/src/lib/components/controller/WarningMessages.type.ts new file mode 100644 index 000000000..d2b7da11c --- /dev/null +++ b/frontend/src/lib/components/controller/WarningMessages.type.ts @@ -0,0 +1,13 @@ +import type { SvelteHTMLElements } from 'svelte/elements'; +import type { JobMessage } from '$lib/server/admin/jobs/jobStore.type'; + +export type WarningMessagesProps = SvelteHTMLElements['div'] & { + /** + * Array of warning messages to display. @default [] + */ + warnings?: Array<JobMessage>; + /** + * Array of error messages to display. @default [] + */ + errors?: Array<JobMessage>; +}; diff --git a/frontend/src/lib/components/controller/index.ts b/frontend/src/lib/components/controller/index.ts index e91df14d9..c9041285e 100644 --- a/frontend/src/lib/components/controller/index.ts +++ b/frontend/src/lib/components/controller/index.ts @@ -1,3 +1,6 @@ export { default as InfoMessages } from './InfoMessages.svelte'; +export * from './InfoMessages.type'; export { default as ProgressBar } from './ProgressBar.svelte'; +export * from './ProgressBar.type'; export { default as WarningMessages } from './WarningMessages.svelte'; +export * from './WarningMessages.type'; diff --git a/frontend/src/lib/components/electionTag/ElectionTag.svelte b/frontend/src/lib/components/electionTag/ElectionTag.svelte index 55d138e81..b9ad41f80 100644 --- a/frontend/src/lib/components/electionTag/ElectionTag.svelte +++ b/frontend/src/lib/components/electionTag/ElectionTag.svelte @@ -7,8 +7,8 @@ Used when the application has multiple elections and question may apply to only ### Properties - `election`: The `Election` object. -- `variant`: Whether to use an abbreviation or the full name. @default `'short'` -- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. @default false +- `variant`: Whether to use an abbreviation or the full name. Default: `'short'` +- `onShadedBg`: Set to `true` if using the component on a dark (`base-300`) background. Default: `false` - Any valid attributes of a `<span>` element. ### Usage diff --git a/frontend/src/lib/components/entityFilters/EntityFilters.svelte b/frontend/src/lib/components/entityFilters/EntityFilters.svelte index 41fdce52a..572f69e52 100644 --- a/frontend/src/lib/components/entityFilters/EntityFilters.svelte +++ b/frontend/src/lib/components/entityFilters/EntityFilters.svelte @@ -4,9 +4,9 @@ Show filters for entities. This component and the individual filter components o ### Properties -- `filters`: The filter objects to render. +- `filterGroup`: The filters applied to the contents. - `targets`: The target entitiess of the filter objects. Note that these will only be used to get value options, not for actual filtering. -- Any valid attributes of a `<div>` element +- Any valid attributes of a `<div>` element. ### Usage diff --git a/frontend/src/lib/components/entityFilters/enumerated/EnumeratedEntityFilter.svelte b/frontend/src/lib/components/entityFilters/enumerated/EnumeratedEntityFilter.svelte index 7e20f6fb5..633b75bff 100644 --- a/frontend/src/lib/components/entityFilters/enumerated/EnumeratedEntityFilter.svelte +++ b/frontend/src/lib/components/entityFilters/enumerated/EnumeratedEntityFilter.svelte @@ -83,7 +83,7 @@ Render an enumerated filter for entities that displays a list of values to inclu /** * Parse the selected checkboxes into values accepted by `filter.include` - * @param selectedValues We need to explicitly pass this to trigger reactive updates + * @param selectedValues - We need to explicitly pass this to trigger reactive updates * @returns The selected values for `filter.include` */ function parseSelected(selectedValues: typeof selected) { @@ -113,7 +113,7 @@ Render an enumerated filter for entities that displays a list of values to inclu /** * Get the text label from the object returned by the filter. - * @param object party object or choice + * @param object - party object or choice */ function getLabel(object: AnyEntityVariant | AnyChoice | undefined): string | undefined { if (!object) return $t('entityFilters.missingValue'); diff --git a/frontend/src/lib/components/entityFilters/text/TextEntityFilter.svelte b/frontend/src/lib/components/entityFilters/text/TextEntityFilter.svelte index 3e1d194fc..712dd7450 100644 --- a/frontend/src/lib/components/entityFilters/text/TextEntityFilter.svelte +++ b/frontend/src/lib/components/entityFilters/text/TextEntityFilter.svelte @@ -4,9 +4,10 @@ Render a text filter for entities. ### Properties -- `filter`: The text filter object to render. -- `placeholder`: The placeholder text. @default `$t('entityFilters.text.placeholder')` -- Any valid attributes of a `<div>` element +- `filter`: The text filter object. +- `placeholder`: The placeholder text. Default: `$t('components.entityFilters.text.placeholder')` +- `variant`: The styling variant for the text field. Default: `'default'` +- Any valid attributes of a `<div>` element. ### Usage diff --git a/frontend/src/lib/components/entityTag/EntityTag.svelte b/frontend/src/lib/components/entityTag/EntityTag.svelte index b3e713bb7..ce59e4ce0 100644 --- a/frontend/src/lib/components/entityTag/EntityTag.svelte +++ b/frontend/src/lib/components/entityTag/EntityTag.svelte @@ -5,8 +5,8 @@ Used to display an `Entity` as small tag including an icon. ### Properties - `entity`: A possibly wrapped entity, e.g. candidate or a party. -- `variant`: Whether to use an abbreviation or the full name. @default `'default'` -- `hideParent`: Whether to hide the possible parent nomination. @default false +- `variant`: Whether to use an abbreviation or the full name. Default: `'default'` +- `hideParent`: Whether to hide the possible parent nomination. Default: `false` - Any valid attributes of a `<div>` element. ### Usage diff --git a/frontend/src/lib/components/errorMessage/ErrorMessage.svelte b/frontend/src/lib/components/errorMessage/ErrorMessage.svelte index e01af11cd..0a0ccc966 100644 --- a/frontend/src/lib/components/errorMessage/ErrorMessage.svelte +++ b/frontend/src/lib/components/errorMessage/ErrorMessage.svelte @@ -4,9 +4,9 @@ Used to display an error message. Also logs the error to the console. ### Properties -- `inline`: Whether to show an inline version of the message. By default the message tries to center itself in the available area and displays a large emoji. @default `false` -- `message`: The message to display. Default `$t('error.default')` -- `logMessage`: The message to log in the console in development mode. @default value of `message` +- `inline`: Whether to show an inline version of the message. By default the message tries to center itself in the available area and displays a large emoji. Default: `false` +- `message`: The message to display. Default: `$t('error.default')` +- `logMessage`: The message to log in the console in development mode. Default: value of `message` - Any valid attributes of a `<div>` element. ### Usage diff --git a/frontend/src/lib/components/expander/Expander.svelte b/frontend/src/lib/components/expander/Expander.svelte index 2d03b9438..a116b1497 100644 --- a/frontend/src/lib/components/expander/Expander.svelte +++ b/frontend/src/lib/components/expander/Expander.svelte @@ -13,15 +13,14 @@ A component for expanders that contain a title and some content. Use the ### Properties -- `title`: Title used for the expander. This is also used as the aria-label for - the checkbox on which the expander operates on. -- `variant`: The type for the expander. -- `iconColor`: The color for the icon. Default color is primary. -- `iconPos`: The position for the icon. Default is text, which means the icon will - be where the text ends. -- `titleClass`: Custom class string to add to the `<div>` containing the title. -- `contentClass`: Custom class string to add to the `<div>` containing the main content. +- `title`: Title is seen as the text in the expander's visible part, and it is mandatory. Title will also be used as a 'aria-label' for a checkbow on which the expander operates on. +- `iconColor`: The color of the next-icon that is used in the expander. Default: `'primary'` +- `iconPos`: The position of the next-icon that is used in the expander. Default: `'text'` +- `titleClass`: Variable with which to configure the expanders title if no variants are in use. +- `contentClass`: Variable with which to configure the expanders content if no variants are in use. - `defaultExpanded`: Variable used to define if the expander is expanded or not by default. +- `variant`: Variable used to define a variant for the expander. +- Any valid attributes of a `<div>` element. You should not try to use a variant and customize at the same time. diff --git a/frontend/src/lib/components/headingGroup/HeadingGroup.svelte b/frontend/src/lib/components/headingGroup/HeadingGroup.svelte index 6d417dd21..47b2a329e 100644 --- a/frontend/src/lib/components/headingGroup/HeadingGroup.svelte +++ b/frontend/src/lib/components/headingGroup/HeadingGroup.svelte @@ -5,8 +5,8 @@ and the main title. ### Properties -- `aria-roledescription`: The Aria role description of the `<hgroup>` element representing the pre-title. @default $t('components.headingGroup.roleDescription') -- `role`: The Aria role of the `<hgroup>` element. @default 'group' +- `aria-roledescription`: The Aria role description of the `<hgroup>` element. Default: `$t('aria.headingGroup')` +- `role`: The Aria role of the `<hgroup>` element. Default: `'group'` - Any valid attributes of a `<hgroup>` element. ### Slots diff --git a/frontend/src/lib/components/headingGroup/PreHeading.svelte b/frontend/src/lib/components/headingGroup/PreHeading.svelte index ad66f50ba..6787d4b1e 100644 --- a/frontend/src/lib/components/headingGroup/PreHeading.svelte +++ b/frontend/src/lib/components/headingGroup/PreHeading.svelte @@ -4,7 +4,7 @@ Used for a pre-title, or kicker, above the main title of a page within a `Headin ### Properties -- `aria-roledescription`: The Aria role description of the `<p>` element representing the pre-title. @default $t('components.preHeading.roleDescription') +- `aria-roledescription`: The Aria role description of the `<p>` element representing the pre-title. Default: `$t('aria.preHeading')` - Any valid attributes of a `<p>` element. ### Slots diff --git a/frontend/src/lib/components/heroEmoji/HeroEmoji.svelte b/frontend/src/lib/components/heroEmoji/HeroEmoji.svelte index d22e9486e..1727a70b4 100644 --- a/frontend/src/lib/components/heroEmoji/HeroEmoji.svelte +++ b/frontend/src/lib/components/heroEmoji/HeroEmoji.svelte @@ -12,10 +12,7 @@ using the `class` attribute, e.g. `class="text-[10rem]"`. ### Properties -- `emoji`: The emoji to use. Note that all non-emoji characters will be removed. If `undefined` the component will not be rendered at all. @default `undefined` -- `aria-hidden`: @default `true` -- `role`: Aria role @default `img` -- `class`: Additional class string to append to the element's default classes. +- `emoji`: The emoji to use. Note that all non-emoji characters will be removed. If `undefined` the component will not be rendered at all. Default: `undefined` - Any valid attributes of a `<div>` element. ### Usage diff --git a/frontend/src/lib/components/icon/Icon.svelte b/frontend/src/lib/components/icon/Icon.svelte index 283cc596e..f3b8e1bbc 100644 --- a/frontend/src/lib/components/icon/Icon.svelte +++ b/frontend/src/lib/components/icon/Icon.svelte @@ -8,15 +8,12 @@ any valid attributes of the `<svg>` element. ### Properties -- `name`: the name of the icon to use -- `size`: The size of the icon as one of the predefined sizes 'sm', 'md' or 'lg'. For arbitrary values, you can supply a `class` property, such as `h-[3.15rem] w-[3.15rem]`. @default 'md' -- `color`: The color of the icon as one of the predefined colours. For arbitrary values, use the `customColor` and `customColorDark` properties. @default 'current' +- `name`: The name of the icon to use. +- `size`: The size of the icon as one of the predefined sizes 'sm', 'md' or 'lg'. For arbitrary values, you can supply a `class` property, such as `h-[3.15rem] w-[3.15rem]`. Default: `'md'` +- `color`: The color of the icon as one of the predefined colours. For arbitrary values, use the `customColor` and `customColorDark` properties. Default: `'current'` - `customColor`: A custom color string to use for the icon, e.g. in case of parties, which will override the `color` property. Make sure to define both `customColor` and `customColorDark` together. - `customColorDark`: A custom color string to use for the icon in dark mode, which will override the `color` property. -- `aria-hidden`: @default `true` -- `role`: Aria role @default `img` -- `class`: Additional class string to append to the element's default classes. -- Any valid attributes of a `<svg>` element +- Any valid attributes of a `<svg>` element. ### Usage diff --git a/frontend/src/lib/components/infoAnswer/InfoAnswer.svelte b/frontend/src/lib/components/infoAnswer/InfoAnswer.svelte index 4d4f9b472..e6d333ce7 100644 --- a/frontend/src/lib/components/infoAnswer/InfoAnswer.svelte +++ b/frontend/src/lib/components/infoAnswer/InfoAnswer.svelte @@ -6,7 +6,7 @@ Used to display a possibly wrapped entity's answer to an info question. Dependin - `answer`: The possibly missing answer to the question. - `question`: The info question object. -- `format`: How to format the answer. @default `default` +- `format`: How to format the answer. Default: `'default'` - `default`: use the same format as in `<EntityDetails>`. - `tag`: format the answers as a pill or tag. Nb. links are always rendered as tags. - Any valid common attributes of an HTML element. diff --git a/frontend/src/lib/components/infoBadge/InfoBadge.svelte b/frontend/src/lib/components/infoBadge/InfoBadge.svelte index a0d03016c..ab99d8d55 100644 --- a/frontend/src/lib/components/infoBadge/InfoBadge.svelte +++ b/frontend/src/lib/components/infoBadge/InfoBadge.svelte @@ -1,16 +1,18 @@ <!-- - @component - Small badge component used to display information next to other components. +@component +Small badge component used to display information next to other components. - ### Properties +### Properties - - `text`: Text to be dispyled in the badge. - - `classes`: Custom css classes added to the wrapping div element. +- `text`: Text to be displayed in the badge. +- `classes`: Custom css classes. +- `disabled`: Whether the badge is disabled and rendered as grey. - ### Usage - - <InfoBadge text="example" classes="p-10" /> +### Usage +```tsx +<InfoBadge text="example" classes="p-10" /> +``` --> <script lang="ts"> diff --git a/frontend/src/lib/components/input/Input.svelte b/frontend/src/lib/components/input/Input.svelte index 30aa7e15b..97a028dc8 100644 --- a/frontend/src/lib/components/input/Input.svelte +++ b/frontend/src/lib/components/input/Input.svelte @@ -18,7 +18,7 @@ The input itself is wrapped in multiple container elements, the outermost of whi - `textarea`: A multi-line text input. - `textarea-multilingual`: A multilingual multi-line text input. - `label`: The label to show for the input or group of inputs if `multilingual`. -- `containerProps`: Any additional props to be passed to the container element of the input. @default {} +- `containerProps`: Any additional props to be passed to the container element of the input. - `id`: The id of the input. If not provided, a unique id will be generated. - `info`: Additional info displayed below the input. - `disabled`: Works the same way as a normal `input`'s `disabled` attribute. diff --git a/frontend/src/lib/components/input/PreviewAllInputs.svelte b/frontend/src/lib/components/input/PreviewAllInputs.svelte index bbfd49bfa..a5fdd6c66 100644 --- a/frontend/src/lib/components/input/PreviewAllInputs.svelte +++ b/frontend/src/lib/components/input/PreviewAllInputs.svelte @@ -1,12 +1,32 @@ +<!-- +@component +Preview component displaying all available input types. + +### Properties + +- `info`: Optional info text to display with inputs. +- `locked`: Whether inputs are locked. Default: `false` +- Any valid attributes of a `<div>` element + +### Usage + +```tsx +<PreviewAllInputs info="Example info" locked={false} /> +``` +--> + <script lang="ts"> import { Input } from '.'; + import type { PreviewAllInputsProps } from './PreviewAllInputs.type'; + + type $$Props = PreviewAllInputsProps; + + export let info: $$Props['info'] = undefined; + export let locked: $$Props['locked'] = false; function onChange(value: unknown) { console.info('New value:', value); } - - export let info: string | undefined = undefined; - export let locked = false; </script> <div class="mb-[10rem] flex w-[30rem] flex-col gap-md"> diff --git a/frontend/src/lib/components/input/PreviewAllInputs.type.ts b/frontend/src/lib/components/input/PreviewAllInputs.type.ts new file mode 100644 index 000000000..57b5e77cc --- /dev/null +++ b/frontend/src/lib/components/input/PreviewAllInputs.type.ts @@ -0,0 +1,12 @@ +import type { SvelteHTMLElements } from 'svelte/elements'; + +export type PreviewAllInputsProps = SvelteHTMLElements['div'] & { + /** + * Optional info text to display with inputs. + */ + info?: string; + /** + * Whether inputs are locked. @default false + */ + locked?: boolean; +}; diff --git a/frontend/src/lib/components/input/index.ts b/frontend/src/lib/components/input/index.ts index f24890611..a57399a6f 100644 --- a/frontend/src/lib/components/input/index.ts +++ b/frontend/src/lib/components/input/index.ts @@ -2,6 +2,8 @@ export { default as Input } from './Input.svelte'; export * from './Input.type'; export { default as InputGroup } from './InputGroup.svelte'; export * from './InputGroup.type'; +export { default as PreviewAllInputs } from './PreviewAllInputs.svelte'; +export * from './PreviewAllInputs.type'; export { default as QuestionInput } from './QuestionInput.svelte'; export * from './QuestionInput.type'; export * from './shared'; diff --git a/frontend/src/lib/components/loading/Loading.svelte b/frontend/src/lib/components/loading/Loading.svelte index 797018e8b..4139ea182 100644 --- a/frontend/src/lib/components/loading/Loading.svelte +++ b/frontend/src/lib/components/loading/Loading.svelte @@ -4,10 +4,10 @@ Used to display a loading spinner with an optionally visible text label. ### Properties -- `inline`: Whether to show an inline version of the spinner. By default the spinner tries to center itself in the available area. @default `false` -- `label`: he label text. @default `$t('common.loading')` -- `showLabel`: Whether to show the text label. The label will always be shown to screen readers. @default `false` -- `size`: The size of the loading spinner. @default `'lg'` +- `inline`: Whether to show an inline version of the spinner. By default the spinner tries to center itself in the available area. Default: `false` +- `label`: The label text. Default: `$t('common.loading')` +- `showLabel`: Whether to show the text label. The label will always be shown to screen readers. Default: `false` +- `size`: The size of the loading spinner. Default: `'lg'` - Any valid attributes of a `<div>` element. ### Usage diff --git a/frontend/src/lib/components/matchScore/MatchScore.svelte b/frontend/src/lib/components/matchScore/MatchScore.svelte index ec10ca6d0..125984298 100644 --- a/frontend/src/lib/components/matchScore/MatchScore.svelte +++ b/frontend/src/lib/components/matchScore/MatchScore.svelte @@ -5,7 +5,8 @@ Display an entity's match score. ### Properties - `score`: The match score as a `string` or a `number`. Note that `$t('components.matchScore.label')` will be used display the score. -- `label`: The label to display under the score. @default `$t('components.matchScore.label')` +- `label`: The label to display under the score. Default: `$t('components.matchScore.label')` +- `showLabel`: Whether to show the label. Default: `true` - Any valid attributes of a `<div>` element ### Usage diff --git a/frontend/src/lib/components/modal/Modal.svelte b/frontend/src/lib/components/modal/Modal.svelte index 9e5830ab4..ea6df6e23 100644 --- a/frontend/src/lib/components/modal/Modal.svelte +++ b/frontend/src/lib/components/modal/Modal.svelte @@ -1,7 +1,7 @@ <!-- @component A modal dialog. -See [`<ModalContainer>` component](./ModalContainer.svelte) for more information. +See `<ModalContainer>` component for more information. ### Slots @@ -26,7 +26,7 @@ See [`<ModalContainer>` component](./ModalContainer.svelte) for more information ### Accessibility -See the [`<ModalContainer>` component](../ModalContainer.svelte) documentation for more information. +See the `<ModalContainer>` component documentation for more information. ### Usage diff --git a/frontend/src/lib/components/modal/ModalContainer.svelte b/frontend/src/lib/components/modal/ModalContainer.svelte index 2f83ff369..5ae36233c 100644 --- a/frontend/src/lib/components/modal/ModalContainer.svelte +++ b/frontend/src/lib/components/modal/ModalContainer.svelte @@ -91,7 +91,7 @@ A modal dialog. /** * Close the dialog by pressing the escape key. NB. Some browsers implement a default behaviour for the escape key, which closes the dialog, but this prevents us from performing cleanup, so we need a custom event handler. - * @param e The keyboard event. + * @param e - The keyboard event. */ function handleEscape(e: KeyboardEvent) { if (isOpen && e.key == 'Escape') { diff --git a/frontend/src/lib/components/modal/confirmation/ConfirmationModal.svelte b/frontend/src/lib/components/modal/confirmation/ConfirmationModal.svelte index e0634f34c..dc8901f68 100644 --- a/frontend/src/lib/components/modal/confirmation/ConfirmationModal.svelte +++ b/frontend/src/lib/components/modal/confirmation/ConfirmationModal.svelte @@ -27,7 +27,7 @@ A modal dialog that will automatically close after a set amount of time. ### Accessibility -See the [`<Modal>` component](../Modal.svelte) documentation for more information. +See the `<Modal>` component documentation for more information. ### Usage diff --git a/frontend/src/lib/components/modal/drawer/Drawer.svelte b/frontend/src/lib/components/modal/drawer/Drawer.svelte index e596591f5..1767e1bc1 100644 --- a/frontend/src/lib/components/modal/drawer/Drawer.svelte +++ b/frontend/src/lib/components/modal/drawer/Drawer.svelte @@ -25,7 +25,7 @@ A modal dialog that looks like a drawer. ### Accessibility -See the [`<ModalContainer>` component](../ModalContainer.svelte) documentation for more information. +See the `<ModalContainer>` component documentation for more information. ### Usage diff --git a/frontend/src/lib/components/modal/timed/TimedModal.svelte b/frontend/src/lib/components/modal/timed/TimedModal.svelte index f83892fd7..b06e803f8 100644 --- a/frontend/src/lib/components/modal/timed/TimedModal.svelte +++ b/frontend/src/lib/components/modal/timed/TimedModal.svelte @@ -27,7 +27,7 @@ A modal dialog that will automatically close after a set amount of time. ### Accessibility -See the [`<Modal>` component](../Modal.svelte) documentation for more information. +See the `<Modal>` component documentation for more information. ### Usage diff --git a/frontend/src/lib/components/openVAALogo/OpenVAALogo.svelte b/frontend/src/lib/components/openVAALogo/OpenVAALogo.svelte index 4f30aa7ba..ae420a21b 100644 --- a/frontend/src/lib/components/openVAALogo/OpenVAALogo.svelte +++ b/frontend/src/lib/components/openVAALogo/OpenVAALogo.svelte @@ -8,15 +8,10 @@ attributes of one. ### Properties -- `title`: The `<title>` of the SVG logo. Functions much the same way as the `alt`` - attribute of an `<img>`. -- `color`: The color of the logo as one of the predefined colours. - For arbitrary values, you can supply a `class` property, such as - `fill-[#123456]`. @default `'neutral'` - - `size`: The size of the logo as one of the predefined sizes 'sm', 'md' or 'lg'. - For arbitrary values, you can supply a `class` attribute, such as - `class="h-[3.5rem]"`. @default `'md'` - - Any valid attributes of a `<svg>` element +- `title`: The `<title>` of the SVG logo. Functions much the same way as the `alt` attribute of an `<img>`. Default: `'OpenVAA'` +- `size`: The size of the logo as one of the predefined sizes 'sm', 'md' or 'lg'. For arbitrary values, you can supply a `class` property, such as `h-[3.15rem] w-[3.15rem]`. Default: `'md'` +- `color`: The color of the logo as one of the predefined colours. For arbitrary values, you can supply a `class` property, such as `fill-[#123456]`. Default: `'neutral'` +- Any valid attributes of a `<svg>` element. ### Usage diff --git a/frontend/src/lib/components/questions/QuestionChoices.svelte b/frontend/src/lib/components/questions/QuestionChoices.svelte index 5bca2954e..b3f80f833 100644 --- a/frontend/src/lib/components/questions/QuestionChoices.svelte +++ b/frontend/src/lib/components/questions/QuestionChoices.svelte @@ -188,7 +188,7 @@ The same component can also be used to display the answers of the voter and anot * * NB. The event will only be dispatched if a valid value is either supplied or selected in the radio group. * - * @param value Optional value that overrides the current value of the radio group. This should be passed when invoking this function from the `click` event handler, because it is fired before the radio group's value is updated. @default undefined + * @param value - Optional value that overrides the current value of the radio group. This should be passed when invoking this function from the `click` event handler, because it is fired before the radio group's value is updated. @default undefined */ function triggerCallback(value?: Id | null): void { // Use the selected value if no value is supplied diff --git a/frontend/src/lib/components/successMessage/SuccessMessage.svelte b/frontend/src/lib/components/successMessage/SuccessMessage.svelte index d787cffa1..ff849744f 100644 --- a/frontend/src/lib/components/successMessage/SuccessMessage.svelte +++ b/frontend/src/lib/components/successMessage/SuccessMessage.svelte @@ -4,8 +4,8 @@ Used to display a message when an action succeeds. ### Properties -- `inline`: Whether to show an inline version of the message. By default the message tries to center itself in the available area and displays a large emoji. @default `false` -- `message`: The error message to display. Default `$t('common.success')` +- `inline`: Whether to show an inline version of the message. By default the message tries to center itself in the available area and displays a large emoji. Default: `false` +- `message`: The message to display. Default: `$t('common.success')` - Any valid attributes of a `<div>` element. ### Usage diff --git a/frontend/src/lib/components/video/Video.svelte b/frontend/src/lib/components/video/Video.svelte index 4de1d7ec0..0b150ad97 100644 --- a/frontend/src/lib/components/video/Video.svelte +++ b/frontend/src/lib/components/video/Video.svelte @@ -58,7 +58,7 @@ If not provided, the `video` element will be hidden until these properties are p ### Tracking events -- `video`: The video player creates an analytics event for each video viewed which combines a number of properties. See the `VideoTrackingEventData` in [`Video.type.ts`](./Video.type.ts) for a complete description. The event is started and submitted when: +- `video`: The video player creates an analytics event for each video viewed which combines a number of properties. See the `VideoTrackingEventData` in `Video.type.ts` for a complete description. The event is started and submitted when: - the component is created/destroyed - when the video shown is changed with `reload` - when the page's visibility changes to `hidden`. @@ -219,7 +219,7 @@ If not provided, the `video` element will be hidden until these properties are p /** * Call when explicitly toggling play/paused to use `errorCheckInterval` to check whether we should show an error message. This is automatically called by `setPaused()`. - * @param value Whether the video should be playing + * @param value - Whether the video should be playing */ function setShouldPlay(value: boolean): void { if (!hasContent || !value) { @@ -390,7 +390,7 @@ If not provided, the `video` element will be hidden until these properties are p /** * Fired when the invisible jump areas of the screen are pressed. We treat this clicks as jumps only if the video is not paused. - * @param steps Passed to `jump` + * @param steps - Passed to `jump` */ function screenJump(steps: number): void { if (!playing || steps === 0) { @@ -412,7 +412,7 @@ If not provided, the `video` element will be hidden until these properties are p /** * Toggle video playback or replay. - * @param action The action to perform: `'play'`, `'pause'` or `'replay'`. Default: Toggle between `'play'` and `'pause'`. + * @param action - The action to perform: `'play'`, `'pause'` or `'replay'`. Default: Toggle between `'play'` and `'pause'`. */ export function togglePlay(action?: PlayButtonAction): void { if (action == null) action = playButtonAction; @@ -426,7 +426,7 @@ If not provided, the `video` element will be hidden until these properties are p /** * Toggle sound - * @param unmuted If defined will define whether sounds are unmuted, otherwise sound will be toggled. + * @param unmuted - If defined will define whether sounds are unmuted, otherwise sound will be toggled. */ export function toggleSound(unmute?: boolean): void { unmute ??= muted; @@ -436,7 +436,7 @@ If not provided, the `video` element will be hidden until these properties are p /** * Show or hide captions. - * @param show If defined will define whether the captions should be shown, otherwise their visibility will be toggled. + * @param show - If defined will define whether the captions should be shown, otherwise their visibility will be toggled. */ export function toggleCaptions(show?: boolean): void { const track = getTrack(); @@ -449,7 +449,7 @@ If not provided, the `video` element will be hidden until these properties are p /** * Toggle transcript visibility. - * @param show If defined will define whether the transcript should be shown, otherwise its visibility will be toggled. + * @param show - If defined will define whether the transcript should be shown, otherwise its visibility will be toggled. */ export function toggleTranscript(show?: boolean): void { if (!hasContent) return; @@ -468,7 +468,7 @@ If not provided, the `video` element will be hidden until these properties are p /** * Skip the video a number of steps based on text track cues or `skipAmount` if cues are not available. If the video is in the end, a `steps` of `-1` will be skip to the beginning of the last cue. If `steps` would result in a negative index or one greater than the number of cues, the video will be scrolled to the beginning or the end. - * @param steps A positive or negative number of steps to skip. If zero, the current cue will be rewound. + * @param steps - A positive or negative number of steps to skip. If zero, the current cue will be rewound. */ export function jump(steps: number): void { if (!video || !hasContent || (steps > 0 && atEnd)) return; @@ -498,7 +498,7 @@ If not provided, the `video` element will be hidden until these properties are p /** * Try to play or pause the video. - * @param paused Whether to pause or play the video. + * @param paused - Whether to pause or play the video. */ function setPaused(paused: boolean): void { if (!video || !hasContent) return; @@ -615,7 +615,7 @@ If not provided, the `video` element will be hidden until these properties are p /** * Returns the index of the cue which would have been the current or the last to display for the given time, or -1 if the first cue would not have been displayed yet. - * @param timepoint The time point on the video to check. + * @param timepoint - The time point on the video to check. * @returns The index of the cue or `-1` if the first cue would not have been displayed. * @throws Error if no cues are available. */ diff --git a/frontend/src/lib/contexts/README.md b/frontend/src/lib/contexts/README.md new file mode 100644 index 000000000..5329d7885 --- /dev/null +++ b/frontend/src/lib/contexts/README.md @@ -0,0 +1,17 @@ +# Contexts + +> See also the online doc [Contexts](https://openvaa.org/developers-guide/frontend/contexts) (or [locally](/docs/src/routes/developers-guide/frontend/contexts/+page.md)) + +This directory contains context providers for managing application state throughout the OpenVAA frontend, including: + +- `DataContext` - Access to the `@openvaa/data` model +- `VoterContext` - Voter state (answers, results) +- `CandidateContext` - Candidate authentication and data +- `AppContext` - Application settings +- `I18nContext` - Localization +- `ComponentContext` - Component state +- `AuthContext` - Authentication state +- `LayoutContext` - Layout state +- `AdminContext` - Admin functionality + +See the documentation for detailed information on how to use these contexts. diff --git a/frontend/src/lib/contexts/app/appContext.type.ts b/frontend/src/lib/contexts/app/appContext.type.ts index 5be4e5364..cbfd6077b 100644 --- a/frontend/src/lib/contexts/app/appContext.type.ts +++ b/frontend/src/lib/contexts/app/appContext.type.ts @@ -52,27 +52,27 @@ export type AppContext = ComponentContext & sendFeedback: FeedbackWriter['postFeedback']; /** * Start the countdown for the feedback popup, after which it will be shown on next page load. This will do nothing, if the user has already given their feedback. - * @param delay The delay in seconds. @default 180 (3 minutes) + * @param delay - The delay in seconds. @default 180 (3 minutes) */ startFeedbackPopupCountdown: (delay?: number) => void; /** * Start the countdown for the survey popup, after which it will be shown on next page load. This will do nothing, if the user has already opened the survey. - * @param delay The delay in seconds. @default 300 (5 minutes) + * @param delay - The delay in seconds. @default 300 (5 minutes) */ startSurveyPopupCountdown: (delay?: number) => void; /** * Set the user's data consent together with the date of giving or denying the consent. - * @param consent The value for the consent + * @param consent - The value for the consent */ setDataConsent: (consent: UserDataCollectionConsent) => void; /** * Set the user's feedback status together with the date of update. - * @param status The value for the status + * @param status - The value for the status */ setFeedbackStatus: (status: UserFeedbackStatus) => void; /** * Set the user's survey filling status together with the date of update. - * @param status The value for the status + * @param status - The value for the status */ setSurveyStatus: (status: UserFeedbackStatus) => void; }; diff --git a/frontend/src/lib/contexts/utils/storageStore.ts b/frontend/src/lib/contexts/utils/storageStore.ts index d7dbb0dd2..1457c839d 100644 --- a/frontend/src/lib/contexts/utils/storageStore.ts +++ b/frontend/src/lib/contexts/utils/storageStore.ts @@ -18,8 +18,8 @@ type LocallyStoredValue<TData> = { /** * Create a store that is persisted in `localStorage` and initiate it unless a saved value is found. The version number saved with the item is checked and the data is expired if it does match the required version defined in `settings`. * NB. The type of `defaultValue` should be one that can be serialized to JSON. We cannot define typing for that in a satisfactory way due to TS limitations. See: https://github.com/microsoft/TypeScript/issues/1897#issuecomment-1415776159 - * @param key The key to store the value under. - * @param defaultValue The default value for the store. + * @param key - The key to store the value under. + * @param defaultValue - The default value for the store. */ export function localStorageWritable<TValue>(key: string, defaultValue: TValue) { return storageWritable('localStorage', key, defaultValue); @@ -28,8 +28,8 @@ export function localStorageWritable<TValue>(key: string, defaultValue: TValue) /** * Create a store that is persisted in `sessionStorage` and initiate it unless a saved value is found. * NB. The type of `defaultValue` should be one that can be serialized to JSON. We cannot define typing for that in a satisfactory way due to TS limitations. See: https://github.com/microsoft/TypeScript/issues/1897#issuecomment-1415776159 - * @param key The key to store the value under. - * @param defaultValue The default value for the store. + * @param key - The key to store the value under. + * @param defaultValue - The default value for the store. */ export function sessionStorageWritable<TValue>(key: string, defaultValue: TValue) { return storageWritable('sessionStorage', key, defaultValue); @@ -38,9 +38,9 @@ export function sessionStorageWritable<TValue>(key: string, defaultValue: TValue /** * Create a store that is persisted in `localStorage` or `sessionStorage` and initiate it unless a saved value is found. * NB. The type of `defaultValue` should be one that can be serialized to JSON. We cannot define typing for that in a satisfactory way due to TS limitations. See: https://github.com/microsoft/TypeScript/issues/1897#issuecomment-1415776159 - * @param type The type of storage to use. - * @param key The key to store the value under. - * @param defaultValue The default value for the store. + * @param type - The type of storage to use. + * @param key - The key to store the value under. + * @param defaultValue - The default value for the store. */ export function storageWritable<TValue>(type: StorageType, key: string, defaultValue: TValue): Writable<TValue> { const value = getItemFromStorage<TValue>(type, key); diff --git a/frontend/src/lib/dynamic-components/README.md b/frontend/src/lib/dynamic-components/README.md new file mode 100644 index 000000000..38a7823b1 --- /dev/null +++ b/frontend/src/lib/dynamic-components/README.md @@ -0,0 +1,7 @@ +# Dynamic Components + +> See also the online doc [Components](https://openvaa.org/developers-guide/frontend/components) (or [locally](/docs/src/routes/developers-guide/frontend/components/+page.md)) + +This directory contains dynamic components used throughout the OpenVAA frontend application. + +For auto-generated component documentation, see the [generated components documentation](https://openvaa.org/developers-guide/frontend/components/generated). diff --git a/frontend/src/lib/dynamic-components/appLogo/AppLogo.svelte b/frontend/src/lib/dynamic-components/appLogo/AppLogo.svelte index 64002c7e2..6be371a3f 100644 --- a/frontend/src/lib/dynamic-components/appLogo/AppLogo.svelte +++ b/frontend/src/lib/dynamic-components/appLogo/AppLogo.svelte @@ -10,11 +10,10 @@ Logo files for use on a light and a dark background can be defined. If the latte ### Properties -- `alt`: The `alt` text for the logo image. -- `inverse`: If `true`, the light and dark versions of the logo will be reversed. Set to `true` if using the logo on a dark background. @default `false` -- `size`: The size of the logo as one of the predefined sizes 'sm', 'md' or 'lg'. For arbitrary values, you can supply a `class` attribute, such as `class="h-[3.5rem]"`. @default `'md'` -- `class`: Additional class string to append to the element's default classes. -- Any valid attributes of the `<div>` element wrapping the light and dark `<img>` elements +- `alt`: The `alt` text for the logo image. If missing, the publisher name or 'OpenVAA' will be used, depending on the logo shown. +- `inverse`: If `true`, the light and dark versions of the logo will be reversed. Set to `true` if using the logo on a dark background. Default: `false` +- `size`: The size of the logo as one of the predefined sizes 'sm', 'md' or 'lg'. For arbitrary values, you can supply a `class` attribute, such as `class="h-[3.5rem]"`. Default: `'md'` +- Any valid attributes of a `<div>` element ### Usage diff --git a/frontend/src/lib/dynamic-components/dataConsent/DataConsent.svelte b/frontend/src/lib/dynamic-components/dataConsent/DataConsent.svelte index 06aca1b97..cc995a86d 100644 --- a/frontend/src/lib/dynamic-components/dataConsent/DataConsent.svelte +++ b/frontend/src/lib/dynamic-components/dataConsent/DataConsent.svelte @@ -8,10 +8,10 @@ Accesses `AppContext` to set and read `userPreferences`. ### Properties -- `description`: Whether and how to show the data consent description. @default `modal` - - `none`: Don’t show the description. - - `inline`: Show the consent description above the buttons. - - `modal`: Show a button that opens the description in a modal. +- `description`: Whether and how to show the data consent description. Default: `'modal'` + - `'none'`: Don't show the description. + - `'inline'`: Show the consent description above the buttons. + - `'modal'`: Show a button that opens the description in a modal. - Any valid attributes of a `<div>` element. ### Events diff --git a/frontend/src/lib/dynamic-components/entityCard/EntityCard.svelte b/frontend/src/lib/dynamic-components/entityCard/EntityCard.svelte index bfb2604ad..02a9f2b02 100644 --- a/frontend/src/lib/dynamic-components/entityCard/EntityCard.svelte +++ b/frontend/src/lib/dynamic-components/entityCard/EntityCard.svelte @@ -12,14 +12,14 @@ This is a dynamic component, because it accesses the `dataRoot` and other proper ### Properties -- `action`: Custom action to take when the card is clicked, defaults to a link to the entity’s `ResultEntity` route. If the card has subentites, the action will only be triggered by clicking the content above them. -- `entity`: A possibly ranked nakedEntity, e.g. candidate or a party. -- `variant`: The variant-dependend layout variant. Usually set automatically. - - `'list'`: In a list of entities. The default. +- `action`: Custom action to take when the card is clicked, defaults to a link to the entity's `ResultEntity` route. If the card has subentites, the action will only be triggered by clicking the content above them. +- `entity`: A possibly ranked entity, e.g. candidate or a party. +- `variant`: The context-dependend layout variant. Usually set automatically. Default: `'list'` + - `'list'`: In a list of entities. - `'details'`: As part of the header of `EntityDetails`. - - `'subcard'`; In a list of nested nakedEntity cards, e.g., the candidates for a party. -- `maxSubcards`: The maximum number of sub-entities to show. If there are more a button will be shown for displaying the remaining ones. @default `3` -- `showElection`: Whether to show the possible nomination’s election and constituency. @default `false` + - `'subcard'`: In a list of nested entity cards, e.g., the candidates for a party. +- `maxSubcards`: The maximum number of sub-entities to show. If there are more a button will be shown for displaying the remaining ones. Default: `3` +- `showElection`: Whether to show the possible nomination's election and constituency. Default: `false` - Any valid attributes of an `<article>` element. ### Tracking events diff --git a/frontend/src/lib/dynamic-components/entityCard/EntityCardAction.svelte b/frontend/src/lib/dynamic-components/entityCard/EntityCardAction.svelte index 44f00a43f..62fed30f0 100644 --- a/frontend/src/lib/dynamic-components/entityCard/EntityCardAction.svelte +++ b/frontend/src/lib/dynamic-components/entityCard/EntityCardAction.svelte @@ -6,7 +6,7 @@ TODO[Svelte 5]: Maybe convert into `$snippet`. ### Properties - `action`: The action to take when the part or card is clicked. -- `shadeOnHover`: Whether to shade the element on hover. Use when applying to subcards or their parent card's header. @default `false` +- `shadeOnHover`: Whether to shade the element on hover. Use when applying to subcards or their parent card's header. Default: `false` - Any valid attributes common to HTML elements. Note that these will only be applied if `<EntityCardAction>` is rendered. ### Slots diff --git a/frontend/src/lib/dynamic-components/entityDetails/EntityChildren.svelte b/frontend/src/lib/dynamic-components/entityDetails/EntityChildren.svelte index f9d35a0b0..6a22e33f2 100644 --- a/frontend/src/lib/dynamic-components/entityDetails/EntityChildren.svelte +++ b/frontend/src/lib/dynamic-components/entityDetails/EntityChildren.svelte @@ -4,9 +4,10 @@ Used to show an entity's children in an `EntityDetails` component. ### Properties -- `entities`: An array of possibly ranked entities, e.g. a party’s candidates. -- `entityType`: The type of the entities being displayed. Used to pick correct translations +- `entities`: An array of possibly ranked entities, e.g. a party's candidates. +- `entityType`: The type of the entities being displayed. Used to pick correct translations. - `action`: An optional callback for building the card actions for the child possible entities. If nullish, the default action filled in by `EntityCard` will be used. If `false`, no actions will be added. +- Any valid attributes of a `<div>` element ### Usage @@ -19,12 +20,14 @@ Used to show an entity's children in an `EntityDetails` component. import { getComponentContext } from '$lib/contexts/component'; import { EntityList } from '$lib/dynamic-components/entityList'; import { EntityListControls } from '../entityList'; - import type { EntityType } from '@openvaa/data'; - import type { CardAction, EntityCardProps } from '../entityCard'; + import type { EntityCardProps } from '../entityCard'; + import type { EntityChildrenProps } from './EntityChildren.type'; - export let entities: Array<MaybeWrappedEntityVariant>; - export let entityType: EntityType; - export let action: ((entity: MaybeWrappedEntityVariant) => CardAction) | false | null | undefined = undefined; + type $$Props = EntityChildrenProps; + + export let entities: $$Props['entities']; + export let entityType: $$Props['entityType']; + export let action: $$Props['action'] = undefined; //////////////////////////////////////////////////////////////////// // Get contexts diff --git a/frontend/src/lib/dynamic-components/entityDetails/EntityChildren.type.ts b/frontend/src/lib/dynamic-components/entityDetails/EntityChildren.type.ts new file mode 100644 index 000000000..b48b291f4 --- /dev/null +++ b/frontend/src/lib/dynamic-components/entityDetails/EntityChildren.type.ts @@ -0,0 +1,18 @@ +import type { EntityType } from '@openvaa/data'; +import type { SvelteHTMLElements } from 'svelte/elements'; +import type { CardAction } from '../entityCard'; + +export type EntityChildrenProps = SvelteHTMLElements['div'] & { + /** + * An array of possibly ranked entities, e.g. a party's candidates. + */ + entities: Array<MaybeWrappedEntityVariant>; + /** + * The type of the entities being displayed. Used to pick correct translations. + */ + entityType: EntityType; + /** + * An optional callback for building the card actions for the child possible entities. If nullish, the default action filled in by `EntityCard` will be used. If `false`, no actions will be added. + */ + action?: ((entity: MaybeWrappedEntityVariant) => CardAction) | false | null; +}; diff --git a/frontend/src/lib/dynamic-components/entityDetails/EntityInfo.svelte b/frontend/src/lib/dynamic-components/entityDetails/EntityInfo.svelte index 3889983ed..2824a7d75 100644 --- a/frontend/src/lib/dynamic-components/entityDetails/EntityInfo.svelte +++ b/frontend/src/lib/dynamic-components/entityDetails/EntityInfo.svelte @@ -9,7 +9,8 @@ This is a dynamic component, because it accesses `appSettings` and `dataRoot` fr ### Properties - `entity`: A possibly ranked entity, e.g. candidate or a party. -- `questions`: An array of `info` questions +- `questions`: An array of `info` questions. +- Any valid attributes of a `<div>` element ### Settings @@ -28,7 +29,6 @@ This is a dynamic component, because it accesses `appSettings` and `dataRoot` fr import { type AnyEntityVariant, type AnyNominationVariant, - type AnyQuestionVariant, ENTITY_TYPE, type EntityType, isObjectType, @@ -42,10 +42,12 @@ This is a dynamic component, because it accesses `appSettings` and `dataRoot` fr import { unwrapEntity } from '$lib/utils/entities'; import { sanitizeHtml } from '$lib/utils/sanitize'; import InfoItem from './InfoItem.svelte'; - import type { EntityDetailsProps } from './EntityDetails.type'; + import type { EntityInfoProps } from './EntityInfo.type'; - export let entity: EntityDetailsProps['entity']; - export let questions: Array<AnyQuestionVariant>; + type $$Props = EntityInfoProps; + + export let entity: $$Props['entity']; + export let questions: $$Props['questions']; //////////////////////////////////////////////////////////////////// // Get contexts diff --git a/frontend/src/lib/dynamic-components/entityDetails/EntityInfo.type.ts b/frontend/src/lib/dynamic-components/entityDetails/EntityInfo.type.ts new file mode 100644 index 000000000..0527f27ca --- /dev/null +++ b/frontend/src/lib/dynamic-components/entityDetails/EntityInfo.type.ts @@ -0,0 +1,14 @@ +import type { AnyQuestionVariant } from '@openvaa/data'; +import type { SvelteHTMLElements } from 'svelte/elements'; +import type { EntityDetailsProps } from './EntityDetails.type'; + +export type EntityInfoProps = SvelteHTMLElements['div'] & { + /** + * A possibly ranked entity, e.g. candidate or a party. + */ + entity: EntityDetailsProps['entity']; + /** + * An array of `info` questions. + */ + questions: Array<AnyQuestionVariant>; +}; diff --git a/frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.svelte b/frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.svelte index a029d3be7..9c80eb680 100644 --- a/frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.svelte +++ b/frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.svelte @@ -6,7 +6,8 @@ Used to show an entity's answers to `opinion` questions and possibly those of th - `entity`: A possibly ranked entity, e.g. candidate or a party. - `questions`: An array of `opinion` questions. -- `answers`: An optional `AnswerStore` with the Voter’s answers to the questions. +- `answers`: An optional `AnswerStore` with the Voter's answers to the questions. +- Any valid attributes of a `<div>` element ### Usage @@ -21,13 +22,14 @@ Used to show an entity's answers to `opinion` questions and possibly those of th import { OpinionQuestionInput, QuestionOpenAnswer } from '$lib/components/questions'; import { getAppContext } from '$lib/contexts/app'; import { unwrapEntity } from '$lib/utils/entities'; - import type { AnyEntityVariant, AnyQuestionVariant } from '@openvaa/data'; - import type { AnswerStore } from '$lib/contexts/voter'; - import type { EntityDetailsProps } from './EntityDetails.type'; + import type { AnyEntityVariant } from '@openvaa/data'; + import type { EntityOpinionsProps } from './EntityOpinions.type'; - export let entity: EntityDetailsProps['entity']; - export let questions: Array<AnyQuestionVariant>; - export let answers: AnswerStore | undefined = undefined; + type $$Props = EntityOpinionsProps; + + export let entity: $$Props['entity']; + export let questions: $$Props['questions']; + export let answers: $$Props['answers'] = undefined; //////////////////////////////////////////////////////////////////// // Get contexts diff --git a/frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.type.ts b/frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.type.ts new file mode 100644 index 000000000..c96327f1d --- /dev/null +++ b/frontend/src/lib/dynamic-components/entityDetails/EntityOpinions.type.ts @@ -0,0 +1,19 @@ +import type { AnyQuestionVariant } from '@openvaa/data'; +import type { SvelteHTMLElements } from 'svelte/elements'; +import type { AnswerStore } from '$lib/contexts/voter'; +import type { EntityDetailsProps } from './EntityDetails.type'; + +export type EntityOpinionsProps = SvelteHTMLElements['div'] & { + /** + * A possibly ranked entity, e.g. candidate or a party. + */ + entity: EntityDetailsProps['entity']; + /** + * An array of `opinion` questions. + */ + questions: Array<AnyQuestionVariant>; + /** + * An optional `AnswerStore` with the Voter's answers to the questions. + */ + answers?: AnswerStore; +}; diff --git a/frontend/src/lib/dynamic-components/entityDetails/InfoItem.svelte b/frontend/src/lib/dynamic-components/entityDetails/InfoItem.svelte index 38b40ede7..d4797ab50 100644 --- a/frontend/src/lib/dynamic-components/entityDetails/InfoItem.svelte +++ b/frontend/src/lib/dynamic-components/entityDetails/InfoItem.svelte @@ -4,8 +4,9 @@ Used to show a label-content pair in a Candidate's basic information. ### Properties -- `label`: the label of the information -- `vertical`: layout mode for the item +- `label`: The label of the information. +- `vertical`: Layout mode for the item. Default: `false` +- Any valid attributes of a `<div>` element ### Slots @@ -21,10 +22,12 @@ Used to show a label-content pair in a Candidate's basic information. --> <script lang="ts"> - /** The info label */ - export let label: string; - /** The info layout mode */ - export let vertical = false; + import type { InfoItemProps } from './InfoItem.type'; + + type $$Props = InfoItemProps; + + export let label: $$Props['label']; + export let vertical: $$Props['vertical'] = false; </script> <div class="grid justify-start gap-md {vertical ? 'vertical-grid' : 'horizontal-grid'}"> diff --git a/frontend/src/lib/dynamic-components/entityDetails/InfoItem.type.ts b/frontend/src/lib/dynamic-components/entityDetails/InfoItem.type.ts new file mode 100644 index 000000000..e3248efd0 --- /dev/null +++ b/frontend/src/lib/dynamic-components/entityDetails/InfoItem.type.ts @@ -0,0 +1,12 @@ +import type { SvelteHTMLElements } from 'svelte/elements'; + +export type InfoItemProps = SvelteHTMLElements['div'] & { + /** + * The label of the information. + */ + label: string; + /** + * Layout mode for the item. @default false + */ + vertical?: boolean; +}; diff --git a/frontend/src/lib/dynamic-components/entityDetails/index.ts b/frontend/src/lib/dynamic-components/entityDetails/index.ts index 92236e8f8..54e046dc1 100644 --- a/frontend/src/lib/dynamic-components/entityDetails/index.ts +++ b/frontend/src/lib/dynamic-components/entityDetails/index.ts @@ -1,7 +1,12 @@ export { default as EntityChildren } from './EntityChildren.svelte'; +export * from './EntityChildren.type'; export { default as EntityDetails } from './EntityDetails.svelte'; export * from './EntityDetails.type'; export { default as EntityDetailsDrawer } from './EntityDetailsDrawer.svelte'; export * from './EntityDetailsDrawer.type'; export { default as EntityInfo } from './EntityInfo.svelte'; +export * from './EntityInfo.type'; export { default as EntityOpinions } from './EntityOpinions.svelte'; +export * from './EntityOpinions.type'; +export { default as InfoItem } from './InfoItem.svelte'; +export * from './InfoItem.type'; diff --git a/frontend/src/lib/dynamic-components/entityList/EntityListControls.svelte b/frontend/src/lib/dynamic-components/entityList/EntityListControls.svelte index a45da8189..3f09f4d2d 100644 --- a/frontend/src/lib/dynamic-components/entityList/EntityListControls.svelte +++ b/frontend/src/lib/dynamic-components/entityList/EntityListControls.svelte @@ -7,14 +7,11 @@ TODO: Consider moving the tracking events away from the component and just addin ### Properties - `entities`: A list of possibly ranked entities, e.g. candidates or a parties. -- `filterGroup`: The filters applied to the entities -- `searchProperty`: The property used for the search tool. Default 'name' +- `filterGroup`: The filters applied to the contents. +- `searchProperty`: The property used for the search tool. Default: `'name'` +- `onUpdate`: Callback for when the filters are applied. - Any valid attributes of a `<div>` element. -### Callbacks - -- `onUpdate`: Callback for when the filters are applied. - ### Tracking events - `filters_reset` diff --git a/frontend/src/lib/dynamic-components/feedback/Feedback.svelte b/frontend/src/lib/dynamic-components/feedback/Feedback.svelte index 3eeaad309..96c23a4f0 100644 --- a/frontend/src/lib/dynamic-components/feedback/Feedback.svelte +++ b/frontend/src/lib/dynamic-components/feedback/Feedback.svelte @@ -8,14 +8,16 @@ Accesses the `AppContext` and the `FeedbackWriter` api. ### Properties +- `showActions`: Whether to show the standard action buttons below the feedback form. Default: `true` +- `variant`: The layout variant of the feedback form. Default: `'default'` - Any valid attributes of a `<form>` element. -### Bindable properties and functions +### Bindable properties -- `status`: The status of the feedback form. @default `'default'` -- `canSubmit`: Bind to this to know whether the feedback can be submitted, i.e. the user has entered something. @default `false` -- `reset()`: Reset the form so that if the user opens it again, they can fill new feedback. You should call this when closing any modal containing the feedback. -- `submit()`: Submit the feedback. +- `canSubmit`: Bind to this to know whether the feedback can be submitted, i.e. the user has entered something. Default: `false` +- `status`: Bind to this to access the status of the feedback form. Default: `'default'` +- `submit`: Submit the feedback or close the modal if it's already been submitted. +- `reset`: Reset the form so that if the user opens it again, they can fill new feedback. You should call this when closing any modal containing the feedback. ### Events diff --git a/frontend/src/lib/dynamic-components/logoutButton/LogoutButton.svelte b/frontend/src/lib/dynamic-components/logoutButton/LogoutButton.svelte index 23f60d4a7..c8c274ebf 100644 --- a/frontend/src/lib/dynamic-components/logoutButton/LogoutButton.svelte +++ b/frontend/src/lib/dynamic-components/logoutButton/LogoutButton.svelte @@ -6,7 +6,13 @@ Allows user to log out. Accesses `AuthContext` and `AppContext`. +### Properties + +- `redirectTo`: The route to redirect to after logging out. Default: `'Home'` +- Any valid properties of a `<Button>` component. + ### Usage + ```tsx <LogoutButton /> ``` diff --git a/frontend/src/lib/dynamic-components/navigation/NavGroup.svelte b/frontend/src/lib/dynamic-components/navigation/NavGroup.svelte index b4c0b27c8..d859ad8bd 100644 --- a/frontend/src/lib/dynamic-components/navigation/NavGroup.svelte +++ b/frontend/src/lib/dynamic-components/navigation/NavGroup.svelte @@ -2,16 +2,14 @@ @component Use to group `NavItem` components. Displays a faint line above the group. -### Slots +### Properties -- default: The contents of the navigation group. Should be mostly -`<NavItem>` components. +- `title`: Optional title for the navigation group. +- Any valid attributes of a `<ul>` element. -### Properties +### Slots -- `role`: Aria role @default `list` -- `class`: Additional class string to append to the element's default classes. -- Any valid attributes of a `<section>` element. +- default: The contents of the navigation group. Should be mostly `<NavItem>` components. ### Usage diff --git a/frontend/src/lib/dynamic-components/navigation/NavItem.svelte b/frontend/src/lib/dynamic-components/navigation/NavItem.svelte index a15125411..af8739c14 100644 --- a/frontend/src/lib/dynamic-components/navigation/NavItem.svelte +++ b/frontend/src/lib/dynamic-components/navigation/NavItem.svelte @@ -10,12 +10,10 @@ Accesses `LayoutContext`. ### Properties -- `href`: The URL to navigate to. If this is not supplied be sure to provide an `on:click` event handler or other way of making the item interactive. -- `icon`: An optional `IconName` of the icon to use. @default `undefined` -- `text`: A required text to display. -- `autoCloseNav`: Whether the menu available from the page context should be closed when the item is clicked. @default `true` +- `icon`: The optional name of the icon to use with the navigation item. See the `Icon` component for more details. +- `text`: The text to display in the navigation item. - `disabled`: Whether the button is disabled. This can also be used with items rendered as `<a>` elements. -- `class`: Additional class string to append to the element's default classes. +- `autoCloseNav`: Whether the menu available from the page context should be closed when the item is clicked. Default: `true` - Any valid attributes of either an `<a>` or `<button>` element depending whether `href` was defined or not, respectively. ### Usage diff --git a/frontend/src/lib/dynamic-components/navigation/Navigation.svelte b/frontend/src/lib/dynamic-components/navigation/Navigation.svelte index cc49d6c74..74f16a6b6 100644 --- a/frontend/src/lib/dynamic-components/navigation/Navigation.svelte +++ b/frontend/src/lib/dynamic-components/navigation/Navigation.svelte @@ -2,16 +2,15 @@ @component Create navigation menus for the application in a predefined style. -### Slots - -- default: The content of the navigation menu. It should mainly consist - of `<NavGroup>` components containing `<NavItem>` components. - ### Properties -- `hidden`: Set to `true` to whenever the navigation is hidden. @default false +- `hidden`: Set to `true` to whenever the navigation is hidden. Default: `false` - Any valid attributes of a `<nav>` element. +### Slots + +- default: The content of the navigation menu. It should mainly consist of `<NavGroup>` components containing `<NavItem>` components. + ### Events - `keyboardFocusOut`: Emitted when the component loses a keyboard user's diff --git a/frontend/src/lib/dynamic-components/navigation/admin/AdminNav.svelte b/frontend/src/lib/dynamic-components/navigation/admin/AdminNav.svelte index dcd72908d..f51986600 100644 --- a/frontend/src/lib/dynamic-components/navigation/admin/AdminNav.svelte +++ b/frontend/src/lib/dynamic-components/navigation/admin/AdminNav.svelte @@ -25,6 +25,10 @@ A template part that outputs the navigation menu for the Admin App for use in `L import { getLayoutContext } from '$lib/contexts/layout'; import { NavGroup, Navigation, NavItem } from '$lib/dynamic-components/navigation'; import { LanguageSelection } from '../languages'; + import type { AdminNavProps } from './AdminNav.type'; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type $$Props = AdminNavProps; const { navigation } = getLayoutContext(onDestroy); const { authToken, t, getRoute } = getAdminContext(); diff --git a/frontend/src/lib/dynamic-components/navigation/admin/AdminNav.type.ts b/frontend/src/lib/dynamic-components/navigation/admin/AdminNav.type.ts new file mode 100644 index 000000000..a51f691d5 --- /dev/null +++ b/frontend/src/lib/dynamic-components/navigation/admin/AdminNav.type.ts @@ -0,0 +1,3 @@ +import type { NavigationProps } from '../Navigation.type'; + +export type AdminNavProps = NavigationProps; diff --git a/frontend/src/lib/dynamic-components/navigation/admin/index.ts b/frontend/src/lib/dynamic-components/navigation/admin/index.ts index fbafe1b5e..01b70f44e 100644 --- a/frontend/src/lib/dynamic-components/navigation/admin/index.ts +++ b/frontend/src/lib/dynamic-components/navigation/admin/index.ts @@ -1 +1,2 @@ export { default as AdminNav } from './AdminNav.svelte'; +export * from './AdminNav.type'; diff --git a/frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.svelte b/frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.svelte index bf6ce6e48..965045f03 100644 --- a/frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.svelte +++ b/frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.svelte @@ -1,11 +1,15 @@ <!-- @component -A template part that outputs the navigation menu for the Candidate App for use in use in `Layout`. +A template part that outputs the navigation menu for the Candidate App for use in `Layout`. ### Dynamic component - Accesses the `CandidateContext`. +### Properties + +- Any valid properties of a `Navigation` component + ### Usage ```tsx @@ -22,6 +26,10 @@ A template part that outputs the navigation menu for the Candidate App for use i import { getLayoutContext } from '$lib/contexts/layout'; import { NavGroup, Navigation, NavItem } from '$lib/dynamic-components/navigation'; import { LanguageSelection } from '../languages'; + import type { CandidateNavProps } from './CandidateNav.type'; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type $$Props = CandidateNavProps; const { navigation } = getLayoutContext(onDestroy); const { diff --git a/frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.type.ts b/frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.type.ts new file mode 100644 index 000000000..540f9db9a --- /dev/null +++ b/frontend/src/lib/dynamic-components/navigation/candidate/CandidateNav.type.ts @@ -0,0 +1,3 @@ +import type { NavigationProps } from '../Navigation.type'; + +export type CandidateNavProps = NavigationProps; diff --git a/frontend/src/lib/dynamic-components/navigation/candidate/index.ts b/frontend/src/lib/dynamic-components/navigation/candidate/index.ts index 17cace842..6a623911a 100644 --- a/frontend/src/lib/dynamic-components/navigation/candidate/index.ts +++ b/frontend/src/lib/dynamic-components/navigation/candidate/index.ts @@ -1 +1,2 @@ export { default as CandidateNav } from './CandidateNav.svelte'; +export * from './CandidateNav.type'; diff --git a/frontend/src/lib/dynamic-components/navigation/voter/VoterNav.svelte b/frontend/src/lib/dynamic-components/navigation/voter/VoterNav.svelte index 6cdb23b66..c5dc05ed3 100644 --- a/frontend/src/lib/dynamic-components/navigation/voter/VoterNav.svelte +++ b/frontend/src/lib/dynamic-components/navigation/voter/VoterNav.svelte @@ -31,6 +31,10 @@ A template part that outputs the navigation menu for the Voter App for use in `L import { getVoterContext } from '$lib/contexts/voter'; import { NavGroup, Navigation, NavItem } from '$lib/dynamic-components/navigation'; import { LanguageSelection } from '../languages'; + import type { VoterNavProps } from './VoterNav.type'; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type $$Props = VoterNavProps; const { navigation } = getLayoutContext(onDestroy); diff --git a/frontend/src/lib/dynamic-components/navigation/voter/VoterNav.type.ts b/frontend/src/lib/dynamic-components/navigation/voter/VoterNav.type.ts new file mode 100644 index 000000000..96e6ad512 --- /dev/null +++ b/frontend/src/lib/dynamic-components/navigation/voter/VoterNav.type.ts @@ -0,0 +1,3 @@ +import type { NavigationProps } from '../Navigation.type'; + +export type VoterNavProps = NavigationProps; diff --git a/frontend/src/lib/dynamic-components/navigation/voter/index.ts b/frontend/src/lib/dynamic-components/navigation/voter/index.ts index b1457f6fc..0aa55f2fd 100644 --- a/frontend/src/lib/dynamic-components/navigation/voter/index.ts +++ b/frontend/src/lib/dynamic-components/navigation/voter/index.ts @@ -1 +1,2 @@ export { default as VoterNav } from './VoterNav.svelte'; +export * from './VoterNav.type'; diff --git a/frontend/src/lib/dynamic-components/survey/SurveyButton.type.ts b/frontend/src/lib/dynamic-components/survey/SurveyButton.type.ts index 7d6fe7208..6c6ff7057 100644 --- a/frontend/src/lib/dynamic-components/survey/SurveyButton.type.ts +++ b/frontend/src/lib/dynamic-components/survey/SurveyButton.type.ts @@ -1,4 +1,4 @@ -import type { ButtonProps } from '../button'; +import type { ButtonProps } from '$lib/components/button'; export type SurveyButtonProps = Partial<ButtonProps> & { /** diff --git a/frontend/src/lib/i18n/utils/isLocale.ts b/frontend/src/lib/i18n/utils/isLocale.ts index 04d4503a2..21f7fba47 100644 --- a/frontend/src/lib/i18n/utils/isLocale.ts +++ b/frontend/src/lib/i18n/utils/isLocale.ts @@ -1,7 +1,7 @@ import { canonize } from './canonize'; /** - * @param locale The code to test + * @param locale - The code to test * @returns True if `locale` is a valid BCP 47 locale */ export function isLocale(locale: string) { diff --git a/frontend/src/lib/i18n/utils/matchLocale.ts b/frontend/src/lib/i18n/utils/matchLocale.ts index 79e28b28e..05e821faf 100644 --- a/frontend/src/lib/i18n/utils/matchLocale.ts +++ b/frontend/src/lib/i18n/utils/matchLocale.ts @@ -2,8 +2,8 @@ * Performs a soft match between locales. * NB. This implementation is not 100% accurate, and it does not work properly * with some legacy ('grandfathered') locales or fully proprietary ones. - * @param targets The locale or ordered list of locales to find - * @param locales The locales to find the target in + * @param targets - The locale or ordered list of locales to find + * @param locales - The locales to find the target in * @returns The first matching locale in `locales` or `undefined` if not found. */ export function matchLocale(targets: string | Array<string>, locales: Array<string>): string | undefined { diff --git a/frontend/src/lib/i18n/utils/parseAcceptedLanguages.ts b/frontend/src/lib/i18n/utils/parseAcceptedLanguages.ts index bb5adae71..3b37547ae 100644 --- a/frontend/src/lib/i18n/utils/parseAcceptedLanguages.ts +++ b/frontend/src/lib/i18n/utils/parseAcceptedLanguages.ts @@ -2,7 +2,7 @@ * Parse the `Accept-Language` request header and return an ordered list * of preferred locales. The format of the header is as follows: * `fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5` - * @param acceptLanguage The `Accept-Language` request header. + * @param acceptLanguage - The `Accept-Language` request header. * @returns An ordered list of preferred locale strings */ export function parseAcceptedLanguages(acceptLanguage: string): Array<string> { diff --git a/frontend/src/lib/server/api/README.md 21-40-30-014.md b/frontend/src/lib/server/api/README.md 21-40-30-014.md new file mode 100644 index 000000000..fd5de1f56 --- /dev/null +++ b/frontend/src/lib/server/api/README.md 21-40-30-014.md @@ -0,0 +1,7 @@ +# Server API + +> See also the online doc [Data API](https://openvaa.org/developers-guide/frontend/data-api) (or [locally](/docs/src/routes/developers-guide/frontend/data-api/+page.md)) + +This directory contains the server-side API implementation for the OpenVAA frontend, including server-only data providers and utilities. + +See the Data API documentation for detailed information on how the API works and how to use it. diff --git a/frontend/src/lib/server/constants.ts b/frontend/src/lib/server/constants.ts index fd8a82438..60b69d5e0 100644 --- a/frontend/src/lib/server/constants.ts +++ b/frontend/src/lib/server/constants.ts @@ -1,16 +1,16 @@ import { env } from '$env/dynamic/private'; export const constants = { - BACKEND_API_TOKEN: env.BACKEND_API_TOKEN, - IDENTITY_PROVIDER_CLIENT_SECRET: env.IDENTITY_PROVIDER_CLIENT_SECRET, - IDENTITY_PROVIDER_DECRYPTION_JWKS: env.IDENTITY_PROVIDER_DECRYPTION_JWKS, - IDENTITY_PROVIDER_JWKS_URI: env.IDENTITY_PROVIDER_JWKS_URI, - IDENTITY_PROVIDER_ISSUER: env.IDENTITY_PROVIDER_ISSUER, - IDENTITY_PROVIDER_TOKEN_ENDPOINT: env.IDENTITY_PROVIDER_TOKEN_ENDPOINT, - LOCAL_DATA_DIR: env.LOCAL_DATA_DIR, - CACHE_DIR: env.CACHE_DIR, - CACHE_TTL: env.CACHE_TTL, - CACHE_LRU_SIZE: env.CACHE_LRU_SIZE, - CACHE_EXPIRATION_INTERVAL: env.CACHE_EXPIRATION_INTERVAL, - LLM_OPENAI_API_KEY: env.LLM_OPENAI_API_KEY + BACKEND_API_TOKEN: env.BACKEND_API_TOKEN ?? '', + IDENTITY_PROVIDER_CLIENT_SECRET: env.IDENTITY_PROVIDER_CLIENT_SECRET ?? '', + IDENTITY_PROVIDER_DECRYPTION_JWKS: env.IDENTITY_PROVIDER_DECRYPTION_JWKS ?? '', + IDENTITY_PROVIDER_JWKS_URI: env.IDENTITY_PROVIDER_JWKS_URI ?? '', + IDENTITY_PROVIDER_ISSUER: env.IDENTITY_PROVIDER_ISSUER ?? '', + IDENTITY_PROVIDER_TOKEN_ENDPOINT: env.IDENTITY_PROVIDER_TOKEN_ENDPOINT ?? '', + LOCAL_DATA_DIR: env.LOCAL_DATA_DIR ?? '', + CACHE_DIR: env.CACHE_DIR ?? '', + CACHE_TTL: env.CACHE_TTL ?? '', + CACHE_LRU_SIZE: env.CACHE_LRU_SIZE ?? '', + CACHE_EXPIRATION_INTERVAL: env.CACHE_EXPIRATION_INTERVAL ?? '', + LLM_OPENAI_API_KEY: env.LLM_OPENAI_API_KEY ?? '' }; diff --git a/frontend/src/lib/types/global.d.ts b/frontend/src/lib/types/global.d.ts index a8f0ca66b..7d9d7e991 100644 --- a/frontend/src/lib/types/global.d.ts +++ b/frontend/src/lib/types/global.d.ts @@ -22,8 +22,9 @@ declare global { /* * The format for JSON structure. + * Unfortunately we cannot use a more restrictive formulation: null | string | number | boolean | { [x: string]: JSONData } | Array<JSONData>; */ - type JSONData = null | string | number | boolean | { [x: string]: JSONData } | Array<JSONData>; + type JSONData = Parameters<typeof JSON.stringify>[0]; /** * Make specific properties of an interface required. Works the same way as diff --git a/frontend/src/lib/utils/aria/focus.ts b/frontend/src/lib/utils/aria/focus.ts index 009195941..7373cb12c 100644 --- a/frontend/src/lib/utils/aria/focus.ts +++ b/frontend/src/lib/utils/aria/focus.ts @@ -2,8 +2,8 @@ // Partly derived from example code at https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/dialog/ which is licensed according to the W3C Software License at https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document /** - * @description Set focus on descendant elements until the first focusable element is found. - * @param el The element for which to find the first focusable descendant. + * Set focus on descendant elements until the first focusable element is found. + * @param el - The element for which to find the first focusable descendant. * @returns true if a focusable element is found and focus is set. */ export function focusFirstDescendant(el: Element): boolean { @@ -14,8 +14,8 @@ export function focusFirstDescendant(el: Element): boolean { } /** - * @description Attempt to set focus on the current element. - * @param el The element to attempt to focus on. + * Attempt to set focus on the current element. + * @param el - The element to attempt to focus on. * @returns true if element is focused */ export function attemptFocus(el: Element): boolean { @@ -30,7 +30,7 @@ export function attemptFocus(el: Element): boolean { /** * Check whether the given element is focusable by keyboard navigation. - * @param el Any HTML element + * @param el - Any HTML element * @returns true if the element is focusable */ export function isFocusable(el: Element): el is Element & { focus: () => void } { diff --git a/frontend/src/lib/utils/color/adjustContrast.ts b/frontend/src/lib/utils/color/adjustContrast.ts index c9fbbdaa3..a2b074e1e 100644 --- a/frontend/src/lib/utils/color/adjustContrast.ts +++ b/frontend/src/lib/utils/color/adjustContrast.ts @@ -11,9 +11,9 @@ const CONTRAST_EPS = 0.05; export function adjustContrast(color: RGB, bgColor: RGB, minContrast?: number): string; export function adjustContrast(color: string, bgColor: string, minContrast?: number): string | undefined; /** Adjust the luminance of `color` so that it reaches `minContrast` on `bgColor`. It's best to set the contrast a bit higher than desired because the process is not analytical. - * @param color The foreground color as either an RGB array or a hex string. - * @param bgColor The background color as either an RGB array or a hex string. - * @param minContrast The minimum contrast. @default 4.55 + * @param color - The foreground color as either an RGB array or a hex string. + * @param bgColor - The background color as either an RGB array or a hex string. + * @param minContrast - The minimum contrast. @default 4.55 * @returns The adjusted foreground color as hex string. */ export function adjustContrast(color: RGB | string, bgColor: RGB | string, minContrast = 4.55): string | undefined { @@ -42,8 +42,8 @@ export function adjustContrast(color: RGB | string, bgColor: RGB | string, minCo /** * Calculates the contrast between two colors - * @param color The foreground color. - * @param bgColor The background color. + * @param color - The foreground color. + * @param bgColor - The background color. * @returns An object containing the contrast ratio, the luminance of the color, and the luminance of the background color. */ export function calcContrast(color: RGB, bgColor: RGB) { @@ -56,10 +56,10 @@ export function calcContrast(color: RGB, bgColor: RGB) { /** * Calculates the target luminance for the foreground color. - * @param colorL The luminance of the foreground color - * @param bgL The luminance of the background color - * @param minContrast The minimum contrast - * @param bgDarker Whether the background color should be darker than the foreground color. @default true + * @param colorL - The luminance of the foreground color + * @param bgL - The luminance of the background color + * @param minContrast - The minimum contrast + * @param bgDarker - Whether the background color should be darker than the foreground color. @default true * @returns The target luminance of the color */ function calcTargetLuminance(colorL: number, bgL: number, minContrast: number, bgDarker = true) { diff --git a/frontend/src/lib/utils/color/ensureColors.ts b/frontend/src/lib/utils/color/ensureColors.ts index e00f4b948..0b3debda0 100644 --- a/frontend/src/lib/utils/color/ensureColors.ts +++ b/frontend/src/lib/utils/color/ensureColors.ts @@ -7,8 +7,8 @@ const bgDark = staticSettings.colors?.dark?.['base-300'] ?? '1f2324'; /** * Tries to ensure that `color` and `colorDark` have a sufficient contrast ratio on their respective backgrounds. If `colorDark` is `undefined`, it will be constructed from `color`, if available. In case either color cannot be constructed, an empty string is returned for it. - * @param color The light theme color - * @param colorDark The dark theme color + * @param color - The light theme color + * @param colorDark - The dark theme color * @returns An object with the adjusted colors */ export function ensureColors({ normal, dark }: Partial<Colors>): WithRequired<Colors, 'dark'> { diff --git a/frontend/src/lib/utils/color/luminance.ts b/frontend/src/lib/utils/color/luminance.ts index 4b1d74723..275fe3223 100644 --- a/frontend/src/lib/utils/color/luminance.ts +++ b/frontend/src/lib/utils/color/luminance.ts @@ -5,7 +5,7 @@ export const LUM_F = [0.2126, 0.7152, 0.0722]; /** * Calculates the luminance of an RGB color array. - * @param color The color to calculate the luminance of. + * @param color - The color to calculate the luminance of. * @returns The luminance of the color. */ export function luminance(color: RGB) { @@ -14,7 +14,7 @@ export function luminance(color: RGB) { /** * Calculates the partial luminance of an RGB color channel value. - * @param v The channel value + * @param v - The channel value * @returns The partial luminance of the value */ function valueToLum(v: number): number { @@ -25,11 +25,11 @@ function valueToLum(v: number): number { /** * Set the luminance of a color to a target maintaining its hue. This works by iteratively shifting the color either towards white or black, bc of difficulty of an analytical solution. - * @param color The RGB color array to adjust - * @param lum The target luminance (between 0 and 1) - * @param eps The difference to target luminance to accept. @default 1e-3 - * @param maxSteps The maximum number of steps to take. @default 50 - * @param stepFactor The factor by which to increase or decrease the step size. @default 0.91 + * @param color - The RGB color array to adjust + * @param lum - The target luminance (between 0 and 1) + * @param eps - The difference to target luminance to accept. @default 1e-3 + * @param maxSteps - The maximum number of steps to take. @default 50 + * @param stepFactor - The factor by which to increase or decrease the step size. @default 0.91 * @returns The adjusted color */ export function setLuminance(color: RGB, lum: number, eps = 0.001, maxSteps = 50, stepFactor = 0.91) { diff --git a/frontend/src/lib/utils/color/parseColorString.ts b/frontend/src/lib/utils/color/parseColorString.ts index a37b26c31..4ae719b75 100644 --- a/frontend/src/lib/utils/color/parseColorString.ts +++ b/frontend/src/lib/utils/color/parseColorString.ts @@ -3,7 +3,7 @@ import type { RGB } from './rgb'; /** * Parses a color string and returns an RGB array. - * @param color The color string to parse in any of the formats: '000', '000000', '#000', '#000000', 'rgb(0,0,0)', + * @param color - The color string to parse in any of the formats: '000', '000000', '#000', '#000000', 'rgb(0,0,0)', * @returns An RGB array, or `undefined` if the color string is not valid. */ diff --git a/frontend/src/lib/utils/color/rgbToHex.ts b/frontend/src/lib/utils/color/rgbToHex.ts index 0de49f840..2fe1d0f0d 100644 --- a/frontend/src/lib/utils/color/rgbToHex.ts +++ b/frontend/src/lib/utils/color/rgbToHex.ts @@ -2,7 +2,7 @@ import type { RGB } from './rgb'; /** * Converts an RGB color value to hex. - * @param rgb The RGB color value to convert. + * @param rgb - The RGB color value to convert. * @returns The hex color string. */ diff --git a/frontend/src/lib/utils/components.ts b/frontend/src/lib/utils/components.ts index 53c3cb63d..2e3c36d16 100644 --- a/frontend/src/lib/utils/components.ts +++ b/frontend/src/lib/utils/components.ts @@ -7,8 +7,8 @@ export function getUUID(): string { * Concat string values with properties passed by the user. Mainly used to * prepend default `class` values into `$$restProps` before passing them * to an element or a Svelte component. - * @param props The passed properties, usually `$$restProps` - * @param defaults A record of string properties that will be prepended to the + * @param props - The passed properties, usually `$$restProps` + * @param defaults - A record of string properties that will be prepended to the * same values in `props` * @returns The merged props, based on a shallow copy of `props` */ @@ -26,8 +26,8 @@ export function concatProps<TObject extends object>(props: TObject, defaults: Pa /** * Prepend default class value into `$$restProps` before passing them * to an element or a Svelte component. A shorthand for `concatProps`. - * @param props The passed properties, usually `$$restProps` - * @param classes The base classes to use + * @param props - The passed properties, usually `$$restProps` + * @param classes - The base classes to use * @returns The merged props, based on a shallow copy of `props` with * `classes` joined with possible `props.class` */ diff --git a/frontend/src/lib/utils/constants.ts b/frontend/src/lib/utils/constants.ts index d0a5f669c..792b7bf1a 100644 --- a/frontend/src/lib/utils/constants.ts +++ b/frontend/src/lib/utils/constants.ts @@ -1,12 +1,12 @@ import { env } from '$env/dynamic/public'; export const constants = { - PUBLIC_BROWSER_BACKEND_URL: env.PUBLIC_BROWSER_BACKEND_URL, - PUBLIC_SERVER_BACKEND_URL: env.PUBLIC_SERVER_BACKEND_URL, - PUBLIC_BROWSER_FRONTEND_URL: env.PUBLIC_BROWSER_FRONTEND_URL, - PUBLIC_SERVER_FRONTEND_URL: env.PUBLIC_SERVER_FRONTEND_URL, - PUBLIC_IDENTITY_PROVIDER_CLIENT_ID: env.PUBLIC_IDENTITY_PROVIDER_CLIENT_ID, - PUBLIC_IDENTITY_PROVIDER_AUTHORIZATION_ENDPOINT: env.PUBLIC_IDENTITY_PROVIDER_AUTHORIZATION_ENDPOINT, + PUBLIC_BROWSER_BACKEND_URL: env.PUBLIC_BROWSER_BACKEND_URL ?? '', + PUBLIC_SERVER_BACKEND_URL: env.PUBLIC_SERVER_BACKEND_URL ?? '', + PUBLIC_BROWSER_FRONTEND_URL: env.PUBLIC_BROWSER_FRONTEND_URL ?? '', + PUBLIC_SERVER_FRONTEND_URL: env.PUBLIC_SERVER_FRONTEND_URL ?? '', + PUBLIC_IDENTITY_PROVIDER_CLIENT_ID: env.PUBLIC_IDENTITY_PROVIDER_CLIENT_ID ?? '', + PUBLIC_IDENTITY_PROVIDER_AUTHORIZATION_ENDPOINT: env.PUBLIC_IDENTITY_PROVIDER_AUTHORIZATION_ENDPOINT ?? '', PUBLIC_DEBUG: env.PUBLIC_DEBUG?.toLowerCase() === 'true', PUBLIC_CACHE_ENABLED: env.PUBLIC_CACHE_ENABLED?.toLowerCase() === 'true' }; diff --git a/frontend/src/lib/utils/links.ts b/frontend/src/lib/utils/links.ts index aa588ef41..b347319c6 100644 --- a/frontend/src/lib/utils/links.ts +++ b/frontend/src/lib/utils/links.ts @@ -52,9 +52,9 @@ function isValidDomain(domain: string): boolean { /** * Returns a nice text to display for a link. - * @param url The link - * @param maxLength The maximum length of the text to display - * @param defaultText The text to display if the url is longer than `maxLength`. + * @param url - The link + * @param maxLength - The maximum length of the text to display + * @param defaultText - The text to display if the url is longer than `maxLength`. */ export function getLinkText(url: string, maxLength = 30, defaultText?: string): string | undefined { try { diff --git a/frontend/src/lib/utils/logger.ts b/frontend/src/lib/utils/logger.ts index a1c2ffee0..6f70791c8 100644 --- a/frontend/src/lib/utils/logger.ts +++ b/frontend/src/lib/utils/logger.ts @@ -2,8 +2,8 @@ import { constants } from '$lib/utils/constants'; /** * Allows debug messages to be logged in development environment, but filtered out for production. - * @param message Message or object to log into console - * @param error Potential error message to print out completely + * @param message - Message or object to log into console + * @param error - Potential error message to print out completely */ export function logDebugError(message: unknown, error: unknown = null) { if (import.meta.env.DEV || constants.PUBLIC_DEBUG) { diff --git a/frontend/src/lib/utils/matching/median.ts b/frontend/src/lib/utils/matching/median.ts index 3903698a1..7056cddd0 100644 --- a/frontend/src/lib/utils/matching/median.ts +++ b/frontend/src/lib/utils/matching/median.ts @@ -1,6 +1,6 @@ /** * Calculates the median of a list of numbers. - * @param - values - The list of numbers + * @param - - values - The list of numbers * @param returnFirstWhenTied - If true, return the first encountered value when the list has an even number of elements. If falsish, the average of the two middle values is returned. * Orig. author jdmdevdotnet: https://stackoverflow.com/questions/45309447/calculating-median-javascript * Modified to support returnFirstWhenTied diff --git a/frontend/src/lib/utils/merge.ts b/frontend/src/lib/utils/merge.ts index c070cfb7e..2567b0fff 100644 --- a/frontend/src/lib/utils/merge.ts +++ b/frontend/src/lib/utils/merge.ts @@ -4,13 +4,13 @@ export type DeepPartial<TObject> = { /** * Deep merge two plain (non-constructed) objects with settings. - * NB. For `AppSettings`, use the `mergeAppSettings` function in `$lib/utils/settings.ts` instead. + * NB. The function does not support constructed objects (f.e. dates) and arrays containing functions. + * For `AppSettings`, use the `mergeAppSettings` function in `$lib/utils/settings.ts` instead. * - * @param target The target. - * @param source The source. + * @param target - The target. + * @param source - The source. * @returns A new plain object that contains a deep merge of target and source. * - * @note The function does not support constructed objects (f.e. dates) and arrays containing functions. */ export function mergeSettings<TTarget extends object, TSource extends object>( target: TTarget, diff --git a/frontend/src/lib/utils/onKeyboardFocusOut.ts b/frontend/src/lib/utils/onKeyboardFocusOut.ts index 626cf3e96..b6bd7d697 100644 --- a/frontend/src/lib/utils/onKeyboardFocusOut.ts +++ b/frontend/src/lib/utils/onKeyboardFocusOut.ts @@ -15,8 +15,8 @@ * track whether we have focus or not. (We cannot use * `document.activeElement` because it's not reliable.) * - * @param node The target element - * @param callback The event handler to call + * @param node - The target element + * @param callback - The event handler to call * * @example `<Navigation on:keyboardFocusOut={closeDrawer} />` */ diff --git a/frontend/src/lib/utils/sanitize.ts b/frontend/src/lib/utils/sanitize.ts index ad9cee31e..7f2786390 100644 --- a/frontend/src/lib/utils/sanitize.ts +++ b/frontend/src/lib/utils/sanitize.ts @@ -2,7 +2,7 @@ import DOMPurify from 'isomorphic-dompurify'; /** * Sanitize HTML received from the server for displaying with Svelte `@html` - * @param html Dirty HTML + * @param html - Dirty HTML * @returns Sanitized HTML */ export function sanitizeHtml(html?: string): string { diff --git a/frontend/src/lib/utils/sorting.ts b/frontend/src/lib/utils/sorting.ts index b2b537786..ba32c2c2f 100644 --- a/frontend/src/lib/utils/sorting.ts +++ b/frontend/src/lib/utils/sorting.ts @@ -5,7 +5,7 @@ import type { AnyNominationVariant, AnyQuestionVariant } from '@openvaa/data'; /** * Moves items with a specified value to the front of the array while retaining the order of other items. * @param array - The array to sort. - * @param target . The value to move to the front. + * @param target - . The value to move to the front. * @returns A new array with the target value at the front. */ export function sortToFirst<TItem>(array: ReadonlyArray<TItem>, targetValue: TItem): Array<TItem> { diff --git a/frontend/src/lib/utils/text/abbreviate.ts b/frontend/src/lib/utils/text/abbreviate.ts index 20ea85423..05bee7f98 100644 --- a/frontend/src/lib/utils/text/abbreviate.ts +++ b/frontend/src/lib/utils/text/abbreviate.ts @@ -2,9 +2,9 @@ import { locale } from '$lib/i18n'; /** * Abbreviate a string. - * @param text The localized string json to translate - * @param method The abbreviation method. @default acronym - * @param length The maximum abbreviation length. @default 3 + * @param text - The localized string json to translate + * @param method - The abbreviation method. @default acronym + * @param length - The maximum abbreviation length. @default 3 */ export function abbreviate(text: string, options: AbbreviationOptions = {}) { if (!text) return ''; diff --git a/frontend/src/params/entityType.ts b/frontend/src/params/entityType.ts index 2607bb433..92799e241 100644 --- a/frontend/src/params/entityType.ts +++ b/frontend/src/params/entityType.ts @@ -1,6 +1,6 @@ /** * Matches an entity type */ -export function match(param: LegacyEntityType) { +export function match(param: string) { return ['candidate', 'party'].includes(param); } diff --git a/frontend/src/routes/README.md b/frontend/src/routes/README.md new file mode 100644 index 000000000..8ed622046 --- /dev/null +++ b/frontend/src/routes/README.md @@ -0,0 +1,13 @@ +# Routes + +> See also the online doc [Routing](https://openvaa.org/developers-guide/frontend/routing) (or [locally](/docs/src/routes/developers-guide/frontend/routing/+page.md)) + +This directory contains the SvelteKit route structure for the OpenVAA frontend application. + +All routes support an optional locale parameter: `[[lang=locale]]` + +- Voters app routes are in `[[lang=locale]]/(voters)/` +- Candidate app routes are in `[[lang=locale]]/candidate/` +- Protected candidate routes are in `[[lang=locale]]/candidate/(protected)/` + +See the Routing documentation for detailed information. diff --git a/frontend/src/routes/[[lang=locale]]/candidate/README.md b/frontend/src/routes/[[lang=locale]]/candidate/README.md new file mode 100644 index 000000000..4e739b5dd --- /dev/null +++ b/frontend/src/routes/[[lang=locale]]/candidate/README.md @@ -0,0 +1,10 @@ +# Candidate Routes + +> See also the online docs: +> +> - [Candidate user management](https://openvaa.org/developers-guide/candidate-user-management) (or [locally](/docs/src/routes/developers-guide/candidate-user-management/+page.md)) +> - [Routing](https://openvaa.org/developers-guide/frontend/routing) (or [locally](/docs/src/routes/developers-guide/frontend/routing/+page.md)) + +This directory contains routes specific to the candidate application, where candidates can register, authenticate, and manage their profile and answers. + +See the documentation for detailed information on candidate user management and routing. diff --git a/frontend/tools/editTranslations/editTranslations.ts b/frontend/tools/editTranslations/editTranslations.ts index 36c140781..6ae00e9a8 100644 --- a/frontend/tools/editTranslations/editTranslations.ts +++ b/frontend/tools/editTranslations/editTranslations.ts @@ -292,7 +292,7 @@ function readJsonTranslations(locale: string, filename: string): Translations { } /** - * Return a recursive async generator yielding file paths in @param {string} dir. + * Return a recursive async generator yielding file paths in @param {string} - dir. * (By qwtel)[https://stackoverflow.com/a/45130990/13409631] */ async function* getFiles(dir: string): AsyncGenerator<string> { diff --git a/frontend/tsconfig.tsbuildinfo b/frontend/tsconfig.tsbuildinfo new file mode 100644 index 000000000..6bc84e80d --- /dev/null +++ b/frontend/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.es2016.d.ts","../node_modules/typescript/lib/lib.es2017.d.ts","../node_modules/typescript/lib/lib.es2018.d.ts","../node_modules/typescript/lib/lib.es2019.d.ts","../node_modules/typescript/lib/lib.es2020.d.ts","../node_modules/typescript/lib/lib.es2021.d.ts","../node_modules/typescript/lib/lib.es2022.d.ts","../node_modules/typescript/lib/lib.es2023.d.ts","../node_modules/typescript/lib/lib.es2024.d.ts","../node_modules/typescript/lib/lib.esnext.d.ts","../node_modules/typescript/lib/lib.dom.d.ts","../node_modules/typescript/lib/lib.dom.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../node_modules/typescript/lib/lib.es2016.intl.d.ts","../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../node_modules/typescript/lib/lib.es2017.date.d.ts","../node_modules/typescript/lib/lib.es2017.object.d.ts","../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2017.string.d.ts","../node_modules/typescript/lib/lib.es2017.intl.d.ts","../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../node_modules/typescript/lib/lib.es2018.intl.d.ts","../node_modules/typescript/lib/lib.es2018.promise.d.ts","../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../node_modules/typescript/lib/lib.es2019.array.d.ts","../node_modules/typescript/lib/lib.es2019.object.d.ts","../node_modules/typescript/lib/lib.es2019.string.d.ts","../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../node_modules/typescript/lib/lib.es2019.intl.d.ts","../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../node_modules/typescript/lib/lib.es2020.date.d.ts","../node_modules/typescript/lib/lib.es2020.promise.d.ts","../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2020.string.d.ts","../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2020.intl.d.ts","../node_modules/typescript/lib/lib.es2020.number.d.ts","../node_modules/typescript/lib/lib.es2021.promise.d.ts","../node_modules/typescript/lib/lib.es2021.string.d.ts","../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../node_modules/typescript/lib/lib.es2021.intl.d.ts","../node_modules/typescript/lib/lib.es2022.array.d.ts","../node_modules/typescript/lib/lib.es2022.error.d.ts","../node_modules/typescript/lib/lib.es2022.intl.d.ts","../node_modules/typescript/lib/lib.es2022.object.d.ts","../node_modules/typescript/lib/lib.es2022.string.d.ts","../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../node_modules/typescript/lib/lib.es2023.array.d.ts","../node_modules/typescript/lib/lib.es2023.collection.d.ts","../node_modules/typescript/lib/lib.es2023.intl.d.ts","../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../node_modules/typescript/lib/lib.es2024.collection.d.ts","../node_modules/typescript/lib/lib.es2024.object.d.ts","../node_modules/typescript/lib/lib.es2024.promise.d.ts","../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2024.string.d.ts","../node_modules/typescript/lib/lib.esnext.array.d.ts","../node_modules/typescript/lib/lib.esnext.collection.d.ts","../node_modules/typescript/lib/lib.esnext.intl.d.ts","../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../node_modules/typescript/lib/lib.esnext.decorators.d.ts","../node_modules/typescript/lib/lib.esnext.iterator.d.ts","../node_modules/typescript/lib/lib.decorators.d.ts","../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../node_modules/@types/estree/index.d.ts","../node_modules/magic-string/dist/magic-string.es.d.mts","../node_modules/estree-walker/types/walker.d.ts","../node_modules/estree-walker/types/sync.d.ts","../node_modules/estree-walker/types/async.d.ts","../node_modules/estree-walker/types/index.d.ts","../node_modules/svelte/types/index.d.ts","../node_modules/vite/types/hmrpayload.d.ts","../node_modules/vite/types/customevent.d.ts","../node_modules/vite/types/hot.d.ts","../node_modules/vite/types/importglob.d.ts","../node_modules/vite/types/importmeta.d.ts","../node_modules/vite/client.d.ts","../node_modules/@types/node/compatibility/disposable.d.ts","../node_modules/@types/node/compatibility/indexable.d.ts","../node_modules/@types/node/compatibility/iterators.d.ts","../node_modules/@types/node/compatibility/index.d.ts","../node_modules/@types/node/globals.typedarray.d.ts","../node_modules/@types/node/buffer.buffer.d.ts","../node_modules/buffer/index.d.ts","../node_modules/@types/node/node_modules/undici-types/header.d.ts","../node_modules/@types/node/node_modules/undici-types/readable.d.ts","../node_modules/@types/node/node_modules/undici-types/file.d.ts","../node_modules/@types/node/node_modules/undici-types/fetch.d.ts","../node_modules/@types/node/node_modules/undici-types/formdata.d.ts","../node_modules/@types/node/node_modules/undici-types/connector.d.ts","../node_modules/@types/node/node_modules/undici-types/client.d.ts","../node_modules/@types/node/node_modules/undici-types/errors.d.ts","../node_modules/@types/node/node_modules/undici-types/dispatcher.d.ts","../node_modules/@types/node/node_modules/undici-types/global-dispatcher.d.ts","../node_modules/@types/node/node_modules/undici-types/global-origin.d.ts","../node_modules/@types/node/node_modules/undici-types/pool-stats.d.ts","../node_modules/@types/node/node_modules/undici-types/pool.d.ts","../node_modules/@types/node/node_modules/undici-types/handlers.d.ts","../node_modules/@types/node/node_modules/undici-types/balanced-pool.d.ts","../node_modules/@types/node/node_modules/undici-types/agent.d.ts","../node_modules/@types/node/node_modules/undici-types/mock-interceptor.d.ts","../node_modules/@types/node/node_modules/undici-types/mock-agent.d.ts","../node_modules/@types/node/node_modules/undici-types/mock-client.d.ts","../node_modules/@types/node/node_modules/undici-types/mock-pool.d.ts","../node_modules/@types/node/node_modules/undici-types/mock-errors.d.ts","../node_modules/@types/node/node_modules/undici-types/proxy-agent.d.ts","../node_modules/@types/node/node_modules/undici-types/env-http-proxy-agent.d.ts","../node_modules/@types/node/node_modules/undici-types/retry-handler.d.ts","../node_modules/@types/node/node_modules/undici-types/retry-agent.d.ts","../node_modules/@types/node/node_modules/undici-types/api.d.ts","../node_modules/@types/node/node_modules/undici-types/interceptors.d.ts","../node_modules/@types/node/node_modules/undici-types/util.d.ts","../node_modules/@types/node/node_modules/undici-types/cookies.d.ts","../node_modules/@types/node/node_modules/undici-types/patch.d.ts","../node_modules/@types/node/node_modules/undici-types/websocket.d.ts","../node_modules/@types/node/node_modules/undici-types/eventsource.d.ts","../node_modules/@types/node/node_modules/undici-types/filereader.d.ts","../node_modules/@types/node/node_modules/undici-types/diagnostics-channel.d.ts","../node_modules/@types/node/node_modules/undici-types/content-type.d.ts","../node_modules/@types/node/node_modules/undici-types/cache.d.ts","../node_modules/@types/node/node_modules/undici-types/index.d.ts","../node_modules/@types/node/globals.d.ts","../node_modules/@types/node/assert.d.ts","../node_modules/@types/node/assert/strict.d.ts","../node_modules/@types/node/async_hooks.d.ts","../node_modules/@types/node/buffer.d.ts","../node_modules/@types/node/child_process.d.ts","../node_modules/@types/node/cluster.d.ts","../node_modules/@types/node/console.d.ts","../node_modules/@types/node/constants.d.ts","../node_modules/@types/node/crypto.d.ts","../node_modules/@types/node/dgram.d.ts","../node_modules/@types/node/diagnostics_channel.d.ts","../node_modules/@types/node/dns.d.ts","../node_modules/@types/node/dns/promises.d.ts","../node_modules/@types/node/domain.d.ts","../node_modules/@types/node/dom-events.d.ts","../node_modules/@types/node/events.d.ts","../node_modules/@types/node/fs.d.ts","../node_modules/@types/node/fs/promises.d.ts","../node_modules/@types/node/http.d.ts","../node_modules/@types/node/http2.d.ts","../node_modules/@types/node/https.d.ts","../node_modules/@types/node/inspector.d.ts","../node_modules/@types/node/module.d.ts","../node_modules/@types/node/net.d.ts","../node_modules/@types/node/os.d.ts","../node_modules/@types/node/path.d.ts","../node_modules/@types/node/perf_hooks.d.ts","../node_modules/@types/node/process.d.ts","../node_modules/@types/node/punycode.d.ts","../node_modules/@types/node/querystring.d.ts","../node_modules/@types/node/readline.d.ts","../node_modules/@types/node/readline/promises.d.ts","../node_modules/@types/node/repl.d.ts","../node_modules/@types/node/sea.d.ts","../node_modules/@types/node/stream.d.ts","../node_modules/@types/node/stream/promises.d.ts","../node_modules/@types/node/stream/consumers.d.ts","../node_modules/@types/node/stream/web.d.ts","../node_modules/@types/node/string_decoder.d.ts","../node_modules/@types/node/test.d.ts","../node_modules/@types/node/timers.d.ts","../node_modules/@types/node/timers/promises.d.ts","../node_modules/@types/node/tls.d.ts","../node_modules/@types/node/trace_events.d.ts","../node_modules/@types/node/tty.d.ts","../node_modules/@types/node/url.d.ts","../node_modules/@types/node/util.d.ts","../node_modules/@types/node/v8.d.ts","../node_modules/@types/node/vm.d.ts","../node_modules/@types/node/wasi.d.ts","../node_modules/@types/node/worker_threads.d.ts","../node_modules/@types/node/zlib.d.ts","../node_modules/@types/node/index.d.ts","../node_modules/rollup/dist/rollup.d.ts","../node_modules/rollup/dist/parseast.d.ts","../node_modules/vite/dist/node/types.d-agj9qkwt.d.ts","../node_modules/vite/node_modules/esbuild/lib/main.d.ts","../node_modules/source-map-js/source-map.d.ts","../node_modules/postcss/lib/previous-map.d.ts","../node_modules/postcss/lib/input.d.ts","../node_modules/postcss/lib/css-syntax-error.d.ts","../node_modules/postcss/lib/declaration.d.ts","../node_modules/postcss/lib/root.d.ts","../node_modules/postcss/lib/warning.d.ts","../node_modules/postcss/lib/lazy-result.d.ts","../node_modules/postcss/lib/no-work-result.d.ts","../node_modules/postcss/lib/processor.d.ts","../node_modules/postcss/lib/result.d.ts","../node_modules/postcss/lib/document.d.ts","../node_modules/postcss/lib/rule.d.ts","../node_modules/postcss/lib/node.d.ts","../node_modules/postcss/lib/comment.d.ts","../node_modules/postcss/lib/container.d.ts","../node_modules/postcss/lib/at-rule.d.ts","../node_modules/postcss/lib/list.d.ts","../node_modules/postcss/lib/postcss.d.ts","../node_modules/postcss/lib/postcss.d.mts","../node_modules/vite/dist/node/runtime.d.ts","../node_modules/vite/types/metadata.d.ts","../node_modules/vite/dist/node/index.d.ts","../node_modules/@sveltejs/vite-plugin-svelte-inspector/types/index.d.ts","../node_modules/@sveltejs/vite-plugin-svelte/types/index.d.ts","../node_modules/@types/cookie/index.d.ts","../node_modules/@sveltejs/kit/types/index.d.ts","../node_modules/vite-tsconfig-paths/dist/index.d.ts","../node_modules/vite-tsconfig-paths/dist/index.d.mts","./vite.config.ts","./src/lib/api/base/adaptertype.type.ts","../node_modules/@openvaa/app-shared/build/esm/data/argumenttype.d.ts","../packages/core/build/controller/aborterror.d.ts","../packages/core/build/controller/controller.type.d.ts","../packages/core/build/controller/controller.d.ts","../packages/core/build/controller/noopcontroller.d.ts","../packages/core/build/id/id.type.d.ts","../packages/core/build/matching/hasanswers.type.d.ts","../packages/core/build/entity/entity.type.d.ts","../packages/core/build/entity/getentity.d.ts","../packages/core/build/id/havesameid.d.ts","../packages/core/build/id/isvalidid.d.ts","../packages/core/build/matching/missingvalue.d.ts","../packages/core/build/matching/distance.type.d.ts","../packages/core/build/matching/distance.d.ts","../packages/core/build/matching/hasanswers.d.ts","../packages/core/build/matching/matchablequestion.type.d.ts","../packages/core/build/matching/missingvalue.type.d.ts","../packages/core/build/pipelines/metrics.type.d.ts","../packages/core/build/serializable/serializable.type.d.ts","../packages/core/build/index.d.ts","../packages/data/build/core/filter.type.d.ts","../packages/data/build/core/filter.d.ts","../packages/data/build/core/collection.type.d.ts","../packages/data/build/core/colors.type.d.ts","../packages/data/build/core/image.type.d.ts","../packages/data/build/core/updatable.type.d.ts","../packages/data/build/core/updatable.d.ts","../packages/data/build/root/dataroot.type.d.ts","../packages/data/build/root/dataroot.d.ts","../packages/data/build/core/objecttypes.d.ts","../packages/data/build/core/dataobject.type.d.ts","../packages/data/build/core/dataobject.d.ts","../packages/data/build/core/dataaccessor.type.d.ts","../packages/data/build/core/error.d.ts","../packages/data/build/utils/withoptional.type.d.ts","../packages/data/build/utils/answer.d.ts","../packages/data/build/utils/choice.d.ts","../packages/data/build/utils/createdeterministicid.d.ts","../packages/data/build/utils/ensurevalue.d.ts","../packages/data/build/utils/filter.d.ts","../packages/data/build/utils/intersectfilters.d.ts","../packages/data/build/utils/format.type.d.ts","../packages/data/build/utils/format.d.ts","../packages/data/build/utils/formatanswer.type.d.ts","../packages/data/build/utils/formatanswer.d.ts","../packages/data/build/utils/order.d.ts","../packages/data/build/utils/parsefullvaadata.d.ts","../packages/data/build/utils/removeduplicates.d.ts","../packages/data/build/utils/typeguards.d.ts","../packages/data/build/i18n/localized.d.ts","../packages/data/build/i18n/translate.d.ts","../packages/data/build/objects/questions/base/answer.type.d.ts","../packages/data/build/objects/questions/base/choice.type.d.ts","../packages/data/build/objects/questions/base/questionandcategorybase.type.d.ts","../packages/data/build/objects/questions/base/questionandcategorybase.d.ts","../packages/data/build/objects/questions/base/question.type.d.ts","../packages/data/build/objects/questions/base/question.d.ts","../packages/data/build/objects/questions/base/choicequestion.type.d.ts","../packages/data/build/objects/questions/base/choicequestion.d.ts","../packages/data/build/objects/questions/base/multiplechoicequestion.type.d.ts","../packages/data/build/objects/questions/base/multiplechoicequestion.d.ts","../packages/data/build/objects/questions/base/questiontypes.d.ts","../packages/data/build/objects/questions/base/singlechoicequestion.d.ts","../packages/data/build/objects/questions/category/questioncategorytypes.d.ts","../packages/data/build/objects/questions/category/questioncategory.type.d.ts","../packages/data/build/objects/questions/category/questioncategory.d.ts","../packages/data/build/objects/questions/variants/booleanquestion.d.ts","../packages/data/build/objects/questions/variants/datequestion.type.d.ts","../packages/data/build/objects/questions/variants/datequestion.d.ts","../packages/data/build/objects/questions/variants/imagequestion.d.ts","../packages/data/build/objects/questions/variants/multiplechoicecategoricalquestion.d.ts","../packages/data/build/objects/questions/variants/multipletextquestion.d.ts","../packages/data/build/objects/questions/variants/numberquestion.type.d.ts","../packages/data/build/objects/questions/variants/numberquestion.d.ts","../packages/data/build/objects/questions/variants/singlechoicecategoricalquestion.d.ts","../packages/data/build/objects/questions/variants/singlechoiceordinalquestion.d.ts","../packages/data/build/objects/questions/variants/textquestion.d.ts","../packages/data/build/objects/questions/variants/variants.d.ts","../packages/data/build/objects/election/election.type.d.ts","../packages/data/build/objects/election/election.d.ts","../packages/data/build/objects/constituency/constituency.type.d.ts","../packages/data/build/objects/constituency/constituency.d.ts","../packages/data/build/objects/constituency/constituencygroup.type.d.ts","../packages/data/build/objects/constituency/constituencygroup.d.ts","../packages/data/build/objects/entities/base/entitytypes.d.ts","../packages/data/build/objects/entities/base/entity.type.d.ts","../packages/data/build/objects/entities/base/entity.d.ts","../packages/data/build/objects/entities/variants/candidate.type.d.ts","../packages/data/build/objects/entities/variants/candidate.d.ts","../packages/data/build/objects/entities/variants/faction.type.d.ts","../packages/data/build/objects/entities/variants/faction.d.ts","../packages/data/build/objects/entities/variants/organization.type.d.ts","../packages/data/build/objects/entities/variants/organization.d.ts","../packages/data/build/objects/entities/variants/alliance.type.d.ts","../packages/data/build/objects/entities/variants/alliance.d.ts","../packages/data/build/objects/entities/variants/variants.d.ts","../packages/data/build/objects/nominations/base/nomination.type.d.ts","../packages/data/build/objects/nominations/base/nomination.d.ts","../packages/data/build/objects/nominations/variants/candidatenomination.type.d.ts","../packages/data/build/objects/nominations/variants/candidatenomination.d.ts","../packages/data/build/objects/nominations/variants/factionnomination.type.d.ts","../packages/data/build/objects/nominations/variants/factionnomination.d.ts","../packages/data/build/objects/nominations/variants/organizationnomination.type.d.ts","../packages/data/build/objects/nominations/variants/organizationnomination.d.ts","../packages/data/build/objects/nominations/variants/alliancenomination.type.d.ts","../packages/data/build/objects/nominations/variants/alliancenomination.d.ts","../packages/data/build/objects/nominations/variants/variants.d.ts","../packages/data/build/internal.d.ts","../packages/data/build/index.d.ts","../node_modules/@openvaa/app-shared/build/esm/data/customdata.type.d.ts","../node_modules/@openvaa/app-shared/build/esm/data/localized.type.d.ts","../node_modules/@openvaa/app-shared/build/esm/data/extendeddata.type.d.ts","../node_modules/@openvaa/app-shared/build/esm/data/getcustomdata.d.ts","../node_modules/@openvaa/app-shared/build/esm/data/isemoji.d.ts","../node_modules/@openvaa/app-shared/build/esm/data/isimage.d.ts","../node_modules/@openvaa/app-shared/build/esm/data/islocalized.d.ts","../node_modules/@openvaa/app-shared/build/esm/settings/dynamicsettings.type.d.ts","../node_modules/@openvaa/app-shared/build/esm/settings/dynamicsettings.d.ts","../node_modules/@openvaa/app-shared/build/esm/settings/staticsettings.type.d.ts","../node_modules/@openvaa/app-shared/build/esm/settings/staticsettings.d.ts","../node_modules/@openvaa/app-shared/build/esm/utils/passwordvalidation.d.ts","../node_modules/@openvaa/app-shared/build/esm/index.d.ts","../node_modules/@openvaa/data/build/index.d.ts","../node_modules/@types/qs/index.d.ts","./src/lib/utils/constants.ts","./src/lib/api/utils/addheader.ts","./src/lib/api/utils/authheaders.ts","./src/lib/api/base/universalapiroutes.ts","./src/lib/api/utils/cachifyurl.ts","./src/lib/api/utils/parseresponse.ts","./src/lib/api/base/universaladapter.type.ts","./src/lib/api/base/universaladapter.ts","./src/lib/api/base/actionresult.type.ts","./src/lib/api/base/feedbackwriter.type.ts","./src/lib/api/base/universalfeedbackwriter.ts","../node_modules/@openvaa/core/build/index.d.ts","../node_modules/@types/json-schema/index.d.ts","../node_modules/@ai-sdk/provider/dist/index.d.ts","../node_modules/zod/v4/core/standard-schema.d.cts","../node_modules/zod/v4/core/util.d.cts","../node_modules/zod/v4/core/versions.d.cts","../node_modules/zod/v4/core/schemas.d.cts","../node_modules/zod/v4/core/checks.d.cts","../node_modules/zod/v4/core/errors.d.cts","../node_modules/zod/v4/core/core.d.cts","../node_modules/zod/v4/core/parse.d.cts","../node_modules/zod/v4/core/regexes.d.cts","../node_modules/zod/v4/locales/ar.d.cts","../node_modules/zod/v4/locales/az.d.cts","../node_modules/zod/v4/locales/be.d.cts","../node_modules/zod/v4/locales/ca.d.cts","../node_modules/zod/v4/locales/cs.d.cts","../node_modules/zod/v4/locales/da.d.cts","../node_modules/zod/v4/locales/de.d.cts","../node_modules/zod/v4/locales/en.d.cts","../node_modules/zod/v4/locales/eo.d.cts","../node_modules/zod/v4/locales/es.d.cts","../node_modules/zod/v4/locales/fa.d.cts","../node_modules/zod/v4/locales/fi.d.cts","../node_modules/zod/v4/locales/fr.d.cts","../node_modules/zod/v4/locales/fr-ca.d.cts","../node_modules/zod/v4/locales/he.d.cts","../node_modules/zod/v4/locales/hu.d.cts","../node_modules/zod/v4/locales/id.d.cts","../node_modules/zod/v4/locales/is.d.cts","../node_modules/zod/v4/locales/it.d.cts","../node_modules/zod/v4/locales/ja.d.cts","../node_modules/zod/v4/locales/ka.d.cts","../node_modules/zod/v4/locales/kh.d.cts","../node_modules/zod/v4/locales/km.d.cts","../node_modules/zod/v4/locales/ko.d.cts","../node_modules/zod/v4/locales/lt.d.cts","../node_modules/zod/v4/locales/mk.d.cts","../node_modules/zod/v4/locales/ms.d.cts","../node_modules/zod/v4/locales/nl.d.cts","../node_modules/zod/v4/locales/no.d.cts","../node_modules/zod/v4/locales/ota.d.cts","../node_modules/zod/v4/locales/ps.d.cts","../node_modules/zod/v4/locales/pl.d.cts","../node_modules/zod/v4/locales/pt.d.cts","../node_modules/zod/v4/locales/ru.d.cts","../node_modules/zod/v4/locales/sl.d.cts","../node_modules/zod/v4/locales/sv.d.cts","../node_modules/zod/v4/locales/ta.d.cts","../node_modules/zod/v4/locales/th.d.cts","../node_modules/zod/v4/locales/tr.d.cts","../node_modules/zod/v4/locales/ua.d.cts","../node_modules/zod/v4/locales/uk.d.cts","../node_modules/zod/v4/locales/ur.d.cts","../node_modules/zod/v4/locales/vi.d.cts","../node_modules/zod/v4/locales/zh-cn.d.cts","../node_modules/zod/v4/locales/zh-tw.d.cts","../node_modules/zod/v4/locales/yo.d.cts","../node_modules/zod/v4/locales/index.d.cts","../node_modules/zod/v4/core/registries.d.cts","../node_modules/zod/v4/core/doc.d.cts","../node_modules/zod/v4/core/api.d.cts","../node_modules/zod/v4/core/json-schema.d.cts","../node_modules/zod/v4/core/to-json-schema.d.cts","../node_modules/zod/v4/core/index.d.cts","../node_modules/zod/v4/classic/errors.d.cts","../node_modules/zod/v4/classic/parse.d.cts","../node_modules/zod/v4/classic/schemas.d.cts","../node_modules/zod/v4/classic/checks.d.cts","../node_modules/zod/v4/classic/compat.d.cts","../node_modules/zod/v4/classic/iso.d.cts","../node_modules/zod/v4/classic/coerce.d.cts","../node_modules/zod/v4/classic/external.d.cts","../node_modules/zod/v4/classic/index.d.cts","../node_modules/zod/v4/index.d.cts","../node_modules/zod/v3/helpers/typealiases.d.cts","../node_modules/zod/v3/helpers/util.d.cts","../node_modules/zod/v3/zoderror.d.cts","../node_modules/zod/v3/locales/en.d.cts","../node_modules/zod/v3/errors.d.cts","../node_modules/zod/v3/helpers/parseutil.d.cts","../node_modules/zod/v3/helpers/enumutil.d.cts","../node_modules/zod/v3/helpers/errorutil.d.cts","../node_modules/zod/v3/helpers/partialutil.d.cts","../node_modules/zod/v3/standard-schema.d.cts","../node_modules/zod/v3/types.d.cts","../node_modules/zod/v3/external.d.cts","../node_modules/zod/v3/index.d.cts","../node_modules/@standard-schema/spec/dist/index.d.ts","../node_modules/eventsource-parser/dist/stream.d.ts","../node_modules/@ai-sdk/provider-utils/dist/index.d.ts","../node_modules/@ai-sdk/gateway/dist/index.d.ts","../node_modules/@opentelemetry/api/build/src/baggage/internal/symbol.d.ts","../node_modules/@opentelemetry/api/build/src/baggage/types.d.ts","../node_modules/@opentelemetry/api/build/src/baggage/utils.d.ts","../node_modules/@opentelemetry/api/build/src/common/exception.d.ts","../node_modules/@opentelemetry/api/build/src/common/time.d.ts","../node_modules/@opentelemetry/api/build/src/common/attributes.d.ts","../node_modules/@opentelemetry/api/build/src/context/types.d.ts","../node_modules/@opentelemetry/api/build/src/context/context.d.ts","../node_modules/@opentelemetry/api/build/src/api/context.d.ts","../node_modules/@opentelemetry/api/build/src/diag/types.d.ts","../node_modules/@opentelemetry/api/build/src/diag/consolelogger.d.ts","../node_modules/@opentelemetry/api/build/src/api/diag.d.ts","../node_modules/@opentelemetry/api/build/src/metrics/observableresult.d.ts","../node_modules/@opentelemetry/api/build/src/metrics/metric.d.ts","../node_modules/@opentelemetry/api/build/src/metrics/meter.d.ts","../node_modules/@opentelemetry/api/build/src/metrics/noopmeter.d.ts","../node_modules/@opentelemetry/api/build/src/metrics/meterprovider.d.ts","../node_modules/@opentelemetry/api/build/src/api/metrics.d.ts","../node_modules/@opentelemetry/api/build/src/propagation/textmappropagator.d.ts","../node_modules/@opentelemetry/api/build/src/baggage/context-helpers.d.ts","../node_modules/@opentelemetry/api/build/src/api/propagation.d.ts","../node_modules/@opentelemetry/api/build/src/trace/attributes.d.ts","../node_modules/@opentelemetry/api/build/src/trace/trace_state.d.ts","../node_modules/@opentelemetry/api/build/src/trace/span_context.d.ts","../node_modules/@opentelemetry/api/build/src/trace/link.d.ts","../node_modules/@opentelemetry/api/build/src/trace/status.d.ts","../node_modules/@opentelemetry/api/build/src/trace/span.d.ts","../node_modules/@opentelemetry/api/build/src/trace/span_kind.d.ts","../node_modules/@opentelemetry/api/build/src/trace/spanoptions.d.ts","../node_modules/@opentelemetry/api/build/src/trace/tracer.d.ts","../node_modules/@opentelemetry/api/build/src/trace/tracer_options.d.ts","../node_modules/@opentelemetry/api/build/src/trace/proxytracer.d.ts","../node_modules/@opentelemetry/api/build/src/trace/tracer_provider.d.ts","../node_modules/@opentelemetry/api/build/src/trace/proxytracerprovider.d.ts","../node_modules/@opentelemetry/api/build/src/trace/samplingresult.d.ts","../node_modules/@opentelemetry/api/build/src/trace/sampler.d.ts","../node_modules/@opentelemetry/api/build/src/trace/trace_flags.d.ts","../node_modules/@opentelemetry/api/build/src/trace/internal/utils.d.ts","../node_modules/@opentelemetry/api/build/src/trace/spancontext-utils.d.ts","../node_modules/@opentelemetry/api/build/src/trace/invalid-span-constants.d.ts","../node_modules/@opentelemetry/api/build/src/trace/context-utils.d.ts","../node_modules/@opentelemetry/api/build/src/api/trace.d.ts","../node_modules/@opentelemetry/api/build/src/context-api.d.ts","../node_modules/@opentelemetry/api/build/src/diag-api.d.ts","../node_modules/@opentelemetry/api/build/src/metrics-api.d.ts","../node_modules/@opentelemetry/api/build/src/propagation-api.d.ts","../node_modules/@opentelemetry/api/build/src/trace-api.d.ts","../node_modules/@opentelemetry/api/build/src/index.d.ts","../node_modules/ai/dist/index.d.ts","../node_modules/zod/index.d.cts","../node_modules/@openvaa/llm/build/utils/costcalculation.type.d.ts","../node_modules/@openvaa/llm/build/llm-providers/provider.types.d.ts","../node_modules/@openvaa/llm/build/llm-providers/llmprovider.d.ts","../node_modules/@openvaa/llm/build/modelpricing.d.ts","../node_modules/@openvaa/llm/build/prompts/localizationinstructions.d.ts","../node_modules/@openvaa/llm/build/prompts/types.d.ts","../node_modules/@openvaa/llm/build/prompts/promptregistry.d.ts","../node_modules/@openvaa/llm/build/prompts/index.d.ts","../node_modules/@openvaa/llm/build/types/commonllmparams.d.ts","../node_modules/@openvaa/llm/build/types/llmpipelineresult.d.ts","../node_modules/@openvaa/llm/build/utils/costcalculation.d.ts","../node_modules/@openvaa/llm/build/utils/latencytracker.type.d.ts","../node_modules/@openvaa/llm/build/utils/latencytracker.d.ts","../node_modules/@openvaa/llm/build/utils/parseratelimiterror.d.ts","../node_modules/@openvaa/llm/build/utils/prompting/loadpackageprompts.d.ts","../node_modules/@openvaa/llm/build/utils/prompting/setpromptvars.d.ts","../node_modules/@openvaa/llm/build/utils/index.d.ts","../node_modules/@openvaa/llm/build/index.d.ts","./src/lib/utils/route/params.ts","./src/lib/utils/route/filterpersistent.ts","./src/lib/utils/logger.ts","./src/lib/utils/route/parseparams.ts","./src/lib/utils/route/route.ts","./src/lib/utils/removeduplicates.ts","./src/lib/utils/route/buildroute.ts","./src/lib/utils/route/impliedparams.ts","./src/lib/utils/route/index.ts","./src/lib/admin/features.ts","./src/lib/server/admin/jobs/jobstore.type.ts","./src/lib/api/adapters/strapi/strapidata.type.ts","./src/lib/api/adapters/strapi/strapiapi.ts","./src/lib/api/base/datawriter.type.ts","./src/lib/api/adapters/strapi/strapiadapter.type.ts","./src/lib/api/adapters/strapi/strapiadapter.ts","./src/lib/api/adapters/strapi/feedbackwriter/strapifeedbackwriter.ts","./src/lib/api/adapters/strapi/feedbackwriter/index.ts","./src/lib/api/adapters/apiroute/apiroutes.ts","./src/lib/api/adapters/apiroute/apirouteadapter.type.ts","./src/lib/api/adapters/apiroute/apirouteadapter.ts","./src/lib/api/adapters/apiroute/feedbackwriter/apiroutefeedbackwriter.ts","./src/lib/api/adapters/apiroute/feedbackwriter/index.ts","./src/lib/api/feedbackwriter.ts","../node_modules/svelte/elements.d.ts","./src/lib/components/icon/icons.ts","./src/lib/components/icon/icon.type.ts","./src/lib/components/icon/index.ts","./src/lib/components/alert/alert.type.ts","./src/lib/components/alert/index.ts","./src/lib/dynamic-components/feedback/popup/feedbackpopup.type.ts","./src/lib/dynamic-components/feedback/popup/index.ts","./src/lib/dynamic-components/survey/popup/surveypopup.type.ts","./src/lib/dynamic-components/survey/popup/index.ts","./src/lib/utils/settings.ts","./src/lib/contexts/app/getroute.ts","./src/lib/contexts/app/popup/popupcomponent.type.ts","./src/lib/contexts/app/popup/popupstore.type.ts","./src/lib/contexts/app/popup/popupstore.ts","./src/lib/contexts/app/popup/index.ts","./src/lib/contexts/app/tracking/trackingevent.type.ts","./src/lib/utils/components.ts","./src/lib/utils/purgenullish.ts","./src/lib/contexts/utils/storagestore.ts","./src/lib/contexts/app/userpreferences.type.ts","./src/lib/contexts/app/tracking/trackingservice.type.ts","./src/lib/contexts/app/tracking/trackingservice.ts","./src/lib/contexts/app/tracking/index.ts","./src/lib/contexts/app/survey.ts","./src/lib/contexts/component/darkmode.ts","../node_modules/@sveltekit-i18n/base/dist/index.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/canonicalizelocalelist.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/canonicalizetimezonename.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/coerceoptionstoobject.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/getnumberoption.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/getoption.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/getoptionsobject.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/getstringorbooleanoption.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/issanctionedsimpleunitidentifier.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/isvalidtimezonename.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/iswellformedcurrencycode.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/iswellformedunitidentifier.d.ts","../node_modules/decimal.js/decimal.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/types/core.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/types/plural-rules.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/types/number.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/applyunsignedroundingmode.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/collapsenumberrange.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/computeexponent.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/computeexponentformagnitude.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/currencydigits.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/format_to_parts.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/formatapproximately.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/formatnumericrange.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/formatnumericrangetoparts.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/formatnumerictoparts.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/formatnumerictostring.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/getunsignedroundingmode.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/initializenumberformat.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/partitionnumberpattern.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/partitionnumberrangepattern.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/setnumberformatdigitoptions.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/setnumberformatunitoptions.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/torawfixed.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/numberformat/torawprecision.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/partitionpattern.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/supportedlocales.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/utils.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/262.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/data.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/types/date-time.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/types/displaynames.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/types/list.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/types/relative-time.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/constants.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/ecma402-abstract/index.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/icu-skeleton-parser/date-time.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/icu-skeleton-parser/number.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/icu-skeleton-parser/index.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/icu-messageformat-parser/types.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/icu-messageformat-parser/error.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/icu-messageformat-parser/parser.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/@formatjs/icu-messageformat-parser/index.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/intl-messageformat/src/formatters.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/intl-messageformat/src/core.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/intl-messageformat/src/error.d.ts","../node_modules/@sveltekit-i18n/parser-icu/node_modules/intl-messageformat/index.d.ts","../node_modules/@sveltekit-i18n/parser-icu/dist/index.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/canonicalizelocalelist.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/canonicalizetimezonename.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/coerceoptionstoobject.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/getnumberoption.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/getoption.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/getoptionsobject.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/getstringorbooleanoption.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/issanctionedsimpleunitidentifier.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/isvalidtimezonename.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/iswellformedcurrencycode.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/iswellformedunitidentifier.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/types/core.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/types/plural-rules.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/types/number.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/applyunsignedroundingmode.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/collapsenumberrange.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/computeexponent.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/computeexponentformagnitude.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/currencydigits.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/format_to_parts.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/formatapproximately.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/formatnumeric.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/formatnumericrange.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/formatnumericrangetoparts.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/formatnumerictoparts.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/formatnumerictostring.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/getunsignedroundingmode.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/initializenumberformat.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/partitionnumberpattern.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/partitionnumberrangepattern.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/setnumberformatdigitoptions.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/setnumberformatunitoptions.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/torawfixed.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/numberformat/torawprecision.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/partitionpattern.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/supportedlocales.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/utils.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/262.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/data.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/types/date-time.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/types/displaynames.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/types/list.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/types/relative-time.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/constants.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/tointlmathematicalvalue.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract/index.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/icu-skeleton-parser/date-time.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/icu-skeleton-parser/number.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/icu-skeleton-parser/index.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/icu-messageformat-parser/types.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/icu-messageformat-parser/error.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/icu-messageformat-parser/parser.d.ts","../node_modules/intl-messageformat/node_modules/@formatjs/icu-messageformat-parser/index.d.ts","../node_modules/intl-messageformat/src/formatters.d.ts","../node_modules/intl-messageformat/src/core.d.ts","../node_modules/intl-messageformat/src/error.d.ts","../node_modules/intl-messageformat/index.d.ts","./src/lib/utils/text/ucfirst.ts","./src/lib/i18n/translations/translations.type.ts","./src/lib/i18n/translations/index.ts","./src/lib/types/generated/translationkey.ts","./src/lib/types/index.ts","./src/lib/i18n/utils/asserttranslationkey.ts","./src/lib/i18n/utils/canonize.ts","./src/lib/i18n/utils/islocale.ts","./src/lib/i18n/utils/matchlocale.ts","./src/lib/i18n/utils/parseacceptedlanguages.ts","./src/lib/i18n/utils/index.ts","./src/lib/i18n/init.ts","./src/lib/i18n/index.ts","./src/lib/contexts/i18n/i18ncontext.type.ts","./src/lib/contexts/i18n/i18ncontext.ts","./src/lib/contexts/i18n/index.ts","./src/lib/contexts/component/componentcontext.type.ts","./src/lib/contexts/component/componentcontext.ts","./src/lib/contexts/component/index.ts","./src/lib/contexts/utils/parsimoniusderived.ts","./src/lib/contexts/utils/paramstore.ts","./src/lib/contexts/data/datacontext.type.ts","./src/lib/contexts/data/datacontext.ts","./src/lib/contexts/data/index.ts","./src/lib/contexts/utils/pagedatumstore.ts","./src/lib/contexts/app/appcustomization.type.ts","./src/lib/contexts/app/appcontext.type.ts","./src/lib/contexts/app/appcontext.ts","./src/lib/contexts/app/index.ts","./src/lib/api/base/datatypes.ts","./src/lib/api/base/getdatafilters.type.ts","./src/lib/api/base/getdataoptions.type.ts","./src/lib/api/base/dataprovider.type.ts","./src/app.d.ts","./src/env.d.ts","./src/lib/auth/authtoken.ts","./src/lib/api/base/universaldatawriter.ts","./src/lib/api/utils/translatequestioninfosections.ts","./src/lib/api/adapters/strapi/utils/buildfilterparams.ts","./src/lib/api/utils/formatid.ts","./src/lib/api/adapters/strapi/utils/parsebasics.ts","./src/lib/api/utils/parseanswers.ts","./src/lib/utils/links.ts","./src/lib/api/adapters/strapi/utils/parseimage.ts","./src/lib/api/adapters/strapi/utils/parsecandidate.ts","./src/lib/api/adapters/strapi/utils/parseentitytype.ts","./src/lib/api/adapters/strapi/utils/parsenominations.ts","./src/lib/api/adapters/strapi/utils/parseorganization.ts","./src/lib/api/adapters/strapi/utils/parsequestiontype.ts","./src/lib/api/adapters/strapi/utils/parserelationids.ts","./src/lib/api/adapters/strapi/utils/parseuser.ts","./src/lib/api/adapters/strapi/utils/index.ts","./src/lib/api/adapters/strapi/datawriter/strapidatawriter.ts","./src/lib/api/adapters/strapi/datawriter/index.ts","./src/lib/api/datawriter.ts","./src/lib/auth/getuserdata.ts","./src/lib/auth/index.ts","./src/hooks.server.ts","./src/lib/admin/components/jobs/featurejobs.type.ts","./src/lib/admin/components/jobs/jobdetails.type.ts","./src/lib/admin/components/jobs/index.ts","./src/lib/admin/components/jobs/shared.ts","./src/lib/components/select/select.type.ts","./src/lib/admin/components/languagefeatures/languageselector.type.ts","./src/lib/admin/components/languagefeatures/index.ts","./src/lib/utils/color/rgb.ts","./src/lib/utils/color/luminance.ts","./src/lib/utils/color/parsecolorstring.ts","./src/lib/utils/color/rgbtohex.ts","./src/lib/utils/color/adjustcontrast.ts","./src/lib/utils/color/ensurecolors.ts","./src/lib/api/base/universaldataprovider.ts","./src/lib/api/utils/translateherocontent.ts","./src/lib/api/utils/translatevideocontent.ts","./src/lib/api/adapters/strapi/utils/parsequestioncategorycustomdata.ts","./src/lib/api/utils/translatequestionarguments.ts","./src/lib/api/utils/translatequestionterms.ts","./src/lib/api/adapters/strapi/utils/parsequestioncustomdata.ts","./src/lib/api/adapters/strapi/dataprovider/strapidataprovider.ts","./src/lib/api/adapters/strapi/dataprovider/index.ts","./src/lib/api/adapters/apiroute/dataprovider/apiroutedataprovider.ts","./src/lib/api/adapters/apiroute/dataprovider/index.ts","./src/lib/api/dataprovider.ts","./src/lib/api/utils/isvalidresult.ts","./src/lib/admin/utils/loadelectiondata.ts","./src/lib/admin/utils/loginerror.ts","../node_modules/@vitest/pretty-format/dist/index.d.ts","../node_modules/@vitest/utils/dist/types.d.ts","../node_modules/@vitest/utils/dist/helpers.d.ts","../node_modules/tinyrainbow/dist/index-c1cfc5e9.d.ts","../node_modules/tinyrainbow/dist/node.d.ts","../node_modules/@vitest/utils/dist/index.d.ts","../node_modules/@vitest/runner/dist/tasks-3znpj1lr.d.ts","../node_modules/@vitest/utils/dist/types-bxe-2udy.d.ts","../node_modules/@vitest/utils/dist/diff.d.ts","../node_modules/@vitest/runner/dist/types.d.ts","../node_modules/@vitest/utils/dist/error.d.ts","../node_modules/@vitest/runner/dist/index.d.ts","../node_modules/vitest/dist/chunks/environment.looobwuu.d.ts","../node_modules/@vitest/snapshot/dist/environment-ddx0edty.d.ts","../node_modules/@vitest/snapshot/dist/rawsnapshot-cpnkto81.d.ts","../node_modules/@vitest/snapshot/dist/index.d.ts","../node_modules/@vitest/snapshot/dist/environment.d.ts","../node_modules/vitest/dist/chunks/config.cy0c388z.d.ts","../node_modules/vite-node/dist/trace-mapping.d-dlvdeqop.d.ts","../node_modules/vite-node/dist/index-z0r8hvru.d.ts","../node_modules/vite-node/dist/index.d.ts","../node_modules/@vitest/utils/dist/source-map.d.ts","../node_modules/vite-node/dist/client.d.ts","../node_modules/vite-node/dist/server.d.ts","../node_modules/@vitest/runner/dist/utils.d.ts","../node_modules/tinybench/dist/index.d.ts","../node_modules/vitest/dist/chunks/benchmark.geerunq4.d.ts","../node_modules/@vitest/snapshot/dist/manager.d.ts","../node_modules/vitest/dist/chunks/reporters.d7jzd9gs.d.ts","../node_modules/vitest/dist/chunks/worker.tn5kgiih.d.ts","../node_modules/vitest/dist/chunks/worker.b9fxpcac.d.ts","../node_modules/vitest/dist/chunks/vite.c-n5bbze.d.ts","../node_modules/@vitest/expect/dist/chai.d.cts","../node_modules/@vitest/expect/dist/index.d.ts","../node_modules/@vitest/expect/index.d.ts","../node_modules/@vitest/spy/dist/index.d.ts","../node_modules/@vitest/mocker/dist/types-dzoqtgin.d.ts","../node_modules/@vitest/mocker/dist/index.d.ts","../node_modules/vitest/dist/chunks/mocker.crtm890j.d.ts","../node_modules/vitest/dist/chunks/suite.b2jumifp.d.ts","../node_modules/expect-type/dist/utils.d.ts","../node_modules/expect-type/dist/overloads.d.ts","../node_modules/expect-type/dist/branding.d.ts","../node_modules/expect-type/dist/messages.d.ts","../node_modules/expect-type/dist/index.d.ts","../node_modules/vitest/dist/index.d.ts","./src/lib/api/base/universaladapter.test.ts","./src/lib/api/utils/authheaders.test.ts","./src/lib/api/utils/cachifyurl.test.ts","./src/lib/api/utils/fail.ts","./src/lib/api/utils/filterdata.ts","./src/lib/api/utils/filterdata.test.ts","./src/lib/api/utils/filterentitiesbynomination.ts","./src/lib/api/utils/filterentitiesbynomination.test.ts","./src/lib/api/utils/parseresponse.test.ts","./src/lib/api/utils/translateherocontent.test.ts","./src/lib/api/utils/translatelocalizedcandidate.ts","./src/lib/api/utils/auth/generatechallenge.ts","../node_modules/jose/dist/types/types.d.ts","../node_modules/jose/dist/types/jwe/compact/decrypt.d.ts","../node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts","../node_modules/jose/dist/types/jwe/general/decrypt.d.ts","../node_modules/jose/dist/types/jwe/general/encrypt.d.ts","../node_modules/jose/dist/types/jws/compact/verify.d.ts","../node_modules/jose/dist/types/jws/flattened/verify.d.ts","../node_modules/jose/dist/types/jws/general/verify.d.ts","../node_modules/jose/dist/types/jwt/verify.d.ts","../node_modules/jose/dist/types/jwt/decrypt.d.ts","../node_modules/jose/dist/types/jwt/produce.d.ts","../node_modules/jose/dist/types/jwe/compact/encrypt.d.ts","../node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts","../node_modules/jose/dist/types/jws/compact/sign.d.ts","../node_modules/jose/dist/types/jws/flattened/sign.d.ts","../node_modules/jose/dist/types/jws/general/sign.d.ts","../node_modules/jose/dist/types/jwt/sign.d.ts","../node_modules/jose/dist/types/jwt/encrypt.d.ts","../node_modules/jose/dist/types/jwk/thumbprint.d.ts","../node_modules/jose/dist/types/jwk/embedded.d.ts","../node_modules/jose/dist/types/jwks/local.d.ts","../node_modules/jose/dist/types/jwks/remote.d.ts","../node_modules/jose/dist/types/jwt/unsecured.d.ts","../node_modules/jose/dist/types/key/export.d.ts","../node_modules/jose/dist/types/key/import.d.ts","../node_modules/jose/dist/types/util/decode_protected_header.d.ts","../node_modules/jose/dist/types/util/decode_jwt.d.ts","../node_modules/jose/dist/types/util/errors.d.ts","../node_modules/jose/dist/types/key/generate_key_pair.d.ts","../node_modules/jose/dist/types/key/generate_secret.d.ts","../node_modules/jose/dist/types/util/base64url.d.ts","../node_modules/jose/dist/types/util/runtime.d.ts","../node_modules/jose/dist/types/index.d.ts","./src/lib/server/constants.ts","./src/lib/api/utils/auth/getidtokenclaims.ts","./src/lib/components/button/button.type.ts","./src/lib/components/button/index.ts","./src/lib/candidate/components/logoutbutton/logoutbutton.type.ts","./src/lib/candidate/components/logoutbutton/index.ts","./src/lib/candidate/components/passwordfield/passwordfield.type.ts","./src/lib/candidate/components/passwordfield/index.ts","./src/lib/candidate/components/passwordsetter/passwordsetter.type.ts","./src/lib/candidate/components/passwordsetter/index.ts","./src/lib/candidate/components/passwordvalidator/passwordvalidator.type.ts","./src/lib/candidate/components/passwordvalidator/index.ts","./src/lib/candidate/components/preregisterednotification/preregisterednotification.type.ts","./src/lib/candidate/components/preregisterednotification/index.ts","./src/lib/candidate/components/termsofuse/termsofuseform.type.ts","./src/lib/candidate/components/termsofuse/index.ts","./src/lib/candidate/utils/loginerror.ts","./src/lib/candidate/utils/preregistrationerror.ts","./src/lib/components/accordionselect/accordionselect.type.ts","./src/lib/components/accordionselect/index.ts","./src/lib/components/analytics/umami/umamianalytics.type.ts","./src/lib/components/analytics/umami/index.ts","./src/lib/components/avatar/avatar.type.ts","./src/lib/components/avatar/index.ts","./src/lib/components/modal/modalcontainer.type.ts","./src/lib/components/modal/modal.type.ts","./src/lib/components/modal/confirmation/confirmationmodal.type.ts","./src/lib/components/modal/confirmation/index.ts","./src/lib/components/buttonwithconfirmation/buttonwithconfirmation.type.ts","./src/lib/components/buttonwithconfirmation/index.ts","./src/lib/components/categorytag/categorytag.type.ts","./src/lib/components/categorytag/index.ts","./src/lib/components/constituencyselector/constituencyselector.type.ts","./src/lib/components/constituencyselector/singlegroupconstituencyselector.type.ts","./src/lib/components/constituencyselector/index.ts","./src/lib/components/controller/infomessages.type.ts","./src/lib/components/controller/progressbar.type.ts","./src/lib/components/controller/warningmessages.type.ts","./src/lib/components/controller/index.ts","./src/lib/components/electionselector/electionselector.type.ts","./src/lib/components/electionselector/index.ts","./src/lib/components/electionsymbol/electionsymbol.type.ts","./src/lib/components/electionsymbol/index.ts","./src/lib/components/electiontag/electiontag.type.ts","./src/lib/components/electiontag/index.ts","../node_modules/@openvaa/filters/build/filter/base/filter.type.d.ts","../node_modules/@openvaa/filters/build/filter/base/castvalue.d.ts","../node_modules/@openvaa/filters/build/missingvalue/missingvalue.d.ts","../node_modules/@openvaa/filters/build/missingvalue/index.d.ts","../node_modules/@openvaa/filters/build/filter/rules/rules.type.d.ts","../node_modules/@openvaa/filters/build/filter/rules/rules.d.ts","../node_modules/@openvaa/filters/build/filter/rules/index.d.ts","../node_modules/@openvaa/filters/build/filter/base/filtertypes.d.ts","../node_modules/@openvaa/filters/build/filter/base/filter.d.ts","../node_modules/@openvaa/filters/build/filter/base/index.d.ts","../node_modules/@openvaa/filters/build/filter/enumerated/enumeratedfilter.d.ts","../node_modules/@openvaa/filters/build/filter/enumerated/choicequestionfilter.d.ts","../node_modules/@openvaa/filters/build/filter/enumerated/objectfilter.d.ts","../node_modules/@openvaa/filters/build/filter/enumerated/index.d.ts","../node_modules/@openvaa/filters/build/filter/number/numberfilter.d.ts","../node_modules/@openvaa/filters/build/filter/number/numberquestionfilter.d.ts","../node_modules/@openvaa/filters/build/filter/number/index.d.ts","../node_modules/@openvaa/filters/build/filter/text/textfilter.d.ts","../node_modules/@openvaa/filters/build/filter/text/textpropertyfilter.d.ts","../node_modules/@openvaa/filters/build/filter/text/textquestionfilter.d.ts","../node_modules/@openvaa/filters/build/filter/text/index.d.ts","../node_modules/@openvaa/filters/build/filter/index.d.ts","../node_modules/@openvaa/filters/build/group/combineresults.d.ts","../node_modules/@openvaa/filters/build/group/filtergroup.d.ts","../node_modules/@openvaa/filters/build/group/index.d.ts","../node_modules/@openvaa/filters/build/utils/typeguards.d.ts","../node_modules/@openvaa/filters/build/utils/index.d.ts","../node_modules/@openvaa/filters/build/index.d.ts","./src/lib/components/entityfilters/entityfilters.type.ts","./src/lib/components/entityfilters/index.ts","./src/lib/components/entityfilters/enumerated/enumeratedentityfilter.type.ts","./src/lib/components/entityfilters/enumerated/index.ts","./src/lib/components/entityfilters/numeric/numericentityfilter.type.ts","./src/lib/components/entityfilters/numeric/index.ts","./src/lib/components/entityfilters/text/textentityfilter.type.ts","./src/lib/components/entityfilters/text/index.ts","./src/lib/components/entitytag/entitytag.type.ts","./src/lib/components/entitytag/index.ts","./src/lib/components/errormessage/errormessage.type.ts","./src/lib/components/errormessage/index.ts","./src/lib/components/expander/expander.type.ts","./src/lib/components/expander/index.ts","./src/lib/components/headinggroup/headinggroup.type.ts","./src/lib/components/headinggroup/preheading.type.ts","./src/lib/components/headinggroup/index.ts","./src/lib/components/hero/hero.type.ts","./src/lib/components/hero/index.ts","./src/lib/components/heroemoji/heroemoji.type.ts","./src/lib/components/heroemoji/index.ts","./src/lib/components/icon/previewallicons.type.ts","./src/lib/components/icon/svg/custom/missing_icon.ts","./src/lib/components/icon/svg/custom/opinion.ts","./src/lib/components/icon/svg/custom/skip.ts","./src/lib/components/icon/svg/custom/subtitles_off.ts","./src/lib/components/icon/svg/custom/subtitles_on.ts","./src/lib/components/icon/svg/material/arrow_back_ios.ts","./src/lib/components/icon/svg/material/arrow_forward_ios.ts","./src/lib/components/icon/svg/material/article.ts","./src/lib/components/icon/svg/material/check.ts","./src/lib/components/icon/svg/material/close.ts","./src/lib/components/icon/svg/material/create.ts","./src/lib/components/icon/svg/material/done_all.ts","./src/lib/components/icon/svg/material/download.ts","./src/lib/components/icon/svg/material/error.ts","./src/lib/components/icon/svg/material/expand_less.ts","./src/lib/components/icon/svg/material/expand_more.ts","./src/lib/components/icon/svg/material/face.ts","./src/lib/components/icon/svg/material/feedback.ts","./src/lib/components/icon/svg/material/filter_alt.ts","./src/lib/components/icon/svg/material/flag.ts","./src/lib/components/icon/svg/material/format_list_bulleted.ts","./src/lib/components/icon/svg/material/format_list_numbered.ts","./src/lib/components/icon/svg/material/group.ts","./src/lib/components/icon/svg/material/help.ts","./src/lib/components/icon/svg/material/home.ts","./src/lib/components/icon/svg/material/how_to_vote.ts","./src/lib/components/icon/svg/material/info.ts","./src/lib/components/icon/svg/material/language.ts","./src/lib/components/icon/svg/material/lightbulb.ts","./src/lib/components/icon/svg/material/link.ts","./src/lib/components/icon/svg/material/lock.ts","./src/lib/components/icon/svg/material/login.ts","./src/lib/components/icon/svg/material/logout.ts","./src/lib/components/icon/svg/material/menu.ts","./src/lib/components/icon/svg/material/pause.ts","./src/lib/components/icon/svg/material/person.ts","./src/lib/components/icon/svg/material/person_pin_circle.ts","./src/lib/components/icon/svg/material/photo_camera.ts","./src/lib/components/icon/svg/material/play_arrow.ts","./src/lib/components/icon/svg/material/playlist_add_check.ts","./src/lib/components/icon/svg/material/playlist_remove.ts","./src/lib/components/icon/svg/material/priority_high.ts","./src/lib/components/icon/svg/material/remove_done.ts","./src/lib/components/icon/svg/material/replay.ts","./src/lib/components/icon/svg/material/science.ts","./src/lib/components/icon/svg/material/search.ts","./src/lib/components/icon/svg/material/security.ts","./src/lib/components/icon/svg/material/settings.ts","./src/lib/components/icon/svg/material/skip_next.ts","./src/lib/components/icon/svg/material/skip_previous.ts","./src/lib/components/icon/svg/material/sort_by_alpha.ts","./src/lib/components/icon/svg/material/switch_account.ts","./src/lib/components/icon/svg/material/videocam.ts","./src/lib/components/icon/svg/material/videocam_off.ts","./src/lib/components/icon/svg/material/visibility.ts","./src/lib/components/icon/svg/material/visibility_off.ts","./src/lib/components/icon/svg/material/volume_off.ts","./src/lib/components/icon/svg/material/volume_up.ts","./src/lib/components/icon/svg/material/warning.ts","./src/lib/components/image/image.type.ts","./src/lib/components/image/index.ts","./src/lib/components/infoanswer/infoanswer.type.ts","./src/lib/components/infoanswer/index.ts","./src/lib/components/infobadge/infobadge.type.ts","./src/lib/components/infobadge/index.ts","./src/lib/components/input/input.type.ts","./src/lib/components/input/inputgroup.type.ts","./src/lib/components/input/previewallinputs.type.ts","./src/lib/components/input/questioninput.type.ts","./src/lib/components/input/shared.ts","./src/lib/components/input/index.ts","./src/lib/components/loading/loading.type.ts","./src/lib/components/loading/index.ts","./src/lib/components/matchscore/matchscore.type.ts","./src/lib/components/matchscore/index.ts","./src/lib/components/modal/index.ts","./src/lib/components/modal/drawer/drawer.type.ts","./src/lib/components/modal/drawer/index.ts","./src/lib/components/modal/timed/timedmodal.type.ts","./src/lib/components/modal/timed/index.ts","./src/lib/components/notification/notification.type.ts","./src/lib/components/notification/index.ts","./src/lib/components/openvaalogo/openvaalogo.type.ts","./src/lib/components/openvaalogo/index.ts","./src/lib/components/preventnavigation/preventnavigation.type.ts","./src/lib/components/preventnavigation/index.ts","./src/lib/components/questions/opinionquestioninput.type.ts","./src/lib/components/questions/questionactions.type.ts","./src/lib/components/questions/questionbasicinfo.type.ts","./src/lib/components/questions/questionchoices.type.ts","./src/lib/components/questions/questionextendedinfo.type.ts","./src/lib/components/questions/questionextendedinfobutton.type.ts","./src/lib/components/questions/questionextendedinfodrawer.type.ts","./src/lib/components/questions/questionopenanswer.type.ts","./src/lib/components/questions/index.ts","./src/lib/components/scoregauge/scoregauge.type.ts","./src/lib/components/scoregauge/index.ts","./src/lib/components/select/index.ts","../node_modules/@openvaa/matching/build/space/position.d.ts","../node_modules/@openvaa/matching/build/space/shape.d.ts","../node_modules/@openvaa/matching/build/space/matchingspace.d.ts","../node_modules/@openvaa/matching/build/space/createsubspace.d.ts","../node_modules/@openvaa/matching/build/space/index.d.ts","../node_modules/@openvaa/matching/build/missingvalue/bias.d.ts","../node_modules/@openvaa/matching/build/missingvalue/missingvaluemethod.d.ts","../node_modules/@openvaa/matching/build/missingvalue/impute.d.ts","../node_modules/@openvaa/matching/build/missingvalue/index.d.ts","../node_modules/@openvaa/matching/build/distance/metric.d.ts","../node_modules/@openvaa/matching/build/distance/measure.type.d.ts","../node_modules/@openvaa/matching/build/distance/measure.d.ts","../node_modules/@openvaa/matching/build/distance/index.d.ts","../node_modules/@openvaa/matching/build/question/categoricalquestion.d.ts","../node_modules/@openvaa/matching/build/question/matchablequestiongroup.d.ts","../node_modules/@openvaa/matching/build/question/ordinalquestion.d.ts","../node_modules/@openvaa/matching/build/question/index.d.ts","../node_modules/@openvaa/matching/build/match/submatch.d.ts","../node_modules/@openvaa/matching/build/match/matchtypes.d.ts","../node_modules/@openvaa/matching/build/match/matchbase.d.ts","../node_modules/@openvaa/matching/build/match/match.d.ts","../node_modules/@openvaa/matching/build/match/index.d.ts","../node_modules/@openvaa/matching/build/algorithms/matchingspaceprojector.d.ts","../node_modules/@openvaa/matching/build/algorithms/matchingalgorithm.type.d.ts","../node_modules/@openvaa/matching/build/algorithms/matchingalgorithm.d.ts","../node_modules/@openvaa/matching/build/algorithms/index.d.ts","../node_modules/@openvaa/matching/build/utils/typeguards.d.ts","../node_modules/@openvaa/matching/build/utils/index.d.ts","../node_modules/@openvaa/matching/build/index.d.ts","./src/lib/components/submatches/submatches.type.ts","./src/lib/components/submatches/index.ts","./src/lib/components/successmessage/successmessage.type.ts","./src/lib/components/successmessage/index.ts","./src/lib/components/tabs/tabs.type.ts","./src/lib/components/tabs/index.ts","./src/lib/components/term/term.type.ts","./src/lib/components/term/index.ts","./src/lib/components/toggle/toggle.type.ts","./src/lib/components/toggle/index.ts","./src/lib/components/video/video.type.ts","./src/lib/components/video/component-stores.ts","./src/lib/components/video/index.ts","./src/lib/components/warning/warning.type.ts","./src/lib/components/warning/index.ts","./src/lib/utils/entities.ts","./src/lib/utils/sorting.ts","./src/lib/contexts/admin/jobstores.type.ts","./src/lib/contexts/admin/jobstores.ts","./src/lib/contexts/utils/preparedatawriter.ts","./src/lib/contexts/auth/authcontext.type.ts","./src/lib/contexts/auth/authcontext.ts","./src/lib/contexts/auth/index.ts","./src/lib/contexts/admin/admincontext.type.ts","./src/lib/contexts/admin/admincontext.ts","./src/lib/contexts/admin/index.ts","./src/lib/contexts/candidate/candidateuserdatastore.type.ts","./src/lib/contexts/candidate/candidateuserdatastore.ts","./src/lib/contexts/utils/questionblockstore.type.ts","./src/lib/contexts/utils/questionblockstore.ts","./src/lib/contexts/utils/questioncategorystore.ts","./src/lib/contexts/utils/questionstore.ts","./src/lib/contexts/candidate/candidatecontext.type.ts","./src/lib/contexts/candidate/candidatecontext.ts","./src/lib/contexts/candidate/index.ts","./src/lib/utils/merge.ts","./src/lib/utils/timing.ts","./src/lib/contexts/utils/stackedstore.ts","./src/lib/contexts/layout/layoutcontext.type.ts","./src/lib/contexts/layout/layoutcontext.ts","./src/lib/contexts/layout/index.ts","./src/lib/contexts/utils/datacollectionstore.ts","./src/lib/utils/freeze.ts","./src/lib/contexts/voter/answerstore.type.ts","./src/lib/contexts/voter/answerstore.ts","./src/lib/contexts/voter/countanswers.ts","./src/lib/contexts/voter/selectiontree.type.ts","./src/lib/contexts/voter/voter.ts","./src/lib/contexts/voter/filters/buildparentfilters.ts","./src/lib/contexts/voter/filters/buildquestionfilter.ts","./src/lib/contexts/voter/nominationandquestionstore.ts","./src/lib/contexts/voter/filters/filterstore.ts","./src/lib/utils/matching/imputeparentanswers.type.ts","./src/lib/utils/matching/median.ts","./src/lib/utils/matching/mode.ts","./src/lib/utils/matching/imputeparentanswers.ts","./src/lib/utils/matching/mean.ts","./src/lib/utils/matching/index.ts","./src/lib/contexts/voter/matchstore.ts","./src/lib/contexts/voter/votercontext.type.ts","./src/lib/contexts/voter/votercontext.ts","./src/lib/contexts/voter/index.ts","./src/lib/dynamic-components/applogo/applogo.type.ts","./src/lib/dynamic-components/applogo/index.ts","./src/lib/dynamic-components/dataconsent/dataconsent.type.ts","./src/lib/dynamic-components/dataconsent/dataconsentinfobutton.type.ts","./src/lib/dynamic-components/dataconsent/index.ts","./src/lib/dynamic-components/dataconsent/popup/dataconsentpopup.type.ts","./src/lib/dynamic-components/dataconsent/popup/index.ts","./src/lib/dynamic-components/entitycard/entitycard.type.ts","./src/lib/dynamic-components/entitycard/entitycardaction.type.ts","./src/lib/dynamic-components/entitycard/index.ts","./src/lib/dynamic-components/entitydetails/entitychildren.type.ts","./src/lib/dynamic-components/entitydetails/entitydetails.type.ts","./src/lib/dynamic-components/entitydetails/entityinfo.type.ts","./src/lib/dynamic-components/entitydetails/entityopinions.type.ts","./src/lib/dynamic-components/entitydetails/infoitem.type.ts","./src/lib/dynamic-components/entitydetails/index.ts","./src/lib/dynamic-components/entitydetails/entitydetailsdrawer.type.ts","./src/lib/dynamic-components/entitylist/entitylist.type.ts","./src/lib/dynamic-components/entitylist/entitylistcontrols.type.ts","./src/lib/dynamic-components/entitylist/index.ts","./src/lib/dynamic-components/feedback/feedback.type.ts","./src/lib/dynamic-components/feedback/index.ts","./src/lib/dynamic-components/feedback/modal/feedbackmodal.type.ts","./src/lib/dynamic-components/feedback/modal/index.ts","./src/lib/dynamic-components/footer/footer.type.ts","./src/lib/dynamic-components/footer/index.ts","./src/lib/dynamic-components/logoutbutton/logoutbutton.type.ts","./src/lib/dynamic-components/logoutbutton/index.ts","./src/lib/dynamic-components/navigation/navgroup.type.ts","./src/lib/dynamic-components/navigation/navitem.type.ts","./src/lib/dynamic-components/navigation/navigation.type.ts","./src/lib/dynamic-components/navigation/index.ts","./src/lib/dynamic-components/navigation/admin/adminnav.type.ts","./src/lib/dynamic-components/navigation/admin/index.ts","./src/lib/dynamic-components/navigation/candidate/candidatenav.type.ts","./src/lib/dynamic-components/navigation/candidate/index.ts","./src/lib/dynamic-components/navigation/languages/index.ts","./src/lib/dynamic-components/navigation/voter/voternav.type.ts","./src/lib/dynamic-components/navigation/voter/index.ts","./src/lib/dynamic-components/questionheading/questionheading.type.ts","./src/lib/dynamic-components/questionheading/index.ts","./src/lib/dynamic-components/survey/surveybutton.type.ts","./src/lib/dynamic-components/survey/index.ts","./src/lib/dynamic-components/survey/banner/surveybanner.type.ts","./src/lib/dynamic-components/survey/banner/index.ts","./src/lib/i18n/tests/translations.test.ts","./src/lib/i18n/tests/utils.test.ts","./src/lib/i18n/utils/intl.d.ts","../node_modules/@openvaa/argument-condensation/build/prompts.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/condensation/argument.d.ts","../packages/llm/build/index.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/condensation/condensationtype.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/api/apiconfig.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/condensation/operation.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/condensation/processparams.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/condensation/processdefinition.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/condensation/supportedquestion.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/condensation/condensationinput.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/api/commentgroup.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/llm/promptinstance.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/condensation/processstepresult.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/condensation/condensationresult.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/base/operationtree.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/llm/responsewitharguments.d.ts","../node_modules/@openvaa/argument-condensation/build/core/types/index.d.ts","../node_modules/@openvaa/argument-condensation/build/api.d.ts","../node_modules/@openvaa/argument-condensation/build/core/condensation/condenser.d.ts","../node_modules/@openvaa/argument-condensation/build/core/utils/condensation/normalizeargumentlists.d.ts","../node_modules/@openvaa/argument-condensation/build/core/utils/condensation/planvalidation.d.ts","../node_modules/@openvaa/argument-condensation/build/core/utils/condensation/validateinputtokencount.d.ts","../node_modules/@openvaa/argument-condensation/build/core/utils/condensation/calculatellmcallcounts.d.ts","../node_modules/@openvaa/argument-condensation/build/core/utils/condensation/createbatches.d.ts","../node_modules/@openvaa/argument-condensation/build/core/utils/condensation/getandslicecomments.d.ts","../node_modules/@openvaa/argument-condensation/build/core/utils/condensation/definecondensationsteps.d.ts","../node_modules/@openvaa/argument-condensation/build/core/utils/condensation/getparallelfactor.d.ts","../node_modules/@openvaa/argument-condensation/build/core/utils/condensation/issupportedquestion.d.ts","../node_modules/@openvaa/argument-condensation/build/core/utils/index.d.ts","../node_modules/@openvaa/argument-condensation/build/defaultvalues.d.ts","../node_modules/@openvaa/argument-condensation/build/index.d.ts","./src/lib/server/llm/llmprovider.ts","./src/lib/server/admin/jobs/jobstore.ts","./src/lib/server/admin/jobs/pipelinecontroller.ts","./src/lib/server/admin/features/condensearguments.ts","../node_modules/@openvaa/question-info/build/prompts.d.ts","../packages/app-shared/build/esm/index.d.ts","../node_modules/@openvaa/question-info/build/core/infogeneration.type.d.ts","../node_modules/@openvaa/question-info/build/api.d.ts","../node_modules/@openvaa/question-info/build/consts.d.ts","../node_modules/@openvaa/question-info/build/index.d.ts","./src/lib/server/admin/features/generatequestioninfo.ts","./src/lib/server/api/adapters/local/localpaths.ts","./src/lib/server/api/adapters/local/localserveradapter.ts","./src/lib/server/api/adapters/local/dataprovider/localserverdataprovider.ts","./src/lib/server/api/adapters/local/dataprovider/index.ts","./src/lib/server/api/dataprovider.ts","./src/lib/server/api/adapters/local/feedbackwriter/localserverfeedbackwriter.ts","./src/lib/server/api/adapters/local/feedbackwriter/index.ts","./src/lib/server/api/feedbackwriter.ts","./src/lib/types/global.d.ts","./src/lib/utils/breakpoints.ts","./src/lib/utils/email.ts","./src/lib/utils/entitycards.ts","./src/lib/utils/matches.ts","./src/lib/utils/entitydetails.ts","./src/lib/utils/image.ts","./src/lib/utils/image.test.ts","./src/lib/utils/onkeyboardfocusout.ts","./src/lib/utils/regexp.ts","../node_modules/@types/trusted-types/lib/index.d.ts","../node_modules/@types/trusted-types/index.d.ts","../node_modules/dompurify/dist/purify.es.d.mts","../node_modules/isomorphic-dompurify/index.d.ts","./src/lib/utils/sanitize.ts","./src/lib/utils/aria/focus.ts","./src/lib/utils/color/parsecolors.ts","./src/lib/utils/matching/mean.test.ts","./src/lib/utils/matching/median.test.ts","./src/lib/utils/matching/mode.test.ts","./src/lib/utils/questions/electiontags.ts","./src/lib/utils/questions/index.ts","./src/lib/utils/text/abbreviate.ts","./src/lib/utils/text/tonamecase.ts","./src/params/entitytype.ts","./src/params/locale.ts","./tests/password-validation.spec.ts","./tests/sample.spec.ts","./tests/strapidataprovider/strapidataprovider.test.ts","../node_modules/vitest/globals.d.ts"],"fileIdsList":[[97,140,533,717],[97,140],[97,140,220,352,522,695,697,741],[97,140,529,544],[85,97,140,743,744],[97,140,530,544],[85,97,140],[97,140,747],[97,140,528],[97,140,244,333,714,767,768],[97,140,689],[97,140,356,538,539],[97,140,355,538],[97,140,352,357,714],[97,140,540,714,716,756],[97,140,765],[97,140,358,359,538,540],[97,140,541],[97,140,763],[97,140,333,534,535,697,714,716,730,736,756,759,762],[97,140,737],[97,140,244,333,357,533,534,535,721,736],[97,140,536],[97,140,357,358,359,535],[97,140,220,333,349,356,531,532,533,534],[97,140,355,532,533],[97,140,531],[97,140,244,346,519,529,530],[97,140,534,715],[97,140,722,723,725,728,729,730,731,732,733,734,735],[97,140,244,697,724],[97,140,333,531,533,724,726,728],[97,140,333,531],[97,140,333,349,531,727],[97,140,244,333,346,522,531,736],[97,140,333,531,725,726,728],[97,140,346,531,757,758],[97,140,346,531,722,757,758,760,761],[97,140,333,346,531,697,724],[97,140,244,531,724],[97,140,531,533,724],[97,140,224,714,716],[97,140,333,346,713,717],[97,140,224,244,333,346,357,529,530,714],[97,140,224,357],[97,140,244,333],[97,140,715],[97,140,349,356,816],[97,140,348,349,350,351,353,354,355],[97,140,348,354],[97,140,333,356,714,716,717,755],[97,140,220,244,352,356,357,530,533],[97,140,220,356,357,358],[97,140,346,756,764,766],[97,140,346,522,721,738],[97,140,346,359,537,542],[97,140,349,861,862],[97,140,351,816],[97,140,353,816],[97,140,352],[97,140,220,357],[97,140,244,333,816,821],[97,140,333,816,823],[97,140,244],[97,140,522,714],[97,140,333,346,533,697,724],[97,140,354,816],[97,140,333,346,757,816],[97,140,346,697],[97,140,333,533,726],[97,140,220,522,533,720,739],[97,140,720,740],[85,97,140,866],[97,140,865],[85,97,140,868],[97,140,544],[85,97,140,870],[85,97,140,872],[85,97,140,874],[97,140,549],[85,97,140,876],[85,97,140,880],[97,140,544,547],[85,97,140,548],[85,97,140,882],[97,140,567],[85,97,140,884],[85,97,140,864],[97,140,865,889],[85,97,140,890],[97,140,333,544],[85,97,140,892],[97,140,244,333,544],[85,97,140,894,895],[85,97,140,897,898,899],[85,97,140,901],[85,97,140,903],[85,97,140,905],[97,140,544,934],[97,140,333,544,934],[85,97,140,937],[85,97,140,935],[85,97,140,939],[85,97,140,941],[85,97,140,943],[85,97,140,945],[85,97,140,947],[85,97,140,949,950],[97,140,346,544],[85,97,140,952],[85,97,140,954],[97,140,544,545],[85,97,140,545,546],[97,140,546],[85,97,140,1016],[85,97,140,1018],[85,97,140,1020],[85,97,140,1022,1023,1024,1025,1026],[97,140,333,1022],[85,97,140,1028],[85,97,140,1030],[97,140,887],[85,97,140,888],[97,140,886],[85,97,140,1033],[85,97,140,886,887],[85,97,140,1035],[85,97,140,1037],[97,140,346,549],[85,97,140,1039],[85,97,140,1041],[85,97,140,1043,1044,1046,1047,1048,1049,1050],[97,140,333,1027],[97,140,948],[97,140,333,865,1047],[97,140,333,1034,1047],[85,97,140,1052],[85,97,140,747],[85,97,140,1084],[97,140,333,544,1053,1083],[85,97,140,1086],[85,97,140,1088],[85,97,140,1090],[85,97,140,1092],[85,97,140,1094],[97,140,346,544,567],[85,97,140,220,533,713,739,1102,1103,1106,1107],[85,97,140,533,713,1101,1106],[97,140,1101,1107,1108],[85,97,140,352,522,529,530,704,1100,1101],[85,97,140,529,530],[85,97,140,220,346,357,358,543,551,553,554,555,559,563,564,567,568,703,708,709,710,711],[85,97,140,358,555,559,564,567,703,708,710],[97,140,333,689],[85,97,140,220,528],[97,140,564,710,711,712],[97,140,556,557,558],[85,97,140,556,557],[85,97,140,556],[85,97,140,567],[97,140,560,565,566],[97,140,333],[85,97,140,220,522,560,561,562,563,564,565],[85,97,140,560],[85,97,140,220,357,522,533,739,1103,1104],[85,97,140,533],[97,140,1104,1105],[85,97,140,220,244,333,346,522,525,528,533,563,713,739,1103,1106,1111,1113,1114,1115,1116],[85,97,140,244,333,533,713,1106,1110,1112],[85,97,140,244,333,346,357,533,563,721,1103,1110],[85,97,140,244,346,357,533],[97,140,1110,1116,1117],[85,97,140,220,569,700,701],[97,140,569,700],[85,97,140,220],[97,140,701,702],[85,97,140,220,333,700,705,706],[85,97,140,333],[97,140,706,707],[85,97,140,220,697,698],[97,140,697],[97,140,698,699],[97,140,1122,1123],[85,97,140,220,346,1096,1119,1120,1121,1122],[85,97,140,346,1096,1119,1121],[85,97,140,244,333,525,704],[85,97,140,220,704],[85,97,140,220,528,704],[97,140,220,346,721],[85,97,140,244,333,522,704,1112],[85,97,140,333,704],[85,97,140,220,333,346,704,713],[85,97,140,220,346,522],[97,140,333,522,563,567,1126,1127],[85,97,140,333,1126],[97,140,333,525,934,1099],[97,140,333,522,934],[85,97,140,333,346,685,689,704,934,1130,1132,1133,1134],[97,140,1127,1130,1131,1143,1144],[85,97,140,333,525,704,1083,1127,1129,1130,1131,1134,1141],[85,97,140,244,333,704,1130],[85,97,140,220,244,333,522,528,563,704,705,713,1083,1113,1114,1115,1128,1129,1134,1135,1142,1143],[85,97,140,244,333,713,1083,1112,1127,1135,1142],[85,97,140,1146],[85,97,140,1148,1149],[85,97,140,1151],[97,140,544,1153],[85,97,140,1153],[97,140,333,544,1155],[97,140,1034,1161],[97,140,333,544,1157],[97,140,333,544,1145,1157],[85,97,140,1156,1157,1158,1159,1160,1162],[97,140,544,1155],[85,97,140,1163,1164],[85,97,140,1166],[97,140,1032],[85,97,140,1168],[85,97,140,550],[85,97,140,1170],[97,140,528,865],[97,140,1176],[85,97,140,1178],[85,97,140,1180],[85,97,140,1174,1175,1176],[85,97,140,1183],[85,97,140,1185],[97,140,333,951,1112],[85,97,140,1189],[85,97,140,1187],[85,97,140,552],[97,140,696],[85,97,140,220,346,522,570,627,684,685,687,689,695],[97,140,153,162,182,687,816],[97,140,695,697,816],[97,140,570,686],[97,140,690,691,692,693,694],[97,140,691],[97,140,244,333,346,357,739,769,1224,1225,1226,1227],[97,140,244,333,357,533,739,769,1225,1226,1227,1234],[97,140,529,530,746],[97,140,529],[97,140,244,1226],[97,140,1238],[97,140,220,333,346,522,716,717,821,823,1236,1237],[97,140,1241],[97,140,358,1237],[97,140,162,333,346,713,714,862],[97,140,154,162,220,357,1236],[97,140,346,717,1239],[97,140,346,358,1242],[97,140,719],[97,140,519,862],[97,140,244,333,346],[97,140,688],[97,140,750,751,752,753],[97,140,333,346,754],[97,140,750],[97,140,522,750],[97,140,244,333,1083],[97,140,333,346,1019],[97,140,244,333,1099,1142,1248],[97,140,333,816,1250],[97,140,685],[97,140,349],[97,140,244,333,1083,1099,1100,1142],[97,140,244,333,522,1083,1136,1137,1138],[97,140,1136,1137,1138,1139,1140],[97,140,816,1140],[97,140,816,1137],[97,140,816,1138],[97,140,1264],[97,140,220,348,520,521,523,524,525],[97,140,520],[97,140,520,523,524,526,527],[97,140,348,520,522],[97,140,1257],[97,140,346],[97,140,333,1099],[97,140,695,697],[97,140,816],[97,140,220,816],[97,140,216,220,222,802],[97,140,362,434,450],[97,140,362,434,447,448,449],[97,140,361],[97,140,458],[97,140,461],[97,140,466,468],[97,140,454,458,470,471],[97,140,481,484,490,492],[97,140,453,458],[97,140,452],[97,140,453],[97,140,460],[97,140,463],[97,140,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,493,494,495,496,497,498],[97,140,469],[97,140,465],[97,140,466],[97,140,457,458,464],[97,140,465,466],[97,140,472],[97,140,493],[97,140,457],[97,140,458,475,478],[97,140,474],[97,140,475],[97,140,473,475],[97,140,458,478,480,481,482],[97,140,481,482,484],[97,140,458,473,476,479,486],[97,140,473,474],[97,140,455,456,473,475,476,477],[97,140,475,478],[97,140,456,473,476,479],[97,140,458,478,480],[97,140,481,482],[97,140,225,244,333],[97,140,333,335],[97,140,334],[97,140,335],[97,140,333,334],[97,140,225,334,335,336,337,338,339,340,341,342,343,344,345],[97,140,341],[97,140,244,333,335],[97,140,343],[97,140,244,1210],[97,140,1210],[97,140,519,1197],[97,140,244,1203],[97,140,1195,1199,1203],[97,140,519,1197,1201,1202],[97,140,519,1195,1197],[97,140,1199,1200],[97,140,1195,1205],[97,140,1195,1197,1198,1199,1200,1201,1202,1203,1204,1205,1206,1207,1208,1209],[97,140,1199],[97,140,1195],[97,140,244,1202,1204],[97,140,333,1210],[97,140,519,1213,1214,1215,1216,1217,1218,1219,1220,1221],[97,140,519,1194,1210,1211,1212,1222,1223],[97,140,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243],[97,140,332],[97,140,907],[97,140,244,907,910,913,914],[97,140,928],[97,140,907,908,914,915],[97,140,244,333,910,916,917],[97,140,244,910,915],[97,140,917,918,919],[97,140,244,910,916,917],[97,140,916,920,923,927],[97,140,921,922],[97,140,244,910,916],[97,140,244,333,921],[97,140,911,912],[97,140,911],[97,140,910],[97,140,924,925,926],[97,140,244,916,924],[97,140,244,333,924],[97,140,244,928,929],[97,140,929,930],[97,140,910,928,931,933],[97,140,909],[97,140,932],[97,140,244,928],[97,140,500,503,504,505,509,510,511,518],[97,140,244,500,503],[97,140,244,500,501,502],[97,140,502],[97,140,506,507,508],[97,140,507],[97,140,244,504],[97,140,244,500],[97,140,500,502],[97,140,512,514,515,516,517],[97,140,244,513],[97,140,1077,1078,1079],[97,140,244,1059,1063,1067,1071,1076,1077,1078],[97,140,244,1063,1067,1071,1077],[97,140,1059],[97,140,1064,1065,1066],[97,140,244,1059,1065],[97,140,244,1063,1064],[97,140,244,1059],[97,140,1059,1063,1067,1071,1076,1080,1082],[97,140,1072,1073,1074,1075],[97,140,244,1071,1072,1074],[97,140,244,1073],[97,140,1072,1074,1075],[97,140,244,1071,1074],[97,140,244,1059,1060,1061],[97,140,1060,1061,1062],[97,140,1068,1069,1070],[97,140,244,1057],[97,140,1055,1056,1057,1058],[97,140,244,1055,1056],[97,140,244,1056,1057],[97,140,244,1055],[97,140,1081],[97,140,244,1071,1076],[97,140,333,1231],[97,140,244,346,519],[97,140,1229,1231,1232,1233],[85,91,97,140,155,216,218,219,220,802],[97,140,216,802],[85,97,140,216,217,802],[97,140,570,626],[97,140,582],[97,140,571,572,573,574,575,576,577,578,579,580,581,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614],[97,140,582,585],[97,140,585],[97,140,583],[97,140,582,583,584],[97,140,583,585],[97,140,583,584],[97,140,619],[97,140,619,621],[97,140,619,620],[97,140,615,618],[97,140,616,617],[97,140,615],[97,140,623,624,625],[97,140,622,623],[97,140,615,622],[97,137,140],[97,139,140],[140],[97,140,145,174],[97,140,141,146,152,153,160,171,182],[97,140,141,142,152,160],[92,93,94,97,140],[97,140,143,183],[97,140,144,145,153,161],[97,140,145,171,179],[97,140,146,148,152,160],[97,139,140,147],[97,140,148,149],[97,140,152],[97,140,150,152],[97,139,140,152],[97,140,152,153,154,171,182],[97,140,152,153,154,167,171,174],[97,135,140,187],[97,140,148,152,155,160,171,182],[97,140,152,153,155,156,160,171,179,182],[97,140,155,157,171,179,182],[95,96,97,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188],[97,140,152,158],[97,140,159,182,187],[97,140,148,152,160,171],[97,107,111,140,182],[97,107,140,171,182],[97,102,140],[97,104,107,140,179,182],[97,140,160,179],[97,140,189],[97,102,140,189],[97,104,107,140,160,182],[97,99,100,103,106,140,152,171,182],[97,107,114,140],[97,99,105,140],[97,107,128,129,140],[97,103,107,140,174,182,189],[97,128,140,189],[97,101,102,140,189],[97,107,140],[97,101,102,103,104,105,106,107,108,109,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,129,130,131,132,133,134,140],[97,107,122,140],[97,107,114,115,140],[97,105,107,115,116,140],[97,106,140],[97,99,102,107,140],[97,107,111,115,116,140],[97,111,140],[97,105,107,110,140,182],[97,99,104,107,114,140],[97,140,171],[97,102,107,128,140,187,189],[97,140,161],[97,140,162],[97,139,140,163],[97,137,138,139,140,141,142,143,144,145,146,147,148,149,150,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188],[97,140,165],[97,140,166],[97,140,152,167,168],[97,140,167,169,183,185],[97,140,152,171,172,173,174],[97,140,171,173],[97,140,171,172],[97,140,174],[97,140,175],[97,137,140,171],[97,140,152,177,178],[97,140,177,178],[97,140,145,160,171,179],[97,140,180],[97,140,160,181],[97,140,155,166,182],[97,140,145,183],[97,140,171,184],[97,140,159,185],[97,140,186],[97,140,145,152,154,163,171,182,185,187],[97,140,171,188],[97,140,1254],[97,140,775,776,779],[97,140,804],[97,140,807],[97,140,776,777,779,780,781],[97,140,776],[97,140,776,777,779],[97,140,776,777],[97,140,784],[97,140,771,784,785],[97,140,771,784],[97,140,771,778],[97,140,772],[97,140,771,772,773,775],[97,140,771],[97,140,155,362,434,447,450,451,499],[97,140,1255],[79,81,97,140,190],[79,82,83,97,140,190],[79,97,140,190],[97,140,811,812],[97,140,811,812,813,814],[97,140,811,813],[97,140,811],[97,140,681,682,683],[97,140,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672],[97,140,582,641],[97,140,641],[97,140,639],[97,140,582,639,640],[97,140,639,641],[97,140,639,640],[97,140,677],[97,140,677,679],[97,140,677,678],[97,140,673,676],[97,140,674,675],[97,140,673],[97,140,680,681],[97,140,673,680],[97,140,1256],[97,140,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860],[97,140,829],[97,140,829,839],[97,140,209],[97,140,207,209],[97,140,198,206,207,208,210],[97,140,196],[97,140,199,204,209,212],[97,140,195,212],[97,140,199,200,203,204,205,212],[97,140,199,200,201,203,204,212],[97,140,196,197,198,199,200,204,205,206,208,209,210,212],[97,140,212],[97,140,194,196,197,198,199,200,201,203,204,205,206,207,208,209,210,211],[97,140,194,212],[97,140,199,201,202,204,205,212],[97,140,203,212],[97,140,204,205,209,212],[97,140,197,207],[97,140,190,215],[79,80,84,85,97,140,190],[97,140,774],[97,140,789,790],[97,140,789],[97,140,216,789,790,802],[97,140,216,221,802],[90,97,140],[86,87,88,89,97,140,152,153,155,156,157,160,171,179,182,188,189,190,191,192,193,213,214,215],[86,87,88,97,140,192],[86,87,88,97,140],[86,97,140],[87,97,140],[88,89,97,140],[97,140,190],[97,140,782,795,796,816],[97,140,771,782,786,787,816],[97,140,808],[97,140,153,171,216,771,776,782,783,786,788,791,792,793,794,797,798,802,803,816],[97,140,782,795,796,797,816],[97,140,216,799],[97,140,187,800],[97,140,782,783,786,788,791,816],[97,140,153,171,187,216,771,776,779,782,783,786,787,788,791,792,793,794,795,796,797,798,799,800,801,802,803,805,806,808,809,810,815,816],[97,140,432],[97,140,437,438],[97,140,435,436,437,439,440,445],[97,140,436,437],[97,140,445],[97,140,446],[97,140,437],[97,140,435,436,437,440,441,442,443,444],[97,140,435,436,447],[97,140,424],[97,140,424,427],[97,140,418,424,425,426,427,428,429,430,431],[97,140,424,425],[97,140,424,426],[97,140,364,366,367,368,369],[97,140,364,366,368,369],[97,140,364,366,368],[97,140,363,364,366,367,369],[97,140,364,365,366,367,368,369,370,371,418,419,420,421,422,423],[97,140,366,369],[97,140,363,364,365,367,368,369],[97,140,366,419,422],[97,140,366,367,368,369],[97,140,433],[97,140,368],[97,140,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417],[97,140,227],[97,140,231],[97,140,232],[97,140,230],[97,140,237],[97,140,236],[97,140,230,237],[97,140,255],[97,140,274],[97,140,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331],[97,140,332,333],[97,140,246,332]],"fileInfos":[{"version":"e41c290ef7dd7dab3493e6cbe5909e0148edf4a8dad0271be08edec368a0f7b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"e12a46ce14b817d4c9e6b2b478956452330bf00c9801b79de46f7a1815b5bd40","impliedFormat":1},{"version":"4fd3f3422b2d2a3dfd5cdd0f387b3a8ec45f006c6ea896a4cb41264c2100bb2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"69e65d976bf166ce4a9e6f6c18f94d2424bf116e90837ace179610dbccad9b42","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"62bb211266ee48b2d0edf0d8d1b191f0c24fc379a82bd4c1692a082c540bc6b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"936e80ad36a2ee83fc3caf008e7c4c5afe45b3cf3d5c24408f039c1d47bdc1df","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"fef8cfad2e2dc5f5b3d97a6f4f2e92848eb1b88e897bb7318cef0e2820bceaab","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f1e2a172204962276504466a6393426d2ca9c54894b1ad0a6c9dad867a65f876","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"b5ce7a470bc3628408429040c4e3a53a27755022a32fd05e2cb694e7015386c7","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"bab26767638ab3557de12c900f0b91f710c7dc40ee9793d5a27d32c04f0bf646","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"61d6a2092f48af66dbfb220e31eea8b10bc02b6932d6e529005fd2d7b3281290","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"785b9d575b49124ce01b46f5b9402157c7611e6532effa562ac6aebec0074dfc","impliedFormat":1},{"version":"2be2227c3810dfd84e46674fd33b8d09a4a28ad9cb633ed536effd411665ea1e","impliedFormat":99},{"version":"d8e062c357df76b711f92e07446c99714390a79b9ab008facfb18b457aa436b4","impliedFormat":99},{"version":"94894f6fb4c3d669472c373e07d291a3fe14c19f3f195fb1e6338e7df0a25154","impliedFormat":99},{"version":"e344cc4394fa9b1f42058282fd262657a90f7b121677e99d527dd393cadd4226","impliedFormat":99},{"version":"bf8f7578124e832be0956030b8d6cd38cc81cafb973dc6f7154265a1ca1545e9","impliedFormat":99},{"version":"f3f060b72e0750b840b771e88e4adf7018569498cf247f9ccb1c79e1d933ea70","impliedFormat":99},{"version":"282f98006ed7fa9bb2cd9bdbe2524595cfc4bcd58a0bb3232e4519f2138df811","impliedFormat":1},{"version":"6222e987b58abfe92597e1273ad7233626285bc2d78409d4a7b113d81a83496b","impliedFormat":1},{"version":"cbe726263ae9a7bf32352380f7e8ab66ee25b3457137e316929269c19e18a2be","impliedFormat":1},{"version":"7f698624bbbb060ece7c0e51b7236520ebada74b747d7523c7df376453ed6fea","impliedFormat":1},{"version":"4025a454b1ca489b179ee8c684bdd70ff8c1967e382076ade53e7e4653e1daec","affectsGlobalScope":true,"impliedFormat":1},{"version":"984c09345059b76fc4221c2c54e53511f4c27a0794dfd6e9f81dc60f0b564e05","affectsGlobalScope":true,"impliedFormat":99},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"a79e62f1e20467e11a904399b8b18b18c0c6eea6b50c1168bf215356d5bebfaf","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fd06258805d26c72f5997e07a23155d322d5f05387adb3744a791fe6a0b042d","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"24bd580b5743dc56402c440dc7f9a4f5d592ad7a419f25414d37a7bfe11e342b","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"6bdc71028db658243775263e93a7db2fd2abfce3ca569c3cca5aee6ed5eb186d","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"4d2b0eb911816f66abe4970898f97a2cfc902bcd743cbfa5017fad79f7ef90d8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"e53a3c2a9f624d90f24bf4588aacd223e7bec1b9d0d479b68d2f4a9e6011147f","impliedFormat":1},{"version":"24b8685c62562f5d98615c5a0c1d05f297cf5065f15246edfe99e81ec4c0e011","impliedFormat":1},{"version":"93507c745e8f29090efb99399c3f77bec07db17acd75634249dc92f961573387","impliedFormat":1},{"version":"339dc5265ee5ed92e536a93a04c4ebbc2128f45eeec6ed29f379e0085283542c","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"ca6d304b929748ea15c33f28c1f159df18a94470b424ab78c52d68d40a41e1e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"a72ffc815104fb5c075106ebca459b2d55d07862a773768fce89efc621b3964b","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"3d77c73be94570813f8cadd1f05ebc3dc5e2e4fdefe4d340ca20cd018724ee36","impliedFormat":1},{"version":"d674383111e06b6741c4ad2db962131b5b0fa4d0294b998566c635e86195a453","affectsGlobalScope":true,"impliedFormat":1},{"version":"f3e58c4c18a031cbb17abec7a4ad0bd5ae9fc70c1f4ba1e7fb921ad87c504aca","impliedFormat":1},{"version":"a3e8bafb2af8e850c644f4be7f5156cf7d23b7bfdc3b786bd4d10ed40329649c","impliedFormat":1},{"version":"35ec8b6760fd7138bbf5809b84551e31028fb2ba7b6dc91d95d098bf212ca8b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb","impliedFormat":1},{"version":"f77d9188e41291acf14f476e931972460a303e1952538f9546e7b370cb8d0d20","affectsGlobalScope":true,"impliedFormat":1},{"version":"b0c0d1d13be149f790a75b381b413490f98558649428bb916fd2d71a3f47a134","impliedFormat":1},{"version":"3c884d9d9ec454bdf0d5a0b8465bf8297d2caa4d853851d92cc417ac6f30b969","impliedFormat":1},{"version":"5a369483ac4cfbdf0331c248deeb36140e6907db5e1daed241546b4a2055f82c","impliedFormat":1},{"version":"e8f5b5cc36615c17d330eaf8eebbc0d6bdd942c25991f96ef122f246f4ff722f","impliedFormat":1},{"version":"f0bd7e6d931657b59605c44112eaf8b980ba7f957a5051ed21cb93d978cf2f45","impliedFormat":1},{"version":"ee1ee365d88c4c6c0c0a5a5701d66ebc27ccd0bcfcfaa482c6e2e7fe7b98edf7","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ada07543808f3b967624645a8e1ccd446f8b01ade47842acf1328aec899fed0","affectsGlobalScope":true,"impliedFormat":1},{"version":"a4bdde4e601e9554a844e1e0d0ccfa05e183ef9d82ab3ac25f17c1709033d360","impliedFormat":1},{"version":"ad23fd126ff06e72728dd7bfc84326a8ca8cec2b9d2dac0193d42a777df0e7d8","impliedFormat":1},{"version":"c60db41f7bee80fb80c0b12819f5e465c8c8b465578da43e36d04f4a4646f57d","impliedFormat":1},{"version":"93bd413918fa921c8729cef45302b24d8b6c7855d72d5bf82d3972595ae8dcbf","impliedFormat":1},{"version":"4ff41188773cbf465807dd2f7059c7494cbee5115608efc297383832a1150c43","impliedFormat":1},{"version":"dccdf1677e531e33f8ac961a68bc537418c9a414797c1ea7e91307501cdc3f5e","impliedFormat":1},{"version":"1f4fc6905c4c3ae701838f89484f477b8d9b3ef39270e016b5488600d247d9a5","affectsGlobalScope":true,"impliedFormat":1},{"version":"d206b4baf4ddcc15d9d69a9a2f4999a72a2c6adeaa8af20fa7a9960816287555","impliedFormat":1},{"version":"93f437e1398a4f06a984f441f7fa7a9f0535c04399619b5c22e0b87bdee182cb","impliedFormat":1},{"version":"afbe24ab0d74694372baa632ecb28bb375be53f3be53f9b07ecd7fc994907de5","impliedFormat":1},{"version":"70731d10d5311bd4cf710ef7f6539b62660f4b0bfdbb3f9fbe1d25fe6366a7fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"a20f1e119615bf7632729fd89b6c0b5ffdc2df3b512d6304146294528e3ebe19","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e043a1bc8fbf2a255bccf9bf27e0f1caf916c3b0518ea34aa72357c0afd42ec","impliedFormat":1},{"version":"137c2894e8f3e9672d401cc0a305dc7b1db7c69511cf6d3970fb53302f9eae09","impliedFormat":1},{"version":"3bc2f1e2c95c04048212c569ed38e338873f6a8593930cf5a7ef24ffb38fc3b6","impliedFormat":1},{"version":"8145e07aad6da5f23f2fcd8c8e4c5c13fb26ee986a79d03b0829b8fce152d8b2","impliedFormat":1},{"version":"f9d9d753d430ed050dc1bf2667a1bab711ccbb1c1507183d794cc195a5b085cc","impliedFormat":1},{"version":"9eece5e586312581ccd106d4853e861aaaa1a39f8e3ea672b8c3847eedd12f6e","impliedFormat":1},{"version":"235bfb54b4869c26f7e98e3d1f68dbfc85acf4cf5c38a4444a006fbf74a8a43d","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"93452d394fdd1dc551ec62f5042366f011a00d342d36d50793b3529bfc9bd633","impliedFormat":1},{"version":"bb715efb4857eb94539eafb420352105a0cff40746837c5140bf6b035dd220ba","affectsGlobalScope":true,"impliedFormat":1},{"version":"1851a3b4db78664f83901bb9cac9e45e03a37bb5933cc5bf37e10bb7e91ab4eb","impliedFormat":1},{"version":"fdedf82878e4c744bc2a1c1e802ae407d63474da51f14a54babe039018e53d8f","affectsGlobalScope":true,"impliedFormat":1},{"version":"27d8987fd22d92efe6560cf0ce11767bf089903ffe26047727debfd1f3bf438b","affectsGlobalScope":true,"impliedFormat":1},{"version":"578d8bb6dcb2a1c03c4c3f8eb71abc9677e1a5c788b7f24848e3138ce17f3400","impliedFormat":1},{"version":"4f029899f9bae07e225c43aef893590541b2b43267383bf5e32e3a884d219ed5","impliedFormat":1},{"version":"ae56f65caf3be91108707bd8dfbccc2a57a91feb5daabf7165a06a945545ed26","impliedFormat":1},{"version":"a136d5de521da20f31631a0a96bf712370779d1c05b7015d7019a9b2a0446ca9","impliedFormat":1},{"version":"5b566927cad2ed2139655d55d690ffa87df378b956e7fe1c96024c4d9f75c4cf","affectsGlobalScope":true,"impliedFormat":1},{"version":"bce947017cb7a2deebcc4f5ba04cead891ce6ad1602a4438ae45ed9aa1f39104","affectsGlobalScope":true,"impliedFormat":1},{"version":"d3dffd70e6375b872f0b4e152de4ae682d762c61a24881ecc5eb9f04c5caf76f","impliedFormat":1},{"version":"e2c72c065a36bc9ab2a00ac6a6f51e71501619a72c0609defd304d46610487a4","impliedFormat":1},{"version":"d91a7d8b5655c42986f1bdfe2105c4408f472831c8f20cf11a8c3345b6b56c8c","impliedFormat":1},{"version":"616075a6ac578cf5a013ee12964188b4412823796ce0b202c6f1d2e4ca8480d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"e8a979b8af001c9fc2e774e7809d233c8ca955a28756f52ee5dee88ccb0611d2","impliedFormat":1},{"version":"cac793cc47c29e26e4ac3601dcb00b4435ebed26203485790e44f2ad8b6ad847","impliedFormat":1},{"version":"068edc96705c11c7ff5adb82c57ee2212d2379bf52f088542abcdcecfcc7b500","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"8b96046bf5fb0a815cba6b0880d9f97b7f3a93cf187e8dcfe8e2792e97f38f87","impliedFormat":99},{"version":"bacf2c84cf448b2cd02c717ad46c3d7fd530e0c91282888c923ad64810a4d511","affectsGlobalScope":true,"impliedFormat":1},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"8885cf05f3e2abf117590bbb951dcf6359e3e5ac462af1c901cfd24c6a6472e2","impliedFormat":1},{"version":"4d979e3c12ffb6497d2b1dc5613130196d986fff764c4526360c0716a162e7e7","impliedFormat":1},{"version":"e61df3640a38d535fd4bc9f4a53aef17c296b58dc4b6394fd576b808dd2fe5e6","impliedFormat":1},{"version":"80781460eca408fe8d2937d9fdbbb780d6aac35f549621e6200c9bee1da5b8fe","impliedFormat":1},{"version":"4719c209b9c00b579553859407a7e5dcfaa1c472994bd62aa5dd3cc0757eb077","impliedFormat":1},{"version":"7ec359bbc29b69d4063fe7dad0baaf35f1856f914db16b3f4f6e3e1bca4099fa","impliedFormat":1},{"version":"b9261ac3e9944d3d72c5ee4cf888ad35d9743a5563405c6963c4e43ee3708ca4","impliedFormat":1},{"version":"c84fd54e8400def0d1ef1569cafd02e9f39a622df9fa69b57ccc82128856b916","impliedFormat":1},{"version":"a022503e75d6953d0e82c2c564508a5c7f8556fad5d7f971372d2d40479e4034","impliedFormat":1},{"version":"2ed6489ef46eb61442d067c08e87e3db501c0bfb2837eee4041a27bf3e792bb0","impliedFormat":1},{"version":"644491cde678bd462bb922c1d0cfab8f17d626b195ccb7f008612dc31f445d2d","impliedFormat":1},{"version":"d60fe6d59d4e19ecc65359490b8535e359ca4b760d2cdb56897ca75d09d41ba3","impliedFormat":1},{"version":"f45a2a8b1777ecb50ed65e1a04bb899d4b676529b7921bd5d69b08573a00c832","impliedFormat":1},{"version":"774b783046ba3d473948132d28a69f52a295b2f378f2939304118ba571b1355e","impliedFormat":1},{"version":"b5734e05c787a40e4f9efe71f16683c5f7dc3bdb0de7c04440c855bd000f8fa7","impliedFormat":1},{"version":"14ba97f0907144771331e1349fdccb5a13526eba0647e6b447e572376d811b6f","impliedFormat":1},{"version":"2a771d907aebf9391ac1f50e4ad37952943515eeea0dcc7e78aa08f508294668","impliedFormat":1},{"version":"7165050eddaed878c2d2cd3cafcaf171072ac39e586a048c0603712b5555f536","impliedFormat":1},{"version":"26e629be9bbd94ea1d465af83ce5a3306890520695f07be6eb016f8d734d02be","impliedFormat":99},{"version":"82e687ebd99518bc63ea04b0c3810fb6e50aa6942decd0ca6f7a56d9b9a212a6","impliedFormat":99},{"version":"8f07f2b6514744ac96e51d7cb8518c0f4de319471237ea10cf688b8d0e9d0225","impliedFormat":1},{"version":"9ae0ca65717af0d3b554a26fd333ad9c78ad3910ad4b22140ff02acb63076927","impliedFormat":99},{"version":"77f36b412f8aef062ff1901c4b8e61b04239a19bad2d0a20822682cdfdf38b0d","impliedFormat":99},{"version":"151b5c9a372aa02fba7827d13afcaf2d90623d3f483248897245ee3740d4c104","impliedFormat":99},{"version":"1748c03e7a7d118f7f6648c709507971eb0d416f489958492c5ae625de445184","impliedFormat":1},{"version":"3510d1edea79bd03625a1b9f2a4be40c16dc621dafb3049e35c63dc40c68662f","affectsGlobalScope":true,"impliedFormat":99},{"version":"d6838763d96ca56f56a7acdefb550e929f8a0c25d4d1e8b01a1bcc5ecfcad2cd","impliedFormat":1},{"version":"b943e4cfae007bf0e8b5aa9cbb979865505e89586fd1e45bb7aabf0f855ed1d5","impliedFormat":99},"d24b3136b425d3f7f0e243eb4bee21cedb5b67074c75b4f9149b584c17183204","962244d11a7ef6839e115ba23fff40ae303421074988150248e9d7443e58a3d1",{"version":"41e6183216bbf69c09dcbea4311418d0c97abbe42d666d3916a2d8166efa980a","impliedFormat":99},"887a2a79a8c9b1c89c0fdee3dfc9879a5176cb88da719fda48da1b199c150f44","223098e4ac44361fde72acb9eca1a81e9180621f50e6d6b868db260448daac5a","ef8ad16657a9d9d9080249bc81ea349474ba35b7ce938ae97e07cee34c66c4e5","2d47260e9f833c033ee59b25b0925f208376d8bad55cd51228ec851f658c46e4","6494e4d9fd3503ef697897bb45cefcae64fc67d93261878213b938fa04245ba4","9512663a1c6523ac4b9f19e6154a19b2ee9b9888e5573753dc4d5c86960f7634","45cec5e6a6ee4eadc9786d0994f74d2e072915591834735e3e000462577b94cd","712af190ec02c982d687cf3d9c18592529f2c7a5cc4266f998dd64be2fe60d99","00fb0034c4f90de53f0377da64a652050c3780fd817883647f4bb13863e805aa","a9b1a90e9b8cb93e100ba09482823d755070789e6bc9260d279d0a4f3cc95b4e","040644c70957912a2fc06056b32bdb4ae4938695cdc4a7437aec4301a6c765b6","b16cd06d96f7890de844875fbcf1e8d3eb0e793584229b0bbc919bad9518135d","32c88434ea0972b887417f7f47b9198ec09aca3d7b198d6cabcf4e558831c21d","fc256ccac6f456a2eb35823aec1f920c49c720d0f40a8858e7a555de40b4a3bd","66933cfa17f7d2f349190a84152652a81fbdfe4024dc1a590b130f963af2553d","f149efc4740b6ab01f5dcf8d48d4061987c058aa34afb3ef5e554647fc4087ee","be8469882adbf97af3343a1bab6cc87dc593662bedcd307c6a10a58610f161df","2aeb47e8d2b9c84a00d562ef1ce8db6ae8a15d8e14437ddb925d80850133ab0a","5b423d456cd0f7aba7b92c1483e129411ea42e0d2c44de6036a8f77f7047b2f1","90ace39a01079d9af4bf35f093ae2b6879d967a6038a76b98760168557d97fca","3ecac7db4fe31b7a46215b64cb05a87f0d0b0bf7af4a70ba84494fd67c7c8ab4","2b3c1f16fd83b9b2b516043f2f7f29a46882e44aa03ae09305853367de86a06d","fd328ee87767fd1ecc538337f66ce888fa75b2710f5a22d74f66a7af76b13dbd","8d14941701ce55f65f25d1e518a8bafa32cc24ae915e4372566eace8628f9c7e","3140bff3a3dd7a0bd190eedbfa85d86ea975f5a7d7432e7161866da81b217e3c","f07882dd78acd12abd6953f578e03416a3579532935d3f5b7945aefe51170114","f1b72a1b0235f18a132905b7432d6fdc4f0d101d4a5aa419108c6ad2082cdf43","41a1b986fb863f593c1f1e1713ceffde4eb836003d15b7dbcb00d91605a010fa","4155931b602027cc3e3d2f3050a268798c23149ad3b527d3238ee1f499c5abda","8da42651d480ad87b4b8d26e5cb81c6350f54e0f2eb4ea0498fd9e8962bf9571","a3e45348872087c42bf52e383aca0af279b1a16f2c675099cd9920f1dd0c93c4","28c68ce921114ec7209792f8d7b16ea187850fbb8568115b2ccce44fdb662a1b","809cec0fc44ba92e2529cf784c69b1bd5f47a2d6acdf6e611e935969773a36cd","5647945aa06546e6b6d296b39330936c5788d6178a9fc35b2f462c6b8ae287d0","8cce15b577b9de996c35e172097902b832cfaec2a20bf069522a706f002193d1","920af138572d6da4fd8558bfdeddd5ab7560c4bd88fa4a488f30d4c559e4465d","d423de9c82cc80747eae270595e24ea421e123a15d00988d11b3c2c49ecc3f45","bf7b7de80399123a033514b6a3e9086682c75e03fcb52af4823c3921d3c5bc17","07dcd998bf1a3ac451de426f04592afc75f2e28c3d8c30fe5a9858fdd07bc784","072ed07ca567d90604b4a0844502002dbc1cb1c72ae47a92d71f45a5b7f6a57a","cfe34044051a728d3b983ab7f97232df22c59b0a46e172356087f7c874b8f796","2807911e1eb8dd9b2b6dc65602934259a32c4fa640701764fe4d0f5d9bc34a2b","d2e1a109af568790752a9515265687748f59bedc7686b131d24a849a59544e15","099c03ae0e3b5043949125c80fd27abf5f548337f49e9469aac9b8b449191a9b","2a051ae6baa0b3ade2c4c0a28f44882b53be1583cdb32a483fc62d0fc96670cf","1482f7b28def114c6cb8a3b2756f7f21d3c8c276b629e780264cd323d27b27bc","39b21cd6d6129dee12c4dd2a707ed267f9f5ccdb7df7c8e7068ee7380eb457e8","69fdb8bb0e5717364ed1cfc87a4973d04972add9ba8399c1f9e65b0722f96722","4b4e7244781313bc249c5aba003585601c9a060f0edde77216e7673b8b7feaa9","b5a2416d2f6fe1c4ff0e1e790ea6778e2e05797f34c85baf9746ade3bffa675e","af0c79c38132939f41f06ac36f9a78598311fdde873bf0f6d0863ce37e83d64b","9760473b496b4067e35f6efcf6d657159a607ad93631728aaaf668947f2f77a3","767a071af186b88c693ddecbb36af75b25cf0fe88c2ec49b1481249c92e0b779","24e3bc5ca5b28ed6c8835582d26cd11fe40f7d0ea6b1ed8584a8e14c2bf51094","de32dcadd0a62a75ebc2a64f85e18053c1bb17eb95cf9925df04ec0a93fad686","41ba1a6fbdb85960c78cd1d6a835c3e5ff2aa966b2cd26e00bcf1be815abc2b6","4ed9f4d3812264bd9ad904127db95a7b94df0e52492486c7a1844278dbcdd7d1","b655528258f09d438fdfc64a311ff0aff6aed519ab911d269776cfc368463766","57e38af3df48f9b91ddb32bcd1763741821bd578ed4f55f171a020d93f2483cf","0bd10d7d7419bdb855dfb722d29a0c85a05afc7619ee3249901890053357c1a3","dedddd5653d9818b2157f8d725e4e33dc10c5c3e5eed04c3daaf6a33214bd44b","349a3264e9859d62d09a8dc1e887830ed447d44881bf18f9435388b4a9ad6b2f","dc28d6fa8a7dfbcb42d26c3cd555473dcd2d7d32f8485c6e97aa1fd683894d3d","a837161f15876b9cf196bc355d74dfa8edccf128a56c0b35c9b46fc6b7710bfe","e7735f0a4d30c6a8d7261fdc88f009eb7c18e9b3414e009592194eedc66f49be","60b54df5e2e4944d525e045359f08bb838d362ab7c4caf6e2161ad6b52efc169","59a56f5fdabec44f064aba3ac88d111dd66e148f1519f0c24cbd17525f335e10","161fcfb4667ae42647ccc21ce5ac7530fad5b6cc7d3dd9ee95eb0c742cfbcae6","9042e69cdeb7f199b0c5c09fe741eef26ebdebaf531242e001777e1657f46792","17d686a44614b6497400190008a7a07752149f002d790b821f69b777f2d13cf0","0ee88d5e92e8bf316678b9f40ad899773cdb4e6ce6aac2c8988a1b67bf56efb9","db3cdc0306f22ae3697bc7916efe7e574434ba6c0e84aa7a593bb18f80ea922f","580d137bdcce04662f114c0c7eab93931b486e8feb687762f60f9295ddbc9012","d78896ad93c2f6d96597a934ff416a8048ba3f596ac90899719152fe058e4729","26ed087121813761ce03f2bab7d59a47125b62c797b7576aed1479af91c5f97e","dbad290e0b8f0cb2cd22f1680dc76b0271d549c72a76ca99f3d54224f952e49d","375c2de7574d4f3508d7987c7fa2bfe67fa40219643aa5345e77c0c799560a8c","f32e07a2c39beb6454c757d705cbd290b927235139670e9021f6386d9cce29f8","4127f25b9c0b2721a8243bc2ba2872a12cb0517802317385946bb3741034f732","03aea7b033dd4b3ee7a9d8e6c21ad9c5266936e8fe5b1673de674c5e3f2412b8","11c51c8958b292ec93583f38fce1a65baa37c7885c2638ef99415bb7aeebe50c","cda2e3a00bb002accce114390b0bd307ce78208fb8f5acbb9b23c83c67a7e026","d62e23541bdfcd2953d6193bcdda728ffef6a940400dc95864b4ddd2aa045250","4ab7f35adcccfbed7557eec01cef85f9feb3164e8bb9154e725e2329bc1710a3","a6966c234b2156250ed910fc21a8388cf2b37bfe03a8229e3373f363d59d9ddc","6150340c5e4bbfa70276ae1fdbd5c10abbe36343371cca24cc33a62cb3f42288","dbca6e74170098c59e2fc511f73be188ac0d33ee1c4ff735ca736dd614eb87ce","54d7b7f0cf9a899b9b2ae7ac56ec76eaaa4c96c1d407c46b7996bdc97087bef7","03c4fc286702e63ea0aba85d551f00521ef1e0db725c3e67727621fd6a2f78e6","b05ec355d72dc4db4942067280019607500a35255fdd889bd91c747b779a4fc2","25f009483a4e0c95fee8cafe9c80096b3bcca03e7b8c3b5f4ad44f0ad1b8634b","6465d7bfa0962fc155fad47d47b0b7470f0f2d156acde8c8e3ae04b4b7757b39","b5c9b4cf9c9abbdb75e2ee40c7350990554968343a6bf2a42fbe89137030c8d3","dafb1004bba93075b108914f768b53acce5ff894d918e6c2ec8c6a11471210ef","d414f87d86ff388670092ed63057db48e7fbdbf3f2b388182c8bcf9991cdc273","87525e9e72664d4c2f429e0e3f3233804c5a745ad431885f71dbb40a79205447","89cc546577cb2f00d83395f3d6f3befdc959cf43c1c83e7aa666432c11d3c732","b1c073115449bcc484587c249c5806a3910e1297f0380d916ae0b58fa66c50eb","fb1e562fc1af8ab7a74867941eaee1b24fa562ee637592ec87cbb090e9f60e4c","dc723727f1566fb69f9953df4822a60422e6717a8f720764d0a991f1ff9c4a13","d3eb4b0f33ac4c202160f53e0666c84afddb9dccedd8e1ce64f229a5a848f586","ae0976ada9121c6edc980a30040b640543fb32119f39b120f16314ac0bc00255","7b9f5799489925795b447e5786c4894dec5899b7a03701696e8fa3742b5130db","7d8192633dc6fae8c67d1dc378f6d710d747719686434eec70119da6ceb0323a","55ce25433f60336dc03f96c6f1c7cff263495f27f6cc1282a91e0433ae912363","fd578a74b6d79fecfd0aac6f457c76ef88e0e236345f22c4b7824bc638b641dd","5c0cf3919a28a312138c81022b2238baed543864c680247e6852b3f742f07ebe","683d3acf8fb3339671c1392a9ac95a17bd56f456d5cdcca26a44c61445e98618",{"version":"e778bd85068f2fc1e0965fb850c8a2635b2135f9bdf7ae33a8672e36b2ea508b","impliedFormat":99},{"version":"74f1b2ebe529d481e69fd0db8f10478e6f5334ff40e8a8c3ff1d856aba4526cd","impliedFormat":99},{"version":"592c7589ae79980ce97a996856df78881aa327e5f51ad2870314517c85fcd7f7","impliedFormat":99},{"version":"cb1c4a0fc0252c040c3bde11dfcef698a2bc0fe9e5cb3c52711e70d31e858ea3","impliedFormat":99},{"version":"8005827811d1d7781528630713ea92d428a05b23572058e522b26b0ccc3859ec","impliedFormat":99},{"version":"075195fe73300e2419f73a4b5965a27789a14799cbe77559818b1ad6b776a911","impliedFormat":99},{"version":"ee3b1737aa80a9c57608b0be63a364f0edd9b43107698c3c5c635f0a284e1ce5","impliedFormat":99},{"version":"5b72701b29bdc15afdba088e392396cf5bc3bc599a7b954a0ee342383b8c107b","impliedFormat":99},{"version":"4423c748dda269387c06591981a13f5df738bd05e10f91799514ecd4ed34b9f8","impliedFormat":99},{"version":"03ffd8faf43123867541e561eaba7c2e163444b345eb077194fa193ad7e371eb","impliedFormat":99},{"version":"354181f9e82731c58e2971d17a5ccd29e73d53ed45f1fb1ded64f6b9233d2ebc","impliedFormat":99},{"version":"873e17a93f2b15b4f01938496e6b97ab45f6252b94db9bd897c88409580186e7","impliedFormat":99},{"version":"207a41bab3547f0e2a2b7e48a4391cba43b7fccd433bf378925bd585ecedc88b","impliedFormat":99},"683d3acf8fb3339671c1392a9ac95a17bd56f456d5cdcca26a44c61445e98618",{"version":"f0c3a51c7314523b169d4756b2df6e3e59a3f0d9bc4848248362edaf75b5d315","impliedFormat":1},{"version":"037518e189f4c9a63288c31328da15f6ceeb3497ef5910c29b6ae793cf57b2d0","signature":"c6673c95b656592b6668e5fac0bb614d7062158aee5fcf189fe4ce9848b204dd"},"8aefb69e1d63a7777ab9ddfa584e6d215186930cddae795bc6eda014abcc92a7","440f9d8c2b934ee725b39fedcf783c6d6c6115be64931fbb94bfc4ab957fbd8f","84191a3b86dc470e4bf53ddab3452bc71d8a8ec4f4efa5fa6135ac2ef9931d47","8377c7e5ce8e9de73f24ab8406cb863ff5e6d6a33a80711ff460b42d139f6405","51dc92731f7f457f6d54ffe996feda8bc4da95390334f442fef4a36084ffc54b","eb739e1666918073210a8e4018a611ecd975f54c435ad497a8c2b15eae5bc216","c01af99a6fd0a8108435021b85d70774cc58e5c0ec945166d4220c30932a44aa","7bd24729c5bc07bab3df7e8a585715f3d0341b41a3b0e229843d215ac3d14a2c","825f4bfd84ba1ea7d2a2cd05dca2c2b8d805b4be3cb28c1450b16fccb652b3e1","1661250f21f58030a47e3437b9c75705e4ab7b6072f03c4d4a57b3096fe1dd3e","5b423d456cd0f7aba7b92c1483e129411ea42e0d2c44de6036a8f77f7047b2f1",{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"3e8b97f70a096dd3ce1757d460810e58e4a7de0d3d0ddfe430d02dc27295b3f4","impliedFormat":1},{"version":"309ebd217636d68cf8784cbc3272c16fb94fb8e969e18b6fe88c35200340aef1","impliedFormat":1},{"version":"f987c74a4b4baf361afbf22a16d230ee490d662f9aa2066853bb7ebbb8611355","impliedFormat":1},{"version":"1ff91526fcdd634148c655ef86e912a273ce6a0239e2505701561f086678262b","impliedFormat":1},{"version":"bd93f6fc4da70275db4def32903eed2be03547a41857142df63ddfebb9a67bdf","impliedFormat":1},{"version":"8d67b13da77316a8a2fabc21d340866ddf8a4b99e76a6c951cc45189142df652","impliedFormat":1},{"version":"7952419455ca298776db0005b9b5b75571d484d526a29bfbdf041652213bce6f","impliedFormat":1},{"version":"21360500b20e0ec570f26f1cbb388c155ede043698970f316969840da4f16465","impliedFormat":1},{"version":"3a819c2928ee06bbcc84e2797fd3558ae2ebb7e0ed8d87f71732fb2e2acc87b4","impliedFormat":1},{"version":"1765e61249cb44bf5064d42bfa06956455bbc74dc05f074d5727e8962592c920","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"26384fb401f582cae1234213c3dc75fdc80e3d728a0a1c55b405be8a0c6dddbe","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"e0bfe601a9fdf6defe94ed62dc60ac71597566001a1f86e705c95e431a9c816d","impliedFormat":1},{"version":"7b9e6b3c726d47935bdc9ebc78fe5398e28e751ba7d70e9e011f01fbd5b618be","impliedFormat":1},{"version":"6e5857f38aa297a859cab4ec891408659218a5a2610cd317b6dcbef9979459cc","impliedFormat":1},{"version":"add0ce7b77ba5b308492fa68f77f24d1ed1d9148534bdf05ac17c30763fc1a79","impliedFormat":1},{"version":"56ccc6238510b913f5e6c21afdc447632873f76748d0b30a87cb313b42f1c196","impliedFormat":1},{"version":"c1a2e05eb6d7ca8d7e4a7f4c93ccf0c2857e842a64c98eaee4d85841ee9855e6","impliedFormat":1},{"version":"85021a58f728318a9c83977a8a3a09196dcfc61345e0b8bbbb39422c1594f36b","impliedFormat":1},{"version":"d91805544905a40fbd639ba1b85f65dc13d6996a07034848d634aa9edb63479e","impliedFormat":1},{"version":"6042774c61ece4ba77b3bf375f15942eb054675b7957882a00c22c0e4fe5865c","impliedFormat":1},{"version":"5a3bd57ed7a9d9afef74c75f77fce79ba3c786401af9810cdf45907c4e93f30e","impliedFormat":1},{"version":"8610f5dc475d74c4b095aafa0c191548bfd43f65802e6da54b5e526202b8cfe0","impliedFormat":1},{"version":"7b9496d2e1664155c3c293e1fbbe2aba288614163c88cb81ed6061905924b8f9","impliedFormat":1},{"version":"e27451b24234dfed45f6cf22112a04955183a99c42a2691fb4936d63cfe42761","impliedFormat":1},{"version":"58d65a2803c3b6629b0e18c8bf1bc883a686fcf0333230dd0151ab6e85b74307","impliedFormat":1},{"version":"e818471014c77c103330aee11f00a7a00b37b35500b53ea6f337aefacd6174c9","impliedFormat":1},{"version":"2fbc91ba70096f93f57e22d1f0af22b707dbb3f9f5692cc4f1200861d3b75d88","impliedFormat":1},{"version":"d8bc0c5487582c6d887c32c92d8b4ffb23310146fcb1d82adf4b15c77f57c4ac","impliedFormat":1},{"version":"8cb31102790372bebfd78dd56d6752913b0f3e2cefbeb08375acd9f5ba737155","impliedFormat":1},{"version":"d3cfde44f8089768ebb08098c96d01ca260b88bccf238d55eee93f1c620ff5a5","impliedFormat":1},{"version":"293eadad9dead44c6fd1db6de552663c33f215c55a1bfa2802a1bceed88ff0ec","impliedFormat":1},{"version":"206e73f49f16633113787cc651dc03dc900379395dfa02ab1ef4c9cbbcd5adc2","impliedFormat":1},{"version":"fec412ded391a7239ef58f455278154b62939370309c1fed322293d98c8796a6","impliedFormat":1},{"version":"e3498cf5e428e6c6b9e97bd88736f26d6cf147dedbfa5a8ad3ed8e05e059af8a","impliedFormat":1},{"version":"dba3f34531fd9b1b6e072928b6f885aa4d28dd6789cbd0e93563d43f4b62da53","impliedFormat":1},{"version":"f672c876c1a04a223cf2023b3d91e8a52bb1544c576b81bf64a8fec82be9969c","impliedFormat":1},{"version":"e4b03ddcf8563b1c0aee782a185286ed85a255ce8a30df8453aade2188bbc904","impliedFormat":1},{"version":"2329d90062487e1eaca87b5e06abcbbeeecf80a82f65f949fd332cfcf824b87b","impliedFormat":1},{"version":"25b3f581e12ede11e5739f57a86e8668fbc0124f6649506def306cad2c59d262","impliedFormat":1},{"version":"93c3e73824ad57f98fd23b39335dbdae2db0bd98199b0dc0b9ccc60bf3c5134a","impliedFormat":1},{"version":"a9ebb67d6bbead6044b43714b50dcb77b8f7541ffe803046fdec1714c1eba206","impliedFormat":1},{"version":"833e92c058d033cde3f29a6c7603f517001d1ddd8020bc94d2067a3bc69b2a8e","impliedFormat":1},{"version":"76af14c3cce62da183aaf30375e3a4613109d16c7f16d30702f16d625a95e62c","impliedFormat":99},{"version":"56e0775830b68d13c3d7f4ec75df7d016db6b879ef9676affb5233a9a289c192","impliedFormat":99},{"version":"152e89924be2192959c4e275cd3237e0da5cce04d1a41b914cbcff0081583038","impliedFormat":1},{"version":"f878ab5495f737dc9b211dfea74564ef0831410d5d38cc329afe53faae5d16c7","impliedFormat":1},{"version":"a4e9e0d92dcad2cb387a5f1bdffe621569052f2d80186e11973aa7080260d296","impliedFormat":1},{"version":"f6380cc36fc3efc70084d288d0a05d0a2e09da012ee3853f9d62431e7216f129","impliedFormat":1},{"version":"497c3e541b4acf6c5d5ba75b03569cfe5fe25c8a87e6c87f1af98da6a3e7b918","impliedFormat":1},{"version":"d9429b81edf2fb2abf1e81e9c2e92615f596ed3166673d9b69b84c369b15fdc0","impliedFormat":1},{"version":"7e22943ae4e474854ca0695ab750a8026f55bb94278331fda02a4fb42efce063","impliedFormat":1},{"version":"7da9ff3d9a7e62ddca6393a23e67296ab88f2fcb94ee5f7fb977fa8e478852ac","impliedFormat":1},{"version":"e1b45cc21ea200308cbc8abae2fb0cfd014cb5b0e1d1643bcc50afa5959b6d83","impliedFormat":1},{"version":"c9740b0ce7533ce6ba21a7d424e38d2736acdddeab2b1a814c00396e62cc2f10","impliedFormat":1},{"version":"b3c1f6a3fdbb04c6b244de6d5772ffdd9e962a2faea1440e410049c13e874b87","impliedFormat":1},{"version":"dcaa872d9b52b9409979170734bdfd38f846c32114d05b70640fd05140b171bb","impliedFormat":1},{"version":"6c434d20da381fcd2e8b924a3ec9b8653cf8bed8e0da648e91f4c984bd2a5a91","impliedFormat":1},{"version":"992419d044caf6b14946fa7b9463819ab2eeb7af7c04919cc2087ce354c92266","impliedFormat":1},{"version":"fa9815e9ce1330289a5c0192e2e91eb6178c0caa83c19fe0c6a9f67013fe795c","impliedFormat":1},{"version":"06384a1a73fcf4524952ecd0d6b63171c5d41dd23573907a91ef0a687ddb4a8c","impliedFormat":1},{"version":"34b1594ecf1c84bcc7a04d9f583afa6345a6fea27a52cf2685f802629219de45","impliedFormat":1},{"version":"d82c9ca830d7b94b7530a2c5819064d8255b93dfeddc5b2ebb8a09316f002c89","impliedFormat":1},{"version":"7e046b9634add57e512412a7881efbc14d44d1c65eadd35432412aa564537975","impliedFormat":1},{"version":"aac9079b9e2b5180036f27ab37cb3cf4fd19955be48ccc82eab3f092ee3d4026","impliedFormat":1},{"version":"3d9c38933bc69e0a885da20f019de441a3b5433ce041ba5b9d3a541db4b568cb","impliedFormat":1},{"version":"606aa2b74372221b0f79ca8ae3568629f444cc454aa59b032e4cb602308dec94","impliedFormat":1},{"version":"50474eaea72bfda85cc37ae6cd29f0556965c0849495d96c8c04c940ef3d2f44","impliedFormat":1},{"version":"b4874382f863cf7dc82b3d15aed1e1372ac3fede462065d5bfc8510c0d8f7b19","impliedFormat":1},{"version":"df10b4f781871afb72b2d648d497671190b16b679bf7533b744cc10b3c6bf7ea","impliedFormat":1},{"version":"1fdc28754c77e852c92087c789a1461aa6eed19c335dc92ce6b16a188e7ba305","impliedFormat":1},{"version":"a656dab1d502d4ddc845b66d8735c484bfebbf0b1eda5fb29729222675759884","impliedFormat":1},{"version":"465a79505258d251068dc0047a67a3605dd26e6b15e9ad2cec297442cbb58820","impliedFormat":1},{"version":"ddae22d9329db28ce3d80a2a53f99eaed66959c1c9cd719c9b744e5470579d2f","impliedFormat":1},{"version":"d0e25feadef054c6fc6a7f55ccc3b27b7216142106b9ff50f5e7b19d85c62ca7","impliedFormat":1},{"version":"111214009193320cacbae104e8281f6cb37788b52a6a84d259f9822c8c71f6ca","impliedFormat":1},{"version":"01c8e2c8984c96b9b48be20ee396bd3689a3a3e6add8d50fe8229a7d4e62ff45","impliedFormat":1},{"version":"a4a0800b592e533897b4967b00fb00f7cd48af9714d300767cc231271aa100af","impliedFormat":1},{"version":"20aa818c3e16e40586f2fa26327ea17242c8873fe3412a69ec68846017219314","impliedFormat":1},{"version":"f498532f53d54f831851990cb4bcd96063d73e302906fa07e2df24aa5935c7d1","impliedFormat":1},{"version":"5fd19dfde8de7a0b91df6a9bbdc44b648fd1f245cae9e8b8cf210d83ee06f106","impliedFormat":1},{"version":"3b8d6638c32e63ea0679eb26d1eb78534f4cc02c27b80f1c0a19f348774f5571","impliedFormat":1},{"version":"ce0da52e69bc3d82a7b5bc40da6baad08d3790de13ad35e89148a88055b46809","impliedFormat":1},{"version":"9e01233da81bfed887f8d9a70d1a26bf11b8ddff165806cc586c84980bf8fc24","impliedFormat":1},{"version":"214a6afbab8b285fc97eb3cece36cae65ea2fca3cbd0c017a96159b14050d202","impliedFormat":1},{"version":"14beeca2944b75b229c0549e0996dc4b7863e07257e0d359d63a7be49a6b86a4","impliedFormat":1},{"version":"f7bb9adb1daa749208b47d1313a46837e4d27687f85a3af7777fc1c9b3dc06b1","impliedFormat":1},{"version":"c549fe2f52101ffe47f58107c702af7cdcd42da8c80afd79f707d1c5d77d4b6e","impliedFormat":1},{"version":"3966ea9e1c1a5f6e636606785999734988e135541b79adc6b5d00abdc0f4bf05","impliedFormat":1},{"version":"0b60b69c957adb27f990fbc27ea4ac1064249400262d7c4c1b0a1687506b3406","impliedFormat":1},{"version":"12c26e5d1befc0ded725cee4c2316f276013e6f2eb545966562ae9a0c1931357","impliedFormat":1},{"version":"27b247363f1376c12310f73ebac6debcde009c0b95b65a8207e4fa90e132b30a","impliedFormat":1},{"version":"05bd302e2249da923048c09dc684d1d74cb205551a87f22fb8badc09ec532a08","impliedFormat":1},{"version":"fe930ec064571ab3b698b13bddf60a29abf9d2f36d51ab1ca0083b087b061f3a","impliedFormat":1},{"version":"6b85c4198e4b62b0056d55135ad95909adf1b95c9a86cdbed2c0f4cc1a902d53","impliedFormat":1},{"version":"30838de87a6c48e48b40967bbfbb204b9cec4df65fc9bba63b674a259d79a851","affectsGlobalScope":true,"impliedFormat":1},{"version":"29f823cbe0166e10e7176a94afe609a24b9e5af3858628c541ff8ce1727023cd","impliedFormat":1},{"version":"a3e282ed97e1a63a7d4cafb00c1559610d97e8b7bb098a4e0b572ddcf930636b","impliedFormat":99},{"version":"672bc25c539ab8ad9d66b165dac2d0451ab2c011f50fe88d1ba7155eee81efcd","impliedFormat":99},{"version":"a742da5aab9873a6dd36ab62891eb3833026fdcdec3e0cab1e24dffed2f825d3","impliedFormat":99},{"version":"1947f4abe483c0aea8e9db2e20db684cab0846855e1b7cd10847692da05ff721","impliedFormat":99},{"version":"39d006df3b67f27394764f90e8642d568716864a0e07da425e6d7bc21b7d0477","impliedFormat":99},{"version":"9d67c37aa078617cf4b48e7a0ba33cfc6c1243656e5333b70cb1be311313ea06","impliedFormat":99},{"version":"9413bd1e9958eb902d5acc92b78a3d83ada4325f0d3ffed300f354a340e32302","impliedFormat":99},{"version":"a8025ecfa6f49ce2c9607b12b543303d9a0d9d2621f906eb00385ae2748283b1","impliedFormat":99},{"version":"2ea9495d638c97cbe2f4117cb214561ebe17b114025bc5f6a8fc11164bf49916","impliedFormat":99},{"version":"f9e1eaba7cffce9603bd7e45e36bc24b3f5b718e19ec14ab62c4f986ccf0d1d2","impliedFormat":99},{"version":"5e0fa24c323ba50195ac31fbac35df4c03b87e02e855bcc0ad21cddaa5ed3055","impliedFormat":99},{"version":"ab68b02cba65b71ba2949ecbb7ac9fee53c21e6d782c3f90aa64d82170608915","impliedFormat":99},{"version":"8d55e8af8863c87a5e5c71847c8240f0f95607ae56d84571f330c375ee12acf2","impliedFormat":99},{"version":"48809bd1ac26bc3fc2ac77442d5435e6b264a87b68c3e4b8b3cab6e90c86eb6c","impliedFormat":99},{"version":"3a40fdd35c813242c237de2a1e71b8229470b067435151223445ae821c97e171","impliedFormat":99},{"version":"00e0cfb112d6dd84d41e84451245ed593d0fb6d565ccbdda1bb1ddfb0f4d7d10","impliedFormat":99},{"version":"ef6e35655cfa9eb8ebb308c009c0ee5b1067e1aa347a21d5fe4c63ba0476e838","impliedFormat":99},{"version":"6806908726f0021341085810505fd0d425851cbfaa3c25c1aa6b164874347ae1","impliedFormat":99},"046aab0f3b41bdea8dbd9a3994bffaf7d7c5814f80d7ddeacc69c5195441fe1a","06a4dc36debbafd7debceee651340641116c1d17dad73d1575fca5a789ca8544","aa7b0160a275d1f3824265c16f92999f15a7ec283496fb75f18893b394606997","173262cb97a3e652e3adbbd7a898620a16e89988113d4ade2ac1cad7b565525f","a890ff34f3e6c2155a676016b010721bbacd90a075a192da2b53f779a9379edf","34823601b0a5621dc216eb3b423e230788fa74a745a2985b166ffbf25ad6751c","029cf9623856dcfaa4752b57b10014da93dd8312637c31bd47a0861e684f47af","a40a3bed0551ca0e06699246e1f58b0f705bef9d9949ec18311c311ba9d7ebb5","6d6bfbc8f9860a044770761a91079a2ffb861f7fdcd2eaf96d375faf8b8f55ab","43ae8b7d05ae70b72cbad5c39825d122b2e985cca331fbf892d6137df6384d4d","197f9b5ce0914e63eeb10e80f7cb50583c8488bf6a60946ee08072c6e3832daa","e0e22d5b38e88b2f2bfd36219290affc867a0025b65019177aab8913788a9ddc","6057601ea80afc8ed3ea25a9c33a7b8e3aab70e9f13fe51e6a9b7dc220a597ce","6b66d681023002d569a6f9c48c3cefaa964388417909f0b3abaa425945776eb2","04a44738b967b2fc8f1f8ac7905acf10027d53b9d160334f84ef68ba0ca4bb38","e3d1bdbff7d757deb5d0328af2ce669fe9149169bd3271943c1a1953c59d2f8f","bfd49173038b7b07ec12e4fc8da08820f81ff06a41e86094bfc6e9c015ebdccd","edcf104f55207c244106db41bea4ee7f7fe177d027d4946fd4cfe185db84eb1e","6b5d1861958efe2b8e99bcacfe3315de1b52ae04153e0d9e27083b8105e4304d","475dbd2f8e6dc7d8fb22e9ee1c3b0d57a337c818e4584b475b579bb1b0206270","97fffad8f6d9ab84d337a837c42f94802dc8759cb4eb26cb890fbce3e52eb092","3a6a8d5ff174dc451a32971626f5354f6bbd00c385ac7c35f37fd086a2ed4abc","a930222c5dd9fb960a8d6d50754b45bb5e5ecb067a772887426c321751aadd8b","0604d08b58d586b70a19b56231fb05fe8e7091dbead0f382e1d424d5f837a0c7",{"version":"c39b23a3a89565e626cc0276a8c4a02be7135e91a44037ebba93219f3f36f041","impliedFormat":99},"9cb9751e4db9a32e7e8ed31149540b9ca8460a9d0cf4ba9fd65f39de585fd1f8","fbea5ce06faab8e965469eeabc0c3f18eab42fe4d9f56790911aff1a4b5a712d","ced410f1668a1cb9dfe838804234bb8fd9bf6e9549e5e140b5b0d03646bdf11c","55a2a2d5d6d1e23e5e4f5c05604de4b38bebd9f06e13eccb1502513adf1bf55d","d6b61fb7b1720c9e6ab557b656f9990574d05985d699d76ba3962f0c1cbf7c66","2fa1f51ae28fadf82f63b29c1a6e9f04ab67c4adac9c5785587c17db67719bbd","cb6f1df771f09e22d701746e1ef7a6db8db6fd59d3307c2e17b5c92324c0cedf","1896c1e1a8b1cf54e79ee2782873360c38d43303202d9df6343db9ee22e7ff81","23cd7b7937220ab404a9b4e04d3875e2801c6bb101d9817238ae6ed4c5a41cde","eed19bb90fa42fd8e606dd622d89f862d798930c84faec50c50357bd754a83f1","5dfe5a6ab7aff79fa80213f5360e8b1c5bb9af1c07fd83758c0d07616675367b","29a013c198a4221444ca1a855e6260cc0c17c0d43331bf26d12d2b2b1c7830c9","8ae94b3d6229e0ea71c482af724c0498b37702f56039d080faefdb0b86905a95","5b0d2ea89c5f98b9b83220ef51a664e24e3c99b1aa9f2aad0b3ad7eb76df7517","277ba2afe960dff185e3e15d95d138a5c2516cb8922fcdbdc3f06db7d926b7b9","4e856632d3a07e6d39f63fb85f14092e307f2b698790cc8ded8ad819e189f1c9","3b0f7c1e5afe7e051abf6f92062f5b108ac20be9643b238fde7820058261ea9e","2c81d0b3cc0f2345dac4a36292164e52b374ffdab33a4325a4806e6d7e98afe1","7246b59e95403e1074e81466e8cad5098d5ac0bfe345597869e50faff6afda97","83173234cc7a3c96b551fbbea5a6d40ab9789976d1b841b22cfb40be7cd945ee","0f7a585ca36d8d791431d5518f8950850080b64dca0754a31cc00b0c2d7a4c0f","c943376855669dad57f6e6e1a4387489eb2e9f71b4a27f8ae04c125fb1c60f03","88e0f321bb1f2b726b4e54593c6343c6aaf7edcbeb7a17add895316117f247b8","30548c34ab37a86e41f5e037e7fd52b44ac0ce054902fc5552e0a44b3486f542","d36fd719abbbff83f132738d78c382a1e6ea10b118f5211c17caffbbd1457a29",{"version":"619854ebfeecc39620c4fd29c5faad00d89c5193b97f55f50426f73aff1372ba","impliedFormat":99},{"version":"5e35a2a3f0b62ee763fd1d1f13cdec015ea10fb1ed7a670989b1ba49b37ad287","impliedFormat":1},{"version":"b3b5aca751100320745c8bfd826202aed7d753d336448ce2265b9470dfa8a298","impliedFormat":1},{"version":"5fa35c6051059d5ed57cbda5479b593cec15d5405229542042bd583c1e680fb4","impliedFormat":1},{"version":"7df3932c1b8816845e1774538c4e921e196d396b3419e2e18bc973079b4064a3","impliedFormat":1},{"version":"c8a7131a27d7892f009ab03d78dc113582f819c429af2064280bec83c2e7c599","impliedFormat":1},{"version":"19629032a378771a07e93c0ab8253b92cb83e786446f1c0aed01d8f9b96a3fb6","impliedFormat":1},{"version":"fd4b51f120103d53cc03eea9d98d6a1c7e6c07f04847c0658ec925ceeb7667aa","impliedFormat":1},{"version":"53bacb19d6714c3ea41bebf01a34d35468a0ac0c9331d2ffdc411ce452444a2f","impliedFormat":1},{"version":"e2ce339ecc8f65810eda93bb801eb9278f616b653f5974135908df2c30acc5ae","impliedFormat":1},{"version":"234058398306e26bc917e6efba8fb26c9d9f2cfdfbaa17abfcb11138847de081","impliedFormat":1},{"version":"b3ff9aff54c18834bce9690184e69fd44fd5d57273a98a47fbf518b68cc4ec60","impliedFormat":1},{"version":"67577c6ac7cc408265b36f54548167d12f419cab47c3567db64d0a990c089e88","impliedFormat":1},{"version":"3dc40ead9c5ac3f164af434069561d6c660e64f77c71ab6ad405c5edc0724a94","impliedFormat":1},{"version":"d5fb34e3200ce13445c603012c0dfbd116317f8d5fef294e11f49d00a859a3d0","impliedFormat":1},{"version":"58fc843cdfd37a8b1ae2cbf3d6d3718d41cdafcbbf17e228bd6a7762a7235bf0","impliedFormat":1},{"version":"a4d0945318f81b27529abcae16d65612decf4164021a0d4d2ec19fbfcbaf1555","impliedFormat":1},{"version":"fbe57f37a07a627af9ae5922c86132677e58689427cc748866a549ef3862f859","impliedFormat":1},{"version":"d41eef6c22f2a92e2e99a1037bc2056e90199278f01e0eea561d0a7c5178d816","impliedFormat":1},{"version":"47cb9e8283c2f71c56a81130a395727769a83bc6d33983dae98292bddf3e48a0","impliedFormat":1},{"version":"83dc862cd9b7b1a929bcc03e9bbc8690cebc7e29b1edfa263f6fd11b737f19df","impliedFormat":1},{"version":"fffacebbcc213081096e101e64402c9fb772c5b4b36ad5e3d675e8d487c9e8af","impliedFormat":1},{"version":"796eb04b5d7cb757528f82c9dcf577dce94e52e3f0e85fb52660e5d90f901747","impliedFormat":1},{"version":"e0b50044596bf7b246a9ad7b804cc5ab521f02e89460a017981384895a468f23","impliedFormat":1},{"version":"b303a99933b69d9d6589ac24f215e5d987933782244251a10e62534f08852d94","impliedFormat":1},{"version":"e052b679185d44460040d5ce3d703d503e5f7108cd4e9d057323f307c6c0e42e","impliedFormat":1},{"version":"8278a9533c3b2f544ef29ba0903c3bde0091809304bb78234b96a2f1a05deda7","impliedFormat":1},{"version":"8b3de2f727cfd97055765350c2e4d50ea322cabb517ff7aa3fa0ad74aab4826e","impliedFormat":1},{"version":"b3e584a57553f573aa01b34bf0d08c4dfefb2b9ede471c70d85207131f0f742f","impliedFormat":1},{"version":"dd1ecd5219280dfdaf8d17abed3594a2f20e054e7ebb6428a167e2644a701210","impliedFormat":1},{"version":"6849f3dd56770a08b9783d61e3ba6e2d0ba82850a20ae97e1bdcaeb231d2f7fc","impliedFormat":1},{"version":"6fb23beb59f1f5c8dc97bfc012d5edac81ffca1c1b83a91381b4e130e7ce24f3","impliedFormat":1},{"version":"46410e3c36bc44cfcb49fdc9790a7c3364f19d34e886a29ee99cec122a15d660","impliedFormat":1},{"version":"64522b77f907974a4ea1a8f3e3fc2ed53b1cac8d52ac70dbb2ad37b75fba4596","impliedFormat":1},{"version":"7d71830269a3f102c69e90559661bf05468778a21463337dbfe96118546b3a09","impliedFormat":1},{"version":"b4d286a3c858e8fb00c4f5da6928a09cb6f8143aa35f15c96354ab07b6f78508","impliedFormat":1},{"version":"c7e7d48913bfa205453911f699307e7ce630deb3c3e68326377bc2ba20abb1f9","impliedFormat":1},{"version":"4b78505d4f7ba7a80b24dae9b9808c2ec3ecb6171af03a4b86a7a0855d7a80c1","impliedFormat":1},{"version":"c7ad84661c51e66b7ca4da98f46442fa7586b2f9fd929a45e15b6d32f72d2f66","impliedFormat":1},{"version":"50c0c2b5e76e48e1168355e3622ca22e939c09867e3deb9b7a260d5f4e8d890c","impliedFormat":1},{"version":"66491ea35e30cc8c11169e5580aef31e30fdf20b39bc22e0847c2c7994e2071b","impliedFormat":1},{"version":"35680fb7f25a165e31e93ea22d106220db4450b1270a135b73f731b66b3d4539","impliedFormat":1},{"version":"5865007a5331be0842d8f0aace163deda0a0672e95389fe6f87b61988478a626","impliedFormat":1},{"version":"dddc865f251a4993b9e23494a9ae0fb58997e0941b1ec774490a272d5a0b29bd","impliedFormat":1},{"version":"76d1f106ef20648708a7d410326b8ad90fc6f7d4cdf0e262edd6bd150676151b","impliedFormat":1},{"version":"aa7662f3e1f7c648559e0c4acb9495e312a95dfdc5e3a3a5a568d9d23637f1aa","impliedFormat":1},{"version":"69055f4f0b1b2df9f0ca89231075c0578975518543100582dd37adb956ad6135","impliedFormat":1},{"version":"c3f85a0f71b64d78e7dfb27a12d10b0cd621745f40752b8e9fa61a7099d4290e","impliedFormat":1},{"version":"0b4b2424b5d19bbac7e7ad9366419746fff0f70001c1867b04440d0031b26991","impliedFormat":1},{"version":"1e9bcdcb2ee81b6155cb057d50574a5aeb77f22ee850d5feb9a999f81de866c6","impliedFormat":1},{"version":"4fd695c068c325f2eb6effd7a2ed607d04f4ed24b1f7cc006b8325b3eb5bd595","impliedFormat":1},{"version":"c18fb9b8d4a7f41ae537512368ec9028d50b17e33e26c99f864912824b6e8c30","impliedFormat":1},{"version":"7012daa2ae62b734cad2c74c54a36ec63ee17185c32634ab241f0bc1d8edcfc5","impliedFormat":1},{"version":"9b923be7ef4337bbddbd1713b13cf81da9a955034bdf657bb9e60a8fc9b20ac5","affectsGlobalScope":true,"impliedFormat":1},{"version":"527668d62da5909154a74b74a7a9ae59c41ab4a70da76c2f476765308efafb0f","impliedFormat":1},{"version":"e2974b2b0a7ba6384f5f3338d2a6a70170c3002112d6e05ce593d966100bf232","impliedFormat":1},{"version":"cc3738598b5fe875e341f701824403b3cac48c50472c72423d3e236b610fa977","impliedFormat":1},{"version":"4d2beddaa6b753968fb80b544268ba521ef13287d4284f0655dc7aeea347f4f8","impliedFormat":99},{"version":"5e35a2a3f0b62ee763fd1d1f13cdec015ea10fb1ed7a670989b1ba49b37ad287","impliedFormat":1},{"version":"b3b5aca751100320745c8bfd826202aed7d753d336448ce2265b9470dfa8a298","impliedFormat":1},{"version":"5fa35c6051059d5ed57cbda5479b593cec15d5405229542042bd583c1e680fb4","impliedFormat":1},{"version":"7df3932c1b8816845e1774538c4e921e196d396b3419e2e18bc973079b4064a3","impliedFormat":1},{"version":"c8a7131a27d7892f009ab03d78dc113582f819c429af2064280bec83c2e7c599","impliedFormat":1},{"version":"19629032a378771a07e93c0ab8253b92cb83e786446f1c0aed01d8f9b96a3fb6","impliedFormat":1},{"version":"fd4b51f120103d53cc03eea9d98d6a1c7e6c07f04847c0658ec925ceeb7667aa","impliedFormat":1},{"version":"53bacb19d6714c3ea41bebf01a34d35468a0ac0c9331d2ffdc411ce452444a2f","impliedFormat":1},{"version":"e2ce339ecc8f65810eda93bb801eb9278f616b653f5974135908df2c30acc5ae","impliedFormat":1},{"version":"234058398306e26bc917e6efba8fb26c9d9f2cfdfbaa17abfcb11138847de081","impliedFormat":1},{"version":"b3ff9aff54c18834bce9690184e69fd44fd5d57273a98a47fbf518b68cc4ec60","impliedFormat":1},{"version":"3dc40ead9c5ac3f164af434069561d6c660e64f77c71ab6ad405c5edc0724a94","impliedFormat":1},{"version":"d5fb34e3200ce13445c603012c0dfbd116317f8d5fef294e11f49d00a859a3d0","impliedFormat":1},{"version":"58fc843cdfd37a8b1ae2cbf3d6d3718d41cdafcbbf17e228bd6a7762a7235bf0","impliedFormat":1},{"version":"a4d0945318f81b27529abcae16d65612decf4164021a0d4d2ec19fbfcbaf1555","impliedFormat":1},{"version":"fbe57f37a07a627af9ae5922c86132677e58689427cc748866a549ef3862f859","impliedFormat":1},{"version":"8df750d51d498be760d538ac9818c7aebea597f21d4937a65fb2ebedd8a976e7","impliedFormat":1},{"version":"5b9c5efb469020fd6a8c6cb8c4b378ef3dc46ad97938ac900882f1d5f237bc91","impliedFormat":1},{"version":"83dc862cd9b7b1a929bcc03e9bbc8690cebc7e29b1edfa263f6fd11b737f19df","impliedFormat":1},{"version":"fffacebbcc213081096e101e64402c9fb772c5b4b36ad5e3d675e8d487c9e8af","impliedFormat":1},{"version":"1b243b5a51dff2bf70b7a6ce368fe7ff845c300027404b5a41a87ce5490cdad0","impliedFormat":1},{"version":"dfb119c12d7d177eb47b98c011677ca852dff82ddbe40ea571e31e04d2b84278","impliedFormat":1},{"version":"e0b50044596bf7b246a9ad7b804cc5ab521f02e89460a017981384895a468f23","impliedFormat":1},{"version":"b303a99933b69d9d6589ac24f215e5d987933782244251a10e62534f08852d94","impliedFormat":1},{"version":"e052b679185d44460040d5ce3d703d503e5f7108cd4e9d057323f307c6c0e42e","impliedFormat":1},{"version":"ddb79ad4350198a188ad3230d2646b4c67467941ddf4022ed01e4511a56d2cd9","impliedFormat":1},{"version":"8b3de2f727cfd97055765350c2e4d50ea322cabb517ff7aa3fa0ad74aab4826e","impliedFormat":1},{"version":"b3e584a57553f573aa01b34bf0d08c4dfefb2b9ede471c70d85207131f0f742f","impliedFormat":1},{"version":"23a24f7efe3c9186a1b05cd9a64a300818dd0716ffbd522d27178ec13dc1f620","impliedFormat":1},{"version":"6849f3dd56770a08b9783d61e3ba6e2d0ba82850a20ae97e1bdcaeb231d2f7fc","impliedFormat":1},{"version":"6fb23beb59f1f5c8dc97bfc012d5edac81ffca1c1b83a91381b4e130e7ce24f3","impliedFormat":1},{"version":"bc759b587b3e7213fc658fe78dbaf7b0e7c0a85f37626823b4bbef063759c406","impliedFormat":1},{"version":"04ed59801192608de22461e38b9f2e300953f1d6d6c05332f19e78e668d6a843","impliedFormat":1},{"version":"bf5cfc96bacabfe71962c32755df63ac499f732571368db3bdd7e144336c50f7","impliedFormat":1},{"version":"b4d286a3c858e8fb00c4f5da6928a09cb6f8143aa35f15c96354ab07b6f78508","impliedFormat":1},{"version":"c7e7d48913bfa205453911f699307e7ce630deb3c3e68326377bc2ba20abb1f9","impliedFormat":1},{"version":"4b78505d4f7ba7a80b24dae9b9808c2ec3ecb6171af03a4b86a7a0855d7a80c1","impliedFormat":1},{"version":"d09d8ac8da326eb4cf708d3a3937266180fe28e91c3a26e47218425b2ec1851d","impliedFormat":1},{"version":"50c0c2b5e76e48e1168355e3622ca22e939c09867e3deb9b7a260d5f4e8d890c","impliedFormat":1},{"version":"66491ea35e30cc8c11169e5580aef31e30fdf20b39bc22e0847c2c7994e2071b","impliedFormat":1},{"version":"35680fb7f25a165e31e93ea22d106220db4450b1270a135b73f731b66b3d4539","impliedFormat":1},{"version":"5865007a5331be0842d8f0aace163deda0a0672e95389fe6f87b61988478a626","impliedFormat":1},{"version":"dddc865f251a4993b9e23494a9ae0fb58997e0941b1ec774490a272d5a0b29bd","impliedFormat":1},{"version":"76d1f106ef20648708a7d410326b8ad90fc6f7d4cdf0e262edd6bd150676151b","impliedFormat":1},{"version":"6e974c9f7e02b1f1b7c9538619fe25d9d23e4eb5df3102f62f3bb0cb3d735d1a","impliedFormat":1},{"version":"18f3835257e2f87f8dc995c566217c5434d9bc14a6d18e7ca0e2afbfc2f1eca8","impliedFormat":1},{"version":"69055f4f0b1b2df9f0ca89231075c0578975518543100582dd37adb956ad6135","impliedFormat":1},{"version":"c3f85a0f71b64d78e7dfb27a12d10b0cd621745f40752b8e9fa61a7099d4290e","impliedFormat":1},{"version":"0b4b2424b5d19bbac7e7ad9366419746fff0f70001c1867b04440d0031b26991","impliedFormat":1},{"version":"1e9bcdcb2ee81b6155cb057d50574a5aeb77f22ee850d5feb9a999f81de866c6","impliedFormat":1},{"version":"4fd695c068c325f2eb6effd7a2ed607d04f4ed24b1f7cc006b8325b3eb5bd595","impliedFormat":1},{"version":"c18fb9b8d4a7f41ae537512368ec9028d50b17e33e26c99f864912824b6e8c30","impliedFormat":1},{"version":"7012daa2ae62b734cad2c74c54a36ec63ee17185c32634ab241f0bc1d8edcfc5","impliedFormat":1},{"version":"9b923be7ef4337bbddbd1713b13cf81da9a955034bdf657bb9e60a8fc9b20ac5","affectsGlobalScope":true,"impliedFormat":1},{"version":"527668d62da5909154a74b74a7a9ae59c41ab4a70da76c2f476765308efafb0f","impliedFormat":1},{"version":"e2974b2b0a7ba6384f5f3338d2a6a70170c3002112d6e05ce593d966100bf232","impliedFormat":1},{"version":"cc3738598b5fe875e341f701824403b3cac48c50472c72423d3e236b610fa977","impliedFormat":1},"235e06bdfae707f7640c5bc13ec831dc629be95cf2f3173a2c1e798f714144c8","6a5e207f4a22041aad8f7f77cfb879030693ea12f4cf29d5fe781cd478ab013f","d99e092d9a79f3c59a6ef22702752ea0a0ca0bd80dd0d565e524a029088217fe","ac155ef470a9d35b6e8a51b1faa411c7848fb238ef38a0199fb4d2ed99f79194","241e5ecc6b14eb79531276123e36566ab566b1f910c1331715c7baf71f583bbd","2ff5d98c80cb266e4f5664a5682776207b936e402eb1924041b5be3d7d5a6c90","d13fa07af59fc8c30feeac3151314ac35b207d828baae5d3bd159c656e0fcfe6","0e913b9efa3636e35a99affa7fc67e3aa05366edffaff188bbd888466c5e7267","ddbe0c30712a3de90b69e22dec7cb2232bfead04d03b2ba8fa1a491c093091ad","311e35e5ef796795cdcb5a72d3af742159b828464fa1d553dc15ae3e0f946b16","e703cefa260d54c45ea86ff7e637987f3e7fe772d95834c3641c473626604380","7c2badda14ce0b58f7338d8f640a925e14ebf2f38b022aaadbffdcdc83bfdef3","9981b7a8987a235e66e05248e4faae27d835c2ac5ea8c0c2b7eece10645a7e73","8a610d11d4de788aab1895f2094468bc05f1cd3c1426fa2c60780453cf37731d","7289fcc4b42ee234131f8e52c4c56b8cea35ca3de44e563884f75e4712375348","7cd33070ad84e3cd727168097fd81b1befe1cd73e6254bb9d3689b7178155c45","13557a19616f91ee99c1b92e83c1588806c0042352b0ee8098d4d6e499d6d7b7","f8689a423a003317ee9eb0c949cca9e61c837ea70e127fa616accf0a2dcbb651","182d609ccc20dc9ce1419d154c12141af0e4ba734816f10d1528098d2c1b5c60","82558e64eee22c8bf4bc9667e0cf312a5b56ca461c9da5a494a864d8b0df4ea8","4427f5dc1a8306b1892dc82db3c555125031d246f3f856208edfb7d15d722336","a955865215e584d70ac293eef845b1b6a9fc970c1e4ac1349204bcdb1cfeb346","723aee04ac373308301b56d19fcf847dcd3c22d4d4ca42ec229177d225cc8940","b34d79ddeb58609e5c90ea98fd910fc56012e318018b1113be2a2b2236cb53c7","ec13bff5faecbee14ac5f0ced519d1d22c70d3641e21b55dcf09dcfca0a3079e","6ad314056719207b169959ea70e221296f6e85f22b1f261b908a96e5b5266015","32d32b484dd0db8266b9c782a372bacfd93c206baeb776e94160d09b5f76f6c7","7986124ceea51bf4ed2cccdd4382bf43ad998567299068f8bb24457725230373","c547c0c96aff2864949c48cfdee69ef0378a5d762fbfec30be46ad0d39e5ed77","501ae3028253b85ef4e28283ecbad47b30d9696a789b11384b35a7c31a2bede2","d724cb477cc0100d2a2f076c28666a5caaf319f9cf40f913e7bfa7b788124a0d","a5a9d368c945a86fa63b6872d23057b699218ba2355316c1053f6ed3265adb5c","cca200c23a62fd970bcb049de2102f5f3033cc232ba4bc86631cc7f4e705c2b4",{"version":"2efec76e9b27185cf35bae565af3a382e875a5c2ccc4fd6653d4adf68147f0c7","affectsGlobalScope":true},"46bc032cd54381e206c4cd46e5cc38d27d969780bd239af239bb0df82c312999","2d00a5ce0f7a0cc774ddaff01dbc35f111c46ebc8a1542cba7bd7cd3be5efcc4","da0d9c4145100dc1339decce17e8b7a34bb90399d4b75613d31ec4628e39aa10","79bec024cce000eb5cdc1519f9d69370ff3a0f0b63a28d4818667f6badeab7fa","7d21d7db57a654d8050aa9270723ec0a6ec5565c14eaf954b665bbb55d275145","41bd2341af90363c6fed4fb2eb99655cb07ad9be9fcd02546761c5fd163d5b04","c1e73a54dab71b092eeed7cf2968c880ba85ac80f3485d8b1961fce18a4595d4","d202b4bb5f6c456e514b112d4d0e80357896724c4b23a1482810f0b9e1b911ff","55b508605ab94b3b6eacb3685b34600bea52b3f929128001940d98e356585bc2","8741246d8c61ca59b277d79ea942b6b9c936317a6e66ad3decbee4c50778c7ea","c73e2449d7b8195fb5b3e1d431eadf9e6b7bcd2daa7367defedab69dcb8148c7","a3f121bdd56b711412f3d83fce08455a553811118d08661a67a1e5170aa89d56","f4bb83a75db3a01894ce848ac9567802ec892d7c2885c7a5942b270dbced2553","ac776fe7abe6598096903a0fb1d96cb907dd185ba927f867bbf7f872da525166","92af696a0fa705b9f92f76ddfb1e607f7bbce69995a5a6d0723cb29d47549c89","9bb9bfecb48b5cbcacb943cab287f8facde7fb1ed134dec521c6ee6c51d83eca","baa5c84268745e03ec4b8375538c6bb439b44ed3573864329e23cb135ef9e387","d9eea66d14c46a463215d8175af30233ceccba974ed35426048ec3402c841181","8efb4b06b6587d0ad3577333d51164d7385b459b463ee5462053ca9cde648a12","f3bbc3dc4f56345b1fd3858ec5256db4c5f2859e9c4f2ee6af3ec50fb735474e","db9e8577d2af896df4e1a4dde14bc50b7aa85e7d7e0831b96e03a3142dbac9c2","a2746ae465f426a471a317a2f1dbac66fe477c58049bb3a76ec056d09a41f6d4","a0ec9b83a3bb3f2d4e481f13e286e89bc3530272e110fbfc30d1fd0aa084f51f","77e8c2f202ed6bc9ada0eabc691a9a3191374db6a92853bbaa424b633172e031","ff25398e57ecc00e8728ea203f37bf6194b997d9e8da761885428645a01b7dfa","a8704522f0d06d2e9fe16cd5c92f99b744be7b917973a3cdc8fca748e531ff0e","7caa71db544771e7f36155909651c0bf35ecabf9548d054edc3f2245804bd075","fff84b6f06e9fdf4c9e07b66911714c408e418bf67751157d0010d5e8d9b5ca5","542c588ca7a8ad5b156fe8131a2377faee900aa76e19c41618267f0a94628bf0","55f9754294481ea1c17ad989599424a48376ce205e2916b0e26d8e2ecfc6da82","776fb86a9acd70b841b5d984464b5087941c96db6b57adf39983d515d25147c7","f131a6a4d63b473231131f8f64133e4463e5edf7eebb3616847082bfbbb894c0","92264cc2cc28275ca0f9ea63a7316319cbba07963041e92216f4c119155cef4c","38c82d76525c218404b9bdcefd4f547f335b1f15c2efefdf0e5a34f110bd451e","aac300ac9f13bdd22b59bb29a6cf4216e0e397dda7745342303e16caec8e4c8e","5219c84f064b700fc53f9701442b4a0f159a0e620e5e9b7ad6e7746cf06c5924","17d9d1205ebedf54b55a63fc047dd992f9c195403e7287e74a9436133bfd5b50","aae1e2a40c19c8eda7257a70107d92e9da6157b1fbf3e2174859411c9360480e","e82e2954ee5e3451afbb1fdcc2176f5956e4972ef09ff8cd8e0d55eb7e93e16b","dc913c8055574e33b6507ecb760aa2841bf91fde93a0c49552040ad8469de8ee","1b7595161bbd8973c380321295a290a844de9ed99ae8483981afb0963716d5c9","4a92b125fead7a864a9355eb7e8d662fa43f52388bd1d2cde79e46c9221cdf3b","ff2368d2421b2d6bcd0a9a9a34d50f5aaed1da6f174cc4232d0dc820b555fc5d","d791fd374a74cd44634cd79d70b02bdf9c9fadf15082973abb1ac9e7bfa0cca0","8c991610fb81153d91f9e36362a682d4fc1990d0dbdae69b62ab2b0656a4e0eb","a619cfb634f71272f97b7a0b3958960aef2e0cf652673173a24e82a44db28ac6","926402cc51633e03b23c486ea83cdc45d91a7721b5940887e85cf1a2ee65b24e","da5ed61f8a25871d10b99e33a7b86d2d1cb683ac03df6947812e5399a33ace86","11d500ce874e2998bfb08dfd08d174770abc4bd27ae1dcf1c1ce84896c920d61","a31a6e7abbccac4afce731c590870f98132c797326a2c1b0943d6d36f25c4e25","1f64ee1408a78df47a166d83115e1da49b8e0c46c54547a8ac0672cb8f7c811b","39b61ca6728f7fec338c9198ae62cacc8183b7bf41abfeb13144aea1fc1abc73",{"version":"d2e64a6f25013b099e83bfadb2c388d7bef3e8f3fdb25528225bbc841e7e7e3a","impliedFormat":99},{"version":"369ba5259e66ca8c7d35e3234f7a2a0863a770fdb8266505747c65cf346a0804","impliedFormat":99},{"version":"64d984f55025daf604f670b7dfd090ea765f2098aee871174ef2ee3e94479098","impliedFormat":99},{"version":"f147b6710441cf3ec3234adf63b0593ce5e8c9b692959d21d3babc8454bcf743","impliedFormat":99},{"version":"e96d5373a66c2cfbbc7e6642cf274055aa2c7ff6bd37be7480c66faf9804db6d","impliedFormat":99},{"version":"02bcdd7a76c5c1c485cbf05626d24c86ac8f9a1d8dc31f8924108bbaa4cf3ba9","impliedFormat":99},{"version":"c874ab6feac6e0fdf9142727c9a876065777a5392f14b0bbcf869b1e69eb46b5","impliedFormat":99},{"version":"7c553fc9e34773ddbaabe0fa1367d4b109101d0868a008f11042bee24b5a925d","impliedFormat":99},{"version":"9962ce696fbdce2421d883ca4b062a54f982496625437ae4d3633376c5ad4a80","impliedFormat":99},{"version":"e3ea467c4a7f743f3548c9ed61300591965b1d12c08c8bb9aaff8a002ba95fce","impliedFormat":99},{"version":"4c17183a07a63bea2653fbfc0a942b027160ddbee823024789a415f9589de327","impliedFormat":99},{"version":"3e2203c892297ea44b87470fde51b3d48cfe3eeb6901995de429539462894464","impliedFormat":99},{"version":"c84bf7a4abc5e7fdf45971a71b25b0e0d34ccd5e720a866dd78bb71d60d41a3f","impliedFormat":99},{"version":"e01ea380015ed698c3c0e2ccd0db72f3fc3ef1abc4519f122aa1c1a8d419a505","impliedFormat":99},{"version":"5ada1f8a9580c0f7478fe03ae3e07e958f0b79bdfb9dd50eeb98c1324f40011b","impliedFormat":99},{"version":"a8301dc90b4bd9fba333226ee0f1681aeeff1bd90233a8f647e687cb4b7d3521","impliedFormat":99},{"version":"e3225dc0bec183183509d290f641786245e6652bc3dce755f7ef404060693c35","impliedFormat":99},{"version":"09a03870ed8c55d7453bc9ad684df88965f2f770f987481ca71b8a09be5205bc","impliedFormat":99},{"version":"e6233e1c976265e85aa8ad76c3881febe6264cb06ae3136f0257e1eab4a6cc5a","impliedFormat":99},{"version":"2cdd50ddc49e2d608ee848fc4ab0db9a2716624fabb4209c7c683d87e54d79c5","impliedFormat":99},{"version":"e431d664338b8470abb1750d699c7dfcebb1a25434559ef85bb96f1e82de5972","impliedFormat":99},{"version":"2c4254139d037c3caca66ce291c1308c1b5092cfcb151eb25980db932dd3b01a","impliedFormat":99},{"version":"970ae00ed018cb96352dc3f37355ef9c2d9f8aa94d7174ccd6d0ed855e462097","impliedFormat":99},{"version":"d2f8dee457ef7660b604226d471d55d927c3051766bdd80353553837492635c3","impliedFormat":99},{"version":"110a503289a2ef76141ffff3ffceb9a1c3662c32748eb9f6777a2bd0866d6fb1","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"310e6b62c493ce991624169a1c1904015769d947be88dc67e00adc7ebebcfa87","impliedFormat":99},{"version":"62fefda288160bf6e435b21cc03d3fbac11193d8d3bd0e82d86623cca7691c29","impliedFormat":99},{"version":"4f40c8f34ff420c9a4eff580b2503856efb722cb4a4956cee9044ef5ee3b9939","impliedFormat":99},{"version":"0309a01650023994ed96edbd675ea4fdc3779a823ce716ad876cc77afb792b62","impliedFormat":99},{"version":"f13d7beeea58e219daef3a40e0dc4f2bd7d9581ac04cedec236102a12dfd2090","impliedFormat":99},{"version":"34bd231cdbfcaed4a33638fa67333b17457f410a25b221ed841b8c3ada3ea1a9","impliedFormat":99},{"version":"48c411efce1848d1ed55de41d7deb93cbf7c04080912fd87aa517ed25ef42639","affectsGlobalScope":true,"impliedFormat":1},{"version":"a094636c05f3e75cb072684dd42cd25a4c1324bec4a866706c85c04cecd49613","affectsGlobalScope":true,"impliedFormat":99},{"version":"fe2d63fcfdde197391b6b70daf7be8c02a60afa90754a5f4a04bdc367f62793d","impliedFormat":99},{"version":"9a3e2c85ec1ab7a0874a19814cc73c691b716282cb727914093089c5a8475955","impliedFormat":99},{"version":"cbdc781d2429935c9c42acd680f2a53a9f633e8de03290ec6ea818e4f7bff19a","impliedFormat":99},{"version":"9f6d9f5dd710922f82f69abf9a324e28122b5f31ae6f6ce78427716db30a377e","impliedFormat":99},{"version":"ac2414a284bdecfd6ab7b87578744ab056cd04dd574b17853cd76830ef5b72f2","impliedFormat":99},{"version":"c3f921bbc9d2e65bd503a56fbc66da910e68467baedb0b9db0cc939e1876c0d7","impliedFormat":99},{"version":"2330087dfe056bbfd0f5590a99d1912ce6b4b6a65cca94466cdd142d55503e84","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"4c8ca51077f382498f47074cf304d654aba5d362416d4f809dfdd5d4f6b3aaca","impliedFormat":1},{"version":"bcc1a744205fcd40c6c5c5fac042307fac6f9d6055a4f4e9b96f80599b9cd4cc","impliedFormat":1},{"version":"bdb83c76f2c2961c45d90f66f0cebeccee91704dfce8e4f7c7d298e94e2673d1","affectsGlobalScope":true,"impliedFormat":99},"be592674884b12aeb2519c8475b8a7d21d6901a80dba305acc8c04a21ded1570","9abf33ae5fdae6771726e43258d87913ac81b0ba82d488045e3a2eb3c074c257","8a9bc9cfdd69286684adb508d99da34d2d60244a7183c95c6daaa7d5eb9fbc27","be7c8dd5d513b1ed96b4244a2a4b37675fea247890dbdb2dc7a655dfdaa8c10f","1eadca308ed5d28161c2a21a11245289063dc8eb42ce7af66dd196e8096066b2","a6f09841e61dc0b64f0b2a86bfcb65320e7f0330980055781ff83b12dfade2b3","5511134f005b398379af4233ecce5eb1fd781d2b282079a7b2ca0ac616205761","c52613898c101a512082003ce4c09f0f67af2f0c3d8d0fe109b518a7c5eb812b","2c01552aa1ee944791ac758517f476b39f49893c01c5cb6d834d0043b65ce89a","2dbe3fe7dcab755dee92ee649c88c54a5cbc13c95d49d59b3eb6a9d09708463f","20c0786c365b79d33f93f47d9fe3afbce08e30b2cf4202c66cf51b7485ba3406","35d48300089e6172bbd52824530796bfb867c63e7bebc4296199fce065b7b0d9",{"version":"6db928ecb8b9450aecc2a5ecdead68fa8b7a72130008c77e7524dfa7ef6c7002","impliedFormat":1},{"version":"4083e6d84bfe72b0835b600185c7b7ce321da3d6053f866859185eefc161e7a0","impliedFormat":1},{"version":"b883e245dc30c73b655ffe175712cac82981fc999d6284685f0ed7c1dac8aa6f","impliedFormat":1},{"version":"626e3504b81883fa94578c2a97eff345fadc5eae17a57c39f585655eef5b8272","impliedFormat":1},{"version":"e9a15eeba29ceb0ee109dd5e0282d2877d8165d87251f2ea9741a82685a25c61","impliedFormat":1},{"version":"c6cb06cc021d9149301f3c51762a387f9d7571feed74273b157d934c56857fac","impliedFormat":1},{"version":"cd7c133395a1c72e7c9e546f62292f839819f50a8aa46050f8588b63ef56df88","impliedFormat":1},{"version":"196f5f74208ce4accea017450ed2abc9ce4ab13c29a9ea543db4c2d715a19183","impliedFormat":1},{"version":"4687c961ab2e3107379f139d22932253afb7dd52e75a18890e70d4a376cdf5d9","impliedFormat":1},{"version":"ae8cfe2e3bdef3705fc294d07869a0ab8a52d9b623d1cc0482b6fc2be262b015","impliedFormat":1},{"version":"94c8e9c00244bbf1c868ca526b12b4db1fab144e3f5e18af3591b5b471854157","impliedFormat":1},{"version":"827d576995f67a6205c0f048ae32f6a1cf7bda9a7a76917ab286ef11d7987fd7","impliedFormat":1},{"version":"cb5dc83310a61d2bb351ddcdcaa6ec1cf60cc965d26ce6f156a28b4062e96ab2","impliedFormat":1},{"version":"0091cb2456a823e123fe76faa8b94dea81db421770d9a9c9ade1b111abe0fcd1","impliedFormat":1},{"version":"034d811fd7fb2262ad35b21df0ecab14fdd513e25dbf563572068e3f083957d9","impliedFormat":1},{"version":"298bcc906dd21d62b56731f9233795cd11d88e062329f5df7cdb4e499207cdd4","impliedFormat":1},{"version":"f7e64be58c24f2f0b7116bed8f8c17e6543ddcdc1f46861d5c54217b4a47d731","impliedFormat":1},{"version":"966394e0405e675ca1282edbfa5140df86cb6dc025e0f957985f059fe4b9d5d6","impliedFormat":1},{"version":"b0587deb3f251b7ad289240c54b7c41161bb6488807d1f713e0a14c540cbcaee","impliedFormat":1},{"version":"4254aab77d0092cab52b34c2e0ab235f24f82a5e557f11d5409ae02213386e29","impliedFormat":1},{"version":"19db45929fad543b26b12504ee4e3ff7d9a8bddc1fc3ed39723c2259e3a4590f","impliedFormat":1},{"version":"b21934bebe4cd01c02953ab8d17be4d33d69057afdb5469be3956e84a09a8d99","impliedFormat":1},{"version":"b2b734c414d440c92a17fd409fa8dac89f425031a6fc7843bac765c6c174d1ca","impliedFormat":1},{"version":"239f39e8ad95065f5188a7acd8dbefbbbf94d9e00c460ffdc331e24bc1f63a54","impliedFormat":1},{"version":"d44f78893cb79e00e16a028e3023a65c1f2968352378e8e323f8c8f88b8da495","impliedFormat":1},{"version":"32afc9daae92391cb4efeb0d2dac779dc0fb17c69be0eb171fd5ed7f7908eeb4","impliedFormat":1},{"version":"b835c6e093ad9cda87d376c248735f7e4081f64d304b7c54a688f1276875cbf0","impliedFormat":1},{"version":"a9eabe1d0b20e967a18758a77884fbd61b897d72a57ddd9bf7ea6ef1a3f4514b","impliedFormat":1},{"version":"64c5059e7d7a80fe99d7dad639f3ba765f8d5b42c5b265275d7cd68f8426be75","impliedFormat":1},{"version":"05dc1970dc02c54db14d23ff7a30af00efbd7735313aa8af45c4fd4f5c3d3a33","impliedFormat":1},{"version":"a0caf07fe750954ad4cf079c5cf036be2191a758c2700424085ffde6af60d185","impliedFormat":1},{"version":"1ea59d0d71022de8ea1c98a3f88d452ad5701c7f85e74ddaa0b3b9a34ed0e81c","impliedFormat":1},{"version":"eab89b3aa37e9e48b2679f4abe685d56ac371daa8fbe68526c6b0c914eb28474","impliedFormat":1},{"version":"dea7c456048a2566e52afbe7651d2a87e19f7bf7fc9ff5341a7cb719208a7e6f","signature":"5e8bf6ed94f499bf54718e8089e7ed26f880d23b38a6d76fd7ad42945b515de4"},"b575c3ed7aa6d8f6f00946e159dfd4162d1ad580d12d7437121f06085f1fc514","1a8c50517ed5bbc84fa24ad1324dfba8ca895627f22c08cd3f11ea4535a162d9","dbaa543867b679c499c34fcab9cae59d6f10a8143b801399a311a5f6180506ea","4884378046c2784ff1f2289abc2890d01bf7e29feff47bf1c1beccab01eb57b9","6464c691e5e97ac1225f795a85c83f65a06e28811d132cb02b222d856f83e876","36235cd372867b7e1af7aae488a8f9ec21121919d120c20430bafb175c6401d5","79e40a10718218e602e33a04348364017dc6758b3aef97b5606ac7c63b92b54d","4336a713dbe015b43442dc01827f007de97955b201289e07fe938a3618934784","4271aa737a6d7f172bc2a03538341c69042a9ac0560b7ce3b52c8931c2c82d31","f6ff351d5684d8ab1ac21d4858b981e31d2a165580134ce37b7ad33ef39aeeb5","1a541ee8a0a332bcbd5d961503ae6cc118bf15b47a38fd8904474bd49a5b6633","d138e539d7eb2f80f4165a20d832368791bd26a38d9fccef7b8bc4a935a6235d","0ef2d445bd9b0e21d6a6460c7b77248673a6e3aa2e4556ce690fcfc536735a4e","90da059d69ef622ac82507ba6b6615005da2a8f3b8e0a527791943773932a2aa","fd5d61681763cfc7cecc3900c090ceab537dcecf87e1af5dc81c313b260d9a2e","bcd7b1c32b089f0dbb0d157fcb75067c5ce9f51156dd3911790e83da89a213bb","bf0f38c23cb48d8569712589aacd71be285fe2af4fdcb4352a3cd55057327df2","5d5f9c820b2d013c9331d9a4ab483b17b05430cd94b58af1ab034788d038f3fc","c39dd607194edd9586da4a2fc1fc1d701714344591e1d70c4f13d0656b7cc10b","302d003d6ec03465616725e976f36d9ae1b90a295048c968fd3e2ec481eeeca5","94844a872a3ed341737854871df665b6bdac1c0fed91d45152abc34486de8a49","2d9ead24dc6c2921043423e8098ac007bca0c8a95be841237f8ba115938ea13e","31483dc8535992e8e11ea96a1c6caa68d96f3d90d29919e3a3f1ccee0a9533f5","776f23efa90ffaf6eb6d430e6030df007c361b86705215876ec11a317c2f8872","6b112022fc280ff203ecdf1ed93cb52accdcf866c671960d2a5761a6287f407a","6f29fcd20c5b4640bf77ff8b78b93ac02477792a6ba02e643ea9d4bc96ed9ee8","48573d1404ac8d6fdc374d238cea1687430eded9a668a8bc96f22467b5ac9b75","e0a1a96a5debea39cff564ee5a181c201f16771570476d228a8cd539df7e4711","e07c765d41f6901d5d29248a38c0798eb0d6a75aaefee75698eb57a76607f5b3","21157dd88062a5dd68739343451fb3403d8d640038ffce93a370ee14009ad605","8162da369bb1d3ab56f1e434335d3db9a5de803e1ade53c6f23b1083e15aa407","78d2408cbd25656ac94864d9ef8f0e9e0c3d29347f2efdd882b4b8a00c4077f7","6a756e9b859115b6b25637b6017f24439eff4bfadf0246e200ab7aad357499de","39904b141a5724ee62f0212fd2778f151a997ecee7ab022cfe63920e2549fa1d","3806f73a259c2d1eafbb0a57a530452621a60201554d3b21513cd3f4d5e132e9","1ae4458cc58158306d9fc235c4cd185dade936301d46e626ce39499314d63b96","5dfc0c81bd4a5edaf6b89118fccc08a4fee977cc385707294216a54acf754d03","138263bb2a4ab2daaaff75b750794f83c2d38c82c7d0a6249587e831ce175ff6","21ff8d5653489e64848e46bd4e721549567da52455956351d1e1d3b20cb0da19","7d85d361a37a839aff2bb34727ab510f69654681a24407fdf613a4836b3eb616","b4ce4f11b06ee171661ad049cbd55a49ba2eb3d7d47945d8ea55f360c3b562b7","59057294a6b5e124617731f27798fcdaf61ecb3340ad7dc4daa0c2eb79334f0d","175fa55656913492a50432b02a2234c220d16d4010c73f2f9a684f1beae12e4f","38e9b646a9997b53766a67fc8b79dca01ed8f35aaa6fffd6f755d4af4741ead4",{"version":"80fb01234518e4f221db21b8cb3a6f019e0a7ed58f21454b89bdb422b33ea1d3","impliedFormat":99},{"version":"f454b6f363d3ff61a7709af142254e55fcd7400e5cade3a7e5d72bcee5ed14a6","impliedFormat":99},{"version":"2efa0ce1c439d63f23a4c3f71f30bb397f9ba20a44bcb9f0f0d4a101545fdc15","impliedFormat":99},{"version":"38b840883016a61f764fe088f4aa0410e3b93d47061da2ef76e0555caf992a4f","impliedFormat":99},{"version":"c22ebb3a0c787cb1aa4972f33cf19b9d1975db05dbb582d5cb6488754786e5b5","impliedFormat":99},{"version":"c60df36a0e934223fa3da6a800b804477616bbc7c849a6fec9e6ed7c82c3a1c1","impliedFormat":99},{"version":"4b8379d3cdc7e7046e1311fd946896864b93a821562fccf1503c12d4006cc9ce","impliedFormat":99},{"version":"0d2c4e8c2efc906514b7cf9f574e1a5a0c83461ecdc9b6453bd3527067865204","impliedFormat":99},{"version":"a1566822c360f0d5e3cc2cf051ac7f628938ce078750923ddf195435153f62ce","impliedFormat":99},{"version":"fe6373a621d38c3d1561234cbaf39c6f406095319c206ec02c3b7e8b9175f9b7","impliedFormat":99},{"version":"61e08be2655fdaf73b8747d84645a0dbc0d4e9676e5dc762eb0e782040e9ded5","impliedFormat":99},{"version":"781405816c5165ba03baace79c470bb5bf814c6fa71c6840dc17401997e61d87","impliedFormat":99},{"version":"8147841109690d8b7e2dc844e0b90a17a8a088c8a8568d3437715a7e550a1735","impliedFormat":99},{"version":"dbd28b535b2c0ee8aadf62b2ec75a5530bb5fa61e9b77e48f21aeb9dd82e6195","impliedFormat":99},{"version":"d0f0afd59124efb04320e52d939aba30cafd42389193645022847bf6abc42d78","impliedFormat":99},{"version":"ad1d17f4c8000566cbf45f4fea87b28961383695619326d57c59b6d203e5844a","impliedFormat":99},{"version":"7897ecf8c7553ce580b71a17a56f4c86577f5ff90798cc092ef8a15bc926a607","impliedFormat":99},{"version":"3bd646dbedf82bb4307825770683a0fcb98d341e5080fb960379bfb00d7f7b75","impliedFormat":99},{"version":"b18f1146b26e8aa4043cd545518c3ed2b2bd17120b213b7db3ca9ad481e94c0f","impliedFormat":99},{"version":"824d39653f076600347b3556d3f724dd144959597f726c2f5ddacd4e6bcbf191","impliedFormat":99},{"version":"bc077b216bc3a172ec747da246463d1cd0b2958c30d2c05a673989e2d5d0bcc1","impliedFormat":99},{"version":"4ca8c930581be9055ab171dfad669344bf0d6fd21d8918724d2a36f2a80549c1","impliedFormat":99},{"version":"70aaea8a2a8cb701199ffa9f27f14a683df20767f21f9a8e58b924967e49eb6d","impliedFormat":99},{"version":"21ddede6462ddf922453c05d717cf1f9c8fcaf7732a32bde9c135edce3ad8039","impliedFormat":99},{"version":"9615baea7ac1308f022e280d110e28868bf8ab1654c88838ce6f92290da3dd00","impliedFormat":99},{"version":"240cb68f9fdd6c531d4910b29c30427659e52ec6917edc9e3b07e4350e3cf1eb","impliedFormat":99},{"version":"31b1be5f3ecedb5178aab523a1b8bf142a0685f38b42d51104ea5a51c54df8ea","impliedFormat":99},{"version":"2ca845793b8a7b8813753967e5906e197f73365eb489878511e7b425b3581009","impliedFormat":99},"f2f4aada10991bc5d0d1cd062694ed648937c91ea259f92afe0ba68528d94044","800f9b4654ac5702f939b265d4676e021deb610bfba71cc9a564e9d4710b1563","16a5fabfdecb54de4dbb2d943e92b0ed2fa56c054f1abe4b5866834d1e5f03f4","e27b9d1c6afca63029ac394b77ef5f6068a8eca049aad51cea4c3dcc28bb1119","635e152ed3acb30b3af4ac299463ec9bd6879751b444d1573194f601424ac011","276be190682c68c0d46041b810640ad6d0daba9adee7f45349811924c243cacc","d91cbcc7306a3c423f7134ecb9473ad488189b03b7495b950b705f4098f987eb","cbd1de4ea414db648147bf4063d3f04364599166d3700ba5bb3daa32894bca76","64704761aeab31e33236ed54e0e4eaee91888a4e2e6a37da8d8aa47fcf45c005","9e295916ae9cc00c14b3ad7be303a520c15208ceb962e30346435ab187bb0e5d","1fe6d124060f4791b9a882b1d740d56a0e511bf8f825de3079c3968309dd7302","488c87563c0bb65b8e87b1d7f2eb2a1f679b3c7d56538c2dfaa1466fdf0f8495","14c3e50fe810381df8877e927f317e2f9b5bab43c0ffc49602b530b83f6f121f","949e45eb9bda5729691aca9e8bc9c14af63d2fdd52535c716dde292d1df52189","876c9e92fce2e9171e8db9c4f0f267ae17a66af3bfacc0a730d7c76372f4a8c1","dad4b5f22f30d43def2f537907afdcfcb263d6ecea4d6acfc44d02c9f271e960","e9540d8f48455484f3dc635e0c04bc339de088f401bf9048bc1ecb004ab339ca","71bd628d752aac58563c57ae5f90ae4b864c35ce2f0e1521c8ba0570048d88cb","ca23fb1153ca4e121473dab11478733974559952a7776d6075730f26fe4ab613","50bc74b5a828c90bd395a3f12bfba0dd6cb4622fc065c0a2d030ccdfec9bebad","6c5fa01c17016254371e66c76ca2d7739f18b36f7f221a1a294d8c4a27ebb393","4ef3f55201f7f9a10959e2251ca386d89d561288f93f5d75a242d062e1858a5c","5c9cf1f0949185c26963a3240cc55942df9819b4d0be2b3ad8ae44a1d1d6ae94","e31d1d4cabace52fb85fde398c2ea02c22c53c1ba1d3c330ab07f9d331926566","be36b0c33c44edbb4e2d40fc79e3170705840b0863d25e5488d373f6c9b7ead1","7311dcefa0a69fb31847c915876c0212cb173d86510866e99d98a2ccc9a8a54e","db04bb5859c7c34b7eada26ba1eef5c94add8706b7a0b04e155e11d3af6dcf05","4d70e7888e671804ba8b3386c8446d89104d30206143c04bdf6b350abf00efca","7072b5a5604c52d3fc2fc66a01c2c8b0db24cf84a4993a541ca5e722d193c4d7","f27ed1abeb9d6b86088edd8e0928c8809fbf0b0307acc79023c2518766a91f78","de3b554910c5aa2258cc398b2d19512fe81b5adf2eea839cea345145ac1a703b","c7259dd634401ec19e01c5d84fde738693e28385815053ba80816de5a7b01e1a","35c4292220f807bdcd4e8dde824428d3d2f64655e1e526292aede8b98e8825fe","66639051f6c6959692fc77d7e4f4aab8f4a5b7df3b1aa42c7f026ad91d7c2ec8","851cfeb2fea663f9d6361a6c36214636a4d67ea617f5ce203d32ea39d991f037","a5a4002bab99adfc24f9785e369f929e24b2adf6a5a012e4cd2211c8f5177003","d5749403e84017a617c53743289bfbcb936b6e54d0b06aeca1c5ec14124c99ee","2c5d3d761b06791c17c0a64efd2ed03b7038e30a9c4aacec84feab754ce9f488","991c058a7dd407c6a9355b9951f3cfc6a5b8ddf2efbf45c81643aa50c0532760","deebbde6d65f61d54d0d4de5eb62fa382af34e2b3d11bc427ab228e5322a8228","da947ee827c4619b2be44c6be924dc235549ecada239f2f36e6b715d7cca4e8c","8805340bdf2803428ccc95cf6955321c22b3314d4b4f872576b5d59637af7605","84b29f845aef7600845b618fcf2ed2d845f695f3ee38d9370c12620725e129cd","b75ed6ba2ea13e7e5aea5d2fe886ed9d533b038bb16e85d165bfe44294741a9e","57c7b64980a8a9a2e4ae78668e9c11d9aa61059413234e06e1ba7850b907c414","d1408cdca543118c7b284d75c884bf7dbe85e5b5a2fcf7e05f47ce6db70b4c9a","74629c6cbf9c6270875f7d18d993f713bb8d6c60dd7d6482559ae7a851938eaf","0d21480f88cf8038b3677311ae1508bbf35930dbcd14cd97fde674f9e975a995","9deeeff22613feff10203fd915c7afc2e47298422ec7ed471275d643dced5d2b","4dd14c2df43de927b06255c8d8a8955b28ddf201b3e946d6d3e228c5fb5f4970","a353fab59fc0cd316366839b5b747a36643a757fa7dc15f487241e6b172ebe90","fba2889180dda506cbc32bd3fa3e45e1fc31f4fe6215930b5443ad9fc73a1ccb","20feb8048be0d52ac0ebd1ca9f5b1dfad3004cbd249eb82d6755317c0e5b6daa","baa6d6d3133173d013d87a944c60a7a743a6d799578f6c7fd81c27c1623c5e9c","333f5690af2d5b97dc4ac23104a9c75382bee4f1096a8f1af2600d803f4e5f89","4aa51c0e05cdd44e993fff0241dfeb5084385f64a7d0f94a42e8c3d6ce958990","bca78afe437a38df1dd90c4b070da3408cd841c38f4d0f1e2578d004591df02e","d01429ec130c43554234026f2d83b887edeb83a17f9295df63123e6a49c4cbbd","17d2eed4d34f1e3694f2f9f431d9a0e58fa3c5ecfc340b24df0c6e54b7a98243","062e2b7b635ddaa22bccbb8ec85c59265a90688d03821baedf1786f6bbad9837","92bfe5cfac8654dd315d787746d108f1306a69d157b115deb5fa2a1c6f0ac8bd","1c3f1a3f2644fcb23df029d3c5e7b4de444af82b7791540b59f118b7ab5a36fe","c67a283f4b7bd24ee1b3fb7a0571c8aa0465e686385caf57c321a7a6d94c5036","cf142fa626c47de3e6c000fbe8f5725e4347bedd199b3774fa63388a0011904e","e15992aa4dd9fc16964a3d91fbc08bcd1cc7f19f8bf1420df787a826857151e6","73e0ca0ca81f4e7b4f0df3435a66e7ea0a80b92abb6f3334543c54df7cbd7e7d","d14b0fdf9f75722f995f0ecfae79a6134b4231340a1b9ecdbc8fe4555d6f2a8f","0b9ae5d8ccde5dfe5202337f56672d2ca2be767833d363759ee164ecc1bf0c85","49a6b457f69389c33100f5096f9de6ccbedf68db6b4b4de17c4deabfb383543b","2388643c69d0cab37734f862c8f28b0f95bedb50bf3740b1288a91238fe2dd5d","6843cfc890510d093e54908ed9f04a7d89898d684976f8628e2cf875927af9a3","9b12e8d5bdf154c8ffb8d582f48e02350b57e8b5434f74ba0dd40161e5043c2a","39835d472d93c31e064fc6e34c2c9c6763086debef77ae6b0487970441d2efd3","28c0ba7f98fbbaa9fd0acfbebdb7a9d7509a0dccbe9bd9639a9dfd28c87013a6","8c3f69d106697b88e29aa994d9216756a25f4924e95c93db30658290a6cd0ba7","c732b05380a6861fac1b203b83b675fb90968a415ea68ebde8df29572e6f5ff1","e16f4ffedfec1d1f8cc3b9600cb4c2d4c38264b24bbf5d77fd7160844e8319c1","407f02233f537f35d116ad72dafe15482d01b69066d958374f7b31f50a064871","53bae61d8ffec612ad26dcb3a1fe9d7be4fd5076dd6d5ebbedcf891df4fe259d","bfd21a9df201df2baadc52c70feb8b341eb0708f524ad2e154dfee65e863c641","f6341f6967b97b84994fb28ce097f5cb54757020315f734be7b3b9915415d8b2","1a35816bd8bb19c4b12f512773673a6e92d74f08b2428292c5a29e162e4c653d","d3da19682101bb23171fc7926b832826ecb75fcea03136389ba92f3c8956f3d1","d39e5e39eabf82c5e89b5b94800b0945d8f6f70e74b2b6a66455980328ba591c","4842c6e81183cc39c09b6b1320123213fad55ed44ad2475b24c8a5dc5a13fddb","7db20d3e1c1c5d553b25115690483713b25902870e7893f68b812bd21eb79033","bf0c3af4566f114b68499267ed56f63ac75ea27190c9d7367c7e8ac8b1c447f5","122a48d2b8097e72ece711473342a56a1871cd1ab802b9bca2f134d0ea96315c","136dbde98aa435bbb2f90301b9a41b520d4ccddc19260cf5bd45d81c8f823aa4","ebbde526ab56485a127a26d970a5390772b35267d9c4492577182de87f5960c8","45942b72508374eba54d133bd9c8701920fafee0dc40591815996f0e4be37bc3","3b50334e31bafa743d3740b8f1229d51c302714eb271a259c75d90334a95b50a","d56a3e42d90942eaa7c697cfc7c41a35b289515ae2df7ead368618fb88a392af","d7bef710f263a8b51681fdd83004e5413c20398c5c8f4162b37991f662dbea7d","35376432ad05a2f37b71bc1bc4f3f4ff6ea59a2a08abba9e027d6acc09fffaa3","44ed88753d653c03f43f3b572bbe49dbe74c3d91f9ea2ea9cdc26c6ba1b387cb","efe75e9137d343187b4572ee29bc8ce880f349c40d591ada14c08c69169d9f14","408ace56142a37b863dbc79f7a959723e7bea5e816119a1d4389d934cb80968f","b0642f6d045b38fab4ad7aa4f43aee2aab08d7e5a86e92746af9f0a9977a4c3c","82e0aa13d8e9554da63a56e8ac98f28374d9a06ce0b8f1528341fb9ccd949015","757364e5ad290ce142c342e695dbb69fc7fafe6dec5f85f1c1360417b164dacb","84d1b236c493a8c26229b18f82c81a52335021d0d12bdc15f46cb4deda932a2f","ae114379aa7748d82cbc4244c6e115648caf3e4cc076cf951782743df60841cc","96a8729d31e421275176e72718bfbbf61f234563838de45a19a56d20a2f73c00","66201603e0364d3655fbce9f480fad869f4dbd131885f3999dd1f1a739f608fd","4ad16d32ec2acd0ead0b95e3ede7bb0e2ab9ce5348d3129a04a8b3b729bfff02","9c086df46aea6d6fee4f5f935e3473c36e9a20fea5c0571f5afdbf0c49080c80","09e78228130d52589cd56b7434f8d4d879de0c3aae51b986048bd28850298c4c","4e4d31ecaff80608a068bea92b6e6f2027b7fa592031f3aaaeead0da669e87d9","d5b3babc67aea3f593d75134c376c85b04dd3a81c36de4ccbcc10b46caa43eef","f06a75d2fa9e0f3446b843a243739c15fe84da23861c76b52affc23060b5fa95","a61443c646a45e836a2cfcd55b643ca6f3dea27340545030155db950062702a5","4b0b60f68ea27ae4d7177474b3be9b5ae1bb2641cbbdb4e7bf9e828d327a9777","3983db39cfee9e757d308a61b30dc26897f11a38ec5b72223755d5bb08c7b82f","a647b42408d702c123e8ef88ac0d5c81e36c0492d8d75930df562a8a165da64f","ccbe3b958b861f58079fbce48c32d1e8fe7dcba1864a6ccf1643de4679167cd9","afdf6feb126384c5cbad2dbcd816deb6810dfb998ba9671ec0d16aaad544f550","4725c5e02f946f0c14e2941949f10bf1f1ea0f01eea7e6d908104264af36cab9","358550b7ca1af5723f452cb842790dde27873349e1420ba07f522817ccb15f80","c7e2bbc438c5ef7298269c3aaeae3c7427e5897d135f8c972076c33373afeec6",{"version":"00ab47de33ee184bb2c51523354676486571b82bd198426e11d5b428a1aa4168","impliedFormat":99},{"version":"000f451efdd9f13e5843c41df3c0829ed7afb2a127403aed67a4a2bb91b6c944","impliedFormat":99},{"version":"a2e3deaccce3634fe345f14783545cd459dc56e472ea8c6c9d930d398660463f","impliedFormat":99},{"version":"6ba2ad4fc1f01a5c3860f155159431d11397f21069c65c3cf4ff658c1c0627aa","impliedFormat":99},{"version":"22a297185ecce7e4ba97a0f0513ec97b8a088ab0457efb14319f11d57a7f525f","impliedFormat":99},{"version":"88f49951d48140cbc3ec84e41614bdb518f9082be84ebd18903b77b99441f17e","impliedFormat":99},{"version":"32c18d1756158bb903d1a011506c3e5e21bcb68f7cd3c5e7396bba3fceb25151","impliedFormat":99},{"version":"a05d9ca4455435971b38541e77886bf6f5af2554b9ff8b7a097f5cbade758046","impliedFormat":99},{"version":"d90c1e3eee91af3ded5f5384ae6eb641c378c5648bb6bc6257fbc8dd2c1c8c96","impliedFormat":99},{"version":"2915824b3e0bed452e922cb099e62470c9d5ae9aa53bbf06901f23ab0f37ecd2","impliedFormat":99},{"version":"115d81a2b337c16899004df26633823c621a4f8364ff570dae48d6ae9d809c54","impliedFormat":99},{"version":"97b87fc983fedbb257b4cdc7f647baf584d739537b34a48a3390a11f9722c0cf","impliedFormat":99},{"version":"88c59a729c1ed583ad8b1ddb0bac63b3f447549ca3c2d73e69156469517f9125","impliedFormat":99},{"version":"7df2b79f6bf94df6aa981e1379f2b310e8324cf295ea2948aaee888897aa4769","impliedFormat":99},{"version":"ab30ec668c9d955cf06a42845e9771f0f0ed8ef602a11c47ec2c5d5ef0398765","impliedFormat":99},{"version":"753bdd93ea367cb0c7403fb6870ffa490debec776e083c914556b7473427154e","impliedFormat":99},{"version":"437fbde858c47bf660528b6db20ecc3dbc791ef51a1291722866a632a19c0271","impliedFormat":99},{"version":"928eea6afe245ccff719646bc28e6edd3394ed5ecfe5596bfad303a7a4df6a45","impliedFormat":99},{"version":"2aae03df240704ffb8757f0cc1c1baf983e530ac569cb6b13b12dfbf84a5ba71","impliedFormat":99},{"version":"c446ecac8a295717c2d3adc324c8eb1e92a91f8c99a063e0a4e49b2f02d01013","impliedFormat":99},{"version":"15c951bd74da5acbfefc5d176da585ee7c8d3c316dce9f13849616ca53b3ebf5","impliedFormat":99},{"version":"ffb9754b2742fc9e22e3c6e3bb68d69dd115f7bab5cb599e9b4a14ff4eca7a63","impliedFormat":99},{"version":"79b83b997a521c7046358579df4b549782b97d2be393e299dbf60d777e05010a","impliedFormat":99},{"version":"983e18a6895166e3c031feb6e13dc6d26421d0669b7f23a59cfa5c62c58ca75d","impliedFormat":99},{"version":"361b153d2e9ef31f6e1a8b0b75f085d4ccad8374d2fb458341b9980d2fd6cb16","impliedFormat":99},{"version":"b1fe78ef075d86471435f93fdeb966bf0f3c943465ba275ba157918b60d17d9b","impliedFormat":99},{"version":"374007605674074e4f06e482f972e73add2f431303399b0bd620f7feeae38421","impliedFormat":99},{"version":"807363750352e25dace9e19e863be429ac90e741cc608c757ef2827bfb9128aa","impliedFormat":99},{"version":"3bb582db180fb819393facad64b67997c85c77bc6d3d5891c2ef9327541720c9","impliedFormat":99},"13dd46f324998e8a76b33171968dc208c4c4faa58af426995a606794af9a0de9","b1dd0a3aa01781b901e85531a689c333b886d47d19339fa4de1952e9bde0e767","78a12971ff4a73cfdcfddc44c19dd9ae0660967c6a335a12a3c12ec27e872ff7","d588136b5eb6b767742196b5a5913f8bd813a7f5819b89033bd01767885ecae8","39b2ab5c45ae595269ff33cc2a5c28f525f93433a4f81c89081950cff20201f9","384cc2ec841a0cd6fa3fbe2330539d966f83d8da9a7deee2c9313bfc148d1b62","578407c14ad218b2da895538b96e44923b911e762a8d865add89a800a434c942","521a6b7096ee666e96f4089a1d276ea98c6265142ca321b5ffa3b2f6d618edb0","a69776aed1075d326a31d43a325c51e07400911bfeebc0177b64a2e18153f367","2d9b78b2d1eacc3d4bc42f5077899be6261e3c3d23b9772c2232097fb276ebc7","510be7a694bf2d9d21cc626945977bb2441239c20c8011315d10b68e8dc53990","0487552a151f6141c4e3814582da48deaaa32125c317053be7d2194e382590e0","ff21e0ddc4cbbc40011bb3a2c96ff5a18523e74a35400ccbfa7a7b300c115f3b","500e7b40b26d79bf4110f0782645da4776ae9a7e429b328c33412bc26942d647","d1f85990ef22e5ff6e13a92c2b03f1b553e8743d65831c4a0b852349f574d9a1","e7b5a908fd76338d0789b4d7cd816db9d0ce2dc58877d0b9e9f9d48100240f0a","df55857c7b4703c3da4e6fd5be567c7a8917e8b12ffeaa098a74ab95ed759284","63e222b06f40abf74a1843d68dd3131629ba8c8729f752d8b7d089ddd2023c84","49f8ae6cf942c0261c2e8dc58e5a0cc74f8cbf33d010ff8344e6fe7b39633a7d","c1b13432d085226dd352e2f6777a952c77e362056fea17241ad7441b6b2aa9fd","c47a235ad8798b2700377efe112009738f729886f21a2cd03d767b16d44346c6","610d491980b66580c2ee33c5c2ce9e529d078edfa4a2f72af4abde13e409b1f8","b357c3ea2923350ae430e64b34b9b864b5be160bbf00469101e15742e451b838","b561710378060fbf249d86dce55c7de6dda63e1d4fff3fd98792f36f8957d7ca","1fd2b2d32c9936de300a8c99b0a14c0e59b674d2ab8fd03ded676efc42d150aa","bb61050f51bedb84d906d854c1847d3ac8e0d996ece1d0ca510976d9f864a0c9","f437b76d4cb35d15fb2183a60b18e54c2cfdad15515f753613ea8f982cf67660","fa36310c37b2e1594ce14be99cfeb5da9b380cfd236c299ed9b9e94234688454","40bb8881832a6bba6804b56a44a7b7f544b0a899d184be67caac6c3628c365eb","fac6dd9567113601aaf7016870e0d4cb6a70f84df2787b435f8d718a1a8e6537","4f409b0bf4970b845a5bcb2a1d6b6dd9e772340b399e79e2b9e6af7786f5e72a","a45533efa92242dd84cc1bae0ad3e02640b123686d24f7649f9c0cbe44cf9770","1f1a01e0160e1dc12fc7f59c770a0b9e8a90b8ff4232b93448096628be3d10a5","2d7a250881993ff8a094d4d24098b9e62e68ce88b3710c3ff13a6321c96c56e5","fb4c238b079b67b243c997d402f7e6a79a5eef7ae7f26228d7eee4a857f336b9","406c5b7d6fac83d55e4482b7687aaca5a0306c9adcb88063603499455357be80","34eea1ea1341fe950ea5d92b9ab8fd82abfb2940f57f9a808c646aac0ade2964","3ba3b97f66debe9828fe6d4268a7ab4b5ffdeafd694b211e0a62b1be253cad91","c231d7b3e0dd6cfb7d9a9e8735964035651d7819726bb89281999c65b63a8144","fba348de71b078083b0119207edd84a6d0b4151fab8a1a410d829b26190ba601","1a2193c22e2d164869ba0c331c46020182f0abddf1599d0ee2b8d70fbb05bb24","3549b967553d00bf6f0630999ebea40d03839546560990798e373e7c0625b128","ae250d0479fc08f700d015b67a3c667b4841a98d046c11aa95d6b78a459e0b33","027c12c4439ce81febea0bb4941a847d03a5828061e33f1ecdce6cb81a35242f","28e7c3102cd41f5fd057c7fe54df6f519babf36f37e2e86d2f5329c314784a92","faa992f269e96d75c11adcccee59b7cd30813ebac6d446f1f29a1a5ec631523f","9dd8e24358c79b5c49d9fc1e95f1d2f205f26bb8ed2b127d9302887f9fb95e2f","02f28fd03247034582967fbb07d4971239af7931ed6fba6b03584376927bd6ed","1c8fd010c22ab8c860acee6e7e066def27d00da4926e844438f4f7563a715a3f","4b1ec9c4adf25c810167da6c31dc02eed12925466f938c8a7c565e72cb8eb96a","3eea866560f1428034727de4261251cced14a3e7a7664ccb0a3b23a1b52d8f35","ecb91b4502ebfbce55bb9591a8d305e1494d7906cd59bd8c3266212e281a41e3","0b02becf28a33669681ea559a0c7037fb231047d9fda1dae7bda4f471e88ae5c","1d50cf7b3e2256f8c84ac3199d35ed19c3bcb22fe07b74bd243efb151a04fed0","86164253dd3059a40be724f3ff76dd76fc9d47257f5b021987f5b1ab5e515297","0b26a647b9b15991c29ec764dbe583cc9301af172df5fafd286d681a16659202","cb4f4a8012f4cf0b6ebafad1ae3e8d6f6ef19b887977d48aebcc8ed064086509","970ee362f17e136be88d4c9b03597afcea3c000bc3c0e84312c5b326d8679f97","35fd9428d477f45fad089b13649ac0e5dcb79c88642aefcf84f1587da8210783","a880b697e53319e202273121e82cf8143ec4154d1c91df7919f13deb1d9a696e","faf1ad71edf7196334c983ebcb51ab90d88773771005163b11aa0bbe0bbe8c3c","5581f122a0ae640ef8b10d38af977f8f6440d7e7cb8ea46ba7cd195934550f86","1e674f4bdc55e3b93bfe43a617013f2b079832d3df363e0aadcb64164b82b808","36f77422f23c0f9592042571e3f2aaa4f3dd8d98e6a990cc4eb1ad8e0046f812","2df8a6ee83d5cf1b0436563cb1e4dbe2caae4da18f23a11af8e9e0849b9b1a1b","c0f59866e888a4f8c26fed7606b994135731cd8ab9ff38ea2d36388aa9661e03","2f295f45a8848ee3649e8a82b61435e36da505679abddfe6fb5000dc7631af6f","75de31c5ea26996ad3553307d44da9886c4e014b8278de54a2a37423cd04fb96","789a12ebfb74e3c03c4cbe25e593966f6ad201f96592f7c5312a95e31c1f1343","a2b8b1a690220f33174918aba61da4df373a3fc7954e2d68b2182e8c808921cf","891bcee88cb76f473986e6a149fa6a24ad59a6b21530f7189eb33cabc5b2b25d","508b6eeac92b9bc3f8ced07e932e418055656cbce27a0d8b91ac2785ec2d748d","59b965e6b8a6d13ca9562de3f9f3e85d09ed36de601c63c925181206df91d4b4","9bf7a822a5bfaa4a7b0aa2330b2451566f61382ba1e7a9f3ffbe47fb3d662789","324a81d1b47bdff3a9e6ca7520b5659f4dc108fe5de1fb2d98cad40a14fc3764","c011da8d6f3e34b736f9368a6bbef32f65269acaf306561e79d21d748c7d8960","94f9e2909648d2e9659b2d69011fa16378c052f33e66c7910a242bbd04794003","ff74f51330a642065d94671e7d7a00ac37540a237218859328deaccdfae398c9","814a760d561991a46bdbff6b13207fac67e8fa989d0b77ec4fe72a908061dbad","1b70a1a7f3f3bbd99ffffd8bfd9b7329721c9f8397f48928b905c5a751867df8","bb61fc7acc0f401f1b84e01e4b8b1de05a2004aef81f798c0a93e70351db8d45","53907353cf8064b429b54fa2f91c81c8c1995cb36ecf0237c309fe298aeac839","4248f6f6317cecbbcfed2dd95dd8825a2742efcb0e82f7efe897af30da53c5d4","f207bd3c102b366e06600a42d5689bbb7991277a9b0a8d0f658b13d41e1dd6c4","2310e4bf9426471e5537495ca4e86616be2362a9d860f7e4d72b294cf41228db","aac75cc377b40781fd877defe3ebb30b670299fda9c3fe9513e6c026ec026e34","75b3a1a8a81484522f3989749d11871de311c97e9b16221785224c3b9878887a","b7c02d5dd8578446b2607e02c98b44b8c278ea0823337d8b24d412fbaaf92248","e7670c3b89491bc53e67538edf2f3f425ee0c207d6319aa43a2669069754d1ad","110d527fe007ef32be116e0b3f94ee395eed490140dd758352e800065e817d01","53c4cc76486b725072ec007f2b4072f90b33d02c4167933cbe0506215634c3b2","280fee3e33dc6db26f1d98a55bc8fb8deca2a461828bd28bb2595cbb03cdeaed","31a0479d0619bfc3fddeb5eab77424a97ec0197af0648974ec138b0b0eec03a0","49252c4078b619d8d2c0d69f9b923cc3a2a9ba4a78088ba2160636b058dff4e6","daa43aa55c8dcfb31b4793d95e0ba0c3f6bd3130181525566aa9c8f8f9880fc4","d626bff8e277358a4d4503ae2fc959e5a996191dc14e71b4454476be0f4614cf","5a3a73da99e0308d1e3d3afad6aebfc5430ccf4a4e259f329382cfeea466b9f1","5922780c6a2eaeb122261fc903010be5270a377a229f41ba1cfc949ab3641e39","bd0f5a44a2ae464fd6b5a649848088f5c773ea9a2c7ddbfb969a1d449c98036d","ff9fd5db60913019f048767bcae5f6145ae8a4680572b079cb10070688239295","a9b6684b0887b8eec0573bd338ad9d48218b44316708eea83f3be82b66fc0cab","17d0e176f677bff0552937187e206286fd12b32102e998c202faa1fbc6112afc","3ea31f5f6a12682cbd0ca3c8c5e3c2d5292335fe99ca359b4ffe0d3f6a275600","f4f3b5e2ecd2d0b187c2c6dbe47fc1fe5975fe55a526632d4c3db3053378f3b4","25c30fa1662df9defa835f78b6deb815c45e4350144c4c00adfebbbf0ab4b722","b67ee5cdc1268e21d7b268c3439460dde6a530dfe5f64b6cb4a29a5b05d5687e","b881dca47c8bafe3f3f82ac2fe93357ed46ab391a0b318f2fc9b6ca7b8d6d9f7","0a1d0773a603277a873433b0a035dc79e9bf57fb580b7dc15c830ca2ea5dac4f","d4afaad0ccab84d2ce2a9a28e57ef7aad3404e94faa2c03dba29d1299bcd4cba",{"version":"b2022d0032b249ce6bf470d30d6c4affd66ef228f32eea5f60750bebdcc1c404","affectsGlobalScope":true},{"version":"2cd2fd90e3c5b835f4548e6a7779252e9342ee172e3c9bc50a7ecc68b56f2324","impliedFormat":99},{"version":"98fc53e95165b9fa08da3d4fbca2cc4996e61491dd5f0452daac7e52062eb7d8","impliedFormat":99},{"version":"6806908726f0021341085810505fd0d425851cbfaa3c25c1aa6b164874347ae1","impliedFormat":99},{"version":"cba519c30d826f1bf43475cc56f66fdc8b721adfd93be7ead3c0c0cdc7f327cb","impliedFormat":99},{"version":"1a0f7f175bb97e80465b4d9f655e9bff7b0123edc981c57c4e484442c565b96f","impliedFormat":99},{"version":"92ac597438cc1c722390685e4ab83a7c69458f35f6874479c02149c3f7a731ef","impliedFormat":99},{"version":"12087e4372c3da80593dab47c55b291d8218bd586a74009b5050b9b3a92cfd4e","impliedFormat":99},{"version":"a31dd2ad59cb7f1a71f2d8832f8b3a5d8d074491a651a2e976f4859bdc0f46e6","impliedFormat":99},{"version":"ab2ae761e05172c8a548fd0a05f63298170f1277a2d08de3177758cba83de533","impliedFormat":99},{"version":"9dd5eb4a69ed29b1cc94bf8b57e92f4330a42beef4bc626c91f6315d21942933","impliedFormat":99},{"version":"7ff889d7f41877b25b9c11ab6c4c5710db3a1ac8e21b7e7afe99896c092944e2","impliedFormat":99},{"version":"f6cd73df2d6e80628b81cb633e74d18b06d1163024eddbe88c56184134231ad6","impliedFormat":99},{"version":"dc8ff648e8af469521a0a1b90d9731ce83302196532aea874b4409f1c2026766","impliedFormat":99},{"version":"aea6f58bf8e04fde1a256c45fc06c1f0a71668593b86f57539240274a5053495","impliedFormat":99},{"version":"14a773316aed97b2f995c10e3eea3eaabc1e0023eaf71b7ebbe267ae1d38acb7","impliedFormat":99},{"version":"d1cc503a4ec680f481ed61cf27f7e4c7215c4c6128b83539db6589c49f82307a","impliedFormat":99},{"version":"995025b2cf84aed17b47efe478b4a61e78be4c5913759a7ab03be7e8b1541406","impliedFormat":99},{"version":"7d3372552c8d5de509261287bf6288b2baacbcb384b6ad12a27d53e4bbecb3f5","impliedFormat":99},{"version":"cfa079571a3c039ed655bf5c72d1d9deffd6cb626c8294c167ba70ae84c27dc4","impliedFormat":99},{"version":"349a2243a2397e5f6cadd501f4796be48df69485d9a56f04fb486a60f8c757ba","impliedFormat":99},{"version":"4ad304a4f4491293ee17f3bbd93b8a32f1eeef9d5b96378ee10940d262d0516e","impliedFormat":99},{"version":"ab5b87e1ce81e722a0ad6629f8b0b18c9bf985b05a320a1f2fac98efc4b9888a","impliedFormat":99},{"version":"80cb0b7699b356b8fbdcde374a32b06260c40e2741b976ac65c82dae16691940","impliedFormat":99},{"version":"9c57cd7154424347adc2279f56afccf610d7defc174baf6637a8964d6136771d","impliedFormat":99},{"version":"b94a527e36c9f635f170adf2d9a9884f31f126a554b543f61570ccdd3d33e08a","impliedFormat":99},{"version":"1b54dcc0515822e9863c332b28f6479b977ef7a3e7d367290b0ed5c1253b9e1f","impliedFormat":99},{"version":"188acf2048c6df970211536efc8aea430aa6ae4ef7f922ca79b59f3b7cb091e3","impliedFormat":99},{"version":"bb57b313a0ad4e46ca01bc4d367c7c039af94ea68a34ee17a4fa1fd099b31418","impliedFormat":99},{"version":"376ae60eb6f00fe1d0755370181fce27d83dadfe96c324dc8ac2e4648b1304db","impliedFormat":99},{"version":"d12a7e4e80678e3e8f2cfe366e44b7ab86fce3a2cd1ea798113638f26861b7ad","impliedFormat":99},{"version":"f64758f30015ed71ad199d80c01197121168d7de53085d6008a453921464a43e","impliedFormat":99},"d2c8d6f2776f10cd92e1e8258ac69e82c81cf2d6fc74548cc8442d29feec230f","ee8fa9496ef9108c968157df7a236ce30030a13b6541ad2875848e4716b94301","f19641a3995318b16040c9cde448141dbce069b73fb91793c074ca513dcbbd11","99c0a81baaff8d3bd66120b8851d201b4e7ba5a69ebcd4d8a1f63d65b5a2975e",{"version":"78a467286ae9c68076afad97a99d9a663ae4bcbd4419d45780a68d0ae3242d2b","impliedFormat":99},{"version":"207a41bab3547f0e2a2b7e48a4391cba43b7fccd433bf378925bd585ecedc88b","impliedFormat":99},{"version":"5564b295f11104bd9624820015d145cdc48020dc9906c23c3b31725a3f01f88e","impliedFormat":99},{"version":"4e368f0c517cdd4bd7606e3f724c16610056b4a31124cbba41bebef49aa90447","impliedFormat":99},{"version":"f654c57e86e665b1433c72e438fecf604783ba11e19d6d8e0234e882f2169972","impliedFormat":99},{"version":"4028796c875e2417c5000b124a084ae03c3dfc7f9d2baab00b8818aeef34a144","impliedFormat":99},"b41e99b960e60301525ccfd87d7f25d0fdc1431f940cd962d63ef273bec0e14e","6aa10fcd500bc7e4923f3cc98ed4ff37b2170680afa19a79596aacf00f4e15e9","0b95ef9123adbd8bdeb60b2ac49d6b27cf658ecea51be83245af9d37cbcc79c1","5b03cbce66ee91c12382fa03e95e51673aeff1b093ede702021bf3ab64d08022","739be986bd27b7ca2abeae858dc447620dce45a682384e66432ff37b41ef97cb","48a9cee06adb94d932de0da9e46f8c78baacd2b233a7a44ef2808384ce8efa56","55173940e44941cee1a378668ef649f7a922a8b8b9a8342e35e325ff68fdf055","6e440df0377ae1d4b159dac1d27e6df175805345a029bf7fb48f8219b1535e7d","9199e75e3e5564dc91cedbb61045959a3285f703511afa88b2d21a176f9cc120",{"version":"d237c0b33ed92bdde34c82860fb585083af6d2e2d3d6616ff5f348fda76cd338","affectsGlobalScope":true},"fffe4809beefdeccec8d3f756485ab44d503ea718d5bda6cfa0948dfb51a9129","04727b2a4a1b8c9bac9ac52863d38a85d0242f00cc2501397b3f37f9d0267c63","88a73c58ca75ff2e43a3c4fa019065c218709da80c6809a25657b213c8604930","98454efca5589f3d037912272e719fcee67892c2664c24bd687b99a640c8a2d1","5fdf147f471dbe79527802bddfced92e2467fe3b815425d7d24f1753df7e6e85","afbf8ff733ba18bf7a9f02578eb8988f90b0b912435b418afd6adb51e5045d68","093add21cd683a3a340cfe143b6f6fbd5cff35acd5fa35f4d8f15751651fcfe7","71b7d99df191d016182df2c00cb38046da50ea61eb2bc500b529bca94074081a","e5624bd86a2cf18450cdca00427c0f5212ba064efca87bc9f0db40d7d0c2d217",{"version":"15fe687c59d62741b4494d5e623d497d55eb38966ecf5bea7f36e48fc3fbe15e","impliedFormat":1},{"version":"2c3b8be03577c98530ef9cb1a76e2c812636a871f367e9edf4c5f3ce702b77f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"5221b4739633a57c4251be880c0626c78391fe3ebf568f2f6ff5e4df60e71bb7","impliedFormat":99},{"version":"26d51d186bf97cd7a0aa6ad19e87195fd08ed72bb8df46e07cc2257b4d338eb5","impliedFormat":1},"1951ca159d21a8cd27a602782779409314db25ded4026df1c8a9e7aa402d57af","ba6978a2092fb70a30087c7de6c174394364fe687510073f9b9ac8f1c5732a71","041b3302aef4af7673bd5dfe9bd2557a7f3f8539f7e0160e3aae3a7d4b6e27ba","501a639e8dd5baef4f0fd389ade176d520e3b882e37ee9ad21287208e8193d3e","313a1914e4f7447da0b3adfae979cfab3de1296716215e179b3322c28567bf23","abeea35050c134d30c3a0b21406effcbd437594353feed08d0ebcbe9c766359e","98aa1eb9b08a46337175a49811b20f918a15c1dedbcff6860ac060019ecebfde","d152eeb8cd29d149c2e735822aa947f9b374c51f385bcf27aaa2b37b1be54471","0ecc78a0e4bf7fcbb0696fca250150f60d2c48732fae0d33aaf5ced1fc683683","50d9368953ace37a11e378021f8e2da9339b69e0ccc40e6733b1f34ee789bc1b","89693732425ec3d8742dc67628a5b17f7743a842ab407beb434169075801c4a4","b31ec9d538849d5d08858a92c6be579112daa41cbc32ef4a8aa025b3f29defb8","fe12d30bde72fe3126188da2f1b37a30c6d890d03c1d25104ba22de24a8bd161","2743eb304235f239edc067dd73cc8273a0375a1de60f10272547f3d9f57cdb64","ad6fcbac18fd1943f717927100e9b4da24a520a7ba694c501c9ecd7cdef234e4",{"version":"381d27c35f5a5bf6c09dd238ec26fef30a03d12ea84589c621ebc208d7dc8378","affectsGlobalScope":true,"impliedFormat":99}],"root":[223,224,[349,359],[520,543],[545,569],[685,770],[817,828],[862,906],[935,1054],[1084,1193],[1225,1228],[1235,1253],[1258,1272]],"options":{"allowJs":true,"checkJs":true,"composite":true,"declarationMap":true,"esModuleInterop":true,"module":99,"skipLibCheck":true,"sourceMap":true,"strict":true,"target":99,"verbatimModuleSyntax":true},"referencedMap":[[718,1],[719,2],[742,3],[743,4],[745,5],[744,6],[746,2],[749,7],[748,8],[529,9],[769,10],[770,11],[540,12],[539,13],[538,14],[765,15],[766,16],[541,17],[542,18],[764,19],[763,20],[738,21],[737,22],[537,23],[536,24],[535,25],[534,26],[532,27],[531,28],[723,29],[736,30],[725,31],[729,32],[730,33],[728,34],[731,35],[732,36],[759,37],[762,38],[733,39],[734,40],[735,41],[357,2],[224,2],[717,42],[714,43],[533,44],[358,45],[715,46],[716,47],[817,48],[356,49],[355,50],[352,2],[756,51],[721,52],[359,53],[767,54],[739,55],[543,56],[350,2],[828,2],[863,57],[818,58],[351,2],[819,59],[353,60],[820,61],[822,62],[821,47],[824,63],[823,46],[724,64],[768,65],[726,66],[825,67],[354,2],[826,68],[757,69],[827,70],[760,69],[722,69],[761,69],[758,69],[720,2],[740,71],[741,72],[867,73],[866,74],[869,75],[868,76],[871,77],[870,76],[873,78],[872,76],[875,79],[874,80],[877,81],[876,76],[878,11],[879,11],[880,76],[881,82],[548,83],[549,84],[883,85],[882,86],[884,76],[885,87],[864,83],[865,88],[890,89],[891,90],[892,91],[893,92],[894,93],[896,94],[895,93],[900,95],[897,6],[898,76],[899,6],[901,93],[902,96],[903,76],[904,97],[905,91],[906,98],[935,99],[937,100],[938,101],[936,102],[940,103],[939,99],[942,104],[941,99],[943,76],[944,105],[945,76],[946,106],[947,76],[948,107],[949,76],[951,108],[950,76],[952,109],[953,110],[954,76],[955,111],[546,112],[545,2],[547,113],[956,114],[957,2],[958,2],[959,2],[960,2],[961,2],[962,2],[963,2],[964,2],[965,2],[966,2],[967,2],[968,2],[969,2],[970,2],[971,2],[972,2],[973,2],[974,2],[975,2],[976,2],[977,2],[978,2],[979,2],[980,2],[981,2],[982,2],[983,2],[984,2],[985,2],[986,2],[987,2],[988,2],[989,2],[990,2],[991,2],[992,2],[993,2],[994,2],[995,2],[996,2],[997,2],[998,2],[999,2],[1000,2],[1001,2],[1002,2],[1003,2],[1004,2],[1005,2],[1006,2],[1007,2],[1008,2],[1009,2],[1010,2],[1011,2],[1012,2],[1013,2],[1014,2],[1015,2],[1016,91],[1017,115],[1019,116],[1018,91],[1021,117],[1020,2],[1027,118],[1022,93],[1023,76],[1024,76],[1025,119],[1026,2],[1029,120],[1028,76],[1031,121],[1030,76],[888,122],[889,123],[1033,124],[1034,125],[1032,126],[887,124],[886,76],[1036,127],[1035,122],[1038,128],[1037,129],[1040,130],[1039,76],[1042,131],[1041,2],[1051,132],[1043,133],[1044,76],[1045,134],[1046,93],[1047,109],[1048,135],[1049,136],[1050,76],[1053,137],[1052,91],[1054,138],[747,76],[1085,139],[1084,140],[1087,141],[1086,76],[1089,142],[1088,76],[1091,143],[1090,2],[1093,144],[1092,83],[1095,7],[1096,145],[1094,146],[1098,7],[1097,76],[1108,147],[1107,148],[1109,149],[1102,150],[1101,151],[712,152],[711,153],[710,154],[555,155],[713,156],[559,157],[556,7],[558,158],[557,159],[568,160],[567,161],[560,162],[566,163],[565,164],[564,2],[1105,165],[1104,166],[1106,167],[1117,168],[1116,169],[1111,170],[1110,171],[1118,172],[702,173],[701,174],[569,175],[703,176],[707,177],[706,178],[708,179],[699,180],[698,181],[700,182],[1124,183],[1123,184],[1122,185],[1125,186],[709,187],[705,188],[704,7],[1103,189],[1113,190],[1112,162],[1114,191],[1115,192],[1121,175],[563,193],[1128,194],[1127,195],[1129,162],[1132,196],[1133,197],[1135,198],[1145,199],[1142,200],[1134,201],[1130,46],[1131,46],[1144,202],[1143,203],[1146,76],[1147,204],[1148,76],[1149,74],[1150,205],[1151,80],[1152,206],[1153,76],[1154,207],[1155,208],[1156,209],[1157,76],[1162,210],[1158,211],[1159,212],[1161,213],[1160,76],[1163,214],[1164,99],[1165,215],[1166,76],[1167,216],[1168,217],[1169,218],[550,80],[551,219],[1170,76],[1171,220],[1173,7],[1172,221],[1178,222],[1179,223],[1180,222],[1181,224],[1177,225],[1182,7],[1174,76],[1176,76],[1175,83],[1184,226],[1183,222],[1186,227],[1185,228],[1190,229],[1189,76],[1188,230],[553,231],[552,80],[1187,74],[697,232],[696,233],[1191,234],[1192,235],[687,236],[686,2],[690,11],[691,2],[695,237],[1193,2],[692,238],[693,2],[694,2],[1228,239],[1235,240],[1226,241],[530,242],[1227,243],[1239,244],[1238,245],[1242,246],[1241,247],[1236,248],[1237,249],[1240,250],[1243,251],[862,252],[1225,253],[688,2],[1244,254],[689,255],[1259,2],[1245,2],[754,256],[755,257],[751,258],[1260,162],[752,259],[750,2],[753,258],[561,2],[349,252],[1246,2],[1099,260],[1247,261],[1249,262],[1126,2],[1251,263],[1250,162],[727,264],[522,265],[1248,266],[1139,267],[1136,46],[1141,268],[1261,269],[1140,2],[1262,270],[1137,2],[1263,271],[1138,2],[1119,2],[1252,2],[562,2],[1264,162],[1265,272],[1253,2],[525,2],[526,273],[521,274],[527,46],[528,275],[520,2],[523,276],[524,2],[1258,277],[554,278],[1100,279],[1266,181],[1267,2],[685,2],[1120,2],[1268,2],[1269,280],[1270,278],[1271,281],[1272,282],[223,283],[451,284],[450,285],[362,286],[460,287],[463,288],[469,289],[472,290],[493,291],[471,292],[452,2],[453,293],[454,294],[457,2],[455,2],[456,2],[494,295],[459,287],[458,2],[495,296],[462,288],[461,2],[499,297],[496,298],[466,299],[468,300],[465,301],[467,302],[464,299],[497,303],[470,287],[498,304],[473,305],[492,306],[489,307],[491,308],[476,309],[483,310],[485,311],[487,312],[486,313],[478,314],[475,307],[479,2],[490,315],[480,316],[477,2],[488,2],[474,2],[481,317],[482,2],[484,318],[225,2],[334,319],[336,320],[337,321],[338,321],[339,162],[340,322],[335,323],[346,324],[342,325],[341,326],[344,327],[343,2],[345,2],[1211,328],[1212,329],[1198,330],[1204,331],[1208,332],[1195,2],[1203,333],[1207,334],[1197,2],[1199,2],[1201,335],[1200,2],[1206,336],[1202,162],[1210,337],[1205,338],[1209,339],[1216,329],[1217,2],[1219,328],[1218,340],[1220,2],[1221,341],[1213,329],[1214,329],[1215,329],[1222,342],[1223,2],[1224,343],[1194,2],[360,344],[347,345],[908,346],[915,347],[907,46],[914,348],[916,349],[918,350],[917,351],[920,352],[919,353],[928,354],[923,355],[921,356],[922,357],[913,358],[912,359],[911,360],[927,361],[924,356],[925,362],[926,363],[929,64],[930,364],[931,365],[934,366],[910,367],[909,2],[933,368],[932,369],[519,370],[504,371],[503,372],[505,373],[509,374],[506,2],[508,375],[507,64],[510,376],[511,377],[512,378],[502,2],[518,379],[514,380],[513,2],[515,2],[516,2],[517,64],[1080,381],[1079,382],[1078,383],[1077,384],[1067,385],[1066,386],[1065,387],[1064,388],[1083,389],[1076,390],[1075,391],[1074,392],[1073,393],[1072,394],[1060,2],[1062,395],[1063,396],[1061,2],[1068,64],[1071,397],[1069,64],[1070,64],[1058,398],[1059,399],[1057,400],[1055,401],[1056,402],[1082,403],[1081,404],[1232,405],[1233,2],[1231,406],[1234,407],[1229,2],[448,2],[220,408],[217,409],[218,410],[570,7],[627,411],[608,412],[571,2],[572,2],[573,2],[614,412],[609,2],[574,2],[575,2],[576,2],[577,2],[615,413],[578,2],[579,2],[580,2],[581,2],[586,414],[587,415],[588,414],[589,414],[590,2],[591,414],[592,415],[593,414],[594,414],[595,414],[596,414],[597,415],[598,415],[599,414],[600,414],[601,415],[602,415],[603,414],[604,414],[605,2],[606,2],[583,2],[610,2],[611,416],[612,416],[585,417],[584,418],[613,419],[607,2],[620,420],[622,421],[621,422],[619,423],[616,2],[618,424],[617,425],[626,426],[624,427],[625,2],[623,428],[219,2],[79,2],[361,2],[137,429],[138,429],[139,430],[97,431],[140,432],[141,433],[142,434],[92,2],[95,435],[93,2],[94,2],[143,436],[144,437],[145,438],[146,439],[147,440],[148,441],[149,441],[151,442],[150,443],[152,444],[153,445],[154,446],[136,447],[96,2],[155,448],[156,449],[157,450],[189,451],[158,452],[159,453],[160,454],[114,455],[124,456],[113,455],[134,457],[105,458],[104,459],[133,460],[127,461],[132,462],[107,463],[121,464],[106,465],[130,466],[102,467],[101,460],[131,468],[103,469],[108,470],[109,2],[112,470],[99,2],[135,471],[125,472],[116,473],[117,474],[119,475],[115,476],[118,477],[128,460],[110,478],[111,479],[120,480],[100,481],[123,472],[122,470],[126,2],[129,482],[161,483],[162,484],[163,485],[164,486],[165,487],[166,488],[167,489],[168,489],[169,490],[170,2],[171,491],[173,492],[172,493],[174,494],[175,495],[176,496],[177,497],[178,498],[179,499],[180,500],[181,501],[182,502],[183,503],[184,504],[185,505],[186,506],[187,507],[188,508],[348,2],[1255,509],[1254,2],[803,2],[804,510],[805,511],[808,512],[807,2],[771,2],[782,513],[777,514],[780,515],[795,516],[784,2],[787,517],[786,518],[798,518],[785,519],[806,2],[779,520],[781,520],[773,521],[776,522],[792,521],[778,523],[772,2],[500,524],[98,2],[582,2],[1256,525],[83,526],[84,527],[82,526],[81,528],[449,2],[813,529],[815,530],[814,531],[812,532],[811,2],[684,533],[665,412],[628,2],[629,2],[630,2],[671,412],[666,2],[631,2],[632,2],[633,2],[634,2],[673,534],[635,2],[636,2],[637,2],[638,2],[642,535],[643,536],[644,535],[645,535],[646,2],[647,535],[648,536],[649,535],[650,535],[651,535],[652,535],[653,535],[654,536],[655,536],[656,535],[657,535],[658,536],[659,536],[660,535],[661,535],[662,2],[663,2],[672,412],[639,2],[667,2],[668,537],[669,537],[641,538],[640,539],[670,540],[664,2],[678,541],[680,542],[679,543],[677,544],[674,2],[676,545],[675,546],[682,547],[683,2],[681,548],[1257,549],[861,550],[830,551],[840,551],[831,551],[841,551],[832,551],[833,551],[848,551],[847,551],[849,551],[850,551],[842,551],[834,551],[843,551],[835,551],[844,551],[836,551],[838,551],[846,552],[839,551],[845,552],[851,552],[837,551],[852,551],[857,551],[858,551],[853,551],[829,2],[859,2],[855,551],[854,551],[856,551],[860,551],[80,2],[210,553],[208,554],[209,555],[197,556],[198,554],[205,557],[196,558],[201,559],[211,2],[202,560],[207,561],[213,562],[212,563],[195,564],[203,565],[204,566],[199,567],[206,553],[200,568],[191,569],[190,528],[194,2],[544,2],[85,570],[796,2],[774,2],[775,571],[77,2],[78,2],[13,2],[14,2],[16,2],[15,2],[2,2],[17,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[24,2],[3,2],[25,2],[26,2],[4,2],[27,2],[31,2],[28,2],[29,2],[30,2],[32,2],[33,2],[34,2],[5,2],[35,2],[36,2],[37,2],[38,2],[6,2],[42,2],[39,2],[40,2],[41,2],[43,2],[7,2],[44,2],[49,2],[50,2],[45,2],[46,2],[47,2],[48,2],[8,2],[54,2],[51,2],[52,2],[53,2],[55,2],[9,2],[56,2],[57,2],[58,2],[60,2],[59,2],[61,2],[62,2],[10,2],[63,2],[64,2],[65,2],[11,2],[66,2],[67,2],[68,2],[69,2],[70,2],[1,2],[71,2],[72,2],[12,2],[75,2],[74,2],[73,2],[76,2],[793,572],[790,573],[791,572],[794,574],[789,2],[222,575],[221,409],[91,576],[216,577],[214,578],[192,579],[193,2],[87,580],[86,2],[88,581],[89,2],[90,582],[215,583],[797,584],[788,585],[783,2],[809,586],[799,587],[810,588],[802,589],[801,590],[800,591],[816,592],[1273,281],[501,593],[439,594],[446,595],[441,2],[442,2],[440,596],[443,597],[435,2],[436,2],[447,598],[438,599],[444,2],[445,600],[437,601],[428,602],[431,603],[429,603],[425,602],[432,604],[433,593],[430,603],[426,605],[427,606],[421,607],[367,608],[369,609],[420,2],[368,610],[424,611],[422,2],[370,608],[371,2],[419,612],[366,613],[363,2],[423,614],[364,615],[365,2],[434,616],[372,617],[373,617],[374,617],[375,617],[376,617],[377,617],[378,617],[379,617],[380,617],[381,617],[382,617],[383,617],[385,617],[384,617],[386,617],[387,617],[388,617],[418,618],[389,617],[390,617],[391,617],[392,617],[393,617],[394,617],[395,617],[396,617],[397,617],[398,617],[399,617],[400,617],[401,617],[403,617],[402,617],[404,617],[405,617],[406,617],[407,617],[408,617],[409,617],[410,617],[411,617],[412,617],[413,617],[414,617],[417,617],[415,617],[416,617],[1230,324],[226,2],[228,619],[227,2],[229,619],[232,620],[233,621],[234,622],[230,2],[235,622],[244,344],[238,623],[237,624],[239,620],[231,622],[240,625],[236,2],[241,624],[242,2],[243,2],[247,345],[248,2],[257,626],[256,345],[255,345],[258,2],[246,2],[245,2],[249,2],[254,345],[251,345],[250,2],[274,2],[275,627],[333,345],[332,628],[306,345],[305,345],[308,345],[307,345],[304,345],[303,345],[311,345],[310,345],[309,2],[319,345],[318,345],[313,345],[312,345],[315,345],[314,345],[317,345],[316,345],[320,345],[322,345],[321,345],[330,345],[329,345],[324,345],[323,345],[326,345],[325,345],[328,345],[327,345],[331,345],[276,345],[277,345],[283,345],[282,345],[285,345],[284,345],[281,345],[280,345],[279,345],[278,345],[286,2],[287,345],[290,345],[289,345],[288,2],[291,345],[293,345],[292,345],[294,629],[295,345],[296,345],[298,345],[297,345],[299,345],[300,345],[301,345],[302,345],[253,345],[252,345],[260,345],[261,345],[262,345],[263,345],[264,345],[267,345],[266,2],[269,345],[268,345],[265,630],[270,345],[271,345],[272,2],[273,345],[259,2],[1196,370]],"affectedFilesPendingEmit":[[742,51],[743,51],[745,51],[744,51],[746,51],[749,51],[748,51],[529,51],[769,51],[770,51],[540,51],[539,51],[538,51],[765,51],[766,51],[541,51],[542,51],[764,51],[763,51],[738,51],[737,51],[537,51],[536,51],[535,51],[534,51],[532,51],[531,51],[723,51],[736,51],[725,51],[729,51],[730,51],[728,51],[731,51],[732,51],[759,51],[762,51],[733,51],[734,51],[735,51],[357,51],[224,51],[717,51],[714,51],[533,51],[358,51],[715,51],[716,51],[817,51],[356,51],[355,51],[352,51],[756,51],[721,51],[359,51],[767,51],[739,51],[543,51],[350,51],[828,51],[863,51],[818,51],[351,51],[819,51],[353,51],[820,51],[822,51],[821,51],[824,51],[823,51],[724,51],[768,51],[726,51],[825,51],[354,51],[826,51],[757,51],[827,51],[760,51],[722,51],[761,51],[758,51],[720,51],[740,51],[741,51],[867,51],[866,51],[869,51],[868,51],[871,51],[870,51],[873,51],[872,51],[875,51],[874,51],[877,51],[876,51],[878,51],[879,51],[880,51],[881,51],[548,51],[549,51],[883,51],[882,51],[884,51],[885,51],[864,51],[865,51],[890,51],[891,51],[892,51],[893,51],[894,51],[896,51],[895,51],[900,51],[897,51],[898,51],[899,51],[901,51],[902,51],[903,51],[904,51],[905,51],[906,51],[935,51],[937,51],[938,51],[936,51],[940,51],[939,51],[942,51],[941,51],[943,51],[944,51],[945,51],[946,51],[947,51],[948,51],[949,51],[951,51],[950,51],[952,51],[953,51],[954,51],[955,51],[546,51],[545,51],[547,51],[956,51],[957,51],[958,51],[959,51],[960,51],[961,51],[962,51],[963,51],[964,51],[965,51],[966,51],[967,51],[968,51],[969,51],[970,51],[971,51],[972,51],[973,51],[974,51],[975,51],[976,51],[977,51],[978,51],[979,51],[980,51],[981,51],[982,51],[983,51],[984,51],[985,51],[986,51],[987,51],[988,51],[989,51],[990,51],[991,51],[992,51],[993,51],[994,51],[995,51],[996,51],[997,51],[998,51],[999,51],[1000,51],[1001,51],[1002,51],[1003,51],[1004,51],[1005,51],[1006,51],[1007,51],[1008,51],[1009,51],[1010,51],[1011,51],[1012,51],[1013,51],[1014,51],[1015,51],[1016,51],[1017,51],[1019,51],[1018,51],[1021,51],[1020,51],[1027,51],[1022,51],[1023,51],[1024,51],[1025,51],[1026,51],[1029,51],[1028,51],[1031,51],[1030,51],[888,51],[889,51],[1033,51],[1034,51],[1032,51],[887,51],[886,51],[1036,51],[1035,51],[1038,51],[1037,51],[1040,51],[1039,51],[1042,51],[1041,51],[1051,51],[1043,51],[1044,51],[1045,51],[1046,51],[1047,51],[1048,51],[1049,51],[1050,51],[1053,51],[1052,51],[1054,51],[747,51],[1085,51],[1084,51],[1087,51],[1086,51],[1089,51],[1088,51],[1091,51],[1090,51],[1093,51],[1092,51],[1095,51],[1096,51],[1094,51],[1098,51],[1097,51],[1108,51],[1107,51],[1109,51],[1102,51],[1101,51],[712,51],[711,51],[710,51],[555,51],[713,51],[559,51],[556,51],[558,51],[557,51],[568,51],[567,51],[560,51],[566,51],[565,51],[564,51],[1105,51],[1104,51],[1106,51],[1117,51],[1116,51],[1111,51],[1110,51],[1118,51],[702,51],[701,51],[569,51],[703,51],[707,51],[706,51],[708,51],[699,51],[698,51],[700,51],[1124,51],[1123,51],[1122,51],[1125,51],[709,51],[705,51],[704,51],[1103,51],[1113,51],[1112,51],[1114,51],[1115,51],[1121,51],[563,51],[1128,51],[1127,51],[1129,51],[1132,51],[1133,51],[1135,51],[1145,51],[1142,51],[1134,51],[1130,51],[1131,51],[1144,51],[1143,51],[1146,51],[1147,51],[1148,51],[1149,51],[1150,51],[1151,51],[1152,51],[1153,51],[1154,51],[1155,51],[1156,51],[1157,51],[1162,51],[1158,51],[1159,51],[1161,51],[1160,51],[1163,51],[1164,51],[1165,51],[1166,51],[1167,51],[1168,51],[1169,51],[550,51],[551,51],[1170,51],[1171,51],[1173,51],[1172,51],[1178,51],[1179,51],[1180,51],[1181,51],[1177,51],[1182,51],[1174,51],[1176,51],[1175,51],[1184,51],[1183,51],[1186,51],[1185,51],[1190,51],[1189,51],[1188,51],[553,51],[552,51],[1187,51],[697,51],[696,51],[1191,51],[1192,51],[687,51],[686,51],[690,51],[691,51],[695,51],[692,51],[693,51],[694,51],[1228,51],[1235,51],[1226,51],[530,51],[1227,51],[1239,51],[1238,51],[1242,51],[1241,51],[1236,51],[1237,51],[1240,51],[1243,51],[862,51],[1225,51],[688,51],[689,51],[1259,51],[1245,51],[754,51],[755,51],[751,51],[1260,51],[752,51],[750,51],[753,51],[561,51],[349,51],[1246,51],[1099,51],[1247,51],[1249,51],[1126,51],[1251,51],[1250,51],[727,51],[522,51],[1248,51],[1139,51],[1136,51],[1141,51],[1261,51],[1140,51],[1262,51],[1137,51],[1263,51],[1138,51],[1119,51],[1252,51],[562,51],[1264,51],[1265,51],[1253,51],[525,51],[526,51],[521,51],[527,51],[528,51],[520,51],[523,51],[524,51],[1258,51],[554,51],[1100,51],[1266,51],[1267,51],[685,51],[1120,51],[1268,51],[1269,51],[1270,51],[1271,51],[1272,51],[223,51]],"emitSignatures":[223,224,349,350,351,352,353,354,355,356,357,358,359,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,817,818,819,820,821,822,823,824,825,826,827,828,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126,1127,1128,1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1225,1226,1227,1228,1235,1236,1237,1238,1239,1240,1241,1242,1243,1245,1246,1247,1248,1249,1250,1251,1252,1253,1258,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272],"version":"5.7.3"} \ No newline at end of file diff --git a/package.json b/package.json index 36b2b82fa..4adcf1dfe 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,17 @@ "test:e2e": "playwright test -c ./tests/playwright.config.ts ./tests", "test:unit": "vitest run && yarn workspace @openvaa/frontend test:unit && yarn workspace @openvaa/strapi test:unit", "test:unit:watch": "echo '###################################\nNB! Running only tests in /packages\n###################################\n' && vitest", - "format": "yarn build:app-shared && prettier --write .", - "format:check": "yarn build:app-shared && prettier --check .", - "lint:fix": "yarn build:app-shared && prettier --write . && eslint --flag unstable_config_lookup_from_file --fix backend/vaa-strapi/src backend/vaa-strapi/tests frontend packages tests", - "lint:check": "yarn build:app-shared && eslint --flag unstable_config_lookup_from_file backend/vaa-strapi/src backend/vaa-strapi/tests frontend packages tests" + "format": "yarn build:app-shared && prettier --write . && yarn workspace @openvaa/docs format", + "format:check": "yarn build:app-shared && prettier --check . && yarn workspace @openvaa/docs format:check", + "lint:fix": "yarn build:app-shared && prettier --write . && eslint --flag v10_config_lookup_from_file --fix backend/vaa-strapi/src backend/vaa-strapi/tests frontend packages tests", + "lint:check": "yarn build:app-shared && eslint --flag v10_config_lookup_from_file backend/vaa-strapi/src backend/vaa-strapi/tests frontend packages tests", + "docs:prepare": "yarn build:shared && yarn workspace @openvaa/frontend prepare", + "docs:generate": "yarn workspace @openvaa/docs generate", + "docs:typedoc": "yarn workspace @openvaa/docs generate:typedoc", + "docs:typedoc-frontend": "yarn workspace @openvaa/docs generate:typedoc-frontend", + "docs:components": "yarn workspace @openvaa/docs generate:components", + "docs:routes": "yarn workspace @openvaa/docs generate:routes", + "docs:dev": "yarn workspace @openvaa/docs dev" }, "devDependencies": { "@faker-js/faker": "^8.4.1", @@ -31,13 +38,15 @@ "@types/node": "^20.17.12", "cheerio": "^1.0.0", "dotenv": "^16.4.7", - "eslint": "~9.14.0", + "eslint": "^9.39.2", "eslint-plugin-simple-import-sort": "^12.1.1", + "glob": "^11.0.0", "husky": "^9.1.7", - "lint-staged": "^15.3.0", + "lint-staged": "^16.2.7", "mailparser": "^3.7.2", "onchange": "^7.1.0", "prettier": "^3.4.2", + "tsx": "^4.19.2", "vitest": "^2.1.8" }, "engine": { @@ -50,7 +59,8 @@ "packages/*", "backend/vaa-strapi", "backend/vaa-strapi/src/plugins/*", - "frontend" + "frontend", + "docs" ], "packageManager": "yarn@4.6.0" } diff --git a/packages/app-shared/src/settings/README.md b/packages/app-shared/src/settings/README.md new file mode 100644 index 000000000..b5b0f9739 --- /dev/null +++ b/packages/app-shared/src/settings/README.md @@ -0,0 +1,15 @@ +# Settings + +> See also the online docs: +> +> - [Static settings](https://openvaa.org/developers-guide/configuration/static-settings) (or [locally](/docs/src/routes/developers-guide/configuration/static-settings/+page.md)) +> - [App Settings](https://openvaa.org/developers-guide/configuration/app-settings) (or [locally](/docs/src/routes/developers-guide/configuration/app-settings/+page.md)) + +This directory contains the configuration system for OpenVAA, including both static and dynamic settings. + +## Files + +- `staticSettings.ts` - Hardcoded values like colors, fonts, supported locales, etc. +- `dynamicSettings.ts` - Runtime-configurable values like election data and feature flags + +See the documentation links above for detailed information on how to use and customize these settings. diff --git a/packages/app-shared/src/settings/dynamicSettings.type.ts b/packages/app-shared/src/settings/dynamicSettings.type.ts index 0d8c54c88..26de69394 100644 --- a/packages/app-shared/src/settings/dynamicSettings.type.ts +++ b/packages/app-shared/src/settings/dynamicSettings.type.ts @@ -59,18 +59,42 @@ export type DynamicSettings = { showHelp: boolean; }; /** - * Settings related to app header styling + * Settings related to app header styling. TODO: Move these to App Customization. */ headerStyle: { + /** + * Background colors for the header in dark mode. + */ dark: { + /** + * Default background color of the header. + */ bgColor?: string; + /** + * Background color of the header when it’s over an image. + */ overImgBgColor?: string; }; + /** + * Background colors for the header in light mode. + */ light: { + /** + * Default background color of the header. + */ bgColor?: string; + /** + * Background color of the header when it’s over an image. + */ overImgBgColor?: string; }; + /** + * The size of the background image in the header. E.g. `cover`, `contain`, or specific sizes like `100% 50%`. + */ imgSize?: string; + /** + * The positioning of the background image in the header. E.g. `center`, `top`, `bottom`, `left`, `right`, or specific positions like `50% 25%`. + */ imgPosition?: string; }; /** @@ -100,9 +124,9 @@ export type DynamicSettings = { */ minimumAnswers: number; /** - * The method with which parties are matched. • None: no party matching is done • answersOnly: matching is only performed on the parties explicit answers • Impute: missing party answers are substituted with an anwswer imputed from the party's candidates' answers. + * The method with which parties are matched. */ - organizationMatching: 'none' | 'answersOnly' | 'impute'; + organizationMatching: OrganizationMatchingMethod; }; /** * Settings related to the question view. @@ -165,6 +189,9 @@ export type DynamicSettings = { * The additional contents of [ENTITY_TYPE.Candidate] cards. NB. the order of the items has currently no effect. */ [ENTITY_TYPE.Candidate]: Array< + /** + * Show the matching scores for each question category. + */ | 'submatches' /** * Show the entity's answer to a specific question. Only applies to the results list. @@ -175,6 +202,9 @@ export type DynamicSettings = { * The additional contents of party cards. NB. the order of the items has currently no effect. */ [ENTITY_TYPE.Organization]: Array< + /** + * Show the matching scores for each question category. + */ | 'submatches' /** * List party's the top 3 candidates within it's card. Only applies to the results list. @@ -208,7 +238,7 @@ export type DynamicSettings = { */ disallowSelection?: boolean; /** - * Whether to show the election tags along the question text. Only applicable when there are multiple elections. + * Whether to show the election tags along the question text. */ showElectionTags: boolean; /** @@ -233,7 +263,7 @@ export type DynamicSettings = { */ adminApp: boolean; /** - * If `true`, an under maintenance error page will be shown. + * If `true`, an under maintenance error page will be shown when attempting to access any part of the app. */ underMaintenance?: boolean; /** @@ -256,6 +286,14 @@ export type DynamicSettings = { }; }; +export type OrganizationMatchingMethod = + /** No party matching is done */ + | 'none' + /** Matching is only performed on the parties explicit answers. */ + | 'answersOnly' + /** Missing party answers are substituted with an anwswer imputed from the party's candidates' answers. */ + | 'impute'; + /** * Used to show the entity's answer to a specific question. Only applies to the results list. */ @@ -269,20 +307,42 @@ export type QuestionInCardContent = { */ hideLabel?: boolean; /** - * How to format the answer. • Default: use the same format as in EntityDetails. • Tag: format the answers as a pill or tag. + * How to format the answer. */ - format?: 'default' | 'tag'; + format?: QuestionInCardContentFormat; }; +export type QuestionInCardContentFormat = + /** + * Use the same format as in EntityDetails. + */ + | 'default' + /** + * Format the answers as a pill or tag. + */ + | 'tag'; + /** * The possible content tabs to show for all entities. */ -export type EntityDetailsContent = 'info' | 'opinions'; +export type EntityDetailsContent = + /** + * Basic information. + */ + | 'info' + /** + * Answers to opinion questions. + */ + | 'opinions'; /** * The possible content tabs to show for `Organization`s. */ -export type OrganizationDetailsContent = 'candidates'; +export type OrganizationDetailsContent = + /** + * The party’s candidates. + */ + 'candidates'; /** * The data for a notification to be shown to users. diff --git a/packages/app-shared/src/settings/staticSettings.type.ts b/packages/app-shared/src/settings/staticSettings.type.ts index 676b9c520..3e912608a 100644 --- a/packages/app-shared/src/settings/staticSettings.type.ts +++ b/packages/app-shared/src/settings/staticSettings.type.ts @@ -120,7 +120,7 @@ export type StaticSettings = { readonly trackEvents: boolean; }; /** - * Settings related to Candidate App pre-registration. + * Settings related to Candidate App pre-registration. If enabled, make sure the set the relevant env variables as well. */ readonly preRegistration: { /** diff --git a/packages/app-shared/src/utils/passwordValidation.ts b/packages/app-shared/src/utils/passwordValidation.ts index 4d2a86604..1a2ce4119 100644 --- a/packages/app-shared/src/utils/passwordValidation.ts +++ b/packages/app-shared/src/utils/passwordValidation.ts @@ -24,7 +24,7 @@ export interface PasswordValidation { * Checks if the given password contains any repeated characters. * The number of repeated characters is defined in the `repetitionLimit` variable. * - * @param {string} password - The password to check. + * @param {string} - password - The password to check. * @returns {boolean} `true` if the password contains repeated characters, `false` otherwise. */ function checkRepetition(password: string): boolean { @@ -45,8 +45,8 @@ function isLetter(character: string): boolean { /** * Checks if the given password contains a character that satisfies the given condition. * - * @param {string} password - The password to check. - * @param {(char: string) => boolean} condition - The condition to check for each character. + * @param {string} - password - The password to check. + * @param {(char: - string) => boolean} condition - The condition to check for each character. * @returns {boolean} `true` if the password contains a character that satisfies the condition, `false` otherwise. */ function containsCharacter(password: string, condition: (char: string) => boolean): boolean { @@ -56,8 +56,8 @@ function containsCharacter(password: string, condition: (char: string) => boolea /** * Validates the given password and username according to the defined rules. * - * @param {string} password - The password to validate. - * @param {string} username - The username used to check that the password does not contain the username. + * @param {string} - password - The password to validate. + * @param {string} - username - The username used to check that the password does not contain the username. * @returns {Record<string, ValidationDetail>} An object containing the validation details for each rule. */ function passwordValidation(password: string, username: string): Record<string, ValidationDetail> { @@ -102,7 +102,7 @@ function passwordValidation(password: string, username: string): Record<string, /** * Checks if all enforced requirements are met in the given validation object. * - * @param {Record<string, ValidationDetail>} validation - The validation object to check. + * @param {Record<string, - ValidationDetail>} validation - The validation object to check. * @returns {boolean} `true` if all enforced requirements are met, `false` otherwise. */ function isValid(validation: Record<string, ValidationDetail>): boolean { @@ -115,8 +115,8 @@ function isValid(validation: Record<string, ValidationDetail>): boolean { /** * Validates the given password and returns whether the password is valid. * - * @param {string} password - The password to validate. - * @param {string} [username=''] - The username used to check that the password does not contain the username + * @param {string} - password - The password to validate. + * @param {string} - [username=''] - The username used to check that the password does not contain the username * @returns {boolean} `true` if the password is valid, `false` otherwise. */ export function validatePassword(password: string, username = ''): boolean { @@ -127,8 +127,8 @@ export function validatePassword(password: string, username = ''): boolean { * Validates the given password and username and returns a PasswordValidation object. * The PasswordValidation object contains the validation status and details for each requirement. * - * @param {string} password - The password to validate. - * @param {string} [username=''] - The username used to check that the password does not contain the username + * @param {string} - password - The password to validate. + * @param {string} - [username=''] - The username used to check that the password does not contain the username * @returns {PasswordValidation} An object containing the validation status and details. */ export function validatePasswordDetails(password: string, username = ''): PasswordValidation { diff --git a/packages/app-shared/tsconfig.json b/packages/app-shared/tsconfig.json index bbf3b9c58..a4032dfe7 100644 --- a/packages/app-shared/tsconfig.json +++ b/packages/app-shared/tsconfig.json @@ -6,6 +6,5 @@ "outDir": "./build" }, "include": ["src/**/*"], - "exclude": ["**/*.test.ts"], - "references": [{ "path": "../data/tsconfig.json" }] + "exclude": ["**/*.test.ts"] } diff --git a/packages/argument-condensation/README.md b/packages/argument-condensation/README.md index 11b4481a7..f764561ef 100644 --- a/packages/argument-condensation/README.md +++ b/packages/argument-condensation/README.md @@ -120,7 +120,7 @@ const results = await handleQuestion({ Not recommended - see Package API Usage above for the preferred approach. -If you want to configure comment grouping and prompt selection, please consult [`main.ts`](src/main.ts). +If you want to configure comment grouping and prompt selection, please consult [`api.ts`](src/api.ts). ```typescript import { Condenser } from '@openvaa/argument-condensation'; @@ -203,7 +203,7 @@ const condenser = new Condenser(input); const output = await condenser.run(); // the arguments with metadata and other jazz ``` -Currently, we automatically run MAP --> ITERATE_MAP --> REDUCE (k times as needed to reduce to 1 list). The reasoning for the extra iteration step is to make sure that information gathered from the source data is maximized before moving onto processing generated arguments. To change to a custom plan, please see [`core/utils/condensation/defineCondensationPlan.ts`](src/core/utils/condensation/defineCondensationPlan.ts) +Currently, we automatically run MAP --> ITERATE_MAP --> REDUCE (k times as needed to reduce to 1 list). The reasoning for the extra iteration step is to make sure that information gathered from the source data is maximized before moving onto processing generated arguments. To change to a custom plan, please see [`core/utils/condensation/defineCondensationSteps.ts`](src/core/utils/condensation/defineCondensationSteps.ts) ### Visualization @@ -228,7 +228,7 @@ Also, if you want to see the exact prompts and outputs in each prompt call, you ## Package Structure -- [`src/main.ts`](src/main.ts): Contains outward-facing API function `handleQuestion`. This is where you should start your investigation of the system. +- [`src/api.ts`](src/api.ts): Contains outward-facing API function `handleQuestion`. This is where you should start your investigation of the system. - `src/core`: Contains the condensation logic. Many subtasks are delegated to utils/ - `/condensation`: The `Condenser` class and other core logic for running the condensation process. Good place to dive deeper into the implementation details. Sub-dir prompts has the prompts used in condensation operations. Add your own prompts as needed, either to provide language support or to simply get more control of the LLM instructions. - `/types`: All TypeScript type definitions used in the package. @@ -240,11 +240,11 @@ Also, if you want to see the exact prompts and outputs in each prompt call, you The most important data structures you need to know about are: -- `SupportedQuestion`: Type of the questions you can condense arguments for in the OpenVAA repo. It's a subset of the question types from `@openvaa/data`. For more info, see [`@openvaa/data/objects/questions/base/question.ts`](@openvaa/data/objects/questions/base/question.ts). -- `HasAnswers`: A generic entity (like a candidate or party) that has answers and some freely drafted texts regarding a particular quesetion. A condensation run doesn't need anything else than VAA answers with non-empty accompanied texts. Every HasAnswers entity has answers but may not have non-empty comments on why they answered as they did. Defined in [`@openvaa/core/src/matching/hasAnswers.type.ts`](@openvaa/core/src/matching/hasAnswers.type.ts). +- `SupportedQuestion`: Type of the questions you can condense arguments for in the OpenVAA repo. It's a subset of the question types from `@openvaa/data`. For more info, see the `@openvaa/data` package documentation on Question types. +- `HasAnswers`: A generic entity (like a candidate or party) that has answers and some freely drafted texts regarding a particular question. A condensation run doesn't need anything else than VAA answers with non-empty accompanied texts. Every HasAnswers entity has answers but may not have non-empty comments on why they answered as they did. Defined in the `@openvaa/core` package. - `CondensationRunInput`: Configuration for the condensation process. See [`src/core/types/condensation/condensationInput.ts`](src/core/types/condensation/condensationInput.ts). - `CondensationRunResult`: The final output. It contains the list of condensed arguments, metadata about the run, and the original comments that were processed. See [`src/core/types/condensation/condensationResult.ts`](src/core/types/condensation/condensationResult.ts). -- `Argument`: A single condensed argument, including its text and an ID. Note that the ID is currently without much purpose other than being a placeholder. They are a mandatory field in the Argument type, because it keeps the Argument abstraction clean and ready to handle ids without needing to change it. Currently, LLM generates mock ids for the 'id' field, so we can simply parse the arguments with a single parsing contract instead of having different contracts for id-free and id-able arguments. See [`src/core/types/base/argument.ts`](src/core/types/base/argument.ts). +- `Argument`: A single condensed argument, including its text and an ID. Note that the ID is currently without much purpose other than being a placeholder. They are a mandatory field in the Argument type, because it keeps the Argument abstraction clean and ready to handle ids without needing to change it. Currently, LLM generates mock ids for the 'id' field, so we can simply parse the arguments with a single parsing contract instead of having different contracts for id-free and id-able arguments. See [`src/core/types/condensation/argument.ts`](src/core/types/condensation/argument.ts). ## Adding Language Support diff --git a/packages/argument-condensation/src/core/types/condensation/condensationType.ts b/packages/argument-condensation/src/core/types/condensation/condensationType.ts index 1a0f32697..0d12fd82c 100644 --- a/packages/argument-condensation/src/core/types/condensation/condensationType.ts +++ b/packages/argument-condensation/src/core/types/condensation/condensationType.ts @@ -1,7 +1,7 @@ /** * Defines the types of output that can be generated by the condensation process. - * @todo - * Implement support for the Number answer format + * + * TODO: Implement support for the Number answer format */ export const CONDENSATION_TYPE = { LikertPros: 'likertPros', diff --git a/packages/core/README.md b/packages/core/README.md index 23211d6d2..c25072c82 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -8,7 +8,8 @@ It contains: - Typing for [`Id`s](./src/id) and related utilities - Typing for JSON [`Serializable`](./src/serializable/serializable.type.ts) objects - Typing for distances, answers and missing values related to [matching](./src/matching/) -- An interface and a default implementation for [progress logging](./src/logger/), which is used as a common method of providing progress, info and warning callback to (server) functions that take a longer time to complete +- Typing and base implementation for [Controller](src/controller/controller.ts) +- Typing for [`LLMPipelineMetrics`](src/pipelines/metrics.type.ts) ## Developing diff --git a/packages/core/src/controller/controller.type.ts b/packages/core/src/controller/controller.type.ts index bccce6944..a331fdca0 100644 --- a/packages/core/src/controller/controller.type.ts +++ b/packages/core/src/controller/controller.type.ts @@ -1,7 +1,7 @@ /** * A callback passed to admin or other operations that take long to complete. * - * @usage + * @example * ```ts * // In the backend * diff --git a/packages/core/src/entity/getEntity.ts b/packages/core/src/entity/getEntity.ts index 7c9cf2332..0026de134 100644 --- a/packages/core/src/entity/getEntity.ts +++ b/packages/core/src/entity/getEntity.ts @@ -2,7 +2,7 @@ import type { Entity, MaybeWrappedEntity } from './entity.type'; /** * Return the entity for a wrapped entity or the entity itself if it's not wrapped. - * @param target A possibly wrapped entity. + * @param target - A possibly wrapped entity. * @returns The entity. */ export function getEntity<TEntity extends Entity>(maybeWrapped: MaybeWrappedEntity<TEntity>): TEntity { diff --git a/packages/core/src/id/isValidId.ts b/packages/core/src/id/isValidId.ts index 65885da97..0debe47db 100644 --- a/packages/core/src/id/isValidId.ts +++ b/packages/core/src/id/isValidId.ts @@ -4,7 +4,7 @@ const INVALID_ID_CHAR_RE = /\s/; /** * Returns `true` if the given value is a valid `Id`, `false` otherwise. - * @param id The value to check + * @param id - The value to check * @returns `true` if the given value is a valid `Id`, `false` otherwise. */ export function isValidId(id: Id): id is Id { diff --git a/packages/core/src/matching/distance.ts b/packages/core/src/matching/distance.ts index 5ed674d45..3d1611bae 100644 --- a/packages/core/src/matching/distance.ts +++ b/packages/core/src/matching/distance.ts @@ -21,9 +21,9 @@ export const COORDINATE = { /** * A utility that will normalize the given value within the given range. - * @param value The numeric value - * @param min The minimum of the value range - * @param max The maximum of the value range + * @param value - The numeric value + * @param min - The minimum of the value range + * @param max - The maximum of the value range */ export function normalizeCoordinate({ value, min, max }: { value: number; min: number; max: number }): Coordinate { if (min > max || min == max) throw new Error('Range is invalid'); @@ -33,7 +33,7 @@ export function normalizeCoordinate({ value, min, max }: { value: number; min: n /** * Assert that `value` is a `Coordinate` within the correct range. - * @param value The number to test + * @param value - The number to test * @returns True if the value is a `Coordinate` * @throws If the value is not a `Coordinate` */ @@ -45,7 +45,7 @@ export function assertCoordinate(value: Coordinate): value is Coordinate { /** * Assert that `value` is a `NormalizedDistance` within the correct range. - * @param value The number to test + * @param value - The number to test * @returns True if the value is a `NormalizedDistance` * @throws If the value is not a `NormalizedDistance` */ diff --git a/packages/core/src/pipelines/promptRegistry.ts b/packages/core/src/pipelines/promptRegistry.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/data/README.md b/packages/data/README.md index 95c339bd7..2d0c5d677 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -111,7 +111,7 @@ The data is expected to be immutable and no setters are provided for the propert The [`DataRoot`](./src/root/dataRoot.ts) exposes data provision methods like `provideElectionData(data: Readonly<Array<ElectionData>>)`, which are used to build the child [`DataObject`](./src/core/dataObject.ts)s. The objects are stored in the root’s `children` collections. -Data provision is designed to be idempotent and for this reason whenever data is provided the whole collection is rewritten, along with those depending on it. See [`DEPENDENT_COLLECTIONS`](./src/root/dependentCollections.ts) for details. This means that data should be provided in this order: +Data provision is designed to be idempotent and for this reason whenever data is provided the whole collection is rewritten, along with those depending on it. See the `DataRoot` implementation for details on dependent collections. This means that data should be provided in this order: 1. Elections 2. Constituency groups diff --git a/packages/data/src/objects/nominations/base/nomination.ts b/packages/data/src/objects/nominations/base/nomination.ts index 7943d80d9..46a0c3cf1 100644 --- a/packages/data/src/objects/nominations/base/nomination.ts +++ b/packages/data/src/objects/nominations/base/nomination.ts @@ -32,8 +32,8 @@ export abstract class Nomination< /** * The `Nomination` constructor can be called with data lacking an `id` in which case a non-deterministic, unique `id` will be generated. - * @param data The data with an optional `id`. - * @param root The `DataRoot` object. + * @param data - The data with an optional `id`. + * @param root - The `DataRoot` object. */ constructor({ data, root }: { data: WithOptional<TData, 'id'>; root: DataRoot }) { if ( diff --git a/packages/data/src/objects/nominations/variants/allianceNomination.test.ts b/packages/data/src/objects/nominations/variants/allianceNomination.test.ts index 4a2732426..a82ac71f5 100644 --- a/packages/data/src/objects/nominations/variants/allianceNomination.test.ts +++ b/packages/data/src/objects/nominations/variants/allianceNomination.test.ts @@ -37,8 +37,8 @@ test('Should have nested nominations', () => { /** * Find the implicit alliance that matches the nomination, i.e. has the same electionId, constituencyId, and member organizations - * @param entities The entities to search in - * @param nominationData The data for the nomination + * @param entities - The entities to search in + * @param nominationData - The data for the nomination * @returns An array of matching implicit alliances, which should have one and only one item */ function findEntities(entities: Array<Alliance>, nominationData: PublicAllianceNominationData): Array<Alliance> { @@ -62,8 +62,8 @@ function findEntities(entities: Array<Alliance>, nominationData: PublicAllianceN /** * Find the nomination object that matches the nomination data, i.e. has the same electionId, electionRound, constituencyId, and member organizations - * @param nominations The entities to search in - * @param nominationData The data for the nomination + * @param nominations - The entities to search in + * @param nominationData - The data for the nomination * @returns An array of matching implicit nominations, which should have one and only one item */ function findNominations( diff --git a/packages/data/src/objects/nominations/variants/candidateNomination.test.ts b/packages/data/src/objects/nominations/variants/candidateNomination.test.ts index 4a4f3bb8c..39c89a995 100644 --- a/packages/data/src/objects/nominations/variants/candidateNomination.test.ts +++ b/packages/data/src/objects/nominations/variants/candidateNomination.test.ts @@ -32,8 +32,8 @@ test('Should have nomination objects for all candidate nominations with the corr /** * Find the nomination object that matches the nomination data, i.e. has the same electionId, electionRound, constituencyId, entityId and parent nomination. - * @param nominations The entities to search in - * @param nominationData The data for the nomination + * @param nominations - The entities to search in + * @param nominationData - The data for the nomination * @returns An array of matching implicit nominations, which should have one and only one item */ function findNominations( diff --git a/packages/data/src/objects/nominations/variants/factionNomination.test.ts b/packages/data/src/objects/nominations/variants/factionNomination.test.ts index 29370b38d..b25af6ee1 100644 --- a/packages/data/src/objects/nominations/variants/factionNomination.test.ts +++ b/packages/data/src/objects/nominations/variants/factionNomination.test.ts @@ -32,8 +32,8 @@ test('Should have nomination objects for all faction nominations with the correc /** * Find the nomination object that matches the nomination data, i.e. has the same electionId, constituencyId, electionRound and child and parent nominations. - * @param nominations The entities to search in - * @param nominationData The data for the nomination + * @param nominations - The entities to search in + * @param nominationData - The data for the nomination * @returns An array of matching implicit nominations, which should have one and only one item */ function findNominations( diff --git a/packages/data/src/objects/nominations/variants/organizationNomination.test.ts b/packages/data/src/objects/nominations/variants/organizationNomination.test.ts index 3c4a3080c..a367627f5 100644 --- a/packages/data/src/objects/nominations/variants/organizationNomination.test.ts +++ b/packages/data/src/objects/nominations/variants/organizationNomination.test.ts @@ -46,8 +46,8 @@ test('Should have nomination objects for all Organization nominations with the c /** * Find the nomination object that matches the nomination data, i.e. has the same electionId, electionRound, constituencyId and entityId. - * @param nominations The entities to search in - * @param nominationData The data for the nomination + * @param nominations - The entities to search in + * @param nominationData - The data for the nomination * @returns An array of matching implicit nominations, which should have one and only one item */ function findNominations( diff --git a/packages/data/src/root/dataRoot.ts b/packages/data/src/root/dataRoot.ts index 5d75657e0..b53a072ca 100644 --- a/packages/data/src/root/dataRoot.ts +++ b/packages/data/src/root/dataRoot.ts @@ -455,7 +455,7 @@ export class DataRoot extends Updatable { * Get all provided `Nomination`s for a specific `Entity` passing either itself or its `type` and `id`. * @param type - The type of the nominated entity. * @param id - The id of the nominated entity. - * @paramn options - Additional options passed to `findNominations`. + * @param options - Additional options passed to `findNominations`. * @returns An array of `Nomination`s for the given `Entity` or `undefined` if `Nomination`s have not yet been provided. */ getNominationsForEntity<TEntity extends EntityType>( @@ -606,7 +606,7 @@ export class DataRoot extends Updatable { } /** - * Provide the constituency and group data to the `DataRoot`. Existing data will be reset, along with any {@link DEPENDENT_COLLECTIONS | dependent collections}. + * Provide the constituency and group data to the `DataRoot`. Existing data will be reset, along with any dependent collections. */ provideConstituencyData({ groups, @@ -622,14 +622,14 @@ export class DataRoot extends Updatable { } /** - * Provide election data to the `DataRoot`. Existing data will be reset, along with any {@link DEPENDENT_COLLECTIONS | dependent collections}. + * Provide election data to the `DataRoot`. Existing data will be reset, along with any dependent collections. */ provideElectionData(data: Readonly<Array<ElectionData>>): void { this.provideData('elections', data, (args) => new Election(args)); } /** - * Provide the data for all entities to the `DataRoot`. Existing data will be reset, along with any {@link DEPENDENT_COLLECTIONS | dependent collections}. + * Provide the data for all entities to the `DataRoot`. Existing data will be reset, along with any dependent collections. * @param data - The data for all entities. Two formats are supported: a single array of `AnyEntityVariantData` with the `type` specified for each, or a structured object with these `EntityType`s as keys and the actual data without them. * @param options - Additional options for data provision. */ @@ -662,7 +662,7 @@ export class DataRoot extends Updatable { } /** - * Provide the data for all nominations to the `DataRoot`. Existing data will be reset, along with any {@link DEPENDENT_COLLECTIONS | dependent collections}. + * Provide the data for all nominations to the `DataRoot`. Existing data will be reset, along with any dependent collections. * @param data - The data for all nominations. Two formats are supported: a single array of `AnyNominationVariantData` with the `electionId` and `constituencyId` specified for each, or a structured object with these `Id`s as keys and the actual data without them. * @param options - Additional options for data provision. * @returns The created nominations because they’re needed by some nomination initializers. @@ -703,7 +703,7 @@ export class DataRoot extends Updatable { } /** - * Provide question and question category data to the `DataRoot`. Existing data will be reset, along with any {@link DEPENDENT_COLLECTIONS | dependent collections}. + * Provide question and question category data to the `DataRoot`. Existing data will be reset, along with any dependent collections. */ provideQuestionData({ categories, diff --git a/packages/filters/README.md b/packages/filters/README.md index 6da2b7bd3..ae6f3dd13 100644 --- a/packages/filters/README.md +++ b/packages/filters/README.md @@ -22,8 +22,8 @@ The module uses [`tsc-esm-fix`](https://github.com/antongolub/tsc-esm-fix) which ## Basic use -1. Choose from one of the implemented filters, e.g. [`SingleChoiceQuestionFilter`](./src/filter/enumerated/singleChoiceQuestionFilter.ts), which works on questions that have an enumerated list of values of which the entity may select one. -2. Depending on the filter, they take different constructor parameters and type generics. [`SingleChoiceQuestionFilter`](./src/filter/enumerated/singleChoiceQuestionFilter.ts) takes a `question` parameter implementing the [`ChoiceQuestion`](./src/question/filterableQuestion.ts) interface and an optional `locale` code, which is used in value sorting. +1. Choose from one of the implemented filters, e.g. `SingleChoiceQuestionFilter`, which works on questions that have an enumerated list of values of which the entity may select one. +2. Depending on the filter, they take different constructor parameters and type generics. `SingleChoiceQuestionFilter` takes a `question` parameter implementing the `ChoiceQuestion` interface and an optional `locale` code, which is used in value sorting. 3. After creating the filter, you can add rules to it to filter down the results. 4. To apply the filter, use the `apply` method. 5. To list the value options, use the `parse` method on the available targets. diff --git a/packages/filters/src/filter/base/castValue.ts b/packages/filters/src/filter/base/castValue.ts index f6f5fcc35..2198f6cf7 100644 --- a/packages/filters/src/filter/base/castValue.ts +++ b/packages/filters/src/filter/base/castValue.ts @@ -3,9 +3,9 @@ import type { FilterOptions } from './filter.type'; /** * Cast a non-missing value to the correct data type. * @typeParam V The data type to cast to. - * @param value The value to cast - * @param type A filter value type - * @param multiple Whether the value is an array of the type + * @param value - The value to cast + * @param type - A filter value type + * @param multiple - Whether the value is an array of the type * @returns The value cast to the correct data type or an array of that type */ export function castValue<TValue>(value: unknown, type: FilterOptions['type'], multiple?: false): TValue; diff --git a/packages/filters/src/filter/base/filter.ts b/packages/filters/src/filter/base/filter.ts index 68c78d188..6790e0281 100644 --- a/packages/filters/src/filter/base/filter.ts +++ b/packages/filters/src/filter/base/filter.ts @@ -52,7 +52,7 @@ export abstract class Filter<TTarget extends MaybeWrappedEntity = MaybeWrappedEn /** * Ge the value from an entity. - * @param target The target entity. + * @param target - The target entity. * @returns The value to filter on or `MISSING_VALUE` or an array of these if `this.options.multipleValues` is true. */ getValue(target: TTarget): MaybeMissing<TValue> | Array<MaybeMissing<TValue>> { @@ -83,7 +83,7 @@ export abstract class Filter<TTarget extends MaybeWrappedEntity = MaybeWrappedEn /** * Apply the filter to the inputs. - * @param targets A list of entities. + * @param targets - A list of entities. * @returns Filtered targets */ apply<TType extends TTarget>(targets: Array<TType>): Array<TTarget> { @@ -92,7 +92,7 @@ export abstract class Filter<TTarget extends MaybeWrappedEntity = MaybeWrappedEn /** * Test an entity against the filter. - * @param target The target entity. + * @param target - The target entity. * @returns true if the entity passes the filter. */ test(target: TTarget): boolean { @@ -130,8 +130,8 @@ export abstract class Filter<TTarget extends MaybeWrappedEntity = MaybeWrappedEn /** * Add a rule to the filter. - * @param name The rule key - * @param value The rule value + * @param name - The rule key + * @param value - The rule value */ setRule(name: keyof typeof this._rules, value: Rule): void { // If there's no change, don't trigger an update @@ -147,8 +147,8 @@ export abstract class Filter<TTarget extends MaybeWrappedEntity = MaybeWrappedEn /** * Add or remove an event handlers that is called when the filter's results change. To use the changed results, access `filter.results`. - * @param handler The event handler - * @add Add the event handler if true, remove it otherwise + * @param handler - The event handler + * @param add - Add the event handler if true, remove it otherwise */ onChange(handler: (filter: unknown) => void, add = true): void { if (add) { @@ -169,7 +169,7 @@ export abstract class Filter<TTarget extends MaybeWrappedEntity = MaybeWrappedEn /** * Wrap a function call with this to temporarily bypass `onChange` events. - * @param f The function to call without triggering `onChange` + * @param f - The function to call without triggering `onChange` */ withoutOnChange(f: () => void): void { this.suspendOnChange = true; @@ -183,7 +183,7 @@ export abstract class Filter<TTarget extends MaybeWrappedEntity = MaybeWrappedEn /** * Test one value against this filter. Implement this method in subclasses. This throws by default and will only be called if `this.options.multipleValues` is `false`. - * @param value A possibly missing value + * @param value - A possibly missing value * @returns true if the value passes the filter. */ testValue(value: MaybeMissing<TValue>): boolean { @@ -192,7 +192,7 @@ export abstract class Filter<TTarget extends MaybeWrappedEntity = MaybeWrappedEn /** * Test multiple values against this filter. Implement this method in subclasses. This throws by default and will only be called if `this.options.multipleValues` is `true`. - * @param values An array of possibly missing values + * @param values - An array of possibly missing values * @returns true if the value passes the filter. */ testValues(values: Array<MaybeMissing<TValue>>): boolean { diff --git a/packages/filters/src/filter/enumerated/choiceQuestionFilter.ts b/packages/filters/src/filter/enumerated/choiceQuestionFilter.ts index 33136a6c2..51e71dbdf 100644 --- a/packages/filters/src/filter/enumerated/choiceQuestionFilter.ts +++ b/packages/filters/src/filter/enumerated/choiceQuestionFilter.ts @@ -23,9 +23,9 @@ export class ChoiceQuestionFilter<TEntity extends MaybeWrappedEntity = MaybeWrap /** * Create a filter for single or multiple choice questions - * @param question The single or multiple choice question - * @param locale The locale is used for value sorting - * @param name Optional name for use when displaying the filter + * @param question - The single or multiple choice question + * @param locale - The locale is used for value sorting + * @param name - Optional name for use when displaying the filter */ constructor( { diff --git a/packages/filters/src/filter/enumerated/enumeratedFilter.ts b/packages/filters/src/filter/enumerated/enumeratedFilter.ts index d81ac6f5e..a389827b2 100644 --- a/packages/filters/src/filter/enumerated/enumeratedFilter.ts +++ b/packages/filters/src/filter/enumerated/enumeratedFilter.ts @@ -22,7 +22,7 @@ export abstract class EnumeratedFilter< /** * Parse all the values from the targets into a map that contains the value counts as well. - * @input A list of entities. + * @param targets - A list of entities. * @returns An array of the values, their counts and possible other properties. */ parseValues(targets: Array<TEntity>): Array<ReturnType<typeof this.processValueForDisplay>> { diff --git a/packages/filters/src/filter/enumerated/intersect.ts b/packages/filters/src/filter/enumerated/intersect.ts index 6904999c9..a3e83efd1 100644 --- a/packages/filters/src/filter/enumerated/intersect.ts +++ b/packages/filters/src/filter/enumerated/intersect.ts @@ -1,7 +1,7 @@ /** * Check whether two arrays have any elements in common. - * @param a Array of values. - * @param b Array of values. + * @param a - Array of values. + * @param b - Array of values. * @returns true if one or more elements are in common. */ diff --git a/packages/filters/src/filter/enumerated/objectFilter.ts b/packages/filters/src/filter/enumerated/objectFilter.ts index ebcde2343..9ede1318f 100644 --- a/packages/filters/src/filter/enumerated/objectFilter.ts +++ b/packages/filters/src/filter/enumerated/objectFilter.ts @@ -17,12 +17,12 @@ export class ObjectFilter< /** * Create a filter for properties which are objects with a string-index label and key for filtering, e.g. party objects of candidates. - * @param property The property of the entity, e.g. candidate, in which the object is stored, e.g. party - * @param keyProperty The key property of the object, usually id - * @param labelProperty The label property of the object, usually name or shortName - * @param objects A list of all the possible objects, e.g. parties - * @param name Optional name for use when displaying the filter - * @param locale The locale is used for value sorting + * @param property - The property of the entity, e.g. candidate, in which the object is stored, e.g. party + * @param keyProperty - The key property of the object, usually id + * @param labelProperty - The label property of the object, usually name or shortName + * @param objects - A list of all the possible objects, e.g. parties + * @param name - Optional name for use when displaying the filter + * @param locale - The locale is used for value sorting */ constructor( { diff --git a/packages/filters/src/filter/number/numberFilter.ts b/packages/filters/src/filter/number/numberFilter.ts index 73cad7b4b..0ef86b461 100644 --- a/packages/filters/src/filter/number/numberFilter.ts +++ b/packages/filters/src/filter/number/numberFilter.ts @@ -15,7 +15,7 @@ export abstract class NumberFilter<TTarget extends MaybeWrappedEntity> extends F /** * Create a number filter. - * @param options The filter options + * @param options - The filter options */ constructor( options: Omit<FilterOptionsBase<TTarget>, 'type' | 'multipleValues'> & @@ -30,7 +30,7 @@ export abstract class NumberFilter<TTarget extends MaybeWrappedEntity> extends F /** * Parse all the values from the targets to find min and max values. - * @input A list of entities. + * @param targets - A list of entities. * @returns The possible min and max values as well as the number of missing values. */ parseValues(targets: Array<TTarget>): { diff --git a/packages/filters/src/filter/number/numberQuestionFilter.ts b/packages/filters/src/filter/number/numberQuestionFilter.ts index 2a573851a..a665b0058 100644 --- a/packages/filters/src/filter/number/numberQuestionFilter.ts +++ b/packages/filters/src/filter/number/numberQuestionFilter.ts @@ -13,8 +13,8 @@ export class NumberQuestionFilter< /** * Create a number question filter. - * @param question The number question - * @param name Optional name for use when displaying the filter + * @param question - The number question + * @param name - Optional name for use when displaying the filter */ constructor({ question, name }: { question: NumberQuestion; name?: string }) { super({ question, name }); diff --git a/packages/filters/src/filter/rules/rules.ts b/packages/filters/src/filter/rules/rules.ts index 906655259..419603023 100644 --- a/packages/filters/src/filter/rules/rules.ts +++ b/packages/filters/src/filter/rules/rules.ts @@ -22,7 +22,7 @@ export function copyRules<TRule extends Rules>(rules: TRule): TRule { } /** - * Check whether @param rule is empty for purposes of finding out whether a filter is active or not. + * Check whether @param rule - is empty for purposes of finding out whether a filter is active or not. */ export function ruleIsActive(rule: Rule): boolean { if (rule == null || rule === '' || rule === false) return false; @@ -34,8 +34,8 @@ export function ruleIsActive(rule: Rule): boolean { /** * A simple utility to check whether to rule values are equal. - * @param a A rule value - * @param b A rule value + * @param a - A rule value + * @param b - A rule value * @returns true if the rules are equal */ export function matchRules(a: Rule, b: Rule): boolean { diff --git a/packages/filters/src/filter/text/textFilter.ts b/packages/filters/src/filter/text/textFilter.ts index 17c3c190b..6b5377c81 100644 --- a/packages/filters/src/filter/text/textFilter.ts +++ b/packages/filters/src/filter/text/textFilter.ts @@ -23,8 +23,8 @@ export class TextFilter<TEntity extends MaybeWrappedEntity = MaybeWrappedEntity> /** * Create a filter for text matching - * @param options The filter options - * @param locale The locale is used for case-insensitive matching + * @param options - The filter options + * @param locale - The locale is used for case-insensitive matching */ constructor( options: Omit<FilterOptionsBase<TEntity>, 'type'> & (PropertyFilterOptions | QuestionFilterOptions), @@ -78,7 +78,7 @@ export class TextFilter<TEntity extends MaybeWrappedEntity = MaybeWrappedEntity> } /** - * Test whether @param rule is found in @param text + * Test whether @param rule - is found in @param text */ testText(rule: string, text: string): boolean { // We do not care about leading and trailing whitespace. Because we use indexOf, we only need to trim the rule diff --git a/packages/filters/src/filter/text/textPropertyFilter.ts b/packages/filters/src/filter/text/textPropertyFilter.ts index d240aa792..de6bfd7de 100644 --- a/packages/filters/src/filter/text/textPropertyFilter.ts +++ b/packages/filters/src/filter/text/textPropertyFilter.ts @@ -7,10 +7,10 @@ export class TextPropertyFilter<TEntity extends MaybeWrappedEntity = MaybeWrappe /** * Create a filter for matching text to an entity's property. - * @param property The property name - * @param subProperty An optional sub-property name - * @param name Optional name for use when displaying the filter - * @param locale The locale is used for case-insensitive matching + * @param property - The property name + * @param subProperty - An optional sub-property name + * @param name - Optional name for use when displaying the filter + * @param locale - The locale is used for case-insensitive matching */ constructor( options: { diff --git a/packages/filters/src/filter/text/textQuestionFilter.ts b/packages/filters/src/filter/text/textQuestionFilter.ts index 2aef0f4fc..250f7dd31 100644 --- a/packages/filters/src/filter/text/textQuestionFilter.ts +++ b/packages/filters/src/filter/text/textQuestionFilter.ts @@ -9,9 +9,9 @@ export class TextQuestionFilter<TEntity extends MaybeWrappedEntity = MaybeWrappe /** * Create a filter for matching a text question. - * @param question The text question - * @param name Optional name for use when displaying the filter - * @param locale The locale is used for case-insensitive matching + * @param question - The text question + * @param name - Optional name for use when displaying the filter + * @param locale - The locale is used for case-insensitive matching */ constructor( options: { diff --git a/packages/filters/src/group/combineResults.ts b/packages/filters/src/group/combineResults.ts index 0d2a1c8ff..e3615fd69 100644 --- a/packages/filters/src/group/combineResults.ts +++ b/packages/filters/src/group/combineResults.ts @@ -14,8 +14,8 @@ export type LogicOp = (typeof LOGIC_OP)[keyof typeof LOGIC_OP]; /** * Combine the results of multiple filters. - * @param results An array of results arrays - * @param logicOperator And or Or logic operator to use in combination. @default LogicOp.And + * @param results - An array of results arrays + * @param logicOperator - And or Or logic operator to use in combination. @default LogicOp.And * @returns Combined results. */ export function combineResults<TEntity extends MaybeWrappedEntity>( diff --git a/packages/filters/src/group/filterGroup.ts b/packages/filters/src/group/filterGroup.ts index ff7816640..482698a99 100644 --- a/packages/filters/src/group/filterGroup.ts +++ b/packages/filters/src/group/filterGroup.ts @@ -39,7 +39,7 @@ export class FilterGroup<TEntity extends MaybeWrappedEntity> { /** * Apply the filters to the inputs. If the group has no active filters (or any filters at all), returns the original list. - * @input A list of entities. + * @param targets - A list of entities. * @returns Filtered targets */ apply<TTarget extends TEntity>(targets: Array<TTarget>) { @@ -102,8 +102,8 @@ export class FilterGroup<TEntity extends MaybeWrappedEntity> { /** * Add or remove an event handlers that is called when the results change. To use the changed results, access `filterGroup.results`. - * @param handler The event handler - * @add Add the event handler if true, remove it otherwise + * @param handler - The event handler + * @param add - Add the event handler if true, remove it otherwise */ onChange(handler: (filterGroup: typeof this) => void, add = true) { if (add) { @@ -115,7 +115,7 @@ export class FilterGroup<TEntity extends MaybeWrappedEntity> { /** * Wrap a function call with this to temporarily bypass `onChange` events. - * @param f The function to call without triggering `onChange` + * @param f - The function to call without triggering `onChange` */ withoutOnChange(f: () => void) { this.suspendOnChange = true; diff --git a/packages/filters/src/missingValue/missingValue.ts b/packages/filters/src/missingValue/missingValue.ts index 461850449..78c00cf96 100644 --- a/packages/filters/src/missingValue/missingValue.ts +++ b/packages/filters/src/missingValue/missingValue.ts @@ -5,7 +5,7 @@ export const MISSING_VALUE = { export type MaybeMissing<TType> = TType | typeof MISSING_VALUE; /** - * Check whether @param value is a missing value. Prefer this to explicit tests against the const because the logic may change in the future. + * Check whether @param value - is a missing value. Prefer this to explicit tests against the const because the logic may change in the future. */ export function isMissing(value: unknown): value is typeof MISSING_VALUE { return value === MISSING_VALUE; diff --git a/packages/matching/examples/example.ts b/packages/matching/examples/example.ts index 4e63b8d27..2ce2ceedc 100644 --- a/packages/matching/examples/example.ts +++ b/packages/matching/examples/example.ts @@ -84,9 +84,9 @@ function main( /** * Create dummy answers. * - * @param questions Question list - * @param answerValue The same value for all or a function to generate on - * @param missing Number of missing answers + * @param questions - Question list + * @param answerValue - The same value for all or a function to generate on + * @param missing - Number of missing answers * @returns Answer dict */ function createAnswers( diff --git a/packages/matching/src/algorithms/matchingAlgorithm.ts b/packages/matching/src/algorithms/matchingAlgorithm.ts index 68d8877b9..2b66a4335 100644 --- a/packages/matching/src/algorithms/matchingAlgorithm.ts +++ b/packages/matching/src/algorithms/matchingAlgorithm.ts @@ -24,9 +24,9 @@ export class MatchingAlgorithm { /** * Create a new MatchingAlgorithm. - * @param distanceMetric The metric to use for distance calculations, e.g. `DistanceMetric.Manhattan`. - * @param missingValueOptions The options to use for imputing missing values - * @param projector An optional projector that will project the results from one matching space to another, usually lower-dimensional one + * @param distanceMetric - The metric to use for distance calculations, e.g. `DistanceMetric.Manhattan`. + * @param missingValueOptions - The options to use for imputing missing values + * @param projector - An optional projector that will project the results from one matching space to another, usually lower-dimensional one */ constructor({ distanceMetric, missingValueOptions, projector }: MatchingAlgorithmOptions) { this.distanceMetric = distanceMetric; @@ -36,10 +36,10 @@ export class MatchingAlgorithm { /** * Calculate matches between the reference and the other targets. - * @param questions The questions to include in the matching. Note that only those of the questions that the reference has answered will be included in the matching. - * @param reference The entity to match against, e.g. voter - * @param targets The targets to match with, e.g. candidates - * @options Matching options, see. `MatchingOptions`. + * @param questions - The questions to include in the matching. Note that only those of the questions that the reference has answered will be included in the matching. + * @param reference - The entity to match against, e.g. voter + * @param targets - The targets to match with, e.g. candidates + * @param options - Matching options, see. `MatchingOptions`. * @returns An array of Match objects */ match<TTarget extends HasAnswers, TGroup extends MatchableQuestionGroup = MatchableQuestionGroup>({ @@ -123,8 +123,8 @@ export class MatchingAlgorithm { /** * Project targets into a normalized `MatchingSpace`, where distances can be calculated. - * @param questions The list of questions to use for distance calculations - * @param targets The targets to project + * @param questions - The list of questions to use for distance calculations + * @param targets - The targets to project * @returns An array of positions in the normalized `MatchingSpace` */ projectToNormalizedSpace({ diff --git a/packages/matching/src/distance/measure.ts b/packages/matching/src/distance/measure.ts index 31824ff95..ce292ec55 100644 --- a/packages/matching/src/distance/measure.ts +++ b/packages/matching/src/distance/measure.ts @@ -22,10 +22,10 @@ export function measureDistance(params: { * 1. Impute values for missing values if necessary. * 2. Measure the distances using the provided distance metric in the global space and all subspaces. * NB. The reference position `reference` is treated differently from `target` when dealing with missing values. Thus, switching them will in general yield a different result. - * @param reference The reference position to measure against. - * @param target The other position - * @param options See the `DistanceMeasurementOptions` type - * @param subspaces A list of subspaces in which distances are also measured. Used to compute, e.g., matches within question categories, in which case pass a llist of `MatchingSpaces`, where the weights of irrelevant questions are zero. It's a bit clunky to deal with subspaces here and not on a higher level, but this way we can avoid duplicate missing value imputations and distance calculations. + * @param reference - The reference position to measure against. + * @param target - The other position + * @param options - See the `DistanceMeasurementOptions` type + * @param subspaces - A list of subspaces in which distances are also measured. Used to compute, e.g., matches within question categories, in which case pass a llist of `MatchingSpaces`, where the weights of irrelevant questions are zero. It's a bit clunky to deal with subspaces here and not on a higher level, but this way we can avoid duplicate missing value imputations and distance calculations. * @returns A normalized distance, e.g. [0, 1] (the range is defined by `[0, COORDINATE.Extent]`) or both a global distance and a list of distances in `subspaces` if they are provided. */ export function measureDistance({ diff --git a/packages/matching/src/distance/metric.ts b/packages/matching/src/distance/metric.ts index 8803bf110..56a7df691 100644 --- a/packages/matching/src/distance/metric.ts +++ b/packages/matching/src/distance/metric.ts @@ -46,10 +46,10 @@ type MetricFunction = (params: { /** * Calculate the Manhattan distance between two `Position`s. See `distance` for more details. - * @param a The first `Position` - * @param b The second `Position` - * @param space An optional separate `MatchingSpace` in which to measure the distance. @default a.space - * @param allowMissing If `true` and dimensions with missing coordinates in either `Position` are ignored. Otherwise, an error will be thrown in such cases. @default false + * @param a - The first `Position` + * @param b - The second `Position` + * @param space - An optional separate `MatchingSpace` in which to measure the distance. @default a.space + * @param allowMissing - If `true` and dimensions with missing coordinates in either `Position` are ignored. Otherwise, an error will be thrown in such cases. @default false * @returns A normalized distance */ export function manhattanDistance({ @@ -76,10 +76,10 @@ export function manhattanDistance({ /** * Calculate the Directional distance between two `Position`s. See `distance` for more details. - * @param a The first `Position` - * @param b The second `Position` - * @param space An optional separate `MatchingSpace` in which to measure the distance. @default a.space - * @param allowMissing If `true` and dimensions with missing coordinates in either `Position` are ignored. Otherwise, an error will be thrown in such cases. @default false + * @param a - The first `Position` + * @param b - The second `Position` + * @param space - An optional separate `MatchingSpace` in which to measure the distance. @default a.space + * @param allowMissing - If `true` and dimensions with missing coordinates in either `Position` are ignored. Otherwise, an error will be thrown in such cases. @default false * @returns A normalized distance */ export function directionalDistance({ @@ -106,10 +106,10 @@ export function directionalDistance({ /** * Calculate the Euclidean distance between two `Position`s. See `distance` for more details. - * @param a The first `Position` - * @param b The second `Position` - * @param space An optional separate `MatchingSpace` in which to measure the distance. @default a.space - * @param allowMissing If `true` and dimensions with missing coordinates in either `Position` are ignored. Otherwise, an error will be thrown in such cases. @default false + * @param a - The first `Position` + * @param b - The second `Position` + * @param space - An optional separate `MatchingSpace` in which to measure the distance. @default a.space + * @param allowMissing - If `true` and dimensions with missing coordinates in either `Position` are ignored. Otherwise, an error will be thrown in such cases. @default false * @returns A normalized distance */ export function euclideanDistance({ @@ -187,13 +187,13 @@ export function euclideanSubdimWeight(numDimensions: number): number { * 3. Compute the distances between `Position`s for each (flattened) dimension using `kernel(a, b)` * 4. Sum the distances for each dimension using `sum(distances)` * 5. Divided the sum by a similar sum computed over the maximum possible distances - * @param a The first `Position` - * @param b The second `Position` - * @param kernel A kernel function to compute the distance between a pair of coordinates in one dimension. - * @param sum A function to compute the sum of the dimensions’ distances. - * @param subdimWeight A function to compute the relative weight of each subdimension. The function is passed the number of subdimensions and it should return the relative weight of one subdimension (the same is used for all). In most circumstances, this should be the inverse of `sum`. - * @param space An optional separate `MatchingSpace` in which to measure the distance. @default a.space - * @param allowMissing If `true` and dimensions with missing coordinates in either `Position` are ignored. Otherwise, an error will be thrown in such cases. @default false + * @param a - The first `Position` + * @param b - The second `Position` + * @param kernel - A kernel function to compute the distance between a pair of coordinates in one dimension. + * @param sum - A function to compute the sum of the dimensions’ distances. + * @param subdimWeight - A function to compute the relative weight of each subdimension. The function is passed the number of subdimensions and it should return the relative weight of one subdimension (the same is used for all). In most circumstances, this should be the inverse of `sum`. + * @param space - An optional separate `MatchingSpace` in which to measure the distance. @default a.space + * @param allowMissing - If `true` and dimensions with missing coordinates in either `Position` are ignored. Otherwise, an error will be thrown in such cases. @default false * @returns A normalized distance */ export function distance({ diff --git a/packages/matching/src/match/match.ts b/packages/matching/src/match/match.ts index c92c50d1a..423f67439 100644 --- a/packages/matching/src/match/match.ts +++ b/packages/matching/src/match/match.ts @@ -20,9 +20,9 @@ export class Match< /** * Create a new `Match`. - * @param distance The match distance as an unsigned normalized distance, e.g. [0, 1] (the range is defined by `COORDINATE.Extent`). Note that a large distance (e.g. 1) means a bad match and a low one (e.g. 0) a perfect one. - * @param target The entity to which the match belongs. - * @param subMatches Possible submatches for the target. + * @param distance - The match distance as an unsigned normalized distance, e.g. [0, 1] (the range is defined by `COORDINATE.Extent`). Note that a large distance (e.g. 1) means a bad match and a low one (e.g. 0) a perfect one. + * @param target - The entity to which the match belongs. + * @param subMatches - Possible submatches for the target. */ constructor({ distance, diff --git a/packages/matching/src/match/matchBase.ts b/packages/matching/src/match/matchBase.ts index 27f6828ec..16d86e269 100644 --- a/packages/matching/src/match/matchBase.ts +++ b/packages/matching/src/match/matchBase.ts @@ -24,7 +24,7 @@ export class MatchBase { /** * Create a new MatchBase. - * @param distance The match distance as an unsigned normalized distance, e.g. [0, 1] (the range is defined by `COORDINATE.Extent`). Note that 1 means a bad match and 0 a perfect one. + * @param distance - The match distance as an unsigned normalized distance, e.g. [0, 1] (the range is defined by `COORDINATE.Extent`). Note that 1 means a bad match and 0 a perfect one. */ constructor(distance: NormalizedDistance) { this.distance = distance; @@ -39,7 +39,7 @@ export class MatchBase { /** * Set the match distance as an unsigned normalized distance. - * @param value The match distance as an unsigned normalized distance, e.g. [0, 1] (the range is defined by `COORDINATE.Extent`). Note that 1 means a bad match and 0 a perfect one. + * @param value - The match distance as an unsigned normalized distance, e.g. [0, 1] (the range is defined by `COORDINATE.Extent`). Note that 1 means a bad match and 0 a perfect one. */ set distance(value: NormalizedDistance) { assertDistance(value); diff --git a/packages/matching/src/match/subMatch.ts b/packages/matching/src/match/subMatch.ts index e01c2845d..f8779f47a 100644 --- a/packages/matching/src/match/subMatch.ts +++ b/packages/matching/src/match/subMatch.ts @@ -11,8 +11,8 @@ export class SubMatch<TGroup extends MatchableQuestionGroup = MatchableQuestionG readonly questionGroup: TGroup; /** * Create a new `SubMatch`. - * @param distance The match distance as an unsigned normalized distance, e.g. [0, 1] (the range is defined by `COORDINATE.Extent`). Note that a large distance (e.g. 1) means a bad match and a low one (e.g. 0) a perfect one. - * @param questionGroup The subgroup of questions for which the match is computed. + * @param distance - The match distance as an unsigned normalized distance, e.g. [0, 1] (the range is defined by `COORDINATE.Extent`). Note that a large distance (e.g. 1) means a bad match and a low one (e.g. 0) a perfect one. + * @param questionGroup - The subgroup of questions for which the match is computed. */ constructor({ distance, questionGroup }: { distance: NormalizedDistance; questionGroup: TGroup }) { super(distance); diff --git a/packages/matching/src/missingValue/impute.ts b/packages/matching/src/missingValue/impute.ts index 1db1c958b..e1b910ec9 100644 --- a/packages/matching/src/missingValue/impute.ts +++ b/packages/matching/src/missingValue/impute.ts @@ -15,9 +15,9 @@ export interface MissingValueImputationOptions { /** * Impute a value for a missing one. - * @param reference The value used as reference, e.g. the voter's answer - * @param method The imputation method - * @param bias The direction of the bias used in imputing missing values, when the reference value is neutral + * @param reference - The value used as reference, e.g. the voter's answer + * @param method - The imputation method + * @param bias - The direction of the bias used in imputing missing values, when the reference value is neutral * @returns The imputed coordinate. */ export function imputeMissingValue({ @@ -46,9 +46,9 @@ export function imputeMissingValue({ /** * Impute a `Position` for where coordinates missing in `target` are imputed based on `reference`. * NB. If a coordinate is missing in `target`, the neutral coordinate is imputed. - * @param reference The `Position` used as reference, e.g. the voter's answers - * @param target The `Position` for which to impute missing coordinates - * @param options Options passed to `imputeMissingValue` + * @param reference - The `Position` used as reference, e.g. the voter's answers + * @param target - The `Position` for which to impute missing coordinates + * @param options - Options passed to `imputeMissingValue` * @returns The imputed position. */ export function imputeMissingPosition({ diff --git a/packages/matching/src/question/categoricalQuestion.ts b/packages/matching/src/question/categoricalQuestion.ts index 425f879da..13b9ce59e 100644 --- a/packages/matching/src/question/categoricalQuestion.ts +++ b/packages/matching/src/question/categoricalQuestion.ts @@ -20,9 +20,9 @@ export class CategoricalQuestion implements MatchableQuestion { [key: string]: unknown; /** - * @param id Unique id - * @param values Array of objects with a value property - * @param ordinal Whether the question is ordinal (e.g. Likert scale) @default true + * @param id - Unique id + * @param values - Array of objects with a value property + * @param ordinal - Whether the question is ordinal (e.g. Likert scale) @default true */ constructor({ id, values }: { id: Id; values: ReadonlyArray<MultipleChoiceValue> }) { if (values.length < 2) throw new Error('There must be at least 2 values in the values array.'); @@ -39,7 +39,7 @@ export class CategoricalQuestion implements MatchableQuestion { /** * Used to convert answers to the question into normalized distances for used in matching. - * @param value A question's native value + * @param value - A question's native value * @returns The value in the signed normalized range (e.g. [-.5, .5]) */ normalizeValue(value: unknown): CoordinateOrMissing | Array<CoordinateOrMissing> { diff --git a/packages/matching/src/question/ordinalQuestion.ts b/packages/matching/src/question/ordinalQuestion.ts index 2e26b692e..9f6ada245 100644 --- a/packages/matching/src/question/ordinalQuestion.ts +++ b/packages/matching/src/question/ordinalQuestion.ts @@ -23,8 +23,8 @@ export class OrdinalQuestion implements MatchableQuestion { [key: string]: unknown; /** - * @param id Unique id - * @param values Array of objects with a value property + * @param id - Unique id + * @param values - Array of objects with a value property */ constructor({ id, values }: { id: Id; values: ReadonlyArray<MultipleChoiceValue> }) { if (values.length < 2) throw new Error('There must be at least 2 values in the values array.'); @@ -44,7 +44,7 @@ export class OrdinalQuestion implements MatchableQuestion { /** * Used to convert answers to the question into normalized distances for used in matching. - * @param value A question's native value + * @param value - A question's native value * @returns The value in the signed normalized range (e.g. [-.5, .5]) */ normalizeValue(value: unknown): CoordinateOrMissing { @@ -57,7 +57,7 @@ export class OrdinalQuestion implements MatchableQuestion { /** * Utility for creating Likert questions. - * @param scale The number of options to show + * @param scale - The number of options to show * @returns A OrdinalQuestion object */ static fromLikert({ id, scale }: { id: string; scale: number }): OrdinalQuestion { diff --git a/packages/matching/src/space/createSubspace.ts b/packages/matching/src/space/createSubspace.ts index 0d402d180..0b8af5645 100644 --- a/packages/matching/src/space/createSubspace.ts +++ b/packages/matching/src/space/createSubspace.ts @@ -3,8 +3,8 @@ import type { MatchableQuestion } from '@openvaa/core'; /** * A utility function to create a subspace for a subset of questions that can be passed to `measureDistance`. The main intended use is in computing submatches for question categories, such as, 'Economy' or 'The Environment'. - * @param questions The full set of questions - * @param subset The subset of questions for which the subspace is created, effectively one where the weights of the dimensions pertaining to the exluded questions are zero. Note that if none of the questions overlap, all of the dimensions of the subspace will have zero length. + * @param questions - The full set of questions + * @param subset - The subset of questions for which the subspace is created, effectively one where the weights of the dimensions pertaining to the exluded questions are zero. Note that if none of the questions overlap, all of the dimensions of the subspace will have zero length. */ export function createSubspace({ questions, diff --git a/packages/matching/tests/utils.ts b/packages/matching/tests/utils.ts index 7cdee9504..ca52d7c48 100644 --- a/packages/matching/tests/utils.ts +++ b/packages/matching/tests/utils.ts @@ -11,11 +11,11 @@ import type { MissingValueMethod } from '../src/missingValue'; /** * Create dummy matches for testing. - * @param voterAnswers Array of voter answers as indeces of the likert scale - * @param candidateAnswers Array of Arrays of candidate answers as numbers of the likert scale (1-based) - * @param likertScale The likert scale, e.g. 5 - * @param distanceMetric The DistanceMetric to use - * @param method The MISSING_VALUE_METHOD + * @param voterAnswers - Array of voter answers as indeces of the likert scale + * @param candidateAnswers - Array of Arrays of candidate answers as numbers of the likert scale (1-based) + * @param likertScale - The likert scale, e.g. 5 + * @param distanceMetric - The DistanceMetric to use + * @param method - The MISSING_VALUE_METHOD * @returns A dict of all generated objects */ export function createMatchesAndEntities( @@ -87,8 +87,8 @@ export class MockQuestion implements MatchableQuestion { /** * Create dummy answers. - * @param questions Question list - * @param answerValues The answer values + * @param questions - Question list + * @param answerValues - The answer values * @returns An answer dict */ export function createAnswers(questions: Array<MatchableQuestion>, answerValues: Array<Id | undefined>): AnswerDict { @@ -101,8 +101,8 @@ export function createAnswers(questions: Array<MatchableQuestion>, answerValues: /** * Create dummy questions. - * @param numQuestions Number of questions to creata - * @param scale The likert scale, e.g. 5 + * @param numQuestions - Number of questions to creata + * @param scale - The likert scale, e.g. 5 * @returns Array of questions */ export function createQuestions(numQuestions: number, scale: number): Array<OrdinalQuestion> { @@ -111,8 +111,8 @@ export function createQuestions(numQuestions: number, scale: number): Array<Ordi /** * Create dummy candidates - * @param questions The dummy questions - * @param candidateAnswers Array of Arrays of candidate answers + * @param questions - The dummy questions + * @param candidateAnswers - Array of Arrays of candidate answers * @returns Array of candidates */ export function createCandidates( @@ -124,8 +124,8 @@ export function createCandidates( /** * Create a dummy Candidate to represent the voter - * @param questions The dummy questions - * @param voterAnswers Array of voter answers + * @param questions - The dummy questions + * @param voterAnswers - Array of voter answers * @returns A candidate */ export function createVoter(questions: Array<MatchableQuestion>, voterAnswers: Array<Id | undefined>): Candidate { diff --git a/packages/shared-config/package.json b/packages/shared-config/package.json index 49869502a..29990dcd2 100644 --- a/packages/shared-config/package.json +++ b/packages/shared-config/package.json @@ -10,7 +10,7 @@ "@eslint/js": "^9.17.0", "@typescript-eslint/eslint-plugin": "^8.19.1", "@typescript-eslint/parser": "^8.19.1", - "eslint": "~9.14.0", + "eslint": "^9.39.2", "eslint-config-prettier": "^9.1.0", "eslint-plugin-simple-import-sort": "^12.1.1", "prettier": "^3.4.2", diff --git a/render.example.yaml b/render.example.yaml index a21030c69..f01832f9d 100644 --- a/render.example.yaml +++ b/render.example.yaml @@ -99,8 +99,6 @@ services: value: 'false' - key: GENERATE_MOCK_DATA_ON_INITIALISE value: 'false' - - key: LOAD_DATA_ON_INITIALISE_FOLDER - value: '' # Deprecated - key: API_TOKEN_SALT generateValue: true diff --git a/yarn.lock b/yarn.lock index 997710e36..7dfddbb9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1880,6 +1880,34 @@ __metadata: languageName: node linkType: hard +"@emnapi/core@npm:^1.7.1": + version: 1.7.1 + resolution: "@emnapi/core@npm:1.7.1" + dependencies: + "@emnapi/wasi-threads": "npm:1.1.0" + tslib: "npm:^2.4.0" + checksum: 10c0/f3740be23440b439333e3ae3832163f60c96c4e35337f3220ceba88f36ee89a57a871d27c94eb7a9ff98a09911ed9a2089e477ab549f4d30029f8b907f84a351 + languageName: node + linkType: hard + +"@emnapi/runtime@npm:^1.7.1": + version: 1.7.1 + resolution: "@emnapi/runtime@npm:1.7.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/26b851cd3e93877d8732a985a2ebf5152325bbacc6204ef5336a47359dedcc23faeb08cdfcb8bb389b5401b3e894b882bc1a1e55b4b7c1ed1e67c991a760ddd5 + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.1.0, @emnapi/wasi-threads@npm:^1.1.0": + version: 1.1.0 + resolution: "@emnapi/wasi-threads@npm:1.1.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/e6d54bf2b1e64cdd83d2916411e44e579b6ae35d5def0dea61a3c452d9921373044dff32a8b8473ae60c80692bdc39323e98b96a3f3d87ba6886b24dd0ef7ca1 + languageName: node + linkType: hard + "@emotion/babel-plugin@npm:^11.13.5": version: 11.13.5 resolution: "@emotion/babel-plugin@npm:11.13.5" @@ -2055,6 +2083,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/aix-ppc64@npm:0.27.1" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/android-arm64@npm:0.16.17" @@ -2097,6 +2132,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/android-arm64@npm:0.27.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/android-arm@npm:0.16.17" @@ -2139,6 +2181,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/android-arm@npm:0.27.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/android-x64@npm:0.16.17" @@ -2181,6 +2230,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/android-x64@npm:0.27.1" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/darwin-arm64@npm:0.16.17" @@ -2223,6 +2279,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/darwin-arm64@npm:0.27.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/darwin-x64@npm:0.16.17" @@ -2265,6 +2328,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/darwin-x64@npm:0.27.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/freebsd-arm64@npm:0.16.17" @@ -2307,6 +2377,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/freebsd-arm64@npm:0.27.1" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/freebsd-x64@npm:0.16.17" @@ -2349,6 +2426,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/freebsd-x64@npm:0.27.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-arm64@npm:0.16.17" @@ -2391,6 +2475,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/linux-arm64@npm:0.27.1" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-arm@npm:0.16.17" @@ -2433,6 +2524,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/linux-arm@npm:0.27.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-ia32@npm:0.16.17" @@ -2475,6 +2573,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/linux-ia32@npm:0.27.1" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-loong64@npm:0.16.17" @@ -2517,6 +2622,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/linux-loong64@npm:0.27.1" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-mips64el@npm:0.16.17" @@ -2559,6 +2671,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/linux-mips64el@npm:0.27.1" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-ppc64@npm:0.16.17" @@ -2601,6 +2720,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/linux-ppc64@npm:0.27.1" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-riscv64@npm:0.16.17" @@ -2643,6 +2769,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/linux-riscv64@npm:0.27.1" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-s390x@npm:0.16.17" @@ -2685,6 +2818,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/linux-s390x@npm:0.27.1" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-x64@npm:0.16.17" @@ -2727,6 +2867,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/linux-x64@npm:0.27.1" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-arm64@npm:0.25.2": version: 0.25.2 resolution: "@esbuild/netbsd-arm64@npm:0.25.2" @@ -2734,6 +2881,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-arm64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/netbsd-arm64@npm:0.27.1" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/netbsd-x64@npm:0.16.17" @@ -2776,6 +2930,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/netbsd-x64@npm:0.27.1" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-arm64@npm:0.23.1": version: 0.23.1 resolution: "@esbuild/openbsd-arm64@npm:0.23.1" @@ -2790,6 +2951,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-arm64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/openbsd-arm64@npm:0.27.1" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/openbsd-x64@npm:0.16.17" @@ -2832,6 +3000,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/openbsd-x64@npm:0.27.1" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/openharmony-arm64@npm:0.27.1" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/sunos-x64@npm:0.16.17" @@ -2874,6 +3056,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/sunos-x64@npm:0.27.1" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/win32-arm64@npm:0.16.17" @@ -2916,6 +3105,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/win32-arm64@npm:0.27.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/win32-ia32@npm:0.16.17" @@ -2958,6 +3154,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/win32-ia32@npm:0.27.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/win32-x64@npm:0.16.17" @@ -3000,7 +3203,14 @@ __metadata: languageName: node linkType: hard -"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": +"@esbuild/win32-x64@npm:0.27.1": + version: 0.27.1 + resolution: "@esbuild/win32-x64@npm:0.27.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.1 resolution: "@eslint-community/eslint-utils@npm:4.4.1" dependencies: @@ -3011,6 +3221,17 @@ __metadata: languageName: node linkType: hard +"@eslint-community/eslint-utils@npm:^4.6.1, @eslint-community/eslint-utils@npm:^4.7.0, @eslint-community/eslint-utils@npm:^4.8.0": + version: 4.9.0 + resolution: "@eslint-community/eslint-utils@npm:4.9.0" + dependencies: + eslint-visitor-keys: "npm:^3.4.3" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10c0/8881e22d519326e7dba85ea915ac7a143367c805e6ba1374c987aa2fbdd09195cc51183d2da72c0e2ff388f84363e1b220fd0d19bef10c272c63455162176817 + languageName: node + linkType: hard + "@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.1": version: 4.12.1 resolution: "@eslint-community/regexpp@npm:4.12.1" @@ -3018,34 +3239,50 @@ __metadata: languageName: node linkType: hard -"@eslint/config-array@npm:^0.18.0": - version: 0.18.0 - resolution: "@eslint/config-array@npm:0.18.0" +"@eslint/compat@npm:^1.4.0": + version: 1.4.1 + resolution: "@eslint/compat@npm:1.4.1" + dependencies: + "@eslint/core": "npm:^0.17.0" + peerDependencies: + eslint: ^8.40 || 9 + peerDependenciesMeta: + eslint: + optional: true + checksum: 10c0/46f5ff884873c2e2366df55dd7b2d6b12f7f852bfba8e2a48dae4819cc5e58756deefa9b7f87f1b107af725ee883a05fcc02caf969b58fb142e790c6036a0450 + languageName: node + linkType: hard + +"@eslint/config-array@npm:^0.21.1": + version: 0.21.1 + resolution: "@eslint/config-array@npm:0.21.1" dependencies: - "@eslint/object-schema": "npm:^2.1.4" + "@eslint/object-schema": "npm:^2.1.7" debug: "npm:^4.3.1" minimatch: "npm:^3.1.2" - checksum: 10c0/0234aeb3e6b052ad2402a647d0b4f8a6aa71524bafe1adad0b8db1dfe94d7f5f26d67c80f79bb37ac61361a1d4b14bb8fb475efe501de37263cf55eabb79868f + checksum: 10c0/2f657d4edd6ddcb920579b72e7a5b127865d4c3fb4dda24f11d5c4f445a93ca481aebdbd6bf3291c536f5d034458dbcbb298ee3b698bc6c9dd02900fe87eec3c languageName: node linkType: hard -"@eslint/core@npm:^0.10.0": - version: 0.10.0 - resolution: "@eslint/core@npm:0.10.0" +"@eslint/config-helpers@npm:^0.4.2": + version: 0.4.2 + resolution: "@eslint/config-helpers@npm:0.4.2" dependencies: - "@types/json-schema": "npm:^7.0.15" - checksum: 10c0/074018075079b3ed1f14fab9d116f11a8824cdfae3e822badf7ad546962fafe717a31e61459bad8cc59cf7070dc413ea9064ddb75c114f05b05921029cde0a64 + "@eslint/core": "npm:^0.17.0" + checksum: 10c0/92efd7a527b2d17eb1a148409d71d80f9ac160b565ac73ee092252e8bf08ecd08670699f46b306b94f13d22e88ac88a612120e7847570dd7cdc72f234d50dcb4 languageName: node linkType: hard -"@eslint/core@npm:^0.7.0": - version: 0.7.0 - resolution: "@eslint/core@npm:0.7.0" - checksum: 10c0/3cdee8bc6cbb96ac6103d3ead42e59830019435839583c9eb352b94ed558bd78e7ffad5286dc710df21ec1e7bd8f52aa6574c62457a4dd0f01f3736fa4a7d87a +"@eslint/core@npm:^0.17.0": + version: 0.17.0 + resolution: "@eslint/core@npm:0.17.0" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10c0/9a580f2246633bc752298e7440dd942ec421860d1946d0801f0423830e67887e4aeba10ab9a23d281727a978eb93d053d1922a587d502942a713607f40ed704e languageName: node linkType: hard -"@eslint/eslintrc@npm:^3.1.0, @eslint/eslintrc@npm:^3.2.0": +"@eslint/eslintrc@npm:^3.2.0": version: 3.2.0 resolution: "@eslint/eslintrc@npm:3.2.0" dependencies: @@ -3062,10 +3299,27 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.14.0": - version: 9.14.0 - resolution: "@eslint/js@npm:9.14.0" - checksum: 10c0/a423dd435e10aa3b461599aa02f6cbadd4b5128cb122467ee4e2c798e7ca4f9bb1fce4dcea003b29b983090238cf120899c1af657cf86300b399e4f996b83ddc +"@eslint/eslintrc@npm:^3.3.1": + version: 3.3.3 + resolution: "@eslint/eslintrc@npm:3.3.3" + dependencies: + ajv: "npm:^6.12.4" + debug: "npm:^4.3.2" + espree: "npm:^10.0.1" + globals: "npm:^14.0.0" + ignore: "npm:^5.2.0" + import-fresh: "npm:^3.2.1" + js-yaml: "npm:^4.1.1" + minimatch: "npm:^3.1.2" + strip-json-comments: "npm:^3.1.1" + checksum: 10c0/532c7acc7ddd042724c28b1f020bd7bf148fcd4653bb44c8314168b5f772508c842ce4ee070299cac51c5c5757d2124bdcfcef5551c8c58ff9986e3e17f2260d + languageName: node + linkType: hard + +"@eslint/js@npm:9.39.2, @eslint/js@npm:^9.39.1": + version: 9.39.2 + resolution: "@eslint/js@npm:9.39.2" + checksum: 10c0/00f51c52b04ac79faebfaa65a9652b2093b9c924e945479f1f3945473f78aee83cbc76c8d70bbffbf06f7024626575b16d97b66eab16182e1d0d39daff2f26f5 languageName: node linkType: hard @@ -3076,20 +3330,20 @@ __metadata: languageName: node linkType: hard -"@eslint/object-schema@npm:^2.1.4": - version: 2.1.5 - resolution: "@eslint/object-schema@npm:2.1.5" - checksum: 10c0/5320691ed41ecd09a55aff40ce8e56596b4eb81f3d4d6fe530c50fdd6552d88102d1c1a29d970ae798ce30849752a708772de38ded07a6f25b3da32ebea081d8 +"@eslint/object-schema@npm:^2.1.7": + version: 2.1.7 + resolution: "@eslint/object-schema@npm:2.1.7" + checksum: 10c0/936b6e499853d1335803f556d526c86f5fe2259ed241bc665000e1d6353828edd913feed43120d150adb75570cae162cf000b5b0dfc9596726761c36b82f4e87 languageName: node linkType: hard -"@eslint/plugin-kit@npm:^0.2.0": - version: 0.2.5 - resolution: "@eslint/plugin-kit@npm:0.2.5" +"@eslint/plugin-kit@npm:^0.4.1": + version: 0.4.1 + resolution: "@eslint/plugin-kit@npm:0.4.1" dependencies: - "@eslint/core": "npm:^0.10.0" + "@eslint/core": "npm:^0.17.0" levn: "npm:^0.4.1" - checksum: 10c0/ba9832b8409af618cf61791805fe201dd62f3c82c783adfcec0f5cd391e68b40beaecb47b9a3209e926dbcab65135f410cae405b69a559197795793399f61176 + checksum: 10c0/51600f78b798f172a9915dffb295e2ffb44840d583427bc732baf12ecb963eb841b253300e657da91d890f4b323d10a1bd12934bf293e3018d8bb66fdce5217b languageName: node linkType: hard @@ -3502,6 +3756,19 @@ __metadata: languageName: node linkType: hard +"@gerrit0/mini-shiki@npm:^3.17.0": + version: 3.20.0 + resolution: "@gerrit0/mini-shiki@npm:3.20.0" + dependencies: + "@shikijs/engine-oniguruma": "npm:^3.20.0" + "@shikijs/langs": "npm:^3.20.0" + "@shikijs/themes": "npm:^3.20.0" + "@shikijs/types": "npm:^3.20.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + checksum: 10c0/d362ea7c20709ddfaa651003d8013d58aa6e63ff1ec49f20461ce49708ad5963c2aec3c3b4c322bb4646b9c8c761ccbf5acfe7a29d8507930de163ba3591d4af + languageName: node + linkType: hard + "@hapi/bourne@npm:^3.0.0": version: 3.0.0 resolution: "@hapi/bourne@npm:3.0.0" @@ -3540,10 +3807,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/retry@npm:^0.4.0": - version: 0.4.1 - resolution: "@humanwhocodes/retry@npm:0.4.1" - checksum: 10c0/be7bb6841c4c01d0b767d9bb1ec1c9359ee61421ce8ba66c249d035c5acdfd080f32d55a5c9e859cdd7868788b8935774f65b2caf24ec0b7bd7bf333791f063b +"@humanwhocodes/retry@npm:^0.4.2": + version: 0.4.3 + resolution: "@humanwhocodes/retry@npm:0.4.3" + checksum: 10c0/3775bb30087d4440b3f7406d5a057777d90e4b9f435af488a4923ef249e93615fb78565a85f173a186a076c7706a81d0d57d563a2624e4de2c5c9c66c486ce42 languageName: node linkType: hard @@ -3689,6 +3956,22 @@ __metadata: languageName: node linkType: hard +"@isaacs/balanced-match@npm:^4.0.1": + version: 4.0.1 + resolution: "@isaacs/balanced-match@npm:4.0.1" + checksum: 10c0/7da011805b259ec5c955f01cee903da72ad97c5e6f01ca96197267d3f33103d5b2f8a1af192140f3aa64526c593c8d098ae366c2b11f7f17645d12387c2fd420 + languageName: node + linkType: hard + +"@isaacs/brace-expansion@npm:^5.0.0": + version: 5.0.0 + resolution: "@isaacs/brace-expansion@npm:5.0.0" + dependencies: + "@isaacs/balanced-match": "npm:^4.0.1" + checksum: 10c0/b4d4812f4be53afc2c5b6c545001ff7a4659af68d4484804e9d514e183d20269bb81def8682c01a22b17c4d6aed14292c8494f7d2ac664e547101c1a905aa977 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -3973,6 +4256,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/remapping@npm:^2.3.4": + version: 2.3.5 + resolution: "@jridgewell/remapping@npm:2.3.5" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/3de494219ffeb2c5c38711d0d7bb128097edf91893090a2dbc8ee0b55d092bb7347b1fd0f478486c5eab010e855c73927b1666f2107516d472d24a73017d1194 + languageName: node + linkType: hard + "@jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.2 resolution: "@jridgewell/resolve-uri@npm:3.1.2" @@ -4004,6 +4297,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.5.5": + version: 1.5.5 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" + checksum: 10c0/f9e538f302b63c0ebc06eecb1dd9918dd4289ed36147a0ddce35d6ea4d7ebbda243cda7b2213b6a5e1d8087a298d5cf630fb2bd39329cdecb82017023f6081a0 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": version: 0.3.25 resolution: "@jridgewell/trace-mapping@npm:0.3.25" @@ -4149,6 +4449,17 @@ __metadata: languageName: node linkType: hard +"@napi-rs/wasm-runtime@npm:^1.1.0": + version: 1.1.0 + resolution: "@napi-rs/wasm-runtime@npm:1.1.0" + dependencies: + "@emnapi/core": "npm:^1.7.1" + "@emnapi/runtime": "npm:^1.7.1" + "@tybys/wasm-util": "npm:^0.10.1" + checksum: 10c0/ee351052123bfc635c4cef03ac273a686522394ccd513b1e5b7b3823cecd6abb4a31f23a3a962933192b87eb7b7c3eb3def7748bd410edc66f932d90cf44e9ab + languageName: node + linkType: hard + "@noble/hashes@npm:^1.1.5": version: 1.6.1 resolution: "@noble/hashes@npm:1.6.1" @@ -4286,6 +4597,54 @@ __metadata: languageName: unknown linkType: soft +"@openvaa/docs@workspace:docs": + version: 0.0.0-use.local + resolution: "@openvaa/docs@workspace:docs" + dependencies: + "@eslint/compat": "npm:^1.4.0" + "@eslint/js": "npm:^9.39.1" + "@openvaa/app-shared": "workspace:^" + "@openvaa/shared-config": "workspace:^" + "@playwright/test": "npm:^1.57.0" + "@sveltejs/adapter-static": "npm:^3.0.10" + "@sveltejs/kit": "npm:^2.49.1" + "@sveltejs/vite-plugin-svelte": "npm:^6.2.1" + "@tailwindcss/forms": "npm:^0.5.10" + "@tailwindcss/typography": "npm:^0.5.19" + "@tailwindcss/vite": "npm:^4.1.17" + "@types/node": "npm:^20" + "@typescript-eslint/eslint-plugin": "npm:^8.19.1" + "@typescript-eslint/parser": "npm:^8.19.1" + "@vitest/browser-playwright": "npm:^4.0.15" + daisyui: "npm:^5.5.14" + eslint: "npm:^9.39.2" + eslint-config-prettier: "npm:^10.1.8" + eslint-plugin-simple-import-sort: "npm:^12.1.1" + eslint-plugin-svelte: "npm:^3.13.1" + globals: "npm:^16.5.0" + mdsvex: "npm:^0.12.6" + playwright: "npm:^1.57.0" + prettier: "npm:^3.7.4" + prettier-plugin-svelte: "npm:^3.4.0" + prettier-plugin-tailwindcss: "npm:^0.7.2" + rehype-autolink-headings: "npm:^7.1.0" + rehype-slug: "npm:^6.0.0" + svelte: "npm:^5.46.1" + svelte-check: "npm:^4.3.4" + tailwindcss: "npm:^4.1.17" + tsx: "npm:^4.19.2" + typedoc: "npm:^0.28.15" + typedoc-plugin-markdown: "npm:^4.9.0" + typescript: "npm:^5.7.2" + typescript-eslint: "npm:^8.48.1" + unist-util-visit: "npm:^5.0.0" + vite: "npm:^7.2.6" + vite-plugin-devtools-json: "npm:^1.0.0" + vitest: "npm:^4.0.15" + vitest-browser-svelte: "npm:^2.0.1" + languageName: unknown + linkType: soft + "@openvaa/filters@workspace:^, @openvaa/filters@workspace:packages/filters": version: 0.0.0-use.local resolution: "@openvaa/filters@workspace:packages/filters" @@ -4331,7 +4690,7 @@ __metadata: ai: "npm:^5.0.0" autoprefixer: "npm:^10.4.20" daisyui: "npm:^4.12.23" - eslint: "npm:~9.14.0" + eslint: "npm:^9.39.2" eslint-plugin-svelte: "npm:^2.46.1" flat-cache: "npm:^6.1.7" globals: "npm:^15.14.0" @@ -4418,7 +4777,7 @@ __metadata: "@eslint/js": "npm:^9.17.0" "@typescript-eslint/eslint-plugin": "npm:^8.19.1" "@typescript-eslint/parser": "npm:^8.19.1" - eslint: "npm:~9.14.0" + eslint: "npm:^9.39.2" eslint-config-prettier: "npm:^9.1.0" eslint-plugin-simple-import-sort: "npm:^12.1.1" prettier: "npm:^3.4.2" @@ -4478,7 +4837,7 @@ __metadata: "@types/react": "npm:^18.3.18" "@types/react-is": "npm:^19.0.0" "@typescript-eslint/parser": "npm:^8.19.1" - eslint: "npm:~9.14.0" + eslint: "npm:^9.39.2" globals: "npm:^15.14.0" jest: "npm:^29.7.0" pg: "npm:^8.13.1" @@ -4530,6 +4889,17 @@ __metadata: languageName: node linkType: hard +"@playwright/test@npm:^1.57.0": + version: 1.57.0 + resolution: "@playwright/test@npm:1.57.0" + dependencies: + playwright: "npm:1.57.0" + bin: + playwright: cli.js + checksum: 10c0/35ba4b28be72bf0a53e33dbb11c6cff848fb9a37f49e893ce63a90675b5291ec29a1ba82c8a3b043abaead129400f0589623e9ace2e6a1c8eaa409721ecc3774 + languageName: node + linkType: hard + "@pmmmwh/react-refresh-webpack-plugin@npm:0.5.15": version: 0.5.15 resolution: "@pmmmwh/react-refresh-webpack-plugin@npm:0.5.15" @@ -5695,6 +6065,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.53.4" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@rollup/rollup-android-arm64@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-android-arm64@npm:4.29.1" @@ -5702,6 +6079,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm64@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-android-arm64@npm:4.53.4" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-arm64@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-darwin-arm64@npm:4.29.1" @@ -5709,6 +6093,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-arm64@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-darwin-arm64@npm:4.53.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-x64@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-darwin-x64@npm:4.29.1" @@ -5716,6 +6107,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-x64@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-darwin-x64@npm:4.53.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-freebsd-arm64@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-freebsd-arm64@npm:4.29.1" @@ -5723,6 +6121,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-freebsd-arm64@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.53.4" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-freebsd-x64@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-freebsd-x64@npm:4.29.1" @@ -5730,6 +6135,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-freebsd-x64@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-freebsd-x64@npm:4.53.4" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-gnueabihf@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.29.1" @@ -5737,6 +6149,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-gnueabihf@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.53.4" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-musleabihf@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.29.1" @@ -5744,6 +6163,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-musleabihf@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.53.4" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-gnu@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.29.1" @@ -5751,6 +6177,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-gnu@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.53.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-musl@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-linux-arm64-musl@npm:4.29.1" @@ -5758,6 +6191,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-musl@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.53.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.53.4" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-loongarch64-gnu@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.29.1" @@ -5772,6 +6219,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-ppc64-gnu@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.53.4" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-riscv64-gnu@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.29.1" @@ -5779,10 +6233,31 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.29.1": - version: 4.29.1 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.29.1" - conditions: os=linux & cpu=s390x & libc=glibc +"@rollup/rollup-linux-riscv64-gnu@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.53.4" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.53.4" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.29.1": + version: 4.29.1 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.29.1" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.53.4" + conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard @@ -5793,6 +6268,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-gnu@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.53.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-musl@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-linux-x64-musl@npm:4.29.1" @@ -5800,6 +6282,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-musl@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.53.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.53.4" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-win32-arm64-msvc@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.29.1" @@ -5807,6 +6303,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-arm64-msvc@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.53.4" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-win32-ia32-msvc@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.29.1" @@ -5814,6 +6317,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-ia32-msvc@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.53.4" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.53.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-win32-x64-msvc@npm:4.29.1": version: 4.29.1 resolution: "@rollup/rollup-win32-x64-msvc@npm:4.29.1" @@ -5821,6 +6338,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-x64-msvc@npm:4.53.4": + version: 4.53.4 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.53.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rushstack/node-core-library@npm:5.10.1": version: 5.10.1 resolution: "@rushstack/node-core-library@npm:5.10.1" @@ -5886,6 +6410,51 @@ __metadata: languageName: node linkType: hard +"@shikijs/engine-oniguruma@npm:^3.20.0": + version: 3.20.0 + resolution: "@shikijs/engine-oniguruma@npm:3.20.0" + dependencies: + "@shikijs/types": "npm:3.20.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + checksum: 10c0/4a5a8f316a8482e799cd836c8e3f8ba83274b3631b2d66ec82ad839b0ee1dd3df50a08480f791d59f22e42bf6b707032f043a9f651445efc04e59b7ec9e669c9 + languageName: node + linkType: hard + +"@shikijs/langs@npm:^3.20.0": + version: 3.20.0 + resolution: "@shikijs/langs@npm:3.20.0" + dependencies: + "@shikijs/types": "npm:3.20.0" + checksum: 10c0/6830460025d0df4c527ffeacf0a78cd4331ffde1cfcd1e8028aa9814be8a4cea84367dd938528a9b55de72b163c58ad3576915ea08c3d0a29ef1dc80e120116c + languageName: node + linkType: hard + +"@shikijs/themes@npm:^3.20.0": + version: 3.20.0 + resolution: "@shikijs/themes@npm:3.20.0" + dependencies: + "@shikijs/types": "npm:3.20.0" + checksum: 10c0/d6fc059c51c3c0694e026cc1f80eed927d9b91adaeda0fb3fd5725eabc6d066aaf022919def435245446ae91e3da541ed6d88d875cf59a35bfbabb6920efb6da + languageName: node + linkType: hard + +"@shikijs/types@npm:3.20.0, @shikijs/types@npm:^3.20.0": + version: 3.20.0 + resolution: "@shikijs/types@npm:3.20.0" + dependencies: + "@shikijs/vscode-textmate": "npm:^10.0.2" + "@types/hast": "npm:^3.0.4" + checksum: 10c0/7faea130362a6cdf3d66fcb47d6b609a8e0209e76ba86688f56a65411b6ae400a37414cd1a3a2fe1ee3fe39f18e274585d3972129c7e79244aaa0c15bc8f1c21 + languageName: node + linkType: hard + +"@shikijs/vscode-textmate@npm:^10.0.2": + version: 10.0.2 + resolution: "@shikijs/vscode-textmate@npm:10.0.2" + checksum: 10c0/36b682d691088ec244de292dc8f91b808f95c89466af421cf84cbab92230f03c8348649c14b3251991b10ce632b0c715e416e992dd5f28ff3221dc2693fd9462 + languageName: node + linkType: hard + "@simov/deep-extend@npm:^1.0.0": version: 1.0.0 resolution: "@simov/deep-extend@npm:1.0.0" @@ -8193,6 +8762,15 @@ __metadata: languageName: node linkType: hard +"@sveltejs/acorn-typescript@npm:^1.0.5": + version: 1.0.8 + resolution: "@sveltejs/acorn-typescript@npm:1.0.8" + peerDependencies: + acorn: ^8.9.0 + checksum: 10c0/3de68af48db0b9cbc82872b218cd9134f494ba7716872e8c11bfdbb156b11dba2205541a627eed943733e4a4e8bbb261fe898bf7659105e1a4c641033fe3d4fe + languageName: node + linkType: hard + "@sveltejs/adapter-auto@npm:^3.3.1": version: 3.3.1 resolution: "@sveltejs/adapter-auto@npm:3.3.1" @@ -8218,6 +8796,15 @@ __metadata: languageName: node linkType: hard +"@sveltejs/adapter-static@npm:^3.0.10": + version: 3.0.10 + resolution: "@sveltejs/adapter-static@npm:3.0.10" + peerDependencies: + "@sveltejs/kit": ^2.0.0 + checksum: 10c0/14a8c05e1ef2bd07fe0fbc709f13f13cdd17b8f7940ccae61f6aa1b2c1a32e8067c2a36b3a80b10558a52fe2c8b07f581b848af2fe123c41b79a5fc4ec8d3140 + languageName: node + linkType: hard + "@sveltejs/kit@npm:^2.15.2": version: 2.15.2 resolution: "@sveltejs/kit@npm:2.15.2" @@ -8244,6 +8831,37 @@ __metadata: languageName: node linkType: hard +"@sveltejs/kit@npm:^2.49.1": + version: 2.49.2 + resolution: "@sveltejs/kit@npm:2.49.2" + dependencies: + "@standard-schema/spec": "npm:^1.0.0" + "@sveltejs/acorn-typescript": "npm:^1.0.5" + "@types/cookie": "npm:^0.6.0" + acorn: "npm:^8.14.1" + cookie: "npm:^0.6.0" + devalue: "npm:^5.3.2" + esm-env: "npm:^1.2.2" + kleur: "npm:^4.1.5" + magic-string: "npm:^0.30.5" + mrmime: "npm:^2.0.0" + sade: "npm:^1.8.1" + set-cookie-parser: "npm:^2.6.0" + sirv: "npm:^3.0.0" + peerDependencies: + "@opentelemetry/api": ^1.0.0 + "@sveltejs/vite-plugin-svelte": ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + bin: + svelte-kit: svelte-kit.js + checksum: 10c0/05bf6d0d9fb87d894d1a5667f0473d8e83a4584f5c80850ed9eef1d1c8982236ea0d703bbd83befdff30c729d102fc67f2839014d900edb15f6b449ee059b9aa + languageName: node + linkType: hard + "@sveltejs/vite-plugin-svelte-inspector@npm:^2.1.0": version: 2.1.0 resolution: "@sveltejs/vite-plugin-svelte-inspector@npm:2.1.0" @@ -8257,6 +8875,19 @@ __metadata: languageName: node linkType: hard +"@sveltejs/vite-plugin-svelte-inspector@npm:^5.0.0": + version: 5.0.1 + resolution: "@sveltejs/vite-plugin-svelte-inspector@npm:5.0.1" + dependencies: + debug: "npm:^4.4.1" + peerDependencies: + "@sveltejs/vite-plugin-svelte": ^6.0.0-next.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + checksum: 10c0/d8bdd22936c60098640c82d12f976e978c1de0a9a801a49f0a438aa4fc8a47847460a7412613151e1e4423b56ab0f436b96b6386f992da5a464e02d8634e72b7 + languageName: node + linkType: hard + "@sveltejs/vite-plugin-svelte@npm:^3.1.2": version: 3.1.2 resolution: "@sveltejs/vite-plugin-svelte@npm:3.1.2" @@ -8275,6 +8906,22 @@ __metadata: languageName: node linkType: hard +"@sveltejs/vite-plugin-svelte@npm:^6.2.1": + version: 6.2.1 + resolution: "@sveltejs/vite-plugin-svelte@npm:6.2.1" + dependencies: + "@sveltejs/vite-plugin-svelte-inspector": "npm:^5.0.0" + debug: "npm:^4.4.1" + deepmerge: "npm:^4.3.1" + magic-string: "npm:^0.30.17" + vitefu: "npm:^1.1.1" + peerDependencies: + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + checksum: 10c0/b521837fbcf33586e1013d3b8b1b2ab20158a3e35ccc9db553b94a8eeb136be1f113705a8d9c1bbb086729fa621721eaa17e354b7f1b5f29818b6244028af26e + languageName: node + linkType: hard + "@sveltekit-i18n/base@npm:~1.3.0": version: 1.3.7 resolution: "@sveltekit-i18n/base@npm:1.3.7" @@ -8450,6 +9097,192 @@ __metadata: languageName: node linkType: hard +"@tailwindcss/forms@npm:^0.5.10": + version: 0.5.10 + resolution: "@tailwindcss/forms@npm:0.5.10" + dependencies: + mini-svg-data-uri: "npm:^1.2.3" + peerDependencies: + tailwindcss: ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + checksum: 10c0/235cbf08edf09362418808ebddcc767c9e151fba55f3d7d2d5c47862c715b6169204b9277461036707b4c30f3fb1e217933278f80525840e198f8e7dd9168e0d + languageName: node + linkType: hard + +"@tailwindcss/node@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/node@npm:4.1.18" + dependencies: + "@jridgewell/remapping": "npm:^2.3.4" + enhanced-resolve: "npm:^5.18.3" + jiti: "npm:^2.6.1" + lightningcss: "npm:1.30.2" + magic-string: "npm:^0.30.21" + source-map-js: "npm:^1.2.1" + tailwindcss: "npm:4.1.18" + checksum: 10c0/0527f4cb602a80413a7f135edc9a9c785edd543cceedd046ed2401d4c35c1ec433d5162c325d31ee7248f3560a709dafe30a50c1406662f28a2b3aaeb21f69fe + languageName: node + linkType: hard + +"@tailwindcss/oxide-android-arm64@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-android-arm64@npm:4.1.18" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-darwin-arm64@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-darwin-arm64@npm:4.1.18" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-darwin-x64@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-darwin-x64@npm:4.1.18" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-freebsd-x64@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-freebsd-x64@npm:4.1.18" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.1.18" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm64-gnu@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-linux-arm64-gnu@npm:4.1.18" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm64-musl@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-linux-arm64-musl@npm:4.1.18" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-x64-gnu@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-linux-x64-gnu@npm:4.1.18" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-x64-musl@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-linux-x64-musl@npm:4.1.18" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@tailwindcss/oxide-wasm32-wasi@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-wasm32-wasi@npm:4.1.18" + dependencies: + "@emnapi/core": "npm:^1.7.1" + "@emnapi/runtime": "npm:^1.7.1" + "@emnapi/wasi-threads": "npm:^1.1.0" + "@napi-rs/wasm-runtime": "npm:^1.1.0" + "@tybys/wasm-util": "npm:^0.10.1" + tslib: "npm:^2.4.0" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@tailwindcss/oxide-win32-arm64-msvc@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-win32-arm64-msvc@npm:4.1.18" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-win32-x64-msvc@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide-win32-x64-msvc@npm:4.1.18" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide@npm:4.1.18": + version: 4.1.18 + resolution: "@tailwindcss/oxide@npm:4.1.18" + dependencies: + "@tailwindcss/oxide-android-arm64": "npm:4.1.18" + "@tailwindcss/oxide-darwin-arm64": "npm:4.1.18" + "@tailwindcss/oxide-darwin-x64": "npm:4.1.18" + "@tailwindcss/oxide-freebsd-x64": "npm:4.1.18" + "@tailwindcss/oxide-linux-arm-gnueabihf": "npm:4.1.18" + "@tailwindcss/oxide-linux-arm64-gnu": "npm:4.1.18" + "@tailwindcss/oxide-linux-arm64-musl": "npm:4.1.18" + "@tailwindcss/oxide-linux-x64-gnu": "npm:4.1.18" + "@tailwindcss/oxide-linux-x64-musl": "npm:4.1.18" + "@tailwindcss/oxide-wasm32-wasi": "npm:4.1.18" + "@tailwindcss/oxide-win32-arm64-msvc": "npm:4.1.18" + "@tailwindcss/oxide-win32-x64-msvc": "npm:4.1.18" + dependenciesMeta: + "@tailwindcss/oxide-android-arm64": + optional: true + "@tailwindcss/oxide-darwin-arm64": + optional: true + "@tailwindcss/oxide-darwin-x64": + optional: true + "@tailwindcss/oxide-freebsd-x64": + optional: true + "@tailwindcss/oxide-linux-arm-gnueabihf": + optional: true + "@tailwindcss/oxide-linux-arm64-gnu": + optional: true + "@tailwindcss/oxide-linux-arm64-musl": + optional: true + "@tailwindcss/oxide-linux-x64-gnu": + optional: true + "@tailwindcss/oxide-linux-x64-musl": + optional: true + "@tailwindcss/oxide-wasm32-wasi": + optional: true + "@tailwindcss/oxide-win32-arm64-msvc": + optional: true + "@tailwindcss/oxide-win32-x64-msvc": + optional: true + checksum: 10c0/1ff978ef24ffae6369e0468bd8c71d1995a00f1697ac1b8f24e92d2d5505ae23534e6257194e78360c16abbe34fc70de508c86d589917336067a60d755b86fcb + languageName: node + linkType: hard + +"@tailwindcss/typography@npm:^0.5.19": + version: 0.5.19 + resolution: "@tailwindcss/typography@npm:0.5.19" + dependencies: + postcss-selector-parser: "npm:6.0.10" + peerDependencies: + tailwindcss: "*" + checksum: 10c0/b9eb38e9c7adca59b55d7321275f59ea62c7d65a0c3d324c18c1c5864c69ec6e15b838e7df61e531cda62cfea08627b4343bc419bb0996182321891e5171e4d6 + languageName: node + linkType: hard + +"@tailwindcss/vite@npm:^4.1.17": + version: 4.1.18 + resolution: "@tailwindcss/vite@npm:4.1.18" + dependencies: + "@tailwindcss/node": "npm:4.1.18" + "@tailwindcss/oxide": "npm:4.1.18" + tailwindcss: "npm:4.1.18" + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + checksum: 10c0/7605364d29cd5683948f7b74f22c5f3b39b89b54d25b3b1094f8300ec6fe9f053f73246170debad86e01080f858d0bf6d0ef8e398a9dc0ce1f5a02c34447726b + languageName: node + linkType: hard + "@testing-library/dom@npm:10.1.0": version: 10.1.0 resolution: "@testing-library/dom@npm:10.1.0" @@ -8540,6 +9373,15 @@ __metadata: languageName: node linkType: hard +"@tybys/wasm-util@npm:^0.10.1": + version: 0.10.1 + resolution: "@tybys/wasm-util@npm:0.10.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/b255094f293794c6d2289300c5fbcafbb5532a3aed3a5ffd2f8dc1828e639b88d75f6a376dd8f94347a44813fd7a7149d8463477a9a49525c8b2dcaa38c2d1e8 + languageName: node + linkType: hard + "@types/accepts@npm:*": version: 1.3.7 resolution: "@types/accepts@npm:1.3.7" @@ -8626,6 +9468,16 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^5.2.2": + version: 5.2.3 + resolution: "@types/chai@npm:5.2.3" + dependencies: + "@types/deep-eql": "npm:*" + assertion-error: "npm:^2.0.1" + checksum: 10c0/e0ef1de3b6f8045a5e473e867c8565788c444271409d155588504840ad1a53611011f85072188c2833941189400228c1745d78323dac13fcede9c2b28bacfb2f + languageName: node + linkType: hard + "@types/cheerio@npm:^0.22.35": version: 0.22.35 resolution: "@types/cheerio@npm:0.22.35" @@ -8680,6 +9532,13 @@ __metadata: languageName: node linkType: hard +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844 + languageName: node + linkType: hard + "@types/eslint-scope@npm:^3.7.7": version: 3.7.7 resolution: "@types/eslint-scope@npm:3.7.7" @@ -8707,6 +9566,13 @@ __metadata: languageName: node linkType: hard +"@types/estree@npm:1.0.8, @types/estree@npm:^1.0.5": + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 + languageName: node + linkType: hard + "@types/express-serve-static-core@npm:^4.17.33": version: 4.19.6 resolution: "@types/express-serve-static-core@npm:4.19.6" @@ -8808,6 +9674,15 @@ __metadata: languageName: node linkType: hard +"@types/hast@npm:^3.0.0, @types/hast@npm:^3.0.4": + version: 3.0.4 + resolution: "@types/hast@npm:3.0.4" + dependencies: + "@types/unist": "npm:*" + checksum: 10c0/3249781a511b38f1d330fd1e3344eed3c4e7ea8eff82e835d35da78e637480d36fad37a78be5a7aed8465d237ad0446abc1150859d0fde395354ea634decf9f7 + languageName: node + linkType: hard + "@types/hoist-non-react-statics@npm:3, @types/hoist-non-react-statics@npm:^3.3.1": version: 3.3.6 resolution: "@types/hoist-non-react-statics@npm:3.3.6" @@ -8989,6 +9864,15 @@ __metadata: languageName: node linkType: hard +"@types/mdast@npm:^4.0.4": + version: 4.0.4 + resolution: "@types/mdast@npm:4.0.4" + dependencies: + "@types/unist": "npm:*" + checksum: 10c0/84f403dbe582ee508fd9c7643ac781ad8597fcbfc9ccb8d4715a2c92e4545e5772cbd0dbdf18eda65789386d81b009967fdef01b24faf6640f817287f54d9c82 + languageName: node + linkType: hard + "@types/mime@npm:^1": version: 1.3.5 resolution: "@types/mime@npm:1.3.5" @@ -9031,6 +9915,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^20": + version: 20.19.27 + resolution: "@types/node@npm:20.19.27" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/7599c24d80465c1aa6e29b53581fc20ad8862ff33e6eef31d05c1c706868476ee57319c89b802ea972dd4d64ce86d18020aa5344f851fb59b730ea509a63300f + languageName: node + linkType: hard + "@types/node@npm:^20.17.12": version: 20.17.12 resolution: "@types/node@npm:20.17.12" @@ -9255,6 +10148,20 @@ __metadata: languageName: node linkType: hard +"@types/unist@npm:*, @types/unist@npm:^3.0.0": + version: 3.0.3 + resolution: "@types/unist@npm:3.0.3" + checksum: 10c0/2b1e4adcab78388e088fcc3c0ae8700f76619dbcb4741d7d201f87e2cb346bfc29a89003cfea2d76c996e1061452e14fcd737e8b25aacf949c1f2d6b2bc3dd60 + languageName: node + linkType: hard + +"@types/unist@npm:^2.0.0, @types/unist@npm:^2.0.2, @types/unist@npm:^2.0.3": + version: 2.0.11 + resolution: "@types/unist@npm:2.0.11" + checksum: 10c0/24dcdf25a168f453bb70298145eb043cfdbb82472db0bc0b56d6d51cd2e484b9ed8271d4ac93000a80da568f2402e9339723db262d0869e2bf13bc58e081768d + languageName: node + linkType: hard + "@types/use-sync-external-store@npm:^0.0.3": version: 0.0.3 resolution: "@types/use-sync-external-store@npm:0.0.3" @@ -9278,6 +10185,26 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/eslint-plugin@npm:8.49.0": + version: 8.49.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.49.0" + dependencies: + "@eslint-community/regexpp": "npm:^4.10.0" + "@typescript-eslint/scope-manager": "npm:8.49.0" + "@typescript-eslint/type-utils": "npm:8.49.0" + "@typescript-eslint/utils": "npm:8.49.0" + "@typescript-eslint/visitor-keys": "npm:8.49.0" + ignore: "npm:^7.0.0" + natural-compare: "npm:^1.4.0" + ts-api-utils: "npm:^2.1.0" + peerDependencies: + "@typescript-eslint/parser": ^8.49.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 10c0/f5a6ac622bebad31e6e561caa2e7364bac82e259d487e1832f90c41040c856ed360891c710098f43d3541a17703a429ef023b0f5bb573cc05ee52200096f252f + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:^8.19.1": version: 8.19.1 resolution: "@typescript-eslint/eslint-plugin@npm:8.19.1" @@ -9299,6 +10226,22 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/parser@npm:8.49.0": + version: 8.49.0 + resolution: "@typescript-eslint/parser@npm:8.49.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:8.49.0" + "@typescript-eslint/types": "npm:8.49.0" + "@typescript-eslint/typescript-estree": "npm:8.49.0" + "@typescript-eslint/visitor-keys": "npm:8.49.0" + debug: "npm:^4.3.4" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 10c0/749e6244497f2b617351cce4ae520bc101c948b1c94c28e3bc4c5861eb999ada3b4be5dfad36a377a75675998a27d24c5ffba23ff2af743e2b4c4530f68b2673 + languageName: node + linkType: hard + "@typescript-eslint/parser@npm:^8.19.1": version: 8.19.1 resolution: "@typescript-eslint/parser@npm:8.19.1" @@ -9315,6 +10258,19 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/project-service@npm:8.49.0": + version: 8.49.0 + resolution: "@typescript-eslint/project-service@npm:8.49.0" + dependencies: + "@typescript-eslint/tsconfig-utils": "npm:^8.49.0" + "@typescript-eslint/types": "npm:^8.49.0" + debug: "npm:^4.3.4" + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + checksum: 10c0/da6342fe99786c9d9c1d2fc3291ffd62afa043b42f4c7b5c1f8b3a6af266bd9af662281a0905ee70b069a811b63faf7efb63932f6bf55cb138e42309e4ced425 + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:8.19.1": version: 8.19.1 resolution: "@typescript-eslint/scope-manager@npm:8.19.1" @@ -9325,12 +10281,31 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.19.1": - version: 8.19.1 - resolution: "@typescript-eslint/type-utils@npm:8.19.1" +"@typescript-eslint/scope-manager@npm:8.49.0": + version: 8.49.0 + resolution: "@typescript-eslint/scope-manager@npm:8.49.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.19.1" - "@typescript-eslint/utils": "npm:8.19.1" + "@typescript-eslint/types": "npm:8.49.0" + "@typescript-eslint/visitor-keys": "npm:8.49.0" + checksum: 10c0/fe7a036e186e8cb933375ecc3b6ea8ce7604f1dd53d72c24d26158cbc2563527f8c1ba7a894b58bcbd079315fe950ff3c5eb5f7061658f39ff473c04d54ef701 + languageName: node + linkType: hard + +"@typescript-eslint/tsconfig-utils@npm:8.49.0, @typescript-eslint/tsconfig-utils@npm:^8.49.0": + version: 8.49.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.49.0" + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + checksum: 10c0/1b255149d3f0d99b6cf5df4b62925a79f44f243748c6e877a7cf1dd0cdbff7411f2971d5e9fa85472ed76055bd1826e55c1adc99f3d82f504bd9fcd6e76f4b3a + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:8.19.1": + version: 8.19.1 + resolution: "@typescript-eslint/type-utils@npm:8.19.1" + dependencies: + "@typescript-eslint/typescript-estree": "npm:8.19.1" + "@typescript-eslint/utils": "npm:8.19.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^2.0.0" peerDependencies: @@ -9340,6 +10315,22 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/type-utils@npm:8.49.0": + version: 8.49.0 + resolution: "@typescript-eslint/type-utils@npm:8.49.0" + dependencies: + "@typescript-eslint/types": "npm:8.49.0" + "@typescript-eslint/typescript-estree": "npm:8.49.0" + "@typescript-eslint/utils": "npm:8.49.0" + debug: "npm:^4.3.4" + ts-api-utils: "npm:^2.1.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 10c0/0b5bdcbd100469acc6b02642f1bface12347423fc065b57fce94eef2f059a30ada1dfada2d172531305d4a48640c51236634f25eaa9529a400447862837ff595 + languageName: node + linkType: hard + "@typescript-eslint/types@npm:8.19.1": version: 8.19.1 resolution: "@typescript-eslint/types@npm:8.19.1" @@ -9347,6 +10338,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.49.0, @typescript-eslint/types@npm:^8.49.0": + version: 8.49.0 + resolution: "@typescript-eslint/types@npm:8.49.0" + checksum: 10c0/75b26207b142576cf9af86406815b440c7f4bc6645fa58c58a3d781a5d80a39ba7e44d4b4df297980019a7aa1db10da5ac515191aaaf0f1ef6007996c126d8f9 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:8.19.1": version: 8.19.1 resolution: "@typescript-eslint/typescript-estree@npm:8.19.1" @@ -9365,6 +10363,25 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.49.0": + version: 8.49.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.49.0" + dependencies: + "@typescript-eslint/project-service": "npm:8.49.0" + "@typescript-eslint/tsconfig-utils": "npm:8.49.0" + "@typescript-eslint/types": "npm:8.49.0" + "@typescript-eslint/visitor-keys": "npm:8.49.0" + debug: "npm:^4.3.4" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + tinyglobby: "npm:^0.2.15" + ts-api-utils: "npm:^2.1.0" + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + checksum: 10c0/91d0e4ed00021085142c2845571cc91c89b700ee184eb508e8d1f97a02533c029630f00c3f0f796942b28397ec9f61502b153c81971d228893363fc546bbb341 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:8.19.1": version: 8.19.1 resolution: "@typescript-eslint/utils@npm:8.19.1" @@ -9380,6 +10397,21 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:8.49.0": + version: 8.49.0 + resolution: "@typescript-eslint/utils@npm:8.49.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.7.0" + "@typescript-eslint/scope-manager": "npm:8.49.0" + "@typescript-eslint/types": "npm:8.49.0" + "@typescript-eslint/typescript-estree": "npm:8.49.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 10c0/d10fe4d844dacb2f76f0a6e018455d94ba29204845d57248ae220030bda7e13e0e7b488b3ccf8ce1b5d577e1e1775cbdbbff911261a586d9bc7fdfc3cc001697 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:8.19.1": version: 8.19.1 resolution: "@typescript-eslint/visitor-keys@npm:8.19.1" @@ -9390,6 +10422,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.49.0": + version: 8.49.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.49.0" + dependencies: + "@typescript-eslint/types": "npm:8.49.0" + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10c0/442c47bf8e46dda50a765cddbd524f6fef9e76acc3d11de2505ca7097054f24e53f12fe57be34b72fb56115f8f74499573a2704f3465bffdb96834083b616cf1 + languageName: node + linkType: hard + "@ucast/core@npm:^1.0.0, @ucast/core@npm:^1.4.1, @ucast/core@npm:^1.6.1": version: 1.10.2 resolution: "@ucast/core@npm:1.10.2" @@ -9471,6 +10513,13 @@ __metadata: languageName: node linkType: hard +"@ungap/structured-clone@npm:^1.0.0": + version: 1.3.0 + resolution: "@ungap/structured-clone@npm:1.3.0" + checksum: 10c0/0fc3097c2540ada1fc340ee56d58d96b5b536a2a0dab6e3ec17d4bfc8c4c86db345f61a375a8185f9da96f01c69678f836a2b57eeaa9e4b8eeafd26428e57b0a + languageName: node + linkType: hard + "@vercel/oidc@npm:^3.0.1": version: 3.0.1 resolution: "@vercel/oidc@npm:3.0.1" @@ -9500,6 +10549,41 @@ __metadata: languageName: node linkType: hard +"@vitest/browser-playwright@npm:^4.0.15": + version: 4.0.15 + resolution: "@vitest/browser-playwright@npm:4.0.15" + dependencies: + "@vitest/browser": "npm:4.0.15" + "@vitest/mocker": "npm:4.0.15" + tinyrainbow: "npm:^3.0.3" + peerDependencies: + playwright: "*" + vitest: 4.0.15 + peerDependenciesMeta: + playwright: + optional: false + checksum: 10c0/ce357cc96b5d391fa701d545089475feac64e6febdce0d95a75e7c9c29ad35650372e6930a492750af2a4633f4f9354463968f435713da4f035befeb4e3ecf84 + languageName: node + linkType: hard + +"@vitest/browser@npm:4.0.15": + version: 4.0.15 + resolution: "@vitest/browser@npm:4.0.15" + dependencies: + "@vitest/mocker": "npm:4.0.15" + "@vitest/utils": "npm:4.0.15" + magic-string: "npm:^0.30.21" + pixelmatch: "npm:7.1.0" + pngjs: "npm:^7.0.0" + sirv: "npm:^3.0.2" + tinyrainbow: "npm:^3.0.3" + ws: "npm:^8.18.3" + peerDependencies: + vitest: 4.0.15 + checksum: 10c0/b74c1ab5b03a494b1a91e270417a794e616d3d9d5002de816b6a9913073fdf5939ca63b30a37e4e865cb9402b8682254facaf4b854d002b65b6ea85fccf38253 + languageName: node + linkType: hard + "@vitest/coverage-v8@npm:^2.1.8": version: 2.1.8 resolution: "@vitest/coverage-v8@npm:2.1.8" @@ -9538,6 +10622,20 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:4.0.15": + version: 4.0.15 + resolution: "@vitest/expect@npm:4.0.15" + dependencies: + "@standard-schema/spec": "npm:^1.0.0" + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:4.0.15" + "@vitest/utils": "npm:4.0.15" + chai: "npm:^6.2.1" + tinyrainbow: "npm:^3.0.3" + checksum: 10c0/0cb98a4918ca84b28cd14120bb66c1bc3084f8f95b649066cdab2f5234ecdbe247cdc6bc47c0d939521d964ff3c150aadd9558272495c26872c9f3a97373bf7b + languageName: node + linkType: hard + "@vitest/mocker@npm:2.1.8": version: 2.1.8 resolution: "@vitest/mocker@npm:2.1.8" @@ -9557,6 +10655,25 @@ __metadata: languageName: node linkType: hard +"@vitest/mocker@npm:4.0.15": + version: 4.0.15 + resolution: "@vitest/mocker@npm:4.0.15" + dependencies: + "@vitest/spy": "npm:4.0.15" + estree-walker: "npm:^3.0.3" + magic-string: "npm:^0.30.21" + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + checksum: 10c0/7a164aa25daab3e92013ec95aab5c5778e805b1513e266ce6c00e0647eb9f7b281e33fcaf0d9d2aed88321079183b60c1aeab90961f618c24e2e3a5143308850 + languageName: node + linkType: hard + "@vitest/pretty-format@npm:2.1.8, @vitest/pretty-format@npm:^2.1.8": version: 2.1.8 resolution: "@vitest/pretty-format@npm:2.1.8" @@ -9566,6 +10683,15 @@ __metadata: languageName: node linkType: hard +"@vitest/pretty-format@npm:4.0.15": + version: 4.0.15 + resolution: "@vitest/pretty-format@npm:4.0.15" + dependencies: + tinyrainbow: "npm:^3.0.3" + checksum: 10c0/d863f3818627b198f9c66515f8faa200e76a1c30c7f2b25ac805e253204ae51abbfa6b6211c58b2d75e1a273a2e6925e3a8fa480ebfa9c479d75a19815e1cbea + languageName: node + linkType: hard + "@vitest/runner@npm:2.1.8": version: 2.1.8 resolution: "@vitest/runner@npm:2.1.8" @@ -9576,6 +10702,16 @@ __metadata: languageName: node linkType: hard +"@vitest/runner@npm:4.0.15": + version: 4.0.15 + resolution: "@vitest/runner@npm:4.0.15" + dependencies: + "@vitest/utils": "npm:4.0.15" + pathe: "npm:^2.0.3" + checksum: 10c0/0b0f23b8fed1a98bb85d71a7fc105726e0fae68667b090c40b636011126fef548a5f853eab40aaf47314913ab6480eefe2aa5bd6bcefc4116e034fdc1ac0f7d0 + languageName: node + linkType: hard + "@vitest/snapshot@npm:2.1.8": version: 2.1.8 resolution: "@vitest/snapshot@npm:2.1.8" @@ -9587,6 +10723,17 @@ __metadata: languageName: node linkType: hard +"@vitest/snapshot@npm:4.0.15": + version: 4.0.15 + resolution: "@vitest/snapshot@npm:4.0.15" + dependencies: + "@vitest/pretty-format": "npm:4.0.15" + magic-string: "npm:^0.30.21" + pathe: "npm:^2.0.3" + checksum: 10c0/f05a19f74512cbad9bcfe4afe814c676b72b7e54ccf09c5b36e06e73614a24fdba47bdb8a94279162b7fdf83c9c840f557073a114a9339df7e75ccb9f4e99218 + languageName: node + linkType: hard + "@vitest/spy@npm:2.1.8": version: 2.1.8 resolution: "@vitest/spy@npm:2.1.8" @@ -9596,6 +10743,13 @@ __metadata: languageName: node linkType: hard +"@vitest/spy@npm:4.0.15": + version: 4.0.15 + resolution: "@vitest/spy@npm:4.0.15" + checksum: 10c0/22319cead44964882d9e7bd197a9cd317c945ff75a4a9baefe06c32c5719d4cb887e86b4560d79716765f288a93a6c76e78e3eeab0000f24b2236dab678b7c34 + languageName: node + linkType: hard + "@vitest/utils@npm:2.1.8": version: 2.1.8 resolution: "@vitest/utils@npm:2.1.8" @@ -9607,6 +10761,16 @@ __metadata: languageName: node linkType: hard +"@vitest/utils@npm:4.0.15": + version: 4.0.15 + resolution: "@vitest/utils@npm:4.0.15" + dependencies: + "@vitest/pretty-format": "npm:4.0.15" + tinyrainbow: "npm:^3.0.3" + checksum: 10c0/2ef661c2c2359ae956087f0b728b6a0f7555cd10524a7def27909f320f6b8ba00560ee1bd856bf68d4debc01020cea21b200203a5d2af36c44e94528c5587aee + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.14.1, @webassemblyjs/ast@npm:^1.14.1": version: 1.14.1 resolution: "@webassemblyjs/ast@npm:1.14.1" @@ -9846,6 +11010,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.12.1, acorn@npm:^8.14.1, acorn@npm:^8.15.0": + version: 8.15.0 + resolution: "acorn@npm:8.15.0" + bin: + acorn: bin/acorn + checksum: 10c0/dec73ff59b7d6628a01eebaece7f2bdb8bb62b9b5926dcad0f8931f2b8b79c2be21f6c68ac095592adb5adb15831a3635d9343e6a91d028bbe85d564875ec3ec + languageName: node + linkType: hard + "addressparser@npm:1.0.1": version: 1.0.1 resolution: "addressparser@npm:1.0.1" @@ -10114,7 +11287,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": +"ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": version: 6.2.1 resolution: "ansi-styles@npm:6.2.1" checksum: 10c0/5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c @@ -10210,7 +11383,7 @@ __metadata: languageName: node linkType: hard -"aria-query@npm:^5.0.0, aria-query@npm:^5.3.0": +"aria-query@npm:^5.0.0, aria-query@npm:^5.3.0, aria-query@npm:^5.3.1": version: 5.3.2 resolution: "aria-query@npm:5.3.2" checksum: 10c0/003c7e3e2cff5540bf7a7893775fc614de82b0c5dde8ae823d47b7a28a9d4da1f7ed85f340bdb93d5649caa927755f0e31ecc7ab63edfdfc00c8ef07e505e03e @@ -10321,7 +11494,7 @@ __metadata: languageName: node linkType: hard -"axobject-query@npm:^4.0.0": +"axobject-query@npm:^4.0.0, axobject-query@npm:^4.1.0": version: 4.1.0 resolution: "axobject-query@npm:4.1.0" checksum: 10c0/c470e4f95008f232eadd755b018cb55f16c03ccf39c027b941cd8820ac6b68707ce5d7368a46756db4256fbc91bb4ead368f84f7fb034b2b7932f082f6dc0775 @@ -10425,6 +11598,13 @@ __metadata: languageName: node linkType: hard +"bail@npm:^2.0.0": + version: 2.0.2 + resolution: "bail@npm:2.0.2" + checksum: 10c0/25cbea309ef6a1f56214187004e8f34014eb015713ea01fa5b9b7e9e776ca88d0fdffd64143ac42dc91966c915a4b7b683411b56e14929fad16153fc026ffb8b + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -10992,6 +12172,13 @@ __metadata: languageName: node linkType: hard +"chai@npm:^6.2.1": + version: 6.2.1 + resolution: "chai@npm:6.2.1" + checksum: 10c0/0c2d84392d7c6d44ca5d14d94204f1760e22af68b83d1f4278b5c4d301dabfc0242da70954dd86b1eda01e438f42950de6cf9d569df2103678538e4014abe50b + languageName: node + linkType: hard + "chalk-template@npm:0.4.0": version: 0.4.0 resolution: "chalk-template@npm:0.4.0" @@ -11035,7 +12222,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^5.3.0, chalk@npm:~5.4.1": +"chalk@npm:^5.3.0": version: 5.4.1 resolution: "chalk@npm:5.4.1" checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef @@ -11170,6 +12357,15 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^4.0.1": + version: 4.0.3 + resolution: "chokidar@npm:4.0.3" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/a58b9df05bb452f7d105d9e7229ac82fa873741c0c40ddcc7bb82f8a909fbe3f7814c9ebe9bc9a2bef9b737c0ec6e2d699d179048ef06ad3ec46315df0ebe6ad + languageName: node + linkType: hard + "chownr@npm:^1.1.1": version: 1.1.4 resolution: "chownr@npm:1.1.4" @@ -11325,13 +12521,13 @@ __metadata: languageName: node linkType: hard -"cli-truncate@npm:^4.0.0": - version: 4.0.0 - resolution: "cli-truncate@npm:4.0.0" +"cli-truncate@npm:^5.0.0": + version: 5.1.1 + resolution: "cli-truncate@npm:5.1.1" dependencies: - slice-ansi: "npm:^5.0.0" - string-width: "npm:^7.0.0" - checksum: 10c0/d7f0b73e3d9b88cb496e6c086df7410b541b56a43d18ade6a573c9c18bd001b1c3fba1ad578f741a4218fdc794d042385f8ac02c25e1c295a2d8b9f3cb86eb4c + slice-ansi: "npm:^7.1.0" + string-width: "npm:^8.0.0" + checksum: 10c0/3842920829a62f3e041ce39199050c42706c3c9c756a4efc8b86d464e102d1fa031d8b1b9b2e3bb36e1017c763558275472d031bdc884c1eff22a2f20e4f6b0a languageName: node linkType: hard @@ -11398,6 +12594,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 + languageName: node + linkType: hard + "co-body@npm:^6.1.0": version: 6.2.0 resolution: "co-body@npm:6.2.0" @@ -11564,7 +12767,7 @@ __metadata: languageName: node linkType: hard -"commander@npm:12.1.0, commander@npm:~12.1.0": +"commander@npm:12.1.0": version: 12.1.0 resolution: "commander@npm:12.1.0" checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 @@ -11585,6 +12788,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^14.0.2": + version: 14.0.2 + resolution: "commander@npm:14.0.2" + checksum: 10c0/245abd1349dbad5414cb6517b7b5c584895c02c4f7836ff5395f301192b8566f9796c82d7bd6c92d07eba8775fe4df86602fca5d86d8d10bcc2aded1e21c2aeb + languageName: node + linkType: hard + "commander@npm:^2.20.0": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -11911,7 +13121,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -12089,6 +13299,13 @@ __metadata: languageName: node linkType: hard +"daisyui@npm:^5.5.14": + version: 5.5.14 + resolution: "daisyui@npm:5.5.14" + checksum: 10c0/07fd07e9246efcadc5d6c425ea646580a31856569ee3db8d38519d92713ddf4c3b173379dca67723bc21d529c4b00b3425121a3ea3764381ade3e8b34725db60 + languageName: node + linkType: hard + "data-urls@npm:^5.0.0": version: 5.0.0 resolution: "data-urls@npm:5.0.0" @@ -12133,7 +13350,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.7, debug@npm:~4.4.0": +"debug@npm:4, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.7": version: 4.4.0 resolution: "debug@npm:4.4.0" dependencies: @@ -12166,6 +13383,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 + languageName: node + linkType: hard + "decimal.js@npm:10, decimal.js@npm:^10.4.3": version: 10.4.3 resolution: "decimal.js@npm:10.4.3" @@ -12357,7 +13586,7 @@ __metadata: languageName: node linkType: hard -"dequal@npm:^2.0.3": +"dequal@npm:^2.0.0, dequal@npm:^2.0.3": version: 2.0.3 resolution: "dequal@npm:2.0.3" checksum: 10c0/f98860cdf58b64991ae10205137c0e97d384c3a4edc7f807603887b7c4b850af1224a33d88012009f150861cbee4fa2d322c4cc04b9313bee312e47f6ecaa888 @@ -12399,6 +13628,13 @@ __metadata: languageName: node linkType: hard +"detect-libc@npm:^2.0.3": + version: 2.1.2 + resolution: "detect-libc@npm:2.1.2" + checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 + languageName: node + linkType: hard + "detect-newline@npm:^3.0.0": version: 3.1.0 resolution: "detect-newline@npm:3.1.0" @@ -12434,6 +13670,22 @@ __metadata: languageName: node linkType: hard +"devalue@npm:^5.3.2, devalue@npm:^5.5.0": + version: 5.6.1 + resolution: "devalue@npm:5.6.1" + checksum: 10c0/4dca0e800336003fd1e268c142adfe78f3539cda7384b4f69762a93e0dfc33e223b580251da0a6da4be44962958fcba5eadf122f9720e09f437b28904af9c43e + languageName: node + linkType: hard + +"devlop@npm:^1.0.0": + version: 1.1.0 + resolution: "devlop@npm:1.1.0" + dependencies: + dequal: "npm:^2.0.0" + checksum: 10c0/e0928ab8f94c59417a2b8389c45c55ce0a02d9ac7fd74ef62d01ba48060129e1d594501b77de01f3eeafc7cb00773819b0df74d96251cf20b31c5b3071f45c0e + languageName: node + linkType: hard + "dezalgo@npm:^1.0.4": version: 1.0.4 resolution: "dezalgo@npm:1.0.4" @@ -12832,6 +14084,16 @@ __metadata: languageName: node linkType: hard +"enhanced-resolve@npm:^5.18.3": + version: 5.18.4 + resolution: "enhanced-resolve@npm:5.18.4" + dependencies: + graceful-fs: "npm:^4.2.4" + tapable: "npm:^2.2.0" + checksum: 10c0/8f6d42c8a0787a746c493e724c9de5d091cfe8e3f871f2464e2f78a6c55fa1a3aaba495334f923c8ea3ac23e1472491f79feef6fc0fb46a75169cb447ffbe2dc + languageName: node + linkType: hard + "entities@npm:^2.0.0": version: 2.2.0 resolution: "entities@npm:2.2.0" @@ -12913,6 +14175,13 @@ __metadata: languageName: node linkType: hard +"es-module-lexer@npm:^1.7.0": + version: 1.7.0 + resolution: "es-module-lexer@npm:1.7.0" + checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b + languageName: node + linkType: hard + "es-object-atoms@npm:^1.0.0": version: 1.0.0 resolution: "es-object-atoms@npm:1.0.0" @@ -13292,6 +14561,95 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.27.0": + version: 0.27.1 + resolution: "esbuild@npm:0.27.1" + dependencies: + "@esbuild/aix-ppc64": "npm:0.27.1" + "@esbuild/android-arm": "npm:0.27.1" + "@esbuild/android-arm64": "npm:0.27.1" + "@esbuild/android-x64": "npm:0.27.1" + "@esbuild/darwin-arm64": "npm:0.27.1" + "@esbuild/darwin-x64": "npm:0.27.1" + "@esbuild/freebsd-arm64": "npm:0.27.1" + "@esbuild/freebsd-x64": "npm:0.27.1" + "@esbuild/linux-arm": "npm:0.27.1" + "@esbuild/linux-arm64": "npm:0.27.1" + "@esbuild/linux-ia32": "npm:0.27.1" + "@esbuild/linux-loong64": "npm:0.27.1" + "@esbuild/linux-mips64el": "npm:0.27.1" + "@esbuild/linux-ppc64": "npm:0.27.1" + "@esbuild/linux-riscv64": "npm:0.27.1" + "@esbuild/linux-s390x": "npm:0.27.1" + "@esbuild/linux-x64": "npm:0.27.1" + "@esbuild/netbsd-arm64": "npm:0.27.1" + "@esbuild/netbsd-x64": "npm:0.27.1" + "@esbuild/openbsd-arm64": "npm:0.27.1" + "@esbuild/openbsd-x64": "npm:0.27.1" + "@esbuild/openharmony-arm64": "npm:0.27.1" + "@esbuild/sunos-x64": "npm:0.27.1" + "@esbuild/win32-arm64": "npm:0.27.1" + "@esbuild/win32-ia32": "npm:0.27.1" + "@esbuild/win32-x64": "npm:0.27.1" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/8bfcf13a499a9e7b7da4b68273e12b453c7d7a5e39c944c2e5a4c64a0594d6df1391fc168a5353c22bc94eeae38dd9897199ddbbc4973525b0aae18186e996bd + languageName: node + linkType: hard + "esbuild@npm:~0.23.0": version: 0.23.1 resolution: "esbuild@npm:0.23.1" @@ -13514,6 +14872,17 @@ __metadata: languageName: node linkType: hard +"eslint-config-prettier@npm:^10.1.8": + version: 10.1.8 + resolution: "eslint-config-prettier@npm:10.1.8" + peerDependencies: + eslint: ">=7.0.0" + bin: + eslint-config-prettier: bin/cli.js + checksum: 10c0/e1bcfadc9eccd526c240056b1e59c5cd26544fe59feb85f38f4f1f116caed96aea0b3b87868e68b3099e55caaac3f2e5b9f58110f85db893e83a332751192682 + languageName: node + linkType: hard + "eslint-config-prettier@npm:^9.1.0": version: 9.1.0 resolution: "eslint-config-prettier@npm:9.1.0" @@ -13559,6 +14928,30 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-svelte@npm:^3.13.1": + version: 3.13.1 + resolution: "eslint-plugin-svelte@npm:3.13.1" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.6.1" + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + esutils: "npm:^2.0.3" + globals: "npm:^16.0.0" + known-css-properties: "npm:^0.37.0" + postcss: "npm:^8.4.49" + postcss-load-config: "npm:^3.1.4" + postcss-safe-parser: "npm:^7.0.0" + semver: "npm:^7.6.3" + svelte-eslint-parser: "npm:^1.4.0" + peerDependencies: + eslint: ^8.57.1 || ^9.0.0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + checksum: 10c0/343585bfcdfa3a6bf9879fee00e03c2c5416072b34ae908e3e9b5a33689075acabc99debedbf049d1bbd395ad85f5c19a389014f96c5754bbb14654c10086816 + languageName: node + linkType: hard + "eslint-scope@npm:5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" @@ -13589,6 +14982,16 @@ __metadata: languageName: node linkType: hard +"eslint-scope@npm:^8.4.0": + version: 8.4.0 + resolution: "eslint-scope@npm:8.4.0" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^5.2.0" + checksum: 10c0/407f6c600204d0f3705bd557f81bd0189e69cd7996f408f8971ab5779c0af733d1af2f1412066b40ee1588b085874fc37a2333986c6521669cdbdd36ca5058e0 + languageName: node + linkType: hard + "eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" @@ -13596,6 +14999,13 @@ __metadata: languageName: node linkType: hard +"eslint-visitor-keys@npm:^4.0.0, eslint-visitor-keys@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-visitor-keys@npm:4.2.1" + checksum: 10c0/fcd43999199d6740db26c58dbe0c2594623e31ca307e616ac05153c9272f12f1364f5a0b1917a8e962268fdecc6f3622c1c2908b4fcc2e047a106fe6de69dc43 + languageName: node + linkType: hard + "eslint-visitor-keys@npm:^4.2.0": version: 4.2.0 resolution: "eslint-visitor-keys@npm:4.2.0" @@ -13603,30 +15013,30 @@ __metadata: languageName: node linkType: hard -"eslint@npm:~9.14.0": - version: 9.14.0 - resolution: "eslint@npm:9.14.0" +"eslint@npm:^9.39.2": + version: 9.39.2 + resolution: "eslint@npm:9.39.2" dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" + "@eslint-community/eslint-utils": "npm:^4.8.0" "@eslint-community/regexpp": "npm:^4.12.1" - "@eslint/config-array": "npm:^0.18.0" - "@eslint/core": "npm:^0.7.0" - "@eslint/eslintrc": "npm:^3.1.0" - "@eslint/js": "npm:9.14.0" - "@eslint/plugin-kit": "npm:^0.2.0" + "@eslint/config-array": "npm:^0.21.1" + "@eslint/config-helpers": "npm:^0.4.2" + "@eslint/core": "npm:^0.17.0" + "@eslint/eslintrc": "npm:^3.3.1" + "@eslint/js": "npm:9.39.2" + "@eslint/plugin-kit": "npm:^0.4.1" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" - "@humanwhocodes/retry": "npm:^0.4.0" + "@humanwhocodes/retry": "npm:^0.4.2" "@types/estree": "npm:^1.0.6" - "@types/json-schema": "npm:^7.0.15" ajv: "npm:^6.12.4" chalk: "npm:^4.0.0" - cross-spawn: "npm:^7.0.2" + cross-spawn: "npm:^7.0.6" debug: "npm:^4.3.2" escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.2.0" - eslint-visitor-keys: "npm:^4.2.0" - espree: "npm:^10.3.0" + eslint-scope: "npm:^8.4.0" + eslint-visitor-keys: "npm:^4.2.1" + espree: "npm:^10.4.0" esquery: "npm:^1.5.0" esutils: "npm:^2.0.2" fast-deep-equal: "npm:^3.1.3" @@ -13641,7 +15051,6 @@ __metadata: minimatch: "npm:^3.1.2" natural-compare: "npm:^1.4.0" optionator: "npm:^0.9.3" - text-table: "npm:^0.2.0" peerDependencies: jiti: "*" peerDependenciesMeta: @@ -13649,7 +15058,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10c0/e1cbf571b75519ad0b24c27e66a6575e57cab2671ef5296e7b345d9ac3adc1a549118dcc74a05b651a7a13a5e61ebb680be6a3e04a80e1f22eba1931921b5187 + checksum: 10c0/bb88ca8fd16bb7e1ac3e13804c54d41c583214460c0faa7b3e7c574e69c5600c7122295500fb4b0c06067831111db740931e98da1340329527658e1cf80073d3 languageName: node linkType: hard @@ -13660,6 +15069,13 @@ __metadata: languageName: node linkType: hard +"esm-env@npm:^1.2.2": + version: 1.2.2 + resolution: "esm-env@npm:1.2.2" + checksum: 10c0/3d25c973f2fd69c25ffff29c964399cea573fe10795ecc1d26f6f957ce0483d3254e1cceddb34bf3296a0d7b0f1d53a28992f064ba509dfe6366751e752c4166 + languageName: node + linkType: hard + "esm@npm:^3.2.25": version: 3.2.25 resolution: "esm@npm:3.2.25" @@ -13667,7 +15083,18 @@ __metadata: languageName: node linkType: hard -"espree@npm:^10.0.1, espree@npm:^10.3.0": +"espree@npm:^10.0.0, espree@npm:^10.4.0": + version: 10.4.0 + resolution: "espree@npm:10.4.0" + dependencies: + acorn: "npm:^8.15.0" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10c0/c63fe06131c26c8157b4083313cb02a9a54720a08e21543300e55288c40e06c3fc284bdecf108d3a1372c5934a0a88644c98714f38b6ae8ed272b40d9ea08d6b + languageName: node + linkType: hard + +"espree@npm:^10.0.1": version: 10.3.0 resolution: "espree@npm:10.3.0" dependencies: @@ -13708,6 +15135,15 @@ __metadata: languageName: node linkType: hard +"esrap@npm:^2.2.1": + version: 2.2.1 + resolution: "esrap@npm:2.2.1" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.4.15" + checksum: 10c0/bd351c19b6827b69c73f86d9e5bb20fa890911c8e9aaa0581e61a38211346498e8bf4a1ac58811e9306ddbb13cd395db89e0b5302702aba121e8aaf880006f68 + languageName: node + linkType: hard + "esrecurse@npm:^4.3.0": version: 4.3.0 resolution: "esrecurse@npm:4.3.0" @@ -13826,23 +15262,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:~8.0.1": - version: 8.0.1 - resolution: "execa@npm:8.0.1" - dependencies: - cross-spawn: "npm:^7.0.3" - get-stream: "npm:^8.0.1" - human-signals: "npm:^5.0.0" - is-stream: "npm:^3.0.0" - merge-stream: "npm:^2.0.0" - npm-run-path: "npm:^5.1.0" - onetime: "npm:^6.0.0" - signal-exit: "npm:^4.1.0" - strip-final-newline: "npm:^3.0.0" - checksum: 10c0/2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af - languageName: node - linkType: hard - "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -13873,6 +15292,13 @@ __metadata: languageName: node linkType: hard +"expect-type@npm:^1.2.2": + version: 1.3.0 + resolution: "expect-type@npm:1.3.0" + checksum: 10c0/8412b3fe4f392c420ab41dae220b09700e4e47c639a29ba7ba2e83cc6cffd2b4926f7ac9e47d7e277e8f4f02acda76fd6931cb81fd2b382fa9477ef9ada953fd + languageName: node + linkType: hard + "expect@npm:^29.7.0": version: 29.7.0 resolution: "expect@npm:29.7.0" @@ -13893,7 +15319,7 @@ __metadata: languageName: node linkType: hard -"extend@npm:^3.0.2": +"extend@npm:^3.0.0, extend@npm:^3.0.2": version: 3.0.2 resolution: "extend@npm:3.0.2" checksum: 10c0/73bf6e27406e80aa3e85b0d1c4fd987261e628064e170ca781125c0b635a3dabad5e05adbf07595ea0cf1e6c5396cacb214af933da7cbaf24fe75ff14818e8f9 @@ -14034,6 +15460,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f + languageName: node + linkType: hard + "fecha@npm:^4.2.0": version: 4.2.3 resolution: "fecha@npm:4.2.3" @@ -14240,6 +15678,16 @@ __metadata: languageName: node linkType: hard +"foreground-child@npm:^3.3.1": + version: 3.3.1 + resolution: "foreground-child@npm:3.3.1" + dependencies: + cross-spawn: "npm:^7.0.6" + signal-exit: "npm:^4.0.1" + checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3 + languageName: node + linkType: hard + "fork-ts-checker-webpack-plugin@npm:8.0.0": version: 8.0.0 resolution: "fork-ts-checker-webpack-plugin@npm:8.0.0" @@ -14532,6 +15980,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.3.0": + version: 1.4.0 + resolution: "get-east-asian-width@npm:1.4.0" + checksum: 10c0/4e481d418e5a32061c36fbb90d1b225a254cc5b2df5f0b25da215dcd335a3c111f0c2023ffda43140727a9cafb62dac41d022da82c08f31083ee89f714ee3b83 + languageName: node + linkType: hard + "get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6": version: 1.2.6 resolution: "get-intrinsic@npm:1.2.6" @@ -14614,13 +16069,6 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^8.0.1": - version: 8.0.1 - resolution: "get-stream@npm:8.0.1" - checksum: 10c0/5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290 - languageName: node - linkType: hard - "get-stream@npm:^9.0.0": version: 9.0.1 resolution: "get-stream@npm:9.0.1" @@ -14689,6 +16137,13 @@ __metadata: languageName: node linkType: hard +"github-slugger@npm:^2.0.0": + version: 2.0.0 + resolution: "github-slugger@npm:2.0.0" + checksum: 10c0/21b912b6b1e48f1e5a50b2292b48df0ff6abeeb0691b161b3d93d84f4ae6b1acd6ae23702e914af7ea5d441c096453cf0f621b72d57893946618d21dd1a1c486 + languageName: node + linkType: hard + "glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -14745,6 +16200,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^11.0.0": + version: 11.1.0 + resolution: "glob@npm:11.1.0" + dependencies: + foreground-child: "npm:^3.3.1" + jackspeak: "npm:^4.1.1" + minimatch: "npm:^10.1.1" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^2.0.0" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/1ceae07f23e316a6fa74581d9a74be6e8c2e590d2f7205034dd5c0435c53f5f7b712c2be00c3b65bf0a49294a1c6f4b98cd84c7637e29453b5aa13b79f1763a2 + languageName: node + linkType: hard + "glob@npm:^7.0.5, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -14830,6 +16301,13 @@ __metadata: languageName: node linkType: hard +"globals@npm:^16.0.0, globals@npm:^16.5.0": + version: 16.5.0 + resolution: "globals@npm:16.5.0" + checksum: 10c0/615241dae7851c8012f5aa0223005b1ed6607713d6813de0741768bd4ddc39353117648f1a7086b4b0fa45eae733f1c0a0fe369aa4e543bb63f8de8990178ea9 + languageName: node + linkType: hard + "globalthis@npm:^1.0.1": version: 1.0.4 resolution: "globalthis@npm:1.0.4" @@ -15046,6 +16524,33 @@ __metadata: languageName: node linkType: hard +"hast-util-heading-rank@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-heading-rank@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 10c0/1879c84f629e73f1f13247ab349324355cd801363b44e3d46f763aa5c0ea3b42dcd47b46e5643a0502cf01a6b1fdb9208fd12852e44ca6c671b3e4bccf9369a1 + languageName: node + linkType: hard + +"hast-util-is-element@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-is-element@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 10c0/f5361e4c9859c587ca8eb0d8343492f3077ccaa0f58a44cd09f35d5038f94d65152288dcd0c19336ef2c9491ec4d4e45fde2176b05293437021570aa0bc3613b + languageName: node + linkType: hard + +"hast-util-to-string@npm:^3.0.0": + version: 3.0.1 + resolution: "hast-util-to-string@npm:3.0.1" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 10c0/b5fa1912a6ba6131affae52a0f4394406c4c0d23c2b0307f1d69988f1030c7bb830289303e67c5ad8f674f5f23a454c1dcd492c39e45a22c1f46d3c9bce5bd0c + languageName: node + linkType: hard + "he@npm:1.2.0, he@npm:^1.2.0": version: 1.2.0 resolution: "he@npm:1.2.0" @@ -15369,13 +16874,6 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^5.0.0": - version: 5.0.0 - resolution: "human-signals@npm:5.0.0" - checksum: 10c0/5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82 - languageName: node - linkType: hard - "human-signals@npm:^8.0.0": version: 8.0.0 resolution: "human-signals@npm:8.0.0" @@ -15472,6 +16970,13 @@ __metadata: languageName: node linkType: hard +"ignore@npm:^7.0.0": + version: 7.0.5 + resolution: "ignore@npm:7.0.5" + checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d + languageName: node + linkType: hard + "immer@npm:9.0.21, immer@npm:^9.0.21, immer@npm:^9.0.6": version: 9.0.21 resolution: "immer@npm:9.0.21" @@ -15831,13 +17336,6 @@ __metadata: languageName: node linkType: hard -"is-fullwidth-code-point@npm:^4.0.0": - version: 4.0.0 - resolution: "is-fullwidth-code-point@npm:4.0.0" - checksum: 10c0/df2a717e813567db0f659c306d61f2f804d480752526886954a2a3e2246c7745fd07a52b5fecf2b68caf0a6c79dcdace6166fdf29cc76ed9975cc334f0a018b8 - languageName: node - linkType: hard - "is-fullwidth-code-point@npm:^5.0.0": version: 5.0.0 resolution: "is-fullwidth-code-point@npm:5.0.0" @@ -15965,7 +17463,7 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^4.1.0": +"is-plain-obj@npm:^4.0.0, is-plain-obj@npm:^4.1.0": version: 4.1.0 resolution: "is-plain-obj@npm:4.1.0" checksum: 10c0/32130d651d71d9564dc88ba7e6fda0e91a1010a3694648e9f4f47bb6080438140696d3e3e15c741411d712e47ac9edc1a8a9de1fe76f3487b0d90be06ac9975e @@ -16002,7 +17500,7 @@ __metadata: languageName: node linkType: hard -"is-reference@npm:^3.0.0, is-reference@npm:^3.0.1": +"is-reference@npm:^3.0.0, is-reference@npm:^3.0.1, is-reference@npm:^3.0.3": version: 3.0.3 resolution: "is-reference@npm:3.0.3" dependencies: @@ -16043,13 +17541,6 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^3.0.0": - version: 3.0.0 - resolution: "is-stream@npm:3.0.0" - checksum: 10c0/eb2f7127af02ee9aa2a0237b730e47ac2de0d4e76a4a905a50a11557f2339df5765eaea4ceb8029f1efa978586abe776908720bfcb1900c20c6ec5145f6f29d8 - languageName: node - linkType: hard - "is-stream@npm:^4.0.1": version: 4.0.1 resolution: "is-stream@npm:4.0.1" @@ -16298,6 +17789,15 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^4.1.1": + version: 4.1.1 + resolution: "jackspeak@npm:4.1.1" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + checksum: 10c0/84ec4f8e21d6514db24737d9caf65361511f75e5e424980eebca4199f400874f45e562ac20fa8aeb1dd20ca2f3f81f0788b6e9c3e64d216a5794fd6f30e0e042 + languageName: node + linkType: hard + "jest-changed-files@npm:^29.7.0": version: 29.7.0 resolution: "jest-changed-files@npm:29.7.0" @@ -16757,6 +18257,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^2.6.1": + version: 2.6.1 + resolution: "jiti@npm:2.6.1" + bin: + jiti: lib/jiti-cli.mjs + checksum: 10c0/79b2e96a8e623f66c1b703b98ec1b8be4500e1d217e09b09e343471bbb9c105381b83edbb979d01cef18318cc45ce6e153571b6c83122170eefa531c64b6789b + languageName: node + linkType: hard + "jju@npm:~1.4.0": version: 1.4.0 resolution: "jju@npm:1.4.0" @@ -16822,6 +18331,17 @@ __metadata: languageName: node linkType: hard +"js-yaml@npm:^4.1.1": + version: 4.1.1 + resolution: "js-yaml@npm:4.1.1" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/561c7d7088c40a9bb53cc75becbfb1df6ae49b34b5e6e5a81744b14ae8667ec564ad2527709d1a6e7d5e5fa6d483aa0f373a50ad98d42fde368ec4a190d4fae7 + languageName: node + linkType: hard + "jsbn@npm:1.1.0": version: 1.1.0 resolution: "jsbn@npm:1.1.0" @@ -17189,6 +18709,13 @@ __metadata: languageName: node linkType: hard +"known-css-properties@npm:^0.37.0": + version: 0.37.0 + resolution: "known-css-properties@npm:0.37.0" + checksum: 10c0/e0ec08cae580e8833254b690971f73ec6f78ac461820a3c755b4a0b62c5b871501753b4aa60b30576a0f621ba44b231235cf9f35ab89e2e7de5448dfe0600241 + languageName: node + linkType: hard + "koa-body@npm:6.0.1": version: 6.0.1 resolution: "koa-body@npm:6.0.1" @@ -17477,6 +19004,126 @@ __metadata: languageName: node linkType: hard +"lightningcss-android-arm64@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-android-arm64@npm:1.30.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-arm64@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-darwin-arm64@npm:1.30.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-x64@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-darwin-x64@npm:1.30.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-freebsd-x64@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-freebsd-x64@npm:1.30.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-linux-arm-gnueabihf@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.30.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"lightningcss-linux-arm64-gnu@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-linux-arm64-gnu@npm:1.30.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-arm64-musl@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-linux-arm64-musl@npm:1.30.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-linux-x64-gnu@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-linux-x64-gnu@npm:1.30.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-x64-musl@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-linux-x64-musl@npm:1.30.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-win32-arm64-msvc@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-win32-arm64-msvc@npm:1.30.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-win32-x64-msvc@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-win32-x64-msvc@npm:1.30.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"lightningcss@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss@npm:1.30.2" + dependencies: + detect-libc: "npm:^2.0.3" + lightningcss-android-arm64: "npm:1.30.2" + lightningcss-darwin-arm64: "npm:1.30.2" + lightningcss-darwin-x64: "npm:1.30.2" + lightningcss-freebsd-x64: "npm:1.30.2" + lightningcss-linux-arm-gnueabihf: "npm:1.30.2" + lightningcss-linux-arm64-gnu: "npm:1.30.2" + lightningcss-linux-arm64-musl: "npm:1.30.2" + lightningcss-linux-x64-gnu: "npm:1.30.2" + lightningcss-linux-x64-musl: "npm:1.30.2" + lightningcss-win32-arm64-msvc: "npm:1.30.2" + lightningcss-win32-x64-msvc: "npm:1.30.2" + dependenciesMeta: + lightningcss-android-arm64: + optional: true + lightningcss-darwin-arm64: + optional: true + lightningcss-darwin-x64: + optional: true + lightningcss-freebsd-x64: + optional: true + lightningcss-linux-arm-gnueabihf: + optional: true + lightningcss-linux-arm64-gnu: + optional: true + lightningcss-linux-arm64-musl: + optional: true + lightningcss-linux-x64-gnu: + optional: true + lightningcss-linux-x64-musl: + optional: true + lightningcss-win32-arm64-msvc: + optional: true + lightningcss-win32-x64-msvc: + optional: true + checksum: 10c0/5c0c73a33946dab65908d5cd1325df4efa290efb77f940b60f40448b5ab9a87d3ea665ef9bcf00df4209705050ecf2f7ecc649f44d6dfa5905bb50f15717e78d + languageName: node + linkType: hard + "lilconfig@npm:^2.0.5": version: 2.1.0 resolution: "lilconfig@npm:2.1.0" @@ -17484,7 +19131,7 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:^3.0.0, lilconfig@npm:^3.1.3, lilconfig@npm:~3.1.3": +"lilconfig@npm:^3.0.0, lilconfig@npm:^3.1.3": version: 3.1.3 resolution: "lilconfig@npm:3.1.3" checksum: 10c0/f5604e7240c5c275743561442fbc5abf2a84ad94da0f5adc71d25e31fa8483048de3dcedcb7a44112a942fed305fd75841cdf6c9681c7f640c63f1049e9a5dcc @@ -17505,7 +19152,7 @@ __metadata: languageName: node linkType: hard -"linkify-it@npm:5.0.0": +"linkify-it@npm:5.0.0, linkify-it@npm:^5.0.0": version: 5.0.0 resolution: "linkify-it@npm:5.0.0" dependencies: @@ -17523,37 +19170,34 @@ __metadata: languageName: node linkType: hard -"lint-staged@npm:^15.3.0": - version: 15.3.0 - resolution: "lint-staged@npm:15.3.0" - dependencies: - chalk: "npm:~5.4.1" - commander: "npm:~12.1.0" - debug: "npm:~4.4.0" - execa: "npm:~8.0.1" - lilconfig: "npm:~3.1.3" - listr2: "npm:~8.2.5" - micromatch: "npm:~4.0.8" - pidtree: "npm:~0.6.0" - string-argv: "npm:~0.3.2" - yaml: "npm:~2.6.1" +"lint-staged@npm:^16.2.7": + version: 16.2.7 + resolution: "lint-staged@npm:16.2.7" + dependencies: + commander: "npm:^14.0.2" + listr2: "npm:^9.0.5" + micromatch: "npm:^4.0.8" + nano-spawn: "npm:^2.0.0" + pidtree: "npm:^0.6.0" + string-argv: "npm:^0.3.2" + yaml: "npm:^2.8.1" bin: lint-staged: bin/lint-staged.js - checksum: 10c0/1ddf9488c523c0b65c85b755428d4ad74fac3aa6ccb2e28e9bff5b8d86503158fe241d20d5433a11146872050b43580644901a5ef4c924b1ad7017c224a07339 + checksum: 10c0/9a677c21a8112d823ae5bc565ba2c9e7b803786f2a021c46827a55fe44ed59def96edb24fc99c06a2545cdbbf366022ad82addcb3bf60c712f3b98ef92069717 languageName: node linkType: hard -"listr2@npm:~8.2.5": - version: 8.2.5 - resolution: "listr2@npm:8.2.5" +"listr2@npm:^9.0.5": + version: 9.0.5 + resolution: "listr2@npm:9.0.5" dependencies: - cli-truncate: "npm:^4.0.0" + cli-truncate: "npm:^5.0.0" colorette: "npm:^2.0.20" eventemitter3: "npm:^5.0.1" log-update: "npm:^6.1.0" rfdc: "npm:^1.4.1" wrap-ansi: "npm:^9.0.0" - checksum: 10c0/f5a9599514b00c27d7eb32d1117c83c61394b2a985ec20e542c798bf91cf42b19340215701522736f5b7b42f557e544afeadec47866e35e5d4f268f552729671 + checksum: 10c0/46448d1ba0addc9d71aeafd05bb8e86ded9641ccad930ac302c2bd2ad71580375604743e18586fcb8f11906edf98e8e17fca75ba0759947bf275d381f68e311d languageName: node linkType: hard @@ -17791,6 +19435,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^11.0.0": + version: 11.2.4 + resolution: "lru-cache@npm:11.2.4" + checksum: 10c0/4a24f9b17537619f9144d7b8e42cd5a225efdfd7076ebe7b5e7dc02b860a818455201e67fbf000765233fe7e339d3c8229fc815e9b58ee6ede511e07608c19b2 + languageName: node + linkType: hard + "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -17842,6 +19493,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.11, magic-string@npm:^0.30.17, magic-string@npm:^0.30.21": + version: 0.30.21 + resolution: "magic-string@npm:0.30.21" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.5" + checksum: 10c0/299378e38f9a270069fc62358522ddfb44e94244baa0d6a8980ab2a9b2490a1d03b236b447eee309e17eb3bddfa482c61259d47960eb018a904f0ded52780c4a + languageName: node + linkType: hard + "magicast@npm:^0.3.5": version: 0.3.5 resolution: "magicast@npm:0.3.5" @@ -18056,6 +19716,22 @@ __metadata: languageName: node linkType: hard +"markdown-it@npm:^14.1.0": + version: 14.1.0 + resolution: "markdown-it@npm:14.1.0" + dependencies: + argparse: "npm:^2.0.1" + entities: "npm:^4.4.0" + linkify-it: "npm:^5.0.0" + mdurl: "npm:^2.0.0" + punycode.js: "npm:^2.3.1" + uc.micro: "npm:^2.1.0" + bin: + markdown-it: bin/markdown-it.mjs + checksum: 10c0/9a6bb444181d2db7016a4173ae56a95a62c84d4cbfb6916a399b11d3e6581bf1cc2e4e1d07a2f022ae72c25f56db90fbe1e529fca16fbf9541659dc53480d4b4 + languageName: node + linkType: hard + "marked@npm:^4.3.0": version: 4.3.0 resolution: "marked@npm:4.3.0" @@ -18098,6 +19774,22 @@ __metadata: languageName: node linkType: hard +"mdsvex@npm:^0.12.6": + version: 0.12.6 + resolution: "mdsvex@npm:0.12.6" + dependencies: + "@types/mdast": "npm:^4.0.4" + "@types/unist": "npm:^2.0.3" + prism-svelte: "npm:^0.4.7" + prismjs: "npm:^1.17.1" + unist-util-visit: "npm:^2.0.1" + vfile-message: "npm:^2.0.4" + peerDependencies: + svelte: ^3.56.0 || ^4.0.0 || ^5.0.0-next.120 + checksum: 10c0/97aa5631be43f9130c0fb43420d39edacc6579a0502493d5681048b088a08662fdabbae7ea31092842a250bcf8b70993267759fbffd25b0a9a0ecc64e693f192 + languageName: node + linkType: hard + "mdurl@npm:^1.0.1": version: 1.0.1 resolution: "mdurl@npm:1.0.1" @@ -18105,6 +19797,13 @@ __metadata: languageName: node linkType: hard +"mdurl@npm:^2.0.0": + version: 2.0.0 + resolution: "mdurl@npm:2.0.0" + checksum: 10c0/633db522272f75ce4788440669137c77540d74a83e9015666a9557a152c02e245b192edc20bc90ae953bbab727503994a53b236b4d9c99bdaee594d0e7dd2ce0 + languageName: node + linkType: hard + "media-chrome@npm:^4.1.0": version: 4.3.0 resolution: "media-chrome@npm:4.3.0" @@ -18177,7 +19876,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.8, micromatch@npm:~4.0.8": +"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -18249,13 +19948,6 @@ __metadata: languageName: node linkType: hard -"mimic-fn@npm:^4.0.0": - version: 4.0.0 - resolution: "mimic-fn@npm:4.0.0" - checksum: 10c0/de9cc32be9996fd941e512248338e43407f63f6d497abe8441fa33447d922e927de54d4cc3c1a3c6d652857acd770389d5a3823f311a744132760ce2be15ccbf - languageName: node - linkType: hard - "mimic-function@npm:^5.0.0": version: 5.0.1 resolution: "mimic-function@npm:5.0.1" @@ -18295,6 +19987,15 @@ __metadata: languageName: node linkType: hard +"mini-svg-data-uri@npm:^1.2.3": + version: 1.4.4 + resolution: "mini-svg-data-uri@npm:1.4.4" + bin: + mini-svg-data-uri: cli.js + checksum: 10c0/24545fa30b5a45449241bf19c25b8bc37594b63ec06401b3d563bd1c2e8a6abb7c18741f8b354e0064baa63c291be214154bf3a66f201ae71dfab3cc1a5e3191 + languageName: node + linkType: hard + "minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1": version: 1.0.1 resolution: "minimalistic-assert@npm:1.0.1" @@ -18327,6 +20028,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.1.1": + version: 10.1.1 + resolution: "minimatch@npm:10.1.1" + dependencies: + "@isaacs/brace-expansion": "npm:^5.0.0" + checksum: 10c0/c85d44821c71973d636091fddbfbffe62370f5ee3caf0241c5b60c18cd289e916200acb2361b7e987558cd06896d153e25d505db9fc1e43e6b4b6752e2702902 + languageName: node + linkType: hard + "minimatch@npm:^8.0.2": version: 8.0.4 resolution: "minimatch@npm:8.0.4" @@ -18336,7 +20046,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.1, minimatch@npm:^9.0.3, minimatch@npm:^9.0.4": +"minimatch@npm:^9.0.1, minimatch@npm:^9.0.3, minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": version: 9.0.5 resolution: "minimatch@npm:9.0.5" dependencies: @@ -18590,6 +20300,13 @@ __metadata: languageName: node linkType: hard +"nano-spawn@npm:^2.0.0": + version: 2.0.0 + resolution: "nano-spawn@npm:2.0.0" + checksum: 10c0/d00f9b5739f86e28cb732ffd774793e110810cded246b8393c75c4f22674af47f98ee37b19f022ada2d8c9425f800e841caa0662fbff4c0930a10e39339fb366 + languageName: node + linkType: hard + "nano-time@npm:1.0.0": version: 1.0.0 resolution: "nano-time@npm:1.0.0" @@ -18606,6 +20323,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.11": + version: 3.3.11 + resolution: "nanoid@npm:3.3.11" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b + languageName: node + linkType: hard + "nanoid@npm:^3.3.7": version: 3.3.8 resolution: "nanoid@npm:3.3.8" @@ -19038,15 +20764,6 @@ __metadata: languageName: node linkType: hard -"npm-run-path@npm:^5.1.0": - version: 5.3.0 - resolution: "npm-run-path@npm:5.3.0" - dependencies: - path-key: "npm:^4.0.0" - checksum: 10c0/124df74820c40c2eb9a8612a254ea1d557ddfab1581c3e751f825e3e366d9f00b0d76a3c94ecd8398e7f3eee193018622677e95816e8491f0797b21e30b2deba - languageName: node - linkType: hard - "npm-run-path@npm:^6.0.0": version: 6.0.0 resolution: "npm-run-path@npm:6.0.0" @@ -19158,6 +20875,13 @@ __metadata: languageName: node linkType: hard +"obug@npm:^2.1.1": + version: 2.1.1 + resolution: "obug@npm:2.1.1" + checksum: 10c0/59dccd7de72a047e08f8649e94c1015ec72f94eefb6ddb57fb4812c4b425a813bc7e7cd30c9aca20db3c59abc3c85cc7a62bb656a968741d770f4e8e02bc2e78 + languageName: node + linkType: hard + "on-finished@npm:^2.3.0": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -19218,15 +20942,6 @@ __metadata: languageName: node linkType: hard -"onetime@npm:^6.0.0": - version: 6.0.0 - resolution: "onetime@npm:6.0.0" - dependencies: - mimic-fn: "npm:^4.0.0" - checksum: 10c0/4eef7c6abfef697dd4479345a4100c382d73c149d2d56170a54a07418c50816937ad09500e1ed1e79d235989d073a9bade8557122aee24f0576ecde0f392bb6c - languageName: node - linkType: hard - "onetime@npm:^7.0.0": version: 7.0.0 resolution: "onetime@npm:7.0.0" @@ -19761,6 +21476,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^2.0.0": + version: 2.0.1 + resolution: "path-scurry@npm:2.0.1" + dependencies: + lru-cache: "npm:^11.0.0" + minipass: "npm:^7.1.2" + checksum: 10c0/2a16ed0e81fbc43513e245aa5763354e25e787dab0d539581a6c3f0f967461a159ed6236b2559de23aa5b88e7dc32b469b6c47568833dd142a4b24b4f5cd2620 + languageName: node + linkType: hard + "path-to-regexp@npm:3.3.0": version: 3.3.0 resolution: "path-to-regexp@npm:3.3.0" @@ -19796,6 +21521,13 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^2.0.3": + version: 2.0.3 + resolution: "pathe@npm:2.0.3" + checksum: 10c0/c118dc5a8b5c4166011b2b70608762e260085180bb9e33e80a50dcdb1e78c010b1624f4280c492c92b05fc276715a4c357d1f9edc570f8f1b3d90b6839ebaca1 + languageName: node + linkType: hard + "pathval@npm:^2.0.0": version: 2.0.0 resolution: "pathval@npm:2.0.0" @@ -19944,7 +21676,14 @@ __metadata: languageName: node linkType: hard -"pidtree@npm:~0.6.0": +"picomatch@npm:^4.0.3": + version: 4.0.3 + resolution: "picomatch@npm:4.0.3" + checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2 + languageName: node + linkType: hard + +"pidtree@npm:^0.6.0": version: 0.6.0 resolution: "pidtree@npm:0.6.0" bin: @@ -19974,6 +21713,17 @@ __metadata: languageName: node linkType: hard +"pixelmatch@npm:7.1.0": + version: 7.1.0 + resolution: "pixelmatch@npm:7.1.0" + dependencies: + pngjs: "npm:^7.0.0" + bin: + pixelmatch: bin/pixelmatch + checksum: 10c0/ff069f92edaa841ac9b58b0ab74e1afa1f3b5e770eea0218c96bac1da4e752f5f6b79a0f9c4ba6b02afb955d39b8c78bcc3cc884f8122b67a1f2efbbccbe1a73 + languageName: node + linkType: hard + "pkg-dir@npm:^4.2.0": version: 4.2.0 resolution: "pkg-dir@npm:4.2.0" @@ -20010,8 +21760,17 @@ __metadata: languageName: node linkType: hard -"playwright@npm:1.49.1": - version: 1.49.1 +"playwright-core@npm:1.57.0": + version: 1.57.0 + resolution: "playwright-core@npm:1.57.0" + bin: + playwright-core: cli.js + checksum: 10c0/798e35d83bf48419a8c73de20bb94d68be5dde68de23f95d80a0ebe401e3b83e29e3e84aea7894d67fa6c79d2d3d40cc5bcde3e166f657ce50987aaa2421b6a9 + languageName: node + linkType: hard + +"playwright@npm:1.49.1": + version: 1.49.1 resolution: "playwright@npm:1.49.1" dependencies: fsevents: "npm:2.3.2" @@ -20025,6 +21784,21 @@ __metadata: languageName: node linkType: hard +"playwright@npm:1.57.0, playwright@npm:^1.57.0": + version: 1.57.0 + resolution: "playwright@npm:1.57.0" + dependencies: + fsevents: "npm:2.3.2" + playwright-core: "npm:1.57.0" + dependenciesMeta: + fsevents: + optional: true + bin: + playwright: cli.js + checksum: 10c0/ab03c99a67b835bdea9059f516ad3b6e42c21025f9adaa161a4ef6bc7ca716dcba476d287140bb240d06126eb23f889a8933b8f5f1f1a56b80659d92d1358899 + languageName: node + linkType: hard + "plist@npm:^3.0.5, plist@npm:^3.1.0": version: 3.1.0 resolution: "plist@npm:3.1.0" @@ -20061,6 +21835,13 @@ __metadata: languageName: node linkType: hard +"pngjs@npm:^7.0.0": + version: 7.0.0 + resolution: "pngjs@npm:7.0.0" + checksum: 10c0/0d4c7a0fd476a9c33df7d0a2a73e1d56537628a668841f6995c2bca070cf30819f9254a64363266bc14ef2fee47659dd3b4f2b18eec7ab65143015139f497b38 + languageName: node + linkType: hard + "pony-cause@npm:^2.1.4": version: 2.1.11 resolution: "pony-cause@npm:2.1.11" @@ -20192,6 +21973,15 @@ __metadata: languageName: node linkType: hard +"postcss-safe-parser@npm:^7.0.0": + version: 7.0.1 + resolution: "postcss-safe-parser@npm:7.0.1" + peerDependencies: + postcss: ^8.4.31 + checksum: 10c0/6957b10b818bd8d4664ec0e548af967f7549abedfb37f844d389571d36af681340f41f9477b9ccf34bcc7599bdef222d1d72e79c64373001fae77089fba6d965 + languageName: node + linkType: hard + "postcss-scss@npm:^4.0.9": version: 4.0.9 resolution: "postcss-scss@npm:4.0.9" @@ -20201,6 +21991,16 @@ __metadata: languageName: node linkType: hard +"postcss-selector-parser@npm:6.0.10": + version: 6.0.10 + resolution: "postcss-selector-parser@npm:6.0.10" + dependencies: + cssesc: "npm:^3.0.0" + util-deprecate: "npm:^1.0.2" + checksum: 10c0/a0b27c5e3f7604c8dc7cd83f145fdd7b21448e0d86072da99e0d78e536ba27aa9db2d42024c50aa530408ee517c4bdc0260529e1afb56608f9a82e839c207e82 + languageName: node + linkType: hard + "postcss-selector-parser@npm:^6.1.0, postcss-selector-parser@npm:^6.1.1, postcss-selector-parser@npm:^6.1.2": version: 6.1.2 resolution: "postcss-selector-parser@npm:6.1.2" @@ -20239,6 +22039,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.5.6": + version: 8.5.6 + resolution: "postcss@npm:8.5.6" + dependencies: + nanoid: "npm:^3.3.11" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/5127cc7c91ed7a133a1b7318012d8bfa112da9ef092dddf369ae699a1f10ebbd89b1b9f25f3228795b84585c72aabd5ced5fc11f2ba467eedf7b081a66fad024 + languageName: node + linkType: hard + "postgres-array@npm:~2.0.0": version: 2.0.0 resolution: "postgres-array@npm:2.0.0" @@ -20335,6 +22146,16 @@ __metadata: languageName: node linkType: hard +"prettier-plugin-svelte@npm:^3.4.0": + version: 3.4.1 + resolution: "prettier-plugin-svelte@npm:3.4.1" + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + checksum: 10c0/ae19ea425458ae26b79eba303a9dd9ffacfb17bf233fa9879a108d4be63929459b226d693e972514afd2fd3c9fa8ee31f9a1bb4749d6dce9012f70f282704c5d + languageName: node + linkType: hard + "prettier-plugin-tailwindcss@npm:^0.6.9": version: 0.6.9 resolution: "prettier-plugin-tailwindcss@npm:0.6.9" @@ -20393,6 +22214,64 @@ __metadata: languageName: node linkType: hard +"prettier-plugin-tailwindcss@npm:^0.7.2": + version: 0.7.2 + resolution: "prettier-plugin-tailwindcss@npm:0.7.2" + peerDependencies: + "@ianvs/prettier-plugin-sort-imports": "*" + "@prettier/plugin-hermes": "*" + "@prettier/plugin-oxc": "*" + "@prettier/plugin-pug": "*" + "@shopify/prettier-plugin-liquid": "*" + "@trivago/prettier-plugin-sort-imports": "*" + "@zackad/prettier-plugin-twig": "*" + prettier: ^3.0 + prettier-plugin-astro: "*" + prettier-plugin-css-order: "*" + prettier-plugin-jsdoc: "*" + prettier-plugin-marko: "*" + prettier-plugin-multiline-arrays: "*" + prettier-plugin-organize-attributes: "*" + prettier-plugin-organize-imports: "*" + prettier-plugin-sort-imports: "*" + prettier-plugin-svelte: "*" + peerDependenciesMeta: + "@ianvs/prettier-plugin-sort-imports": + optional: true + "@prettier/plugin-hermes": + optional: true + "@prettier/plugin-oxc": + optional: true + "@prettier/plugin-pug": + optional: true + "@shopify/prettier-plugin-liquid": + optional: true + "@trivago/prettier-plugin-sort-imports": + optional: true + "@zackad/prettier-plugin-twig": + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-svelte: + optional: true + checksum: 10c0/072fb79be58ba6b9743eb4b85be7da06f7d719764e293fdaf6515f15b21af2d22ab0e6476c636c69983949e42c4382e9566b17e3b123a2df6cf5100336729f3c + languageName: node + linkType: hard + "prettier@npm:2.8.8": version: 2.8.8 resolution: "prettier@npm:2.8.8" @@ -20420,6 +22299,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:^3.7.4": + version: 3.7.4 + resolution: "prettier@npm:3.7.4" + bin: + prettier: bin/prettier.cjs + checksum: 10c0/9675d2cd08eacb1faf1d1a2dbfe24bfab6a912b059fc9defdb380a408893d88213e794a40a2700bd29b140eb3172e0b07c852853f6e22f16f3374659a1a13389 + languageName: node + linkType: hard + "pretty-error@npm:^4.0.0": version: 4.0.0 resolution: "pretty-error@npm:4.0.0" @@ -20461,6 +22349,20 @@ __metadata: languageName: node linkType: hard +"prism-svelte@npm:^0.4.7": + version: 0.4.7 + resolution: "prism-svelte@npm:0.4.7" + checksum: 10c0/406c87e808b8ab5510fef505c02e40cfcb751dbcbbc1c12fe71959a3c2b450177110333cd4673ed1f1ae8c34327c8a3448f5bcfa21027b3463b02f5adc9437b0 + languageName: node + linkType: hard + +"prismjs@npm:^1.17.1": + version: 1.30.0 + resolution: "prismjs@npm:1.30.0" + checksum: 10c0/f56205bfd58ef71ccfcbcb691fd0eb84adc96c6ff21b0b69fc6fdcf02be42d6ef972ba4aed60466310de3d67733f6a746f89f2fb79c00bf217406d465b3e8f23 + languageName: node + linkType: hard + "prismjs@npm:^1.29.0": version: 1.29.0 resolution: "prismjs@npm:1.29.0" @@ -20584,7 +22486,7 @@ __metadata: languageName: node linkType: hard -"punycode.js@npm:2.3.1": +"punycode.js@npm:2.3.1, punycode.js@npm:^2.3.1": version: 2.3.1 resolution: "punycode.js@npm:2.3.1" checksum: 10c0/1d12c1c0e06127fa5db56bd7fdf698daf9a78104456a6b67326877afc21feaa821257b171539caedd2f0524027fa38e67b13dd094159c8d70b6d26d2bea4dfdb @@ -21278,6 +23180,33 @@ __metadata: languageName: node linkType: hard +"rehype-autolink-headings@npm:^7.1.0": + version: 7.1.0 + resolution: "rehype-autolink-headings@npm:7.1.0" + dependencies: + "@types/hast": "npm:^3.0.0" + "@ungap/structured-clone": "npm:^1.0.0" + hast-util-heading-rank: "npm:^3.0.0" + hast-util-is-element: "npm:^3.0.0" + unified: "npm:^11.0.0" + unist-util-visit: "npm:^5.0.0" + checksum: 10c0/3d6653047c83cf7f9455bc0f5483a40170aee211cc057b7541b491d31e54b08fda72ab5ec4caeacea63e8af130584f0e14d5c63f6ddb86b7f34584b9e3f92dcf + languageName: node + linkType: hard + +"rehype-slug@npm:^6.0.0": + version: 6.0.0 + resolution: "rehype-slug@npm:6.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + github-slugger: "npm:^2.0.0" + hast-util-heading-rank: "npm:^3.0.0" + hast-util-to-string: "npm:^3.0.0" + unist-util-visit: "npm:^5.0.0" + checksum: 10c0/51303c33d039c271cabe62161b49fa737be488f70ced62f00c165e47a089a99de2060050385e5c00d0df83ed30c7fa1c79a51b78508702836aefa51f7e7a6760 + languageName: node + linkType: hard + "relateurl@npm:^0.2.7": version: 0.2.7 resolution: "relateurl@npm:0.2.7" @@ -21659,6 +23588,87 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.43.0": + version: 4.53.4 + resolution: "rollup@npm:4.53.4" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.53.4" + "@rollup/rollup-android-arm64": "npm:4.53.4" + "@rollup/rollup-darwin-arm64": "npm:4.53.4" + "@rollup/rollup-darwin-x64": "npm:4.53.4" + "@rollup/rollup-freebsd-arm64": "npm:4.53.4" + "@rollup/rollup-freebsd-x64": "npm:4.53.4" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.53.4" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.53.4" + "@rollup/rollup-linux-arm64-gnu": "npm:4.53.4" + "@rollup/rollup-linux-arm64-musl": "npm:4.53.4" + "@rollup/rollup-linux-loong64-gnu": "npm:4.53.4" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.53.4" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.53.4" + "@rollup/rollup-linux-riscv64-musl": "npm:4.53.4" + "@rollup/rollup-linux-s390x-gnu": "npm:4.53.4" + "@rollup/rollup-linux-x64-gnu": "npm:4.53.4" + "@rollup/rollup-linux-x64-musl": "npm:4.53.4" + "@rollup/rollup-openharmony-arm64": "npm:4.53.4" + "@rollup/rollup-win32-arm64-msvc": "npm:4.53.4" + "@rollup/rollup-win32-ia32-msvc": "npm:4.53.4" + "@rollup/rollup-win32-x64-gnu": "npm:4.53.4" + "@rollup/rollup-win32-x64-msvc": "npm:4.53.4" + "@types/estree": "npm:1.0.8" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loong64-gnu": + optional: true + "@rollup/rollup-linux-ppc64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-openharmony-arm64": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-gnu": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/d498156cec601d31345bb98a92d84067bdf58175055481dcdd246ae1492d6d7da1f4e56a5a86e2d7558e68189b7e52908d088232477e2aa996133fb10029ab8a + languageName: node + linkType: hard + "root-workspace-0b6124@workspace:.": version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." @@ -21671,13 +23681,15 @@ __metadata: "@types/node": "npm:^20.17.12" cheerio: "npm:^1.0.0" dotenv: "npm:^16.4.7" - eslint: "npm:~9.14.0" + eslint: "npm:^9.39.2" eslint-plugin-simple-import-sort: "npm:^12.1.1" + glob: "npm:^11.0.0" husky: "npm:^9.1.7" - lint-staged: "npm:^15.3.0" + lint-staged: "npm:^16.2.7" mailparser: "npm:^3.7.2" onchange: "npm:^7.1.0" prettier: "npm:^3.4.2" + tsx: "npm:^4.19.2" vitest: "npm:^2.1.8" languageName: unknown linkType: soft @@ -21920,6 +23932,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.6.3": + version: 7.7.3 + resolution: "semver@npm:7.7.3" + bin: + semver: bin/semver.js + checksum: 10c0/4afe5c986567db82f44c8c6faef8fe9df2a9b1d98098fc1721f57c696c4c21cebd572f297fc21002f81889492345b8470473bc6f4aff5fb032a6ea59ea2bc45e + languageName: node + linkType: hard + "sendmail@npm:^1.6.1": version: 1.6.1 resolution: "sendmail@npm:1.6.1" @@ -22226,6 +24247,17 @@ __metadata: languageName: node linkType: hard +"sirv@npm:^3.0.2": + version: 3.0.2 + resolution: "sirv@npm:3.0.2" + dependencies: + "@polka/url": "npm:^1.0.0-next.24" + mrmime: "npm:^2.0.0" + totalist: "npm:^3.0.0" + checksum: 10c0/5930e4397afdb14fbae13751c3be983af4bda5c9aadec832607dc2af15a7162f7d518c71b30e83ae3644b9a24cea041543cc969e5fe2b80af6ce8ea3174b2d04 + languageName: node + linkType: hard + "sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" @@ -22301,16 +24333,6 @@ __metadata: languageName: node linkType: hard -"slice-ansi@npm:^5.0.0": - version: 5.0.0 - resolution: "slice-ansi@npm:5.0.0" - dependencies: - ansi-styles: "npm:^6.0.0" - is-fullwidth-code-point: "npm:^4.0.0" - checksum: 10c0/2d4d40b2a9d5cf4e8caae3f698fe24ae31a4d778701724f578e984dcb485ec8c49f0c04dab59c401821e80fcdfe89cace9c66693b0244e40ec485d72e543914f - languageName: node - linkType: hard - "slice-ansi@npm:^7.1.0": version: 7.1.0 resolution: "slice-ansi@npm:7.1.0" @@ -22632,6 +24654,13 @@ __metadata: languageName: node linkType: hard +"std-env@npm:^3.10.0": + version: 3.10.0 + resolution: "std-env@npm:3.10.0" + checksum: 10c0/1814927a45004d36dde6707eaf17552a546769bc79a6421be2c16ce77d238158dfe5de30910b78ec30d95135cc1c59ea73ee22d2ca170f8b9753f84da34c427f + languageName: node + linkType: hard + "std-env@npm:^3.8.0": version: 3.8.0 resolution: "std-env@npm:3.8.0" @@ -22712,7 +24741,7 @@ __metadata: languageName: node linkType: hard -"string-argv@npm:~0.3.1, string-argv@npm:~0.3.2": +"string-argv@npm:^0.3.2, string-argv@npm:~0.3.1": version: 0.3.2 resolution: "string-argv@npm:0.3.2" checksum: 10c0/75c02a83759ad1722e040b86823909d9a2fc75d15dd71ec4b537c3560746e33b5f5a07f7332d1e3f88319909f82190843aa2f0a0d8c8d591ec08e93d5b8dec82 @@ -22762,6 +24791,16 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^8.0.0": + version: 8.1.0 + resolution: "string-width@npm:8.1.0" + dependencies: + get-east-asian-width: "npm:^1.3.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/749b5d0dab2532b4b6b801064230f4da850f57b3891287023117ab63a464ad79dd208f42f793458f48f3ad121fe2e1f01dd525ff27ead957ed9f205e27406593 + languageName: node + linkType: hard + "string_decoder@npm:^1.1.1": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" @@ -22826,13 +24865,6 @@ __metadata: languageName: node linkType: hard -"strip-final-newline@npm:^3.0.0": - version: 3.0.0 - resolution: "strip-final-newline@npm:3.0.0" - checksum: 10c0/a771a17901427bac6293fd416db7577e2bc1c34a19d38351e9d5478c3c415f523f391003b42ed475f27e33a78233035df183525395f731d3bfb8cdcbd4da08ce - languageName: node - linkType: hard - "strip-final-newline@npm:^4.0.0": version: 4.0.0 resolution: "strip-final-newline@npm:4.0.0" @@ -23018,6 +25050,24 @@ __metadata: languageName: node linkType: hard +"svelte-check@npm:^4.3.4": + version: 4.3.4 + resolution: "svelte-check@npm:4.3.4" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.25" + chokidar: "npm:^4.0.1" + fdir: "npm:^6.2.0" + picocolors: "npm:^1.0.0" + sade: "npm:^1.7.4" + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: ">=5.0.0" + bin: + svelte-check: bin/svelte-check + checksum: 10c0/c84a054daa2bdd377357082eb317ae86dabfc935a8b67867588c8a4e98ad644b67d3aa0f8572bf37cec3a2c37cae0b60632b430dc9d98faff9981efe74e0ca95 + languageName: node + linkType: hard + "svelte-eslint-parser@npm:^0.43.0": version: 0.43.0 resolution: "svelte-eslint-parser@npm:0.43.0" @@ -23036,6 +25086,25 @@ __metadata: languageName: node linkType: hard +"svelte-eslint-parser@npm:^1.4.0": + version: 1.4.1 + resolution: "svelte-eslint-parser@npm:1.4.1" + dependencies: + eslint-scope: "npm:^8.2.0" + eslint-visitor-keys: "npm:^4.0.0" + espree: "npm:^10.0.0" + postcss: "npm:^8.4.49" + postcss-scss: "npm:^4.0.9" + postcss-selector-parser: "npm:^7.0.0" + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + checksum: 10c0/67283b6cb6eee400a3a68e9d33a2f7373695a50a28df487260414d9cea776eba2d195c57cda4cc1597e01790f1ceb9880b77d12f09f9ed5c8acb1b26733e08db + languageName: node + linkType: hard + "svelte-hmr@npm:^0.16.0": version: 0.16.0 resolution: "svelte-hmr@npm:0.16.0" @@ -23160,6 +25229,29 @@ __metadata: languageName: node linkType: hard +"svelte@npm:^5.46.1": + version: 5.46.1 + resolution: "svelte@npm:5.46.1" + dependencies: + "@jridgewell/remapping": "npm:^2.3.4" + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + "@sveltejs/acorn-typescript": "npm:^1.0.5" + "@types/estree": "npm:^1.0.5" + acorn: "npm:^8.12.1" + aria-query: "npm:^5.3.1" + axobject-query: "npm:^4.1.0" + clsx: "npm:^2.1.1" + devalue: "npm:^5.5.0" + esm-env: "npm:^1.2.1" + esrap: "npm:^2.2.1" + is-reference: "npm:^3.0.3" + locate-character: "npm:^3.0.0" + magic-string: "npm:^0.30.11" + zimmerframe: "npm:^1.1.2" + checksum: 10c0/63ba18676fc8e1806795ac49b81b26ff69778ad5f89ae0bc6c3bb7c6ab4eab75a60793d8b9fda2380a789d8baaa01af9a5b2eb27cb27a35b42f356e2e4a6eaa3 + languageName: node + linkType: hard + "sveltekit-i18n@npm:^2.4.2": version: 2.4.2 resolution: "sveltekit-i18n@npm:2.4.2" @@ -23206,6 +25298,13 @@ __metadata: languageName: node linkType: hard +"tailwindcss@npm:4.1.18, tailwindcss@npm:^4.1.17": + version: 4.1.18 + resolution: "tailwindcss@npm:4.1.18" + checksum: 10c0/c79263cea0b2c577859b02f28284caa4eb3844e4d0f563686726ca97817c045c5c395a55bc776daaa351ba9e4aefa9a75bfbb43c22d86f3c573eecc2b87d6bf1 + languageName: node + linkType: hard + "tailwindcss@npm:^3.4.17": version: 3.4.17 resolution: "tailwindcss@npm:3.4.17" @@ -23408,13 +25507,6 @@ __metadata: languageName: node linkType: hard -"text-table@npm:^0.2.0": - version: 0.2.0 - resolution: "text-table@npm:0.2.0" - checksum: 10c0/02805740c12851ea5982686810702e2f14369a5f4c5c40a836821e3eefc65ffeec3131ba324692a37608294b0fd8c1e55a2dd571ffed4909822787668ddbee5c - languageName: node - linkType: hard - "thenify-all@npm:^1.0.0": version: 1.6.0 resolution: "thenify-all@npm:1.6.0" @@ -23504,6 +25596,23 @@ __metadata: languageName: node linkType: hard +"tinyexec@npm:^1.0.2": + version: 1.0.2 + resolution: "tinyexec@npm:1.0.2" + checksum: 10c0/1261a8e34c9b539a9aae3b7f0bb5372045ff28ee1eba035a2a059e532198fe1a182ec61ac60fa0b4a4129f0c4c4b1d2d57355b5cb9aa2d17ac9454ecace502ee + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.15": + version: 0.2.15 + resolution: "tinyglobby@npm:0.2.15" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.3" + checksum: 10c0/869c31490d0d88eedb8305d178d4c75e7463e820df5a9b9d388291daf93e8b1eb5de1dad1c1e139767e4269fe75f3b10d5009b2cc14db96ff98986920a186844 + languageName: node + linkType: hard + "tinypool@npm:^1.0.1": version: 1.0.2 resolution: "tinypool@npm:1.0.2" @@ -23518,6 +25627,13 @@ __metadata: languageName: node linkType: hard +"tinyrainbow@npm:^3.0.3": + version: 3.0.3 + resolution: "tinyrainbow@npm:3.0.3" + checksum: 10c0/1e799d35cd23cabe02e22550985a3051dc88814a979be02dc632a159c393a998628eacfc558e4c746b3006606d54b00bcdea0c39301133956d10a27aa27e988c + languageName: node + linkType: hard + "tinyspy@npm:^3.0.2": version: 3.0.2 resolution: "tinyspy@npm:3.0.2" @@ -23679,6 +25795,13 @@ __metadata: languageName: node linkType: hard +"trough@npm:^2.0.0": + version: 2.2.0 + resolution: "trough@npm:2.2.0" + checksum: 10c0/58b671fc970e7867a48514168894396dd94e6d9d6456aca427cc299c004fe67f35ed7172a36449086b2edde10e78a71a284ec0076809add6834fb8f857ccb9b0 + languageName: node + linkType: hard + "ts-api-utils@npm:^2.0.0": version: 2.0.0 resolution: "ts-api-utils@npm:2.0.0" @@ -23688,6 +25811,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^2.1.0": + version: 2.1.0 + resolution: "ts-api-utils@npm:2.1.0" + peerDependencies: + typescript: ">=4.8.4" + checksum: 10c0/9806a38adea2db0f6aa217ccc6bc9c391ddba338a9fe3080676d0d50ed806d305bb90e8cef0276e793d28c8a929f400abb184ddd7ff83a416959c0f4d2ce754f + languageName: node + linkType: hard + "ts-interface-checker@npm:^0.1.9": version: 0.1.13 resolution: "ts-interface-checker@npm:0.1.13" @@ -23906,6 +26038,15 @@ __metadata: languageName: node linkType: hard +"typedoc-plugin-markdown@npm:^4.9.0": + version: 4.9.0 + resolution: "typedoc-plugin-markdown@npm:4.9.0" + peerDependencies: + typedoc: 0.28.x + checksum: 10c0/12040bbff7bc7e4f777d06710e39421f8ebaaf706f4b8075536453f35bc86726c5ce743de0cc9c39ee1bdfc8611b2878a4cf4c83a493e0678fc640dd71fe97fc + languageName: node + linkType: hard + "typedoc@npm:0.25.10": version: 0.25.10 resolution: "typedoc@npm:0.25.10" @@ -23922,6 +26063,38 @@ __metadata: languageName: node linkType: hard +"typedoc@npm:^0.28.15": + version: 0.28.15 + resolution: "typedoc@npm:0.28.15" + dependencies: + "@gerrit0/mini-shiki": "npm:^3.17.0" + lunr: "npm:^2.3.9" + markdown-it: "npm:^14.1.0" + minimatch: "npm:^9.0.5" + yaml: "npm:^2.8.1" + peerDependencies: + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x + bin: + typedoc: bin/typedoc + checksum: 10c0/b5988ebebb367fed44f110bbd37baee85fe95fe10c8d5a511c33d787eb1e924e66ba54cb0763d63ed2c406adbd32fcb87bcbc3fd61b0bc8ab6f3a6c06f2de978 + languageName: node + linkType: hard + +"typescript-eslint@npm:^8.48.1": + version: 8.49.0 + resolution: "typescript-eslint@npm:8.49.0" + dependencies: + "@typescript-eslint/eslint-plugin": "npm:8.49.0" + "@typescript-eslint/parser": "npm:8.49.0" + "@typescript-eslint/typescript-estree": "npm:8.49.0" + "@typescript-eslint/utils": "npm:8.49.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 10c0/6559807ab5f0708455e22eeface154e5386506d22e4f0c5b1b2038c671ec9fb8b18f0319f6fc1f776f125902e3dc1fcfa99cc44ccf694ccac768c7034499c7e6 + languageName: node + linkType: hard + "typescript@npm:5.3.2": version: 5.3.2 resolution: "typescript@npm:5.3.2" @@ -23952,6 +26125,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^5.7.2": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 + languageName: node + linkType: hard + "typescript@npm:^5.7.3": version: 5.7.3 resolution: "typescript@npm:5.7.3" @@ -23992,6 +26175,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@npm%3A^5.7.2#optional!builtin<compat/typescript>": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin<compat/typescript>::version=5.9.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 + languageName: node + linkType: hard + "typescript@patch:typescript@npm%3A^5.7.3#optional!builtin<compat/typescript>": version: 5.7.3 resolution: "typescript@patch:typescript@npm%3A5.7.3#optional!builtin<compat/typescript>::version=5.7.3&hash=5786d5" @@ -24009,7 +26202,7 @@ __metadata: languageName: node linkType: hard -"uc.micro@npm:^2.0.0": +"uc.micro@npm:^2.0.0, uc.micro@npm:^2.1.0": version: 2.1.0 resolution: "uc.micro@npm:2.1.0" checksum: 10c0/8862eddb412dda76f15db8ad1c640ccc2f47cdf8252a4a30be908d535602c8d33f9855dfcccb8b8837855c1ce1eaa563f7fa7ebe3c98fd0794351aab9b9c55fa @@ -24073,6 +26266,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard + "undici@npm:6.19.2": version: 6.19.2 resolution: "undici@npm:6.19.2" @@ -24094,6 +26294,21 @@ __metadata: languageName: node linkType: hard +"unified@npm:^11.0.0": + version: 11.0.5 + resolution: "unified@npm:11.0.5" + dependencies: + "@types/unist": "npm:^3.0.0" + bail: "npm:^2.0.0" + devlop: "npm:^1.0.0" + extend: "npm:^3.0.0" + is-plain-obj: "npm:^4.0.0" + trough: "npm:^2.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/53c8e685f56d11d9d458a43e0e74328a4d6386af51c8ac37a3dcabec74ce5026da21250590d4aff6733ccd7dc203116aae2b0769abc18cdf9639a54ae528dfc9 + languageName: node + linkType: hard + "unique-filename@npm:^1.1.1": version: 1.1.1 resolution: "unique-filename@npm:1.1.1" @@ -24139,6 +26354,82 @@ __metadata: languageName: node linkType: hard +"unist-util-is@npm:^4.0.0": + version: 4.1.0 + resolution: "unist-util-is@npm:4.1.0" + checksum: 10c0/21ca3d7bacc88853b880b19cb1b133a056c501617d7f9b8cce969cd8b430ed7e1bc416a3a11b02540d5de6fb86807e169d00596108a459d034cf5faec97c055e + languageName: node + linkType: hard + +"unist-util-is@npm:^6.0.0": + version: 6.0.1 + resolution: "unist-util-is@npm:6.0.1" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10c0/5a487d390193811d37a68264e204dbc7c15c40b8fc29b5515a535d921d071134f571d7b5cbd59bcd58d5ce1c0ab08f20fc4a1f0df2287a249c979267fc32ce06 + languageName: node + linkType: hard + +"unist-util-stringify-position@npm:^2.0.0": + version: 2.0.3 + resolution: "unist-util-stringify-position@npm:2.0.3" + dependencies: + "@types/unist": "npm:^2.0.2" + checksum: 10c0/46fa03f840df173b7f032cbfffdb502fb05b79b3fb5451681c796cf4985d9087a537833f5afb75d55e79b46bbbe4b3d81dd75a1062f9289091c526aebe201d5d + languageName: node + linkType: hard + +"unist-util-stringify-position@npm:^4.0.0": + version: 4.0.0 + resolution: "unist-util-stringify-position@npm:4.0.0" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10c0/dfe1dbe79ba31f589108cb35e523f14029b6675d741a79dea7e5f3d098785045d556d5650ec6a8338af11e9e78d2a30df12b1ee86529cded1098da3f17ee999e + languageName: node + linkType: hard + +"unist-util-visit-parents@npm:^3.0.0": + version: 3.1.1 + resolution: "unist-util-visit-parents@npm:3.1.1" + dependencies: + "@types/unist": "npm:^2.0.0" + unist-util-is: "npm:^4.0.0" + checksum: 10c0/231c80c5ba8e79263956fcaa25ed2a11ad7fe77ac5ba0d322e9d51bbc4238501e3bb52f405e518bcdc5471e27b33eff520db0aa4a3b1feb9fb6e2de6ae385d49 + languageName: node + linkType: hard + +"unist-util-visit-parents@npm:^6.0.0": + version: 6.0.2 + resolution: "unist-util-visit-parents@npm:6.0.2" + dependencies: + "@types/unist": "npm:^3.0.0" + unist-util-is: "npm:^6.0.0" + checksum: 10c0/f1e4019dbd930301825895e3737b1ee0cd682f7622ddd915062135cbb39f8c090aaece3a3b5eae1f2ea52ec33f0931abb8f8a8b5c48a511a4203e3d360a8cd49 + languageName: node + linkType: hard + +"unist-util-visit@npm:^2.0.1": + version: 2.0.3 + resolution: "unist-util-visit@npm:2.0.3" + dependencies: + "@types/unist": "npm:^2.0.0" + unist-util-is: "npm:^4.0.0" + unist-util-visit-parents: "npm:^3.0.0" + checksum: 10c0/7b11303d82271ca53a2ced2d56c87a689dd518596c99ff4a11cdff750f5cc5c0e4b64b146bd2363557cb29443c98713bfd1e8dc6d1c3f9d474b9eb1f23a60888 + languageName: node + linkType: hard + +"unist-util-visit@npm:^5.0.0": + version: 5.0.0 + resolution: "unist-util-visit@npm:5.0.0" + dependencies: + "@types/unist": "npm:^3.0.0" + unist-util-is: "npm:^6.0.0" + unist-util-visit-parents: "npm:^6.0.0" + checksum: 10c0/51434a1d80252c1540cce6271a90fd1a106dbe624997c09ed8879279667fb0b2d3a685e02e92bf66598dcbe6cdffa7a5f5fb363af8fdf90dda6c855449ae39a5 + languageName: node + linkType: hard + "universalify@npm:^0.1.0": version: 0.1.2 resolution: "universalify@npm:0.1.2" @@ -24358,6 +26649,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^11.1.0": + version: 11.1.0 + resolution: "uuid@npm:11.1.0" + bin: + uuid: dist/esm/bin/uuid + checksum: 10c0/34aa51b9874ae398c2b799c88a127701408cd581ee89ec3baa53509dd8728cbb25826f2a038f9465f8b7be446f0fbf11558862965b18d21c993684297628d4d3 + languageName: node + linkType: hard + "uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" @@ -24411,6 +26711,36 @@ __metadata: languageName: node linkType: hard +"vfile-message@npm:^2.0.4": + version: 2.0.4 + resolution: "vfile-message@npm:2.0.4" + dependencies: + "@types/unist": "npm:^2.0.0" + unist-util-stringify-position: "npm:^2.0.0" + checksum: 10c0/ce50d90e0e5dc8f995f39602dd2404f1756388a54209c983d259b17c15e6f262a53546977a638065bc487d0657799fa96f4c1ba6b2915d9724a4968e9c7ff1c8 + languageName: node + linkType: hard + +"vfile-message@npm:^4.0.0": + version: 4.0.3 + resolution: "vfile-message@npm:4.0.3" + dependencies: + "@types/unist": "npm:^3.0.0" + unist-util-stringify-position: "npm:^4.0.0" + checksum: 10c0/33d9f219610d27987689bb14fa5573d2daa146941d1a05416dd7702c4215b23f44ed81d059e70d0e4e24f9a57d5f4dc9f18d35a993f04cf9446a7abe6d72d0c0 + languageName: node + linkType: hard + +"vfile@npm:^6.0.0": + version: 6.0.3 + resolution: "vfile@npm:6.0.3" + dependencies: + "@types/unist": "npm:^3.0.0" + vfile-message: "npm:^4.0.0" + checksum: 10c0/e5d9eb4810623f23758cfc2205323e33552fb5972e5c2e6587babe08fe4d24859866277404fb9e2a20afb71013860d96ec806cb257536ae463c87d70022ab9ef + languageName: node + linkType: hard + "vite-node@npm:2.1.8": version: 2.1.8 resolution: "vite-node@npm:2.1.8" @@ -24426,6 +26756,17 @@ __metadata: languageName: node linkType: hard +"vite-plugin-devtools-json@npm:^1.0.0": + version: 1.0.0 + resolution: "vite-plugin-devtools-json@npm:1.0.0" + dependencies: + uuid: "npm:^11.1.0" + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + checksum: 10c0/9b5c0666b9609306d230e0e2e637ab6c73e0323afc8aed39582d2ca486af90a0bc52162f5579cb684f4e2fcdedd460932c57ff8d5aa2cdce1a2a98374d8c94d4 + languageName: node + linkType: hard + "vite-tsconfig-paths@npm:^4.3.2": version: 4.3.2 resolution: "vite-tsconfig-paths@npm:4.3.2" @@ -24568,6 +26909,61 @@ __metadata: languageName: node linkType: hard +"vite@npm:^6.0.0 || ^7.0.0, vite@npm:^7.2.6": + version: 7.3.0 + resolution: "vite@npm:7.3.0" + dependencies: + esbuild: "npm:^0.27.0" + fdir: "npm:^6.5.0" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.3" + postcss: "npm:^8.5.6" + rollup: "npm:^4.43.0" + tinyglobby: "npm:^0.2.15" + 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 + dependenciesMeta: + fsevents: + optional: true + 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 + bin: + vite: bin/vite.js + checksum: 10c0/0457c196cdd5761ec351c0f353945430fbad330e615b9eeab729c8ae163334f18acdc1d9cd7d9d673dbf111f07f6e4f0b25d4ac32360e65b4a6df9991046f3ff + languageName: node + linkType: hard + "vitefu@npm:^0.2.5": version: 0.2.5 resolution: "vitefu@npm:0.2.5" @@ -24580,6 +26976,28 @@ __metadata: languageName: node linkType: hard +"vitefu@npm:^1.1.1": + version: 1.1.1 + resolution: "vitefu@npm:1.1.1" + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + checksum: 10c0/7e0d0dd6fb73bd80cb56a3f47ccc44159e0330c3e94b2951648079b35711226f9088dbe616d910b931740b92259230b874fbe351108b49f5c11b629b641292a5 + languageName: node + linkType: hard + +"vitest-browser-svelte@npm:^2.0.1": + version: 2.0.1 + resolution: "vitest-browser-svelte@npm:2.0.1" + peerDependencies: + svelte: ^3 || ^4 || ^5 || ^5.0.0-next.0 + vitest: ^4.0.0 + checksum: 10c0/47afa35683f0dd08f1b0d5204061cb294eb5a5e0f79cf5c1e7c82581549dd75291713a5ef9a80b2fcdb324a69e90e2f12d844a9d58a3db4626ec1ba52ffbb782 + languageName: node + linkType: hard + "vitest@npm:^2.1.8": version: 2.1.8 resolution: "vitest@npm:2.1.8" @@ -24630,6 +27048,65 @@ __metadata: languageName: node linkType: hard +"vitest@npm:^4.0.15": + version: 4.0.15 + resolution: "vitest@npm:4.0.15" + dependencies: + "@vitest/expect": "npm:4.0.15" + "@vitest/mocker": "npm:4.0.15" + "@vitest/pretty-format": "npm:4.0.15" + "@vitest/runner": "npm:4.0.15" + "@vitest/snapshot": "npm:4.0.15" + "@vitest/spy": "npm:4.0.15" + "@vitest/utils": "npm:4.0.15" + es-module-lexer: "npm:^1.7.0" + expect-type: "npm:^1.2.2" + magic-string: "npm:^0.30.21" + obug: "npm:^2.1.1" + pathe: "npm:^2.0.3" + picomatch: "npm:^4.0.3" + std-env: "npm:^3.10.0" + tinybench: "npm:^2.9.0" + tinyexec: "npm:^1.0.2" + tinyglobby: "npm:^0.2.15" + tinyrainbow: "npm:^3.0.3" + vite: "npm:^6.0.0 || ^7.0.0" + why-is-node-running: "npm:^2.3.0" + peerDependencies: + "@edge-runtime/vm": "*" + "@opentelemetry/api": ^1.9.0 + "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 + "@vitest/browser-playwright": 4.0.15 + "@vitest/browser-preview": 4.0.15 + "@vitest/browser-webdriverio": 4.0.15 + "@vitest/ui": 4.0.15 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@opentelemetry/api": + optional: true + "@types/node": + optional: true + "@vitest/browser-playwright": + optional: true + "@vitest/browser-preview": + optional: true + "@vitest/browser-webdriverio": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 10c0/fd57913dbcba81b67ca67bae37f0920f2785a60939a9029a82ebb843253f7a67f93f2c959cb90bb23a57055c0256ec0a6059ec9a10c129e8912c09b6e407242b + languageName: node + linkType: hard + "vscode-oniguruma@npm:^1.7.0": version: 1.7.0 resolution: "vscode-oniguruma@npm:1.7.0" @@ -25093,6 +27570,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^8.18.3": + version: 8.18.3 + resolution: "ws@npm:8.18.3" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/eac918213de265ef7cb3d4ca348b891a51a520d839aa51cdb8ca93d4fa7ff9f6ccb339ccee89e4075324097f0a55157c89fa3f7147bde9d8d7e90335dc087b53 + languageName: node + linkType: hard + "xdg-app-paths@npm:8.3.0": version: 8.3.0 resolution: "xdg-app-paths@npm:8.3.0" @@ -25233,12 +27725,12 @@ __metadata: languageName: node linkType: hard -"yaml@npm:~2.6.1": - version: 2.6.1 - resolution: "yaml@npm:2.6.1" +"yaml@npm:^2.8.1": + version: 2.8.2 + resolution: "yaml@npm:2.8.2" bin: yaml: bin.mjs - checksum: 10c0/aebf07f61c72b38c74d2b60c3a3ccf89ee4da45bcd94b2bfb7899ba07a5257625a7c9f717c65a6fc511563d48001e01deb1d9e55f0133f3e2edf86039c8c1be7 + checksum: 10c0/703e4dc1e34b324aa66876d63618dcacb9ed49f7e7fe9b70f1e703645be8d640f68ab84f12b86df8ac960bac37acf5513e115de7c970940617ce0343c8c9cd96 languageName: node linkType: hard @@ -25339,6 +27831,13 @@ __metadata: languageName: node linkType: hard +"zimmerframe@npm:^1.1.2": + version: 1.1.4 + resolution: "zimmerframe@npm:1.1.4" + checksum: 10c0/9470cbf22cefae975ab413c7158a119d082b354ddcf0da48a842f2f42246fa15943cd9b92c047de39db38015e3b866e32f383bc217e8e4f4192945c7d425536b + languageName: node + linkType: hard + "zod@npm:^3.19.1, zod@npm:^3.22.4": version: 3.24.1 resolution: "zod@npm:3.24.1"