diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..dcdbcf8 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,42 @@ +name: Github Pages + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: write + id-token: write + pages: write + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - name: 🛎️ Checkout + uses: actions/checkout@v4 + + - name: 🐧 Use Node.js v20.x + uses: actions/setup-node@v4 + with: + node-version: v20.x + cache: "pnpm" + + - run: corepack enable + - run: pnpm install + - run: pnpm build + - run: pnpm search:build + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: "./build" + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f5e086 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store +.idea diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1f6a343 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +dist +node_modules +.astro +.vscode diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..fbd1886 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,21 @@ +{ + "trailingComma": "all", + "tabWidth": 2, + "semi": true, + "singleQuote": false, + "printWidth": 100, + "plugins": ["prettier-plugin-tailwindcss", "prettier-plugin-organize-attributes"], + "overrides": [ + { + "files": "*.astro", + "options": { + "parser": "astro", + "plugins": [ + "prettier-plugin-tailwindcss", + "prettier-plugin-astro", + "prettier-plugin-organize-attributes" + ] + } + } + ] +} diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..97c5773 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,140 @@ +Zero Bias +andretshurotshka +Dmitry Boldyrev +Sergey Sova +Alexander Khoroshikh <32790736+AlexandrHoroshih@users.noreply.github.com> +popuguytheparrot +github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> +Igor Kamyshev +Sova <5620073+sergeysova@users.noreply.github.com> +AlexandrHoroshih +Valery Kobzar +doasync +Egor Guscha +Illia Osmanov (iosmanov) +mg901 +Yan +Igor Ryzhov +Illia Osmanov +Arthur Irgashev +bloadvenro +YanLobat +Ilya <10588170+Komar0ff@users.noreply.github.com> +Kirill Mironov +Ainur Sharaev +Arutyunyan Artyom +drevoed +Nikita Nafranets +dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> +popuguy <19804652+popuguytheparrot@users.noreply.github.com> +Aleksandr Osipov +Andrew Gopienko +bakugod <47696795+bakugod@users.noreply.github.com> +Victor Didenko +sterios_sav +Dmitrij Shuleshov <3arabotaet@gmail.com> +Viktor Pasynok +zukhrik +Mika Andrianarijaona +SLy +Spoki4 +uulaal +Antropov Andrey +Bohdan Petrov +Egor Aristov +sergey20x25 +Ainur <33234903+ainursharaev@users.noreply.github.com> +Alexander Belov <16794134+belovweb@users.noreply.github.com> +Anton Kosykh +Anton Yurovskykh <11337124+yurovant@users.noreply.github.com> +Arsen-95 <83858217+Arsen-95@users.noreply.github.com> +Denis Sikuler +Kirill <79372250+bybbsy@users.noreply.github.com> +Komar0ff +Maxim Sidorov +Renat Sagdeev +Samir +Sozonov +Stanislav +Tauyekel Kunzhol +Victor +Vladimir Ivakin +aldibatyr@gmail.com <47831819+aldibatyr@users.noreply.github.com> +allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> +cqh +ivanov-v +xaota <1172619+xaota@users.noreply.github.com> +☃︎ +0xflotus <0xflotus@gmail.com> +45m0d3u5 +7iomka <7iomka@gmail.com> +Abel Siqueira +Aleksandr Grigorii <42298502+domosedov@users.noreply.github.com> +Aleksei Arro +Aleksei Pudnikov +Alex Anokhin +Alexander Mikhaylov +Alexandrsv +Andrew Laiff +Artsiom Rymarchyk +DecliningLotus +Denis Maushov +Denis Skiba +Dmitry Dudin +Ed Prince +Egor Gorbachev +Gabriel Husek +Ilya Martynov <84752977+Ilmar7786@users.noreply.github.com> +Infant Frontender <34810942+newbornfrontender@users.noreply.github.com> +Ivan Oshchepkov +Jan Keromnes +Jesse Jackson +Joel Bandi +Kireev Mikhail <29187880+kireevmp@users.noreply.github.com> +Kirill Tyurin +Kurzdor +Lebedev Konstantin +Leniorko +Ludovic Dem +MiiZZo +Mike Cann +Oleg <64708593+olejech@users.noreply.github.com> +Oleh +Pasha Grekovich +Pavel Tereschenko +Rachael Dawn +Rafael Fakhreev +Raman Aktsisiuk +Robert Kuzhin <82271383+raidenmiro@users.noreply.github.com> +Roman +Satya Rohith +Sergey Belozyorcev +Shiyan7 <57950265+Shiyan7@users.noreply.github.com> +The Gitter Badger +Usman Yunusov +Vadim Filimonov +Vasili Sviridov +Vasili Svirydau +Victor Kolb +Victor-Nikliaiev +Vladislav +Vladislav Botvin +Will Heslam +WoodenPC +Yesset +alek-de-mik +anatolykopyl +ansunrisein <45604301+ansunrisein@users.noreply.github.com> +c01nd01r +dmitryplyaskin <35658668+dmitryplyaskin@users.noreply.github.com> +gzaripov +houston[bot] +ilfey +kanno <812137533@qq.com> +leushkin +miyaokamarina +nulladdict +risenforces +seonmyung.lim +vladthelittleone +xxxue <1659809758@qq.com> diff --git a/README.md b/README.md new file mode 100644 index 0000000..f9cc6fd --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# Documentation + +## Content files + +All files of the content can be written using MD or MDX inside `src/content/docs/[lang]`. + +Astro can find localized version via the same slug (path to the document) in the another directory: + +- `src/content/docs/[lang]/[slug].md` (_or `.mdx`_) +- `src/content/docs/en/api/effector/clearNode.md` +- `src/content/docs/ru/api/effector/clearNode.md` + +## Configuration + +The most basic setup is in `src/consts.ts`. + +Translation of the UI elements is in `src/languages.ts`. + +All navigation structure is in `src/navigation.ts`. + +## Run + +Simply install the `pnpm` and run: + +```shell +pnpm install +pnpm start +``` + +To fetch commits history, you need Github Personal Access Token. Just add it: + +```shell +GITHUB_TOKEN=github_pat_REDACTED_TOKEN pnpm start +``` + +### Search + +By default search disabled on the local machine due to index requirements. But you can enable it by following steps: + +1. Build the docs — `COMPRESS=false pnpm build` +2. Start search serve server — `pnpm search:dev` +3. Start astro with env parameter `PUBLIC_SEARCH=true pnpm dev` + +`COMPRESS=false` disables compressing, to speed up build. diff --git a/astro.config.ts b/astro.config.ts new file mode 100644 index 0000000..63b84a3 --- /dev/null +++ b/astro.config.ts @@ -0,0 +1,56 @@ +import "dotenv/config"; +import { defineConfig } from "astro/config"; +import mdx from "@astrojs/mdx"; +import compress from "astro-compress"; +import preact from "@astrojs/preact"; +import tailwind from "@astrojs/tailwind"; +import directive from "remark-directive"; +import github from "remark-github"; +import breaks from "remark-breaks"; +import rehypeAutolinkHeadings from "rehype-autolink-headings"; + +import { remarkHeadingId } from "@effector/remark-heading-id"; + +import { admonitions } from "./plugins/admonitions"; +import { remarkFallbackLang } from "./plugins/remark-fallback-lang"; + +// https://astro.build/config +export default defineConfig({ + site: process.env.NODE_ENV === "development" ? "http://localhost:4321" : `https://effector.dev`, + integrations: [ + tailwind({ applyBaseStyles: false }), + preact({ compat: true }), + mdx({ extendMarkdownConfig: true }), + process.env.COMPRESS !== "false" && compress(), + ], + prefetch: true, + base: "/", + build: { + assets: "assets", + }, + scopedStyleStrategy: "where", + markdown: { + syntaxHighlight: "prism", + remarkPlugins: [directive, admonitions, github, remarkHeadingId], + rehypePlugins: [ + [ + rehypeAutolinkHeadings, + { + behavior: "prepend", + properties: { class: "href" }, + }, + ], + ], + }, + vite: { + server: { + proxy: { + "/_pagefind": { + target: "http://127.0.0.1:1414", + rewrite: (path) => path.replace(/^\/dist/, ""), + }, + }, + }, + }, + devToolbar: { enabled: false }, +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..b3e00d7 --- /dev/null +++ b/package.json @@ -0,0 +1,68 @@ +{ + "name": "@effector/docs", + "type": "module", + "private": true, + "version": "23.0.0", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro", + "search:dev": "pagefind --serve", + "search:build": "pagefind" + }, + "repository": { + "type": "git", + "url": "https://github.com/effector/effector" + }, + "packageManager": "pnpm@8.11.0", + "overrides": { + "react": "npm:@preact/compat@latest", + "react-dom": "npm:@preact/compat@latest" + }, + "dependencies": { + "@algolia/client-search": "^4.22.1", + "@astrojs/mdx": "^2.2.1", + "@astrojs/preact": "^3.1.1", + "@astrojs/tailwind": "^5.1.0", + "@codesandbox/sandpack-react": "^2.10.0", + "@effector/remark-heading-id": "^2.0.2", + "@octokit/rest": "^19.0.13", + "@pagefind/default-ui": "^1.0.4", + "@tailwindcss/typography": "^0.5.10", + "@types/html-escaper": "^3.0.2", + "@types/mdast": "^4.0.3", + "@types/node": "^20.11.30", + "@types/react": "^18.2.69", + "@types/react-dom": "^18.2.22", + "@types/unist": "^3.0.2", + "astro": "^4.5.9", + "astro-compress": "^2.2.17", + "clsx": "^1.2.1", + "cssnano": "^6.1.1", + "dotenv": "^16.4.5", + "effector": "^23.2.0", + "html-escaper": "^3.0.3", + "nanoid": "^4.0.2", + "pagefind": "^1.0.4", + "preact": "^10.18.1", + "rehype-autolink-headings": "^7.1.0", + "remark-breaks": "^4.0.0", + "remark-directive": "^3.0.0", + "remark-github": "^12.0.0", + "rollup": "^4.13.0", + "tailwindcss": "^3.4.1", + "unist-util-map": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.1" + }, + "devDependencies": { + "mdast-util-directive": "^3.0.0", + "prettier": "^3.2.5", + "prettier-plugin-astro": "^0.13.0", + "prettier-plugin-organize-attributes": "^1.0.0", + "prettier-plugin-tailwindcss": "^0.5.13", + "typescript": "^5.2.2" + } +} diff --git a/pagefind.yml b/pagefind.yml new file mode 100644 index 0000000..7202ace --- /dev/null +++ b/pagefind.yml @@ -0,0 +1,4 @@ +exclude_selectors: + - "pre > code" +site: ./dist +output_subdir: ./_pagefind diff --git a/plugins/admonitions.ts b/plugins/admonitions.ts new file mode 100644 index 0000000..35960ce --- /dev/null +++ b/plugins/admonitions.ts @@ -0,0 +1,53 @@ +import { map } from "unist-util-map"; +import "mdast-util-directive"; +import type { Root, Node } from "mdast"; +import type { VFile } from "vfile"; +import type { TextDirective } from "mdast-util-directive"; + +// TODO: Add svg icons + +export function admonitions({ types = ["tip", "info", "warning"] } = {}) { + return (tree: Root, file: VFile) => { + return map(tree, (node) => { + if ( + node.type === "containerDirective" || + node.type === "textDirective" || + node.type === "leafDirective" + ) { + if (types.includes(node.name)) { + const children = node.children; + delete node.children; + + return h("div", { className: `admonition admonition-${node.name}` }, [ + h("div", { className: "admonition-heading" }, [ + h("span", { className: "admonition-title" }, [ + text(node.attributes!.title ?? node.name), + ]), + ]), + h("div", { className: "admonition-content" }, children), + ]) satisfies TextDirective; + } + } + return node; + }); + }; +} + +function node(type: string, name: string, props = {}, rest = {}) { + return { + type, + data: { + hName: name, + hProperties: props, + }, + ...rest, + }; +} + +function text(value: string) { + return { type: "text", value }; +} + +function h(name: string, props = {}, children: unknown[] = []): T { + return node("element", name, props, { children }) as T; +} diff --git a/plugins/remark-fallback-lang.ts b/plugins/remark-fallback-lang.ts new file mode 100644 index 0000000..d4730ab --- /dev/null +++ b/plugins/remark-fallback-lang.ts @@ -0,0 +1,80 @@ +// Copied from https://github.com/withastro/docs/blob/main/plugins/remark-fallback-lang.ts +import fs from "node:fs"; +import path from "node:path"; +import type { Root } from "mdast"; +import type { Plugin, Transformer } from "unified"; +import { visit } from "unist-util-visit"; + +export function remarkFallbackLang() { + const pageSourceDir = path.resolve("./src/content/docs"); + const baseUrl = "https://effector.dev/"; + + const transformer: Transformer = (tree, file) => { + const pageUrl = mdFilePathToUrl(file.path, pageSourceDir, baseUrl); + const pageLang = getLanguageCodeFromPathname(pageUrl.pathname); + + // Ignore pages without language prefix and English pages + if (!pageLang || pageLang === "en") return; + + visit(tree, "link", (link) => { + const linkUrl = new URL(link.url, pageUrl); + + // Ignore external links + if (pageUrl.host !== linkUrl.host) return; + + // Ignore link targets without language prefix + const linkLang = getLanguageCodeFromPathname(linkUrl.pathname); + if (!linkLang) return; + + // Ignore link targets that have a valid source file + const linkSourceFileName = tryFindSourceFileForPathname(linkUrl.pathname, pageSourceDir); + if (linkSourceFileName) return; + + link.children.push({ + type: "html", + value: ` (EN)`, + }); + }); + }; + + return transformer; +} + +export function mdFilePathToUrl(mdFilePath: string, pageSourceDir: string, baseUrl: string) { + const pathBelowRoot = path.relative(pageSourceDir, mdFilePath); + const pathname = pathBelowRoot.replace(/\\/g, "/").replace(/\.mdx?$/i, "/"); + + return new URL(pathname, baseUrl); +} + +export function getLanguageCodeFromPathname(pathname: string) { + // Assuming that `pathname` always starts with a `/`, retrieve the first path part, + // which is usually the language code + const firstPathPart = pathname.split("/")[1]; + // Only return parts that look like a two-letter language code + // with optional two-letter country code + if (firstPathPart.match(/^[a-z]{2}(-[a-zA-Z]{2})?$/)) return firstPathPart; +} + +/** + * Attempts to find a Markdown source file for the given `pathname`. + * + * Example: Given a pathname of `/en/some-page` or `/en/some-page/`, + * searches for the source file in the following locations + * and returns the first matching path: + * - `${this.pageSourceDir}/en/some-page.md` + * - `${this.pageSourceDir}/en/some-page/index.md` + * - `${this.pageSourceDir}/e n/some-page.mdx` + * - `${this.pageSourceDir}/en/some-page/index.mdx` + * + * If no existing file is found, returns `undefined`. + */ +export function tryFindSourceFileForPathname(pathname: string, pageSourceDir: string) { + const possibleSourceFilePaths = [ + path.join(pageSourceDir, pathname, ".") + ".md", + path.join(pageSourceDir, pathname, "index.md"), + path.join(pageSourceDir, pathname, ".") + ".mdx", + path.join(pageSourceDir, pathname, "index.mdx"), + ]; + return possibleSourceFilePaths.find((possiblePath) => fs.existsSync(possiblePath)); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..4889913 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,6814 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@algolia/client-search': + specifier: ^4.22.1 + version: 4.22.1 + '@astrojs/mdx': + specifier: ^2.2.1 + version: 2.2.1(astro@4.5.9) + '@astrojs/preact': + specifier: ^3.1.1 + version: 3.1.1(@babel/core@7.24.3)(preact@10.18.1)(vite@5.2.4) + '@astrojs/tailwind': + specifier: ^5.1.0 + version: 5.1.0(astro@4.5.9)(tailwindcss@3.4.1) + '@codesandbox/sandpack-react': + specifier: ^2.10.0 + version: 2.10.0(@lezer/common@1.2.1)(react-dom@18.2.0)(react@18.2.0) + '@effector/remark-heading-id': + specifier: ^2.0.2 + version: 2.0.2(@types/mdast@4.0.3)(unified@10.1.2) + '@octokit/rest': + specifier: ^19.0.13 + version: 19.0.13 + '@pagefind/default-ui': + specifier: ^1.0.4 + version: 1.0.4 + '@tailwindcss/typography': + specifier: ^0.5.10 + version: 0.5.10(tailwindcss@3.4.1) + '@types/html-escaper': + specifier: ^3.0.2 + version: 3.0.2 + '@types/mdast': + specifier: ^4.0.3 + version: 4.0.3 + '@types/node': + specifier: ^20.11.30 + version: 20.11.30 + '@types/react': + specifier: ^18.2.69 + version: 18.2.69 + '@types/react-dom': + specifier: ^18.2.22 + version: 18.2.22 + '@types/unist': + specifier: ^3.0.2 + version: 3.0.2 + astro: + specifier: ^4.5.9 + version: 4.5.9(@types/node@20.11.30)(typescript@5.2.2) + astro-compress: + specifier: ^2.2.17 + version: 2.2.17 + clsx: + specifier: ^1.2.1 + version: 1.2.1 + cssnano: + specifier: ^6.1.1 + version: 6.1.1(postcss@8.4.38) + dotenv: + specifier: ^16.4.5 + version: 16.4.5 + effector: + specifier: ^23.2.0 + version: 23.2.0 + html-escaper: + specifier: ^3.0.3 + version: 3.0.3 + nanoid: + specifier: ^4.0.2 + version: 4.0.2 + pagefind: + specifier: ^1.0.4 + version: 1.0.4 + preact: + specifier: ^10.18.1 + version: 10.18.1 + rehype-autolink-headings: + specifier: ^7.1.0 + version: 7.1.0 + remark-breaks: + specifier: ^4.0.0 + version: 4.0.0 + remark-directive: + specifier: ^3.0.0 + version: 3.0.0 + remark-github: + specifier: ^12.0.0 + version: 12.0.0 + rollup: + specifier: ^4.13.0 + version: 4.13.0 + tailwindcss: + specifier: ^3.4.1 + version: 3.4.1 + unist-util-map: + specifier: ^4.0.0 + version: 4.0.0 + unist-util-visit: + specifier: ^5.0.0 + version: 5.0.0 + vfile: + specifier: ^6.0.1 + version: 6.0.1 + +devDependencies: + mdast-util-directive: + specifier: ^3.0.0 + version: 3.0.0 + prettier: + specifier: ^3.2.5 + version: 3.2.5 + prettier-plugin-astro: + specifier: ^0.13.0 + version: 0.13.0 + prettier-plugin-organize-attributes: + specifier: ^1.0.0 + version: 1.0.0(prettier@3.2.5) + prettier-plugin-tailwindcss: + specifier: ^0.5.13 + version: 0.5.13(prettier-plugin-astro@0.13.0)(prettier-plugin-organize-attributes@1.0.0)(prettier@3.2.5) + typescript: + specifier: ^5.2.2 + version: 5.2.2 + +packages: + + /@algolia/cache-common@4.22.1: + resolution: {integrity: sha512-TJMBKqZNKYB9TptRRjSUtevJeQVXRmg6rk9qgFKWvOy8jhCPdyNZV1nB3SKGufzvTVbomAukFR8guu/8NRKBTA==} + dev: false + + /@algolia/client-common@4.22.1: + resolution: {integrity: sha512-IvaL5v9mZtm4k4QHbBGDmU3wa/mKokmqNBqPj0K7lcR8ZDKzUorhcGp/u8PkPC/e0zoHSTvRh7TRkGX3Lm7iOQ==} + dependencies: + '@algolia/requester-common': 4.22.1 + '@algolia/transporter': 4.22.1 + dev: false + + /@algolia/client-search@4.22.1: + resolution: {integrity: sha512-yb05NA4tNaOgx3+rOxAmFztgMTtGBi97X7PC3jyNeGiwkAjOZc2QrdZBYyIdcDLoI09N0gjtpClcackoTN0gPA==} + dependencies: + '@algolia/client-common': 4.22.1 + '@algolia/requester-common': 4.22.1 + '@algolia/transporter': 4.22.1 + dev: false + + /@algolia/logger-common@4.22.1: + resolution: {integrity: sha512-OnTFymd2odHSO39r4DSWRFETkBufnY2iGUZNrMXpIhF5cmFE8pGoINNPzwg02QLBlGSaLqdKy0bM8S0GyqPLBg==} + dev: false + + /@algolia/requester-common@4.22.1: + resolution: {integrity: sha512-dgvhSAtg2MJnR+BxrIFqlLtkLlVVhas9HgYKMk2Uxiy5m6/8HZBL40JVAMb2LovoPFs9I/EWIoFVjOrFwzn5Qg==} + dev: false + + /@algolia/transporter@4.22.1: + resolution: {integrity: sha512-kzWgc2c9IdxMa3YqA6TN0NW5VrKYYW/BELIn7vnLyn+U/RFdZ4lxxt9/8yq3DKV5snvoDzzO4ClyejZRdV3lMQ==} + dependencies: + '@algolia/cache-common': 4.22.1 + '@algolia/logger-common': 4.22.1 + '@algolia/requester-common': 4.22.1 + dev: false + + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + dev: false + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: false + + /@astrojs/compiler@1.8.2: + resolution: {integrity: sha512-o/ObKgtMzl8SlpIdzaxFnt7SATKPxu4oIP/1NL+HDJRzxfJcAkOTAb/ZKMRyULbz4q+1t2/DAebs2Z1QairkZw==} + dev: true + + /@astrojs/compiler@2.7.0: + resolution: {integrity: sha512-XpC8MAaWjD1ff6/IfkRq/5k1EFj6zhCNqXRd5J43SVJEBj/Bsmizkm8N0xOYscGcDFQkRgEw6/eKnI5x/1l6aA==} + dev: false + + /@astrojs/internal-helpers@0.3.0: + resolution: {integrity: sha512-tGmHvrhpzuz0JBHaJX8GywN9g4rldVNHtkoVDC3m/DdzBO70jGoVuc0uuNVglRYnsdwkbG0K02Iw3nOOR3/Y4g==} + dev: false + + /@astrojs/markdown-remark@4.3.1: + resolution: {integrity: sha512-eJFi600tkRjTFiwzY9oD8AgCgB7gFqyWCKWuZ33dATVBgLiROD+zlMZ8STZzU7+ZALvmiUAun/K7umTmP5YfVQ==} + dependencies: + '@astrojs/prism': 3.0.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.1 + hast-util-to-text: 4.0.0 + import-meta-resolve: 4.0.0 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.0 + remark-gfm: 4.0.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.0 + remark-smartypants: 2.0.0 + shiki: 1.2.0 + unified: 11.0.4 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@astrojs/mdx@2.2.1(astro@4.5.9): + resolution: {integrity: sha512-bSr/AkvGieD9Pc5ZzMnAk7IHnw0vyt/aOujuYUmYT+NHiWahAUy/+ywNNMhTMmea0irdMYnBVC1AEKMQ/oXxow==} + engines: {node: '>=18.14.1'} + peerDependencies: + astro: ^4.0.0 + dependencies: + '@astrojs/markdown-remark': 4.3.1 + '@mdx-js/mdx': 3.0.1 + acorn: 8.11.2 + astro: 4.5.9(@types/node@20.11.30)(typescript@5.2.2) + es-module-lexer: 1.4.2 + estree-util-visit: 2.0.0 + github-slugger: 2.0.0 + gray-matter: 4.0.3 + hast-util-to-html: 9.0.0 + kleur: 4.1.5 + rehype-raw: 7.0.0 + remark-gfm: 4.0.0 + remark-smartypants: 2.0.0 + source-map: 0.7.4 + unist-util-visit: 5.0.0 + vfile: 6.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@astrojs/preact@3.1.1(@babel/core@7.24.3)(preact@10.18.1)(vite@5.2.4): + resolution: {integrity: sha512-ASgmVzh4wLyIyynp5CIfDwE45Vg/tIP+Y+5SnQtURmCP1qZpjdUbsw+bGQ0wCSXtjIbzCBa7Kw7Qn0g6WE2W2w==} + engines: {node: '>=18.14.1'} + peerDependencies: + preact: ^10.6.5 + dependencies: + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.24.3) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.24.3) + '@preact/preset-vite': 2.8.2(@babel/core@7.24.3)(preact@10.18.1)(vite@5.2.4) + '@preact/signals': 1.2.1(preact@10.18.1) + babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.24.3) + preact: 10.18.1 + preact-render-to-string: 6.3.1(preact@10.18.1) + preact-ssr-prepass: 1.2.1(preact@10.18.1) + transitivePeerDependencies: + - '@babel/core' + - supports-color + - vite + dev: false + + /@astrojs/prism@3.0.0: + resolution: {integrity: sha512-g61lZupWq1bYbcBnYZqdjndShr/J3l/oFobBKPA3+qMat146zce3nz2kdO4giGbhYDt4gYdhmoBz0vZJ4sIurQ==} + engines: {node: '>=18.14.1'} + dependencies: + prismjs: 1.29.0 + dev: false + + /@astrojs/tailwind@5.1.0(astro@4.5.9)(tailwindcss@3.4.1): + resolution: {integrity: sha512-BJoCDKuWhU9FT2qYg+fr6Nfb3qP4ShtyjXGHKA/4mHN94z7BGcmauQK23iy+YH5qWvTnhqkd6mQPQ1yTZTe9Ig==} + peerDependencies: + astro: ^3.0.0 || ^4.0.0 + tailwindcss: ^3.0.24 + dependencies: + astro: 4.5.9(@types/node@20.11.30)(typescript@5.2.2) + autoprefixer: 10.4.16(postcss@8.4.31) + postcss: 8.4.31 + postcss-load-config: 4.0.2(postcss@8.4.31) + tailwindcss: 3.4.1 + transitivePeerDependencies: + - ts-node + dev: false + + /@astrojs/telemetry@3.0.4: + resolution: {integrity: sha512-A+0c7k/Xy293xx6odsYZuXiaHO0PL+bnDoXOc47sGDF5ffIKdKQGRPFl2NMlCF4L0NqN4Ynbgnaip+pPF0s7pQ==} + engines: {node: '>=18.14.1'} + dependencies: + ci-info: 3.9.0 + debug: 4.3.4 + dlv: 1.1.3 + dset: 3.1.3 + is-docker: 3.0.0 + is-wsl: 3.1.0 + which-pm-runs: 1.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + dev: false + + /@babel/code-frame@7.24.2: + resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.24.2 + picocolors: 1.0.0 + dev: false + + /@babel/compat-data@7.23.2: + resolution: {integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/compat-data@7.24.1: + resolution: {integrity: sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/core@7.23.5: + resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.5 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5) + '@babel/helpers': 7.23.5 + '@babel/parser': 7.23.5 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.5 + '@babel/types': 7.23.5 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/core@7.24.3: + resolution: {integrity: sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.24.2 + '@babel/generator': 7.24.1 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.3) + '@babel/helpers': 7.24.1 + '@babel/parser': 7.24.1 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.1 + '@babel/types': 7.24.0 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/generator@7.23.5: + resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + jsesc: 2.5.2 + dev: false + + /@babel/generator@7.24.1: + resolution: {integrity: sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + dev: false + + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-compilation-targets@7.22.15: + resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.2 + '@babel/helper-validator-option': 7.22.15 + browserslist: 4.22.1 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: false + + /@babel/helper-compilation-targets@7.23.6: + resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.24.1 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.23.0 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: false + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: false + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.3): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: false + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: false + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-string-parser@7.23.4: + resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-validator-option@7.22.15: + resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helpers@7.23.5: + resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.5 + '@babel/types': 7.23.5 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/helpers@7.24.1: + resolution: {integrity: sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.1 + '@babel/types': 7.24.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: false + + /@babel/highlight@7.24.2: + resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.0 + dev: false + + /@babel/parser@7.23.5: + resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/parser@7.24.1: + resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.0 + dev: false + + /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.24.3): + resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.24.3): + resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.24.3) + dev: false + + /@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.24.3): + resolution: {integrity: sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.24.3) + '@babel/types': 7.23.5 + dev: false + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.23.5 + '@babel/types': 7.23.5 + dev: false + + /@babel/template@7.24.0: + resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.24.2 + '@babel/parser': 7.24.1 + '@babel/types': 7.24.0 + dev: false + + /@babel/traverse@7.23.5: + resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.5 + '@babel/types': 7.23.5 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/traverse@7.24.1: + resolution: {integrity: sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.24.2 + '@babel/generator': 7.24.1 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.24.1 + '@babel/types': 7.24.0 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/types@7.23.5: + resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: false + + /@babel/types@7.24.0: + resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: false + + /@codemirror/autocomplete@6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.1.1): + resolution: {integrity: sha512-L5UInv8Ffd6BPw0P3EF7JLYAMeEbclY7+6Q11REt8vhih8RuLreKtPy/xk8wPxs4EQgYqzI7cdgpiYwWlbS/ow==} + peerDependencies: + '@codemirror/language': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + '@lezer/common': ^1.0.0 + dependencies: + '@codemirror/language': 6.9.3 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + '@lezer/common': 1.1.1 + dev: false + + /@codemirror/autocomplete@6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.2.1): + resolution: {integrity: sha512-L5UInv8Ffd6BPw0P3EF7JLYAMeEbclY7+6Q11REt8vhih8RuLreKtPy/xk8wPxs4EQgYqzI7cdgpiYwWlbS/ow==} + peerDependencies: + '@codemirror/language': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + '@lezer/common': ^1.0.0 + dependencies: + '@codemirror/language': 6.9.3 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + '@lezer/common': 1.2.1 + dev: false + + /@codemirror/commands@6.3.2: + resolution: {integrity: sha512-tjoi4MCWDNxgIpoLZ7+tezdS9OEB6pkiDKhfKx9ReJ/XBcs2G2RXIu+/FxXBlWsPTsz6C9q/r4gjzrsxpcnqCQ==} + dependencies: + '@codemirror/language': 6.9.3 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + '@lezer/common': 1.1.1 + dev: false + + /@codemirror/lang-css@6.2.1(@codemirror/view@6.22.1): + resolution: {integrity: sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==} + dependencies: + '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.1.1) + '@codemirror/language': 6.9.3 + '@codemirror/state': 6.3.2 + '@lezer/common': 1.1.1 + '@lezer/css': 1.1.4 + transitivePeerDependencies: + - '@codemirror/view' + dev: false + + /@codemirror/lang-html@6.4.7: + resolution: {integrity: sha512-y9hWSSO41XlcL4uYwWyk0lEgTHcelWWfRuqmvcAmxfCs0HNWZdriWo/EU43S63SxEZpc1Hd50Itw7ktfQvfkUg==} + dependencies: + '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.1.1) + '@codemirror/lang-css': 6.2.1(@codemirror/view@6.22.1) + '@codemirror/lang-javascript': 6.2.1 + '@codemirror/language': 6.9.3 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + '@lezer/common': 1.1.1 + '@lezer/css': 1.1.4 + '@lezer/html': 1.3.7 + dev: false + + /@codemirror/lang-javascript@6.2.1: + resolution: {integrity: sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==} + dependencies: + '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.1.1) + '@codemirror/language': 6.9.3 + '@codemirror/lint': 6.4.2 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + '@lezer/common': 1.1.1 + '@lezer/javascript': 1.4.9 + dev: false + + /@codemirror/language@6.9.3: + resolution: {integrity: sha512-qq48pYzoi6ldYWV/52+Z9Ou6QouVI+8YwvxFbUypI33NbjG2UeRHKENRyhwljTTiOqjQ33FjyZj6EREQ9apAOQ==} + dependencies: + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + '@lezer/common': 1.1.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + style-mod: 4.1.0 + dev: false + + /@codemirror/lint@6.4.2: + resolution: {integrity: sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==} + dependencies: + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + crelt: 1.0.6 + dev: false + + /@codemirror/state@6.3.2: + resolution: {integrity: sha512-5jEikOfU0r9y+OTlZn5AEQB15mibu3deLBUp+GnLzVUNezEEuPt/JdSeniQNi+0YviblAvOPO2JQAlgJ3SYYaA==} + dev: false + + /@codemirror/view@6.22.1: + resolution: {integrity: sha512-38BRn1nPqZqiHbmWfI8zri23IbRVbmSpSmh1E/Ysvc+lIGGdBC17K8zlK7ZU6fhfy9x4De9Zyj5JQqScPq5DkA==} + dependencies: + '@codemirror/state': 6.3.2 + style-mod: 4.1.0 + w3c-keyname: 2.2.8 + dev: false + + /@codesandbox/nodebox@0.1.8: + resolution: {integrity: sha512-2VRS6JDSk+M+pg56GA6CryyUSGPjBEe8Pnae0QL3jJF1mJZJVMDKr93gJRtBbLkfZN6LD/DwMtf+2L0bpWrjqg==} + dependencies: + outvariant: 1.4.0 + strict-event-emitter: 0.4.6 + dev: false + + /@codesandbox/sandpack-client@2.10.0: + resolution: {integrity: sha512-1vLN0j8ElzTmTG43yoBj1dsOyuw6SqRH9rRa13D32jtM4v2qPFLWIFElcyNpH/YbWsCRZTUCN6ku6U4Yw/Xdvg==} + dependencies: + '@codesandbox/nodebox': 0.1.8 + buffer: 6.0.3 + dequal: 2.0.3 + outvariant: 1.4.0 + static-browser-server: 1.0.3 + dev: false + + /@codesandbox/sandpack-react@2.10.0(@lezer/common@1.2.1)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-XDUOJMPp97FK9YUjHlgFQQqWJY77ih215wFz2CWEzJTalRIM6yneexcTs62Jv1VtgHmRkUxdsgZPqT6KCqKAEA==} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + dependencies: + '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.2)(@codemirror/view@6.22.1)(@lezer/common@1.2.1) + '@codemirror/commands': 6.3.2 + '@codemirror/lang-css': 6.2.1(@codemirror/view@6.22.1) + '@codemirror/lang-html': 6.4.7 + '@codemirror/lang-javascript': 6.2.1 + '@codemirror/language': 6.9.3 + '@codemirror/state': 6.3.2 + '@codemirror/view': 6.22.1 + '@codesandbox/sandpack-client': 2.10.0 + '@lezer/highlight': 1.2.0 + '@react-hook/intersection-observer': 3.1.1(react@18.2.0) + '@stitches/core': 1.2.8 + anser: 2.1.1 + clean-set: 1.1.2 + codesandbox-import-util-types: 2.2.3 + dequal: 2.0.3 + escape-carriage: 1.3.1 + lz-string: 1.5.0 + react: 18.2.0 + react-devtools-inline: 4.4.0 + react-dom: 18.2.0(react@18.2.0) + react-is: 17.0.2 + transitivePeerDependencies: + - '@lezer/common' + dev: false + + /@effector/remark-heading-id@2.0.2(@types/mdast@4.0.3)(unified@10.1.2): + resolution: {integrity: sha512-W28SkiSOtfJf8P5pTqtoybwC7naPwf0n9rFniC4ym1s3UYnT7Zj3itFo9CfkIZ0i0haPCMztxXjpkxANUKxh5Q==} + engines: {node: '>=8'} + peerDependencies: + '@types/mdast': ^3.0.11 + unified: ^10.1.0 + dependencies: + '@types/mdast': 4.0.3 + lodash: 4.17.21 + unified: 10.1.2 + unist-util-visit: 1.4.1 + dev: false + + /@emnapi/runtime@0.45.0: + resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + + /@esbuild/aix-ppc64@0.19.12: + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: false + optional: true + + /@esbuild/aix-ppc64@0.20.2: + resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-arm64@0.19.12: + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-arm64@0.20.2: + resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-arm@0.19.12: + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-arm@0.20.2: + resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-x64@0.19.12: + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-x64@0.20.2: + resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/darwin-arm64@0.19.12: + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@esbuild/darwin-arm64@0.20.2: + resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@esbuild/darwin-x64@0.19.12: + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@esbuild/darwin-x64@0.20.2: + resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@esbuild/freebsd-arm64@0.19.12: + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/freebsd-arm64@0.20.2: + resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/freebsd-x64@0.19.12: + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/freebsd-x64@0.20.2: + resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-arm64@0.19.12: + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-arm64@0.20.2: + resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-arm@0.19.12: + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-arm@0.20.2: + resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-ia32@0.19.12: + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-ia32@0.20.2: + resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-loong64@0.19.12: + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-loong64@0.20.2: + resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-mips64el@0.19.12: + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-mips64el@0.20.2: + resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-ppc64@0.19.12: + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-ppc64@0.20.2: + resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-riscv64@0.19.12: + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-riscv64@0.20.2: + resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-s390x@0.19.12: + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-s390x@0.20.2: + resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-x64@0.19.12: + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-x64@0.20.2: + resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/netbsd-x64@0.19.12: + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/netbsd-x64@0.20.2: + resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/openbsd-x64@0.19.12: + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/openbsd-x64@0.20.2: + resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/sunos-x64@0.19.12: + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: false + optional: true + + /@esbuild/sunos-x64@0.20.2: + resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-arm64@0.19.12: + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-arm64@0.20.2: + resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-ia32@0.19.12: + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-ia32@0.20.2: + resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-x64@0.19.12: + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-x64@0.20.2: + resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-darwin-arm64@0.33.2: + resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-darwin-x64@0.33.2: + resolution: {integrity: sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-libvips-darwin-arm64@1.0.1: + resolution: {integrity: sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==} + engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-darwin-x64@1.0.1: + resolution: {integrity: sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==} + engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-arm64@1.0.1: + resolution: {integrity: sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-arm@1.0.1: + resolution: {integrity: sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-s390x@1.0.1: + resolution: {integrity: sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==} + engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linux-x64@1.0.1: + resolution: {integrity: sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==} + engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linuxmusl-arm64@1.0.1: + resolution: {integrity: sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-libvips-linuxmusl-x64@1.0.1: + resolution: {integrity: sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==} + engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-linux-arm64@0.33.2: + resolution: {integrity: sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-arm@0.33.2: + resolution: {integrity: sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-s390x@0.33.2: + resolution: {integrity: sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==} + engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.1 + dev: false + optional: true + + /@img/sharp-linux-x64@0.33.2: + resolution: {integrity: sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==} + engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linuxmusl-arm64@0.33.2: + resolution: {integrity: sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 + dev: false + optional: true + + /@img/sharp-linuxmusl-x64@0.33.2: + resolution: {integrity: sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==} + engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.1 + dev: false + optional: true + + /@img/sharp-wasm32@0.33.2: + resolution: {integrity: sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [wasm32] + requiresBuild: true + dependencies: + '@emnapi/runtime': 0.45.0 + dev: false + optional: true + + /@img/sharp-win32-ia32@0.33.2: + resolution: {integrity: sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@img/sharp-win32-x64@0.33.2: + resolution: {integrity: sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: false + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.20 + dev: false + + /@jridgewell/gen-mapping@0.3.5: + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + dev: false + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: false + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: false + + /@jridgewell/set-array@1.2.1: + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + dev: false + + /@jridgewell/source-map@0.3.6: + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + dev: false + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: false + + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + + /@jridgewell/trace-mapping@0.3.25: + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + + /@lezer/common@1.1.1: + resolution: {integrity: sha512-aAPB9YbvZHqAW+bIwiuuTDGB4DG0sYNRObGLxud8cW7osw1ZQxfDuTZ8KQiqfZ0QJGcR34CvpTMDXEyo/+Htgg==} + dev: false + + /@lezer/common@1.2.1: + resolution: {integrity: sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==} + dev: false + + /@lezer/css@1.1.4: + resolution: {integrity: sha512-CuUwjidrU7FOBokqASRJc72SmJ9g1PsHXDOWMoKg4md6+2u/Zxzwx5YsYrAFxRDsLrjLlsIyEF1rZHK3gFEJbw==} + dependencies: + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + dev: false + + /@lezer/highlight@1.2.0: + resolution: {integrity: sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==} + dependencies: + '@lezer/common': 1.1.1 + dev: false + + /@lezer/html@1.3.7: + resolution: {integrity: sha512-Wo+rZ5UjLP0VqUTyXjzgmTYRW5bvTJUFn4Uw0K3HCQjX2/+f+zRo9GLN5BCAojwHQISPvaQk8BWSv2SSKx/UcQ==} + dependencies: + '@lezer/common': 1.1.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + dev: false + + /@lezer/javascript@1.4.9: + resolution: {integrity: sha512-7Uv8mBBE6l44spgWEZvEMdDqGV+FIuY7kJ1o5TFm+jxIuxydO3PcKJYiINij09igd1D/9P7l2KDqpkN8c3bM6A==} + dependencies: + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + dev: false + + /@lezer/lr@1.3.14: + resolution: {integrity: sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==} + dependencies: + '@lezer/common': 1.1.1 + dev: false + + /@mdx-js/mdx@3.0.1: + resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==} + dependencies: + '@types/estree': 1.0.5 + '@types/estree-jsx': 1.0.2 + '@types/hast': 3.0.2 + '@types/mdx': 2.0.9 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-build-jsx: 3.0.1 + estree-util-is-identifier-name: 3.0.0 + estree-util-to-js: 2.0.0 + estree-walker: 3.0.3 + hast-util-to-estree: 3.1.0 + hast-util-to-jsx-runtime: 2.3.0 + markdown-extensions: 2.0.0 + periscopic: 3.1.0 + remark-mdx: 3.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.0 + source-map: 0.7.4 + unified: 11.0.4 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: false + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: false + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: false + + /@octokit/auth-token@3.0.4: + resolution: {integrity: sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==} + engines: {node: '>= 14'} + dev: false + + /@octokit/core@4.2.4: + resolution: {integrity: sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==} + engines: {node: '>= 14'} + dependencies: + '@octokit/auth-token': 3.0.4 + '@octokit/graphql': 5.0.6 + '@octokit/request': 6.2.8 + '@octokit/request-error': 3.0.3 + '@octokit/types': 9.3.2 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.0 + transitivePeerDependencies: + - encoding + dev: false + + /@octokit/endpoint@7.0.6: + resolution: {integrity: sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==} + engines: {node: '>= 14'} + dependencies: + '@octokit/types': 9.3.2 + is-plain-object: 5.0.0 + universal-user-agent: 6.0.0 + dev: false + + /@octokit/graphql@5.0.6: + resolution: {integrity: sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==} + engines: {node: '>= 14'} + dependencies: + '@octokit/request': 6.2.8 + '@octokit/types': 9.3.2 + universal-user-agent: 6.0.0 + transitivePeerDependencies: + - encoding + dev: false + + /@octokit/openapi-types@18.1.1: + resolution: {integrity: sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==} + dev: false + + /@octokit/plugin-paginate-rest@6.1.2(@octokit/core@4.2.4): + resolution: {integrity: sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==} + engines: {node: '>= 14'} + peerDependencies: + '@octokit/core': '>=4' + dependencies: + '@octokit/core': 4.2.4 + '@octokit/tsconfig': 1.0.2 + '@octokit/types': 9.3.2 + dev: false + + /@octokit/plugin-request-log@1.0.4(@octokit/core@4.2.4): + resolution: {integrity: sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==} + peerDependencies: + '@octokit/core': '>=3' + dependencies: + '@octokit/core': 4.2.4 + dev: false + + /@octokit/plugin-rest-endpoint-methods@7.2.3(@octokit/core@4.2.4): + resolution: {integrity: sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==} + engines: {node: '>= 14'} + peerDependencies: + '@octokit/core': '>=3' + dependencies: + '@octokit/core': 4.2.4 + '@octokit/types': 10.0.0 + dev: false + + /@octokit/request-error@3.0.3: + resolution: {integrity: sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==} + engines: {node: '>= 14'} + dependencies: + '@octokit/types': 9.3.2 + deprecation: 2.3.1 + once: 1.4.0 + dev: false + + /@octokit/request@6.2.8: + resolution: {integrity: sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==} + engines: {node: '>= 14'} + dependencies: + '@octokit/endpoint': 7.0.6 + '@octokit/request-error': 3.0.3 + '@octokit/types': 9.3.2 + is-plain-object: 5.0.0 + node-fetch: 2.7.0 + universal-user-agent: 6.0.0 + transitivePeerDependencies: + - encoding + dev: false + + /@octokit/rest@19.0.13: + resolution: {integrity: sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==} + engines: {node: '>= 14'} + dependencies: + '@octokit/core': 4.2.4 + '@octokit/plugin-paginate-rest': 6.1.2(@octokit/core@4.2.4) + '@octokit/plugin-request-log': 1.0.4(@octokit/core@4.2.4) + '@octokit/plugin-rest-endpoint-methods': 7.2.3(@octokit/core@4.2.4) + transitivePeerDependencies: + - encoding + dev: false + + /@octokit/tsconfig@1.0.2: + resolution: {integrity: sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==} + dev: false + + /@octokit/types@10.0.0: + resolution: {integrity: sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==} + dependencies: + '@octokit/openapi-types': 18.1.1 + dev: false + + /@octokit/types@9.3.2: + resolution: {integrity: sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==} + dependencies: + '@octokit/openapi-types': 18.1.1 + dev: false + + /@open-draft/deferred-promise@2.2.0: + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + dev: false + + /@pagefind/darwin-arm64@1.0.4: + resolution: {integrity: sha512-2OcthvceX2xhm5XbgOmW+lT45oLuHqCmvFeFtxh1gsuP5cO8vcD8ZH8Laj4pXQFCcK6eAdSShx+Ztx/LsQWZFQ==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@pagefind/darwin-x64@1.0.4: + resolution: {integrity: sha512-xkdvp0D9Ld/ZKsjo/y1bgfhTEU72ITimd2PMMQtts7jf6JPIOJbsiErCvm37m/qMFuPGEq/8d+fZ4pydOj08HQ==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@pagefind/default-ui@1.0.4: + resolution: {integrity: sha512-edkcaPSKq67C49Vehjo+LQCpT615v4d7JRhfGzFPccePvdklaL+VXrfghN/uIfsdoG+HoLI1PcYy2iFcB9CTkw==} + dev: false + + /@pagefind/linux-arm64@1.0.4: + resolution: {integrity: sha512-jGBrcCzIrMnNxLKVtogaQyajVfTAXM59KlBEwg6vTn8NW4fQ6nuFbbhlG4dTIsaamjEM5e8ZBEAKZfTB/qd9xw==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@pagefind/linux-x64@1.0.4: + resolution: {integrity: sha512-LIn/QcvcEtLEBqKe5vpSbSC2O3fvqbRCWOTIklslqSORisCsvzsWbP6j+LYxE9q0oWIfkdMoWV1vrE/oCKRxHg==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@pagefind/windows-x64@1.0.4: + resolution: {integrity: sha512-QlBCVeZfj9fc9sbUgdOz76ZDbeK4xZihOBAFqGuRJeChfM8pnVeH9iqSnXgO3+m9oITugTf7PicyRUFAG76xeQ==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: false + optional: true + + /@preact/preset-vite@2.8.2(@babel/core@7.24.3)(preact@10.18.1)(vite@5.2.4): + resolution: {integrity: sha512-m3tl+M8IO8jgiHnk+7LSTFl8axdPXloewi7iGVLdmCwf34XOzEUur0bZVewW4DUbUipFjTS2CXu27+5f/oexBA==} + peerDependencies: + '@babel/core': 7.x + vite: 2.x || 3.x || 4.x || 5.x + dependencies: + '@babel/core': 7.24.3 + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.24.3) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.24.3) + '@prefresh/vite': 2.4.3(preact@10.18.1)(vite@5.2.4) + '@rollup/pluginutils': 4.2.1 + babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.24.3) + debug: 4.3.4 + kolorist: 1.8.0 + magic-string: 0.30.5 + node-html-parser: 6.1.12 + resolve: 1.22.8 + source-map: 0.7.4 + stack-trace: 1.0.0-pre2 + vite: 5.2.4(@types/node@20.11.30) + transitivePeerDependencies: + - preact + - supports-color + dev: false + + /@preact/signals-core@1.5.0: + resolution: {integrity: sha512-U2diO1Z4i1n2IoFgMYmRdHWGObNrcuTRxyNEn7deSq2cru0vj0583HYQZHsAqcs7FE+hQyX3mjIV7LAfHCvy8w==} + dev: false + + /@preact/signals@1.2.1(preact@10.18.1): + resolution: {integrity: sha512-hRPvp1C2ooDzOHqfnhdpHgoIFDbYFAXLhoid3+jSItuPPD/J0r/UsiWKv/8ZO/oEhjRaP0M5niuRYsWqmY2GEA==} + peerDependencies: + preact: 10.x + dependencies: + '@preact/signals-core': 1.5.0 + preact: 10.18.1 + dev: false + + /@prefresh/babel-plugin@0.5.0: + resolution: {integrity: sha512-joAwpkUDwo7ZqJnufXRGzUb+udk20RBgfA8oLPBh5aJH2LeStmV1luBfeJTztPdyCscC2j2SmZ/tVxFRMIxAEw==} + dev: false + + /@prefresh/core@1.5.2(preact@10.18.1): + resolution: {integrity: sha512-A/08vkaM1FogrCII5PZKCrygxSsc11obExBScm3JF1CryK2uDS3ZXeni7FeKCx1nYdUkj4UcJxzPzc1WliMzZA==} + peerDependencies: + preact: ^10.0.0 + dependencies: + preact: 10.18.1 + dev: false + + /@prefresh/utils@1.2.0: + resolution: {integrity: sha512-KtC/fZw+oqtwOLUFM9UtiitB0JsVX0zLKNyRTA332sqREqSALIIQQxdUCS1P3xR/jT1e2e8/5rwH6gdcMLEmsQ==} + dev: false + + /@prefresh/vite@2.4.3(preact@10.18.1)(vite@5.2.4): + resolution: {integrity: sha512-diQ8AW+Y2i1QEhO64t2hhV8WFP9X0/NAxuSd9eRlVyS3LOs3RgGOwjOmLoJqwCmIbdq5Szq983gO+xdW/l0G6A==} + peerDependencies: + preact: ^10.4.0 + vite: '>=2.0.0' + dependencies: + '@babel/core': 7.23.5 + '@prefresh/babel-plugin': 0.5.0 + '@prefresh/core': 1.5.2(preact@10.18.1) + '@prefresh/utils': 1.2.0 + '@rollup/pluginutils': 4.2.1 + preact: 10.18.1 + vite: 5.2.4(@types/node@20.11.30) + transitivePeerDependencies: + - supports-color + dev: false + + /@react-hook/intersection-observer@3.1.1(react@18.2.0): + resolution: {integrity: sha512-OTDx8/wFaRvzFtKl1dEUEXSOqK2zVJHporiTTdC2xO++0e9FEx9wIrPis5q3lqtXeZH9zYGLbk+aB75qNFbbuw==} + peerDependencies: + react: '>=16.8' + dependencies: + '@react-hook/passive-layout-effect': 1.2.1(react@18.2.0) + intersection-observer: 0.10.0 + react: 18.2.0 + dev: false + + /@react-hook/passive-layout-effect@1.2.1(react@18.2.0): + resolution: {integrity: sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==} + peerDependencies: + react: '>=16.8' + dependencies: + react: 18.2.0 + dev: false + + /@rollup/pluginutils@4.2.1: + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + dev: false + + /@rollup/rollup-android-arm-eabi@4.13.0: + resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-android-arm64@4.13.0: + resolution: {integrity: sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-darwin-arm64@4.13.0: + resolution: {integrity: sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-darwin-x64@4.13.0: + resolution: {integrity: sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.13.0: + resolution: {integrity: sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.13.0: + resolution: {integrity: sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-arm64-musl@4.13.0: + resolution: {integrity: sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.13.0: + resolution: {integrity: sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-x64-gnu@4.13.0: + resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-x64-musl@4.13.0: + resolution: {integrity: sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.13.0: + resolution: {integrity: sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.13.0: + resolution: {integrity: sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-win32-x64-msvc@4.13.0: + resolution: {integrity: sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@shikijs/core@1.2.0: + resolution: {integrity: sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==} + dev: false + + /@stitches/core@1.2.8: + resolution: {integrity: sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==} + dev: false + + /@tailwindcss/typography@0.5.10(tailwindcss@3.4.1): + resolution: {integrity: sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.1 + dev: false + + /@trysound/sax@0.2.0: + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + dev: false + + /@types/acorn@4.0.6: + resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /@types/babel__core@7.20.5: + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + dependencies: + '@babel/parser': 7.23.5 + '@babel/types': 7.23.5 + '@types/babel__generator': 7.6.6 + '@types/babel__template': 7.4.3 + '@types/babel__traverse': 7.20.3 + dev: false + + /@types/babel__generator@7.6.6: + resolution: {integrity: sha512-66BXMKb/sUWbMdBNdMvajU7i/44RkrA3z/Yt1c7R5xejt8qh84iU54yUWCtm0QwGJlDcf/gg4zd/x4mpLAlb/w==} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@types/babel__template@7.4.3: + resolution: {integrity: sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==} + dependencies: + '@babel/parser': 7.23.5 + '@babel/types': 7.23.5 + dev: false + + /@types/babel__traverse@7.20.3: + resolution: {integrity: sha512-Lsh766rGEFbaxMIDH7Qa+Yha8cMVI3qAK6CHt3OR0YfxOIn5Z54iHiyDRycHrBqeIiqGa20Kpsv1cavfBKkRSw==} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@types/css-tree@2.3.7: + resolution: {integrity: sha512-LUlutQBpR2TgqZJdvXCPOx9EME7a4PHSEo2Y2c8POFpj1E9a6V94PUZNwjVdfHWyb8RQZoNHTYOKs980+sOi+g==} + dev: false + + /@types/csso@5.0.4: + resolution: {integrity: sha512-W/FsRkm/9c04x9ON+bj+HQ0cSgNkG1LvcfuBCpkP7cpikM7+RkrNFLGtiofb++xBG6KGMUycLoDbi9/K621ZCw==} + dependencies: + '@types/css-tree': 2.3.7 + dev: false + + /@types/debug@4.1.10: + resolution: {integrity: sha512-tOSCru6s732pofZ+sMv9o4o3Zc+Sa8l3bxd/tweTQudFn06vAzb13ZX46Zi6m6EJ+RUbRTHvgQJ1gBtSgkaUYA==} + dependencies: + '@types/ms': 0.7.33 + + /@types/estree-jsx@1.0.2: + resolution: {integrity: sha512-GNBWlGBMjiiiL5TSkvPtOteuXsiVitw5MYGY1UYlrAq0SKyczsls6sCD7TZ8fsjRsvCVxml7EbyjJezPb3DrSA==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: false + + /@types/hast@3.0.2: + resolution: {integrity: sha512-B5hZHgHsXvfCoO3xgNJvBnX7N8p86TqQeGKXcokW4XXi+qY4vxxPSFYofytvVmpFxzPv7oxDQzjg5Un5m2/xiw==} + dependencies: + '@types/unist': 3.0.2 + dev: false + + /@types/hast@3.0.4: + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + dependencies: + '@types/unist': 3.0.2 + dev: false + + /@types/html-escaper@3.0.2: + resolution: {integrity: sha512-A8vk09eyYzk8J/lFO4OUMKCmRN0rRzfZf4n3Olwapgox/PtTiU8zPYlL1UEkJ/WeHvV6v9Xnj3o/705PKz9r4Q==} + dev: false + + /@types/html-minifier-terser@7.0.2: + resolution: {integrity: sha512-mm2HqV22l8lFQh4r2oSsOEVea+m0qqxEmwpc9kC1p/XzmjLWrReR9D/GRs8Pex2NX/imyEH9c5IU/7tMBQCHOA==} + dev: false + + /@types/mdast@4.0.3: + resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} + dependencies: + '@types/unist': 3.0.2 + + /@types/mdx@2.0.9: + resolution: {integrity: sha512-OKMdj17y8Cs+k1r0XFyp59ChSOwf8ODGtMQ4mnpfz5eFDk1aO41yN3pSKGuvVzmWAkFp37seubY1tzOVpwfWwg==} + dev: false + + /@types/ms@0.7.33: + resolution: {integrity: sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ==} + + /@types/nlcst@1.0.3: + resolution: {integrity: sha512-cpO6PPMz4E++zxP2Vhp/3KVl2Nbtj+Iksb25rlRinG7mphu2zmCIKWWlqdsx1NwJEISogR2eeZTD7JqLOCzaiw==} + dependencies: + '@types/unist': 2.0.10 + dev: false + + /@types/node@20.11.30: + resolution: {integrity: sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==} + dependencies: + undici-types: 5.26.5 + dev: false + + /@types/prop-types@15.7.12: + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + dev: false + + /@types/react-dom@18.2.22: + resolution: {integrity: sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==} + dependencies: + '@types/react': 18.2.69 + dev: false + + /@types/react@18.2.69: + resolution: {integrity: sha512-W1HOMUWY/1Yyw0ba5TkCV+oqynRjG7BnteBB+B7JmAK7iw3l2SW+VGOxL+akPweix6jk2NNJtyJKpn4TkpfK3Q==} + dependencies: + '@types/prop-types': 15.7.12 + '@types/scheduler': 0.16.8 + csstype: 3.1.3 + dev: false + + /@types/scheduler@0.16.8: + resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} + dev: false + + /@types/unist@2.0.10: + resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + + /@types/unist@3.0.2: + resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: false + + /acorn-jsx@5.3.2(acorn@8.11.2): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.2 + dev: false + + /acorn@8.11.2: + resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + + /anser@2.1.1: + resolution: {integrity: sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ==} + dev: false + + /ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + dependencies: + string-width: 4.2.3 + dev: false + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: false + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: false + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: false + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: false + + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: false + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: false + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: false + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: false + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: false + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: false + + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: false + + /array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + dev: false + + /astring@1.8.6: + resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==} + hasBin: true + dev: false + + /astro-compress@2.2.17: + resolution: {integrity: sha512-nqwLZApVtoMyRjvvsWIyBkzj31YSHtzWPOCLdZZLSu94ieWxIzoapa0jD7Vt+K6Mzc+KfXPdB9dK+hDcJCy9wQ==} + dependencies: + '@types/csso': 5.0.4 + '@types/html-minifier-terser': 7.0.2 + csso: 5.0.5 + files-pipe: 2.1.15 + html-minifier-terser: 7.2.0 + kleur: 4.1.5 + lightningcss: 1.24.1 + sharp: 0.33.2 + svgo: 3.2.0 + terser: 5.29.2 + dev: false + + /astro@4.5.9(@types/node@20.11.30)(typescript@5.2.2): + resolution: {integrity: sha512-GheU72Goz7dYQNKaqTxB2H49cNvHfahGSbNkTvMXk+gyHf/g633qadqlO2ZQbTUacoUWmDzvS5fhMZt2/w4naQ==} + engines: {node: '>=18.14.1', npm: '>=6.14.0'} + hasBin: true + dependencies: + '@astrojs/compiler': 2.7.0 + '@astrojs/internal-helpers': 0.3.0 + '@astrojs/markdown-remark': 4.3.1 + '@astrojs/telemetry': 3.0.4 + '@babel/core': 7.24.3 + '@babel/generator': 7.23.5 + '@babel/parser': 7.23.5 + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.24.3) + '@babel/traverse': 7.23.5 + '@babel/types': 7.23.5 + '@types/babel__core': 7.20.5 + acorn: 8.11.2 + aria-query: 5.3.0 + axobject-query: 4.0.0 + boxen: 7.1.1 + chokidar: 3.5.3 + ci-info: 4.0.0 + clsx: 2.0.0 + common-ancestor-path: 1.0.1 + cookie: 0.6.0 + cssesc: 3.0.0 + debug: 4.3.4 + deterministic-object-hash: 2.0.2 + devalue: 4.3.2 + diff: 5.1.0 + dlv: 1.1.3 + dset: 3.1.3 + es-module-lexer: 1.4.2 + esbuild: 0.19.12 + estree-walker: 3.0.3 + execa: 8.0.1 + fast-glob: 3.3.2 + flattie: 1.1.1 + github-slugger: 2.0.0 + gray-matter: 4.0.3 + html-escaper: 3.0.3 + http-cache-semantics: 4.1.1 + js-yaml: 4.1.0 + kleur: 4.1.5 + magic-string: 0.30.5 + mime: 3.0.0 + ora: 7.0.1 + p-limit: 5.0.0 + p-queue: 8.0.1 + path-to-regexp: 6.2.1 + preferred-pm: 3.1.2 + prompts: 2.4.2 + rehype: 13.0.1 + resolve: 1.22.8 + semver: 7.5.4 + shiki: 1.2.0 + string-width: 7.1.0 + strip-ansi: 7.1.0 + tsconfck: 3.0.0(typescript@5.2.2) + unist-util-visit: 5.0.0 + vfile: 6.0.1 + vite: 5.2.4(@types/node@20.11.30) + vitefu: 0.2.5(vite@5.2.4) + which-pm: 2.1.1 + yargs-parser: 21.1.1 + zod: 3.22.4 + zod-to-json-schema: 3.22.5(zod@3.22.4) + optionalDependencies: + sharp: 0.32.6 + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + - typescript + dev: false + + /autoprefixer@10.4.16(postcss@8.4.31): + resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.22.1 + caniuse-lite: 1.0.30001559 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.31 + postcss-value-parser: 4.2.0 + dev: false + + /axobject-query@4.0.0: + resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} + dependencies: + dequal: 2.0.3 + dev: false + + /b4a@1.6.4: + resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + requiresBuild: true + dev: false + optional: true + + /babel-plugin-transform-hook-names@1.0.2(@babel/core@7.24.3): + resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==} + peerDependencies: + '@babel/core': ^7.12.10 + dependencies: + '@babel/core': 7.24.3 + dev: false + + /bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + dev: false + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: false + + /base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + dev: false + + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: false + + /before-after-hook@2.2.3: + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + dev: false + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: false + + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + requiresBuild: true + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + optional: true + + /bl@5.1.0: + resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + dependencies: + buffer: 6.0.3 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + + /boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: false + + /boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.3.0 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + dev: false + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: false + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: false + + /browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001559 + electron-to-chromium: 1.4.571 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.22.1) + dev: false + + /browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001600 + electron-to-chromium: 1.4.715 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.23.0) + dev: false + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: false + + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + requiresBuild: true + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + optional: true + + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + + /camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + dependencies: + pascal-case: 3.1.2 + tslib: 2.6.2 + dev: false + + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: false + + /camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + dev: false + + /caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + dependencies: + browserslist: 4.23.0 + caniuse-lite: 1.0.30001600 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + dev: false + + /caniuse-lite@1.0.30001559: + resolution: {integrity: sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==} + dev: false + + /caniuse-lite@1.0.30001600: + resolution: {integrity: sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==} + dev: false + + /ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + dev: false + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: false + + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: false + + /character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + /character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + /character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + /character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: false + + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: false + + /chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + requiresBuild: true + dev: false + optional: true + + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + dev: false + + /ci-info@4.0.0: + resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} + engines: {node: '>=8'} + dev: false + + /clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + dependencies: + source-map: 0.6.1 + dev: false + + /clean-set@1.1.2: + resolution: {integrity: sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug==} + dev: false + + /cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + dev: false + + /cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + restore-cursor: 4.0.0 + dev: false + + /cli-spinners@2.9.1: + resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} + engines: {node: '>=6'} + dev: false + + /clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + dev: false + + /clsx@2.0.0: + resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} + engines: {node: '>=6'} + dev: false + + /codesandbox-import-util-types@2.2.3: + resolution: {integrity: sha512-Qj00p60oNExthP2oR3vvXmUGjukij+rxJGuiaKM6tyUmSyimdZsqHI/TUvFFClAffk9s7hxGnQgWQ8KCce27qQ==} + dev: false + + /collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + dev: false + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: false + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: false + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: false + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + requiresBuild: true + dev: false + + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + requiresBuild: true + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + requiresBuild: true + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: false + + /colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + dev: false + + /comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + dev: false + + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: false + + /commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: false + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: false + + /commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + dev: false + + /common-ancestor-path@1.0.1: + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + dev: false + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: false + + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + dev: false + + /crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + dev: false + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: false + + /css-declaration-sorter@7.1.1(postcss@8.4.38): + resolution: {integrity: sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.0.9 + dependencies: + postcss: 8.4.38 + dev: false + + /css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + dev: false + + /css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.0 + dev: false + + /css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.0 + dev: false + + /css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: false + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /cssnano-preset-default@6.1.1(postcss@8.4.38): + resolution: {integrity: sha512-XW/dYN2p8Jdkp1lovFd0UVRh6RB0iMyXJbAE9Qm+taR3p2LGu492AW34lVaukUrXoK9IxK5aK3CUmFpUorU4oA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + browserslist: 4.23.0 + css-declaration-sorter: 7.1.1(postcss@8.4.38) + cssnano-utils: 4.0.2(postcss@8.4.38) + postcss: 8.4.38 + postcss-calc: 9.0.1(postcss@8.4.38) + postcss-colormin: 6.1.0(postcss@8.4.38) + postcss-convert-values: 6.1.0(postcss@8.4.38) + postcss-discard-comments: 6.0.2(postcss@8.4.38) + postcss-discard-duplicates: 6.0.3(postcss@8.4.38) + postcss-discard-empty: 6.0.3(postcss@8.4.38) + postcss-discard-overridden: 6.0.2(postcss@8.4.38) + postcss-merge-longhand: 6.0.5(postcss@8.4.38) + postcss-merge-rules: 6.1.1(postcss@8.4.38) + postcss-minify-font-values: 6.1.0(postcss@8.4.38) + postcss-minify-gradients: 6.0.3(postcss@8.4.38) + postcss-minify-params: 6.1.0(postcss@8.4.38) + postcss-minify-selectors: 6.0.4(postcss@8.4.38) + postcss-normalize-charset: 6.0.2(postcss@8.4.38) + postcss-normalize-display-values: 6.0.2(postcss@8.4.38) + postcss-normalize-positions: 6.0.2(postcss@8.4.38) + postcss-normalize-repeat-style: 6.0.2(postcss@8.4.38) + postcss-normalize-string: 6.0.2(postcss@8.4.38) + postcss-normalize-timing-functions: 6.0.2(postcss@8.4.38) + postcss-normalize-unicode: 6.1.0(postcss@8.4.38) + postcss-normalize-url: 6.0.2(postcss@8.4.38) + postcss-normalize-whitespace: 6.0.2(postcss@8.4.38) + postcss-ordered-values: 6.0.2(postcss@8.4.38) + postcss-reduce-initial: 6.1.0(postcss@8.4.38) + postcss-reduce-transforms: 6.0.2(postcss@8.4.38) + postcss-svgo: 6.0.3(postcss@8.4.38) + postcss-unique-selectors: 6.0.4(postcss@8.4.38) + dev: false + + /cssnano-utils@4.0.2(postcss@8.4.38): + resolution: {integrity: sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + dev: false + + /cssnano@6.1.1(postcss@8.4.38): + resolution: {integrity: sha512-paTFZuiVohpaXJuau8l7buFt9+FTmfjwEO70EKitzYOQw3frib/It4sb6cQ+gJyDEyY+myDSni6IbBvKZ0N8Lw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + cssnano-preset-default: 6.1.1(postcss@8.4.38) + lilconfig: 3.1.1 + postcss: 8.4.38 + dev: false + + /csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + dependencies: + css-tree: 2.2.1 + dev: false + + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dev: false + + /d@1.0.1: + resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} + dependencies: + es5-ext: 0.10.62 + type: 1.2.0 + dev: false + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /decode-named-character-reference@1.0.2: + resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + dependencies: + character-entities: 2.0.2 + + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + mimic-response: 3.1.0 + dev: false + optional: true + + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + requiresBuild: true + dev: false + optional: true + + /deepmerge-ts@5.1.0: + resolution: {integrity: sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==} + engines: {node: '>=16.0.0'} + dev: false + + /deprecation@2.3.1: + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + dev: false + + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + /detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + dev: false + + /detect-libc@2.0.2: + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + engines: {node: '>=8'} + requiresBuild: true + dev: false + optional: true + + /detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + dev: false + + /deterministic-object-hash@2.0.2: + resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} + engines: {node: '>=18'} + dependencies: + base-64: 1.0.0 + dev: false + + /devalue@4.3.2: + resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} + dev: false + + /devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dependencies: + dequal: 2.0.3 + + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: false + + /diff@5.1.0: + resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} + engines: {node: '>=0.3.1'} + dev: false + + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: false + + /dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + dev: false + + /domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: false + + /domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: false + + /domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dev: false + + /dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + dev: false + + /dset@3.1.3: + resolution: {integrity: sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ==} + engines: {node: '>=4'} + dev: false + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: false + + /effector@23.2.0: + resolution: {integrity: sha512-TqR/e5BSfAk4ZkHOykmqPmT9Nn4fLE/BhuBiVmy2cb7Vp6in8N+xMDFhWWZAgOdQjehgt/dIVQyXuWpXh3EoZA==} + engines: {node: '>=11.0.0'} + dev: false + + /electron-to-chromium@1.4.571: + resolution: {integrity: sha512-Sc+VtKwKCDj3f/kLBjdyjMpNzoZsU6WuL/wFb6EH8USmHEcebxRXcRrVpOpayxd52tuey4RUDpUsw5OS5LhJqg==} + dev: false + + /electron-to-chromium@1.4.715: + resolution: {integrity: sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==} + dev: false + + /emoji-regex@10.3.0: + resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} + dev: false + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: false + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: false + + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + requiresBuild: true + dependencies: + once: 1.4.0 + dev: false + optional: true + + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + dev: false + + /es-module-lexer@1.4.2: + resolution: {integrity: sha512-7nOqkomXZEaxUDJw21XZNtRk739QvrPSoZoRtbsEfcii00vdzZUh6zh1CQwHhrib8MdEtJfv5rJiGeb4KuV/vw==} + dev: false + + /es5-ext@0.10.62: + resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} + engines: {node: '>=0.10'} + requiresBuild: true + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + next-tick: 1.1.0 + dev: false + + /es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + es6-symbol: 3.1.3 + dev: false + + /es6-symbol@3.1.3: + resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} + dependencies: + d: 1.0.1 + ext: 1.7.0 + dev: false + + /esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 + dev: false + + /esbuild@0.20.2: + resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.20.2 + '@esbuild/android-arm': 0.20.2 + '@esbuild/android-arm64': 0.20.2 + '@esbuild/android-x64': 0.20.2 + '@esbuild/darwin-arm64': 0.20.2 + '@esbuild/darwin-x64': 0.20.2 + '@esbuild/freebsd-arm64': 0.20.2 + '@esbuild/freebsd-x64': 0.20.2 + '@esbuild/linux-arm': 0.20.2 + '@esbuild/linux-arm64': 0.20.2 + '@esbuild/linux-ia32': 0.20.2 + '@esbuild/linux-loong64': 0.20.2 + '@esbuild/linux-mips64el': 0.20.2 + '@esbuild/linux-ppc64': 0.20.2 + '@esbuild/linux-riscv64': 0.20.2 + '@esbuild/linux-s390x': 0.20.2 + '@esbuild/linux-x64': 0.20.2 + '@esbuild/netbsd-x64': 0.20.2 + '@esbuild/openbsd-x64': 0.20.2 + '@esbuild/sunos-x64': 0.20.2 + '@esbuild/win32-arm64': 0.20.2 + '@esbuild/win32-ia32': 0.20.2 + '@esbuild/win32-x64': 0.20.2 + dev: false + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: false + + /escape-carriage@1.3.1: + resolution: {integrity: sha512-GwBr6yViW3ttx1kb7/Oh+gKQ1/TrhYwxKqVmg5gS+BK+Qe2KrOa/Vh7w3HPBvgGf0LfcDGoY9I6NHKoA5Hozhw==} + dev: false + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: false + + /escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + dev: false + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + dependencies: + '@types/estree-jsx': 1.0.2 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + dev: false + + /estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + dev: false + + /estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + dependencies: + '@types/estree-jsx': 1.0.2 + astring: 1.8.6 + source-map: 0.7.4 + dev: false + + /estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + dependencies: + '@types/estree-jsx': 1.0.2 + '@types/unist': 3.0.2 + dev: false + + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: false + + /estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + dev: false + + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: false + + /expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + requiresBuild: true + dev: false + optional: true + + /ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + dependencies: + type: 2.7.2 + dev: false + + /extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + dependencies: + is-extendable: 0.1.1 + dev: false + + /extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + dev: false + + /fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + requiresBuild: true + dev: false + optional: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: false + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: false + + /files-pipe@2.1.15: + resolution: {integrity: sha512-HYPXV6nh/H3U0WUDErZMVUoUJh15XDe5/rFwHWZXU5wIyCbCkA7UAuhLySs7sua3MRuwILuwZLDnQtXhaibNeg==} + dependencies: + '@types/node': 20.11.30 + deepmerge-ts: 5.1.0 + fast-glob: 3.3.2 + dev: false + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: false + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: false + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: false + + /find-yarn-workspace-root2@1.2.16: + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + dependencies: + micromatch: 4.0.5 + pkg-dir: 4.2.0 + dev: false + + /flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + dev: false + + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: false + + /fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + dev: false + + /fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + requiresBuild: true + dev: false + optional: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: false + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: false + + /get-east-asian-width@1.2.0: + resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + engines: {node: '>=18'} + dev: false + + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: false + + /github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + requiresBuild: true + dev: false + optional: true + + /github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + dev: false + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: false + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: false + + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 7.0.4 + path-scurry: 1.10.1 + dev: false + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: false + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: false + + /gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + dependencies: + js-yaml: 3.14.1 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + dev: false + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: false + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: false + + /hast-util-from-html@2.0.1: + resolution: {integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==} + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.1 + parse5: 7.1.2 + vfile: 6.0.1 + vfile-message: 4.0.2 + dev: false + + /hast-util-from-parse5@8.0.1: + resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} + dependencies: + '@types/hast': 3.0.2 + '@types/unist': 3.0.2 + devlop: 1.1.0 + hastscript: 8.0.0 + property-information: 6.3.0 + vfile: 6.0.1 + vfile-location: 5.0.2 + web-namespaces: 2.0.1 + dev: false + + /hast-util-heading-rank@3.0.0: + resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==} + dependencies: + '@types/hast': 3.0.4 + dev: false + + /hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + dependencies: + '@types/hast': 3.0.4 + dev: false + + /hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + dependencies: + '@types/hast': 3.0.2 + dev: false + + /hast-util-raw@9.0.1: + resolution: {integrity: sha512-5m1gmba658Q+lO5uqL5YNGQWeh1MYWZbZmWrM5lncdcuiXuo5E2HT/CIOp0rLF8ksfSwiCVJ3twlgVRyTGThGA==} + dependencies: + '@types/hast': 3.0.2 + '@types/unist': 3.0.2 + '@ungap/structured-clone': 1.2.0 + hast-util-from-parse5: 8.0.1 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.0.2 + parse5: 7.1.2 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.1 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + dev: false + + /hast-util-to-estree@3.1.0: + resolution: {integrity: sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==} + dependencies: + '@types/estree': 1.0.5 + '@types/estree-jsx': 1.0.2 + '@types/hast': 3.0.2 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.0 + mdast-util-mdx-jsx: 3.1.2 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 6.3.0 + space-separated-tokens: 2.0.2 + style-to-object: 0.4.4 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + dev: false + + /hast-util-to-html@9.0.0: + resolution: {integrity: sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw==} + dependencies: + '@types/hast': 3.0.2 + '@types/unist': 3.0.2 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-raw: 9.0.1 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.0.2 + property-information: 6.3.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.3 + zwitch: 2.0.4 + dev: false + + /hast-util-to-jsx-runtime@2.3.0: + resolution: {integrity: sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==} + dependencies: + '@types/estree': 1.0.5 + '@types/hast': 3.0.2 + '@types/unist': 3.0.2 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.0 + mdast-util-mdx-jsx: 3.1.2 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 6.3.0 + space-separated-tokens: 2.0.2 + style-to-object: 1.0.5 + unist-util-position: 5.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: false + + /hast-util-to-parse5@8.0.0: + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + dependencies: + '@types/hast': 3.0.2 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.3.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + dev: false + + /hast-util-to-text@4.0.0: + resolution: {integrity: sha512-EWiE1FSArNBPUo1cKWtzqgnuRQwEeQbQtnFJRYV1hb1BWDgrAlBU0ExptvZMM/KSA82cDpm2sFGf3Dmc5Mza3w==} + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.2 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + dev: false + + /hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + dependencies: + '@types/hast': 3.0.2 + dev: false + + /hastscript@8.0.0: + resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} + dependencies: + '@types/hast': 3.0.2 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 6.3.0 + space-separated-tokens: 2.0.2 + dev: false + + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: false + + /html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + dev: false + + /html-minifier-terser@7.2.0: + resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 10.0.1 + entities: 4.5.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.29.2 + dev: false + + /html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + dev: false + + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: false + + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: false + + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: false + + /import-meta-resolve@4.0.0: + resolution: {integrity: sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==} + dev: false + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + requiresBuild: true + dev: false + optional: true + + /inline-style-parser@0.1.1: + resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} + dev: false + + /inline-style-parser@0.2.2: + resolution: {integrity: sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==} + dev: false + + /intersection-observer@0.10.0: + resolution: {integrity: sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==} + dev: false + + /is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + /is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + requiresBuild: true + dev: false + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: false + + /is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + dev: false + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.0 + dev: false + + /is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + /is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + dev: false + + /is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + dev: false + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: false + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: false + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: false + + /is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + /is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + dependencies: + is-docker: 3.0.0 + dev: false + + /is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + dev: false + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: false + + /is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + dev: false + + /is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + dev: false + + /is-reference@3.0.2: + resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: false + + /is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + dev: false + + /is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + dependencies: + is-inside-container: 1.0.0 + dev: false + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: false + + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: false + + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + dev: false + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: false + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: false + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: false + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: false + + /kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: false + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: false + + /kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: false + + /kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + dev: false + + /lightningcss-darwin-arm64@1.24.1: + resolution: {integrity: sha512-1jQ12jBy+AE/73uGQWGSafK5GoWgmSiIQOGhSEXiFJSZxzV+OXIx+a9h2EYHxdJfX864M+2TAxWPWb0Vv+8y4w==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /lightningcss-darwin-x64@1.24.1: + resolution: {integrity: sha512-R4R1d7VVdq2mG4igMU+Di8GPf0b64ZLnYVkubYnGG0Qxq1KaXQtAzcLI43EkpnoWvB/kUg8JKCWH4S13NfiLcQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /lightningcss-freebsd-x64@1.24.1: + resolution: {integrity: sha512-z6NberUUw5ALES6Ixn2shmjRRrM1cmEn1ZQPiM5IrZ6xHHL5a1lPin9pRv+w6eWfcrEo+qGG6R9XfJrpuY3e4g==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /lightningcss-linux-arm-gnueabihf@1.24.1: + resolution: {integrity: sha512-NLQLnBQW/0sSg74qLNI8F8QKQXkNg4/ukSTa+XhtkO7v3BnK19TS1MfCbDHt+TTdSgNEBv0tubRuapcKho2EWw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /lightningcss-linux-arm64-gnu@1.24.1: + resolution: {integrity: sha512-AQxWU8c9E9JAjAi4Qw9CvX2tDIPjgzCTrZCSXKELfs4mCwzxRkHh2RCxX8sFK19RyJoJAjA/Kw8+LMNRHS5qEg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /lightningcss-linux-arm64-musl@1.24.1: + resolution: {integrity: sha512-JCgH/SrNrhqsguUA0uJUM1PvN5+dVuzPIlXcoWDHSv2OU/BWlj2dUYr3XNzEw748SmNZPfl2NjQrAdzaPOn1lA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /lightningcss-linux-x64-gnu@1.24.1: + resolution: {integrity: sha512-TYdEsC63bHV0h47aNRGN3RiK7aIeco3/keN4NkoSQ5T8xk09KHuBdySltWAvKLgT8JvR+ayzq8ZHnL1wKWY0rw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /lightningcss-linux-x64-musl@1.24.1: + resolution: {integrity: sha512-HLfzVik3RToot6pQ2Rgc3JhfZkGi01hFetHt40HrUMoeKitLoqUUT5owM6yTZPTytTUW9ukLBJ1pc3XNMSvlLw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /lightningcss-win32-x64-msvc@1.24.1: + resolution: {integrity: sha512-joEupPjYJ7PjZtDsS5lzALtlAudAbgIBMGJPNeFe5HfdmJXFd13ECmEM+5rXNxYVMRHua2w8132R6ab5Z6K9Ow==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /lightningcss@1.24.1: + resolution: {integrity: sha512-kUpHOLiH5GB0ERSv4pxqlL0RYKnOXtgGtVe7shDGfhS0AZ4D1ouKFYAcLcZhql8aMspDNzaUCumGHZ78tb2fTg==} + engines: {node: '>= 12.0.0'} + dependencies: + detect-libc: 1.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.24.1 + lightningcss-darwin-x64: 1.24.1 + lightningcss-freebsd-x64: 1.24.1 + lightningcss-linux-arm-gnueabihf: 1.24.1 + lightningcss-linux-arm64-gnu: 1.24.1 + lightningcss-linux-arm64-musl: 1.24.1 + lightningcss-linux-x64-gnu: 1.24.1 + lightningcss-linux-x64-musl: 1.24.1 + lightningcss-win32-x64-msvc: 1.24.1 + dev: false + + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: false + + /lilconfig@3.1.1: + resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} + engines: {node: '>=14'} + dev: false + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: false + + /load-yaml-file@0.2.0: + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + dev: false + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: false + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: false + + /lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + dev: false + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: false + + /lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: false + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: false + + /lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + dev: false + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + + /log-symbols@5.1.0: + resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} + engines: {node: '>=12'} + dependencies: + chalk: 5.3.0 + is-unicode-supported: 1.3.0 + dev: false + + /longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: false + + /lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + dependencies: + tslib: 2.6.2 + dev: false + + /lru-cache@10.2.0: + resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} + engines: {node: 14 || >=16.14} + dev: false + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: false + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: false + + /lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + dev: false + + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + + /markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + dev: false + + /markdown-table@3.0.3: + resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} + dev: false + + /mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + dependencies: + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + unist-util-visit: 5.0.0 + dev: false + + /mdast-util-directive@3.0.0: + resolution: {integrity: sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==} + dependencies: + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + parse-entities: 4.0.1 + stringify-entities: 4.0.3 + unist-util-visit-parents: 6.0.1 + transitivePeerDependencies: + - supports-color + + /mdast-util-find-and-replace@3.0.1: + resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} + dependencies: + '@types/mdast': 4.0.3 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + dev: false + + /mdast-util-from-markdown@2.0.0: + resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==} + dependencies: + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-decode-string: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + /mdast-util-gfm-autolink-literal@2.0.0: + resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==} + dependencies: + '@types/mdast': 4.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.1 + micromark-util-character: 2.0.1 + dev: false + + /mdast-util-gfm-footnote@2.0.0: + resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + micromark-util-normalize-identifier: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + markdown-table: 3.0.3 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm@3.0.0: + resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} + dependencies: + mdast-util-from-markdown: 2.0.0 + mdast-util-gfm-autolink-literal: 2.0.0 + mdast-util-gfm-footnote: 2.0.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-mdx-expression@2.0.0: + resolution: {integrity: sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==} + dependencies: + '@types/estree-jsx': 1.0.2 + '@types/hast': 3.0.2 + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-mdx-jsx@3.1.2: + resolution: {integrity: sha512-eKMQDeywY2wlHc97k5eD8VC+9ASMjN8ItEZQNGwJ6E0XWKiW/Z0V5/H8pvoXUf+y+Mj0VIgeRRbujBmFn4FTyA==} + dependencies: + '@types/estree-jsx': 1.0.2 + '@types/hast': 3.0.2 + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + parse-entities: 4.0.1 + stringify-entities: 4.0.3 + unist-util-remove-position: 5.0.0 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + dependencies: + mdast-util-from-markdown: 2.0.0 + mdast-util-mdx-expression: 2.0.0 + mdast-util-mdx-jsx: 3.1.2 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + dependencies: + '@types/estree-jsx': 1.0.2 + '@types/hast': 3.0.2 + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-newline-to-break@2.0.0: + resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-find-and-replace: 3.0.1 + dev: false + + /mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + dependencies: + '@types/mdast': 4.0.3 + unist-util-is: 6.0.0 + + /mdast-util-to-hast@13.0.2: + resolution: {integrity: sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==} + dependencies: + '@types/hast': 3.0.2 + '@types/mdast': 4.0.3 + '@ungap/structured-clone': 1.2.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.0 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + dev: false + + /mdast-util-to-markdown@2.1.0: + resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} + dependencies: + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-decode-string: 2.0.0 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + /mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + dependencies: + '@types/mdast': 4.0.3 + + /mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + dev: false + + /mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + dev: false + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: false + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: false + + /micromark-core-commonmark@2.0.0: + resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==} + dependencies: + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-factory-destination: 2.0.0 + micromark-factory-label: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-factory-title: 2.0.0 + micromark-factory-whitespace: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-chunked: 2.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-html-tag-name: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-subtokenize: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + /micromark-extension-directive@3.0.0: + resolution: {integrity: sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg==} + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-factory-whitespace: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + parse-entities: 4.0.1 + dev: false + + /micromark-extension-gfm-autolink-literal@2.0.0: + resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==} + dependencies: + micromark-util-character: 2.0.1 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm-footnote@2.0.0: + resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==} + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==} + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm-table@2.0.0: + resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==} + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + dependencies: + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm-task-list-item@2.0.1: + resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==} + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + dependencies: + micromark-extension-gfm-autolink-literal: 2.0.0 + micromark-extension-gfm-footnote: 2.0.0 + micromark-extension-gfm-strikethrough: 2.0.0 + micromark-extension-gfm-table: 2.0.0 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.0.1 + micromark-util-combine-extensions: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-mdx-expression@3.0.0: + resolution: {integrity: sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==} + dependencies: + '@types/estree': 1.0.5 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.1 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-mdx-jsx@3.0.0: + resolution: {integrity: sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==} + dependencies: + '@types/acorn': 4.0.6 + '@types/estree': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.1 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + vfile-message: 4.0.2 + dev: false + + /micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + dependencies: + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + dependencies: + '@types/estree': 1.0.5 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + dev: false + + /micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + dependencies: + acorn: 8.11.2 + acorn-jsx: 5.3.2(acorn@8.11.2) + micromark-extension-mdx-expression: 3.0.0 + micromark-extension-mdx-jsx: 3.0.0 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-factory-destination@2.0.0: + resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} + dependencies: + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + /micromark-factory-label@2.0.0: + resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + /micromark-factory-mdx-expression@2.0.1: + resolution: {integrity: sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==} + dependencies: + '@types/estree': 1.0.5 + devlop: 1.1.0 + micromark-util-character: 2.0.1 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + dev: false + + /micromark-factory-space@2.0.0: + resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} + dependencies: + micromark-util-character: 2.0.1 + micromark-util-types: 2.0.0 + + /micromark-factory-title@2.0.0: + resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + /micromark-factory-whitespace@2.0.0: + resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + /micromark-util-character@2.0.1: + resolution: {integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==} + dependencies: + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + /micromark-util-character@2.1.0: + resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + dependencies: + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + /micromark-util-chunked@2.0.0: + resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} + dependencies: + micromark-util-symbol: 2.0.0 + + /micromark-util-classify-character@2.0.0: + resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} + dependencies: + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + /micromark-util-combine-extensions@2.0.0: + resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} + dependencies: + micromark-util-chunked: 2.0.0 + micromark-util-types: 2.0.0 + + /micromark-util-decode-numeric-character-reference@2.0.1: + resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} + dependencies: + micromark-util-symbol: 2.0.0 + + /micromark-util-decode-string@2.0.0: + resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 2.1.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-symbol: 2.0.0 + + /micromark-util-encode@2.0.0: + resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + + /micromark-util-events-to-acorn@2.0.2: + resolution: {integrity: sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==} + dependencies: + '@types/acorn': 4.0.6 + '@types/estree': 1.0.5 + '@types/unist': 3.0.2 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + vfile-message: 4.0.2 + dev: false + + /micromark-util-html-tag-name@2.0.0: + resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} + + /micromark-util-normalize-identifier@2.0.0: + resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} + dependencies: + micromark-util-symbol: 2.0.0 + + /micromark-util-resolve-all@2.0.0: + resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} + dependencies: + micromark-util-types: 2.0.0 + + /micromark-util-sanitize-uri@2.0.0: + resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + dependencies: + micromark-util-character: 2.1.0 + micromark-util-encode: 2.0.0 + micromark-util-symbol: 2.0.0 + + /micromark-util-subtokenize@2.0.0: + resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==} + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + /micromark-util-symbol@2.0.0: + resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + + /micromark-util-types@2.0.0: + resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + + /micromark@4.0.0: + resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + dependencies: + '@types/debug': 4.1.10 + debug: 4.3.4 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-combine-extensions: 2.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-encode: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-subtokenize: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + transitivePeerDependencies: + - supports-color + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: false + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: false + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: false + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: false + + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + requiresBuild: true + dev: false + optional: true + + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: false + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + requiresBuild: true + dev: false + optional: true + + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + dev: false + + /mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + requiresBuild: true + dev: false + optional: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: false + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: false + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: false + + /nanoid@4.0.2: + resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} + engines: {node: ^14 || ^16 || >=18} + hasBin: true + dev: false + + /napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + requiresBuild: true + dev: false + optional: true + + /next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + dev: false + + /nlcst-to-string@3.1.1: + resolution: {integrity: sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw==} + dependencies: + '@types/nlcst': 1.0.3 + dev: false + + /no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + dependencies: + lower-case: 2.0.2 + tslib: 2.6.2 + dev: false + + /node-abi@3.51.0: + resolution: {integrity: sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + semver: 7.5.4 + dev: false + optional: true + + /node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + requiresBuild: true + dev: false + optional: true + + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: false + + /node-html-parser@6.1.12: + resolution: {integrity: sha512-/bT/Ncmv+fbMGX96XG9g05vFt43m/+SYKIs9oAemQVYyVcZmDAI2Xq/SbNcpOA35eF0Zk2av3Ksf+Xk8Vt8abA==} + dependencies: + css-select: 5.1.0 + he: 1.2.0 + dev: false + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: false + + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + dev: false + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: false + + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: false + + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: false + + /nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: false + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: false + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: false + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: false + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: false + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: false + + /ora@7.0.1: + resolution: {integrity: sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==} + engines: {node: '>=16'} + dependencies: + chalk: 5.3.0 + cli-cursor: 4.0.0 + cli-spinners: 2.9.1 + is-interactive: 2.0.0 + is-unicode-supported: 1.3.0 + log-symbols: 5.1.0 + stdin-discarder: 0.1.0 + string-width: 6.1.0 + strip-ansi: 7.1.0 + dev: false + + /outvariant@1.4.0: + resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==} + dev: false + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: false + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: false + + /p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + dependencies: + yocto-queue: 1.0.0 + dev: false + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: false + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: false + + /p-queue@8.0.1: + resolution: {integrity: sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==} + engines: {node: '>=18'} + dependencies: + eventemitter3: 5.0.1 + p-timeout: 6.1.2 + dev: false + + /p-timeout@6.1.2: + resolution: {integrity: sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==} + engines: {node: '>=14.16'} + dev: false + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: false + + /pagefind@1.0.4: + resolution: {integrity: sha512-oRIizYe+zSI2Jw4zcMU0ebDZm27751hRFiSOBLwc1OIYMrsZKk+3m8p9EVaOmc6zZdtqwwdilNUNxXvBeHcP9w==} + hasBin: true + optionalDependencies: + '@pagefind/darwin-arm64': 1.0.4 + '@pagefind/darwin-x64': 1.0.4 + '@pagefind/linux-arm64': 1.0.4 + '@pagefind/linux-x64': 1.0.4 + '@pagefind/windows-x64': 1.0.4 + dev: false + + /param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + dependencies: + dot-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /parse-entities@4.0.1: + resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} + dependencies: + '@types/unist': 2.0.10 + character-entities: 2.0.2 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.0.2 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + /parse-latin@5.0.1: + resolution: {integrity: sha512-b/K8ExXaWC9t34kKeDV8kGXBkXZ1HCSAZRYE7HR14eA1GlXX5L8iWhs8USJNhQU9q5ci413jCKF0gOyovvyRBg==} + dependencies: + nlcst-to-string: 3.1.1 + unist-util-modify-children: 3.1.1 + unist-util-visit-children: 2.0.2 + dev: false + + /parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.5.0 + dev: false + + /pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: false + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: false + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: false + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: false + + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.2.0 + minipass: 7.0.4 + dev: false + + /path-to-regexp@6.2.1: + resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} + dev: false + + /periscopic@3.1.0: + resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} + dependencies: + '@types/estree': 1.0.5 + estree-walker: 3.0.3 + is-reference: 3.0.2 + dev: false + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: false + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: false + + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: false + + /pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + dev: false + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: false + + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: false + + /postcss-calc@9.0.1(postcss@8.4.38): + resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.2.2 + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-colormin@6.1.0(postcss@8.4.38): + resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + browserslist: 4.23.0 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-convert-values@6.1.0(postcss@8.4.38): + resolution: {integrity: sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + browserslist: 4.23.0 + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-discard-comments@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + dev: false + + /postcss-discard-duplicates@6.0.3(postcss@8.4.38): + resolution: {integrity: sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + dev: false + + /postcss-discard-empty@6.0.3(postcss@8.4.38): + resolution: {integrity: sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + dev: false + + /postcss-discard-overridden@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + dev: false + + /postcss-import@15.1.0(postcss@8.4.38): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + dev: false + + /postcss-js@4.0.1(postcss@8.4.38): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.38 + dev: false + + /postcss-load-config@4.0.2(postcss@8.4.31): + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 3.1.1 + postcss: 8.4.31 + yaml: 2.4.1 + dev: false + + /postcss-load-config@4.0.2(postcss@8.4.38): + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 3.1.1 + postcss: 8.4.38 + yaml: 2.4.1 + dev: false + + /postcss-merge-longhand@6.0.5(postcss@8.4.38): + resolution: {integrity: sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + stylehacks: 6.1.1(postcss@8.4.38) + dev: false + + /postcss-merge-rules@6.1.1(postcss@8.4.38): + resolution: {integrity: sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + browserslist: 4.23.0 + caniuse-api: 3.0.0 + cssnano-utils: 4.0.2(postcss@8.4.38) + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + dev: false + + /postcss-minify-font-values@6.1.0(postcss@8.4.38): + resolution: {integrity: sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-minify-gradients@6.0.3(postcss@8.4.38): + resolution: {integrity: sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + colord: 2.9.3 + cssnano-utils: 4.0.2(postcss@8.4.38) + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-minify-params@6.1.0(postcss@8.4.38): + resolution: {integrity: sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + browserslist: 4.23.0 + cssnano-utils: 4.0.2(postcss@8.4.38) + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-minify-selectors@6.0.4(postcss@8.4.38): + resolution: {integrity: sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + dev: false + + /postcss-nested@6.0.1(postcss@8.4.38): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + dev: false + + /postcss-normalize-charset@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + dev: false + + /postcss-normalize-display-values@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-normalize-positions@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-normalize-repeat-style@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-normalize-string@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-normalize-timing-functions@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-normalize-unicode@6.1.0(postcss@8.4.38): + resolution: {integrity: sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + browserslist: 4.23.0 + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-normalize-url@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-normalize-whitespace@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-ordered-values@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + cssnano-utils: 4.0.2(postcss@8.4.38) + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-reduce-initial@6.1.0(postcss@8.4.38): + resolution: {integrity: sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + browserslist: 4.23.0 + caniuse-api: 3.0.0 + postcss: 8.4.38 + dev: false + + /postcss-reduce-transforms@6.0.2(postcss@8.4.38): + resolution: {integrity: sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: false + + /postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: false + + /postcss-selector-parser@6.0.16: + resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: false + + /postcss-svgo@6.0.3(postcss@8.4.38): + resolution: {integrity: sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==} + engines: {node: ^14 || ^16 || >= 18} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + svgo: 3.2.0 + dev: false + + /postcss-unique-selectors@6.0.4(postcss@8.4.38): + resolution: {integrity: sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + dev: false + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: false + + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: false + + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + dev: false + + /preact-render-to-string@6.3.1(preact@10.18.1): + resolution: {integrity: sha512-NQ28WrjLtWY6lKDlTxnFpKHZdpjfF+oE6V4tZ0rTrunHrtZp6Dm0oFrcJalt/5PNeqJz4j1DuZDS0Y6rCBoqDA==} + peerDependencies: + preact: '>=10' + dependencies: + preact: 10.18.1 + pretty-format: 3.8.0 + dev: false + + /preact-ssr-prepass@1.2.1(preact@10.18.1): + resolution: {integrity: sha512-bLgbUfy8nL+PZghAPpyk9MF+cmXjdwEnxYPaJBmwbzFQqzIz8dQVBqjwB60RqZ9So/vIf6BRfHCiwFGuMCyfbQ==} + peerDependencies: + preact: '>=10 || ^10.0.0-beta.0 || ^10.0.0-alpha.0' + dependencies: + preact: 10.18.1 + dev: false + + /preact@10.18.1: + resolution: {integrity: sha512-mKUD7RRkQQM6s7Rkmi7IFkoEHjuFqRQUaXamO61E6Nn7vqF/bo7EZCmSyrUnp2UWHw0O7XjZ2eeXis+m7tf4lg==} + dev: false + + /prebuild-install@7.1.1: + resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} + engines: {node: '>=10'} + hasBin: true + requiresBuild: true + dependencies: + detect-libc: 2.0.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.51.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: false + optional: true + + /preferred-pm@3.1.2: + resolution: {integrity: sha512-nk7dKrcW8hfCZ4H6klWcdRknBOXWzNQByJ0oJyX97BOupsYD+FzLS4hflgEu/uPUEHZCuRfMxzCBsuWd7OzT8Q==} + engines: {node: '>=10'} + dependencies: + find-up: 5.0.0 + find-yarn-workspace-root2: 1.2.16 + path-exists: 4.0.0 + which-pm: 2.0.0 + dev: false + + /prettier-plugin-astro@0.13.0: + resolution: {integrity: sha512-5HrJNnPmZqTUNoA97zn4gNQv9BgVhv+et03314WpQ9H9N8m2L9OSV798olwmG2YLXPl1iSstlJCR1zB3x5xG4g==} + engines: {node: ^14.15.0 || >=16.0.0} + dependencies: + '@astrojs/compiler': 1.8.2 + prettier: 3.2.5 + sass-formatter: 0.7.9 + dev: true + + /prettier-plugin-organize-attributes@1.0.0(prettier@3.2.5): + resolution: {integrity: sha512-+NmameaLxbCcylEXsKPmawtzla5EE6ECqvGkpfQz4KM847fXDifB1gFnPQEpoADAq6IXg+cMI8Z0ISJEXa6fhg==} + engines: {node: '>=14.0.0'} + peerDependencies: + prettier: ^3.0.0 + dependencies: + prettier: 3.2.5 + dev: true + + /prettier-plugin-tailwindcss@0.5.13(prettier-plugin-astro@0.13.0)(prettier-plugin-organize-attributes@1.0.0)(prettier@3.2.5): + resolution: {integrity: sha512-2tPWHCFNC+WRjAC4SIWQNSOdcL1NNkydXim8w7TDqlZi+/ulZYz2OouAI6qMtkggnPt7lGamboj6LcTMwcCvoQ==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig-melody': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig-melody': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + dependencies: + prettier: 3.2.5 + prettier-plugin-astro: 0.13.0 + prettier-plugin-organize-attributes: 1.0.0(prettier@3.2.5) + dev: true + + /prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + dev: false + + /prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + dev: false + + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: false + + /property-information@6.3.0: + resolution: {integrity: sha512-gVNZ74nqhRMiIUYWGQdosYetaKc83x8oT41a0LlV3AAFCAZwCpg4vmGkq8t34+cUhp3cnM4XDiU/7xlgK7HGrg==} + dev: false + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + requiresBuild: true + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: false + optional: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: false + + /queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + requiresBuild: true + dev: false + optional: true + + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + requiresBuild: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: false + optional: true + + /react-devtools-inline@4.4.0: + resolution: {integrity: sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==} + dependencies: + es6-symbol: 3.1.3 + dev: false + + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + dev: false + + /react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + dev: false + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: false + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: false + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: false + + /rehype-autolink-headings@7.1.0: + resolution: {integrity: sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==} + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.2.0 + hast-util-heading-rank: 3.0.0 + hast-util-is-element: 3.0.0 + unified: 11.0.4 + unist-util-visit: 5.0.0 + dev: false + + /rehype-parse@9.0.0: + resolution: {integrity: sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw==} + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.1 + unified: 11.0.4 + dev: false + + /rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + dependencies: + '@types/hast': 3.0.2 + hast-util-raw: 9.0.1 + vfile: 6.0.1 + dev: false + + /rehype-stringify@10.0.0: + resolution: {integrity: sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ==} + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.0 + unified: 11.0.4 + dev: false + + /rehype@13.0.1: + resolution: {integrity: sha512-AcSLS2mItY+0fYu9xKxOu1LhUZeBZZBx8//5HKzF+0XP+eP8+6a5MXn2+DW2kfXR6Dtp1FEXMVrjyKAcvcU8vg==} + dependencies: + '@types/hast': 3.0.4 + rehype-parse: 9.0.0 + rehype-stringify: 10.0.0 + unified: 11.0.4 + dev: false + + /relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + dev: false + + /remark-breaks@4.0.0: + resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-newline-to-break: 2.0.0 + unified: 11.0.4 + dev: false + + /remark-directive@3.0.0: + resolution: {integrity: sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-directive: 3.0.0 + micromark-extension-directive: 3.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-gfm@4.0.0: + resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-gfm: 3.0.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-github@12.0.0: + resolution: {integrity: sha512-ByefQKFN184LeiGRCabfl7zUJsdlMYWEhiLX1gpmQ11yFg6xSuOTW7LVCv0oc1x+YvUMJW23NU36sJX2RWGgvg==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-find-and-replace: 3.0.1 + mdast-util-to-string: 4.0.0 + to-vfile: 8.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.1 + dev: false + + /remark-mdx@3.0.1: + resolution: {integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==} + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-from-markdown: 2.0.0 + micromark-util-types: 2.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-rehype@11.1.0: + resolution: {integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==} + dependencies: + '@types/hast': 3.0.2 + '@types/mdast': 4.0.3 + mdast-util-to-hast: 13.0.2 + unified: 11.0.4 + vfile: 6.0.1 + dev: false + + /remark-smartypants@2.0.0: + resolution: {integrity: sha512-Rc0VDmr/yhnMQIz8n2ACYXlfw/P/XZev884QU1I5u+5DgJls32o97Vc1RbK3pfumLsJomS2yy8eT4Fxj/2MDVA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + retext: 8.1.0 + retext-smartypants: 5.2.0 + unist-util-visit: 4.1.2 + dev: false + + /remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-to-markdown: 2.1.0 + unified: 11.0.4 + dev: false + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: false + + /restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: false + + /retext-latin@3.1.0: + resolution: {integrity: sha512-5MrD1tuebzO8ppsja5eEu+ZbBeUNCjoEarn70tkXOS7Bdsdf6tNahsv2bY0Z8VooFF6cw7/6S+d3yI/TMlMVVQ==} + dependencies: + '@types/nlcst': 1.0.3 + parse-latin: 5.0.1 + unherit: 3.0.1 + unified: 10.1.2 + dev: false + + /retext-smartypants@5.2.0: + resolution: {integrity: sha512-Do8oM+SsjrbzT2UNIKgheP0hgUQTDDQYyZaIY3kfq0pdFzoPk+ZClYJ+OERNXveog4xf1pZL4PfRxNoVL7a/jw==} + dependencies: + '@types/nlcst': 1.0.3 + nlcst-to-string: 3.1.1 + unified: 10.1.2 + unist-util-visit: 4.1.2 + dev: false + + /retext-stringify@3.1.0: + resolution: {integrity: sha512-767TLOaoXFXyOnjx/EggXlb37ZD2u4P1n0GJqVdpipqACsQP+20W+BNpMYrlJkq7hxffnFk+jc6mAK9qrbuB8w==} + dependencies: + '@types/nlcst': 1.0.3 + nlcst-to-string: 3.1.1 + unified: 10.1.2 + dev: false + + /retext@8.1.0: + resolution: {integrity: sha512-N9/Kq7YTn6ZpzfiGW45WfEGJqFf1IM1q8OsRa1CGzIebCJBNCANDRmOrholiDRGKo/We7ofKR4SEvcGAWEMD3Q==} + dependencies: + '@types/nlcst': 1.0.3 + retext-latin: 3.1.0 + retext-stringify: 3.1.0 + unified: 10.1.2 + dev: false + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: false + + /rollup@4.13.0: + resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.13.0 + '@rollup/rollup-android-arm64': 4.13.0 + '@rollup/rollup-darwin-arm64': 4.13.0 + '@rollup/rollup-darwin-x64': 4.13.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.13.0 + '@rollup/rollup-linux-arm64-gnu': 4.13.0 + '@rollup/rollup-linux-arm64-musl': 4.13.0 + '@rollup/rollup-linux-riscv64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-musl': 4.13.0 + '@rollup/rollup-win32-arm64-msvc': 4.13.0 + '@rollup/rollup-win32-ia32-msvc': 4.13.0 + '@rollup/rollup-win32-x64-msvc': 4.13.0 + fsevents: 2.3.3 + dev: false + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: false + + /s.color@0.0.15: + resolution: {integrity: sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==} + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + requiresBuild: true + dev: false + + /sass-formatter@0.7.9: + resolution: {integrity: sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==} + dependencies: + suf-log: 2.5.3 + dev: true + + /scheduler@0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + dev: false + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: false + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: false + + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: false + + /sharp@0.32.6: + resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} + engines: {node: '>=14.15.0'} + requiresBuild: true + dependencies: + color: 4.2.3 + detect-libc: 2.0.2 + node-addon-api: 6.1.0 + prebuild-install: 7.1.1 + semver: 7.5.4 + simple-get: 4.0.1 + tar-fs: 3.0.4 + tunnel-agent: 0.6.0 + dev: false + optional: true + + /sharp@0.33.2: + resolution: {integrity: sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==} + engines: {libvips: '>=8.15.1', node: ^18.17.0 || ^20.3.0 || >=21.0.0} + requiresBuild: true + dependencies: + color: 4.2.3 + detect-libc: 2.0.3 + semver: 7.6.0 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.2 + '@img/sharp-darwin-x64': 0.33.2 + '@img/sharp-libvips-darwin-arm64': 1.0.1 + '@img/sharp-libvips-darwin-x64': 1.0.1 + '@img/sharp-libvips-linux-arm': 1.0.1 + '@img/sharp-libvips-linux-arm64': 1.0.1 + '@img/sharp-libvips-linux-s390x': 1.0.1 + '@img/sharp-libvips-linux-x64': 1.0.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 + '@img/sharp-libvips-linuxmusl-x64': 1.0.1 + '@img/sharp-linux-arm': 0.33.2 + '@img/sharp-linux-arm64': 0.33.2 + '@img/sharp-linux-s390x': 0.33.2 + '@img/sharp-linux-x64': 0.33.2 + '@img/sharp-linuxmusl-arm64': 0.33.2 + '@img/sharp-linuxmusl-x64': 0.33.2 + '@img/sharp-wasm32': 0.33.2 + '@img/sharp-win32-ia32': 0.33.2 + '@img/sharp-win32-x64': 0.33.2 + dev: false + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: false + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: false + + /shiki@1.2.0: + resolution: {integrity: sha512-xLhiTMOIUXCv5DqJ4I70GgQCtdlzsTqFLZWcMHHG3TAieBUbvEGthdrlPDlX4mL/Wszx9C6rEcxU6kMlg4YlxA==} + dependencies: + '@shikijs/core': 1.2.0 + dev: false + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: false + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: false + + /simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + requiresBuild: true + dev: false + optional: true + + /simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + requiresBuild: true + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + dev: false + optional: true + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + requiresBuild: true + dependencies: + is-arrayish: 0.3.2 + dev: false + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: false + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: false + + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: false + + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: false + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: false + + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: false + + /space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + dev: false + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: false + + /stack-trace@1.0.0-pre2: + resolution: {integrity: sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==} + engines: {node: '>=16'} + dev: false + + /static-browser-server@1.0.3: + resolution: {integrity: sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA==} + dependencies: + '@open-draft/deferred-promise': 2.2.0 + dotenv: 16.4.5 + mime-db: 1.52.0 + outvariant: 1.4.0 + dev: false + + /stdin-discarder@0.1.0: + resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + bl: 5.1.0 + dev: false + + /streamx@2.15.2: + resolution: {integrity: sha512-b62pAV/aeMjUoRN2C/9F0n+G8AfcJjNC0zw/ZmOHeFsIe4m4GzjVW9m6VHXVjk536NbdU9JRwKMJRfkc+zUFTg==} + requiresBuild: true + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + dev: false + optional: true + + /strict-event-emitter@0.4.6: + resolution: {integrity: sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==} + dev: false + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: false + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: false + + /string-width@6.1.0: + resolution: {integrity: sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==} + engines: {node: '>=16'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 10.3.0 + strip-ansi: 7.1.0 + dev: false + + /string-width@7.1.0: + resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} + engines: {node: '>=18'} + dependencies: + emoji-regex: 10.3.0 + get-east-asian-width: 1.2.0 + strip-ansi: 7.1.0 + dev: false + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + requiresBuild: true + dependencies: + safe-buffer: 5.2.1 + dev: false + + /stringify-entities@4.0.3: + resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==} + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: false + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: false + + /strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + dev: false + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: false + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: false + + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: false + optional: true + + /style-mod@4.1.0: + resolution: {integrity: sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==} + dev: false + + /style-to-object@0.4.4: + resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} + dependencies: + inline-style-parser: 0.1.1 + dev: false + + /style-to-object@1.0.5: + resolution: {integrity: sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==} + dependencies: + inline-style-parser: 0.2.2 + dev: false + + /stylehacks@6.1.1(postcss@8.4.38): + resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + dependencies: + browserslist: 4.23.0 + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + dev: false + + /sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.3.10 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: false + + /suf-log@2.5.3: + resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==} + dependencies: + s.color: 0.0.15 + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: false + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: false + + /svgo@3.2.0: + resolution: {integrity: sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.1.0 + css-tree: 2.3.1 + css-what: 6.1.0 + csso: 5.0.5 + picocolors: 1.0.0 + dev: false + + /tailwindcss@3.4.1: + resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.0 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.38 + postcss-import: 15.1.0(postcss@8.4.38) + postcss-js: 4.0.1(postcss@8.4.38) + postcss-load-config: 4.0.2(postcss@8.4.38) + postcss-nested: 6.0.1(postcss@8.4.38) + postcss-selector-parser: 6.0.16 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + dev: false + + /tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + requiresBuild: true + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: false + optional: true + + /tar-fs@3.0.4: + resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} + requiresBuild: true + dependencies: + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 3.1.6 + dev: false + optional: true + + /tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + optional: true + + /tar-stream@3.1.6: + resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} + requiresBuild: true + dependencies: + b4a: 1.6.4 + fast-fifo: 1.3.2 + streamx: 2.15.2 + dev: false + optional: true + + /terser@5.29.2: + resolution: {integrity: sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.11.3 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: false + + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: false + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: false + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: false + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: false + + /to-vfile@8.0.0: + resolution: {integrity: sha512-IcmH1xB5576MJc9qcfEC/m/nQCFt3fzMHz45sSlgJyTWjRbKW1HAkJpuf3DgE57YzIlZcwcBZA5ENQbBo4aLkg==} + dependencies: + vfile: 6.0.1 + dev: false + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false + + /trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + dev: false + + /trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + dev: false + + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: false + + /tsconfck@3.0.0(typescript@5.2.2): + resolution: {integrity: sha512-w3wnsIrJNi7avf4Zb0VjOoodoO0woEqGgZGQm+LHH9przdUI+XDKsWAXwxHA1DaRTjeuZNcregSzr7RaA8zG9A==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 5.2.2 + dev: false + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + + /tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + requiresBuild: true + dependencies: + safe-buffer: 5.2.1 + dev: false + optional: true + + /type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + dev: false + + /type@1.2.0: + resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} + dev: false + + /type@2.7.2: + resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} + dev: false + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: false + + /unherit@3.0.1: + resolution: {integrity: sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg==} + dev: false + + /unified@10.1.2: + resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} + dependencies: + '@types/unist': 2.0.10 + bail: 2.0.2 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 5.3.7 + dev: false + + /unified@11.0.4: + resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + dependencies: + '@types/unist': 3.0.2 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.1 + dev: false + + /unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + dependencies: + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + dev: false + + /unist-util-is@3.0.0: + resolution: {integrity: sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==} + dev: false + + /unist-util-is@5.2.1: + resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} + dependencies: + '@types/unist': 2.0.10 + dev: false + + /unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + dependencies: + '@types/unist': 3.0.2 + + /unist-util-map@4.0.0: + resolution: {integrity: sha512-HJs1tpkSmRJUzj6fskQrS5oYhBYlmtcvy4SepdDEEsL04FjBrgF0Mgggvxc1/qGBGgW7hRh9+UBK1aqTEnBpIA==} + dependencies: + '@types/unist': 3.0.2 + dev: false + + /unist-util-modify-children@3.1.1: + resolution: {integrity: sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA==} + dependencies: + '@types/unist': 2.0.10 + array-iterate: 2.0.1 + dev: false + + /unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + dependencies: + '@types/unist': 3.0.2 + dev: false + + /unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + dependencies: + '@types/unist': 3.0.2 + dev: false + + /unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + dependencies: + '@types/unist': 3.0.2 + unist-util-visit: 5.0.0 + dev: false + + /unist-util-stringify-position@3.0.3: + resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} + dependencies: + '@types/unist': 2.0.10 + dev: false + + /unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + dependencies: + '@types/unist': 3.0.2 + + /unist-util-visit-children@2.0.2: + resolution: {integrity: sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q==} + dependencies: + '@types/unist': 2.0.10 + dev: false + + /unist-util-visit-parents@2.1.2: + resolution: {integrity: sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==} + dependencies: + unist-util-is: 3.0.0 + dev: false + + /unist-util-visit-parents@5.1.3: + resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} + dependencies: + '@types/unist': 2.0.10 + unist-util-is: 5.2.1 + dev: false + + /unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + dependencies: + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + + /unist-util-visit@1.4.1: + resolution: {integrity: sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==} + dependencies: + unist-util-visit-parents: 2.1.2 + dev: false + + /unist-util-visit@4.1.2: + resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==} + dependencies: + '@types/unist': 2.0.10 + unist-util-is: 5.2.1 + unist-util-visit-parents: 5.1.3 + dev: false + + /unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + dependencies: + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + /universal-user-agent@6.0.0: + resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==} + dev: false + + /update-browserslist-db@1.0.13(browserslist@4.22.1): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.1 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: false + + /update-browserslist-db@1.0.13(browserslist@4.23.0): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.23.0 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: false + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: false + + /vfile-location@5.0.2: + resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==} + dependencies: + '@types/unist': 3.0.2 + vfile: 6.0.1 + dev: false + + /vfile-message@3.1.4: + resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} + dependencies: + '@types/unist': 2.0.10 + unist-util-stringify-position: 3.0.3 + dev: false + + /vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + dependencies: + '@types/unist': 3.0.2 + unist-util-stringify-position: 4.0.0 + dev: false + + /vfile@5.3.7: + resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} + dependencies: + '@types/unist': 2.0.10 + is-buffer: 2.0.5 + unist-util-stringify-position: 3.0.3 + vfile-message: 3.1.4 + dev: false + + /vfile@6.0.1: + resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} + dependencies: + '@types/unist': 3.0.2 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + dev: false + + /vite@5.2.4(@types/node@20.11.30): + resolution: {integrity: sha512-vjFghvHWidBTinu5TCymJk/lRHlR5ljqB83yugr0HA1xspUPdOZHqbqDLnZ8f9/jINrtFHTCYYyIUi+o+Q5iyg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.11.30 + esbuild: 0.20.2 + postcss: 8.4.38 + rollup: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + dev: false + + /vitefu@0.2.5(vite@5.2.4): + resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 5.2.4(@types/node@20.11.30) + dev: false + + /w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + dev: false + + /web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + dev: false + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + + /which-pm-runs@1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + dev: false + + /which-pm@2.0.0: + resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} + engines: {node: '>=8.15'} + dependencies: + load-yaml-file: 0.2.0 + path-exists: 4.0.0 + dev: false + + /which-pm@2.1.1: + resolution: {integrity: sha512-xzzxNw2wMaoCWXiGE8IJ9wuPMU+EYhFksjHxrRT8kMT5SnocBPRg69YAMtyV4D12fP582RA+k3P8H9J5EMdIxQ==} + engines: {node: '>=8.15'} + dependencies: + load-yaml-file: 0.2.0 + path-exists: 4.0.0 + dev: false + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: false + + /widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + dev: false + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: false + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: false + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: false + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: false + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: false + + /yaml@2.4.1: + resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} + engines: {node: '>= 14'} + hasBin: true + dev: false + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: false + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: false + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: false + + /zod-to-json-schema@3.22.5(zod@3.22.4): + resolution: {integrity: sha512-+akaPo6a0zpVCCseDed504KBJUQpEW5QZw7RMneNmKw+fGaML1Z9tUNLnHHAC8x6dzVRO1eB2oEMyZRnuBZg7Q==} + peerDependencies: + zod: ^3.22.4 + dependencies: + zod: 3.22.4 + dev: false + + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + dev: false + + /zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} diff --git a/postcss.config.cjs b/postcss.config.cjs new file mode 100644 index 0000000..06aa677 --- /dev/null +++ b/postcss.config.cjs @@ -0,0 +1,3 @@ +module.exports = { + plugins: [require("tailwindcss/nesting"), require("autoprefixer"), require("cssnano")], +}; diff --git a/public/.nojekyll b/public/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/public/CNAME b/public/CNAME new file mode 100644 index 0000000..e755274 --- /dev/null +++ b/public/CNAME @@ -0,0 +1 @@ +beta.effector.dev diff --git a/public/banner.png b/public/banner.png new file mode 100644 index 0000000..e4fa7f5 Binary files /dev/null and b/public/banner.png differ diff --git a/public/external.svg b/public/external.svg new file mode 100644 index 0000000..1285331 --- /dev/null +++ b/public/external.svg @@ -0,0 +1,16 @@ + + + + + + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..df86553 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..bf714e1 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fonts/LexendDeca-Light.ttf b/public/fonts/LexendDeca-Light.ttf new file mode 100644 index 0000000..485a97f Binary files /dev/null and b/public/fonts/LexendDeca-Light.ttf differ diff --git a/public/fonts/LexendDeca-Regular.ttf b/public/fonts/LexendDeca-Regular.ttf new file mode 100644 index 0000000..6b42154 Binary files /dev/null and b/public/fonts/LexendDeca-Regular.ttf differ diff --git a/public/fonts/LexendDeca-Regular.woff2 b/public/fonts/LexendDeca-Regular.woff2 new file mode 100644 index 0000000..ec78c7d Binary files /dev/null and b/public/fonts/LexendDeca-Regular.woff2 differ diff --git a/public/images/sample-visualization.gif b/public/images/sample-visualization.gif new file mode 100644 index 0000000..7e00099 Binary files /dev/null and b/public/images/sample-visualization.gif differ diff --git a/public/logo/automationhero.svg b/public/logo/automationhero.svg new file mode 100644 index 0000000..7366f13 --- /dev/null +++ b/public/logo/automationhero.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + diff --git a/public/logo/aviasales.dark.svg b/public/logo/aviasales.dark.svg new file mode 100644 index 0000000..8ede2e8 --- /dev/null +++ b/public/logo/aviasales.dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/logo/aviasales.png b/public/logo/aviasales.png new file mode 100644 index 0000000..611f7de Binary files /dev/null and b/public/logo/aviasales.png differ diff --git a/public/logo/avito.svg b/public/logo/avito.svg new file mode 100644 index 0000000..1913267 --- /dev/null +++ b/public/logo/avito.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + diff --git a/public/logo/codengage.png b/public/logo/codengage.png new file mode 100644 index 0000000..251df0c Binary files /dev/null and b/public/logo/codengage.png differ diff --git a/public/logo/docsvision.png b/public/logo/docsvision.png new file mode 100644 index 0000000..bc578a7 Binary files /dev/null and b/public/logo/docsvision.png differ diff --git a/public/logo/express24.svg b/public/logo/express24.svg new file mode 100644 index 0000000..8ad3c35 --- /dev/null +++ b/public/logo/express24.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/logo/foxford.svg b/public/logo/foxford.svg new file mode 100644 index 0000000..46453bc --- /dev/null +++ b/public/logo/foxford.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/logo/globalCtoForum.png b/public/logo/globalCtoForum.png new file mode 100644 index 0000000..c8b4d1b Binary files /dev/null and b/public/logo/globalCtoForum.png differ diff --git a/public/logo/healthSamurai.svg b/public/logo/healthSamurai.svg new file mode 100644 index 0000000..0362a17 --- /dev/null +++ b/public/logo/healthSamurai.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/logo/intouchHealth.png b/public/logo/intouchHealth.png new file mode 100644 index 0000000..de64981 Binary files /dev/null and b/public/logo/intouchHealth.png differ diff --git a/public/logo/joom.svg b/public/logo/joom.svg new file mode 100644 index 0000000..c771ebb --- /dev/null +++ b/public/logo/joom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/logo/junto.png b/public/logo/junto.png new file mode 100644 index 0000000..fb0bb05 Binary files /dev/null and b/public/logo/junto.png differ diff --git a/public/logo/lunatask.png b/public/logo/lunatask.png new file mode 100644 index 0000000..231ef20 Binary files /dev/null and b/public/logo/lunatask.png differ diff --git a/public/logo/mts.svg b/public/logo/mts.svg new file mode 100644 index 0000000..a219222 --- /dev/null +++ b/public/logo/mts.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/public/logo/okoo.png b/public/logo/okoo.png new file mode 100644 index 0000000..b68e6cf Binary files /dev/null and b/public/logo/okoo.png differ diff --git a/public/logo/radity.png b/public/logo/radity.png new file mode 100644 index 0000000..36618da Binary files /dev/null and b/public/logo/radity.png differ diff --git a/public/logo/raiffeisen.svg b/public/logo/raiffeisen.svg new file mode 100644 index 0000000..7e5ca73 --- /dev/null +++ b/public/logo/raiffeisen.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/public/logo/redmadrobot.png b/public/logo/redmadrobot.png new file mode 100644 index 0000000..df006f3 Binary files /dev/null and b/public/logo/redmadrobot.png differ diff --git a/public/logo/sber.png b/public/logo/sber.png new file mode 100644 index 0000000..999c602 Binary files /dev/null and b/public/logo/sber.png differ diff --git a/public/logo/semrush.png b/public/logo/semrush.png new file mode 100644 index 0000000..bce4dc6 Binary files /dev/null and b/public/logo/semrush.png differ diff --git a/public/logo/smartomato.svg b/public/logo/smartomato.svg new file mode 100644 index 0000000..c60a7bd --- /dev/null +++ b/public/logo/smartomato.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/logo/space307.svg b/public/logo/space307.svg new file mode 100644 index 0000000..3a2f819 --- /dev/null +++ b/public/logo/space307.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/logo/stellarX.svg b/public/logo/stellarX.svg new file mode 100644 index 0000000..c721bc4 --- /dev/null +++ b/public/logo/stellarX.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/logo/stmLabs.png b/public/logo/stmLabs.png new file mode 100644 index 0000000..2febd32 Binary files /dev/null and b/public/logo/stmLabs.png differ diff --git a/public/logo/tenpixls.png b/public/logo/tenpixls.png new file mode 100644 index 0000000..ed83a4b Binary files /dev/null and b/public/logo/tenpixls.png differ diff --git a/public/logo/travelpayouts.svg b/public/logo/travelpayouts.svg new file mode 100644 index 0000000..0a4d13d --- /dev/null +++ b/public/logo/travelpayouts.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/logo/unicef.svg b/public/logo/unicef.svg new file mode 100644 index 0000000..818513d --- /dev/null +++ b/public/logo/unicef.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/logo/uptarget.png b/public/logo/uptarget.png new file mode 100644 index 0000000..d607148 Binary files /dev/null and b/public/logo/uptarget.png differ diff --git a/public/logo/vk.svg b/public/logo/vk.svg new file mode 100644 index 0000000..53c444b --- /dev/null +++ b/public/logo/vk.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/make-scrollable-code-focusable.js b/public/make-scrollable-code-focusable.js new file mode 100644 index 0000000..6fbf1ee --- /dev/null +++ b/public/make-scrollable-code-focusable.js @@ -0,0 +1,3 @@ +Array.from(document.getElementsByTagName("pre")).forEach((element) => { + element.setAttribute("tabindex", "0"); +}); diff --git a/src/components/DropDownMenu.astro b/src/components/DropDownMenu.astro new file mode 100644 index 0000000..c9622f9 --- /dev/null +++ b/src/components/DropDownMenu.astro @@ -0,0 +1,208 @@ +--- +import { getPathParamsFromId, getTextLocalized, type LText } from "../languages"; + +interface Link { + link: string; + text: LText; +} + +interface Group { + text: LText; + links: Link[]; +} + +export interface Props { + links: (Group | Link)[]; + class?: string; +} + +const { links } = Astro.props; +const { lang } = getPathParamsFromId(Astro.url.pathname); +--- + +
+ +
+
+
+ { + links.map((linkOrGroup) => { + if ("links" in linkOrGroup) { + return ( +
+

{getTextLocalized(linkOrGroup, lang)}

+ {linkOrGroup.links.map((link) => { + return ( + + {getTextLocalized(link, lang)} + {link.link.startsWith("https://") || link.link.startsWith("http://") ? ( + + + + + + ) : null} + + ); + })} +
+ ); + } + return ( + + {getTextLocalized(linkOrGroup, lang)} + {linkOrGroup.link.startsWith("https://") || + linkOrGroup.link.startsWith("http://") ? ( + + + + + + ) : null} + + ); + }) + } +
+
+
+
+ + diff --git a/src/components/FeatureCard.astro b/src/components/FeatureCard.astro new file mode 100644 index 0000000..f319699 --- /dev/null +++ b/src/components/FeatureCard.astro @@ -0,0 +1,37 @@ +--- +interface Props { + title: string; + description: string; + link: string; +} +--- + + +
+
{Astro.props.title}
+
{Astro.props.description}
+
+ + diff --git a/src/components/Footer/Footer.astro b/src/components/Footer/Footer.astro new file mode 100644 index 0000000..fba6b4e --- /dev/null +++ b/src/components/Footer/Footer.astro @@ -0,0 +1,92 @@ +--- +import { createLink, getPathParamsFromId, getTextLocalized } from "../../languages"; +import { FOOTER_LINKS } from "../../navigation"; +import IconExternal from "../../icons/External.astro"; +import { LINKS } from "../../consts"; + +const { lang } = getPathParamsFromId(Astro.url.pathname); +--- + +
+
+ { + FOOTER_LINKS.map((group) => { + const text = getTextLocalized(group, lang); + + return ( +
+

{text}

+ {group.items.map((link) => { + const text = getTextLocalized(link, lang); + const external = link.link.startsWith("https://") || link.link.startsWith("http://"); + const href = external ? link.link : createLink(link, lang); + + if (external) { + return ( + + ); + } + + return ( + + ); + })} +
+ ); + }) + } +
+ + zerobias & Effector Core team + + © 2018-{new Date().getFullYear()} +
+
+
+ + diff --git a/src/components/HeadCommon.astro b/src/components/HeadCommon.astro new file mode 100644 index 0000000..9ee7000 --- /dev/null +++ b/src/components/HeadCommon.astro @@ -0,0 +1,47 @@ +--- +import "../styles/theme.css"; +import "../styles/index.css"; +import "../styles/language.css"; +--- + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/HeadSEO.astro b/src/components/HeadSEO.astro new file mode 100644 index 0000000..f2209fa --- /dev/null +++ b/src/components/HeadSEO.astro @@ -0,0 +1,48 @@ +--- +import type { CollectionEntry } from "astro:content"; +import { SITE, OPEN_GRAPH } from "../consts"; + +type DocsEntry = CollectionEntry<"docs">["data"]; +interface Props extends DocsEntry { + canonicalUrl: URL; +} + +const { ogLocale, image, title, description, canonicalUrl } = Astro.props; +const formattedContentTitle = `${title} 🚀 ${SITE.title}`; +const imageSrc = image?.src ?? OPEN_GRAPH.image.src; +const canonicalImageSrc = new URL(imageSrc, Astro.site); +const imageAlt = image?.alt ?? OPEN_GRAPH.image.alt; +--- + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Header/EffectorLogo.astro b/src/components/Header/EffectorLogo.astro new file mode 100644 index 0000000..0690f54 --- /dev/null +++ b/src/components/Header/EffectorLogo.astro @@ -0,0 +1,8 @@ +--- +type Props = { + size: number; +}; +const { size } = Astro.props; +--- + +effector comet diff --git a/src/components/Header/Header.astro b/src/components/Header/Header.astro new file mode 100644 index 0000000..6b30ebb --- /dev/null +++ b/src/components/Header/Header.astro @@ -0,0 +1,282 @@ +--- +import { + createChangeLangLinks, + createLink, + getPathParamsFromId, + getTextLocalized, + translations, +} from "../../languages"; +import { SITE } from "../../consts"; +import { DESKTOP_NAVIGATION, SOCIAL_LINKS, DOCS_VERSIONS, QUICK_MENU } from "../../navigation"; + +import MobileNavigation from "../MobileNavigation.astro"; +import DropDownMenu from "../DropDownMenu.astro"; + +import EffectorLogo from "./EffectorLogo.astro"; +import MobileMenuButton from "./MobileMenuButton.astro"; +import Search from "./Search.astro"; +import SkipToContent from "./SkipToContent.astro"; +import ThemeToggle from "./ThemeToggleButton"; + +import IconExternal from "../../icons/External.astro"; +import IconLanguage from "../../icons/Language.astro"; +import IconDots from "../../icons/Dots.astro"; + +import IconGithub from "../../icons/Github.astro"; +import IconTwitter from "../../icons/Twitter.astro"; +import IconDiscord from "../../icons/Discord.astro"; +import IconYoutube from "../../icons/Youtube.astro"; + +const socialIcons = { + github: IconGithub, + twitter: IconTwitter, + discord: IconDiscord, + youtube: IconYoutube, +}; + +type Props = { + currentPage: string; +}; + +const { lang, slug } = getPathParamsFromId(Astro.url.pathname); +const languageLinks = createChangeLangLinks({ slug }); +const homeLink = lang === "en" ? "/" : `/${lang}`; +--- + +
+ + + +
+ + + diff --git a/src/components/Header/MobileMenuButton.astro b/src/components/Header/MobileMenuButton.astro new file mode 100644 index 0000000..03fc7fc --- /dev/null +++ b/src/components/Header/MobileMenuButton.astro @@ -0,0 +1,73 @@ +--- +import IconMenu from "../../icons/Menu.astro"; + +// Animation copied from https://github.com/shuding/nextra/blob/a84ed9642e59a9f60c3cf1eaeca643b1f425bd9f/packages/nextra-theme-docs/css/hamburger.css +--- + + + + + + + + diff --git a/src/components/Header/Search.astro b/src/components/Header/Search.astro new file mode 100644 index 0000000..8f08614 --- /dev/null +++ b/src/components/Header/Search.astro @@ -0,0 +1,368 @@ +--- +import { getPathParamsFromId, getTextLocalized as t, translations } from "../../languages"; +import "@pagefind/default-ui/css/ui.css"; + +const { lang } = getPathParamsFromId(Astro.url.pathname); +const pagefindTranslations = Object.fromEntries( + Object.entries(translations.Pagefind).map(([key, value]) => [key, t(value, lang)]), +); +--- + + + + + +
+ { + import.meta.env.DEV && !import.meta.env.PUBLIC_SEARCH ? ( +
+

{t(translations.Search.devWarning, lang)}

+
+ ) : ( + +
+
+ + + + + + diff --git a/src/components/Header/SkipToContent.astro b/src/components/Header/SkipToContent.astro new file mode 100644 index 0000000..0f94c8a --- /dev/null +++ b/src/components/Header/SkipToContent.astro @@ -0,0 +1,29 @@ +--- +type Props = {}; +--- + + + + diff --git a/src/components/Header/ThemeToggleButton.css b/src/components/Header/ThemeToggleButton.css new file mode 100644 index 0000000..1e420d0 --- /dev/null +++ b/src/components/Header/ThemeToggleButton.css @@ -0,0 +1,30 @@ +.theme-toggle { + @apply flex items-center gap-1 rounded-full px-2 py-1; + background-color: var(--theme-code-inline-bg); +} + +.theme-toggle > label:focus-within { + outline: none; + color: var(--theme-text-accent); + @apply ring-1 ring-current ring-offset-2; +} + +.theme-toggle > label { + @apply relative flex cursor-pointer items-center justify-center rounded-full opacity-50; + color: var(--theme-code-inline-text); +} + +.theme-toggle .checked { + opacity: 1; + color: var(--theme-accent); +} + +input[name="theme-toggle"] { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + opacity: 0; + z-index: -1; +} diff --git a/src/components/Header/ThemeToggleButton.tsx b/src/components/Header/ThemeToggleButton.tsx new file mode 100644 index 0000000..dddb4aa --- /dev/null +++ b/src/components/Header/ThemeToggleButton.tsx @@ -0,0 +1,121 @@ +import type { FunctionalComponent } from "preact"; +import { useState, useEffect } from "preact/hooks"; +import "./ThemeToggleButton.css"; + +const themes = ["light", "auto", "dark"]; + +const icons = [ + + + + + , + + + + + + + + + , + + + + + + , +]; + +const ThemeToggle: FunctionalComponent = () => { + const [selectedTheme, setSelectedTheme] = useState(() => { + if (import.meta.env.SSR) { + return undefined; + } + if (typeof localStorage !== undefined && localStorage.getItem("theme")) { + return localStorage.getItem("theme"); + } + return "auto"; + }); + + useEffect(() => { + const root = document.documentElement; + function setTheme(color: string) { + if (color === "light") { + root.classList.remove("theme-dark"); + } else { + root.classList.add("theme-dark"); + } + } + + if (selectedTheme === "auto") { + const matcher = window.matchMedia("(prefers-color-scheme: dark)"); + setAutomaticTheme(); + matcher.addEventListener("change", setAutomaticTheme); + return () => matcher.removeEventListener("change", setAutomaticTheme); + function setAutomaticTheme() { + setTheme(matcher.matches ? "dark" : "light"); + } + } else { + setTheme(selectedTheme || "light"); + } + }, [selectedTheme]); + + return ( +
+ {themes.map((t, i) => { + const icon = icons[i]; + const checked = t === selectedTheme; + return ( + + ); + })} +
+ ); +}; + +export default ThemeToggle; diff --git a/src/components/Landing/Companies.astro b/src/components/Landing/Companies.astro new file mode 100644 index 0000000..c01432b --- /dev/null +++ b/src/components/Landing/Companies.astro @@ -0,0 +1,221 @@ +--- +import { getPathParamsFromId, getTextLocalized, translations } from "../../languages"; + +const companies = [ + { + url: "aviasales.png", + alt: "Aviasales", + yOffset: true, + }, + { + url: "healthSamurai.svg", + alt: "Health Samurai", + }, + { + url: "raiffeisen.svg", + alt: "Raiffeisen Bank Russia", + }, + { + url: "unicef.svg", + alt: "UNICEF (United Nations Children’s Fund)", + }, + { + url: "joom.svg", + alt: "Joom Group", + }, + { + url: "sber.png", + alt: "Sber", + }, + { + url: "avito.svg", + alt: "Avito", + scale: 1.4, + }, + { + url: "mts.svg", + alt: "MTS", + }, + { + url: "docsvision.png", + alt: "Docsvision", + scale: 1.2, + }, + { + url: "okoo.png", + alt: "Okoo", + yOffset: true, + }, + { + url: "space307.svg", + alt: "space307", + }, + { + url: "redmadrobot.png", + alt: "REDMADROBOT", + yOffset: true, + }, + { + url: "travelpayouts.svg", + alt: "Travelpayouts", + }, + { + url: "junto.png", + alt: "Junto", + }, + { + url: "automationhero.svg", + alt: "automation hero", + }, + { + url: "radity.png", + alt: "Radity", + }, + { + url: "globalCtoForum.png", + alt: "Global CTO Forum", + }, + { + url: "express24.svg", + alt: "Express24", + scale: 1.4, + }, + { + url: "stellarX.svg", + alt: "StellarX", + }, + { + url: "stmLabs.png", + alt: "STM Labs", + }, + { + url: "tenpixls.png", + alt: "TenPixls", + }, + { + url: "uptarget.png", + alt: "Uptarget", + }, + { + url: "smartomato.svg", + alt: "смартомато", + }, + { + url: "foxford.svg", + alt: "Фоксфорд", + }, + { + url: "codengage.png", + alt: "Codengage", + }, + { + url: "lunatask.png", + alt: "Lunatask", + }, + { + url: "semrush.png", + alt: "Semrush", + }, + { + url: "intouchHealth.png", + alt: "Intouch Health, a Teladoc company", + }, +]; + +const { lang } = getPathParamsFromId(Astro.url.pathname); +--- + +
+

+ {getTextLocalized(translations.Landing.Companies.title, lang)} +

+
    + { + companies.map(({ url, alt, yOffset, scale }) => { + return ( +
  • + {alt} +
  • + ); + }) + } +
+
+ + {getTextLocalized(translations.Landing.Companies.wantToAppear, lang)} + + + {getTextLocalized(translations.Landing.Companies.tellUs, lang)} + +
+
+ + diff --git a/src/components/Landing/Feature.astro b/src/components/Landing/Feature.astro new file mode 100644 index 0000000..ac71d2d --- /dev/null +++ b/src/components/Landing/Feature.astro @@ -0,0 +1,59 @@ +--- +interface Props { + kind?: "major" | "minor"; + emoji?: string; + title: string; +} + +const { kind, emoji, title } = Astro.props; +--- + +
+
{emoji}
+
{title}
+

+
+ + diff --git a/src/components/Landing/FeaturesGrid.astro b/src/components/Landing/FeaturesGrid.astro new file mode 100644 index 0000000..3bac2e5 --- /dev/null +++ b/src/components/Landing/FeaturesGrid.astro @@ -0,0 +1,14 @@ +
+
+ +
+
+ + diff --git a/src/components/Landing/Hero.astro b/src/components/Landing/Hero.astro new file mode 100644 index 0000000..23fe375 --- /dev/null +++ b/src/components/Landing/Hero.astro @@ -0,0 +1,110 @@ +--- +import IconChevron from "@icons/Chevron.astro"; + +interface Props { + subtitle: string; +} + +/** + * Pass any content without slot to be a description. + * Add `slot="links"` to appear in the links section. + * Many links allowed. Only class `.primary` is supported now + */ +--- + +
+
effector
+
{Astro.props.subtitle}
+

+ +

+ +
+ + diff --git a/src/components/Landing/Stats.astro b/src/components/Landing/Stats.astro new file mode 100644 index 0000000..2232116 --- /dev/null +++ b/src/components/Landing/Stats.astro @@ -0,0 +1,257 @@ +--- +import { Octokit } from "@octokit/rest"; +import { promisify } from "node:util"; +import * as childProcess from "node:child_process"; + +import { GITHUB_REPO } from "../../consts"; +import IconCopy from "../../icons/Copy.astro"; +import { getPathParamsFromId, getTextLocalized } from "../../languages"; +import { translations } from "src/languages"; + +const exec = promisify(childProcess.exec); + +const { lang } = getPathParamsFromId(Astro.url.pathname); + +const [owner, repo] = GITHUB_REPO.split("/"); +const repoInfo = { owner, repo }; + +const octokit = new Octokit({ auth: import.meta.env.GITHUB_TOKEN }); + +const formatDate = (date: Date) => { + const month = (date.getMonth() + 1).toString().padStart(2, "0"); + const day = date.getDate().toString().padStart(2, "0"); + return `${date.getUTCFullYear()}-${month}-${day}`; +}; + +async function getNpmStats() { + const to = new Date(); + const from = new Date("2018-01-17T03:17:09.763Z"); + const fromDateString = formatDate(from); + const toDateString = formatDate(to); + + const downloadCounts = await fetch( + `https://npm-stat.com/api/download-counts?package=effector&from=${fromDateString}&until=${toDateString}`, + ).then((r) => r.json()); + + const result = await exec("npm info effector --json"); + const npm = JSON.parse(result.stdout); + const latest = npm["dist-tags"].latest; + + const totalDownloads = + Object.values(downloadCounts.effector) + .slice(-30) + .reduce((total, count) => total + count) / 1000; + + return { + latest, + downloads: `${totalDownloads.toFixed(0)}K`, + }; +} + +async function getRepoStats() { + const { data: repo } = await octokit.repos.get(repoInfo); + + let contributorsCount = 0; + let page = 1; + while (true) { + const { data: pageContributors } = await octokit.repos.listContributors({ + ...repoInfo, + per_page: 100, + page, + }); + contributorsCount += pageContributors.length; + + if (pageContributors.length < 100) { + break; + } + page++; + } + + return { + stars: repo.stargazers_count.toLocaleString("en-US"), + contributors: contributorsCount, + license: repo.license?.spdx_id, + }; +} + +async function getGzipSize() { + try { + const response = await fetch("https://bundlephobia.com/api/size?package=effector@latest"); + + if (response.ok) { + const { gzip } = await response.json(); + + if (!gzip) { + throw new Error("No gzip size in the bundlephobia answer"); + } + + return { + size: `${(gzip / 1024).toFixed(1)} kB`, + }; + } else { + console.error(`Bundlephobia answer is not OK: ${response.statusText}`); + return { size: "11.9 kB" }; + } + } catch (error) { + console.error("Failed to fetch gzip size:", error); + } +} + +const statsMeta = { + size: { + label: getTextLocalized(translations.Landing.Stats.size, lang), + link: "https://bundlephobia.com/package/effector@latest", + }, + contributors: { + label: getTextLocalized(translations.Landing.Stats.contributors, lang), + link: "https://github.com/effector/effector/graphs/contributors", + }, + stars: { + label: getTextLocalized(translations.Landing.Stats.stars, lang), + link: "https://star-history.com/#effector/effector&Date", + }, + downloads: { + label: getTextLocalized(translations.Landing.Stats.downloads, lang), + link: "https://npmtrends.com/effector", + }, + latest: { + label: getTextLocalized(translations.Landing.Stats.latest, lang), + link: "https://github.com/effector/effector/releases", + }, + license: { + label: getTextLocalized(translations.Landing.Stats.license, lang), + link: "https://raw.githubusercontent.com/effector/effector/master/LICENSE", + }, +}; + +const gettingStartedText = getTextLocalized( + translations.Landing.Stats.StartByAddingEffectorAsDependency, + lang, +); + +const stats = await Promise.all([getRepoStats(), getNpmStats(), getGzipSize()]).then( + ([repo, npm, size]) => ({ ...repo, ...npm, ...size }), +); +--- + +
+

+ {getTextLocalized(translations.Landing.Stats.title, lang)} +

+
+
+ { + Object.entries(stats).map(([name, counter]) => { + return ( + + {counter} + {statsMeta[name as keyof typeof stats].label} + + ); + }) + } +
+
+ {gettingStartedText}: +
+ + npm install effector + + + + +
+
+
+
+ + + + diff --git a/src/components/LeftSidebar/LeftSidebar.astro b/src/components/LeftSidebar/LeftSidebar.astro new file mode 100644 index 0000000..7c1c97a --- /dev/null +++ b/src/components/LeftSidebar/LeftSidebar.astro @@ -0,0 +1,160 @@ +--- +import type { MarkdownHeading } from "astro"; +import { getPathParamsFromId } from "../../languages"; +import { + getLocalizedSidebar, + getSidebarForSlug, + markActiveNavigation, + MOBILE_NAVIGATION, +} from "../../navigation"; +import NestedLinks from "../NestedLinks.astro"; + +type Props = { + currentPage: string; + headings: MarkdownHeading[]; +}; + +const { currentPage } = Astro.props; +const currentPageMatch = currentPage.endsWith("/") ? currentPage.slice(0, -1) : currentPage; + +const { slug, lang } = getPathParamsFromId(Astro.url.pathname); +const baseSidebar = await getLocalizedSidebar(slug, lang); +// TODO: add collapsible groups +--- + + + + + + + + diff --git a/src/components/LiveDemo.jsx b/src/components/LiveDemo.jsx new file mode 100644 index 0000000..d82dd09 --- /dev/null +++ b/src/components/LiveDemo.jsx @@ -0,0 +1,23 @@ +import {Sandpack} from '@codesandbox/sandpack-react' + +const customSetup = { + dependencies: { + effector: 'latest' + }, +} + +export default function LiveDemo({demoFile, layout = 'console'}) { + const files = { + '/index.js': demoFile, + } + + return ( + + ) +} diff --git a/src/components/MobileNavigation.astro b/src/components/MobileNavigation.astro new file mode 100644 index 0000000..d3b8d43 --- /dev/null +++ b/src/components/MobileNavigation.astro @@ -0,0 +1,117 @@ +--- +import { markActiveNavigation, MOBILE_NAVIGATION, SOCIAL_LINKS } from "../navigation"; +import { createChangeLangLinks, getPathParamsFromId, getTextLocalized } from "../languages"; + +import NestedLinks from "./NestedLinks.astro"; + +import IconGithub from "../icons/Github.astro"; +import IconTwitter from "../icons/Twitter.astro"; +import IconDiscord from "../icons/Discord.astro"; +import IconYoutube from "../icons/Youtube.astro"; + +const socialIcons = { + github: IconGithub, + twitter: IconTwitter, + discord: IconDiscord, + youtube: IconYoutube, +}; + +const { slug, lang } = getPathParamsFromId(Astro.url.pathname); +const languageLinks = createChangeLangLinks({ slug }); +const activeNavigation = markActiveNavigation(`/${slug.replace(/\/$/, "")}`, MOBILE_NAVIGATION); +--- + + + + + + diff --git a/src/components/NestedLinks.astro b/src/components/NestedLinks.astro new file mode 100644 index 0000000..b4b3280 --- /dev/null +++ b/src/components/NestedLinks.astro @@ -0,0 +1,201 @@ +--- +import { createLink, getPathParamsFromId, getTextLocalized } from "../languages"; +import { isNavGroup, isNavLink, isNavLinkGroup, type LMobileNavItem } from "../navigation"; + +import IconChevronRight from "../icons/Chevron.astro"; +import IconExternal from "../icons/External.astro"; +import { nanoid } from "nanoid"; + +const { lang } = getPathParamsFromId(Astro.url.pathname); + +interface Props { + items: LMobileNavItem[]; + firstLevel?: boolean; + expandDefault?: boolean; +} + +const { firstLevel = false, expandDefault, items } = Astro.props; +const instanceId = nanoid(5); +--- + +
    + { + items.map((item) => { + const isExpandable = isNavLinkGroup(item) || isNavGroup(item); + const isLink = isNavLink(item) || isNavLinkGroup(item); + const text = getTextLocalized(item, lang); + const expanded = expandDefault ? expandDefault : item.active; + + if (isLink) { + const external = item.link.startsWith("https://") || item.link.startsWith("http://"); + return ( +
  • +
    + {isExpandable ? ( + + ) : null} + + {text} + {external ? : null} + +
    + {isExpandable ? ( +
    + +
    + ) : null} +
  • + ); + } + + if (isExpandable) { + return ( +
  • +
    + +
    +
    + +
    +
  • + ); + } + + return ( +
  • +
    + {text} +
    +
  • + ); + }) + } +
+ + + + diff --git a/src/components/NotTranslatedYet.astro b/src/components/NotTranslatedYet.astro new file mode 100644 index 0000000..bf7090c --- /dev/null +++ b/src/components/NotTranslatedYet.astro @@ -0,0 +1,29 @@ +--- +import { GITHUB_BRANCH, GITHUB_DOCS_CONTENT_DIR, GITHUB_REPO } from "../consts"; +import { getTextLocalized, translations } from "../languages"; + +interface Props { + lang: string; +} + +const { lang } = Astro.props; + +const DOCS_LINK = `https://github.com/${GITHUB_REPO}/tree/${GITHUB_BRANCH}/${GITHUB_DOCS_CONTENT_DIR}/${lang}`; +--- + +
+
+ + {getTextLocalized(translations.ThisPageIsNotTranslatedYet, lang)} + +
+
+

+ {getTextLocalized(translations.PleaseOpenPRWithTranslations, lang)} + {getTextLocalized(translations.usingThisLink, lang)}. +

+

+ {getTextLocalized(translations.ShowingContentForDefaultLanguage, lang)}. +

+
+
diff --git a/src/components/PageContent/Announcement.astro b/src/components/PageContent/Announcement.astro new file mode 100644 index 0000000..27ffefa --- /dev/null +++ b/src/components/PageContent/Announcement.astro @@ -0,0 +1,50 @@ +--- +import { createLink, getPathParamsFromId, getTextLocalized } from "src/languages"; +import { ANNOUNCEMENT } from "../../consts"; + +const { lang = "en" } = getPathParamsFromId(Astro.url.pathname); +function isRemote(link: string) { + return link.startsWith("http://") || link.startsWith("https://"); +} +--- + +{ + ANNOUNCEMENT ? ( +
+
{getTextLocalized({ text: ANNOUNCEMENT.title }, lang)}
+
{getTextLocalized(ANNOUNCEMENT, lang)}
+ {ANNOUNCEMENT.button ? ( + + ) : null} +
+ ) : null +} + + diff --git a/src/components/PageContent/Contributors.astro b/src/components/PageContent/Contributors.astro new file mode 100644 index 0000000..e1ebc84 --- /dev/null +++ b/src/components/PageContent/Contributors.astro @@ -0,0 +1,208 @@ +--- +import { Octokit } from "@octokit/rest"; +import { + GITHUB_COMMITS_URL, + GITHUB_DOCS_CONTENT_DIR, + GITHUB_DOCS_ROOT, + GITHUB_REPO, +} from "../../consts"; +import { getPathParamsFromId, getTextLocalized, translations } from "src/languages"; + +// fetch all commits for just this page's path +type Props = { + documentId: string; +}; +const { documentId } = Astro.props; +const { lang } = getPathParamsFromId(Astro.url.pathname); + +const fullFilePath = [GITHUB_DOCS_ROOT, GITHUB_DOCS_CONTENT_DIR, documentId].join(""); +const commitsURL = [GITHUB_COMMITS_URL, fullFilePath].join(""); + +const [owner, repo] = GITHUB_REPO.split("/"); + +const octokit = new Octokit({ auth: import.meta.env.GITHUB_TOKEN }); + +async function loadContributors() { + if (!import.meta.env.GITHUB_TOKEN) { + return []; + } + const filePath = [GITHUB_DOCS_ROOT, GITHUB_DOCS_CONTENT_DIR, documentId].join(""); + const { data: commits } = await octokit.repos.listCommits({ + owner, + repo, + path: filePath, + per_page: 100, + }); + + interface Contributor { + id: number; + login: string; + avatar: string; + commits: number; + } + + const contributors = new Map(); + + function add(user: { id: number; login: string; avatar: string }) { + const exist = contributors.get(user.id); + // Let's count total amount of commits + if (exist) { + exist.commits++; + } else { + contributors.set(user.id, { ...user, commits: 1 }); + } + } + commits.forEach((commit) => { + if (commit.author) { + add({ + id: commit.author.id, + login: commit.author.login, + avatar: commit.author.avatar_url, + }); + } + if (commit.committer) { + add({ + id: commit.committer.id, + login: commit.committer.login, + avatar: commit.committer.avatar_url, + }); + } + }); + return Array.from(contributors.values()) + .filter((user) => user.login !== "web-flow") + .sort((a, b) => b.commits - a.commits); +} + +const contributors = await loadContributors().catch((error) => { + if (!import.meta.env.GITHUB_TOKEN) { + console.info("Provide GITHUB_TOKEN env with Personal Access Token."); + } + console.info("Failed to load contributors. Skipping..."); + console.error(error); + return []; +}); + +const recentContributors = contributors.slice(0, 5).sort((a, b) => a.commits - b.commits); // only show avatars for the 3 most recent contributors +const additionalContributors = contributors.length - recentContributors.length; // list the rest of them as # of extra contributors +--- + +
+ {getTextLocalized(translations.Contributors, lang)} +
+ + +
+ { + recentContributors.length > 0 && ( +
    + {recentContributors.map((contributor) => ( +
  • + + {`Contributor + +
  • + ))} +
+ ) + } + { + additionalContributors > 0 && ( + + {`and ${additionalContributors} additional contributor${ + additionalContributors > 1 ? "s" : "" + }.`} + + ) + } + Commits +
+ + diff --git a/src/components/PageContent/PageContent.astro b/src/components/PageContent/PageContent.astro new file mode 100644 index 0000000..d8d5ea8 --- /dev/null +++ b/src/components/PageContent/PageContent.astro @@ -0,0 +1,182 @@ +--- +import type { MarkdownHeading } from "astro"; +import { getPathParamsFromId } from "../../languages"; +import MoreMenu from "../RightSidebar/MoreMenu.astro"; +import TableOfContents from "../RightSidebar/TableOfContents"; + +type Props = { + title: string; + headings: MarkdownHeading[]; + translations: { lang: string; slug: string }[]; + breadcrumbs?: { title: string; path: string }[]; + documentId: string; + pagefindWeight?: number; + indexing: boolean; +}; + +const { + title, + headings, + indexing, + documentId, + translations = [], + breadcrumbs = [], + pagefindWeight = 0, +} = Astro.props; +const { lang } = getPathParamsFromId(Astro.url.pathname); +--- + +
+
+ + + +
+ +
+ + diff --git a/src/components/RightSidebar/MoreMenu.astro b/src/components/RightSidebar/MoreMenu.astro new file mode 100644 index 0000000..ffb297f --- /dev/null +++ b/src/components/RightSidebar/MoreMenu.astro @@ -0,0 +1,61 @@ +--- +import clsx from "clsx"; +import { + COMMUNITY_INVITE_URL, + GITHUB_DOCS_CONTENT_DIR, + GITHUB_DOCS_ROOT, + GITHUB_EDIT_URL, +} from "../../consts"; +import { getPathParamsFromId, getTextLocalized, translations } from "../../languages"; + +import IconDiscordLined from "../../icons/DiscordLined.astro"; +import IconEditPencil from "../../icons/EditPencil.astro"; + +import styles from "./TableOfContents.module.css"; + +type Props = { + documentId: string; +}; + +const { documentId } = Astro.props; +const showMoreSection = Boolean(COMMUNITY_INVITE_URL); +const { lang } = getPathParamsFromId(Astro.url.pathname); +const editFilePath = [GITHUB_DOCS_ROOT, GITHUB_DOCS_CONTENT_DIR, documentId].join(""); +const editUrl = [GITHUB_EDIT_URL, editFilePath].join(""); +--- + +{ + showMoreSection && ( +

+ {getTextLocalized(translations.More, lang)} +

+ ) +} + + + diff --git a/src/components/RightSidebar/RightSidebar.astro b/src/components/RightSidebar/RightSidebar.astro new file mode 100644 index 0000000..d3fa832 --- /dev/null +++ b/src/components/RightSidebar/RightSidebar.astro @@ -0,0 +1,42 @@ +--- +import type { MarkdownHeading } from "astro"; +import TableOfContents from "./TableOfContents"; +import MoreMenu from "./MoreMenu.astro"; + +type Props = { + headings: MarkdownHeading[]; + documentId: string; + lang: string; +}; + +const { headings, documentId, lang } = Astro.props; +--- + + + + diff --git a/src/components/RightSidebar/TableOfContents.module.css b/src/components/RightSidebar/TableOfContents.module.css new file mode 100644 index 0000000..1fb5aef --- /dev/null +++ b/src/components/RightSidebar/TableOfContents.module.css @@ -0,0 +1,108 @@ +.title { + @apply m-0 mb-2 border-0 p-0 uppercase; + font-weight: 700; + font-size: 1rem; + text-transform: uppercase; + font-family: + Lexend Deca, + sans-serif; +} + +.contents { + @apply pr-3 text-sm; +} + +.link { + @apply relative m-0 max-w-full; +} + +.link a { + @apply scroll-py-6 py-1; +} + +:global(.content) ul.contents { + list-style-type: none; + @apply text-base; +} + +.link::before { + content: ""; + @apply absolute bottom-0 right-0 top-0 w-8; + background: linear-gradient(90deg, transparent, var(--theme-bg, white)); +} + +.link a { + @apply flex overflow-clip whitespace-nowrap; +} + +.link a:hover { + color: var(--theme-text-dark); + text-decoration: underline; +} + +.link.active a { + color: var(--theme-text-accent); +} + +.level1:not(:first-child) a { + @apply pt-6; +} + +.level1 a { + @apply font-bold; + color: var(--theme-text); +} + +.level2 { + @apply pl-3; +} + +.level2:not(:first-of-type) a { + @apply pt-3; +} + +.level2 a { + @apply font-medium; + color: var(--theme-text); +} + +.level3 { + @apply pl-7; +} + +.level3 a { + @apply font-normal; + color: var(--theme-text-light); +} + +.expandDetails { + @apply block min-w-max select-none flex-nowrap overflow-hidden text-ellipsis; +} + +.expandDetails summary { + display: inline-block; + -webkit-tap-highlight-color: transparent; + list-style: none; +} + +.expandDetails summary::-webkit-details-marker /* Safari */, +.expandDetails summary::marker /* Latest Chrome, Edge, Firefox */ { + display: none; +} + +.expandDetails summary span { + background-color: var(--theme-code-inline-bg); + color: var(--theme-code-inline-text); + @apply inline-block cursor-pointer rounded-xl px-5 py-2 text-base uppercase; +} + +.expandDetails[open] summary span { + @apply rounded-b-none; +} + +.expandContent { + background-color: var(--theme-code-inline-bg); + --theme-bg: var(--theme-code-inline-bg); + max-width: 52ch; + @apply rounded-2xl rounded-tl-none px-1 py-3; +} diff --git a/src/components/RightSidebar/TableOfContents.tsx b/src/components/RightSidebar/TableOfContents.tsx new file mode 100644 index 0000000..30370c7 --- /dev/null +++ b/src/components/RightSidebar/TableOfContents.tsx @@ -0,0 +1,119 @@ +import type { MarkdownHeading } from "astro"; +import type { FunctionalComponent } from "preact"; +import { useState, useEffect, useRef } from "preact/hooks"; +import { unescape } from "html-escaper"; +import clsx from "clsx"; + +import { getTextLocalized, translations } from "../../languages"; +import styles from "./TableOfContents.module.css"; + +type ItemOffsets = { + id: string; + topOffset: number; +}; + +const TableOfContents: FunctionalComponent<{ + headings: MarkdownHeading[]; + collapsed?: boolean; + lang: string; +}> = ({ headings = [], collapsed = false, lang }) => { + const toc = useRef(null); + const onThisPageID = "on-this-page-heading"; + const itemOffsets = useRef([]); + const [currentID, setCurrentID] = useState("overview"); + useEffect(() => { + const getItemOffsets = () => { + const titles = document.querySelectorAll("article :is(h1, h2, h3, h4)"); + itemOffsets.current = Array.from(titles).map((title) => ({ + id: title.id, + topOffset: title.getBoundingClientRect().top + window.scrollY, + })); + }; + + getItemOffsets(); + window.addEventListener("resize", getItemOffsets); + + return () => { + window.removeEventListener("resize", getItemOffsets); + }; + }, []); + + useEffect(() => { + if (!toc.current) return; + + const setCurrent: IntersectionObserverCallback = (entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + const { id } = entry.target; + if (id === onThisPageID) continue; + setCurrentID(entry.target.id); + break; + } + } + }; + + const observerOptions: IntersectionObserverInit = { + // Negative top margin accounts for `scroll-margin`. + // Negative bottom margin means heading needs to be towards top of viewport to trigger intersection. + rootMargin: "-100px 0% -66%", + threshold: 1, + }; + + const headingsObserver = new IntersectionObserver(setCurrent, observerOptions); + + // Observe all the headings in the main page content. + document.querySelectorAll("article :is(h1,h2,h3)").forEach((h) => headingsObserver.observe(h)); + + // Stop observing when the component is unmounted. + return () => headingsObserver.disconnect(); + }, [toc.current]); + + const onLinkClick: React.MouseEventHandler = (e) => { + setCurrentID(e.currentTarget.getAttribute("href")?.replace("#", "")!); + }; + + if (headings.length === 0) return null; + + const items = ( +
    + {headings + .filter(({ depth }) => depth > 0 && depth < 4) + .map((heading) => { + const linkClass = clsx(styles.link, styles[`level${heading.depth}`], { + [styles.active]: currentID === heading.slug, + }); + + return ( +
  • + + {unescape(heading.text)} + +
  • + ); + })} +
+ ); + + // When component setup as collapsed by default + if (collapsed) { + return ( +
+ + {getTextLocalized(translations.OnThisPage, lang)} + +
{items}
+
+ ); + } + + return ( + <> +

+ {getTextLocalized(translations.OnThisPage, lang)} +

+ {items} + + ); +}; + +export default TableOfContents; diff --git a/src/components/TranslationDisclaimer.astro b/src/components/TranslationDisclaimer.astro new file mode 100644 index 0000000..e34365a --- /dev/null +++ b/src/components/TranslationDisclaimer.astro @@ -0,0 +1,21 @@ +--- +import { getTextLocalized, translations } from "../languages"; + +type Props = { + lang: string; +}; + +const { lang } = Astro.props; +--- + +
+
+ + {getTextLocalized(translations.TranslationDisclaimer.title, lang)} + +
+
+

{getTextLocalized(translations.TranslationDisclaimer.firstLine, lang)}

+

{getTextLocalized(translations.TranslationDisclaimer.secondLine, lang)}

+
+
diff --git a/src/consts.ts b/src/consts.ts new file mode 100644 index 0000000..a98b197 --- /dev/null +++ b/src/consts.ts @@ -0,0 +1,97 @@ +import type { LText } from "./languages"; + +export const SITE = { + title: "effector", + description: "Business logic with ease.", + defaultLanguage: "en", +} as const; + +export const OPEN_GRAPH = { + image: { + src: "/banner.png", + alt: "effector logo is a comet moving away", + }, + twitter: "effectorjs", +}; + +export const KNOWN_LANGUAGES = { + English: "en", + Russian: "ru", + "O'zbekcha": "uz", +} as const; +export const KNOWN_LANGUAGE_CODES = Object.values(KNOWN_LANGUAGES); + +// TODO: update this urls +export const GITHUB_REPO = "effector/effector"; +export const GITHUB_BRANCH = "master"; + +// It is useful when documentation package located in a subdirectory +// There would be 'beta/'. Slash at the end is required +export const GITHUB_DOCS_ROOT = "documentation/"; + +// Used to convert pathname into file path in the repository +// It is where the docs are located related to the docs package.json file +export const GITHUB_DOCS_CONTENT_DIR = "src/content/docs/"; + +// @see https://developer.stackblitz.com/codeflow/integrating-web-publisher +// Format: https://pr.new/github.com/{repo-owner's-username}/{repo}/edit/{branch}/{file-path-in-the-repo} +// export const GITHUB_EDIT_URL = `https://pr.new/github.com/${GITHUB_REPO}/edit/${GITHUB_BRANCH}/`; +// If there will be function, we can add `initialPath` parameter + +export const GITHUB_EDIT_URL = `https://github.com/${GITHUB_REPO}/edit/${GITHUB_BRANCH}/`; +export const GITHUB_COMMITS_URL = `https://github.com/${GITHUB_REPO}/commits/${GITHUB_BRANCH}/`; + +export const LINKS = { + github: "https://github.com/effector/effector", + discord: "https://discord.gg/yHcMcuRWeC", + twitter: `https://twitter.com/${OPEN_GRAPH.twitter}`, + blog: "https://patreon.com/zero_bias", + repl: "https://share.effector.dev", + changelog: "https://changelog.effector.dev", + + community: "https://community.effector.dev/", + telegramRU: "https://t.me/effector_ru", + telegramEN: "https://t.me/effector_en", + devTo: "https://dev.to/effector", + reddit: "https://www.reddit.com/r/effectorjs/", + youtube: "https://youtube.com/@effectorjs", + linesOfCode: "https://t.me/lines_of_code_diagrams", + + zerobias: "https://github.com/zerobias", +}; + +export const COMMUNITY_INVITE_URL = LINKS.discord; + +// See "Algolia" section of the README for more information. +export const ALGOLIA = { + indexName: "effector-beta", + appId: "ARB8LV9Z4L", + apiKey: import.meta.env.ALGOLIA_API_KEY!, +}; + +export const ANNOUNCEMENT: Announcement | null = { + title: { en: "Pay attention", ru: "Обратите внимание", uz: "Etibor bering" }, + text: { + en: "This documentation is for the as yet unreleased version of effector Spacewatch 23.0.", + ru: "Эта версия документации предназначена для еще не вышедшего релиза effector Spacewatch 23.0.", + uz: "Bu hujjat hali chiqmagan effector Spacewatch 23.x versiyasiga tegishli", + }, + button: { + text: { + en: "View actual documentation", + ru: "Посмотреть актуальную документацию", + uz: "Aktual hujjatni ko'rish", + }, + link: "https://effector.dev", + }, +}; + +export interface Announcement { + title: LText; + text: LText; + button?: { + text: LText; + // Slug without language or the absolute URL. + link: string; + }; +} diff --git a/src/content/config.ts b/src/content/config.ts new file mode 100644 index 0000000..3ff7c1b --- /dev/null +++ b/src/content/config.ts @@ -0,0 +1,23 @@ +import { defineCollection, z } from "astro:content"; +import { SITE } from "../consts"; + +const docs = defineCollection({ + schema: z.object({ + title: z.string().default(SITE.title), + description: z.string().default(SITE.description), + lang: z + .union([z.literal("en"), z.literal("ru"), z.literal("uz")]) + .default(SITE.defaultLanguage), + dir: z.union([z.literal("ltr"), z.literal("rtl")]).default("ltr"), + image: z + .object({ + src: z.string(), + alt: z.string(), + }) + .optional(), + ogLocale: z.string().optional(), + redirectFrom: z.array(z.string()).optional(), + }), +}); + +export const collections = { docs }; diff --git a/src/content/docs/en/FAQ.md b/src/content/docs/en/FAQ.md new file mode 100644 index 0000000..e603ff7 --- /dev/null +++ b/src/content/docs/en/FAQ.md @@ -0,0 +1,12 @@ +--- +title: FAQ +description: Frequent questions and answers about effector +redirectFrom: + - /FAQ + - /docs/faq +--- + +## Why do we need to give names to events, effects etc. ? + +This will help in the future, in the development of the effector devtools, and now it is used in the [playground](https://share.effector.dev) on the left sidebar. +If you don't want to do it, you can use the [babel plugin](https://www.npmjs.com/package/@effector/babel-plugin). It will automatically generate the name for events and effects from the variable name. diff --git a/src/content/docs/en/api/effector-react/Gate.md b/src/content/docs/en/api/effector-react/Gate.md new file mode 100644 index 0000000..878e520 --- /dev/null +++ b/src/content/docs/en/api/effector-react/Gate.md @@ -0,0 +1,92 @@ +--- +title: Effector React Gate +redirectFrom: + - /api/effector-react/Gate + - /docs/api/effector-react/Gate +--- + +_Gate_ is a hook for conditional rendering, based on the current value (or values) in props. It can solve problems such as compiling all required data when a component mounts, or showing an alternative component if there is insufficient data in props. Gate is also useful for routing or animations, similar to ReactTransitionGroup. + +This enables the creation of a feedback loop by sending props back to a _Store_. + +Gate can be integrated via the [useGate](/en/api/effector-react/useGate) hook or as a component with props. Gate stores and events function as standard units within an application. + +Gate has two potential states: + +- **Opened**, indicating the component is mounted. +- **Closed**, indicating the component is unmounted. + +
+ +**Example of using Gate as a component:** + +```tsx + +``` + +# Properties (#properties) + +## `.state` Store (#properties-state) + +:::warning{title="Important"} +Do not modify the `state` value! It is a [derived store](/en/api/effector/Store#readonly) and should remain in a predictable state. +::: + +`Store`: [DerivedStore](/en/api/effector/Store#readonly) containing the current state of the gate. This state derives from the second argument of [useGate](/en/api/effector-react/useGate) and from props when rendering the gate as a component. + +### Example (#properties-state-example) + +```tsx +const Gate = createGate(); + +Gate.state.watch((state) => console.info("gate state updated", state)); + +function App() { + useGate(Gate, { props: "yep" }); + return
Example
; +} + +ReactDOM.render(, root); +// => gate state updated { props: "yep" } +``` + +## `.open` Event (#properties-open) + +:::info{title="Important"} +Do not manually invoke this event. It is an event that is triggered based on the gate's state. +::: + +[Event](/en/api/effector/Event): Event fired upon gate mounting. + +## `.close` Event (#properties-close) + +:::info{title=Important} +Do not manually invoke this event. It is an event that is triggered based on the gate's state. +::: + +[Event](/en/api/effector/Event): Event fired upon gate unmounting. + +## `.status` Store (#properties-status) + +:::warning{title="Important"} +Do not modify the `status` value! It is a [derived store](/en/api/effector/Store#readonly) and should remain in a predictable state. +::: + +[Store](/en/api/effector/Store): Boolean [DerivedStore](/en/api/effector/Store#readonly) indicating whether the gate is mounted. + +### Example (#properties-status-example) + +```tsx +const Gate = createGate(); + +Gate.status.watch((opened) => console.info("is Gate opened?", opened)); +// => is Gate opened? false + +function App() { + useGate(Gate); + return
Example
; +} + +ReactDOM.render(, root); +// => is Gate opened? true +``` diff --git a/src/content/docs/en/api/effector-react/Provider.md b/src/content/docs/en/api/effector-react/Provider.md new file mode 100644 index 0000000..288244d --- /dev/null +++ b/src/content/docs/en/api/effector-react/Provider.md @@ -0,0 +1,86 @@ +--- +title: Provider +description: Effector React +redirectFrom: + - /api/effector-react/Provider + - /docs/api/effector-react/Provider +--- + +React `Context.Provider` component, which takes any [Scope](/en/api/effector/Scope) in its `value` prop and makes all hooks in the subtree work with this scope: + +- `useUnit($store)` (and etc.) will read the state and subscribe to updates of the `$store` in this scope +- `useUnit(event)` (and etc.) will [bind](/en/api/effector/scopeBind) provided event or effect to this scope + +# Usage (#usage) + +## Example Usage (#usage-example) + +Here is an example of `` usage. + +```tsx +import { createEvent, createStore, fork } from "effector"; +import { useUnit, Provider } from "effector-react"; +import { render } from "react-dom"; + +const buttonClicked = createEvent(); +const $count = createStore(0); + +$count.on(buttonClicked, (counter) => counter + 1); + +const App = () => { + const [count, handleClick] = useUnit([$count, buttonClicked]); + + return ( + <> +

Count: {count}

+ + + ); +}; + +const myScope = fork({ + values: [[$count, 42]], +}); + +render( + + + , + document.getElementById("root"), +); +``` + +The `` component is placed in the subtree of ``, so its `useUnit([$count, inc])` call will return + +- State of the `$count` store in the `myScope` +- Version of `buttonClicked` event, which is bound to the `myScope`, which, if called, updates the `$count` state in the `myScope` + +## Multiple Providers Usage (#usage-multiple-providers) + +There can be as many `` instances in the tree, as you may need. + +```tsx +import { fork } from "effector"; +import { Provider } from "effector-react"; +import { App } from "@/app"; + +const scopeA = fork(); +const scopeB = fork(); + +const ParallelWidgets = () => ( + <> + + + + + + + +); +``` + +# Provider Properties (#properties) + +## `value` (#properties-value) + +`Scope`: any [Scope](/en/api/effector/Scope). All hooks in the subtree will work with this scope. diff --git a/src/content/docs/en/api/effector-react/connect.md b/src/content/docs/en/api/effector-react/connect.md new file mode 100644 index 0000000..1b90b0d --- /dev/null +++ b/src/content/docs/en/api/effector-react/connect.md @@ -0,0 +1,52 @@ +--- +title: connect +redirectFrom: + - /api/effector-react/connect + - /docs/api/effector-react/connect +--- + +```ts +import { connect } from "effector-react"; +``` + +:::warning{title="Deprecated"} +since [effector-react 23.0.0](https://changelog.effector.dev/#effector-react-23-0-0). + +Consider using [hooks api](/en/api/effector-react/index#hooks) in modern projects. +::: + +Wrapper for [useStore](/en/api/effector-react/useStore) to use during migration from redux and class-based projects. Will merge store value fields to component props. + +# Methods (#methods) + +## `connect($store)(Component)` (#methods-connect-store-component) + +### Formulae (#methods-connect-store-component-formulae) + +```ts +connect($store: Store)(Component): Component +``` + +### Arguments (#methods-connect-store-component-arguments) + +1. `$store` ([Store](/en/api/effector/Store)): store or object with stores + +### Returns (#methods-connect-store-component-returns) + +`(Component) => Component`: Function, which accepts react component and return component with store fields merged into props + +## `connect(Component)($store)` (#methods-connect-component-store) + +### Formulae (#methods-connect-component-store-formulae) + +```ts +connect(Component)($store: Store): Component +``` + +### Arguments (#methods-connect-component-store-arguments) + +1. `Component` (React.ComponentType): react component + +### Returns (#methods-connect-component-store-returns) + +`($store: Store) => Component`: Function, which accepts a store and returns component with store fields merged into props diff --git a/src/content/docs/en/api/effector-react/createComponent.md b/src/content/docs/en/api/effector-react/createComponent.md new file mode 100644 index 0000000..104b4f2 --- /dev/null +++ b/src/content/docs/en/api/effector-react/createComponent.md @@ -0,0 +1,57 @@ +--- +title: createComponent +description: Creates a store-based React component +redirectFrom: + - /api/effector-react/createComponent + - /docs/api/effector-react/createComponent +--- + +```ts +import { createComponent } from "effector-react"; +``` + +:::warning{title="Deprecated"} +since [effector-react 23.0.0](https://changelog.effector.dev/#effector-react-23-0-0). + +You can use [hooks api](/en/api/effector-react/index#hooks) in `createComponent` since [effector-react@20.3.0](https://changelog.effector.dev/#effector-20-3-0). +::: + +# Methods (#methods) + +## `createComponent($store, render)` (#createComponent-store-render) + +Creates a store-based React component. The `createComponent` method is useful for transferring logic and data of state to your View component. + +### Arguments (#createComponent-store-render-arguments) + +1. `$store` (_Store | Object_): `Store` or object of `Store` +2. `render` (_Function_): Render function which will be called with props and state + +### Returns (#createComponent-store-render-returns) + +(_`React.Component`_): Returns a React component. + +### Example (#createComponent-store-render-example) + +```jsx +import { createStore, createEvent } from "effector"; +import { createComponent } from "effector-react"; + +const increment = createEvent(); + +const $counter = createStore(0).on(increment, (n) => n + 1); + +const MyCounter = createComponent($counter, (props, state) => ( +
+ Counter: {state} + +
+)); + +const MyOwnComponent = () => { + // any stuff here + return ; +}; +``` + +[Try it](https://share.effector.dev/kJoLGB6g) diff --git a/src/content/docs/en/api/effector-react/createGate.md b/src/content/docs/en/api/effector-react/createGate.md new file mode 100644 index 0000000..9366d25 --- /dev/null +++ b/src/content/docs/en/api/effector-react/createGate.md @@ -0,0 +1,84 @@ +--- +title: createGate +description: Creates a gate to consume data from view +redirectFrom: + - /api/effector-react/createGate + - /docs/api/effector-react/createGate +--- + +```ts +import { createGate, type Gate } from "effector-react"; +``` + +# Methods (#methods) + +## `createGate(name?)` (#methods-createGate-name) + +Creates a [_Gate_](/en/api/effector-react/Gate) + +### Formulae (#methods-createGate-name-formulae) + +```ts +createGate(name?: string): Gate +``` + +### Arguments (#methods-createGate-name-arguments) + +1. `name?` (_string_): Optional name which will be used as the name of a created React component + +### Returns (#methods-createGate-name-returns) + +[`Gate`](/en/api/effector-react/Gate) + +### Examples (#methods-createGate-name-examples) + +#### Basic Usage (#methods-createGate-name-examples-basic) + +```jsx +import React from "react"; +import ReactDOM from "react-dom"; +import { createGate } from "effector-react"; + +const Gate = createGate("gate with props"); + +const App = () => ( +
+ +
+); + +Gate.state.watch((state) => { + console.log("current state", state); +}); +// => current state {} + +ReactDOM.render(, document.getElementById("root")); +// => current state {foo: 'bar'} + +ReactDOM.unmountComponentAtNode(document.getElementById("root")); +// => current state {} +``` + +[Try it](https://share.effector.dev/mMZSQclh) + +## `createGate(config?)` (#methods-createGate-config) + +Creates a [_Gate_](/en/api/effector-react/Gate), if `defaultState` is defined, [Gate.state](/en/api/effector-react/Gate#properties-state) will be created with passed value. + +### Formulae (#methods-createGate-config-formulae) + +```ts +createGate({ defaultState?: T, domain?: Domain, name?: string }): Gate +``` + +### Arguments (#methods-createGate-config-arguments) + +`config` (_Object_): Optional configuration object + +- `defaultState?`: Optional default state for [Gate.state](/en/api/effector-react/Gate#properties-state) +- `domain?` ([_Domain_](/en/api/effector/Domain)): Optional domain which will be used to create gate units ([Gate.open](/en/api/effector-react/Gate#properties-open) event, [Gate.state](/en/api/effector-react/Gate#properties-state) store, and so on) +- `name?` (_string_): Optional name which will be used as the name of a created React component + +### Returns (#methods-createGate-config-returns) + +[`Gate`](/en/api/effector-react/Gate) diff --git a/src/content/docs/en/api/effector-react/createStoreConsumer.md b/src/content/docs/en/api/effector-react/createStoreConsumer.md new file mode 100644 index 0000000..ba9e102 --- /dev/null +++ b/src/content/docs/en/api/effector-react/createStoreConsumer.md @@ -0,0 +1,46 @@ +--- +title: createStoreConsumer +description: Creates store-based component with render-props +redirectFrom: + - /api/effector-react/createStoreConsumer + - /docs/api/effector-react/createStoreConsumer +--- + +```ts +import { createStoreConsumer } from "effector-react"; +``` + +:::warning{title="Deprecated"} +since [effector-react 23.0.0](https://changelog.effector.dev/#effector-react-23-0-0). + +Consider using [hooks api](/en/api/effector-react/index#hooks) in modern projects. +::: + +# Methods (#methods) + +## `createStoreConsumer($store)` (#methods-createStoreConsumer-store) + +Creates a store-based React component which is watching for changes in the store. Based on _Render Props_ technique. + +### Arguments (#methods-createStoreConsumer-store-arguments) + +1. `$store` ([`Store`](/en/api/effector/Store)) + +### Returns (#methods-createStoreConsumer-store-returns) + +(`React.Component`) + +### Examples (#methods-createStoreConsumer-store-examples) + +```jsx +import { createStore } from "effector"; +import { createStoreConsumer } from "effector-react"; + +const $firstName = createStore("Alan"); + +const FirstName = createStoreConsumer($firstName); + +const App = () => {(name) =>

{name}

}
; +``` + +[Try it](https://share.effector.dev/HbH1tpzQ) diff --git a/src/content/docs/en/api/effector-react/index.md b/src/content/docs/en/api/effector-react/index.md new file mode 100644 index 0000000..21f4cc8 --- /dev/null +++ b/src/content/docs/en/api/effector-react/index.md @@ -0,0 +1,39 @@ +--- +title: effector-react +redirectFrom: + - /api/effector-react + - /docs/api/effector-react +--- + +Effector bindings for ReactJS. + +# Hooks (#hooks) + +- [useUnit(units)](/en/api/effector-react/useUnit) +- [useList(store, renderItem)](/en/api/effector-react/useList) +- [useStoreMap({ store, keys, fn })](/en/api/effector-react/useStoreMap) +- [useStore(store)](/en/api/effector-react/useStore) +- [useEvent(unit)](/en/api/effector-react/useEvent) + +# Components (#components) + +- [Provider](/en/api/effector-react/Provider) + +# Gate API (#gate-api) + +- [Gate](/en/api/effector-react/Gate) +- [createGate()](/en/api/effector-react/createGate) +- [useGate(GateComponent, props)](/en/api/effector-react/useGate) + +# Higher Order Components API (#higher-order-components-api) + +- [createComponent(store, render)](/en/api/effector-react/createComponent) +- [createStoreConsumer(store)](/en/api/effector-react/createStoreConsumer) renders props style +- [connect(store)(Component)](/en/api/effector-react/connect) "connect" style + +# Import map (#import-map) + +Package `effector-react` provides couple different entry points for different purposes: + +- [effector-react/compat](/en/api/effector-react/module/сompat) +- [effector-react/scope](/en/api/effector-react/module/scope) diff --git a/src/content/docs/en/api/effector-react/module/scope.md b/src/content/docs/en/api/effector-react/module/scope.md new file mode 100644 index 0000000..5805f84 --- /dev/null +++ b/src/content/docs/en/api/effector-react/module/scope.md @@ -0,0 +1,103 @@ +--- +title: effector-react/scope +description: Deprecated separate module of effector-react that enforces library to use Scope +--- + +```ts +import {} from "effector-react/scope"; +``` + +:::warning{title="Deprecated"} +Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0) the core team recommends using main module of `effector-react` instead. +::: + +Provides all exports from [effector-react](/en/api/effector-react), but enforces application to use [Scope](/en/api/effector/scope) for all components. + +## Usage (#usage) + +You can use this module in the same way as [effector-react](/en/api/effector-react), but it will require passing [Scope](/en/api/effector/scope) to [Provider](/en/api/effector-react/Provider) component. + +```jsx +// main.js +import { fork } from "effector"; +import { Provider } from "effector-react/scope"; + +import React from "react"; +import ReactDOM from "react-dom/client"; + +const scope = fork(); +const root = ReactDOM.createRoot(document.getElementById("root")); + +root.render( + + + , +); +``` + +## Migration (#migration) + +Since `effector-react/scope` is deprecated, it is better to migrate to [effector-react](/en/api/effector-react) by removing `scope` from import path. + +```diff ++ import { Provider } from "effector-react"; +- import { Provider } from "effector-react/scope"; +``` + +:::warning{title="Continues migration"} +`effector-react` and `effector-react/scope` do not share any code, so you have to migrate all your code to `effector-react` in the same time, because otherwise you will get runtime errors. These errors will be thrown because `effector-react` and `effector-react/scope` will use different instances `Provider` and do not have access to each other's `Provider`. +::: + +If you use [Babel](https://babeljs.io/), you need to remove parameter [`reactSsr`](/en/api/effector/babel-plugin#reactssr) from `babel-plugin` configuration. + +```diff +{ + "plugins": [ + [ + "effector/babel-plugin", + { +- "reactSsr": true + } + ] + ] +} +``` + +If you use [SWC](/en/api/effector/swc-plugin), you need to remove [`bindings.react.scopeReplace`](https://github.com/effector/swc-plugin#bindings) parameter from `@effector/swc-plugin` configuration. + +```diff +{ + "$schema": "https://json.schemastore.org/swcrc", + "jsc": { + "experimental": { + "plugins": [ + "@effector/swc-plugin", + { + "bindings": { + "react": { +- "scopeReplace": true + } + } + } + ] + } + } +} +``` + +## Scope Enforcement (#scope-enforcement) + +All modern hooks of `effector-react` are designed to work with [Scope](/en/api/effector/scope). If you want to imitate the behavior of `effector-react/scope` module, you can use the second parameter of hooks with an option `forceScope: true`. In this case, the hook will throw an error if the [Scope](/en/api/effector/scope) is not passed to [Provider](/en/api/effector-react/Provider). + +```diff +- import { useUnit } from 'effector-react/scope' ++ import { useUnit } from 'effector-react' + + +function Example() { +- const { text } = useUnit({ text: $text }) ++ const { text } = useUnit({ text: $text }, { forceScope: true }) + + return

{text}

+} +``` diff --git "a/src/content/docs/en/api/effector-react/module/\321\201ompat.md" "b/src/content/docs/en/api/effector-react/module/\321\201ompat.md" new file mode 100644 index 0000000..c0f39ff --- /dev/null +++ "b/src/content/docs/en/api/effector-react/module/\321\201ompat.md" @@ -0,0 +1,94 @@ +--- +title: effector-react/compat +description: Separate module of effector-react with compatibility up to IE11 and Chrome 47 (browser for Smart TV devices) +--- + +```ts +import {} from "effector-react/compat"; +``` + +The library provides a separate module with compatibility up to IE11 and Chrome 47 (browser for Smart TV devices). + +:::warning{title="Bundler, Not Transpiler"} +Since third-party libraries can import `effector-react` directly, you **should not** use transpilers like Babel to replace `effector-react` with `effector-react/compat` in your code because by default, Babel will not transform third-party code. + +**Use a bundler instead**, as it will replace `effector-react` with `effector-react/compat` in all modules, including those from third parties. + +::: + +Since `effector-react` uses `effector` under the hood, you need to use the compat-version of `effector` as well. Please, read [`effector/compat`](/en/api/effector/module/compat) for details. + +## Required Polyfills (#required-polyfills) + +You need to install polyfills for these objects: + +- `Promise` +- `Object.assign` +- `Array.prototype.flat` +- `Map` +- `Set` + +In most cases, a bundler can automatically add polyfills. + +### Vite (#required-polyfills-vite) + +
+Vite Configuration Example + +```js +import { defineConfig } from "vite"; +import legacy from "@vitejs/plugin-legacy"; + +export default defineConfig({ + plugins: [ + legacy({ + polyfills: ["es.promise", "es.object.assign", "es.array.flat", "es.map", "es.set"], + }), + ], +}); +``` + +
+ +# Usage (#usage) + +## Manual Usage (#usage-manual) + +You can use it instead of the `effector-react` package if you need to support old browsers. + +```diff +- import {useUnit} from 'effector-react' ++ import {useUnit} from 'effector-react/compat' +``` + +## Automatic Replacement (#usage-automatic-replacement) + +However, you can set up your bundler to automatically replace `effector` with `effector/compat` in your code. + +### Webpack (#usage-automatic-replacement-webpack) + +```js +module.exports = { + resolve: { + alias: { + effector: "effector/compat", + "effector-react": "effector-react/compat", + }, + }, +}; +``` + +### Vite (#usage-automatic-replacement-vite) + +```js +import { defineConfig } from "vite"; + +export default defineConfig({ + resolve: { + alias: { + effector: "effector/compat", + "effector-react": "effector-react/compat", + }, + }, +}); +``` diff --git a/src/content/docs/en/api/effector-react/useEvent.md b/src/content/docs/en/api/effector-react/useEvent.md new file mode 100644 index 0000000..007bc21 --- /dev/null +++ b/src/content/docs/en/api/effector-react/useEvent.md @@ -0,0 +1,128 @@ +--- +title: useEvent +redirectFrom: + - /api/effector-react/useEvent + - /docs/api/effector-react/useEvent +--- + +```ts +import { useEvent } from "effector-react"; +``` + +:::info{title="since"} +`useEvent` introduced in [effector-react 20.9.0](https://changelog.effector.dev/#effector-20-9-0) +::: + +:::warning{title="This is API is deprecated"} + +Prefer [`useUnit`](/api/effector-react/useUnit) hook instead. +::: + +Bind event to current [_scope_](/en/api/effector/Scope) to use in dom event handlers.
+Only `effector-react/scope` version works this way, `useEvent` of `effector-react` is a no-op and does not require `Provider` with scope. + +:::info{title="Note"} +Useful only if you have server-side rendering or writing tests for React-components. +::: + +# Methods (#methods) + +## `useEvent(unit)` (#methods-useEvent-unit) + +### Arguments (#methods-useEvent-unit-arguments) + +1. `unit` ([_Event_](/en/api/effector/Event) or [_Effect_](/en/api/effector/Effect)): Event or effect which will be bound to current `scope` + +### Returns (#methods-useEvent-unit-returns) + +(Function): Function to pass to event handlers. Will trigger a given unit in the current scope. + +### Examples (#methods-useEvent-unit-examples) + +#### Basic Usage (#methods-useEvent-unit-examples-basic) + +```jsx +import ReactDOM from "react-dom"; +import { createEvent, createStore, fork } from "effector"; +import { useStore, useEvent, Provider } from "effector-react"; + +const incremented = createEvent(); +const $count = createStore(0); + +$count.on(incremented, (counter) => counter + 1); + +const App = () => { + const count = useStore($count); + const handleIncrement = useEvent(incremented); + + return ( + <> +

Count: {count}

+ + + ); +}; + +const scope = fork(); + +ReactDOM.render( + + + , + document.getElementById("root"), +); +``` + +[Try it](https://share.effector.dev/GyiJvLdo) + +## `useEvent(shape)` (#methods-useEvent-shape) + +### Arguments (#methods-useEvent-shape-arguments) + +1. `shape` Object or array of ([_Event_](/en/api/effector/Event) or [_Effect_](/en/api/effector/Effect)): Events or effects as values which will be bound to the current `scope` + +### Returns (#methods-useEvent-shape-returns) + +(Object or Array): List of functions with the same names or keys as an argument to pass to event handlers. Will trigger a given unit in the current scope. + +### Examples (#methods-useEvent-shape-examples) + +#### Object Usage (#methods-useEvent-shape-examples-object) + +```jsx +import ReactDOM from "react-dom"; +import { createStore, createEvent, fork } from "effector"; +import { useStore, useEvent, Provider } from "effector-react"; + +const incremented = createEvent(); +const decremented = createEvent(); + +const $count = createStore(0); + +$count.on(incremented, (counter) => counter + 1); +$count.on(decremented, (counter) => counter - 1); + +const App = () => { + const counter = useStore($count); + const handler = useEvent({ incremented, decremented }); + // or + const [handleIncrement, handleDecrement] = useEvent([incremented, decremented]); + + return ( + <> +

Count: {counter}

+ + + + ); +}; + +const scope = fork(); + +ReactDOM.render( + + + , + document.getElementById("root"), +); +``` diff --git a/src/content/docs/en/api/effector-react/useGate.md b/src/content/docs/en/api/effector-react/useGate.md new file mode 100644 index 0000000..e7ae202 --- /dev/null +++ b/src/content/docs/en/api/effector-react/useGate.md @@ -0,0 +1,57 @@ +--- +title: useGate +description: Hook for passing data to Gate +redirectFrom: + - /api/effector-react/useGate + - /docs/api/effector-react/useGate +--- + +```ts +import { useGate } from "effector-react"; +``` + +# Methods (#methods) + +## `useGate(Gate, props?)` (#methods-useGate-Gate-props) + +Hook for passing data to [_Gate_](/en/api/effector-react/Gate). + +### Formulae (#methods-useGate-Gate-props-formulae) + +```ts +const CustomGate: Gate; + +useGate(CustomGate, props?: T): void; +``` + +### Arguments (#methods-useGate-Gate-props-arguments) + +1. `Gate` ([`Gate`](/en/api/effector-react/Gate)) +2. `props` (`T`) + +### Returns (#methods-useGate-Gate-props-returns) + +(`void`) + +### Examples (#methods-useGate-Gate-props-examples) + +#### Basic (#methods-useGate-Gate-props-examples-basic) + +```js +import { createGate, useGate } from "effector-react"; +import { Route } from "react-router"; + +const PageGate = createGate("page"); + +PageGate.state.watch(({ match }) => { + console.log(match); +}); + +const Home = (props) => { + useGate(PageGate, props); + + return
Home
; +}; + +const App = () => ; +``` diff --git a/src/content/docs/en/api/effector-react/useList.md b/src/content/docs/en/api/effector-react/useList.md new file mode 100644 index 0000000..e68fe6f --- /dev/null +++ b/src/content/docs/en/api/effector-react/useList.md @@ -0,0 +1,225 @@ +--- +title: useList +redirectFrom: + - /api/effector-react/useList + - /docs/api/effector-react/useList +--- + +```ts +import { useList } from "effector-react"; +``` + +:::info{title="since"} +`useList` introduced in [effector-react 20.1.1](https://changelog.effector.dev/#effector-react-20-1-1) +::: + +Hook function for efficient rendering of list store. +Every item will be memoized and updated only when their data change. + +# Methods (#methods) + +## `useList($store, fn)` (#methods-useList-store-fn) + +Using `index` as `key` for each element in the list. + +### Formulae (#methods-useList-store-fn-formulae) + +```ts +useList( + $store: Store, + fn: (value: T, index: number) => React.ReactNode, +): React.ReactNode; +``` + +### Arguments (#methods-useList-store-fn-arguments) + +1. `$store` ([`Store`](/en/api/effector/Store)): Store with an array of items +2. `fn` (_Function_): Render function which will be called for every item in list + +### Returns (#methods-useList-store-fn-returns) + +(`React.Node`) + +### Examples (#methods-useList-store-fn-examples) + +#### Basic (#methods-useList-store-fn-examples-basic) + +```jsx +import { createStore } from "effector"; +import { useList } from "effector-react"; + +const $users = createStore([ + { id: 1, name: "Yung" }, + { id: 2, name: "Lean" }, + { id: 3, name: "Kyoto" }, + { id: 4, name: "Sesh" }, +]); + +const App = () => { + // we don't need keys here any more + const list = useList($users, ({ name }, index) => ( +
  • + [{index}] {name} +
  • + )); + + return
      {list}
    ; +}; +``` + +[Try it](https://share.effector.dev/dV9dmuz3) + +#### With store updates (#methods-useList-store-fn-examples-with-store-updates) + +```jsx +import { createStore, createEvent } from "effector"; +import { useList, useUnit } from "effector-react"; + +const todoSubmitted = createEvent(); +const todoToggled = createEvent(); + +const $todoList = createStore([ + { text: "write useList example", done: true }, + { text: "update readme", done: false }, +]); + +$todoList.on(todoToggled, (list, id) => + list.map((todo, index) => { + if (index === id) + return { + ...todo, + done: !todo.done, + }; + return todo; + }), +); + +$todoList.on(todoSubmitted, (list, text) => [...list, { text, done: false }]); + +todoSubmitted.watch((e) => { + e.preventDefault(); +}); + +const TodoList = () => { + const [onTodoToggle] = useUnit([todoToggled]); + return useList($todoList, ({ text, done }, index) => { + const todo = done ? ( + + {text} + + ) : ( + {text} + ); + + return
  • onTodoToggle(index)}>{todo}
  • ; + }); +}; + +const App = () => { + const [onTodoSubmit] = useUnit([todoSubmitted]); + + function handleSubmit(e) { + e.preventDefault(); + onTodoSubmit(e.currentTarget.elements.content.value); + } + + return ( +
    +

    todo list

    +
    + + + +
    +
      + +
    +
    + ); +}; +``` + +[Try it](https://share.effector.dev/dUay9F3U) + +## `useList($store, config)` (#methods-useList-store-config) + +Used when you need to pass dependencies to react (to update items when some of its dependencies are changed). + +By default, `useList` rerenders only when some of its items were changed. +However, sometimes we need to update items when some external value (e.g. props field or state of another store) changes. +In such case, we need to tell React about our dependencies and pass keys explicitly. + +### Formulae (#methods-useList-store-config-formulae) + +```ts +useList( + $store: Store, + config: { + keys: any[], + getKey?: (value: T) => React.Key, + fn: (value: T, index: number) => React.ReactNode, + placeholder?: React.ReactNode, + } +): React.ReactNode; +``` + +### Arguments (#methods-useList-store-config-arguments) + +1. `$store` ([`Store`](/en/api/effector/Store)): Store with an array of items +2. `config` (`Object`) + - `keys` (`Array`): Array of dependencies, which will be passed to react by `useList` + - `fn` (`(value: T) => React.ReactNode`): Render function which will be called for every item in list + - `getKey` (`(value) => React.Key`): Optional function to compute key for every item of list + - `placeholder` (`React.ReactNode`): Optional react node to render instead of an empty list + +:::info{title="since"} +`getKey` option introduced in [effector-react@21.3.0](https://changelog.effector.dev/#effector-react-21-3-0) +::: + +:::info{title="since"} +`placeholder` option introduced in [effector-react@22.1.0](https://changelog.effector.dev/#effector-react-22-1-0) +::: + +### Returns (#methods-useList-store-config-returns) + +(`React.Node`) + +### Examples (#methods-useList-store-config-examples) + +#### Basic (#methods-useList-store-config-examples-config) + +```jsx +import ReactDOM from "react-dom"; +import { createEvent, createStore, restore } from "effector"; +import { useUnit, useList } from "effector-react"; + +const renameUser = createEvent(); + +const $user = createStore("alice"); +const $friends = createStore(["bob"]); + +$user.on(renameUser, (_, name) => name); + +const App = () => { + const user = useUnit($user); + + return useList($friends, { + keys: [user], + fn: (friend) => ( +
    + {friend} is a friend of {user} +
    + ), + }); +}; + +ReactDOM.render(, document.getElementById("root")); +// =>
    bob is a friend of alice
    + +setTimeout(() => { + renameUser("carol"); + // =>
    bob is a friend of carol
    +}, 500); +``` + +[Try it](https://share.effector.dev/ijRS5TYh) diff --git a/src/content/docs/en/api/effector-react/useProvidedScope.md b/src/content/docs/en/api/effector-react/useProvidedScope.md new file mode 100644 index 0000000..34397af --- /dev/null +++ b/src/content/docs/en/api/effector-react/useProvidedScope.md @@ -0,0 +1,47 @@ +--- +title: useProvidedScope +description: Effector React +redirectFrom: + - /api/effector-react/useProvidedScope + - /docs/api/effector-react/useProvidedScope +--- + +```ts +import { useProvidedScope } from "effector-react"; +``` + +Low-level React Hook, which returns current [Scope](/api/effector/Scope) from [Provider](/api/effector-react/Provider). + +:::warning{title="This is a Low-Level API"} +The `useProvidedScope` hook is a low-level API for library developers and **is not intended to be used in production code** directly. + +For production `effector-react` usage, see the [`useUnit`](/api/effector-react/useUnit) hook. +::: + +# Methods (#methods) + +## `useProvidedScope()` (#methods-useProvidedScope) + +### Formulae (#methods-useProvidedScope-formulae) + +```ts +useProvidedScope(): Scope | null +``` + +### Returns (#methods-useProvidedScope-returns) + +([`Scope | null`](/api/effector/Scope)) — if no Scope provided, returns `null`. + +### Examples (#methods-useProvidedScope-examples) + +This hook can be used in library internals to handle various edge-cases, where `createWatch` and `scopeBind` APIs are also needed. + +For production code usage, see the [`useUnit`](/api/effector-react/useUnit) hook instead. + +```tsx +const useCustomLibraryInternals = () => { + const scope = useProvidedScope(); + + // ... +}; +``` diff --git a/src/content/docs/en/api/effector-react/useStore.md b/src/content/docs/en/api/effector-react/useStore.md new file mode 100644 index 0000000..29d4907 --- /dev/null +++ b/src/content/docs/en/api/effector-react/useStore.md @@ -0,0 +1,64 @@ +--- +title: useStore +redirectFrom: + - /api/effector-react/useStore + - /docs/api/effector-react/useStore +--- + +```ts +import { useStore } from "effector-react"; +``` + +React hook, which subscribes to a [store](/en/api/effector/Store) and returns its current value, so when the store is updated, the component will update automatically. + +:::warning{title="This is API is deprecated"} + +Prefer [`useUnit`](/api/effector-react/useUnit) hook instead. +::: + +# Methods (#methods) + +## `useStore($store): State` (#methods-useStore-store) + +### Formulae (#methods-useStore-store-formulae) + +```ts +useStore($store: Store): State +``` + +### Arguments (#methods-useStore-store-arguments) + +1. `$store`: [Store](/en/api/effector/Store) + +### Returns (#methods-useStore-store-returns) + +(_`State`_): The value from the store + +### Examples (#methods-useStore-store-examples) + +```jsx +import { createStore } from "effector"; +import { useStore, useEvent } from "effector-react"; + +const $counter = createStore(0); + +const { incrementClicked, decrementClicked } = createApi($counter, { + incrementClicked: (state) => state + 1, + decrementClicked: (state) => state - 1, +}); + +const App = () => { + const counter = useStore($counter); + const [onIncrement, onDecrement] = useEvent([incrementClicked, decrementClicked]); + + return ( +
    + {counter} + + +
    + ); +}; +``` + +[Try it](https://share.effector.dev/DHzp3z4r) diff --git a/src/content/docs/en/api/effector-react/useStoreMap.md b/src/content/docs/en/api/effector-react/useStoreMap.md new file mode 100644 index 0000000..f924147 --- /dev/null +++ b/src/content/docs/en/api/effector-react/useStoreMap.md @@ -0,0 +1,140 @@ +--- +title: useStoreMap +redirectFrom: + - /api/effector-react/useStoreMap + - /docs/api/effector-react/useStoreMap +--- + +```ts +import { useStoreMap } from "effector-react"; +``` + +:::info{title="since"} +`useStoreMap` introduced in [effector-react 19.1.2](https://changelog.effector.dev/#effector-react-19-1-2) +::: + +React hook, which subscribes to a [store](/en/api/effector/Store) and transforms its value with a given function. The component will update only when the selector function result will change. + +You can read the motivation in the [issue](https://github.com/effector/effector/issues/118). + +# Methods (#methods) + +## `useStoreMap($store, fn)` (#methods-useStoreMap-store-fn) + +:::info{title="since"} +Short version of `useStoreMap` introduced in [effector-react@21.3.0](https://changelog.effector.dev/#effector-react-21-3-0) +::: + +Common use case: subscribe to changes in selected part of store only + +### Formulae (#methods-useStoreMap-store-fn-formulae) + +```ts +useStoreMap( + $store: Store, + fn: (state: State) => Result, +): Result +``` + +### Arguments (#methods-useStoreMap-store-fn-arguments) + +1. `$store`: Source [`Store`](/en/api/effector/Store) +2. `fn` (`(state: State) => Result`): Selector function to receive part of source store + +### Returns (#methods-useStoreMap-store-fn-returns) + +(`Result`): Value from the `fn` function call. + +### Examples (#methods-useStoreMap-store-fn-examples) + +TBD + +## `useStoreMap(config)` (#methods-useStoreMap-config) + +Overload used when you need to pass dependencies to react (to update items when some of its dependencies are changed) + +### Formulae (#methods-useStoreMap-config-formulae) + +```ts +useStoreMap({ + store: Store, + keys: any[], + fn: (state: State, keys: any[]) => Result, + updateFilter?: (newResult: Result, oldResult: Result) => boolean, + defaultValue?: Result, +}): Result; +``` + +### Arguments (#methods-useStoreMap-config-arguments) + +1. `config` (_Object_): Configuration object + - `store`: Source [`Store`](/en/api/effector/Store) + - `keys` (_Array_): This argument will be passed to React.useMemo to avoid unnecessary updates + - `fn` (`(state: State, keys: any[]) => Result`): Selector function to receive part of source store + - `updateFilter` (`(newResult, oldResult) => boolean`): _Optional_ function used to compare old and new updates to prevent unnecessary rerenders. Uses [createStore updateFilter](/en/api/effector/createStore) option under the hood + - `defaultValue`: Optional default value, used whenever `fn` returns undefined + +:::info{title="since"} +`updateFilter` option introduced in [effector-react@21.3.0](https://changelog.effector.dev/#effector-react-21-3-0) +::: + +:::info{title="since"} +`defaultValue` option introduced in [effector-react@22.1.0](https://changelog.effector.dev/#effector-react-22-1-0) +::: + +### Returns (#methods-useStoreMap-config-returns) + +(`Result`): Value from the `fn` function call, or the `defaultValue`. + +### Examples (#methods-useStoreMap-config-examples) + +#### Basic (#methods-useStoreMap-config-examples-basic) + +This hook is useful for working with lists, especially with large ones + +```jsx +import { createStore } from "effector"; +import { useList, useStoreMap } from "effector-react"; + +const usersRaw = [ + { + id: 1, + name: "Yung", + }, + { + id: 2, + name: "Lean", + }, + { + id: 3, + name: "Kyoto", + }, + { + id: 4, + name: "Sesh", + }, +]; + +const $users = createStore(usersRaw); +const $ids = createStore(usersRaw.map(({ id }) => id)); + +const User = ({ id }) => { + const user = useStoreMap({ + store: $users, + keys: [id], + fn: (users, [userId]) => users.find(({ id }) => id === userId) ?? null, + }); + + return ( +
    + [{user.id}] {user.name} +
    + ); +}; + +const UserList = () => { + return useList($ids, (id) => ); +}; +``` + +[Try it](https://share.effector.dev/cAZWHCit) diff --git a/src/content/docs/en/api/effector-react/useUnit.md b/src/content/docs/en/api/effector-react/useUnit.md new file mode 100644 index 0000000..2445dc1 --- /dev/null +++ b/src/content/docs/en/api/effector-react/useUnit.md @@ -0,0 +1,192 @@ +--- +title: useUnit +description: Effector React +redirectFrom: + - /api/effector-react/useUnit + - /docs/api/effector-react/useUnit +--- + +```ts +import { useUnit } from "effector-react"; +``` + +:::info{title="since"} +`useUnit` introduced in [effector-react 22.1.0](https://changelog.effector.dev/#effector-react-22-1-0) +::: + +React hook, which takes any unit or shape of units. + +In the case of [stores](/en/api/effector/Store), it subscribes the component to the provided [store](/en/api/effector/Store) and returns its current value, so when the store updates, the component will update automatically. + +In the case of [events](/en/api/effector/Event)/[effects](/en/api/effector/Effect) – it binds to the current [_scope_](/en/api/effector/Scope) to use in DOM event handlers. +Only the `effector-react/scope` version works this way; the `useUnit` of `effector-react` is no-op for events and does not require a `Provider` with scope. + +# Methods (#methods) + +## `useUnit(unit)` (#methods-useUnit-unit) + +Creates function that calls original unit but bounded to [`Scope`](/en/api/effector/Scope) if provided. + +### Formulae (#methods-useUnit-unit-formulae) + +```ts +useUnit(event: EventCallable): (payload: T) => T; +useUnit(effect: Effect): (payload: Params) => Promise; +``` + +### Arguments (#methods-useUnit-unit-arguments) + +1. `unit` ([`EventCallable`](/en/api/effector/Event#eventCallable) or [`Effect`](/en/api/effector/Effect)): Event or effect which will be bound to the current `scope`. + +### Returns (#methods-useUnit-unit-returns) + +(Function): Function to pass to event handlers. Will trigger the given unit in the current scope. + +### Examples (#methods-useUnit-unit-examples) + +#### Basic (#methods-useUnit-unit-examples-basic) + +```jsx +import { createEvent, createStore, fork } from "effector"; +import { useUnit, Provider } from "effector-react"; +import { render } from "react-dom"; + +const incrementClicked = createEvent(); +const $count = createStore(0); + +$count.on(incrementClicked, (count) => count + 1); + +const App = () => { + const [count, onIncrement] = useUnit([$count, incrementClicked]); + + return ( + <> +

    Count: {count}

    + + + ); +}; + +const scope = fork(); + +render( + () => ( + + + + ), + document.getElementById("root"), +); +``` + +## `useUnit($store)` (#methods-useUnit-store) + +Reads value from the `$store` and rerenders component when `$store` updates in [`Scope`](/en/api/effector/Scope) if provided. + +### Formulae (#methods-useUnit-store-formulae) + +```ts +useUnit($store: Store): T; +``` + +### Arguments (#methods-useUnit-store-arguments) + +1. `$store`: effector ([_Store_](/en/api/effector/Store)) + +### Returns (#methods-useUnit-store-returns) + +Current value of the store. + +### Examples (#methods-useUnit-store-examples) + +#### Basic (#methods-useUnit-store-examples-basic) + +```js +import { createStore, createApi } from "effector"; +import { useUnit } from "effector-react"; + +const $counter = createStore(0); + +const { incrementClicked, decrementClicked } = createApi($counter, { + incrementClicked: (count) => count + 1, + decrementClicked: (count) => count - 1, +}); + +const App = () => { + const counter = useUnit($counter); + const [onIncrement, onDecrement] = useUnit([incrementClicked, decrementClicked]); + + return ( +
    + {counter} + + +
    + ); +}; +``` + +## `useUnit(shape)` (#methods-useUnit-shape) + +### Formulae (#methods-useUnit-shape-formulae) + +```ts +useUnit({ a: Store, b: Event, ... }): { a: A, b: (payload: B) => B; ... } + +useUnit([Store, Event, ... ]): [A, (payload: B) => B, ... ] +``` + +### Arguments (#methods-useUnit-shape-arguments) + +1. `shape`: Object or array of ([`EventCallable`](/en/api/effector/Event#eventCallable), [`Effect`](/en/api/effector/Effect), or [`Store`](/en/api/effector/Store)) + +### Returns (#methods-useUnit-shape-returns) + +(`Object` or `Array`): + +- If passed `EventCallable` or `Effect`: Functions with the same names or keys as the argument to pass to event handlers. Will trigger the given unit in the current scope.
    + _Note: events or effects will be bound to `Scope` **only** if component wrapped into [`Provider`](/en/api/effector-react/Provider)._ +- If passed `Store`: The current value of the store. + +### Examples (#methods-useUnit-shape-examples) + +#### Basic (#methods-useUnit-shape-examples-basic) + +```jsx +import { createStore, createEvent, fork } from "effector"; +import { useUnit, Provider } from "effector-react"; + +const incremented = createEvent(); +const decremented = createEvent(); + +const $count = createStore(0); + +$count.on(incremented, (count) => count + 1); +$count.on(decremented, (count) => count - 1); + +const App = () => { + const count = useUnit($count); + const on = useUnit({ incremented, decremented }); + // or + const [a, b] = useUnit([incremented, decremented]); + + return ( + <> +

    Count: {count}

    + + + + ); +}; + +const scope = fork(); + +render( + () => ( + + + + ), + document.getElementById("root"), +); +``` diff --git a/src/content/docs/en/api/effector-solid/Gate.md b/src/content/docs/en/api/effector-solid/Gate.md new file mode 100644 index 0000000..50f4f02 --- /dev/null +++ b/src/content/docs/en/api/effector-solid/Gate.md @@ -0,0 +1,55 @@ +--- +title: Effector Solid Gate +description: Effector Solid +redirectFrom: + - /api/effector-solid/Gate + - /docs/api/effector-solid/gate +--- + +_Gate_ is a hook for conditional rendering, based on current value (or values) in props. +An example of a problem that Gate can solve – you can put together all required data when component was mounted, or show another component if there is not enough data in props. +Gate also looks good for Routing or animation. + +This allows you to send props back to _Store_ to create a feedback loop. + +Gate can be used via the [useGate](/en/api/effector-solid/useGate) hook or as a component with props (``). +Gate stores and events can be used in the application as regular units. + +Gate can have two states: + +- **Open**, which means mounted +- **Closed**, which means unmounted + +# Properties (#properties) + +## `.state` Store (#properties-state) + +:::warning{title="Important"} +Do not modify the `state` value! It is a [derived store](/en/api/effector/Store#readonly) and should be kept in a predictable state. +::: + +`Store`: [Derived Store](/en/api/effector/Store#readonly) with the current state of the given gate. The state comes from the second argument of [useGate](/en/api/effector-solid/useGate) and from props when rendering the gate as a component. + +## `.open` Event (#properties-open) + +:::info{title="Important"} +Do not manually call this event. It is an event that depends on a Gate's state. +::: + +[Event](/en/api/effector/Event): Event which will be called during the gate's mounting. + +## `.close` Event (#properties-close) + +:::info{title="Important"} +Do not manually call this event. It is an event that depends on a Gate's state. +::: + +[Event](/en/api/effector/Event): Event which will be called during the gate's unmounting. + +## `.status` Store (#properties-status) + +:::warning{title="Important"} +Do not modify the `status` value! It is a [derived store](/en/api/effector/Store#readonly) and should be in a predictable state. +::: + +`Store`: Boolean [Derived Store](/en/api/effector/Store#readonly), which shows if the given gate is mounted. diff --git a/src/content/docs/en/api/effector-solid/createGate.md b/src/content/docs/en/api/effector-solid/createGate.md new file mode 100644 index 0000000..676faa9 --- /dev/null +++ b/src/content/docs/en/api/effector-solid/createGate.md @@ -0,0 +1,77 @@ +--- +title: createGate +description: Creates a gate to consume data from view +redirectFrom: + - /api/effector-solid/createGate + - /docs/api/effector-solid/createGate +--- + +# Methods (#methods) + +## `createGate(config)` (#methods-createGate-config) + +### Formulae (#methods-createGate-config-formulae) + +```ts +createGate(config): Gate +``` + +### Arguments (#methods-createGate-config-arguments) + +`config` (_Object_): Optional configuration object + +- `defaultState?`: Optional default state for [Gate.state](/en/api/effector-solid/Gate#state) +- `domain?` ([_Domain_]/apieffector/Domain)): Optional domain which will be used to create gate units ([Gate.open](/en/api/effector-solid/Gate#open) event, [Gate.state](/en/api/effector-solid/Gate#state) store and so on) +- `name?` (_string_): Optional name which will be used as name of a created Solid component + +### Returns (#methods-createGate-config-returns) + +[_Gate_](/en/api/effector-solid/Gate) + +### Examples (#methods-createGate-config-examples) + +TBD + +## `createGate(name?)` (#methods-createGate-name) + +### Formulae (#methods-createGate-name-formulae) + +```ts +createGate(name): Gate +``` + +### Arguments (#methods-createGate-name-arguments) + +1. `name?` (_string_): Optional name which will be used as name of a created Solid component + +### Returns (#methods-createGate-name-returns) + +[_Gate_](/en/api/effector-solid/Gate) + +### Examples (#methods-createGate-name-examples) + +#### Basic usage (#methods-createGate-name-examples-basic) + +```js +import { createGate } from "effector-solid"; +import { render } from "solid-js/web"; + +const Gate = createGate("gate with props"); + +const App = () => ( +
    + +
    +); + +Gate.state.watch((state) => { + console.log("current state", state); +}); +// => current state {} + +const unmount = render(() => , document.getElementById("root")); +// => current state {foo: 'bar'} + +unmount(); +// => current state {} +``` diff --git a/src/content/docs/en/api/effector-solid/index.md b/src/content/docs/en/api/effector-solid/index.md new file mode 100644 index 0000000..e8038af --- /dev/null +++ b/src/content/docs/en/api/effector-solid/index.md @@ -0,0 +1,25 @@ +--- +title: effector-solid +redirectFrom: + - /api/effector-solid + - /docs/api/effector-solid +--- + +Effector bindings for SolidJS. + +# Reactive Helpers (#reactive-helpers) + +- [useUnit(unit)](/en/api/effector-solid/useUnit) +- [useStoreMap({ store, keys, fn })](/en/api/effector-solid/useStoreMap) + +# Gate API (#gate-api) + +- [Gate](/en/api/effector-solid/Gate) +- [createGate()](/en/api/effector-solid/createGate) +- [useGate(GateComponent, props)](/en/api/effector-solid/useGate) + +# Import Map (#import-map) + +Package `effector-solid` provides couple different entry points for different purposes: + +- [effector-solid/scope](/en/api/effector-solid/module/scope) diff --git a/src/content/docs/en/api/effector-solid/module/scope.md b/src/content/docs/en/api/effector-solid/module/scope.md new file mode 100644 index 0000000..714290b --- /dev/null +++ b/src/content/docs/en/api/effector-solid/module/scope.md @@ -0,0 +1,64 @@ +--- +title: effector-solid/scope +description: Deprecated separate module of effector-solid that enforces library to use Scope +--- + +```ts +import {} from "effector-solid/scope"; +``` + +:::warning{title="Deprecated"} +Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0) the core team recommends using the main module of `effector-solid` instead. +::: + +Provides all exports from [effector-solid](/en/api/effector-solid), but enforces the application to use [Scope](/en/api/effector/scope) for all components. + +## Usage (#usage) + +You can use this module in the same way as [effector-solid](/en/api/effector-solid), but it will require passing [Scope](/en/api/effector/scope) to [Provider](/en/api/effector-solid/Provider) component. + +```jsx +// main.js +import { fork } from "effector"; +import { Provider } from "effector-solid/scope"; +import { render } from "solid-js/web"; + +const scope = fork(); + +render( + + + , + document.getElementById("root"), +); +``` + +## Migration (#migration) + +Since `effector-solid/scope` is deprecated, it is recommended to migrate to [effector-solid](/en/api/effector-solid) by removing `scope` from the import path. + +```diff ++ import { Provider } from "effector-solid"; +- import { Provider } from "effector-solid/scope"; +``` + +:::warning{title="Continued migration"} +`effector-solid` and `effector-solid/scope` do not share any code, so you have to migrate all your code to `effector-solid` at the same time, because otherwise, you will get runtime errors. These errors will occur because `effector-solid` and `effector-solid/scope` will use different instances of `Provider` and do not have access to each other's `Provider`. +::: + +## Scope enforcement (#scope-enforcement) + +All modern hooks of `effector-solid` are designed to work with [Scope](/en/api/effector/scope). If you want to imitate the behavior of the `effector-solid/scope` module, you can pass a second parameter to hooks with an option `forceScope: true`. In this case, the hook will throw an error if the [Scope](/en/api/effector/scope) is not passed to [Provider](/en/api/effector-solid/Provider). + +```diff +- import { useUnit } from 'effector-solid/scope' ++ import { useUnit } from 'effector-solid' + + +function MyComponent() { +- const { test } = useUnit({ text: $text }) ++ const { test } = useUnit({ text: $text }, { forceScope: true }) + + return

    {text}

    +} +``` diff --git a/src/content/docs/en/api/effector-solid/useGate.md b/src/content/docs/en/api/effector-solid/useGate.md new file mode 100644 index 0000000..b44bd94 --- /dev/null +++ b/src/content/docs/en/api/effector-solid/useGate.md @@ -0,0 +1,58 @@ +--- +title: useGate +description: Function for passing data to Gate +redirectFrom: + - /api/effector-solid/useGate + - /docs/api/effector-solid/useGate +--- + +```ts +import { useGate } from "effector-solid"; +``` + +Function for passing data to [_Gate_](/en/api/effector-solid/Gate). + +# Methods (#methods) + +## `useGate(Gate, props)` (#methods-useGate-Gate-props) + +### Formulae (#methods-useGate-Gate-props-formulae) + +```ts +useGate(Gate: Gate, props: Props): void; +``` + +### Arguments (#methods-useGate-Gate-props-arguments) + +1. `Gate` ([`Gate`](/en/api/effector-solid/Gate)) +2. `props` (_Props_) + +### Returns (#methods-useGate-Gate-props-returns) + +(`void`) + +### Examples (#methods-useGate-Gate-props-examples) + +#### Basic Usage (#methods-useGate-Gate-props-examples-basic-usage) + +```jsx +import { createGate, useGate } from "effector-solid"; +import { Route, Routes } from "solid-app-router"; + +const PageGate = createGate("page"); + +const Home = (props) => { + useGate(PageGate, props); + return
    Home
    ; +}; + +PageGate.state.watch(({ match }) => { + console.log(match); +}); + +const App = () => ( + + } /> + +); +``` diff --git a/src/content/docs/en/api/effector-solid/useStoreMap.md b/src/content/docs/en/api/effector-solid/useStoreMap.md new file mode 100644 index 0000000..fe852e3 --- /dev/null +++ b/src/content/docs/en/api/effector-solid/useStoreMap.md @@ -0,0 +1,117 @@ +--- +title: useStoreMap +redirectFrom: + - /api/effector-solid/useStoreMap + - /docs/api/effector-solid/useStoreMap +--- + +```ts +import { useStoreMap } from "effector-solid"; +``` + +# Methods (#methods) + +## `useStoreMap($store, fn)` (#methods-useStoreMap-store-fn) + +Function, which subscribes to a [store](/en/api/effector/Store) and transforms its value with a given function. Signal will update only when the selector function result will change. + +Common use case: subscribe to changes in selected part of store only. + +### Formulae (#methods-useStoreMap-store-fn-formulae) + +```ts +useStoreMap( + $store: Store, + fn: (state: State) => Result, +): Accessor; +``` + +### Arguments (#methods-useStoreMap-store-fn-arguments) + +1. `$store`: Source [`Store`](/en/api/effector/Store) +2. `fn` (`(state: T) => Result`): Selector function to receive part of source store + +### Returns (#methods-useStoreMap-store-fn-returns) + +(`Result`) + +### Examples (#methods-useStoreMap-store-fn-examples) + +TBD + +## `useStoreMap(config)` (#methods-useStoreMap-config) + +### Formulae (#methods-useStoreMap-config-formulae) + +```ts +useStoreMap({ + store: Store, + keys: any[], + fn: (state: State, keys: any[]) => Result, + updateFilter? (newResult, oldResult) => boolean, +}): Result; +``` + +### Arguments (#methods-useStoreMap-config-arguments) + +1. `params` (_Object_): Configuration object + - `store`: Source [store](/en/api/effector/Store) + - `keys` (_Array_): Will be passed to `fn` selector + - `fn` (_(state, keys) => result_): Selector function to receive part of the source store + - `updateFilter` (_(newResult, oldResult) => boolean_): _Optional_ function used to compare old and new updates to prevent unnecessary rerenders. Uses [createStore updateFilter](/en/api/effector/createStore#methods-createStore-defaultState-config-formulae) option under the hood + +### Returns (#methods-useStoreMap-config-returns) + +(`Accessor`) + +### Examples (#methods-useStoreMap-config-examples) + +This hook is very useful for working with lists, especially large ones. + +```jsx +import { createStore } from "effector"; +import { useUnit, useStoreMap } from "effector-solid"; +import { For } from "solid-js/web"; + +const usersRaw = [ + { + id: 1, + name: "Yung", + }, + { + id: 2, + name: "Lean", + }, + { + id: 3, + name: "Kyoto", + }, + { + id: 4, + name: "Sesh", + }, +]; + +const $users = createStore(usersRaw); +const $ids = createStore(usersRaw.map(({ id }) => id)); + +const User = ({ id }) => { + const user = useStoreMap({ + store: $users, + keys: [id], + fn: (users, [userId]) => users.find(({ id }) => id === userId) ?? null, + }); + + return ( +
    + [{user()?.id}] {user()?.name} +
    + ); +}; + +const UserList = () => { + const ids = useUnit($ids); + + return {(id) => }; +}; +``` diff --git a/src/content/docs/en/api/effector-solid/useUnit.md b/src/content/docs/en/api/effector-solid/useUnit.md new file mode 100644 index 0000000..6701ae9 --- /dev/null +++ b/src/content/docs/en/api/effector-solid/useUnit.md @@ -0,0 +1,175 @@ +--- +title: useUnit +description: Effector Solid +redirectFrom: + - /api/effector-solid/useUnit + - /docs/api/effector-solid/useUnit +--- + +```ts +import { useUnit } from "effector-solid"; +``` + +Binds effector stores to the Solid reactivity system or, in the case of events/effects – binds to current [_scope_](/en/api/effector/Scope) to use in dom event handlers. +Only `effector-solid/scope` version works this way, `useUnit` of `effector-solid` is no-op for events and does not require `Provider` with scope. + +# Methods (#methods) + +## `useUnit(unit)` (#methods-useUnit-unit) + +### Arguments (#methods-useUnit-unit-arguments) + +```ts +useUnit(event: EventCallable): (payload: T) => T; +useUnit(effect: Effect): (payload: Params) => Promise; +``` + +### Arguments (#methods-useUnit-unit-arguments) + +1. `unit` ([`EventCallable`](/en/api/effector/Event#eventCallable) or [`Effect`](/en/api/effector/Effect)): Event or effect which will be bound to current `scope`. + +### Returns (#methods-useUnit-unit-returns) + +(`Function`): Function to pass to event handlers. Will trigger the given unit in the current scope. + +### Example (#methods-useUnit-unit-example) + +A basic Solid component using `useUnit` with events and stores. + +```jsx +import { render } from "solid-js/web"; +import { createEvent, createStore, fork } from "effector"; +import { useUnit, Provider } from "effector-solid"; + +const incremented = createEvent(); +const $count = createStore(0); + +$count.on(incremented, (count) => count + 1); + +const App = () => { + const [count, handleIncrement] = useUnit([$count, incremented]); + + return ( + <> +

    Count: {count()}

    + + + ); +}; + +const scope = fork(); + +render( + () => ( + + + + ), + document.getElementById("root"), +); +``` + +## `useUnit(store)` (#methods-useUnit-store) + +### Formulae (#methods-useUnit-store-formulae) + +```ts +useUnit($store: Store): Accessor; +``` + +### Arguments (#methods-useUnit-store-arguments) + +1. `$store` effector ([_Store_](/en/api/effector/Store)). + +### Returns (#methods-useUnit-store-returns) + +(`Accessor`) which will subscribe to store state. + +### Example (#methods-useUnit-store-example) + +```jsx +import { createStore, createApi } from "effector"; +import { useUnit } from "effector-solid"; + +const $counter = createStore(0); + +const { incremented, decremented } = createApi($counter, { + incremented: (count) => count + 1, + decremented: (count) => count - 1, +}); + +const App = () => { + const counter = useUnit($counter); + const [handleIncrement, handleDecrement] = useUnit([incremented, decremented]); + + return ( +
    + {counter()} + + +
    + ); +}; +``` + +## `useUnit(shape)` (#methods-useUnit-shape) + +### Formulae (#methods-useUnit-shape-formulae) + +```ts +useUnit({ a: Store
    , b: Event, ... }): { a: Accessor, b: (payload: B) => B; ... } + +useUnit([Store, Event, ... ]): [Accessor, (payload: B) => B, ... ] +``` + +### Arguments (#methods-useUnit-shape-arguments) + +1. `shape` Object or array of ([`EventCallable`](/en/api/effector/Event#eventCallable), [`Effect`](/en/api/effector/Effect), or [`Store`](/en/api/effector/Store)): Events, or effects, or stores as accessors which will be bound to the current `scope`. + +### Returns (#methods-useUnit-shape-returns) + +(`Object` or `Array`): + +- If `EventCallable` or `Effect`: functions with the same names or keys as argument to pass to event handlers. Will trigger given unit in current scope _Note: events or effects will be bound **only** if `useUnit` is imported from `effector-solid/scope`_. +- If `Store`: accessor signals which will subscribe to the store state. + +### Examples (#methods-useUnit-shape-examples) + +```jsx +import { render } from "solid-js/web"; +import { createStore, createEvent, fork } from "effector"; +import { useUnit, Provider } from "effector-solid/scope"; + +const incremented = createEvent(); +const decremented = createEvent(); + +const $count = createStore(0) + .on(incremented, (count) => count + 1) + .on(decremented, (count) => count - 1); + +const App = () => { + const count = useUnit($count); + const on = useUnit({ incremented, decremented }); + // or + const [a, b] = useUnit([incremented, decremented]); + + return ( + <> +

    Count: {count()}

    + + + + ); +}; + +const scope = fork(); + +render( + () => ( + + + + ), + document.getElementById("root"), +); +``` diff --git a/src/content/docs/en/api/effector-vue/ComponentOptions.md b/src/content/docs/en/api/effector-vue/ComponentOptions.md new file mode 100644 index 0000000..bf09980 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/ComponentOptions.md @@ -0,0 +1,63 @@ +--- +title: ComponentOptions +redirectFrom: + - /api/effector-vue/ComponentOptions + - /docs/api/effector-vue/component-options +--- + +# ComponentOptions (#ComponentOptions) + +## `effector` (#ComponentOptions-effector) + +### Returns (#ComponentOptions-effector-returns) + +(_`Function | Object | Store`_): `Store` or object of `Store`'s, or function which will be called with the Component instance as `this`. + +### Examples (#ComponentOptions-effector-examples) + +#### Basic Usage (#ComponentOptions-effector-examples-basic) + +```js +import Vue from "vue"; +import { createStore, combine } from "effector"; + +const counter = createStore(0); + +new Vue({ + data() { + return { + foo: "bar", + }; + }, + effector() { + // would create `state` in template + return combine( + this.$store(() => this.foo), + counter, + (foo, counter) => `${foo} + ${counter}`, + ); + }, +}); +``` + +#### Using Object Syntax (#ComponentOptions-effector-examples-object) + +```js +import { counter } from "./stores"; + +new Vue({ + effector: { + counter, // would create `counter` in template + }, +}); +``` + +#### Using Store Directly (#ComponentOptions-effector-examples-direct) + +```js +import { counter } from "./stores"; + +new Vue({ + effector: counter, // would create `state` in template +}); +``` diff --git a/src/content/docs/en/api/effector-vue/EffectorScopePlugin.md b/src/content/docs/en/api/effector-vue/EffectorScopePlugin.md new file mode 100644 index 0000000..77a85a4 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/EffectorScopePlugin.md @@ -0,0 +1,34 @@ +--- +title: EffectorScopePlugin +--- + +The Plugin provides a general scope which needs for read and update effector's stores, call effector's events. Required for SSR. + +# Plugins (#plugins) + +## `EffectorScopePlugin({ scope, scopeName })` (#plugins-EffectorScopePlugin-scope-scopeName) + +### Arguments (#plugins-EffectorScopePlugin-scope-scopeName-arguments) + +1. `scope` [Scope](/en/api/effector/Scope) +2. `scopeName?` custom scopeName (default: `root`) + +### Examples (#plugins-EffectorScopePlugin-scope-scopeName-examples) + +#### Basic Usage (#plugins-EffectorScopePlugin-scope-scopeName-examples-basic) + +```js +import { createSSRApp } from "vue"; +import { EffectorScopePlugin } from "effector-vue"; +import { fork } from "effector"; + +const app = createSSRApp(AppComponent); +const scope = fork(); + +app.use( + EffectorScopePlugin({ + scope, + scopeName: "app-scope-name", + }), +); +``` diff --git a/src/content/docs/en/api/effector-vue/Gate.md b/src/content/docs/en/api/effector-vue/Gate.md new file mode 100644 index 0000000..be29294 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/Gate.md @@ -0,0 +1,52 @@ +--- +title: Effector Vue Gate +description: Effector Vue +redirectFrom: + - /api/effector-vue/Gate + - /docs/api/effector-vue/gate +--- + +_Gate_ is a hook for conditional rendering, based on current value (or values) in props. An example of a problem that Gate can solve – you can put together all required data, when component was mounted. + +This allows you to send props back to _Store_ to create feedback loop. + +Gate can be used via [useGate](/en/api/effector-vue/useGate) hook. Gate stores and events can be used in the application as regular units + +Gate can have two states: + +- **Open**, which means mounted +- **Closed**, which means unmounted + +# Gate Properties (#properties) + +## `.state` (#properties-state) + +:::warning{title="Important"} +Do not modify `state` value! It is [derived store](/en/api/effector/Store#readonly) and should be in predictable state. +::: + +`Store`: [DerivedStore](/en/api/effector/Store#readonly) with current state of the given gate. The state comes from the second argument of [useGate](/en/api/effector-vue/useGate) and from props when rendering gate as a component. + +## `.open` (#properties-open) + +:::info{title="Important"} +Do not manually call this event. It is an event that depends on a Gate state. +::: + +[Event](/en/api/effector/Event): Event which will be called during gate mounting + +## `.close` (#properties-close) + +:::info{title="Important"} +Do not manually call this event. It is an event that depends on a Gate state. +::: + +[Event](/en/api/effector/Event): Event which will be called during a gate unmounting. + +## `.status` (#properties-status) + +:::warning{title="Important"} +Do not modify `status` value! It is [derived store](/en/api/effector/Store#readonly) and should be in predictable state. +::: + +`Store`: Boolean [DerivedStore](/en/api/effector/Store#readonly), which show if given gate is mounted. diff --git a/src/content/docs/en/api/effector-vue/VueEffector.md b/src/content/docs/en/api/effector-vue/VueEffector.md new file mode 100644 index 0000000..ef34fe2 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/VueEffector.md @@ -0,0 +1,36 @@ +--- +title: VueEffector +description: effector-vue plugin for vue 2 +redirectFrom: + - /api/effector-vue/VueEffector + - /docs/api/effector-vue/vue-effector +--- + +```ts +import { VueEffector } from "effector-vue"; +``` + +`effector-vue` plugin for vue 2 + +# Methods (#methods) + +## `VueEffector(Vue, options?)` (#methods-VueEffector-Vue-options) + +### Arguments (#methods-VueEffector-Vue-options-arguments) + +1. `Vue` (_class Vue_): Vue class +2. `options` (_Object_): Plugin options + - TBD + +### Returns (#methods-VueEffector-Vue-options-returns) + +(_`void`_) + +### Examples (#methods-VueEffector-Vue-options-examples) + +```js +import Vue from "vue"; +import { VueEffector } from "effector-vue"; + +Vue.use(VueEffector); +``` diff --git a/src/content/docs/en/api/effector-vue/VueSSRPlugin.md b/src/content/docs/en/api/effector-vue/VueSSRPlugin.md new file mode 100644 index 0000000..8e88f54 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/VueSSRPlugin.md @@ -0,0 +1,41 @@ +--- +title: VueSSRPlugin +redirectFrom: + - /api/effector-vue/VueSSRPlugin + - /docs/api/effector-vue/VueSSRPlugin +--- + +The Plugin provides a general scope which needs for read and update effector's stores, call effector's events. Required for SSR. + +# Plugins (#plugins) + +## `VueSSRPlugin({ scope, scopeName })` (#plugins-VueSSRPlugin-scope-scopeName) + +:::warning{title="Deprecated"} +Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0) `VueSSRPlugin` is deprecated. Use [`EffectorScopePlugin`](./EffectorScopePlugin) instead. +::: + +## Arguments (#plugins-VueSSRPlugin-scope-scopeName-arguments) + +1. `scope` [Scope](/en/api/effector/Scope) +2. `scopeName?` custom scopeName (default: `root`) + +## Examples (#plugins-VueSSRPlugin-scope-scopeName-examples) + +### Basic usage (#plugins-VueSSRPlugin-scope-scopeName-examples-basicUsage) + +```js +import { createSSRApp } from "vue"; +import { VueSSRPlugin } from "effector-vue/ssr"; +import { fork } from "effector"; + +const app = createSSRApp(AppComponent); +const scope = fork(); + +app.use( + VueSSRPlugin({ + scope, + scopeName: "app-scope-name", + }), +); +``` diff --git a/src/content/docs/en/api/effector-vue/createComponent.md b/src/content/docs/en/api/effector-vue/createComponent.md new file mode 100644 index 0000000..f52b438 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/createComponent.md @@ -0,0 +1,49 @@ +--- +title: createComponent +redirectFrom: + - /api/effector-vue/createComponent +--- + +# Methods (#methods) + +## `createComponent(options, store?)` (#methods-createComponent-options-store) + +### Arguments (#methods-createComponent-options-store-arguments) + +1. `options` (_Object_): component options (hooks, methods, computed properties) +2. `store` (_Object_): Store object from effector + +### Returns (#methods-createComponent-options-store-returns) + +(_`vue component`_) + +### Example (#methods-createComponent-options-store-example) + +```html + +``` + +```js +// component.vue +import { createComponent } from "effector-vue"; + +const $counter = createStore(0); +const { update } = createApi($counter, { + update: (_, value: number) => value, +}); + +export default createComponent( + { + name: "Counter", + + methods: { + update, + handleClick() { + const value = this.$counter + 1; // this.$counter <- number ( typescript tips ) + this.update(value); + }, + }, + }, + { $counter }, +); +``` diff --git a/src/content/docs/en/api/effector-vue/createGate.md b/src/content/docs/en/api/effector-vue/createGate.md new file mode 100644 index 0000000..5fb1049 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/createGate.md @@ -0,0 +1,80 @@ +--- +title: createGate +description: Creates a gate to consume data from view. Designed for vue 3 +redirectFrom: + - /api/effector-vue/createGate + - /docs/api/effector-vue/createGate +--- + +Creates a [_Gate_](/apieffector-vue/Gate) to consume data from view, designed for vue 3. If `defaultState` is defined, [Gate.state](/apieffector-vue/Gate#state) will be created with passed value. + +# Methods (#methods) + +## `createGate(config?: {defaultState?, domain?, name?})` (#methods-createGate-config) + +### Arguments (#methods-createGate-config-arguments) + +`config` (_Object_): Optional configuration object + +- `defaultState?`: Optional default state for [Gate.state](/en/api/effector-vue/Gate#state) +- `domain?` ([_Domain_](/en/api/effector/Domain)): Optional domain which will be used to create gate units ([Gate.open](/en/api/effector-vue/Gate#open) event, [Gate.state](/en/api/effector-vue/Gate#state) store, and so on) +- `name?` (_string_): Optional name which will be used as the name of a created Vue component + +### Returns (#methods-createGate-config-returns) + +[_Gate_](/en/api/effector-vue/Gate) + +### Examples (#methods-createGate-config-examples) + +#### Basic Usage (#methods-createGate-config-examples-basic) + +```js +import { createGate, useGate } from "effector-vue/composition"; + +const ListGate = createGate({ + name: "Gate with required props", +}); + +const ListItem = { + template: ` +
    + {{id}} +
    + `, + props: { + id: { + type: String, + required: true, + }, + }, + setup(props) { + useGate(ListGate, () => props.id); + }, +}; + +const app = { + template: ` +
    + +
    + `, + components: { + ListItem, + }, + setup() { + const id = ref("1"); + return { id }; + }, +}; + +Gate.state.watch((state) => { + console.log("current state", state); +}); +// => current state null + +app.mount("#app"); +// => current state 1 + +app.unmount(); +// => current state null +``` diff --git a/src/content/docs/en/api/effector-vue/index.md b/src/content/docs/en/api/effector-vue/index.md new file mode 100644 index 0000000..d952456 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/index.md @@ -0,0 +1,38 @@ +--- +title: effector-vue +redirectFrom: + - /api/effector-vue + - /docs/api/effector-vue +--- + +Effector binginds for Vue. + +# Top-Level Exports (#top-level-exports) + +- [VueEffector(Vue, options?)](/en/api/effector-vue/VueEffector) +- [createComponent(ComponentOptions, store?)](/en/api/effector-vue/createComponent) +- [EffectorScopePlugin({scope, scopeName?})](/en/api/effector-vue/EffectorScopePlugin) + +# ComponentOptions API (#componentOptions-api) + +- [ComponentOptions\](/en/api/effector-vue/ComponentOptions) + +# Hooks (#hooks) + +- [useUnit(shape)](/en/api/effector-vue/useUnit) +- [useStore(store)](/en/api/effector-vue/useStore) +- [useStoreMap({store, keys, fn})](/en/api/effector-vue/useStoreMap) +- [useVModel(store)](/en/api/effector-vue/useVModel) + +# Gate API (#gate-api) + +- [Gate](/en/api/effector-vue/Gate) +- [createGate()](/en/api/effector-vue/createGate) +- [useGate(GateComponent, props)](/en/api/effector-vue/useGate) + +# Import map (#import-map) + +Package `effector-vue` provides couple different entry points for different purposes: + +- [effector-vue/composition](/en/api/effector-vue/module/composition) +- [effector-vue/ssr](/en/api/effector-vue/module/ssr) diff --git a/src/content/docs/en/api/effector-vue/module/composition.md b/src/content/docs/en/api/effector-vue/module/composition.md new file mode 100644 index 0000000..b7bc175 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/module/composition.md @@ -0,0 +1,17 @@ +--- +title: effector-vue/composition +description: Separate module of effector-vue that provides additional API for composition API +--- + +```ts +import {} from "effector-vue/composition"; +``` + +Provides additional API for [effector-vue](/en/api/effector-vue) that allows to use [Composition API](https://v3.vuejs.org/guide/composition-api-introduction.html) + +## APIs (#api) + +- [useUnit(shape)](/en/api/effector-vue/useUnit) +- [useStore($store)](/en/api/effector-vue/useStore) +- [useStoreMap({ store, keys, fn })](/en/api/effector-vue/useStoreMap) +- [useVModel($store)](/en/api/effector-vue/useVModel) diff --git a/src/content/docs/en/api/effector-vue/module/ssr.md b/src/content/docs/en/api/effector-vue/module/ssr.md new file mode 100644 index 0000000..6322212 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/module/ssr.md @@ -0,0 +1,19 @@ +--- +title: effector-vue/ssr +description: Deprecated separate module of effector-vue that enforces library to use Scope +--- + +```ts +import {} from "effector-vue/ssr"; +``` + +:::warning{title="Deprecated"} +Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0) the core team recommends using main module of `effector-vue` of `effector-vue/composition` instead. +::: + +Provides additional API for [effector-vue](/en/api/effector-vue) that enforces library to use [Scope](/en/api/effector/scope) + +## APIs + +- [useEvent(event)](../useEvent) +- [VueSSRPlugin](../VueSSRPlugin) diff --git a/src/content/docs/en/api/effector-vue/useEvent.md b/src/content/docs/en/api/effector-vue/useEvent.md new file mode 100644 index 0000000..5a8fb71 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/useEvent.md @@ -0,0 +1,54 @@ +--- +title: useEvent +redirectFrom: + - /api/effector-vue/useEvent + - /docs/api/effector-vue/useEvent +--- + +```ts +import { useEvent } from "effector-vue/ssr"; +``` + +:::warning{title="Deprecated"} +Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0) `useEvent` is deprecated. Use [`useUnit`](./useUnit#useUnit) instead. +::: + +Bind event to current fork instance to use in dom event handlers. Used **only** with ssr, in application without forks `useEvent` will do nothing + +# Methods (#methods) + +## `useEvent(unit)` (#methods-useEvent-unit) + +### Arguments (#methods-useEvent-unit-arguments) + +1. `unit` ([_Event_](/en/api/effector/Event) or [_Effect_](/en/api/effector/Effect)): Event or effect which will be bound to current `scope` + +### Returns (#methods-useEvent-unit-returns) + +(`Function`): Function to pass to event handlers. Will trigger a given unit in current scope + +### Examples (#methods-useEvent-unit-examples) + +#### Basic (#methods-useEvent-unit-examples-basic) + +```js +import { createStore, createEvent } from "effector"; +import { useEvent } from "effector-vue/ssr"; + +const incremented = createEvent(); +const $count = createStore(0); + +$count.on(incremented, (x) => x + 1); + +export default { + setup() { + const counter = useStore($count); + const onIncrement = useEvent(incremented); + + return { + onIncrement, + counter, + }; + }, +}; +``` diff --git a/src/content/docs/en/api/effector-vue/useGate.md b/src/content/docs/en/api/effector-vue/useGate.md new file mode 100644 index 0000000..41bc1a5 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/useGate.md @@ -0,0 +1,30 @@ +--- +title: useGate +description: Using a gate to consume data from view. Designed for vue 3 +redirectFrom: + - /api/effector-vue/useGate + - /docs/api/effector-vue/useGate +--- + +```ts +import { useGate } from "effector-vue/composition"; +``` + +# Methods (#methods) + +## `useGate(Gate, props)` (#methods-useGate-Gate-props) + +Using a [`Gate`](/en/api/effector-vue/Gate) to consume data from view. Designed for Vue 3 + +### Arguments (#methods-useGate-Gate-props-arguments) + +1. `Gate` ([_Gate_](/en/api/effector-vue/Gate)) +2. `props` (_Props_) + +### Returns (#methods-useGate-Gate-props-returns) + +(_`void`_) + +### Examples (#methods-useGate-Gate-props-examples) + +[See example](/en/api/effector-vue/Gate) diff --git a/src/content/docs/en/api/effector-vue/useStore.md b/src/content/docs/en/api/effector-vue/useStore.md new file mode 100644 index 0000000..babc3bb --- /dev/null +++ b/src/content/docs/en/api/effector-vue/useStore.md @@ -0,0 +1,49 @@ +--- +title: useStore +description: Hook function, which subscribes to watcher, that observes changes in store. Designed for vue 3 +redirectFrom: + - /api/effector-vue/useStore + - /docs/api/effector-vue/useStore +--- + +```ts +import { useStore } from "effector-vue/composition"; +``` + +A hook function, which subscribes to watcher, that observes changes in the current **readonly** store, so when recording results, the component will update automatically. You can mutate the store value **only via [createEvent](/en/api/effector/createEvent)**. Designed for vue 3 + +## `useStore($store)` (#useStore-store) + +### Arguments (#useStore-store-arguments) + +1. `$store` ([`Store`](/en/api/effector/Store)) + +### Returns (#useStore-store-returns) + +(`readonly(State)`) + +### Example (#useStore-store-example) + +```js +import { createStore, createApi } from "effector"; +import { useStore } from "effector-vue/composition"; + +const $counter = createStore(0); + +const { incremented, decremented } = createApi($counter, { + incremented: (count) => count + 1, + decremented: (count) => count - 1, +}); + +export default { + setup() { + const counter = useStore($counter); + + return { + counter, + incremented, + decremented, + }; + }, +}; +``` diff --git a/src/content/docs/en/api/effector-vue/useStoreMap.md b/src/content/docs/en/api/effector-vue/useStoreMap.md new file mode 100644 index 0000000..d50f303 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/useStoreMap.md @@ -0,0 +1,130 @@ +--- +title: useStoreMap +redirectFrom: + - /api/effector-vue/useStoreMap + - /docs/api/effector-vue/useStoreMap +--- + +```ts +import { useStoreMap } from "effector-vue/composition"; +``` + +Function, which subscribes to [store](/en/api/effector/Store) and transforms its value with a given function. Signal will update only when the selector function result will change + +# Methods (#methods) + +## `useStoreMap($store, fn)` (#methods-useStoreMap-store-fn) + +### Formulae (#methods-useStoreMap-store-fn-formulae) + +```ts +useStoreMap( + $store: Store, + fn: (state: State) => Result, +): ComputedRef; +``` + +### Arguments (#methods-useStoreMap-store-fn-arguments) + +1. `$store`: Source [`Store`](/en/api/effector/Store) +2. `fn` (_(state) => result_): Selector function to receive part of source store + +### Returns (#methods-useStoreMap-store-fn-returns) + +(`ComputedRef`) + +## `useStoreMap(config)` (#methods-useStoreMap-config) + +### Formulae (#methods-useStoreMap-config-formulae) + +```ts +useStoreMap({ + store: Store, + keys?: () => Keys, + fn: (state: State, keys: Keys) => Result, + defaultValue?: Result, +}): ComputedRef; +``` + +### Arguments (#methods-useStoreMap-config-arguments) + +1. `params` (_Object_): Configuration object + - `store`: Source [store](/en/api/effector/Store) + - `keys` (`() => Keys`): Will be passed to `fn` selector + - `fn` (`(state: State, keys: Keys) => Result`): Selector function to receive part of source store + - `defaultValue` (`Result`): Optional default value if `fn` returned `undefined` + +### Returns (#methods-useStoreMap-config-returns) + +(`ComputedRef`) + +### Examples (#methods-useStoreMap-config-examples) + +This hook is very useful for working with lists, especially with large ones + +##### User.vue (#methods-useStoreMap-config-example-userVue) + +```js +import { createStore } from "effector"; +import { useUnit, useStoreMap } from "effector-vue/composition"; + +const $users = createStore([ + { + id: 1, + name: "Yung", + }, + { + id: 2, + name: "Lean", + }, + { + id: 3, + name: "Kyoto", + }, + { + id: 4, + name: "Sesh", + }, +]); + +export default { + props: { + id: Number, + }, + setup(props) { + const user = useStoreMap({ + store: $users, + keys: () => props.id, + fn: (users, userId) => users.find(({ id }) => id === userId), + }); + + return { user }; + }, +}; +``` + +```jsx +
    + [{user.id}] {user.name} +
    +``` + +##### App.vue (#methods-useStoreMap-config-examples-appVue) + +```js +const $ids = createStore(data.map(({ id }) => id)); + +export default { + setup() { + const ids = useStore($ids); + + return { ids }; + }, +}; +``` + +```jsx +
    + +
    +``` diff --git a/src/content/docs/en/api/effector-vue/useUnit.md b/src/content/docs/en/api/effector-vue/useUnit.md new file mode 100644 index 0000000..029303a --- /dev/null +++ b/src/content/docs/en/api/effector-vue/useUnit.md @@ -0,0 +1,150 @@ +--- +title: useUnit +description: TDB +redirectFrom: + - /docs/api/effector-vue/useUnit +--- + +```ts +import { useUnit } from "effector-vue/composition"; +``` + +Bind [_Stores_](../effector/Store) to Vue reactivity system or, in the case of [_Events_](../effector/Event)/[_Effects_](../effector/Effect) - bind to current [_Scope_](../effector/Scope) to use in DOM event handlers. + +**Designed for Vue 3 and Composition API exclusively.** + +:::info{title="Future"} +This API can completely replace the following APIs: + +- [useStore($store)](./useStore) +- [useEvent(event)](./useEvent) + +In the future, these APIs can be deprecated and removed. + +::: + +# Methods (#methods) + +## `useUnit(unit)` (#methods-useUnit-unit) + +### Arguments (#methods-useUnit-unit-arguments) + +1. `unit` ([_Event_](../effector/Event) or [_Effect_](../effector/Effect)): Event or effect which will be bound to current [_Scope_](../effector/Scope) + +### Returns (#methods-useUnit-unit-returns) + +(`Function`): Function to pass to event handlers. Will trigger given unit in current scope + +### Examples (#methods-useUnit-unit-examples) + +#### Basic Usage (#methods-useUnit-unit-examples-basic) + +```js +// model.js +import { createEvent, createStore, fork } from "effector"; + +const incremented = createEvent(); +const $count = createStore(0); + +$count.on(incremented, (count) => count + 1); +``` + +```html +// App.vue + + + + +``` + +### `useUnit($store)` (#methods-useUnit-store) + +#### Arguments (#methods-useUnit-store-arguments) + +1. `$store` ([_Store_](../effector/Store)): Store which will be bound to Vue reactivity system + +#### Returns (#methods-useUnit-store-returns) + +Reactive value of given [_Store_](../effector/Store) + +#### Examples (#methods-useUnit-store-examples) + +##### Basic Usage (#methods-useUnit-store-examples-basic) + +```js +// model.js +import { createEvent, createStore, fork } from "effector"; + +const incremented = createEvent(); +const $count = createStore(0); + +$count.on(incremented, (count) => count + 1); +``` + +```html +// App.vue + + + + +``` + +### `useUnit(shape)` (#methods-useUnit-shape) + +#### Arguments (#methods-useUnit-shape-arguments) + +1. `shape` Object or array of ([_Events_](../effector/Event) or [_Effects_](../effector/Effect) or [_Stores_](../effector/Store)): Every unit will be processed by `useUnit` and returned as a reactive value in case of [_Store_](../effector/Store) or as a function to pass to event handlers in case of [_Event_](../effector/Event) or [_Effect_](../effector/Effect). + +#### Returns (#methods-useUnit-shape-returns) + +(Object or Array): + +- if [_Event_](../effector/Event) or [_Effect_](../effector/Effect): functions with the same names or keys as argument to pass to event handlers. Will trigger given unit in current [_Scope_](../effector/Scope). +- if [_Store_](../effector/Store): reactive value of given [_Store_](../effector/Store) with the same names or keys as argument. + +#### Examples (#methods-useUnit-shape-examples) + +##### Basic Usage (#methods-useUnit-shape-examples-basic) + +```js +// model.js +import { createEvent, createStore, fork } from "effector"; + +const incremented = createEvent(); +const $count = createStore(0); + +$count.on(incremented, (count) => count + 1); +``` + +```html +// App.vue + + + + +``` diff --git a/src/content/docs/en/api/effector-vue/useVModel.md b/src/content/docs/en/api/effector-vue/useVModel.md new file mode 100644 index 0000000..fd949c4 --- /dev/null +++ b/src/content/docs/en/api/effector-vue/useVModel.md @@ -0,0 +1,108 @@ +--- +title: useVModel +description: hook function, which subscribes to watcher, that observes changes in the current store. Designed for vue 3 +redirectFrom: + - /api/effector-vue/useVModel + - /docs/api/effector-vue/useVModel +--- + +```ts +import { useVModel } from "effector-vue/composition"; +``` + +A hook function, which subscribes to a watcher that observes changes in the current store, so when recording results, the component will automatically update. It is primarily used when working with forms (`v-model`) in Vue 3. + +# Methods (#methods) + +## `useVModel($store)` (#methods-useVModel-store) + +### Formulae (#methods-useVModel-store-formulae) + +```ts +useVModel($store: Store): Ref>; +``` + +Designed for Vue 3. + +### Arguments (#methods-useVModel-store-arguments) + +1. `$store` ([_Store_](/en/api/effector/Store)) +2. `shape of Stores` ([_Store_](/en/api/effector/Store)) + +### Returns (#methods-useVModel-store-returns) + +(`State`) + +### Examples (#methods-useVModel-examples) + +#### Single Store (#methods-useVModel-examples-singleStore) + +```js +import { createStore, createApi } from "effector"; +import { useVModel } from "effector-vue/composition"; + +const $user = createStore({ + name: "", + surname: "", + skills: ["CSS", "HTML"], +}); + +export default { + setup() { + const user = useVModel($user); + + return { user }; + }, +}; +``` + +```html +
    + + + +
    + + + +
    +
    +``` + +#### Store Shape (#methods-useVModel-examples-storeShape) + +```js +import { createStore, createApi } from "effector"; +import { useVModel } from "effector-vue/composition"; + +const $name = createStore(""); +const $surname = createStore(""); +const $skills = createStore([]); + +const model = { + name: $name, + surname: $surname, + skills: $skills, +}; + +export default { + setup() { + const user = useVModel(model); + + return { user }; + }, +}; +``` + +```html +
    + + + +
    + + + +
    +
    +``` diff --git a/src/content/docs/en/api/effector/Domain.md b/src/content/docs/en/api/effector/Domain.md new file mode 100644 index 0000000..e22026e --- /dev/null +++ b/src/content/docs/en/api/effector/Domain.md @@ -0,0 +1,299 @@ +--- +title: Domain +description: Domain, its methods and properties +redirectFrom: + - /api/effector/Domain + - /docs/api/effector/domain +--- + +```ts +import { type Domain } from "effector"; +``` + +Domain is a namespace for your events, stores and effects. + +Domain can subscribe to event, effect, store or nested domain creation with `onCreateEvent`, `onCreateStore`, `onCreateEffect`, `onCreateDomain` methods. + +It is useful for logging or other side effects. + +# Unit creators (#unit-creators) + +:::info{title="since"} +[effector 20.7.0](https://changelog.effector.dev/#effector-20-7-0) +::: + +## `createEvent(name?)` (#unit-creators-createEvent-name) + +### Arguments (#unit-creators-createEvent-name-arguments) + +1. `name`? (_string_): event name + +### Returns (#unit-creators-createEvent-name-returns) + +[_Event_](/en/api/effector/Event): New event + +## `createEffect(handler?)` (#unit-creators-createEffect-handler) + +Creates an [effect](/en/api/effector/Effect) with given handler. + +### Arguments (#unit-creators-createEffect-handler-arguments) + +1. `handler`? (_Function_): function to handle effect calls, also can be set with [use(handler)](#use) + +### Returns (#unit-creators-createEffect-handler-returns) + +[_Effect_](/en/api/effector/Effect): A container for async function. + +:::info{title="since"} +[effector 21.3.0](https://changelog.effector.dev/#effector-21-3-0) +::: + +## `createEffect(name?)` (#unit-creators-createEffect-name) + +### Arguments (#unit-creators-createEffect-name-arguments) + +1. `name`? (_string_): effect name + +### Returns (#unit-creators-createEffect-name-returns) + +[_Effect_](/en/api/effector/Effect): A container for async function. + +## `createStore(defaultState)` (#unit-creators-createStore-defaultState) + +### Arguments (#unit-creators-createStore-defaultState-arguments) + +1. `defaultState` (_State_): store default state + +### Returns (#unit-creators-createStore-defaultState-returns) + +[_Store_](/en/api/effector/Store): New store + +## `createDomain(name?)` (#unit-creators-createDomain-name) + +### Arguments (#unit-creators-createDomain-name-arguments) + +1. `name`? (_string_): domain name + +### Returns (#unit-creators-createDomain-name-returns) + +[_Domain_](/en/api/effector/Domain): New domain + +## Aliases (#unit-creators-aliases) + +### `event(name?)` (#unit-creators-aliases-event-name) + +An alias for [domain.createEvent](/en/api/effector/Domain#createevent-name) + +### `effect(name?)` (#unit-creators-aliases-effect-name) + +An alias for [domain.createEffect](/en/api/effector/Domain#createeffect-name) + +### `store(defaultState)` (#unit-creators-aliases-store-defaultState) + +An alias for [domain.createStore](/en/api/effector/Domain#createstore-defaultstate) + +### `domain(name?)` (#unit-creators-aliases-domain-name) + +An alias for [domain.createDomain](/en/api/effector/Domain#createdomain-name) + +# Domain Properties (#properties) + +## `.history` (#unit-creators-history) + +Contains mutable read-only sets of units inside a domain. + +:::info{title="since"} +[effector 20.3.0](https://changelog.effector.dev/#effector-20-3-0) +::: + +### Formulae (#unit-creators-history-formulae) + +```ts +interface DomainHistory { + stores: Set>; + events: Set>; + domains: Set; + effects: Set>; +} + +const { stores, events, domains, effects } = domain.history; +``` + +When any kind of unit created inside a domain, it appears in a set with the name of type(stores, events, domains, effects) in the same order as created. + +### Examples (#unit-creators-history-examples) + +#### Basic (#unit-creators-history-examples-basic) + +```js +import { createDomain } from "effector"; +const domain = createDomain(); +const eventA = domain.event(); +const $storeB = domain.store(0); +console.log(domain.history); +// => {stores: Set{storeB}, events: Set{eventA}, domains: Set, effects: Set} +``` + +[Try it](https://share.effector.dev/flIV7Fja) + +# Domain hooks (#domain-hooks) + +## `onCreateEvent(callback)` (#domain-hooks-onCreateEvent-callback) + +### Formulae (#domain-hooks-onCreateEvent-callback-formulae) + +```ts +domain.onCreateEvent((event: Event) => {}); +``` + +- Function passed to `onCreateEvent` called every time, as new event created in `domain` +- Function called with `event` as first argument +- The result of function call is ignored + +### Arguments (#domain-hooks-onCreateEvent-callback-arguments) + +1. `callback` ([_Watcher_]): A function that receives [Event](/en/api/effector/Event) and will be called during every [domain.createEvent](/en/api/effector/Domain#unit-creators-createEvent-name) call + +### Returns (#domain-hooks-onCreateEvent-callback-returns) + +[_Subscription_]: Unsubscribe function. + +### Example (#domain-hooks-onCreateEvent-callback-example) + +```js +import { createDomain } from "effector"; + +const domain = createDomain(); + +domain.onCreateEvent((event) => { + console.log("new event created"); +}); + +const a = domain.createEvent(); +// => new event created + +const b = domain.createEvent(); +// => new event created +``` + +[Try it](https://share.effector.dev/QCQpga6u) + +## `onCreateEffect(callback)` (#domain-hooks-onCreateEffect-callback) + +### Formulae (#domain-hooks-onCreateEffect-callback-formulae) + +```ts +domain.onCreateEffect((effect: Effect) => {}); +``` + +- Function passed to `onCreateEffect` called every time, as new effect created in `domain` +- Function called with `effect` as first argument +- The result of function call is ignored + +### Arguments (#domain-hooks-onCreateEffect-callback-arguments) + +1. `callback` ([_Watcher_]): A function that receives [Effect](/en/api/effector/Effect) and will be called during every [domain.createEffect](/en/api/effector/Domain#unit-creators-createEffect-handler) call + +### Returns (#domain-hooks-onCreateEffect-callback-returns) + +[_Subscription_]: Unsubscribe function. + +### Example (#domain-hooks-onCreateEffect-callback-example) + +```js +import { createDomain } from "effector"; + +const domain = createDomain(); + +domain.onCreateEffect((effect) => { + console.log("new effect created"); +}); + +const fooFx = domain.createEffect(); +// => new effect created + +const barFx = domain.createEffect(); +// => new effect created +``` + +[Try it](https://share.effector.dev/uT6f8vv9) + +## `onCreateStore(callback)` (#domain-hooks-onCreateStore-callback) + +### Formulae (#domain-hooks-onCreateStore-callback-formulae) + +```ts +domain.onCreateStore(($store: Store) => {}); +``` + +- Function passed to `onCreateStore` called every time, as new store created in `domain` +- Function called with `$store` as first argument +- The result of function call is ignored + +### Arguments (#domain-hooks-onCreateStore-callback-arguments) + +1. `callback` ([_Watcher_]): A function that receives [Store](/en/api/effector/Store) and will be called during every [domain.createStore](/en/api/effector/Domain#unit-creators-createStore-defaultState) call + +### Returns (#domain-hooks-onCreateStore-callback-returns) + +[_Subscription_]: Unsubscribe function. + +### Example (#domain-hooks-onCreateStore-callback-example) + +```js +import { createDomain } from "effector"; + +const domain = createDomain(); + +domain.onCreateStore((store) => { + console.log("new store created"); +}); + +const $a = domain.createStore(null); +// => new store created +``` + +[Try it](https://share.effector.dev/OGlYOtfz) + +## `onCreateDomain(callback)` (#domain-hooks-onCreateDomain-callback) + +### Formulae (#domain-hooks-onCreateDomain-callback-formulae) + +```ts +domain.onCreateDomain((domain) => {}); +``` + +- Function passed to `onCreateDomain` called every time, as subdomain created in `domain` +- Function called with `domain` as first argument +- The result of function call is ignored + +### Arguments (#domain-hooks-onCreateDomain-callback-arguments) + +1. `callback` ([_Watcher_]): A function that receives [Domain](/en/api/effector/Domain) and will be called during every [domain.createDomain](/en/api/effector/Domain#unit-creators-createDomain-name) call + +### Returns (#domain-hooks-onCreateDomain-callback-returns) + +[_Subscription_]: Unsubscribe function. + +### Example (#domain-hooks-onCreateDomain-callback-example) + +```js +import { createDomain } from "effector"; + +const domain = createDomain(); + +domain.onCreateDomain((domain) => { + console.log("new domain created"); +}); + +const a = domain.createDomain(); +// => new domain created + +const b = domain.createDomain(); +// => new domain created +``` + +[Try it](https://share.effector.dev/dvBLiwHf) + +[_watcher_]: /en/explanation/glossary#watcher +[_subscription_]: /en/explanation/glossary#subscription diff --git a/src/content/docs/en/api/effector/Effect.md b/src/content/docs/en/api/effector/Effect.md new file mode 100644 index 0000000..6d1c936 --- /dev/null +++ b/src/content/docs/en/api/effector/Effect.md @@ -0,0 +1,583 @@ +--- +title: Effect +description: Effect, its methods and properties +redirectFrom: + - /api/effector/Effect + - /docs/api/effector/effect +--- + +```ts +import { type Effect } from "effector"; +``` + +**Effect** is a container for async function or any throwing function. + +It can be safely used in place of the original async function. + +# Methods (#methods) + +## `.use(handler)` (#methods-use-handler) + +Provides a function, which will be called when the effect is triggered. + +### Formulae (#methods-use-handler-formulae) + +```ts +effect.use(fn); +``` + +- Set handler `fn` for `effect` +- If effect already had an implementation at the time of the call, it will be replaced by a new one + +> Hint: current handler can be extracted with [effect.use.getCurrent()](#methods-use-getCurrent). + +You must provide a handler either through [.use](#methods-use-handler) method or `handler` property in [createEffect](/en/api/effector/createEffect), otherwise effect will throw with `no handler used in _%effect name%_` error when effect will be called. + +:::tip{title="See also"} +[Testing api calls with effects and stores](https://www.patreon.com/posts/testing-api-with-32415095) +::: + +### Arguments (#methods-use-handler-arguments) + +1. `handler` (_Function_): Function, that receives the first argument passed to an effect call. + +### Returns (#methods-use-handler-returns) + +([_Effect_](/en/api/effector/Effect)): The same effect + +### Examples (#methods-use-handler-examples) + +```js +const fetchUserReposFx = createEffect(); + +fetchUserReposFx.use(async (params) => { + console.log("fetchUserReposFx called with", params); + + const url = `https://api.github.com/users/${params.name}/repos`; + const req = await fetch(url); + return req.json(); +}); + +fetchUserReposFx({ name: "zerobias" }); +// => fetchUserRepos called with {name: 'zerobias'} +``` + +[Try it](https://share.effector.dev/TlYuDeve) + +## `.use.getCurrent()` (#methods-use-getCurrent) + +Returns current handler of effect. Useful for testing. + +### Formulae (#methods-use-getCurrent-formulae) + +```ts +fn = effect.use.getCurrent(); +``` + +- Returns current handler `fn` for `effect` +- If no handler was assigned to `effect`, default handler will be returned ([that throws an error](https://share.effector.dev/8PBjt3TL)) + +> Hint: to set a new handler use [effect.use(handler)](#methods-use-handler) + +### Returns (#methods-use-getCurrent-returns) + +(_Function_): Current handler, defined by `handler` property or via `.use` call. + +### Examples (#methods-use-getCurrent-examples) + +```js +const handlerA = () => "A"; +const handlerB = () => "B"; + +const fx = createEffect(handlerA); + +console.log(fx.use.getCurrent() === handlerA); +// => true + +fx.use(handlerB); +console.log(fx.use.getCurrent() === handlerB); +// => true +``` + +[Try it](https://share.effector.dev/CM6hgtOM) + +## `.watch(watcher)` (#methods-watch-watcher) + +Subscribe to effect calls. + +### Formulae (#methods-watch-watcher-formulae) + +```ts +const unwatch = effect.watch(watcher); +``` + +- Call `watcher` on each `effect` call, pass payload of `effect` as argument to `watcher` +- When `unwatch` is called, stop calling `watcher` + +### Arguments (#methods-watch-watcher-arguments) + +1. `watcher` ([_Watcher_](/en/explanation/glossary#watcher)): A function that receives `payload`. + +### Returns (#methods-watch-watcher-returns) + +[_Subscription_](/en/explanation/glossary#subscription): Unsubscribe function. + +### Examples (#methods-watch-watcher-examples) + +```js +import { createEffect } from "effector"; + +const fx = createEffect((params) => params); + +fx.watch((params) => { + console.log("effect called with value", params); +}); + +await fx(10); +// => effect called with value 10 +``` + +[Try it](https://share.effector.dev/VN1ef0TZ) + +## `.prepend(fn)` (#methods-prepend-fn) + +Creates an event, upon trigger it sends transformed data into the source event. +Works kind of like reverse `.map`. +In case of `.prepend` data transforms **before the original event occurs** and in the case of `.map`, data transforms **after original event occurred**. + +### Formulae (#methods-prepend-fn-formulae) + +```ts +const event = effect.prepend(fn); +``` + +- When `event` is triggered, call `fn` with payload from `event`, then trigger `effect` with the result of `fn()` +- `event` will have `EventCallable` type, so can be used as `target` in methods like `sample()` + +### Arguments (#methods-prepend-fn-arguments) + +1. `fn` (_Function_): A function that receives `payload`, [should be **pure**](/en/explanation/glossary#purity). + +### Returns (#methods-prepend-fn-returns) + +[_Event_](/en/api/effector/Event): New event. + +## `.map(fn)` (#methods-map-fn) + +Creates a new event, which will be called after the original effect is called, applying the result of a `fn` as a payload. It is a special function which allows you to decompose dataflow, extract or transform data. + +### Formulae (#methods-map-fn-formulae) + +```ts +const second = first.map(fn); +``` + +- When `first` is triggered, pass payload from `first` to `fn` +- Trigger `second` with the result of the `fn()` call as payload +- `second` event will have `Event` type, so it CAN NOT be used as `target` in methods like `sample()` + +### Arguments (#methods-map-fn-arguments) + +1. `fn` (_Function_): A function that receives `payload`, [should be **pure**](/en/explanation/glossary#purity). + +### Returns (#methods-map-fn-returns) + +[_Event_](/en/api/effector/Event): New event. + +### Examples (#methods-map-fn-examples) + +```js +import { createEffect } from "effector"; + +const userUpdate = createEffect(({ name, role }) => { + console.log(name, role); +}); +const userNameUpdated = userUpdate.map(({ name }) => name); // you may decompose dataflow with .map() method +const userRoleUpdated = userUpdate.map(({ role }) => role.toUpperCase()); // either way you can transform data + +userNameUpdated.watch((name) => console.log(`User's name is [${name}] now`)); +userRoleUpdated.watch((role) => console.log(`User's role is [${role}] now`)); + +await userUpdate({ name: "john", role: "admin" }); +// => User's name is [john] now +// => User's role is [ADMIN] now +// => john admin +``` + +[Try it](https://share.effector.dev/MmBBKXZe) + +# Properties (#properties) + +You are not supposed to use parts of effect (like `.done` and `.pending`) as a `target` in [sample](/en/api/effector/sample) (even though they are events and stores), since effect is a complete entity on its own. This behavior will not be supported. + +In the examples below constant `effect` has this signature: + +```ts +effect: Effect; +``` + +## `.done` Event (#properties-done) + +[_Event_](/en/api/effector/Event), which is triggered when _handler_ is _resolved_. + +:::warning{title="Important"} +Do not manually call this event. It is an event that depends on effect. +::: + +### Formulae (#properties-done-formulae) + +```ts +effect.done: Event<{ params: Params; done: Done }>; +``` + +### Properties (#properties-done-properties) + +Event triggered with an object of `params` and `result`: + +1. `params` (_Params_): An argument passed to the effect call +2. `result` (_Done_): A result of the resolved handler + +### Examples (#properties-done-examples) + +```js +import { createEffect } from "effector"; + +const fx = createEffect((value) => value + 1); + +fx.done.watch(({ params, result }) => { + console.log("Call with params", params, "resolved with value", result); +}); + +await fx(2); +// => Call with params 2 resolved with value 3 +``` + +[Try it](https://share.effector.dev/VogsNaDn) + +## `.doneData` Event (#properties-doneData) + +:::info{title="since"} +[effector 20.12.0](https://changelog.effector.dev/#effector-20-12-0) +::: + +Event, which is triggered by the result of the effect execution. + +:::warning{title="Important"} +Do not manually call this event. It is an event that depends on the effect. +::: + +### Formulae (#properties-doneData-formulae) + +```ts +effect.doneData: Event; +``` + +- `doneData` is an event, that triggered when `effect` is successfully resolved with `result` from [.done](#properties-done) + +[_Event_](/en/api/effector/Event) triggered when _handler_ is _resolved_. + +### Examples (#properties-doneData-examples) + +```js +import { createEffect } from "effector"; + +const fx = createEffect((value) => value + 1); + +fx.doneData.watch((result) => { + console.log(`Effect was successfully resolved, returning ${result}`); +}); + +await fx(2); +// => Effect was successfully resolved, returning 3 +``` + +[Try it](https://share.effector.dev/rNesMDtw) + +## `.fail` Event (#properties-fail) + +[_Event_](/en/api/effector/Event), which is triggered when handler is rejected or throws error. + +:::warning{title="Important"} +Do not manually call this event. It is an event that depends on effect. +::: + +### Formulae (#properties-fail-formulae) + +```ts +effect.fail: Event<{ params: Params; error: Fail }>; +``` + +### Properties (#properties-fail-properties) + +Event triggered with an object of `params` and `error`: + +1. `params` (_Params_): An argument passed to effect call +2. `error` (_Fail_): An error caught from the handler + +### Examples (#properties-fail-examples) + +```js +import { createEffect } from "effector"; + +const fx = createEffect(async (value) => { + throw Error(value - 1); +}); + +fx.fail.watch(({ params, error }) => { + console.log("Call with params", params, "rejected with error", error.message); +}); + +fx(2); +// => Call with params 2 rejected with error 1 +``` + +[Try it](https://share.effector.dev/hCPCHQ5N) + +## `.failData` Event (#properties-failData) + +:::info{title="since"} +[effector 20.12.0](https://changelog.effector.dev/#effector-20-12-0) +::: + +Event, which is triggered with error thrown by the effect. + +:::warning{title="Important"} +Do not manually call this event. It is an event that depends on effect. +::: + +### Formulae (#properties-failData-formulae) + +```ts +effect.failData: Event; +``` + +- `failData` is an event, that triggered when `effect` is rejected with `error` from [.fail](#properties-fail) + +[_Event_](/en/api/effector/Event) triggered when handler is rejected or throws error. + +### Examples (#properties-failData-examples) + +```js +import { createEffect } from "effector"; + +const fx = createEffect(async (value) => { + throw Error(value - 1); +}); + +fx.failData.watch((error) => { + console.log(`Execution failed with error ${error.message}`); +}); + +fx(2); +// => Execution failed with error 1 +``` + +[Try it](https://share.effector.dev/rNU3tqEx) + +## `.finally` Event (#properties-finally) + +:::info{title="since"} +[effector 20.0.0](https://changelog.effector.dev/#effector-20-0-0) +::: + +Event, which is triggered when handler is resolved, rejected or throws error. + +:::warning{title="Important"} +Do not manually call this event. It is an event that depends on effect. +::: + +### Properties (#properties-finally-properties) + +```ts +type Success = { status: 'done'; params: Params; result: Done } +type Failure = { status: 'fail'; params: Params; error: Fail } + +effect.finally: Event; +``` + +### Properties (#properties-finally-properties) + +[_Event_](/en/api/effector/Event), which is triggered with an object of `status`, `params` and `error` or `result`: + +1. `status` (_string_): A status of effect (`done` or `fail`) +2. `params` (_Params_): An argument passed to effect call +3. `error` (_Fail_): An error caught from the handler +4. `result` (_Done_): A result of the resolved handler + +### Examples (#properties-finally-examples) + +```js +import { createEffect } from "effector"; + +const fetchApiFx = createEffect(async ({ time, ok }) => { + await new Promise((resolve) => setTimeout(resolve, time)); + if (ok) return `${time} ms`; + throw Error(`${time} ms`); +}); + +fetchApiFx.finally.watch((value) => { + switch (value.status) { + case "done": + console.log("Call with params", value.params, "resolved with value", value.result); + break; + case "fail": + console.log("Call with params", value.params, "rejected with error", value.error.message); + break; + } +}); + +await fetchApiFx({ time: 100, ok: true }); +// => Call with params {time: 100, ok: true} +// resolved with value 100 ms + +fetchApiFx({ time: 100, ok: false }); +// => Call with params {time: 100, ok: false} +// rejected with error 100 ms +``` + +[Try it](https://share.effector.dev/f90vETOc) + +## `.pending` Store (#properties-pending) + +Store contains `true` when effect is called but not resolved yet. Useful to show loaders. + +:::warning{title="Important"} +Do not modify store value! It is [derived store](/en/api/effector/Store#readonly) and should be in predictable state. +::: + +### Formulae (#properties-pending-formulae) + +```ts +effect.pending: Store; +``` + +- [Store](/en/api/effector/Store) will update when `done` or `fail` are triggered +- [Store](/en/api/effector/Store) contains `true` value until the effect is resolved or rejected + +### Returns (#properties-pending-returns) + +[_DerivedStore_](/en/api/effector/Store#readonly): Store that represents current state of the effect + +### Examples (#properties-pending-examples) + +```jsx +import React from "react"; +import ReactDOM from "react-dom"; +import { createEffect } from "effector"; +import { useUnit } from "effector-react"; + +const fetchApiFx = createEffect((ms) => new Promise((resolve) => setTimeout(resolve, ms))); + +fetchApiFx.pending.watch(console.log); + +const Loading = () => { + const loading = useUnit(fetchApiFx.pending); + + return
    {loading ? "Loading..." : "Load complete"}
    ; +}; + +ReactDOM.render(, document.getElementById("root")); + +fetchApiFx(3000); +``` + +[Try it](https://share.effector.dev/wDMQKqhl) + +It's property is a shorthand for common use case: + +```js +import { createEffect, createStore } from "effector"; + +const fetchApiFx = createEffect(); + +// now you can use fetchApiFx.pending instead +const $isLoading = createStore(false) + .on(fetchApiFx, () => true) + .on(fetchApiFx.done, () => false) + .on(fetchApiFx.fail, () => false); +``` + +## `.inFlight` Store (#properties-inFlight) + +:::info{title="since"} +[effector 20.11.0](https://changelog.effector.dev/#effector-20-11-0) +::: + +Shows how many effect calls aren't settled yet. Useful for rate limiting. + +:::warning{title="Important"} +Do not modify `$count` value! It is [derived store](/en/api/effector/Store#readonly) and should be in predictable state. +::: + +### Formulae (#properties-inFlight-formulae) + +```ts +effect.inFlight: Store; +``` + +- The [store](/en/api/effector/Store) will be `0` if no calls of `effect` in pending state, its default state +- On each call of `effect` state in the store will be increased +- When effect resolves to any state(done or fail) state in the store will be decreased + +### Returns (#properties-inFlight-returns) + +[_DerivedStore_](/en/api/effector/Store#readonly): Store that represents count of the running effects + +### Examples (#properties-inFlight-examples) + +```js +import { createEffect } from "effector"; + +const fx = createEffect(() => new Promise((rs) => setTimeout(rs, 500))); + +fx.inFlight.watch((amount) => { + console.log("in-flight requests:", amount); +}); +// => 0 + +const req1 = fx(); +// => 1 + +const req2 = fx(); +// => 2 + +await Promise.all([req1, req2]); + +// => 1 +// => 0 +``` + +[Try it](https://share.effector.dev/XsM8fZXa) + +# Types (#types) + +```ts +import { type EffectParams, type EffectResult, type EffectError } from "effector"; +``` + +## `EffectParams` (#types-EffectParams) + +Allows to extract type of Params from `effect`. + +```ts +const effect: Effect; +type Params = EffectParams; +``` + +## `EffectResult` (#types-EffectResult) + +Allows to extract type of result from `effect`. + +```ts +const effect: Effect; +type Done = EffectResult; +``` + +## `EffectError` (#types-EffectError) + +Allows to extract type of error from `effect`. + +```ts +const effect: Effect; +type Fail = EffectError; +``` diff --git a/src/content/docs/en/api/effector/Event.md b/src/content/docs/en/api/effector/Event.md new file mode 100644 index 0000000..2cc25ae --- /dev/null +++ b/src/content/docs/en/api/effector/Event.md @@ -0,0 +1,893 @@ +--- +title: Event +keywords: + - event + - unit +description: Event, its methods and properties +redirectFrom: + - /api/effector/Event + - /docs/api/effector/event +--- + +```ts +import { type Event, type EventCallable } from "effector"; +``` + +The **Event** in effector represents a user action, a step in the application process, a command to execute, or an intention to make modifications, among other things. +This unit is designed to be a carrier of information/intention/state within the application, not the holder of a state. + +# `EventCallable` (#eventCallable) + +## Construction (#eventCallable-construction) + +There are many ways to create an event: + +- The most common [`createEvent`](/en/api/effector/createEvent) +- Using [Domain `createEvent`](/en/api/effector/Domain#unit-creators-createEvent-name) +- Via [Event's methods](#eventCallable-methods) and it's supertype [EventCallable's methods](#eventCallable-methods) +- Some [Effect's methods](/en/api/effector/Effect#methods) return new events and readonly events +- Operators such as: [`createApi`](/en/api/effector/createApi) + +### Declaring types (#eventCallable-declaringTypes) + +Event carries some data and in a TypeScript ecosystem each data should have a defined type. When an event is explicitly created by [`createEvent`](/en/api/effector/createEvent), type of the argument must be provided as a Generic type argument: + +```ts +import { createEvent } from "effector"; + +interface ItemAdded { + id: string; + title: string; +} + +const itemAdded = createEvent(); +``` + +In most cases, there is no reason to use `void` with another type (~~`Event`~~). Use `void` only to declare the Event or EventCallable without the argument at all. That's why it is possible to send data from an event with an argument into an event without an argument. + +```ts +sample({ + clock: withData, // Event + target: withoutData, // Event +}); +``` + +We **strongly recommend** using `null` for empty values when intended: + +```ts +import { createEvent } from "effector"; + +const maybeDataReceived = createEvent(); +// maybeDataReceived: EventCallable +``` + +[Read more in the explanation section](/en/explanation/events#typescript). + +## Call as function `event(argument)` (#eventCallable-call-argument) + +Initiates an event with the provided argument, which in turn activates any registered subscribers. + +[Read more in the explanation section](/en/explanation/events#event-calling). + +### Formulae (#eventCallable-call-argument-formulae) + +```ts +const event: EventCallable; +event(argument: T): T; +``` + +- `event` called as a function always returns its `argument` as is +- all subscribers of event receives the `argument` passed into +- when `T` is `void`, `event` can be called without arguments +- `T` by default is `void`, so generic type argument can be omitted + +:::warning{title="Important"} + +In Effector, any event supports only **a single argument**. +It is not possible to call an event with two or more arguments, as in `someEvent(first, second)`. + +All arguments beyond the first will be ignored. +The core team has implemented this rule for specific reasons related to the design and functionality. +::: + +### Arguments (#eventCallable-call-argument-arguments) + +1. `argument` is a value of `T`. It's optional if the event is defined as `EventCallable`. + +### Throws (#eventCallable-call-argument-throws) + +#### call of readonly event is not supported, use createEvent instead (#eventCallable-call-argument-throws-call-of-readonly-event) + +:::info{title="since"} +[effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0-spacewatch) +::: + +When user tried to call `Event`. In the most cases it happens when you tried to call derived event: + +```ts +const numberReceived = createEvent(); // EventCallable +const stringifiedReceived = numberReceived.map((number) => String(number)); // Event + +stringifiedReceived("123"); // THROWS! +``` + +The same for all methods returning `Event`. + +To fix it create separate event via `createEvent`, and connect them by `sample`: + +```ts +const numberReceived = createEvent(); +const stringifiedReceived = createEvent(); + +sample({ + clock: numberReceived, + fn: (number) => String(number), + target: stringifiedReceived, +}); + +stringifiedReceived("123"); // OK +``` + +#### unit call from pure function is not supported, use operators like sample instead (#eventCallable-call-argument-throws-unit-call-from-pure) + +:::info{title="since"} +[effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0-spacewatch) +::: + +Happens when events or effects called from [pure functions](/en/glossary#purity), like mappers: + +```ts +const someHappened = createEvent(); +const another = createEvent(); + +const derived = someHappened.map((number) => { + another(); // THROWS! + return String(number); +}); +``` + +To fix this, use `sample`: + +```ts +const someHappened = createEvent(); +const another = createEvent(); +const derived = createEvent(); + +sample({ + clock: someHappened, + target: another, +}); + +// The same as .map(), but using `target` +sample({ + clock: someHappened, + fn: (number) => String(number), + target: derived, +}); +``` + +### Returns (#eventCallable-call-argument-returns) + +`T`: Represents the same value that is passed into the `event`. + +### Types (#eventCallable-call-argument-types) + +```ts +import { createEvent, Event } from "effector"; + +const someHappened = createEvent(); +// someHappened: EventCallable +someHappened(1); + +const anotherHappened = createEvent(); +// anotherHappened: EventCallable +anotherHappened(); +``` + +An event can be specified with a single generic type argument. By default, this argument is set to void, indicating that the event does not accept any parameters. + +## Methods (#eventCallable-methods) + +Since the `createEvent` factory creates `EventCallable` for you, its methods will be described first, even though it is a extension of the `Event` type. + +All the methods and properties from [Event](#event-methods) are also available on `EventCallable` instance. + +:::tip +You can think of the EventCallable and Event as type and its super type: + +`EventCallable extends Event` +::: + +### `.prepend(fn)` (#eventCallable-methods-prepend-fn) + +Creates a new `EventCallable`, that should be called, upon trigger it sends transformed data into the original event. + +Works kind of like reverse `.map`. In case of `.prepend` data transforms **before the original event occurs** and in the +case of `.map`, data transforms **after original event occurred**. + +If the original event belongs to some [domain](/en/api/effector/Domain), then a new event will belong to it as well. + +#### Formulae (#eventCallable-methods-prepend-fn-formulae) + +```ts +const first: EventCallable; +const second: EventCallable = first.prepend(fn); +``` + +- When `second` event is triggered +- Call `fn` with argument from the `second` event +- Trigger `first` event with the result of `fn()` + +#### Arguments (#eventCallable-methods-prepend-fn-arguments) + +1. `fn` (_Function_): A function that receives `argument`, and should be **pure**. + +#### Throws (#eventCallable-methods-prepend-fn-throws) + +##### unit call from pure function is not supported, use operators like sample instead (#eventCallable-methods-prepend-fn-throws-unit-call-from-pure) + +:::info{title="since"} +[effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0-spacewatch) +::: + +Happens when events or effects called from [pure functions](/en/glossary#purity), like mappers: + +```ts +const someHappened = createEvent(); +const another = createEvent(); + +const reversed = someHappened.prepend((input: number) => { + another(input); // THROWS! + return String(input); +}); +``` + +To fix this, use `sample`: + +```ts +const someHappened = createEvent(); +const another = createEvent(); +const reversed = createEvent(); + +// The same as .prepend(), but using `sample` +sample({ + clock: reversed, + fn: (input) => String(input), + target: someHappened, +}); + +sample({ + clock: reversed, + target: another, +}); +``` + +#### Returns (#eventCallable-methods-prepend-fn-returns) + +[`EventCallable`](/en/api/effector/Event): New event. + +#### Types (#eventCallable-methods-prepend-fn-types) + +There TypeScript requires explicitly setting type of the argument of `fn` function: + +```ts +import { createEvent } from "effector"; + +const original = createEvent<{ input: string }>(); + +const prepended = original.prepend((input: string) => ({ input })); +// ^^^^^^ here +``` + +Type of the `original` event argument and the resulting type of the `fn` must be the same. + +#### Examples (#eventCallable-methods-prepend-fn-examples) + +##### Basic (#eventCallable-methods-prepend-fn-examples-basic) + +```js +import { createEvent } from "effector"; + +const userPropertyChanged = createEvent(); + +userPropertyChanged.watch(({ field, value }) => { + console.log(`User property "${field}" changed to ${value}`); +}); + +const changeName = userPropertyChanged.prepend((name) => ({ + field: "name", + value: name, +})); +const changeRole = userPropertyChanged.prepend((role) => ({ + field: "role", + value: role.toUpperCase(), +})); + +changeName("john"); +// => User property "name" changed to john + +changeRole("admin"); +// => User property "role" changed to ADMIN + +changeName("alice"); +// => User property "name" changed to alice +``` + +[Try it](https://share.effector.dev/XGxlG4LD) + +##### Meaningful example (#eventCallable-methods-prepend-fn-examples-meaningful) + +You can think of this method like a wrapper function. Let's assume we have function with not ideal API, but we want to +call it frequently: + +```ts +import { sendAnalytics } from "./analytics"; + +export function reportClick(item: string) { + const argument = { type: "click", container: { items: [arg] } }; + return sendAnalytics(argument); +} +``` + +This is exactly how `.prepend()` works: + +```ts +import { sendAnalytics } from "./analytics"; + +export const reportClick = sendAnalytics.prepend((item: string) => { + return { type: "click", container: { items: [arg] } }; +}); + +reportClick("example"); +// reportClick triggered "example" +// sendAnalytics triggered { type: "click", container: { items: ["example"] } } +``` + +Check all other methods on [Event](#event-methods). + +# `Event` (#event) + +A **Event** is a super type of `EventCallable` with different approach. Firstly, invoking a Event is not +allowed, and it cannot be used as a `target` in the `sample` operator, and so on. + +The primary purpose of a Event is to be triggered by internal code withing the effector library or ecosystem. +For instance, the `.map()` method returns a Event, which is subsequently called by the `.map()` method itself. + +:::info +There is no need for user code to directly invoke such an Event. + +If you find yourself needing to call a Event, it may be necessary to reevaluate and restructure your +application's logic. +::: + +All the functionalities provided by an Event are also supported in an EventCallable. + +## Construction (#event-construction) + +There is no way to manually create Event, but some methods and operators returns derived events, they are return +`Event` type: + +- Event's methods like: [`.map(fn)`](#event-map-fn), [`.filter({fn})`](#event-methods-filterMap-fn), and so on +- Store's property: ['.updates'](/en/api/effector/Store#updates) +- Effect's [methods](/en/api/effector/Effect#effect) and [properties](/en/api/effector/Effect#properties) +- operators like: [`sample`](/en/api/effector/sample), [`merge`](/en/api/effector/merge) + +## Throws (#event-throws) + +- **Errors related to incorrect usage**: More details in specific method sections. + +## Declaring types (#event-types) + +It becomes necessary in cases where a factory or library requires an event to subscribe to its updates, ensuring proper +integration and interaction with the provided functionality: + +```ts +const event: Event; +``` + +## Methods (#event-methods) + +### `.map(fn)` (#event-methods-map-fn) + +Creates a new derived Event, which will be called after the original event is called, using the result of the fn +function as its argument. This special function enables you to break down and manage data flow, as well as extract or +transform data within your business logic model. + +#### Formulae (#event-methods-map-fn-formulae) + +```ts +const first: Event | EventCallable; +const second: Event = first.map(fn); +``` + +- When `first` is triggered, pass payload from `first` to `fn`. +- Trigger `second` with the result of the `fn()` call as payload. +- The function `fn` is invoked each time the `first` event is triggered. +- Also, the `second` event triggered each time the `first` is triggered. + +#### Arguments (#event-methods-map-fn-arguments) + +1. `fn` (_Function_): A function that receives `argument`, and [should be **pure**](/en/explanation/glossary#purity). + +#### Throws (#event-methods-map-fn-throws) + +##### unit call from pure function is not supported, use operators like sample instead (#event-methods-map-fn-throws-unit-call-from-pure) + +:::info{title="since"} +[effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0-spacewatch) +::: + +Happens when events or effects called from [pure functions](/en/glossary#purity), like mappers: + +```ts +const someHappened = createEvent(); +const another = createEvent(); + +const derived = someHappened.map((number) => { + another(); // THROWS! + return String(number); +}); +``` + +To fix this, use `sample`: + +```ts +const someHappened = createEvent(); +const another = createEvent(); +const derived = createEvent(); + +sample({ + clock: someHappened, + target: another, +}); + +// The same as .map(), but using `target` +sample({ + clock: someHappened, + fn: (number) => String(number), + target: derived, +}); +``` + +#### Returns (#event-methods-map-fn-returns) + +[`Event`](#event): The new event. + +#### Types (#event-methods-map-fn-types) + +The resulting type of the `fn` function will be utilized to define the type of the derived event. + +```ts +import { createEvent } from "effector"; + +const first = createEvent(); +// first: Event + +const second = first.map((count) => count.toString()); +// second: Event +``` + +The `first` event can be represented as either `Event` or `Event`.
    +The `second` event will always be represented as `Event`. + +#### Examples (#event-methods-map-fn-examples) + +```js +import { createEvent } from "effector"; + +const userUpdated = createEvent(); + +// you may decompose dataflow with .map() method +const userNameUpdated = userUpdated.map(({ user }) => name); + +// either way you can transform data +const userRoleUpdated = userUpdated.map((user) => user.role.toUpperCase()); + +userNameUpdated.watch((name) => console.log(`User's name is [${name}] now`)); +userRoleUpdated.watch((role) => console.log(`User's role is [${role}] now`)); + +userUpdated({ name: "john", role: "admin" }); +// => User's name is [john] now +// => User's role is [ADMIN] now +``` + +[Try it](https://share.effector.dev/duDut6nR) + +### `.filter({ fn })` (#event-methods-filter-fn) + +This method generates a new derived Event that will be invoked after the original event, but only if the `fn` +function returns `true`. This special function enables you to break down data flow into a branches and +subscribe on them within the business logic model. + +:::tip +[sample](/en/api/effector/sample) operator with `filter` argument is the preferred filtering method. +::: + +#### Formulae (#event-methods-filter-fn-formulae) + +```ts +const first: Event | EventCallable; +const second: Event = first.filter({ fn }); +``` + +- When `first` is triggered, pass payload from `first` to `fn`. +- The `second` event will be triggered only if `fn` returns `true`, with the argument from `first` event. +- The function `fn` is invoked each time the `first` event is triggered. +- Also, the `second` event triggered each time the `first` is triggered, **and** the `fn` returned `true`. + +#### Arguments (#event-methods-filter-fn-arguments) + +1. `fn` (_Function_): A function that receives `argument`, and [should be **pure**](/en/explanation/glossary#purity). + +:::info{title="Note"} +Here, due to legacy restrictions `fn` is required to use object form because `event.filter(fn)` was an alias +for [Event filterMap](/en/api/effector/Event#event-methods-filterMap-fn). + +Use it always like this `.filter({ fn })`. +::: + +#### Throws (#event-methods-filter-fn-throws) + +##### unit call from pure function is not supported, use operators like sample instead (#event-methods-filter-fn-throws-unit-call-from-pure) + +:::info{title="since"} +[effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0-spacewatch) +::: + +Happens when events or effects called from [pure functions](/en/glossary#purity), like guards: + +```ts +const countReceived = createEvent(); +const eachReceived = createEvent(); + +const receivedEven = someHappened.filter({ + fn(count) { + eachReceived(count); // THROWS! + return count % 2 === 0; + }, +}); +``` + +To fix this, use `sample` to call `eachReceived`: + +```ts +const countReceived = createEvent(); +const eachReceived = createEvent(); + +const receivedEven = someHappened.filter({ + fn(count) { + return count % 2 === 0; + }, +}); + +sample({ + clock: someHappened, + target: eachReceived, +}); +``` + +#### Returns (#event-methods-filter-fn-returns) + +[`Event`](#event): The new event + +#### Types (#event-methods-filter-fn-types) + +Method `.filter()` always returns Event. Also this event will have the same type as the original type: + +```ts +import { createEvent } from "effector"; + +const numberReceived = createEvent(); +// numberReceived: Event + +const evenReceived = numberReceived.filter({ + fn: (number) => number % 2 === 0, +}); +// evenReceived: Event + +evenReceived.watch(console.info); +numberReceived(5); // nothing +numberReceived(2); // => 2 +``` + +#### Examples (#event-methods-filter-fn-examples) + +```js +import { createEvent, createStore } from "effector"; + +const numbers = createEvent(); +const positiveNumbers = numbers.filter({ + fn: ({ x }) => x > 0, +}); + +const $lastPositive = createStore(0).on(positiveNumbers, (n, { x }) => x); + +$lastPositive.watch((x) => { + console.log("last positive:", x); +}); + +// => last positive: 0 + +numbers({ x: 0 }); +// no reaction + +numbers({ x: -10 }); +// no reaction + +numbers({ x: 10 }); +// => last positive: 10 +``` + +[Try it](https://share.effector.dev/H2Iu4iJH) + +#### Meaningful example (#event-methods-filter-fn-examples-meaningful) + +Let's assume a standard situation when you want to buy sneakers in the shop, but there is no size. You subscribe to the +particular size of the sneakers' model, and in addition, you want to receive a notification if they have it, and ignore +any other notification. Therefore, filtering can be helpful for that. Event filtering works in the same way. If `filter` +returns `true`, the event will be called. + +```ts +const sneackersReceived = createEvent(); +const uniqueSizeReceived = sneackersReceived.filter({ + fn: (sneackers) => sneackers.size === 48, +}); +``` + +### `.filterMap(fn)` (#event-methods-filterMap-fn) + +:::info{title="since"} +[effector 20.0.0](https://changelog.effector.dev/#effector-20-0-0) +::: + +This methods generates a new derived Event that **may be invoked** after the original event, but with the +transformed argument. This special method enabled you to simultaneously transform data and filter out trigger of the +event. + +This method looks like the `.filter()` and `.map()` merged in the one. That's it. The reason for creating was an +impossibility for event filtering. + +This method is mostly useful with JavaScript APIs whose returns `undefined` sometimes. + +#### Formulae (#event-methods-filterMap-fn-formulae) + +```ts +const first: Event | EventCallable; +const second: Event = first.filterMap(fn); +``` + +- When `first` is triggered, call `fn` with payload from `first` +- If `fn()` returned `undefined` do not trigger `second` +- If `fn()` returned some data, trigger `second` with data from `fn()` + +#### Arguments (#event-methods-filterMap-fn-arguments) + +1. `fn` (_Function_): A function that receives `argument`, [should be **pure**](/en/explanation/glossary#purity). + +The `fn` function should return some data. When `undefined` is returned, the update of derived event will be skipped. + +#### Throws (#event-methods-filterMap-fn-throws) + +##### unit call from pure function is not supported, use operators like sample instead (#event-methods-filterMap-fn-throws-unit-call-from-pure) + +:::info{title="since"} +[effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0-spacewatch) +::: + +Happens when events or effects called from [pure functions](/en/glossary#purity), like mappers: + +```ts +const countReceived = createEvent(); +const eachReceived = createEvent(); + +const receivedEven = someHappened.filterMap((count) => { + eachReceived(count); // THROWS! + return count % 2 === 0 ? Math.abs(count) : undefined; +}); +``` + +To fix this, use `sample` to call `eachReceived`: + +```ts +const countReceived = createEvent(); +const eachReceived = createEvent(); + +const receivedEven = someHappened.filterMap((count) => { + return count % 2 === 0 ? Math.abs(count) : undefined; +}); + +sample({ + clock: someHappened, + target: eachReceived, +}); +``` + +#### Returns (#event-methods-filterMap-fn-returns) + +[`Event`](#event): The new event + +#### Types (#event-methods-filterMap-fn-types) + +The type for the derived event is automatically inferred from the `fn` declaration. +No need to explicitly set type for variable or generic type argument: + +```ts +import { createEvent } from "effector"; + +const first = createEvent(); +// first: Event + +const second = first.filterMap((count) => { + if (count === 0) return; + return count.toString(); +}); +// second: Event +``` + +The `first` event can be represented as either `Event` or `EventCallable`.
    +The `second` event will always be represented as `Event`. + +#### Examples (#event-methods-filterMap-fn-examples) + +```tsx +import { createEvent } from "effector"; + +const listReceived = createEvent(); + +// Array.prototype.find() returns `undefined` when no item is found +const effectorFound = listReceived.filterMap((list) => list.find((name) => name === "effector")); + +effectorFound.watch((name) => console.info("found", name)); + +listReceived(["redux", "effector", "mobx"]); // => found effector +listReceived(["redux", "mobx"]); +``` + +[Try it](https://share.effector.dev/ARDanMAM) + +#### Meaningful example (#event-methods-filterMap-fn-examples-meaningful) + +Consider a scenario where you walk into a grocery store with a specific task: you need to purchase 10 apples, but only +if they're red. If they're not red, you're out of luck. +Let's consider by steps: + +1. Take one apple; +2. Have a look, is it red(put in a pack) or not(take another). + +And you repeat this until you complete the task. Now think about it in the effector terms, and we consider the positive +case: + +1. Take an apple – event; +2. Have a look, red or no – filter; +3. You keep it – map; +4. Put in pack – event. +5. Pack – store + +### `.watch(watcher)` (#event-methods-watch-watcher) + +This method enables you to call callback on each event trigger with the argument of the event. + +:::tip{title="Keep in mind"} +The `watch` method neither handles nor reports exceptions, manages the completion of asynchronous operations, nor +addresses data race issues. + +Its primary intended use is for short-term debugging and logging purposes. +::: + +[Read more in the explanation section](/en/explanation/events#event-watch). + +#### Formulae (#event-methods-watch-watcher-formulae) + +```ts +const event: Event | EventCallable; +const unwatch: () => void = event.watch(fn); +``` + +- The `fn` will be called on each `event` trigger, passed argument of the `event` to the `fn`. +- When `unwatch` is called, stop calling `fn` on each `event` trigger. + +#### Arguments (#event-methods-watch-watcher-arguments) + +1. `watcher` ([_Watcher_](/en/explanation/glossary#watcher)): A function that receives `argument` from the event. + +#### Returns (#event-methods-watch-watcher-returns) + +[_Subscription_](/en/explanation/glossary#subscription): Unsubscribe function. + +#### Examples (#event-methods-watch-watcher-examples) + +```js +import { createEvent } from "effector"; + +const sayHi = createEvent(); +const unwatch = sayHi.watch((name) => console.log(`${name}, hi there!`)); + +sayHi("Peter"); // => Peter, hi there! +unwatch(); + +sayHi("Drew"); // => nothing happened +``` + +[Try it](https://share.effector.dev/9YVgCl4C) + +### `.subscribe(observer)` (#event-methods-subscribe-observer) + +This is the low-level method to integrate event with the standard `Observable` pattern. + +:::tip{title="Keep in mind"} +You don't need to use this method on your own. It is used under the hood by rendering engines or so on. +::: + +Read more: + +- https://rxjs.dev/guide/observable +- https://github.com/tc39/proposal-observable + +## Properties (#event-properties) + +These set of property is mostly set by [`effector/babel-plugin`](/en/api/effector/babel-plugin) +or [`@effector/swc-plugin`](https://github.com/effector/swc-plugin). So they are exist only when babel or SWC is used. + +### `.sid` (#event-properties-sid) + +It is an unique identifier for each event. + +It is important to note, SID is not changes on each app start, it is statically written inside your app bundle to +absolutely identify units. + +It can be useful to send events between workers or +server/browser: [examples/worker-rpc](https://github.com/effector/effector/tree/master/examples/worker-rpc). + +It has the `string | null` type. + +### `.shortName` (#event-properties-shortName) + +It is a `string` type property, contains the name of the variable event declared at. + +```ts +import { createEvent } from "effector"; + +const demo = createEvent(); +// demo.shortName === 'demo' +``` + +But reassign event to another variable changes nothing: + +```ts +const another = demo; +// another.shortName === 'demo' +``` + +### `.compositeName` (#event-properties-compositeName) + +This property contains the full internal chain of units. For example, event can be created by the domain, so the +composite name will contain a domain name inside it. + +```ts +import { createEvent, createDomain } from "effector"; + +const first = createEvent(); +const domain = createDomain(); +const second = domain.createEvent(); + +console.log(first); +// => { shortName: "first", fullName: "first", path: ["first"] } + +console.log(second); +// => { shortName: "second", fullName: "domain/second", path: ["domain", "second"] } +``` + +# Types (#types) + +```ts +import { type EventPayload } from "effector"; +``` + +## `EventPayload` (#types-EventPayload) + +Extracts type of payload from `Event` or `EventCallable`. + +```ts +const event: Event; +type Payload = EventPayload; +``` diff --git a/src/content/docs/en/api/effector/Scope.md b/src/content/docs/en/api/effector/Scope.md new file mode 100644 index 0000000..46d9a2f --- /dev/null +++ b/src/content/docs/en/api/effector/Scope.md @@ -0,0 +1,102 @@ +--- +title: Scope +redirectFrom: + - /api/effector/Scope + - /docs/api/effector/scope +--- + +```ts +import { type Scope } from "effector"; +``` + +`Scope` is a fully isolated instance of application. +The primary purpose of scope includes SSR (Server-Side Rendering) but is not limited to this use case. A `Scope` contains an independent clone of all units (including connections between them) and basic methods to access them. + +A `Scope` can be created using [fork](/en/api/effector/fork). + +## Imperative effects calls with scope (#scope-imperativeEffectCalls) + +When making imperative effect calls within effect handlers, it is supported but **not** within `watch` functions. For effect handlers that call other effects, ensure to only call effects, not common asynchronous functions. Furthermore, effect calls should be awaited: + +**✅ Correct usage for an effect without inner effects:** + +```js +const delayFx = createEffect(async () => { + await new Promise((resolve) => setTimeout(resolve, 80)); +}); +``` + +**✅ Correct usage for an effect with inner effects:** + +```js +const authUserFx = createEffect(); +const sendMessageFx = createEffect(); + +const sendWithAuthFx = createEffect(async () => { + await authUserFx(); + await delayFx(); + await sendMessageFx(); +}); +``` + +**❌ Incorrect usage for an effect with inner effects:** + +```js +const sendWithAuthFx = createEffect(async () => { + await authUserFx(); + + // Incorrect! This should be wrapped in an effect. + await new Promise((resolve) => setTimeout(resolve, 80)); + + // Context is lost here. + await sendMessageFx(); +}); +``` + +For scenarios where an effect might call another effect or perform asynchronous computations, but not both, consider utilizing the [attach](/en/api/effector/attach) method instead for more succinct imperative calls. + +# Methods (#methods) + +## `.getState($store)` (#methods-getState) + +Returns the value of a store in a given `Scope`. + +### Formulae (#methods-getState-formulae) + +```ts +const scope: Scope; +const $value: Store | StoreWritable; + +const value: T = scope.getState($value); +``` + +### Returns (#methods-getState-returns) + +`T` the value of the store + +### Examples (#methods-getState-examples) + +Create two instances of an application, trigger events in them, and test the `$counter` store value in both instances: + +```js +import { createStore, createEvent, fork, allSettled } from "effector"; + +const inc = createEvent(); +const dec = createEvent(); +const $counter = createStore(0); + +$counter.on(inc, (value) => value + 1); +$counter.on(dec, (value) => value - 1); + +const scopeA = fork(); +const scopeB = fork(); + +await allSettled(inc, { scope: scopeA }); +await allSettled(dec, { scope: scopeB }); + +console.log($counter.getState()); // => 0 +console.log(scopeA.getState($counter)); // => 1 +console.log(scopeB.getState($counter)); // => -1 +``` + +[Try it](https://share.effector.dev/0grlV3bA) diff --git a/src/content/docs/en/api/effector/Store.md b/src/content/docs/en/api/effector/Store.md new file mode 100644 index 0000000..ab7d0a0 --- /dev/null +++ b/src/content/docs/en/api/effector/Store.md @@ -0,0 +1,325 @@ +--- +title: Store +keywords: + - store + - unit +description: Store, its methods and properties +redirectFrom: + - /api/effector/Store + - /docs/api/effector/store +--- + +```ts +import { type Store, type StoreWritable } from "effector"; +``` + +_Store_ is an object that holds the state value. Store gets updates when it receives a value that is not equal (`!==`) to the current one and to `undefined`. Store is a [Unit](/en/explanation/glossary#common-unit). Some stores can be [derived](#store-derived). + +# Methods (#methods) + +## `.map(fn)` (#methods-map-fn) + +Creates a derived store. It will call a provided function with the state when the original store updates, and will use the result to update the derived store. + +### Formulae (#methods-map-fn-formulae) + +```ts +const $second = $first.map(fn); +``` + +### Arguments (#methods-map-fn-arguments) + +1. `fn` (_Function_): Function that receives `state` and returns a new state for the derived store. +2. `config` (_Object_): Optional configuration. + +### Returns (#methods-map-fn-returns) + +[_DerivedStore_](/en/api/effector/Store#readonly): New derived store. + +### Examples (#methods-map-fn-examples) + +#### Basic (#methods-map-fn-examples-basic) + +```js +import { createEvent, createStore } from "effector"; + +const changed = createEvent(); +const $title = createStore("").on(changed, (_, newTitle) => newTitle); +const $length = $title.map((title) => title.length); + +$length.watch((length) => { + console.log("new length", length); +}); + +changed("hello"); +changed("world"); +changed("hello world"); +``` + +[Try it](https://share.effector.dev/XGKGMvpF) + +#### SkipVoid (#methods-map-fn-examples-skipVoid) + +```js +const $length = $title.map((title) => title.length, { skipVoid: false }); +``` + +## `.on(trigger, reducer)` (#methods-on-trigger-reducer) + +Updates state when `trigger` is triggered by using a [reducer](/en/explanation/glossary#reducer). + +### Formulae (#methods-on-trigger-reducer-formulae) + +```ts +$store.on(trigger, reducer); +``` + +### Arguments (#methods-on-trigger-reducer-arguments) + +1. `trigger`: _Event_, _Effect_, or another _Store_. +2. `reducer`: _Reducer_: Function that receives `state` and `params` and returns a new state. + +### Returns (#methods-on-trigger-reducer-returns) + +[_Store_](/en/api/effector/Store): Current store. + +### Examples (#methods-on-trigger-reducer-examples) + +#### Basic (#methods-on-trigger-reducer-examples-basic) + +```js +import { createEvent, createStore } from "effector"; + +const $store = createStore(0); +const changed = createEvent(); + +$store.on(changed, (value, incrementor) => value + incrementor); + +$store.watch((value) => { + console.log("updated", value); +}); + +changed(2); +changed(2); +``` + +[Try it](https://share.effector.dev/O0JnDtIl) + +## `.watch(watcher)` (#methods-watch-watcher) + +Calls `watcher` function each time when the store is updated. + +### Formulae (#methods-watch-watcher-formulae) + +```ts +const unwatch = $store.watch(watcher); +``` + +### Arguments (#methods-watch-watcher-arguments) + +1. `watcher`: [_Watcher_](/en/explanation/glossary#watcher): Watcher function that receives the current store state as the first argument. + +### Returns (#methods-watch-watcher-returns) + +[_Subscription_](/en/explanation/glossary#subscription): Unsubscribe function. + +### Examples (#methods-watch-watcher-examples) + +#### Basic (#methods-watch-watcher-examples-basic) + +```js +const add = createEvent(); +const $store = createStore(0).on(add, (state, payload) => state + payload); + +$store.watch((value) => console.log(`current value: ${value}`)); +add(4); +add(3); +``` + +## `.reset(...triggers)` (#methods-reset-triggers) + +Resets store state to the default value. + +### Formulae (#methods-reset-triggers-formulae) + +```ts +$store.reset(...triggers); +``` + +### Arguments (#methods-reset-triggers-arguments) + +1. `triggers`: (_(Event | Effect | Store)[]_): any number of _Events_, _Effects_, or _Stores_. + +### Returns (#methods-reset-triggers-returns) + +[_Store_](/en/api/effector/Store): Current store. + +### Examples (#methods-reset-triggers-examples) + +#### Basic (#methods-reset-triggers-examples-basic) + +```js +import { createEvent, createStore } from "effector"; + +const increment = createEvent(); +const reset = createEvent(); + +const $store = createStore(0) + .on(increment, (state) => state + 1) + .reset(reset); + +$store.watch((state) => console.log("changed", state)); + +increment(); +increment(); +reset(); +``` + +[Try it](https://share.effector.dev/7W8m2Zdg) + +## `.off(trigger)` (#methods-off-trigger) + +Removes reducer for the given `trigger`. + +### Formulae (#methods-off-trigger-formulae) + +```ts +$store.off(trigger); +``` + +### Arguments (#methods-off-trigger-arguments) + +1. `trigger`: _Event_, _Effect_, or _Store_. + +### Returns (#methods-off-trigger-returns) + +[_Store_](/en/api/effector/Store): Current store. + +### Examples (#methods-off-trigger-examples) + +#### Basic (#methods-off-trigger-examples-basic) + +```js +import { createEvent, createStore, merge } from "effector"; + +const changedA = createEvent(); +const changedB = createEvent(); + +const $store = createStore(0); +const changed = merge([changedA, changedB]); + +$store.on(changed, (state, params) => state + params); +$store.off(changed); +``` + +[Try it](https://share.effector.dev/bzdoyLHm) + +# Properties (#properties) + +## `.updates` (#properties-updates) + +### Returns (#properties-updates-returns) + +[_Event_](/en/api/effector/Event): Event that represents updates of the given store. + +### Example (#properties-updates-example) + +```js +import { createStore, is } from "effector"; + +const $clicksAmount = createStore(0); +is.event($clicksAmount.updates); + +$clicksAmount.updates.watch((amount) => { + console.log(amount); +}); +``` + +[Try it](https://share.effector.dev/F5L5kLTE) + +## `.reinit` (#properties-reinit) + +### Returns (#properties-reinit-returns) + +[_Event_](/en/api/effector/Event): Event that can reinitialize a store with a default value. + +### Example (#properties-reinit-example) + +```js +import { createStore, createEvent, sample, is } from "effector"; + +const $counter = createStore(0); +is.event($counter.reinit); + +const increment = createEvent(); + +$counter.reinit(); +console.log($counter.getState()); +``` + +[Try it](https://share.effector.dev/vtJncyYn) + +## `.shortName` (#properties-shortName) + +### Returns (#properties-shortName-returns) + +(_`string`_): ID or short name of the store. + +## `.defaultState` (#properties-defaultState) + +### Returns (#properties-defaultState-returns) + +(_`State`_): Default state of the store. + +### Example (#properties-defaultState-example) + +```ts +const $store = createStore("DEFAULT"); +console.log($store.defaultState === "DEFAULT"); +``` + +# Utility methods (#utility-methods) + +## `.getState()` (#utility-methods-getState) + +Returns the current state of the store. + +### Returns (#utility-methods-getState-returns) + +(_`State`_): Current state of the store. + +### Example (#utility-methods-getState-example) + +```js +import { createEvent, createStore } from "effector"; + +const add = createEvent(); + +const $number = createStore(0).on(add, (state, data) => state + data); + +add(2); +add(3); + +console.log($number.getState()); +``` + +[Try it](https://share.effector.dev/YrnlMuRj) + +# Readonly store (#readonly) + +TBD + +# Types (#types) + +```ts +import { type StoreValue } from "effector"; +``` + +## `StoreValue` (#types-StoreValue) + +Extracts type of `Store` or `StoreWritable` value. + +```ts +const $store: Store; +type Value = StoreValue; +``` diff --git a/src/content/docs/en/api/effector/allSettled.md b/src/content/docs/en/api/effector/allSettled.md new file mode 100644 index 0000000..9d72c77 --- /dev/null +++ b/src/content/docs/en/api/effector/allSettled.md @@ -0,0 +1,92 @@ +--- +title: allSettled +description: Call provided unit in scope and wait for finishing all the triggered effects +redirectFrom: + - /api/effector/allSettled + - /docs/api/effector/allsettled +--- + +# Methods (#methods) + +## `allSettled(unit, {scope, params?})` (#methods-allSettled-unit-scope-params) + +Calls the provided unit within the current scope and wait for all triggered effects to complete. + +### Formulae (#methods-allSettled-unit-scope-params-formulae) + +```ts +allSettled(unit: Event, {scope: Scope, params?: T}): Promise +allSettled(unit: Effect, {scope: Scope, params?: T}): Promise< + | {status: 'done'; value: Done} + | {status: 'fail'; value: Fail} +> +allSettled(unit: Store, {scope: Scope, params?: T}): Promise +``` + +### Arguments (#methods-allSettled-unit-scope-params-arguments) + +1. `unit`: [_Event_](/en/api/effector/Event) or [_Effect_](/en/api/effector/Effect) to be called +2. `scope`: [_Scope_](/en/api/effector/Scope) +3. `params`: params passed to `unit` + +:::info{title="since"} +Return value for effect is supported since [effector 21.4.0](https://changelog.effector.dev/#effector-21-4-0) +::: + +### Examples (#methods-allSettled-unit-scope-params-examples) + +:::tip{title="Contribution"} +TBD + +Please, [open PullRequest](https://github.com/effector/effector) and contribute examples for this section via "Edit this page" link below. +::: + +## `allSettled(scope)` (#methods-allSettled-scope) + +Checks the provided scope for any ongoing computations and wait for their completion. + +### Formulae (#methods-allSettled-scope-formulae) + +```ts +allSettled(scope): Promise +``` + +### Arguments (#methods-allSettled-scope-arguments) + +1. `scope`: [_Scope_](/en/api/effector/Scope) + +:::info{title="since"} +Supported since effector 22.5.0 +::: + +### Examples (#methods-allSettled-scope-examples) + +#### Usage in tests (#methods-allSettled-scope-examples-tests) + +For example, tests that validate the integration with an external reactive API + +```ts +test('integration with externalSource', async () => { + const scope = fork() + + const updated = createEvent() + + sample({ + clock: updated, + target: someOtherLogicStart, + }) + + // 1. Subscribe event to external source + const externalUpdated = scopeBind(updated, {scope}) + externalSource.listen(() => externalUpdates()) + + // 2. Trigger update of external source + externalSource.trigger() + + // 3. Wait for all triggered computations in effector's scope, even though these were not triggered by effector itself + await allSettled(scope) + + // 4. Check anything as usual + expect(...).toBe(...) +}) +``` diff --git a/src/content/docs/en/api/effector/attach.md b/src/content/docs/en/api/effector/attach.md new file mode 100644 index 0000000..3ff8218 --- /dev/null +++ b/src/content/docs/en/api/effector/attach.md @@ -0,0 +1,620 @@ +--- +title: attach +description: Wrapper for effect, which allows to map effect arguments and use data from stores. +redirectFrom: + - /api/effector/attach + - /docs/api/effector/attach +--- + +```ts +import { attach } from "effector"; +``` + +:::info{title="since"} +Available since [effector 20.13.0](https://changelog.effector.dev/#effector-20-13-0). + +Since [effector 22.4.0](https://changelog.effector.dev/#effector-encke-22-4-0), it is available to check whether effect is created via `attach` method — [is.attached](/en/api/effector/is#is-attached). +::: + +Creates new [effects](/en/api/effector/Effect) based on the other effects, [stores](/en/api/effector/Store). Allows mapping params and handling errors. + +Use cases: declarative way to pass values from stores to effects and argument preprocessing. Most useful case is `attach({ source, async effect })`. + +:::tip +The attached effects are the same first-class citizens as the regular effects made by [createEffect](/en/api/effector/createEffect). You should place them in the same files as regular effects, also you can use the same naming strategy. +::: + +# Methods (#methods) + +## `attach({effect})` (#methods-attach-effect) + +:::info{title="since"} +[effector 21.5.0](https://changelog.effector.dev/#effector-21-5-0) +::: + +Create effect which will call `effect` with params as it is. That allows creating separate effects with shared behavior. + +### Formulae (#methods-attach-effect-formulae) + +```ts +const attachedFx = attach({ effect: originalFx }); +``` + +- When `attachedFx` is triggered, then `originalFx` is triggered too +- When `originalFx` is finished (fail/done), then `attachedFx` must be finished with the same state. + +### Arguments (#methods-attach-effect-arguments) + +- `effect` ([_Effect_](/en/api/effector/Effect)): Wrapped effect + +### Returns (#methods-attach-effect-returns) + +[_Effect_](/en/api/effector/Effect): New effect + +### Types (#methods-attach-effect-types) + +```ts +const originalFx: Effect; + +const attachedFx: Effect = attach({ + effect: originalFx, +}); +``` + +In case of this simple variant of `attach`, types of `originalFx` and `attachedFx` will be the same. + +### Examples (#methods-attach-effect-examples) + +It allows to create _local_ copy of the effect, to react only on triggers emitted from the current _local_ code. + +```ts +import { createEffect, attach } from "effector"; + +const originalFx = createEffect((word: string) => { + console.info("Printed:", word); +}); + +const attachedFx = attach({ effect: originalFx }); + +originalFx.watch(() => console.log("originalFx")); +originalFx.done.watch(() => console.log("originalFx.done")); + +attachedFx.watch(() => console.log("attachedFx")); +attachedFx.done.watch(() => console.log("attachedFx.done")); + +originalFx("first"); +// => originalFx +// => Printed: first +// => originalFx.done + +attachedFx("second"); +// => attachedFx +// => originalFx +// Printed: second +// => originalFx.done +// => attachedFx.done +``` + +[Try it](https://share.effector.dev/7Uhk4XfW) + +## `attach({source, effect})` (#methods-attach-source-effect) + +Create effect which will trigger given one with values from `source` stores. + +### Formulae (#methods-attach-source-effect-formulae) + +```ts +const attachedFx = attach({ + source, + effect: originalFx, +}); +``` + +- When `attachedFx` is triggered, read data from `source`, trigger with the data `originalFx` +- When `originalFx` is finished, pass the same resolution (done/fail) into `attachedFx` and finish it + +### Arguments (#methods-attach-source-effect-arguments) + +- `source` ([_Store_](/en/api/effector/Store) | `{[key: string]: Store}`): Store or object with stores, values of which will be passed to the second argument of `mapParams` +- `effect` ([_Effect_](/en/api/effector/Effect)): Original effect + +### Returns (#methods-attach-source-effect-returns) + +[_Effect_](/en/api/effector/Effect): New effect + +### Types (#methods-attach-source-effect-types) + +:::tip +You don't need to explicitly set types for each declaration. The purpose of the following example is to provide a clear understanding. +::: + +In most userland code you will write code like this, without explicit types of the `let`/`const`: + +```ts +const originalFx = createEffect(async () => {}); +const $store = createStore(initialValue); + +const attachedFx = attach({ + source: $store, + effect: originalFx, +}); +``` + +#### Single store + +```ts +const originalFx: Effect; +const $store: Store; + +const attachedFx: Effect = attach({ + source: $store, + effect: originalFx, +}); +``` + +[Try it](https://tsplay.dev/NBJDDN) + +Types of the `source` store and `effect` params must be the same. +But the `attachedFx` will omit the type of params, it means the attached effect not requires any params at all. + +#### Shape of stores + +```ts +const originalFx: Effect<{ a: A; b: B }, Done, Fail>; +const $a: Store
    ; +const $b: Store; + +const attachedFx: Effect = attach({ + source: { a: $a, b: $b }, + effect: originalFx, +}); +``` + +[Try it](https://tsplay.dev/mbE58N) + +Types of the `source` object must be the same as `originalFx` params. But the `attachedFx` will omit the type of params, it means the attached effect not requires any params at all. + +### Examples (#methods-attach-source-effect-examples) + +```ts +const requestPageFx = createEffect<{ page: number; size: number }, string[]>( + async ({ page, size }) => { + console.log("Requested", page); + return page * size; + }, +); + +const $page = createStore(1); +const $size = createStore(20); + +const requestNextPageFx = attach({ + source: { page: $page, size: $size }, + effect: requestPageFx, +}); + +$page.on(requestNextPageFx.done, (page) => page + 1); + +requestPageFx.doneData.watch((position) => console.log("requestPageFx.doneData", position)); + +await requestNextPageFx(); +// => Requested 1 +// => requestPageFx.doneData 20 + +await requestNextPageFx(); +// => Requested 2 +// => requestPageFx.doneData 40 + +await requestNextPageFx(); +// => Requested 3 +// => requestPageFx.doneData 60 +``` + +[Try it](https://share.effector.dev/FGqlrrnw) + +## `attach({source, async effect})` (#methods-attach-source-async-effect) + +:::info{title="since"} +[effector 22.0.0](https://changelog.effector.dev/#effector-22-0-0) +::: + +Creates effect which will call async function with values from the `source` stores. + +### Formulae (#methods-attach-source-async-effect-formulae) + +```ts +const attachedFx = attach({ + source, + async effect(source, params) {}, +}); +``` + +- When `attachedFx` is triggered, read data from the `source`, call `effect` function. +- When `effect` function returns resolved `Promise`, finish `attachedFx` with the data from the function as `attachedFx.done`. +- When `effect` throws exception, or returns rejected `Promise`, finish `attachedFx` with the data from function as `attachedFx.fail`. + +### Arguments (#methods-attach-source-async-effect-arguments) + +- `effect` (_Function_): `(source: Source, params: Params) => Promise | Result` +- `source` ([_Store_](/en/api/effector/Store) | `{[key: string]: Store}`): Store or object with stores, values of which will be passed to the first argument of `effect` + +### Returns (#methods-attach-source-async-effect-returns) + +[_Effect_](/en/api/effector/Effect): New effect + +### Usage with scope (#methods-attach-source-async-effect-scope) + +Any effects called inside `async effect` function will propagate scope. + +```ts +const outerFx = createEffect((count: number) => { + console.log("Hit", count); +}); + +const $store = createStore(0); +const attachedFx = attach({ + source: $store, + async effect(count, _: void) {}, +}); +``` + +**Scope is lost** if there are any asynchronous function calls made: + +```ts +const attachedFx = attach({ + source: $store, + async effect(source) { + // Here is ok, the effect is called + const resultA = await anotherFx(); + + // Be careful: + const resultB = await regularFunction(); + // Here scope is lost. + }, +}); +``` + +To solve this case, you need to just wrap your `regularFunction` into effect: + +```ts +const regularFunctionFx = createEffect(regularFunction); +``` + +### Types (#methods-attach-source-async-effect-types) + +#### Single store (#methods-attach-source-async-effect-types-single-store) + +```ts +const $store: Store; + +const attachedFx: Effect = attach({ + source: $store, + async effect(source, params: Params): Done | Promise {}, +}); +``` + +You need to type explicitly only `params` argument. All other types of arguments should be inferred automatically. Also, you may want to explicitly set the return type of the `effect` function. + +If you want to remove any arguments from the `attachedFx` you need to just remove second argument from `effect` function: + +```ts +const attachedFx: Effect = attach({ + source: $store, + async effect(source) {}, +}); +``` + +#### Multiple stores (#methods-attach-source-async-effect-types-multiple-stores) + +:::tip +For details review [previous section of types](#methods-attach-source-async-effect-types). Here the same logic. +::: + +```ts +// Userland example, without explicit type declarations +const $foo = createStore(100); +const $bar = createStore("demo"); + +const attachedFx = attach({ + source: { foo: $foo, bar: $bar }, + async effect({ foo, bar }, { baz }: { baz: boolean }) { + console.log("Hit!", { foo, bar, baz }); + }, +}); + +attachedFx({ baz: true }); +// => Hit! { foo: 100, bar: "demo", baz: true } +``` + +[Try it](https://tsplay.dev/m3xjbW) + +### Example (#methods-attach-source-async-effect-example) + +:::warning{title="TBD"} +Please, open pull request via "Edit this page" link. +::: + +## `attach({effect, mapParams})` (#methods-attach-effect-mapParams) + +Creates effect which will trigger given one by transforming params by `mapParams` function. + +### Formulae (#methods-attach-effect-mapParams-formulae) + +```ts +const attachedFx = attach({ + effect: originalFx, + mapParams, +}); +``` + +- When `attachedFx` triggered, payload passed into `mapParams` function, then the result of it passed into `originalFx` +- When `originalFx` is finished, then `attachedFx` must be finished with the same resolution (done/fail). +- If `mapParams` throws an exception, then `attachedFx` must be finished with the error as `attachedFx.fail`. But `originalFx` will not be triggered at all. + +### Arguments (#methods-attach-effect-mapParams-arguments) + +- `effect` ([_Effect_](/en/api/effector/Effect)): Wrapped effect +- `mapParams` (`(newParams) => effectParams`): Function which receives new params and maps them to the params of the wrapped `effect`. Works mostly like [event.prepend](/en/api/effector/Event#prepend-fn). Errors happened in `mapParams` function will force attached effect to fail. + +### Returns (#methods-attach-effect-mapParams-returns) + +[_Effect_](/en/api/effector/Effect): New effect + +### Types (#methods-attach-effect-mapParams-types) + +```ts +const originalFx: Effect; + +const attachedFx: Effect = attach({ + effect: originalFx, + mapParams: (params: B): A {}, +}); +``` + +`mapParams` must return the same type `originalFx` receives as params. + +If `attachedFx` must be called without any arguments, then `params` can be safely removed from the `mapParams`: + +```ts +const attachedFx: Effect = attach({ + effect: originalFx, + mapParams: (): A {}, +}); +``` + +[Try it](https://tsplay.dev/wXOYoW) + +But if `mapParams` function throws an exception, it is on your own to check types compatibility, because of TypeScript. + +```ts +const attachedFx: Effect = attach({ + effect: originalFx, + mapParams: (): A { + throw new AnyNonFailType(); // It can be noncompatible with `Fail` type + }, +}); +``` + +### Examples (#methods-attach-effect-mapParams-examples) + +#### Map arguments (#methods-attach-effect-mapParams-examples-map-arguments) + +```ts +const originalFx = createEffect<{ input: number }, void>((a) => a); + +const attachedFx = attach({ + effect: originalFx, + mapParams(a: number) { + return { input: a * 100 }; + }, +}); + +originalFx.watch((params) => console.log("originalFx", params)); + +attachedFx(1); +// => originalFx { input: 100 } +``` + +[Try it](https://share.effector.dev/TFRlrmhm) + +#### Handle exceptions (#methods-attach-effect-mapParams-examples-handle-exceptions) + +```ts +const originalFx = createEffect<{ a: number }, void>((a) => a); + +const attachedFx = attach({ + effect: originalFx, + mapParams(a: number) { + throw new Error("custom error"); + return { a }; + }, +}); + +attachedFx.failData.watch((error) => console.log("attachedFx.failData", error)); + +attachedFx(1); +// => attachedFx.failData +// => Error: custom error +``` + +[Try it](https://share.effector.dev/VYvWQoOk) + +## `attach({source, mapParams, effect})` (#methods-attach-source-mapParams-effect) + +Creates effect which will read values from `source` stores, pass them with params to `mapParams` function and then call `effect` with the result. + +### Formulae (#methods-attach-source-mapParams-effect-formulae) + +:::tip{title="Note"} +This variant of `attach` mostly works like the [attach({effect, mapParams})](#methods-attach-effect-mapParams). The same things are omitted from this section. +::: + +```ts +const attachedFx = attach({ + source, + mapParams, + effect: originalFx, +}); +``` + +- When `attachedFx` triggered, payload passed into `mapParams` function with value from `source` store, then the result of it passed into `originalFx` +- When `originalFx` is finished, then `attachedFx` must be finished with the same resolution (done/fail). +- If `mapParams` throws an exception, then `attachedFx` must be finished with the error as `attachedFx.fail`. But `originalFx` will not be triggered at all. + +### Arguments (#methods-attach-source-mapParams-effect-arguments) + +- `source` ([_Store_](/en/api/effector/Store) | `{[key: string]: Store}`): Store or object with stores, values of which will be passed to the second argument of `mapParams` +- `mapParams` (`(newParams, values) => effectParams`): Function which receives new params and current value of `source` and combines them to the params of the wrapped `effect`. Errors happened in `mapParams` function will force attached effect to fail +- `effect` ([_Effect_](/en/api/effector/Effect)): Wrapped effect + +### Returns (#methods-attach-source-mapParams-effect-returns) + +[_Effect_](/en/api/effector/Effect): New effect + +### Types (#methods-attach-source-mapParams-effect-types) + +:::warning{title="TBD"} +Please, open pull request via "Edit this page" link. +::: + +### Examples (#methods-attach-source-mapParams-effect-examples) + +#### With factory (#methods-attach-source-mapParams-effect-example-with-factory) + +```ts +// ./api/request.ts +import { createEffect, createStore } from "effector"; + +export const backendRequestFx = createEffect(async ({ token, data, resource }) => { + return fetch(`https://example.com/api${resource}`, { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify(data), + }); +}); + +export const $requestsSent = createStore(0); + +$requestsSent.on(backendRequestFx, (total) => total + 1); +``` + +```ts +// ./api/authorized.ts +import { attach, createStore } from "effector"; + +const $token = createStore("guest_token"); + +export const authorizedRequestFx = attach({ + effect: backendRequestFx, + source: $token, + mapParams: ({ data, resource }, token) => ({ data, resource, token }), +}); + +export function createRequest(resource) { + return attach({ + effect: authorizedRequestFx, + mapParams: (data) => ({ data, resource }), + }); +} +``` + +```ts +// ./api/index.ts +import { createRequest } from "./authorized"; +import { $requestsSent } from "./request"; + +const getUserFx = createRequest("/user"); +const getPostsFx = createRequest("/posts"); + +$requestsSent.watch((total) => { + console.log(`client analytics: sent ${total} requests`); +}); + +const user = await getUserFx({ name: "alice" }); +/* +POST https://example.com/api/user +{"name": "alice"} +Authorization: Bearer guest_token +*/ + +// => client analytics: sent 1 requests + +const posts = await getPostsFx({ user: user.id }); +/* +POST https://example.com/api/posts +{"user": 18329} +Authorization: Bearer guest_token +*/ + +// => client analytics: sent 2 requests +``` + +To allow factory works correct, add a path to a `./api/authorized` into `factories` option for Babel plugin: + +```json5 +// .babelrc +{ + plugins: [ + [ + "effector/babel-plugin", + { + factories: ["src/path-to-your-entity/api/authorized"], + }, + ], + ], +} +``` + +## Parameters (#attach-parameters) + +`attach()` also receives extra parameters, you can use it when you need. + +### `name` (#attach-parameters-name) + +```ts +attach({ name: string }); +``` + +It allows us to explicitly set the name of the created attached effect: + +```ts +import { attach } from "effector"; + +const attachedFx = attach({ + name: "anotherUsefulName", + source: $store, + async effect(source, params: Type) { + // ... + }, +}); + +attachedFx.shortName; // "anotherUsefulName" +``` + +This parameter exists in **any variant** of the `attach`. + +### `domain` (#attach-parameters-domain) + +```ts +attach({ domain: Domain }); +``` + +It allows to create effect inside specified domain. + +> Note: this property can only be used with a plain function `effect`. + +```ts +import { createDomain, createStore, attach } from "effector"; + +const reportErrors = createDomain(); +const $counter = createStore(0); + +const attachedFx = attach({ + domain: reportErrors, + source: $counter, + async effect(counter) { + // ... + }, +}); +``` diff --git a/src/content/docs/en/api/effector/babel-plugin.md b/src/content/docs/en/api/effector/babel-plugin.md new file mode 100644 index 0000000..25762cd --- /dev/null +++ b/src/content/docs/en/api/effector/babel-plugin.md @@ -0,0 +1,370 @@ +--- +title: Babel plugin +redirectFrom: + - /api/effector/babel-plugin + - /docs/api/effector/babel-plugin +--- + +Built-in plugin for babel can be used for ssr and debugging. It inserts a name a [Unit](/en/explanation/glossary#unit), +inferred from variable name and `sid` ([Stable IDentifier](/en/explanation/sids)), computed from the location in the source code. + +For example, in case [effects without handlers](/en/api/effector/Effect#use-handler), it improves error messages by +clearly showing in which effect error happened. + +```js +import { createEffect } from "effector"; + +const fetchFx = createEffect(); + +fetchFx(); + +// => no handler used in fetchFx +``` + +[Try it](https://share.effector.dev/Yb8vQ1Ly) + +# Usage (#usage) + +In the simplest case, it can be used without any configuration: + +```json +// .babelrc +{ + "plugins": ["effector/babel-plugin"] +} +``` + +# SID (#sid) + +:::info{title="since"} +[effector 20.2.0](https://changelog.effector.dev/#effector-20-2-0) +::: + +Stable hash identifier for events, effects, stores and domains, preserved between environments, to handle client-server +interaction within the same codebase. + +The crucial value of sid is that it can be autogenerated by `effector/babel-plugin` with default config, and it will be stable between builds. + +:::tip{title="Deep dive explanation"} +If you need the detailed deep-dive explanation about why we need SIDs and how they are used internally, you can find it by [following this link](/en/explanation/sids) +::: + +See [example project](https://github.com/effector/effector/tree/master/examples/worker-rpc) + +```js +// common.js +import { createEffect } from "effector"; + +export const getUser = createEffect({ sid: "GET /user" }); +console.log(getUsers.sid); +// => GET /user +``` + +```js +// worker.js +import { getUsers } from "./common.js"; + +getUsers.use((userID) => fetch(userID)); + +getUsers.done.watch(({ result }) => { + postMessage({ sid: getUsers.sid, result }); +}); + +onmessage = async ({ data }) => { + if (data.sid !== getUsers.sid) return; + getUsers(data.userID); +}; +``` + +```js +// client.js +import { createEvent } from "effector"; +import { getUsers } from "./common.js"; + +const onMessage = createEvent(); + +const worker = new Worker("worker.js"); +worker.onmessage = onMessage; + +getUsers.use( + (userID) => + new Promise((rs) => { + worker.postMessage({ sid: getUsers.sid, userID }); + const unwatch = onMessage.watch(({ data }) => { + if (data.sid !== getUsers.sid) return; + unwatch(); + rs(data.result); + }); + }), +); +``` + +# Configuration (#configuration) + +## `importName` (#configuration-importName) + +Specifying import name or names to process by plugin. Import should be used in the code as specified. + +### Formulae (#configuration-importName-formulae) + +```json +[ + "effector/babel-plugin", + { + "importName": ["effector"] + } +] +``` + +- Type: `string | string[]` +- Default: `['effector', 'effector/compat']` + +## `factories` (#configuration-factories) + +Accepts an array of module names which exports treat as custom factories, therefore, each function call provides a unique prefix for [sids](/en/api/effector/babel-plugin#sid) of units inside them. Used to +SSR([Server Side Rendering](/en/api/effector/Scope)) and it's not required for client-only application. + +:::info{title="since"} +[effector 21.6.0](https://changelog.effector.dev/#effector-21-6-0) +::: + +### Formulae (#configuration-factories-formulae) + +```json +[ + "effector/babel-plugin", + { + "factories": ["path/here"] + } +] +``` + +- Type: `string[]` + +- Factories can have any number of arguments. +- Factories can create any number of units. +- Factories can call any effector methods. +- Factories can call other factories from other modules. +- Modules with factories can export any number of functions. +- Factories should be compiled with `effector/babel-plugin` as well as code which use them. + +### Examples (#configuration-factories-examples) + +```json +// .babelrc +{ + "plugins": [ + [ + "effector/babel-plugin", + { + "factories": ["src/createEffectStatus", "~/createCommonPending"] + } + ] + ] +} +``` + +```js +// ./src/createEffectStatus.js +import { rootDomain } from "./rootDomain"; + +export function createEffectStatus(fx) { + const $status = rootDomain.createStore("init").on(fx.finally, (_, { status }) => status); + + return $status; +} +``` + +```js +// ./src/statuses.js +import { createEffectStatus } from "./createEffectStatus"; +import { fetchUserFx, fetchFriendsFx } from "./api"; + +export const $fetchUserStatus = createEffectStatus(fetchUserFx); +export const $fetchFriendsStatus = createEffectStatus(fetchFriendsFx); +``` + +Import `createEffectStatus` from `'./createEffectStatus'` was treated as factory function, so each store created by it +has its own [sid](/en/api/effector/babel-plugin#sid) and will be handled by [serialize](/en/api/effector/serialize) +independently, although without `factories` they will share the same `sid`. + +## `reactSsr` (#configuration-reactSsr) + +Replaces imports from `effector-react` to `effector-react/scope`. Useful for building both server-side and client-side +builds from the same codebase. + +:::warning{title="Deprecated"} +Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0) the core team recommends deleting this option from `babel-plugin` configuration because [effector-react](/en/api/effector-react) supports SSR by default. +::: + +### Formulae (#configuration-reactSsr-formulae) + +```json +[ + "effector/babel-plugin", + { + "reactSsr": false + } +] +``` + +- Type: `boolean` +- Default: `false` + +## `addNames` (#configuration-addNames) + +Adds name to units factories call. Useful for minification and obfuscation of production builds. + +:::info{title="since"} +[effector 21.8.0](https://changelog.effector.dev/#effector-21-8-0) +::: + +### Formulae (#configuration-addNames-formulae) + +```json +[ + "effector/babel-plugin", + { + "addNames": true + } +] +``` + +- Type: `boolean` +- Default: `true` + +## `addLoc` (#configuration-addLoc) + +Adds location to methods' calls. Used by devtools, for example [effector-logger](https://github.com/effector/logger). + +### Formulae (#configuration-addLoc-formulae) + +```json +[ + "effector/babel-plugin", + { + "addLoc": false + } +] +``` + +- Type: `boolean` +- Default: `false` + +## `debugSids` (#configuration-debugSids) + +Adds a file path and variable name of a unit definition to a sid. Useful for debugging SSR. + +## Formulae (#configuration-debugSids-formulae) + +```json +[ + "effector/babel-plugin", + { + "debugSids": false + } +] +``` + +- Type: `boolean` +- Default: `false` + +## `noDefaults` (#configuration-noDefaults) + +Option for `effector/babel-plugin` for making custom unit factories with clean configuration. + +:::info{title="since"} +[effector 20.2.0](https://changelog.effector.dev/#effector-20-2-0) +::: + +### Formulae (#configuration-noDefaults-formulae) + +```json +[ + "effector/babel-plugin", + { + "noDefaults": false + } +] +``` + +- Type: `boolean` +- Default: `false` + +### Examples (#configuration-noDefaults-examples) + +```json +// .babelrc +{ + "plugins": [ + ["effector/babel-plugin", { "addLoc": true }], + [ + "effector/babel-plugin", + { + "importName": "@lib/createInputField", + "storeCreators": ["createInputField"], + "noDefaults": true + }, + "createInputField" + ] + ] +} +``` + +```js +// @lib/createInputField.js +import { createStore } from "effector"; +import { resetForm } from "./form"; + +export function createInputField(defaultState, { sid, name }) { + return createStore(defaultState, { sid, name }).reset(resetForm); +} +``` + +```js +// src/state.js +import { createInputField } from "@lib/createInputField"; + +const foo = createInputField("-"); +/* + +will be treated as store creator and compiled to + +const foo = createInputField('-', { + name: 'foo', + sid: 'z&si65' +}) + +*/ +``` + +# Usage with Bundlers (#bundlers) + +## Vite + React (SSR) (#bundlers-ViteReactSSR) + +To use with `effector/babel-plugin`, you have to following next steps: + +1. Install `@vitejs/plugin-react` package. +2. `vite.config.js` should be follows: + +> Note: `effector/babel-plugin` is not a package, it is bundled with `effector` + +```js +// vite.config.js +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [ + react({ + babel: { + plugins: ["effector/babel-plugin"], + // Use .babelrc files + babelrc: true, + // Use babel.config.js files + configFile: true, + }, + }), + ], +}); +``` diff --git a/src/content/docs/en/api/effector/clearNode.md b/src/content/docs/en/api/effector/clearNode.md new file mode 100644 index 0000000..0ead7bd --- /dev/null +++ b/src/content/docs/en/api/effector/clearNode.md @@ -0,0 +1,84 @@ +--- +title: clearNode +description: Method for destroying stores, events, effects, subscriptions, and domains +redirectFrom: + - /api/effector/clearNode + - /docs/api/effector/clearnode +--- + +```ts +import { clearNode } from "effector"; +``` + +Method for destroying stores, events, effects, subscriptions, and domains. + +# Methods (#methods) + +## `clearNode(unit, config?)` (#methods-clearNode-unit-config) + +### Formulae (#methods-clearNode-unit-config-formulae) + +```ts +clearNode(unit, config?: {deep?: boolean}): void +``` + +### Arguments (#methods-clearNode-unit-config-arguments) + +1. `unit` ([_Store_](/en/api/effector/Store)/[_Event_](/en/api/effector/Event)/[_Effect_](/en/api/effector/Effect)/[_Domain_](/en/api/effector/Domain)/[_Scope_](/en/api/effector/Scope)): unit to be erased. +2. `config: {}` (optional): config object. + - `deep?: boolean` (optional): erase node _and_ all of its computed values. + +### Returns (#methods-clearNode-unit-config-returns) + +`void` + +### Examples (#methods-clearNode-unit-config-examples) + +#### Simple (#methods-clearNode-unit-config-examples-simple) + +```js +import { createStore, createEvent, clearNode } from "effector"; + +const inc = createEvent(); +const $store = createStore(0).on(inc, (x) => x + 1); + +inc.watch(() => console.log("inc called")); +$store.watch((x) => console.log("store state: ", x)); +// => store state: 0 +inc(); +// => inc called +// => store state: 1 +clearNode($store); +inc(); +// => inc called +``` + +[Try it](https://share.effector.dev/WjuSl6aN) + +#### Deep clear (#methods-clearNode-unit-config-examples-deep) + +```js +import { createStore, createEvent, clearNode } from "effector"; + +const inc = createEvent(); +const trigger = inc.prepend(() => {}); +const $store = createStore(0).on(inc, (x) => x + 1); + +trigger.watch(() => console.log("trigger called")); +inc.watch(() => console.log("inc called")); +$store.watch((x) => console.log("store state: ", x)); +// => store state: 0 +trigger(); +// => trigger called +// => inc called +// => store state: 1 +clearNode(trigger, { deep: true }); +trigger(); +// no reaction +inc(); +// no reaction! +// all units, which depend on trigger, are erased +// including inc and store, because it depends on inc +``` + +[Try it](https://share.effector.dev/EkETZtKI) diff --git a/src/content/docs/en/api/effector/combine.mdx b/src/content/docs/en/api/effector/combine.mdx new file mode 100644 index 0000000..fc94cc4 --- /dev/null +++ b/src/content/docs/en/api/effector/combine.mdx @@ -0,0 +1,167 @@ +--- +title: combine +redirectFrom: + - /api/effector/combine + - /docs/api/effector/combine +--- + +import LiveDemo from "../../../../../components/LiveDemo.jsx"; + +This method allows you to get state from each passed store and **combine** it to a single value and save into a single store, that updates every time like each passed store. + +:::warning{title="Caution"} +`combine` returns not just a common store. Instead, it returns [DerivedStore](/en/api/effector/Store#readonly), it cannot be modified by the events or used as `target` in [sample](/en/api/effector/sample). +::: + +# Methods (#methods) + +## `combine(...stores, fn)` (#methods-combine-stores-fn) + +### Formulae (#methods-combine-stores-fn-formulae) + +```ts +const $first: Store +const $second: StoreWritable +const $third: Store | StoreWritable + +$result: Store = combine( + $first, $second, $third, ..., + (first: A, second: B, third: C, ...) => result +) +``` + +- After call `combine`, state of each store is extracted and passed to function arguments, `result` of a function call will be state of store `$result` +- Any number of stores can be passed to `combine`, but the latest argument always should be function-reducer that returns new state +- If function returned the same `result` as previous, store `$result` will not be triggered +- If several stores updated at the same time (during one tick) there will be single call of function and single update of `$result` store + +### Returns (#methods-combine-stores-fn-returns) + +[_DerivedStore_](/en/api/effector/Store#readonly): New derived store + +### Examples (#methods-combine-stores-fn-examples) + +import demo_combineStoresFn from "./demo/_combine-stores-fn.live.js?raw"; + + + +## `combine({ A, B, C }, fn?)` (#methods-combine-object-fn) + +:::info +Formerly known as `createStoreObject` +::: + +### Formulae (#methods-combine-object-fn-formulae) + +```ts +const $first: Store; +const $second: StoreWritable; +const $third: Store | StoreWritable; + +$result: Store<{ a: A; b: B; c: C }> = combine({ a: $first, b: $second, c: $third }); +``` + +- Read state from stores `$first`, `$second`, `$third` and assign it to properties `a`, `b`, `c` accordingly, that object will be saved to `$result` store +- Store `$result` contain object `{a, b, c}` and will be updated on each update of passed stores +- If several stores updated at the same time (during one tick) there will be single update of `$result` store + +### Formulae with `fn` (#methods-combine-object-fn-formulae-fn) + +```ts +const $first: Store; +const $second: StoreWritable; +const $third: Store | StoreWritable; + +$result: Store = combine( + { a: $first, b: $second, c: $third }, + ({ a, b, c }: { a: A; b: B; c: C }): D => result, +); +``` + +- Read state from stores `$first`, `$second`, `$third` and assign it to properties `a`, `b`, `c` accordingly, calls function with that object +- The `result` of the function call saved in `$result` store +- If function returned the same `result` as previous, store `$result` will not be triggered +- If several stores updated at the same time (during one tick) there will be single call of function and single update of `$result` store + +### Returns (#methods-combine-object-fn-returns) + +[_DerivedStore_](/en/api/effector/Store#readonly): New derived store + +### Examples (#methods-combine-object-fn-examples) + +import demo_combineObjectFn from "./demo/_combine-object-fn.live.js?raw"; + + + +## `combine([A, B, C], fn?)` (#methods-combine-array-fn) + +### Formulae (#methods-combine-array-fn-formulae) + +```ts +const $first: Store; +const $second: StoreWritable; +const $third: Store | StoreWritable; + +$result: Store = combine([$first, $second, $third], ([A, B, C]): D => result); +``` + +- Read state from stores `$first`, `$second`, `$third` and assign it to array with the same order as passed stores, call function with that array +- The `result` of the function call saved in `$result` store +- If function returned the same `result` as previous, store `$result` will not be triggered +- If several stores updated at the same time (during one tick) there will be single call of function and single update of `$result` store + +### Formulae without `fn` (#methods-combine-array-fn-formulae-no-fn) + +```ts +const $first: Store; +const $second: StoreWritable; +const $third: Store | StoreWritable; + +$result: Store<[A, B, C]> = combine([$first, $second, $third]); +``` + +- Read state from stores `$first`, `$second`, `$third` and assign it to array with the same order as passed stores, that array will be saved to `$result` store +- Store `$result` will be updated on each update of passed stores +- If several stores updated at the same time (during one tick) there will be single update of `$result` store + +### Returns (#methods-combine-array-fn-returns) + +[_DerivedStore_](/en/api/effector/Store#readonly): New derived store + +### Examples (#methods-combine-array-fn-examples) + +import demo_combineArrayFn from "./demo/_combine-array-fn.live.js?raw"; + + + +## `combine` with primitives and objects (#methods-combine-primitives-objects) + +It works the same as before. Now primitives and objects can be used in `combine`, and `combine` will not be triggered. Effector will not track mutations of objects and primitives. + +### Examples (#methods-combine-primitives-objects-examples) + +import demo_combineNonStoresFn from "./demo/_combine-non-stores-fn.live.js?raw"; + + + +# Parameters (#parameters) + +All overloads of `combine` with `fn` provided are also supporting optional configuration object as the last parameter. + +## `.skipVoid` (#parameters-skipVoid) + +Flag to control how specifically store should handle `undefined` value _(since `effector 23.0.0`)_. If set to `false` - store will use `undefined` as a value. If set to `true` (deprecated), store will read `undefined` as a "skip update" command and will do nothing + +### Formulae (#parameters-skipVoid-formulae) + +```ts +combine($a, $b, callback, { skipVoid: true }); +``` + +- Type: `boolean` + +### Examples (#parameters-skipVoid-examples) + +```js +const $withFn = combine($a, $b, (a, b) => a || b, { skipVoid: false }); +``` diff --git a/src/content/docs/en/api/effector/createApi.md b/src/content/docs/en/api/effector/createApi.md new file mode 100644 index 0000000..a1c76d5 --- /dev/null +++ b/src/content/docs/en/api/effector/createApi.md @@ -0,0 +1,57 @@ +--- +title: createApi +redirectFrom: + - /api/effector/createApi + - /docs/api/effector/createApi +--- + +```ts +import { createApi } from "effector"; +``` + +`createApi` is a shortcut for generating [events](/en/api/effector/Event) connected to a [store](/en/api/effector/Store) by supplying an object with [_reducers_](/en/explanation/glossary#reducer) for these events. If the source `store` is part of a [domain](/en/api/effector/Domain), then the newly created events will also be within that domain. + +# Methods (#methods) + +## `createApi(store, api)` (#methods-createApi-store-api) + +### Formulae (#methods-createApi-store-api-formulae) + +```ts +createApi(store, api): objectWithEvents +``` + +### Arguments (#methods-createApi-store-api-arguments) + +1. `store` [_Store_](/en/api/effector/Store) +2. `api` (_Object_) An object with [_reducers_](/en/explanation/glossary#reducer) + +### Returns (#methods-createApi-store-api-returns) + +(_Object_) An object with [events](/en/api/effector/Event) + +### Examples (#methods-createApi-store-api-examples) + +```js +import { createStore, createApi } from "effector"; + +const $playerPosition = createStore(0); + +// Creating events and attaching them to the store +const api = createApi($playerPosition, { + moveLeft: (pos, offset) => pos - offset, + moveRight: (pos, offset) => pos + offset, +}); + +$playerPosition.watch((pos) => { + console.log("position", pos); +}); +// => position 0 + +api.moveRight(10); +// => position 10 +api.moveLeft(5); +// => position 5 +``` + +[Try it](https://share.effector.dev/SjVy8dzF) diff --git a/src/content/docs/en/api/effector/createDomain.md b/src/content/docs/en/api/effector/createDomain.md new file mode 100644 index 0000000..709e2fc --- /dev/null +++ b/src/content/docs/en/api/effector/createDomain.md @@ -0,0 +1,47 @@ +--- +title: createDomain +description: createDomain is a method for creating a domain +redirectFrom: + - /api/effector/createDomain + - /docs/api/effector/createDomain +--- + +```ts +import { createDomain, type Domain } from "effector"; +``` + +# Methods (#methods) + +## `createDomain(name?)` (#methods-createDomain-name) + +Creates a [domain](/en/api/effector/Domain) + +### Formulae (#methods-createDomain-name-formulae) + +```typescript +createDomain(name?): Domain +``` + +### Arguments (#methods-createDomain-name-arguments) + +1. `name`? (_string_): domain name. Useful for debugging + +### Returns (#methods-createDomain-name-returns) + +[_Domain_](/en/api/effector/Domain): New domain + +### Examples (#methods-createDomain-name-examples) + +```js +import { createDomain } from "effector"; + +const domain = createDomain(); // Unnamed domain +const httpDomain = createDomain("http"); // Named domain + +const statusCodeChanged = httpDomain.createEvent(); +const downloadFx = httpDomain.createEffect(); +const apiDomain = httpDomain.createDomain(); // nested domain +const $data = httpDomain.createStore({ status: -1 }); +``` + +[Try it](https://share.effector.dev/GMpjINHa) diff --git a/src/content/docs/en/api/effector/createEffect.md b/src/content/docs/en/api/effector/createEffect.md new file mode 100644 index 0000000..94d8ffd --- /dev/null +++ b/src/content/docs/en/api/effector/createEffect.md @@ -0,0 +1,187 @@ +--- +title: createEffect +description: Method for creating an effect +redirectFrom: + - /api/effector/createEffect + - /docs/api/effector/createEffect +--- + +```ts +import { createEffect, type Effect } from "effector"; +``` + +Method for creating an [effect](/en/api/effector/Effect). + +# Methods (#methods) + +## `createEffect(handler)` (#methods-createEffect-handler) + +Creates an effect with the given handler. + +### Formulae (#methods-createEffect-handler-formulae) + +```typescript +createEffect(handler?): Effect +``` + +### Arguments (#methods-createEffect-handler-arguments) + +1. `handler` (_Function_): Function to handle effect calls, can also be set using [`.use(handler)`](/en/api/effector/Effect#methods-use-handler). + +### Returns (#methods-createEffect-handler-returns) + +[_Effect_](/en/api/effector/Effect): A new effect. + +:::tip{title="Reminder"} +You must provide a handler either in [`createEffect`](/en/api/effector/createEffect) or in [`.use`](/en/api/effector/Effect#methods-use-handler) method later; otherwise, the effect will throw with the `no handler used in _%effect name%_` error. +::: + +:::info{title="since"} +[effector 21.3.0](https://changelog.effector.dev/#effector-21-3-0) +::: + +### Examples (#methods-createEffect-handler-examples) + +#### Create effect with handler (#methods-createEffect-handler-examples-create-effect-with-handler) + +```js +import { createEffect } from "effector"; + +const fetchUserReposFx = createEffect(async ({ name }) => { + const url = `https://api.github.com/users/${name}/repos`; + const req = await fetch(url); + return req.json(); +}); + +fetchUserReposFx.done.watch(({ params, result }) => { + console.log(result); +}); + +await fetchUserReposFx({ name: "zerobias" }); +``` + +[Try it](https://share.effector.dev/7K23rdej) + +#### Change state on effect completion (#methods-createEffect-handler-examples-change-state-on-effect-completion) + +```js +import { createStore, createEffect } from "effector"; + +const fetchUserReposFx = createEffect(async ({ name }) => { + const url = `https://api.github.com/users/${name}/repos`; + const req = await fetch(url); + return req.json(); +}); + +const $repos = createStore([]).on(fetchUserReposFx.doneData, (_, repos) => repos); + +$repos.watch((repos) => { + console.log(`${repos.length} repos`); +}); +// => 0 repos + +await fetchUserReposFx({ name: "zerobias" }); +// => 26 repos +``` + +[Try it](https://share.effector.dev/uAJFC1XM) + +#### Set handler to effect after creating (#methods-createEffect-handler-examples-set-handler-to-effect-after-creating) + +```js +import { createEffect } from "effector"; + +const fetchUserReposFx = createEffect(); + +fetchUserReposFx.use(async ({ name }) => { + const url = `https://api.github.com/users/${name}/repos`; + const req = await fetch(url); + return req.json(); +}); + +await fetchUserReposFx({ name: "zerobias" }); +``` + +[Try it](https://share.effector.dev/e1QPH9Uq) + +#### Watch effect status (#methods-createEffect-handler-examples-watch-effect-status) + +```js +import { createEffect } from "effector"; + +const fetchUserReposFx = createEffect(async ({ name }) => { + const url = `https://api.github.com/users/${name}/repos`; + const req = await fetch(url); + return req.json(); +}); + +fetchUserReposFx.pending.watch((pending) => { + console.log(`effect is pending?: ${pending ? "yes" : "no"}`); +}); + +fetchUserReposFx.done.watch(({ params, result }) => { + console.log(params); // {name: 'zerobias'} + console.log(result); // resolved value +}); + +fetchUserReposFx.fail.watch(({ params, error }) => { + console.error(params); // {name: 'zerobias'} + console.error(error); // rejected value +}); + +fetchUserReposFx.finally.watch(({ params, status, result, error }) => { + console.log(params); // {name: 'zerobias'} + console.log(`handler status: ${status}`); + + if (error) { + console.error("handler rejected", error); + } else { + console.log("handler resolved", result); + } +}); + +await fetchUserReposFx({ name: "zerobias" }); +``` + +[Try it](https://share.effector.dev/LeurvtYA) + +## `createEffect(config)` (#methods-createEffect-config) + +Creates an effect with handler and name from a given config object. + +### Formulae (#methods-createEffect-config-formulae) + +```typescript +createEffect({ handler, name }): Effect +``` + +### Arguments (#methods-createEffect-config-arguments) + +1. `config?: {}` (_Object_): Effect configuration. + - `handler` (_Function_): Function to handle effect calls, can also be set using [`.use(handler)`](#use). + - `name?` (_string_): Optional effect name. + +### Returns (#methods-createEffect-config-returns) + +[_Effect_](/en/api/effector/Effect): A new effect. + +### Examples (#methods-createEffect-config-examples) + +#### Create named effect (#methods-createEffect-config-examples-create-named-effect) + +```js +import { createEffect } from "effector"; + +const fetchUserReposFx = createEffect({ + name: "fetch user repositories", + async handler({ name }) { + const url = `https://api.github.com/users/${name}/repos`; + const req = await fetch(url); + return req.json(); + }, +}); + +await fetchUserReposFx({ name: "zerobias" }); +``` + +[Try it](https://share.effector.dev/GynSzKee) diff --git a/src/content/docs/en/api/effector/createEvent.md b/src/content/docs/en/api/effector/createEvent.md new file mode 100644 index 0000000..4a13017 --- /dev/null +++ b/src/content/docs/en/api/effector/createEvent.md @@ -0,0 +1,95 @@ +--- +title: createEvent +description: createEvent is a method for creating an event +redirectFrom: + - /api/effector/createEvent + - /docs/api/effector/createEvent +--- + +```ts +import { createEvent } from "effector"; +``` + +# Methods (#methods) + +## `createEvent(name?)` (#methods-createEvent-name) + +Method for creating an [event](/en/api/effector/Event). + +### Formulae (#methods-createEvent-name-formulae) + +```ts +createEvent(name?): Event +createEvent(name?): Event +``` + +### Arguments (#methods-createEvent-name-arguments) + +1. `name`? (_string_): Event name + +### Returns (#methods-createEvent-name-returns) + +[_EventCallable_](/en/api/effector/Event#eventCallable): New event + +### Notes (#methods-createEvent-name-notes) + +[Event](/en/api/effector/Event) – it is a function which allows to change state when called (see [simple example](#methods-createEvent-name-examples-simple)) also it can be a good way to extract data (see [map and watch example](#methods-createEvent-name-examples-map-watch)). Also, it allows us to send data to another event or effect via effector operators. + +### Examples (#methods-createEvent-name-examples) + +#### Simple (#methods-createEvent-name-examples-simple) + +```js +import { createStore, createEvent } from "effector"; + +const incrementBy = createEvent(); +const resetCounter = createEvent(); +const $counter = createStore(0); + +$counter.on(incrementBy, (counter, number) => counter + number).reset(resetCounter); + +$counter.watch((counter) => { + console.log("counter is now", counter); +}); +// => counter is now 0 + +incrementBy(10); +// => counter is now 10 + +incrementBy(10); +// => counter is now 20 + +incrementBy(10); +// => counter is now 30 + +resetCounter(); +// => counter is now 0 +``` + +[Try it](https://share.effector.dev/oFkPG4yJ) + +We created a store `$counter` and an event `incrementBy`, and started watching the store.
    +Notice the function call `incrementBy(10)`. Whenever you will call `incrementBy(10)`, you can look at the console and see how state of `$counter` changes. + +#### Using `.map` and `.watch` (#methods-createEvent-name-examples-map-watch) + +```js +import { createEvent } from "effector"; + +const fullNameReceived = createEvent(); + +const firstNameReceived = fullNameReceived.map((fullName) => fullName.split(" ")[0]); +const lastNameReceived = fullNameReceived.map((fullName) => fullName.split(" ")[1]); +const firstNameUppercaseReceived = firstNameReceived.map((firstName) => firstName.toUpperCase()); + +firstNameReceived.watch((firstName) => console.log("First name", firstName)); +lastNameReceived.watch((lastName) => console.log("Last name", lastName)); +firstNameUppercaseReceived.watch((firstName) => console.log("Upper case", firstName)); + +fullNameReceived("John Doe"); +// => First name John +// => Last name Doe +// => Upper case JOHN +``` + +[Try it](https://share.effector.dev/TJWghQ2z) diff --git a/src/content/docs/en/api/effector/createStore.md b/src/content/docs/en/api/effector/createStore.md new file mode 100644 index 0000000..2d29eff --- /dev/null +++ b/src/content/docs/en/api/effector/createStore.md @@ -0,0 +1,265 @@ +--- +title: createStore +description: createStore is a method for creating a store +redirectFrom: + - /api/effector/createStore + - /docs/api/effector/createStore +--- + +```ts +import { createStore, type Store, type StoreWritable } from "effector"; +``` + +# Methods (#methods) + +## `createStore(defaultState)` (#methods-createStore-defaultState) + +Method for creating a [store](/en/api/effector/Store). + +### Formulae (#methods-createStore-defaultState-formulae) + +```ts +createStore(defaultState: T): StoreWritable +``` + +### Arguments (#methods-createStore-defaultState-arguments) + +1. `defaultState` (_State_): Default state + +### Throws (#methods-createStore-defaultState-throws) + +#### unit call from pure function is not supported, use operators like sample instead (#methods-createStore-defaultState-throws-unit-call-from-pure) + +> Since: effector 23.0.0 + +Occurs when events or effects are called from [pure functions](/en/explanation/glossary#purity), like updateFilter: + +```ts +const someHappened = createEvent(); +const $counter = createStore(0, { + updateFilter(a, b) { + someHappened(a); // THROWS! + return a < b; + }, +}); +``` + +To resolve this, use `sample`: + +```ts +const someHappened = createEvent(); +const $counter = createStore(0, { + updateFilter(a, b) { + return a < b; + }, +}); + +sample({ + clock: $counter, + target: someHappened, +}); +``` + +### Returns (#methods-createStore-defaultState-returns) + +[_Store_](/en/api/effector/Store): New store + +### Examples (#methods-createStore-defaultState-examples) + +#### Basic (#methods-createStore-defaultState-examples-basic) + +```js +import { createEvent, createStore } from "effector"; + +const addTodo = createEvent(); +const clearTodoList = createEvent(); + +const $todos = createStore([]) + // Will update store when addTodo is fired + .on(addTodo, (list, todo) => [...list, todo]) + // Will reset store to default state when clearTodos is fired + .reset(clearTodoList); + +// Create mapped store +const $selectedTodos = $todos.map((todos) => { + return todos.filter((todo) => todo.selected); +}); + +// Log initial store value and each change +$todos.watch((todos) => { + console.log("todos", todos); +}); +// => todos [] + +addTodo("go shopping"); +// => todos ['go shopping'] + +addTodo("go to the gym"); +// => todos ['go shopping', 'go to the gym'] + +clearTodoList(); +// => todos [] +``` + +[Try it](https://share.effector.dev/MNibrAFC) + +## `createStore(defaultState, config)` (#methods-createStore-defaultState-config) + +Method for creating a [store](/en/api/effector/Store) but with configuration. + +### Formulae (#methods-createStore-defaultState-config-formulae) + +```ts +createStore(defaultState: T, config: { + name?: string + updateFilter?: (update: T, current: T) => boolean + skipVoid?: boolean + serialize?: 'ignore' | { + write: (state: State) => SerializedState + read: (json: SerializedState) => State + } +}): StoreWritable +``` + +### Arguments (#methods-createStore-defaultState-config-arguments) + +1. `defaultState` (_State_): Default state +2. `config` (_Object_): Optional configuration + - `name` (_String_): Name for the store. Babel plugin can set it from the variable name, if not passed explicitly in config. + - `updateFilter` (_Function_): Function that prevents store from updating when it returns `false`. Accepts updated state as the first argument and current state as the second argument. Redundant for most cases since store already ensures that update is not `undefined` and not equal (`!==`) to current state _(since `effector 21.8.0`)_ + - `serialize: 'ignore'`: Option to disable store serialization when [serialize](/en/api/effector/serialize) is called _(since `effector 22.0.0`)_ + - `serialize` (_Object_): Configuration object to handle store state serialization in custom way. `write` – called on [serialize](/en/api/effector/serialize), transforms value to JSON value – primitive type or plain object/array. `read` – parse store state from JSON value, called on [fork](/en/api/effector/fork), if provided `values` is the result of `serialize` call. + - `domain`: (_Domain_): Domain to attach store to after creation. + - `skipVoid`: (_boolean_): Flag to control how specifically store should handle `undefined` value _(since `effector 23.0.0`)_. If set to `false` - store will use `undefined` as a value. If set to `true` (deprecated), store will interpret `undefined` as a "skip update" command and will do nothing. + +### Throws (#methods-createStore-defaultState-config-throws) + +The same behaviour like for regular [`createStore(defaultState)`](#methods-createStore-defaultState-throws). + +### Returns (#methods-createStore-defaultState-config-returns) + +[_Store_](/en/api/effector/Store): New store + +### Examples (#methods-createStore-defaultState-config-examples) + +#### With `updateFilter` (#methods-createStore-defaultState-examples-updateFilter) + +```js +import { createEvent, createStore, sample } from "effector"; + +const punch = createEvent(); +const veryStrongHit = createEvent(); + +const $lastPunchStrength = createStore(0, { + // If store should be updated with strength less than 400 kg + // update will be skipped + updateFilter: (strength) => strength >= 400, +}); + +$lastPunchStrength.on(punch, (_, strength) => strength); + +// Each store update should trigger event `veryStrongHit` +sample({ clock: $lastPunchStrength, target: veryStrongHit }); + +// Watch on store prints initial state +$lastPunchStrength.watch((strength) => console.log("Strength: %skg", strength)); +// => Strength: 0kg + +veryStrongHit.watch((strength) => { + console.log("Wooow! It was very strong! %skg", strength); +}); + +punch(200); // updateFilter prevented update +punch(300); // Same here, store doesn't update, value remains `0` +punch(500); // Yeeah! updateFilter allows store update +// => Strength: 500kg +// => Wooow! It was very strong! 500kg +punch(100); // No update as well +``` + +[Try it](https://share.effector.dev/rtxfqObf) + +#### With `serialize: ignore` (#methods-createStore-defaultState-examples-serializeIgnore) + +```js +import { createEvent, createStore, serialize, fork, allSettled } from "effector"; + +const readPackage = createEvent(); + +const $name = createStore(""); +const $version = createStore(0, { serialize: "ignore" }); + +$name.on(readPackage, (_, { name }) => name); +$version.on(readPackage, (_, { version }) => version); + +// Watchers always called for scoped changes +$name.watch((name) => console.log("name '%s'", name)); +$version.watch((version) => console.log("version %s", version)); +// => name '' +// => version 0 + +// Please, note, `fork()` call doesn't trigger watches +// In the opposit of `hydrate()` call +const scope = fork(); + +// By default serialize saves value only for the changed stores +// Review `onlyChanges` option https://effector.dev/api/effector/serialize +const values = serialize(scope); +console.log(values); +// => {} + +// Let's change our stores +await allSettled(readPackage, { + scope, + params: { name: "effector", version: 22 }, +}); +// => name 'effector' +// => version 22 + +const actualValues = serialize(scope); +console.log(actualValues); +// => {n74m6b: "effector"} +// `$version` store has `serialize: ignore`, so it's not included +``` + +[Try it](https://share.effector.dev/aLKAHDOM) + +#### Custom `serialize` configuration (#methods-createStore-defaultState-examples-customSerialize) + +```ts +import { createEvent, createStore, serialize, fork, allSettled } from "effector"; + +const saveDate = createEvent(); +const $date = createStore(null, { + // Date object is automatically serialized to ISO date string by JSON.stringify + // but it is not parsed to Date object by JSON.parse + // which will lead to a mismatch during server side rendering + // + // Custom `serialize` config solves this issue + serialize: { + write: (dateOrNull) => (dateOrNull ? dateOrNull.toISOString() : dateOrNull), + read: (isoStringOrNull) => (isoStringOrNull ? new Date(isoStringOrNull) : isoStringOrNull), + }, +}).on(saveDate, (_, p) => p); + +const serverScope = fork(); + +await allSettled(saveDate, { scope: serverScope, params: new Date() }); + +const serverValues = serialize(serverScope); +// `serialize.write` of `$date` store is called + +console.log(serverValues); +// => { nq1e2rb: "2022-11-05T15:38:53.108Z" } +// Date object saved as ISO string + +const clientScope = fork({ values: serverValues }); +// `serialize.read` of `$date` store is called + +const currentValue = clientScope.getState($date); +console.log(currentValue); +// => Date 11/5/2022, 10:40:13 PM +// ISO date string is parsed back to Date object +``` + +[Try it](https://share.effector.dev/YFkUlqPv) diff --git a/src/content/docs/en/api/effector/createWatch.md b/src/content/docs/en/api/effector/createWatch.md new file mode 100644 index 0000000..91cf84c --- /dev/null +++ b/src/content/docs/en/api/effector/createWatch.md @@ -0,0 +1,68 @@ +--- +title: createWatch +redirectFrom: + - /docs/api/effector/createwatch +--- + +```ts +import { createWatch } from "effector"; +``` + +# Methods (#methods) + +## `createWatch(config)` (#methods-createWatch-config) + +Creates a subscription on unit (store, event, or effect). + +### Formulae (#methods-createWatch-config-formulae) + +```ts +createWatch(config: { + unit: Unit + fn: (payload: T) => void + scope?: Scope +}): Subscription +``` + +### Arguments (#methods-createWatch-config-arguments) + +1. `config` (_Object_): Configuration + - `unit` (_Unit_): Target unit (store, event of effect) that will be watched + - `fn` (_Function_): Function that will be called when the unit is triggered. Accepts the unit's payload as the first argument. + - `scope` ([_Scope_](/en/api/effector/Scope)): An optional scope object (forked instance) to restrict watcher calls on particular scope. + +### Returns (#methods-createWatch-config-returns) + +[_Subscription_](/en/explanation/glossary#subscription): Unsubscribe function + +### Examples (#methods-createWatch-config-examples) + +#### With scope (#methods-createWatch-config-examples-scope) + +```js +import { createWatch, createEvent, fork, allSettled } from "effector"; + +const changeName = createEvent(); + +const scope = fork(); + +const unwatch = createWatch({ unit: changeName, scope, fn: console.log }); + +await allSettled(changeName, { scope, params: "John" }); // output: John +changeName("John"); // no output +``` + +#### Without scope (#methods-createWatch-config-examples-no-scope) + +```js +import { createWatch, createEvent, fork, allSettled } from "effector"; + +const changeName = createEvent(); + +const scope = fork(); + +const unwatch = createWatch({ unit: changeName, fn: console.log }); + +await allSettled(changeName, { scope, params: "John" }); // output: John +changeName("John"); // output: John +``` diff --git a/src/content/docs/en/api/effector/demo/_combine-array-fn.live.js b/src/content/docs/en/api/effector/demo/_combine-array-fn.live.js new file mode 100644 index 0000000..293ac80 --- /dev/null +++ b/src/content/docs/en/api/effector/demo/_combine-array-fn.live.js @@ -0,0 +1,11 @@ +import { createStore, combine } from "effector"; + +const $r = createStore(255); +const $g = createStore(0); +const $b = createStore(255); + +const $color = combine([$r, $g, $b]); +$color.watch(console.log); // => [255, 0, 255] + +const $sum = combine([$r, $g, $b], ([r, g, b]) => r + g + b); +$sum.watch(console.log); // => 510 diff --git a/src/content/docs/en/api/effector/demo/_combine-non-stores-fn.live.js b/src/content/docs/en/api/effector/demo/_combine-non-stores-fn.live.js new file mode 100644 index 0000000..88d2818 --- /dev/null +++ b/src/content/docs/en/api/effector/demo/_combine-non-stores-fn.live.js @@ -0,0 +1,22 @@ +import { createStore, combine } from "effector"; + +const $a = createStore("a"); +const b = 2; +const c = [false]; +const d = { value: 1 }; + +const $resultUsingComa = combine($a, b, c, d); +const $resultUsingArray = combine([$a, b, c, d]); +const $resultUsingObject = combine({ $a, b, c, d }); + +const $withFn = combine($a, b, c, d, (a, b) => ({ a, b })); + +$resultUsingComa.watch(console.log); // => ["a", 2, [false], {value: 1}] +$resultUsingArray.watch(console.log); // => ["a", 2, [false], {value: 1}] +$resultUsingObject.watch(console.log); // => {$a: "a", b: 2, c: [false], d: {value: 1}} +$withFn.watch(console.log); // => {a: "a", b: 2} + +// will not trigger combine, but object and array will be changed because of reference +// uncomment the code below to see changes +// c.push(true) +// d.value = 2 diff --git a/src/content/docs/en/api/effector/demo/_combine-object-fn.live.js b/src/content/docs/en/api/effector/demo/_combine-object-fn.live.js new file mode 100644 index 0000000..d46156a --- /dev/null +++ b/src/content/docs/en/api/effector/demo/_combine-object-fn.live.js @@ -0,0 +1,11 @@ +import { createStore, combine } from "effector"; + +const $r = createStore(255); +const $g = createStore(0); +const $b = createStore(255); + +const $color = combine({ r: $r, g: $g, b: $b }); +$color.watch(console.log); // => {r: 255, b: 0, b: 255} + +const $sum = combine({ r: $r, g: $g, b: $b }, ({ r, g, b }) => r + g + b); +$sum.watch(console.log); // => 510 diff --git a/src/content/docs/en/api/effector/demo/_combine-stores-fn.live.js b/src/content/docs/en/api/effector/demo/_combine-stores-fn.live.js new file mode 100644 index 0000000..09bf515 --- /dev/null +++ b/src/content/docs/en/api/effector/demo/_combine-stores-fn.live.js @@ -0,0 +1,13 @@ +import { createStore, combine } from "effector"; + +const $balance = createStore(0); +const $username = createStore("zerobias"); + +const $greeting = combine($balance, $username, (balance, username) => { + return `Hello, ${username}. Your balance is ${balance}`; +}); + +$greeting.watch((data) => console.log(data)); // => Hello, zerobias. Your balance is 0 + +const $arrStores = combine([$balance, $username]); +$arrStores.watch(console.log); // => [0, 'zerobias'] diff --git a/src/content/docs/en/api/effector/fork.md b/src/content/docs/en/api/effector/fork.md new file mode 100644 index 0000000..705fa13 --- /dev/null +++ b/src/content/docs/en/api/effector/fork.md @@ -0,0 +1,233 @@ +--- +title: fork +redirectFrom: + - /api/effector/fork + - /docs/api/effector/fork +--- + +```ts +import { fork, type Scope } from "effector"; +``` + +# Methods (#methods) + +## `fork()` (#methods-fork) + +:::info{title="since"} + +introduced in [effector 22.0.0](https://changelog.effector.dev/#effector-22-0-0) + +::: + +Creates an isolated instance of application. +Primary purposes of this method are SSR and testing. + +### Formulae (#methods-fork-formulae) + +```ts +fork(): Scope +``` + +### Returns (#methods-fork-returns) + +[_Scope_](/en/api/effector/Scope): New fresh scope + +### Examples (#methods-fork-examples) + +#### Create two instances with independent counter state (#methods-fork-examples-create-two-instances) + +```js +import { createStore, createEvent, fork, allSettled } from "effector"; + +const inc = createEvent(); +const dec = createEvent(); +const $counter = createStore(0); + +$counter.on(inc, (value) => value + 1); +$counter.on(dec, (value) => value - 1); + +const scopeA = fork(); +const scopeB = fork(); + +await allSettled(inc, { scope: scopeA }); +await allSettled(dec, { scope: scopeB }); + +console.log($counter.getState()); // => 0 +console.log(scopeA.getState($counter)); // => 1 +console.log(scopeB.getState($counter)); // => -1 +``` + +[Try it](https://share.effector.dev/dBSC59h8) + +## `fork(options)` (#methods-fork-options) + +Allows to set values for stores in scope and replace handlers for effects. + +:::info{title="since"} + +support for array of tuples in `values` and `handlers` introduced in [effector 22.0.0](https://changelog.effector.dev/#effector-22-0-0) + +::: + +### Formulae (#methods-fork-options-formulae) + +```ts +fork(options: { values?, handlers? }): Scope +``` + +### Arguments (#methods-fork-options-arguments) + +1. `options: { values?, handlers? }` — Object with optional values and handlers + +#### `values` (#methods-fork-options-arguments-values) + +Option to provide initial states for stores. + +Can be used in three ways: + +1. Array of tuples with stores and values: + +```ts +fork({ + values: [ + [$user, "alice"], + [$age, 21], + ], +}); +``` + +2. Map with stores and values: + +```ts +fork({ + values: new Map().set($user, "alice").set($age, 21), +}); +``` + +3. Plain object: `{[sid: string]: value}` + +```ts +fork({ + values: { + [$user.sid]: "alice", + [$age.sid]: 21, + }, +}); +``` + +
    + +:::info{title="Explanation"} +Such objects are created by [serialize](/en/api/effector/serialize), in application code **array of tuples is preferred** +::: + +#### `handlers` (#methods-fork-options-arguments-handlers) + +Option to provide handlers for effects. + +Can be used in different ways: + +1. Array of tuples with effects and handlers: + +```ts +fork({ + handlers: [ + [getMessageFx, (params) => ({ id: 0, text: "message" })], + [getUserFx, async (params) => ({ name: "alice", age: 21 })], + ], +}); +``` + +2. Map with effects and handlers: + +```ts +fork({ + handlers: new Map() + .set(getMessageFx, (params) => ({ id: 0, text: "message" })) + .set(getUserFx, async (params) => ({ name: "alice", age: 21 })), +}); +``` + +3. Plain object: `{[sid: string]: handler}` + +```ts +fork({ + handlers: { + [getMessageFx.sid]: (params) => ({ id: 0, text: "message" }), + [getUserFx.sid]: async (params) => ({ name: "alice", age: 21 }), + }, +}); +``` + +
    + +:::warning{title="deprecation"} +Such objects are deprecated since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0) and will be removed in future versions. Array of tuples is preferred. +::: + +### Returns (#methods-fork-options-returns) + +[_Scope_](/en/api/effector/Scope): New fresh scope + +### Examples (#methods-fork-options-examples) + +#### Set initial state for store and change handler for effect (#methods-fork-examples-set-initial-state-and-change-handler) + +This is an example of test, which ensures that after a request to the server, the value of `$friends` is filled. + +```ts +import { createEffect, createStore, fork, allSettled } from "effector"; + +const fetchFriendsFx = createEffect<{ limit: number }, string[]>(async ({ limit }) => { + /* some client-side data fetching */ + return []; +}); +const $user = createStore("guest"); +const $friends = createStore([]); + +$friends.on(fetchFriendsFx.doneData, (_, result) => result); + +const testScope = fork({ + values: [[$user, "alice"]], + handlers: [[fetchFriendsFx, () => ["bob", "carol"]]], +}); + +/* trigger computations in scope and await all called effects */ +await allSettled(fetchFriendsFx, { + scope: testScope, + params: { limit: 10 }, +}); + +/* check value of store in scope */ +console.log(testScope.getState($friends)); +// => ['bob', 'carol'] +``` + +[Try it](https://share.effector.dev/gnNbGZuu) + +## `fork(domain, options?)` (#methods-fork-domain) + +:::info{title="since"} + +introduced in [effector 21.0.0](https://changelog.effector.dev/#effector-21-0-0) + +::: + +### Formulae (#methods-fork-domain-formulae) + +```ts +fork(domain: Domain, options?: { values?, handlers? }): Scope +``` + +### Arguments (#methods-fork-domain-arguments) + +1. `domain` ([_Domain_](/en/api/effector/Domain)): Optional domain to fork. +2. `options: { values?, handlers? }` — Object with optional [values](#methods-fork-options-arguments-values) and [handlers](#methods-fork-options-arguments-handlers) + +### Returns (#methods-fork-domain-returns) + +[_Scope_](/en/api/effector/Scope): New fresh scope + +### Examples (#methods-fork-domain-examples) + +TBD diff --git a/src/content/docs/en/api/effector/forward.md b/src/content/docs/en/api/effector/forward.md new file mode 100644 index 0000000..9f4b459 --- /dev/null +++ b/src/content/docs/en/api/effector/forward.md @@ -0,0 +1,112 @@ +--- +title: forward +description: Method to create connection between units in a declarative way. Send updates from one set of units to another +redirectFrom: + - /api/effector/forward + - /docs/api/effector/forward +--- + +```ts +import { forward, type Subscription } from "effector"; +``` + +Method to create connection between units in a declarative way. Send updates from one set of units to another. + +# Methods (#methods) + +## `forward({ from, to })` (#methods-forward) + +:::warning{title="Deprecated"} +Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0). + +The core team recommends using [sample](/en/api/effector/sample) instead of `forward`. +::: + +### Formulae (#methods-forward-formulae) + +```ts +forward({ + from: Unit | Unit[], + to: Unit | Unit[] +}): Subscription +``` + +### Arguments (#methods-forward-arguments) + +1. `from` ([Unit | Unit\[\]](/en/explanation/glossary#common-unit)): Source of updates. Forward will listen for changes of these units + + - if an [_Event_] is passed, `to` will be triggered on each event trigger and receives event argument + - if a [_Store_] is passed, `to` will be triggered on each store **change** and receives new value of the store + - if an [_Effect_] is passed, `to` will be triggered on each effect call and receives effect parameter + - if an array of [units](/en/explanation/glossary#common-unit) is passed, `to` will be triggered when any unit in `from` array is triggered + +2. `to` ([Unit | Unit\[\]](/en/explanation/glossary#common-unit)): Target for updates. `forward` will trigger these units with data from `from` + - if passed an [_Event_], it will be triggered with data from `from` unit + - if passed a [_Store_], data from `from` unit will be written to store and **trigger its update** + - if passed an [_Effect_], it will be called with data from `from` unit as parameter + - if `to` is an array of [units](/en/explanation/glossary#common-unit), each unit in that array will be triggered + +### Returns (#methods-forward-returns) + +[Subscription](/en/explanation/glossary#subscription): Unsubscribe function. It breaks connection between `from` and `to`. After call, `to` will not be triggered anymore. + +:::info{title="since"} +Arrays of units are supported since [effector 20.6.0](https://changelog.effector.dev/#effector-20-6-0) +::: + +### Examples (#methods-forward-examples) + +#### Send store updates to another store (#methods-forward-examples-send-store-updates) + +```js +import { createStore, createEvent, forward } from "effector"; + +const $store = createStore(1); +const event = createEvent(); + +forward({ + from: event, + to: $store, +}); + +$store.watch((state) => console.log("store changed: ", state)); +// => store changed: 1 + +event(200); +// => store changed: 200 +``` + +[Try it](https://share.effector.dev/UeJbgRG9) + +#### Forward between arrays of units (#methods-forward-examples-forward-between-arrays) + +```js +import { createEvent, forward } from "effector"; + +const firstSource = createEvent(); +const secondSource = createEvent(); + +const firstTarget = createEvent(); +const secondTarget = createEvent(); + +forward({ + from: [firstSource, secondSource], + to: [firstTarget, secondTarget], +}); + +firstTarget.watch((e) => console.log("first target", e)); +secondTarget.watch((e) => console.log("second target", e)); + +firstSource("A"); +// => first target A +// => second target A +secondSource("B"); +// => first target B +// => second target B +``` + +[Try it](https://share.effector.dev/8aVpg8nU) + +[_effect_]: /en/api/effector/Effect +[_store_]: /en/api/effector/Store +[_event_]: /en/api/effector/Event diff --git a/src/content/docs/en/api/effector/fromObservable.md b/src/content/docs/en/api/effector/fromObservable.md new file mode 100644 index 0000000..8ede09a --- /dev/null +++ b/src/content/docs/en/api/effector/fromObservable.md @@ -0,0 +1,47 @@ +--- +title: fromObservable +redirectFrom: + - /api/effector/fromObservable + - /docs/api/effector/fromObservable +--- + +```ts +import { fromObservable, type Observable } from "effector"; +``` + +# Methods (#methods) + +## `fromObservable()` (#methods-fromObservable) + +Creates an event containing all items from an Observable. + +### Formulae (#methods-fromObservable-formulae) + +```ts +fromObservable(source: Observable): Event +``` + +### Arguments (#methods-fromObservable-arguments) + +1. `observable` (_Observable_) + +### Returns (#methods-fromObservable-returns) + +[_Event_](/en/api/effector/Event): New event + +### Examples (#methods-fromObservable-examples) + +#### Basic use case (#methods-fromObservable-examples-basic-use-case) + +```js +import { interval } from "rxjs"; +import { fromObservable } from "effector"; + +//emit value in sequence every 1 second +const source = interval(1000); + +const event = fromObservable(source); + +//output: 0,1,2,3,4,5.... +event.watch(console.log); +``` diff --git a/src/content/docs/en/api/effector/guard.md b/src/content/docs/en/api/effector/guard.md new file mode 100644 index 0000000..7286fe0 --- /dev/null +++ b/src/content/docs/en/api/effector/guard.md @@ -0,0 +1,176 @@ +--- +title: guard +description: Method for conditional event routing. +redirectFrom: + - /api/effector/guard + - /docs/api/effector/guard +--- + +```ts +import { guard } from "effector"; +``` + +:::warning{title="Deprecated"} +Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0). + +The core team recommends using [sample](/en/api/effector/sample) instead of `guard`. +::: + +Method for conditional event routing. +It provides a way to control one dataflow with the help of another: when the condition and the data are in different places, we can use `guard` with stores as filters to trigger events when condition state is true, thereby modulate signals without mixing them. + +# Methods (#methods) + +## `guard({ clock?, source?, filter, target? })` (#methods-guard-clock-source-filter-target) + +### Formulae (#methods-guard-clock-source-filter-target-formulae) + +```ts +guard({ clock?, source?, filter, target? }): target +``` + +:::info +Either `clock` or `source` is required +::: + +When `clock` is triggered, check `filter` for [truthy] and call `target` with data from `source` if `true`. + +- If `clock` is not passed, `guard` will be triggered on every `source` update +- If `source` is not passed, call `target` with data from `clock` +- If `target` is not passed, create [_Event_](/en/api/effector/Event) with type of `source` and return it from `guard()` +- If `filter` is [_Store_](/en/api/effector/Store), check it value for [truthy] +- If `filter` is `Function`, call it with data from `source` and check result for [truthy] + +[truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy + +:::info{title="since"} +`clock` in `guard` is available since [effector 21.8.0](https://changelog.effector.dev/#effector-21-8-0) +::: + +## `guard({source, filter, target?})` (#methods-guard-source-filter-target) + +### Arguments (#methods-guard-source-filter-target-arguments) + +1. `params` (_Object_): Configuration object + +### Returns (#methods-guard-source-filter-target-returns) + +[_Event_](/en/api/effector/Event), which fires upon `clock` trigger + +### Examples (#methods-guard-source-filter-target-examples) + +#### Basic (#methods-guard-source-filter-target-examples-basic) + +```js +import { createStore, createEffect, createEvent, guard } from "effector"; + +const clickRequest = createEvent(); +const fetchRequest = createEffect((n) => new Promise((rs) => setTimeout(rs, 2500, n))); + +const $clicks = createStore(0).on(clickRequest, (x) => x + 1); +const $requestsCount = createStore(0).on(fetchRequest, (x) => x + 1); + +const $isIdle = fetchRequest.pending.map((pending) => !pending); + +/* +1. on clickRequest +2. if $isIdle is true +3. take current $clicks value +4. and call fetchRequest with it +*/ +guard({ + clock: clickRequest /* 1 */, + filter: $isIdle /* 2 */, + source: $clicks /* 3 */, + target: fetchRequest /* 4 */, +}); +``` + +See [ui visualization](https://share.effector.dev/zLB4NwNV) + +#### Function predicate (#methods-guard-source-filter-target-examples-function-predicate) + +```js +import { createEffect, createEvent, guard } from "effector"; + +const submitForm = createEvent(); +const searchUser = createEffect(); + +guard({ + source: submitForm, + filter: (user) => user.length > 0, + target: searchUser, +}); + +submitForm(""); // nothing happens +submitForm("alice"); // ~> searchUser('alice') +``` + +[Try it](https://share.effector.dev/84j97tZ7) + +## `guard(source, {filter: booleanStore})` (#methods-guard-source-filter-booleanStore) + +### Arguments (#methods-guard-source-filter-booleanStore-arguments) + +1. `source` ([_Store_](/en/api/effector/Store)/[_Event_](/en/api/effector/Event)/[_Effect_](/en/api/effector/Effect)): Source unit. Will trigger given `guard` on updates +2. `filter` ([_Store_](/en/api/effector/Store)): Filter store + +### Examples (#methods-guard-source-filter-booleanStore-examples) + +#### Store filter (#methods-guard-source-filter-booleanStore-examples-store-filter) + +```js +import { createEvent, createStore, createApi, guard } from "effector"; + +const trigger = createEvent(); +const $unlocked = createStore(true); + +const { lock, unlock } = createApi($unlocked, { + lock: () => false, + unlock: () => true, +}); + +const target = guard(trigger, { + filter: $unlocked, +}); + +target.watch(console.log); +trigger("A"); +lock(); +trigger("B"); // nothing happens +unlock(); +trigger("C"); +``` + +[Try it](https://share.effector.dev/6bqOCO4y) + +## `guard(source, {filter: predicate})` (#methods-guard-source-filter-predicate) + +### Arguments (#methods-guard-source-filter-predicate-arguments) + +1. `source` ([_Store_](/en/api/effector/Store)/[_Event_](/en/api/effector/Event)/[_Effect_](/en/api/effector/Effect)): Source unit. Will trigger given `guard` on updates +2. `filter` (_(payload) => Boolean_): Predicate function, [should be **pure**](/en/explanation/glossary#purity) + +### Examples (#methods-guard-source-filter-predicate-examples) + +#### Predicate function (#methods-guard-source-filter-predicate-examples-predicate-function) + +```js +import { createEvent, guard } from "effector"; + +const source = createEvent(); +const target = guard(source, { + filter: (x) => x > 0, +}); + +target.watch(() => { + console.log("target called"); +}); + +source(0); +// nothing happens +source(1); +// target called +``` + +[Try it](https://share.effector.dev/ethzpd8Y) diff --git a/src/content/docs/en/api/effector/hydrate.md b/src/content/docs/en/api/effector/hydrate.md new file mode 100644 index 0000000..2716102 --- /dev/null +++ b/src/content/docs/en/api/effector/hydrate.md @@ -0,0 +1,56 @@ +--- +title: hydrate +redirectFrom: + - /api/effector/hydrate + - /docs/api/effector/hydrate +--- + +```ts +import { hydrate } from "effector"; +``` + +A companion method for [_serialize_](/en/api/effector/serialize). Hydrates provided values into corresponding stores within a provided domain or scope. The main purpose is an application state hydration on the client side after SSR. + +# Methods (#methods) + +## `hydrate(domainOrScope, {values})` (#methods-hydrate-domainOrScope-values) + +:::warning +You need to make sure that the store is created beforehand, otherwise, the hydration might fail. This could be the case if you keep store initialization/hydration scripts separate from stores' creation. +::: + +### Formulae (#methods-hydrate-domainOrScope-values-formulae) + +```ts +hydrate(domainOrScope: Domain | Scope, { values: Map, any> | {[sid: string]: any} }): void +``` + +### Arguments (#methods-hydrate-domainOrScope-values-arguments) + +1. `domainOrScope`: [domain](/en/api/effector/Domain) or [scope](/en/api/effector/Scope) which will be filled with given `values` +2. `values`: a mapping from store sids to store values or a Map where keys are [store](/en/api/effector/Store) objects and values contain initial store value + +### Returns (#methods-hydrate-domainOrScope-values-returns) + +`void` + +### Examples (#methods-hydrate-domainOrScope-values-examples) + +Populate store with a predefined value + +```js +import { createStore, createDomain, fork, serialize, hydrate } from "effector"; + +const domain = createDomain(); +const $store = domain.createStore(0); + +hydrate(domain, { + values: { + [$store.sid]: 42, + }, +}); + +console.log($store.getState()); // 42 +``` + +[Try it](https://share.effector.dev/zZoQ5Ewm) diff --git a/src/content/docs/en/api/effector/index.md b/src/content/docs/en/api/effector/index.md new file mode 100644 index 0000000..f896421 --- /dev/null +++ b/src/content/docs/en/api/effector/index.md @@ -0,0 +1,69 @@ +--- +title: effector +redirectFrom: + - /api/effector +--- + +List of the methods + +## Unit Definitions (#unit-definitions) + +- [Event](/en/api/effector/Event) +- [Effect](/en/api/effector/Effect) +- [Store](/en/api/effector/Store) +- [Domain](/en/api/effector/Domain) +- [Scope](/en/api/effector/Scope) + +## Unit Creators (#unit-creators) + +- [createEvent](/en/api/effector/createEvent) +- [createStore](/en/api/effector/createStore) +- [createEffect](/en/api/effector/createEffect) +- [createDomain](/en/api/effector/createDomain) + +## Common Methods (#common-methods) + +- [combine(...stores, f)](/en/api/effector/combine) +- [attach({effect, mapParams?, source?})](/en/api/effector/attach) +- [sample({clock, source, fn, target})](/en/api/effector/sample) +- [merge([eventA, eventB])](/en/api/effector/merge) +- [split(event, cases)](/en/api/effector/split) +- [createApi(store, api)](/en/api/effector/createApi) + +## Fork API (#fork-api) + +- [fork](/en/api/effector/fork) +- [serialize](/en/api/effector/serialize) +- [allSettled](/en/api/effector/allSettled) +- [scopeBind](/en/api/effector/scopeBind) +- [hydrate](/en/api/effector/hydrate) + +## Plugins (#plugins) + +- [effector/babel-plugin](/en/api/effector/babel-plugin) +- [@effector/swc-plugin](https://github.com/effector/swc-plugin) + +## Utilities (#utilities) + +- [is](/en/api/effector/is) +- [fromObservable(observable)](/en/api/effector/fromObservable) + +## Low Level API (#low-level-api) + +- [clearNode](/en/api/effector/clearNode) +- [withRegion](/en/api/effector/withRegion) +- [launch](/en/api/effector/launch) +- [inspect](/en/api/effector/inspect) + +## Import Map (#import-map) + +Package `effector` provides couple different entry points for different purposes: + +- [effector/compat](/en/api/effector/module/compat) +- [effector/inspect](/en/api/effector/module/inspect) +- [effector/babel-plugin](/en/api/effector/module/babel-plugin) + +## Deprecated Methods (#deprecated-methods) + +- [forward({from, to})](/en/api/effector/forward) +- [guard({source, filter, target})](/en/api/effector/guard) diff --git a/src/content/docs/en/api/effector/inspect.md b/src/content/docs/en/api/effector/inspect.md new file mode 100644 index 0000000..63fe880 --- /dev/null +++ b/src/content/docs/en/api/effector/inspect.md @@ -0,0 +1,190 @@ +--- +title: inspect +redirectFrom: + - /api/effector/inspect + - /docs/api/effector/inspect +--- + +```ts +import { inspect } from "effector/inspect"; +``` + +Special API methods designed to handle debugging and monitoring use cases without giving too much access to internals of your actual app. + +Useful to create developer tools and production monitoring and observability instruments. + +# Inspect API (#inspect-api) + +Allows us to track any computations that have happened in the effector's kernel. + +## `inspect()` (#inspect-api-inspect) + +### Example (#inspect-api-inspect-example) + +```ts +import { inspect, type Message } from "effector/inspect"; + +import { someEvent } from "./app-code"; + +function logInspectMessage(m: Message) { + const { name, value, kind } = m; + + return console.log(`[${kind}] ${name} ${value}`); +} + +inspect({ + fn: (m) => { + logInspectMessage(m); + }, +}); + +someEvent(42); +// will log something like +// [event] someEvent 42 +// [on] 42 +// [store] $count 1337 +// ☝️ let's say that reducer adds 1295 to provided number +// +// and so on, any triggers +``` + +[Scope](/en/api/effector/Scope) limits the extent to which computations can be tracked. If no scope is provided - default out-of-scope mode computations will be tracked. + +```ts +import { fork, allSettled } from "effector"; +import { inspect, type Message } from "effector/inspect"; + +import { someEvent } from "./app-code"; + +function logInspectMessage(m: Message) { + const { name, value, kind } = m; + + return console.log(`[${kind}] ${name} ${value}`); +} + +const myScope = fork(); + +inspect({ + scope: myScope, + fn: (m) => { + logInspectMessage(m); + }, +}); + +someEvent(42); +// ☝️ No logs! That's because tracking was restricted by myScope + +allSettled(someEvent, { scope: myScope, params: 42 }); +// [event] someEvent 42 +// [on] 42 +// [store] $count 1337 +``` + +## Tracing (#inspect-api-tracing) + +Adding `trace: true` setting allows looking up previous computations, that led to this specific one. It is useful to debug the specific reason for some events happening + +### Example (#inspect-api-tracing-example) + +```ts +import { fork, allSettled } from "effector"; +import { inspect, type Message } from "effector/inspect"; + +import { someEvent, $count } from "./app-code"; + +function logInspectMessage(m: Message) { + const { name, value, kind } = m; + + return console.log(`[${kind}] ${name} ${value}`); +} + +const myScope = fork(); + +inspect({ + scope: myScope, + trace: true, // <- explicit setting is needed + fn: (m) => { + if (m.kind === "store" && m.sid === $count.sid) { + m.trace.forEach((tracedMessage) => { + logInspectMessage(tracedMessage); + // ☝️ here we are logging the trace of specific store update + }); + } + }, +}); + +allSettled(someEvent, { scope: myScope, params: 42 }); +// [on] 42 +// [event] someEvent 42 +// ☝️ traces are provided in backwards order, because we are looking back in time +``` + +## Errors (#inspect-api-errors) + +Effector does not allow exceptions in pure functions. In such case, branch computation is stopped and an exception is logged. There is also a special message type in such case: + +### Example (#inspect-api-errors-example) + +```ts +inspect({ + fn: (m) => { + if (m.type === "error") { + // do something about it + console.log(`${m.kind} ${m.name} computation has failed with ${m.error}`); + } + }, +}); +``` + +# Inspect Graph (#inspect-graph) + +Allows us to track declarations of units, [factories](/en/api/effector/babel-plugin#factories), and [regions](/en/api/effector/withRegion). + +## Example (#inspect-graph-example) + +```ts +import { createStore } from "effector"; +import { inspectGraph, type Declaration } from "effector/inspect"; + +function printDeclaration(d: Declaration) { + console.log(`${d.kind} ${d.name}`); +} + +inspectGraph({ + fn: (d) => { + printDeclaration(d); + }, +}); + +const $count = createStore(0); +// logs "store $count" to console +``` + +## `withRegion` (#inspect-graph-withRegion) + +Meta-data provided via region's root node is available on declaration. + +### Example (#inspect-graph-withRegion-example) + +```ts +import { createNode, withRegion, createStore } from "effector"; +import { inspectGraph, type Declaration } from "effector/inspect"; + +function createCustomSomething(config) { + const $something = createStore(0); + + withRegion(createNode({ meta: { hello: "world" } }), () => { + // some code + }); + + return $something; +} +inspectGraph({ + fn: (d) => { + if (d.type === "region") console.log(d.meta.hello); + }, +}); + +const $some = createCustomSomething({}); +// logs "world" +``` diff --git a/src/content/docs/en/api/effector/is.md b/src/content/docs/en/api/effector/is.md new file mode 100644 index 0000000..3ddbc5c --- /dev/null +++ b/src/content/docs/en/api/effector/is.md @@ -0,0 +1,382 @@ +--- +title: is +redirectFrom: + - /api/effector/is + - /docs/api/effector/is +--- + +```ts +import { is, type Unit } from "effector"; +``` + +Namespace for unit validators. + +# Methods (#methods) + +## `is.store(value)` (#methods-isStore) + +Checks if given value is [_store_](/en/api/effector/Store) + +### Returns (#methods-isStore-returns) + +`boolean` — Type-guard + +### Examples (#methods-isStore-examples) + +```js +import { is, createStore, createEvent, createEffect, createDomain } from "effector"; + +const $store = createStore(null); +const event = createEvent(); +const fx = createEffect(); + +is.store($store); +// => true + +is.store(event); +// => false + +is.store(fx); +// => false + +is.store(createDomain()); +// => false + +is.store(fx.pending); +// => true + +is.store(fx.done); +// => false + +is.store($store.updates); +// => false + +is.store(null); +// => false +``` + +[Try it](https://share.effector.dev/4vzdWan1) + +## `is.event(value)` (#methods-isEvent) + +Checks if given value is [_event_](/en/api/effector/Event) + +### Returns (#methods-isEvent-returns) + +`boolean` — Type-guard + +### Examples (#methods-isEvent-examples) + +```js +import { is, createStore, createEvent, createEffect, createDomain } from "effector"; + +const $store = createStore(null); +const event = createEvent(); +const fx = createEffect(); + +is.event($store); +// => false + +is.event(event); +// => true + +is.event(fx); +// => false + +is.event(createDomain()); +// => false + +is.event(fx.pending); +// => false + +is.event(fx.done); +// => true + +is.event($store.updates); +// => true + +is.event(null); +// => false +``` + +[Try it](https://share.effector.dev/hB0JEiIo) + +## `is.effect(value)` (#methods-isEffect) + +Checks if given value is [_effect_](/en/api/effector/Effect) + +### Returns (#methods-isEffect-returns) + +`boolean` — Type-guard + +### Examples (#methods-isEffect-examples) + +```js +import { is, createStore, createEvent, createEffect, createDomain } from "effector"; + +const $store = createStore(null); +const event = createEvent(); +const fx = createEffect(); + +is.effect($store); +// => false + +is.effect(event); +// => false + +is.effect(fx); +// => true + +is.effect(createDomain()); +// => false + +is.effect(null); +// => false +``` + +[Try it](https://share.effector.dev/ZdZ2N6VG) + +## `is.targetable` (#methods-isTargetable) + +Checks if given value can be used in operators target (or be called as a function in case of events) + +### Returns (#methods-isTargetable-returns) + +`boolean` — Type-guard + +### Examples (#methods-isTargetable-examples) + +```js +import { is, createStore, createEvent, createEffect } from "effector"; + +const $store = createStore(null); +const $mapped = $store.map((x) => x); +const event = createEvent(); +const mappedEvent = event.map((x) => x); +const fx = createEffect(); + +is.targetable($store); +// => true + +is.targetable($mapped); +// => false + +is.targetable(event); +// => true + +is.targetable(mappedEvent); +// => false + +is.targetable(fx); +// => true +``` + +## `is.domain(value)` (#methods-isDomain) + +Checks if given value is [_domain_](/en/api/effector/Domain) + +### Returns (#methods-isDomain-returns) + +`boolean` — Type-guard + +### Examples (#methods-isDomain-examples) + +```js +import { is, createStore, createEvent, createEffect, createDomain } from "effector"; + +const $store = createStore(null); +const event = createEvent(); +const fx = createEffect(); + +is.domain($store); +// => false + +is.domain(event); +// => false + +is.domain(fx); +// => false + +is.domain(createDomain()); +// => true + +is.domain(null); +// => false +``` + +[Try it](https://share.effector.dev/Iea0gmfD) + +## `is.scope(value)` (#methods-isScope) + +:::info{title="since"} +[effector 22.0.0](https://changelog.effector.dev/#effector-22-0-0) +::: + +Checks if given value is [_scope_](/en/api/effector/Scope) since [effector 22.0.0](https://changelog.effector.dev/#effector-22-0-0). + +### Returns (#methods-isScope-returns) + +`boolean` — Type-guard + +### Examples (#methods-isScope-examples) + +```js +import { fork } from "effector"; + +const $store = createStore(null); +const event = createEvent(); +const fx = createEffect(); +const scope = fork(); + +is.scope(scope); +// => true + +is.scope($store); +// => false + +is.scope(event); +// => false + +is.scope(fx); +// => false + +is.scope(createDomain()); +// => false + +is.scope(null); +// => false +``` + +[Try it](https://share.effector.dev/hF0krFUK) + +## `is.unit(value)` (#methods-isUnit) + +Checks if given value is [Unit](/en/explanation/glossary#unit): [Store](/en/api/effector/Store), [Event](/en/api/effector/Event), [Effect](/en/api/effector/Effect), [Domain](/en/api/effector/Domain) or [Scope](/en/api/effector/Scope) + +### Returns (#methods-isUnit-returns) + +`boolean` — Type-guard + +### Examples (#methods-isUnit-examples) + +```js +import { is, createStore, createEvent, createEffect, createDomain, fork } from "effector"; + +const $store = createStore(null); +const event = createEvent(); +const fx = createEffect(); +const scope = fork(); + +is.unit(scope); +// => true + +is.unit($store); +// => true + +is.unit(event); +// => true + +is.unit(fx); +// => true + +is.unit(createDomain()); +// => true + +is.unit(fx.pending); +// => true + +is.unit(fx.done); +// => true + +is.unit($store.updates); +// => true + +is.unit(null); +// => false +``` + +[Try it](https://share.effector.dev/iOpDvweB) + +## `is.attached(value)` (#methods-isAttached) + +:::info{title="since"} +[effector 22.4.0](https://changelog.effector.dev/#effector-22-4-0) +::: + +Checks if given value is [_effect_](/en/api/effector/Effect) created via [_attach_](/en/api/effector/attach) method. If passed not an effect, returns `false`. + +### Returns (#methods-isAttached-returns) + +`boolean` — Type-guard + +### Usage (#methods-isAttached-usage) + +Sometimes you need to add an error log on effects failures, but only on effects that have been "localized" via `attach`. +If you leave `onCreateEffect` as it is, without checks, the error log will be duplicated, because it will happen on the parent and the child effect. + +```js +import { createDomain, attach, is } from "effector"; + +const logFailuresDomain = createDomain(); + +logFailuresDomain.onCreateEffect((effect) => { + if (is.attached(effect)) { + effect.fail.watch(({ params, error }) => { + console.warn(`Effect "${effect.compositeName.fullName}" failed`, params, error); + }); + } +}); + +const baseRequestFx = logFailuresDomain.createEffect((path) => { + throw new Error(`path ${path}`); +}); + +const loadDataFx = attach({ + mapParams: () => "/data", + effect: baseRequestFx, +}); + +const loadListFx = attach({ + mapParams: () => "/list", + effect: baseRequestFx, +}); + +loadDataFx(); +loadListFx(); +``` + +[Try it](https://share.effector.dev/NxQseHOR) + +### Examples (#methods-isAttached-examples) + +```js +import { is, createStore, createEvent, createEffect, createDomain, attach } from "effector"; + +const $store = createStore(null); +const event = createEvent(); +const fx = createEffect(); + +const childFx = attach({ + effect: fx, +}); + +is.attached(childFx); +// => true + +is.attached(fx); +// => false + +is.attached($store); +// => false + +is.attached(event); +// => false + +is.attached(createDomain()); +// => false + +is.attached(null); +// => false +``` + +[Try it](https://share.effector.dev/qsdTF7og) diff --git a/src/content/docs/en/api/effector/launch.md b/src/content/docs/en/api/effector/launch.md new file mode 100644 index 0000000..a19030b --- /dev/null +++ b/src/content/docs/en/api/effector/launch.md @@ -0,0 +1,53 @@ +--- +title: launch +redirectFrom: + - /api/effector/launch + - /docs/api/effector/launch +--- + +```ts +import { launch, type Unit, type Node } from "effector"; +``` + +:::info{title="since"} +[effector 20.10.0](https://changelog.effector.dev/#effector-20-10-0) +::: + +# Methods (#methods) + +## `launch({ target, params })` (#methods-launch-config) + +Low level method for running computation in units (events, effects or stores). Mostly used by library developers for fine-grained control of computations. + +### Formulae (#methods-launch-config-formulae) + +```ts +launch({ + target, + params, + defer?: boolean, + page?: any, + scope?: Scope, + meta?: Record, +}): void +``` + +### Arguments (#methods-launch-config-arguments) + +TBD + +### Returns (#methods-launch-config-returns) + +`void` + +## `launch(unit, params)` (#methods-launch-unit-params) + +### Formulae (#methods-launch-unit-params-formulae) + +```ts +launch(unit: Unit | Node, params: T): void +``` + +### Returns (#methods-launch-unit-params-returns) + +`void` diff --git a/src/content/docs/en/api/effector/merge.md b/src/content/docs/en/api/effector/merge.md new file mode 100644 index 0000000..a5e659a --- /dev/null +++ b/src/content/docs/en/api/effector/merge.md @@ -0,0 +1,103 @@ +--- +title: merge +description: merge is a method for creating an event triggered by given units +redirectFrom: + - /api/effector/merge + - /docs/api/effector/merge +--- + +```ts +import { merge, type Unit } from "effector"; +``` + +# Methods (#methods) + +## `merge(units)` (#methods-merge-units) + +:::info{title="since"} +[effector 20.0.0](https://changelog.effector.dev/#effector-20-0-0) +::: + +Merges an array of units (events, effects, or stores), returning a new event that triggers upon any of the given units being triggered. + +```ts +merge(units: Unit[]): Event +``` + +### Arguments (#methods-merge-units-arguments) + +1. `units`: An array of [units](/en/explanation/glossary#common-unit) to be merged. + +### Returns (#methods-merge-units-returns) + +[_Event_](/en/api/effector/Event): A new event that fires when any of the given units is triggered. + +:::tip +In the case of a store, the resulting event will fire upon store updates. +::: + +### Types (#methods-merge-units-types) + +TBD + +### Examples (#methods-merge-units-examples) + +#### Basic Usage (#methods-merge-units-examples-basic-usage) + +```js +import { createEvent, merge } from "effector"; + +const foo = createEvent(); +const bar = createEvent(); +const baz = merge([foo, bar]); +baz.watch((v) => console.log("merged event triggered: ", v)); + +foo(1); +// => merged event triggered: 1 +bar(2); +// => merged event triggered: 2 +``` + +[Try it](https://share.effector.dev/WxUgr6dZ) + +#### Working with Stores (#methods-merge-units-examples-working-with-stores) + +```js +import { createEvent, createStore, merge } from "effector"; + +const setFoo = createEvent(); +const setBar = createEvent(); + +const $foo = createStore(0).on(setFoo, (_, v) => v); +const $bar = createStore(100).on(setBar, (_, v) => v); + +const anyUpdated = merge([$foo, $bar]); +anyUpdated.watch((v) => console.log(`state changed to: ${v}`)); + +setFoo(1); // => state changed to: 1 +setBar(123); // => state changed to: 123 +``` + +[Try it](https://share.effector.dev/Rp9wuRvl) + +#### Merging a Store and an Event (#methods-merge-units-examples-merging-a-store-and-an-event) + +```js +import { createEvent, createStore, merge } from "effector"; + +const setFoo = createEvent(); +const otherEvent = createEvent(); + +const $foo = createStore(0).on(setFoo, (_, v) => v); +const merged = merge([$foo, otherEvent]); + +merged.watch((v) => console.log(`merged event payload: ${v}`)); + +setFoo(999); +// => merged event payload: 999 + +otherEvent("bar"); +// => merged event payload: bar +``` + +[Try it](https://share.effector.dev/pKkiyhVQ) diff --git a/src/content/docs/en/api/effector/module/babel-plugin.md b/src/content/docs/en/api/effector/module/babel-plugin.md new file mode 100644 index 0000000..14b1d13 --- /dev/null +++ b/src/content/docs/en/api/effector/module/babel-plugin.md @@ -0,0 +1,10 @@ +--- +title: effector/babel-plugin +description: Separate module of Effector with plugin for Babel that improves DX over the library +--- + +Since Effector allows to automate many common tasks (like setting [Stable IDentifiers](/en/explanation/sids) and providing debug information for [Units](/en/explanation/glossary#unit)), there is a built-in plugin for Babel that enhances the developer experience when using the library. + +# Usage (#methods-babel-plugin-usage) + +Please refer to the [Babel plugin](/en/api/effector/babel-plugin) documentation for usage examples. diff --git a/src/content/docs/en/api/effector/module/inspect.md b/src/content/docs/en/api/effector/module/inspect.md new file mode 100644 index 0000000..46f212f --- /dev/null +++ b/src/content/docs/en/api/effector/module/inspect.md @@ -0,0 +1,14 @@ +--- +title: effector/inspect +description: Separate module of Effector with Inspect API +--- + +Effector has special API methods designed to handle debugging and monitoring use cases without giving too much access to the internals of your actual app — [Inspect API](/en/api/effector/inspect). + +## Why a Separate Module? (#why-a-separate-module) + +Inspect API is designed to be disposable. By design, any feature that uses Inspect API can be removed from the production build without any side effects. To emphasize this, Inspect API is not included in the main module. Instead, it's available in a separate module `effector/inspect`. + +## Usage (#usage) + +Please refer to [Inspect API](/en/api/effector/inspect) docs for usage examples. diff --git "a/src/content/docs/en/api/effector/module/\321\201ompat.md" "b/src/content/docs/en/api/effector/module/\321\201ompat.md" new file mode 100644 index 0000000..959f1c8 --- /dev/null +++ "b/src/content/docs/en/api/effector/module/\321\201ompat.md" @@ -0,0 +1,100 @@ +--- +title: effector/compat +description: Separate module of Effector with compatibility up to IE11 and Chrome 47 (browser for Smart TV devices) +--- + +```ts +import {} from "effector/compat"; +``` + +The library provides a separate module with compatibility up to IE11 and Chrome 47 (browser for Smart TV devices). + +:::warning{title="Bundler, Not Transpiler"} +Since third-party libraries can import `effector` directly, you **should not** use transpilers like Babel to replace `effector` with `effector/compat` in your code because by default, Babel will not transform third-party code. + +**Use a bundler instead**, as it will replace `effector` with `effector/compat` in all modules, including those from third parties. + +::: + +## Required Polyfills (#required-polyfills) + +You need to install polyfills for these objects: + +- `Promise` +- `Object.assign` +- `Array.prototype.flat` +- `Map` +- `Set` + +In most cases, a bundler can automatically add polyfills. + +### Vite (#required-polyfills-vite) + +
    +Vite Configuration Example + +```js +import { defineConfig } from "vite"; +import legacy from "@vitejs/plugin-legacy"; + +export default defineConfig({ + plugins: [ + legacy({ + polyfills: ["es.promise", "es.object.assign", "es.array.flat", "es.map", "es.set"], + }), + ], +}); +``` + +
    + +# Usage (#usage) + +## Manual Replacement (#usage-manual) + +You can use `effector/compat` instead of the `effector` package if you need to support old browsers. + +```diff +- import {createStore} from 'effector' ++ import {createStore} from 'effector/compat' +``` + +## Automatic Replacement (#usage-automatic) + +However, you can set up your bundler to automatically replace `effector` with `effector/compat` in your code. + +### Webpack (#usage-automatic-webpack) + +
    +Webpack Configuration Example + +```js +module.exports = { + resolve: { + alias: { + effector: "effector/compat", + }, + }, +}; +``` + +
    + +### Vite (#usage-automatic-vite) + +
    +Vite Configuration Example + +```js +import { defineConfig } from "vite"; + +export default defineConfig({ + resolve: { + alias: { + effector: "effector/compat", + }, + }, +}); +``` + +
    diff --git a/src/content/docs/en/api/effector/restore.md b/src/content/docs/en/api/effector/restore.md new file mode 100644 index 0000000..a130018 --- /dev/null +++ b/src/content/docs/en/api/effector/restore.md @@ -0,0 +1,136 @@ +--- +title: restore +redirectFrom: + - /api/effector/restore + - /docs/api/effector/restore +--- + +```ts +import { restore } from "effector"; +``` + +# Methods (#methods) + +## `restore(event, defaultState)` (#methods-restore-event-defaultState) + +Creates a [_StoreWritable_](/en/api/effector/Store) from an [_Event_](/en/api/effector/Event). It works like a shortcut for `createStore(defaultState).on(event, (_, payload) => payload)` + +:::warning{title="It is not a derived store"} +Restore creates a new store. It is not a [DerivedStore](/en/api/effector/Store#readonly). That means you can modify its state via events, and use it as `target` in [sample](/en/api/effector/sample). +::: + +### Formulae (#methods-restore-event-defaultState-formulae) + +```ts +restore(event: Event, defaultState: T): StoreWritable +``` + +### Arguments (#methods-restore-event-defaultState-arguments) + +1. `event` [_Event_](/en/api/effector/Event) +2. `defaultState` (_Payload_) + +### Returns (#methods-restore-event-defaultState-returns) + +[_StoreWritable_](/en/api/effector/Store): New store + +### Examples (#methods-restore-event-defaultState-examples) + +#### Basic (#methods-restore-event-defaultState-examples-basic) + +```js +import { createEvent, restore } from "effector"; + +const event = createEvent(); +const $store = restore(event, "default"); + +$store.watch((state) => console.log("state: ", state)); +// state: default + +event("foo"); +// state: foo +``` + +[Try it](https://share.effector.dev/MGGQnTlQ) + +## `restore(effect, defaultState)` (#methods-restore-effect-defaultState) + +Creates a [_StoreWritable_](/en/api/effector/Store) out of successful results of an [_Effect_](/en/api/effector/Effect). It works like a shortcut for `createStore(defaultState).on(effect.done, (_, {result}) => result)` + +### Formulae (#methods-restore-effect-defaultState-formulae) + +```ts +restore(effect: Effect, defaultState: Done): StoreWritable +``` + +### Arguments (#methods-restore-effect-defaultState-arguments) + +1. `effect` [_Effect_](/en/api/effector/Effect) +2. `defaultState` (_Done_) + +### Returns (#methods-restore-effect-defaultState-returns) + +[_StoreWritable_](/en/api/effector/Store): New store + +### Types (#methods-restore-effect-defaultState-types) + +Store will have the same type as `Done` from `Effect`. Also, `defaultState` should have `Done` type. + +### Examples (#methods-restore-effect-defaultState-examples) + +#### Effect (#methods-restore-effect-defaultState-examples-effect) + +```js +import { createEffect, restore } from "effector"; + +const fx = createEffect(() => "foo"); +const $store = restore(fx, "default"); + +$store.watch((state) => console.log("state: ", state)); +// => state: default + +await fx(); +// => state: foo +``` + +[Try it](https://share.effector.dev/tP6RQsri) + +## `restore(shape)` (#methods-restore-shape) + +Creates an object with stores from an object with values. + +### Formulae (#methods-restore-shape-formulae) + +TBD + +### Arguments (#methods-restore-shape-arguments) + +1. `shape` (_State_) + +### Returns (#methods-restore-shape-returns) + +[_StoreWritable_](/en/api/effector/Store): New store. + +### Examples (#methods-restore-shape-examples) + +#### Object (#methods-restore-shape-examples-object) + +```js +import { restore } from "effector"; + +const { foo: $foo, bar: $bar } = restore({ + foo: "foo", + bar: 0, +}); + +$foo.watch((foo) => { + console.log("foo", foo); +}); +// => foo 'foo' +$bar.watch((bar) => { + console.log("bar", bar); +}); +// => bar 0 +``` + +[Try it](https://share.effector.dev/NQX0kotI) diff --git a/src/content/docs/en/api/effector/sample.md b/src/content/docs/en/api/effector/sample.md new file mode 100644 index 0000000..56a2a73 --- /dev/null +++ b/src/content/docs/en/api/effector/sample.md @@ -0,0 +1,498 @@ +--- +title: sample +redirectFrom: + - /api/effector/sample + - /docs/api/effector/sample +--- + +```ts +import { sample } from "effector"; +``` + +# Methods (#methods) + +# `sample({ source?, clock?, filter?, fn?, target? })` (#methods-sample-config) + +This method can be used for linking two nodes, resulting in the third one, which will fire only upon the `clock` node trigger. + +Quite a common case, when you need to handle an event with some store's state. Instead of using `store.getState()`, which may cause race conditions and inconsistency of state, it is more suitable to use the `sample` method. + +## Formulae (#methods-sample-config-formulae) + +```ts +sample({ source?, clock?, filter?, fn?, target?}): target +``` + +When `clock` is triggered, read the value from `source` and trigger `target` with it. + +- If the `clock` is not passed, `sample` will be triggered on every `source` update. +- If the `filter` is not passed, continue as it is. If `filter` return `false` or contains `Store` cancel execution otherwise continue +- If the `fn` is passed, pass value from `source` through before passing to `target` +- If the `target` is not passed, create it and return from `sample()` + +## Schema (#methods-sample-config-schema) + +![](/images/sample-visualization.gif) + +## Types (#methods-sample-config-types) + +### Type of the created `target` (#methods-sample-config-types-target) + +If `target` is not passed to `sample()` call, it will be created internally. The type of unit is described in the table below: + +| clock\source | [_Store_](/en/api/effector/Store) | [_Event_](/en/api/effector/Event) | [_Effect_](/en/api/effector/Effect) | +| ----------------------------------- | --------------------------------- | --------------------------------- | ----------------------------------- | +| [_Store_](/en/api/effector/Store) | `Store` | `Event` | `Event` | +| [_Event_](/en/api/effector/Event) | `Event` | `Event` | `Event` | +| [_Effect_](/en/api/effector/Effect) | `Event` | `Event` | `Event` | + +How to read it: + +1. You need to know the type of the `source`, it is a column +2. Type of the `clock` in the rows +3. Match the column and the row + +For example: + +```ts +import { sample } from "effector"; + +const $store = sample({ clock: $store, source: $store }); +// Result will be store, because `source` and `clock` are stores. + +const event = sample({ clock: event, source: $store }); +// Because not all arguments are stores. +``` + +# `sample({clock?, source, filter?, fn?, target?, greedy?})` (#methods-sample-greedy) + +## Formulae (#methods-sample-greedy-formulae) + +TBD + +## Arguments (#methods-sample-greedy-arguments) + +`params` (_Object_): Configuration object + +- `clock?`: [Unit](/en/explanation/glossary#common-unit) or array of units + - If event or effect: trigger `target` upon event or effect is called + - If store: trigger `target` upon store is updated + - If array of units: trigger `target` upon any given unit is called or updated. Shorthand for inline [merge](/en/api/effector/merge) call + - If not passed: `source` is used as `clock` +- `source?`: [Unit](/en/explanation/glossary#common-unit) or object/array with stores + - If event or effect: take last invocation argument value. That event or effect must be invoked at least once + - If store: take current state of given store + - If array or object with stores: take values from given stores combined to object or array. Shorthand for inline [combine](/en/api/effector/combine) call + - If not passed: `clock` is used as `source` +- `target?`: [Unit](/en/explanation/glossary#common-unit) or array of units + - If event or effect: call given event or effect upon `clock` is triggered + - If store: update given store upon `clock` is triggered + - If array of units: trigger every given unit upon `clock` is triggered + - If not passed: new unit will be created under the hood and will be returned as a result of the `sample()` call. Type of created target is described [in table beyond](/en/api/effector/sample#sample-types-target) +- `filter?` _(Function or [Store](/en/api/effector/Store))_ `((sourceData, clockData) => result): boolean | Store`: If returns value of the function or store contains `true` continue execution otherwise cancel +- `fn?` _(Function)_ `((sourceData, clockData) => result)`: Combinator function, which will transform data from `source` and `clock` before passing it to `target`, [should be **pure**](/en/explanation/glossary#purity). If not passed, data from `source` will be passed to `target` as it is +- `greedy?` (boolean) Modifier defines whether sampler will wait for resolving calculation result, and will batch all updates, resulting only one trigger, or will be triggered upon every linked node invocation, e.g. if `greedy` is `true`, `sampler` will fire on trigger of every node, linked to `clock`, whereas `non-greedy sampler(greedy: false)` will fire only upon the last linked node trigger + +:::warning{title="Deprecated"} +Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0) property `greedy` is deprecated. + +Use `batch` instead of `greedy`. +::: + +:::info{title="since"} +Array of units in `target` are supported since [effector 21.8.0](https://changelog.effector.dev/#effector-21-8-0) +::: + +## Returns (#methods-sample-greedy-returns) + +([_Event_](/en/api/effector/Event) | [_Store_](/en/api/effector/Store)) - Unit, which fires/updates upon `clock` is triggered, if `source` is not passed. [The type of returned unit depends on the types of `clock` and `source`](#sample-types-target). + +## Examples (#methods-sample-greedy-examples) + +```js +import { createStore, createEvent, createEffect, sample } from "effector"; + +const submitForm = createEvent(); +const signInFx = createEffect((params) => { + console.log(params); +}); + +const $userName = createStore("john"); + +sample({ + clock: submitForm /* 1 */, + source: $userName /* 2 */, + fn: (name, password) => ({ name, password }) /* 3 */, + target: signInFx /* 4 */, +}); + +submitForm(12345678); +// 1. when submitForm is called with params (12345678) +// 2. take $userName store`s state ('john') +// 3. transform payload from event (1) and current store`s state (2) +// 4. trigger effect signInFx with params received at the step (3) +``` + +[Try it](https://share.effector.dev/PAjWhOJc) + +# `sample(sourceUnit, clockUnit, fn?)` (#sample-sourceUnit-clockUnit-fn) + +It is just another form of the `sample` invocation, with the same sense. + +## Formulae (#sample-sourceUnit-clockUnit-fn-formulae) + +TBD + +## Arguments (#sample-sourceUnit-clockUnit-fn-arguments) + +- `sourceUnit`: Source [unit](/en/explanation/glossary#common-unit) + - If event or effect. Take last invocation argument value. That event or effect must be invoked at least once + - If store. Take current store's state +- `clockUnit`: Clock [unit](/en/explanation/glossary#common-unit). If not passed, `source` is used as `clock` + - If event or effect. Trigger the sampled unit, upon event or effect is called + - If store. Trigger the sampled unit, upon store is updated +- `fn?` (_(sourceData, clockData) => result_): Optional combinator function, [should be **pure**](/en/explanation/glossary#purity). Since, this handler is supposed to organize data flow, you should avoid declaring side effects here. It's more appropriate to place it in `watch` method for sampled node. + +**Returns** + +([_Event_](/en/api/effector/Event) | [_Store_](/en/api/effector/Store)) – Unit, which fires/updates upon `clock` is triggered, if `source` is not passed. +[The type of returned unit depends on the types of `clock` and `source`](#sample-types-target). + +## Examples (#sample-sourceUnit-clockUnit-fn-examples) + +```js +import { createStore, createEvent, createEffect, sample } from "effector"; + +const submitForm = createEvent(); + +const signInFx = createEffect((params) => { + console.log(params); +}); + +const $userName = createStore("john"); + +const sampleUnit = sample( + $userName /* 2 */, + submitForm /* 1 */, + (name, password) => ({ name, password }) /* 3 */, +); +/* 4 */ +sample({ + clock: sampleUnit, + target: signInFx, +}); + +submitForm(12345678); +// 1. when submitForm is called with params (12345678) +// 2. take $userName store`s state ('john') +// 3. transform payload from event (1) and current store`s state (2) +// 4. when sampleUnit (event in this case) is triggered, +// send it payload to effect signInFx with params received at the step (3) +``` + +[Try it](https://share.effector.dev/WO6UT8bV) + +## `sample({name?})` (#sample-name) + +:::info{title="since"} +[effector 20.4.0](https://changelog.effector.dev/#effector-20-4-0) +::: + +Every [unit](/en/explanation/glossary#unit) in effector may have a name. +You now can name sampled entities in the same manner as basic ones. + +```js +import { createStore, sample } from "effector"; + +const $store = createStore(null); + +const sampled = sample({ + source: $store, + name: "sampled $store", +}); + +console.log(sampled.shortName); // 'sampled foo' +``` + +# Objects and Arrays of _Store_ in `sample({ source })` (#sample-source-objects-arrays) + +## Object of Stores (#sample-source-object-stores) + +:::info{title="since"} +[effector 20.8.0](https://changelog.effector.dev/#effector-20-8-0) +::: + +`sample` can be called with an object of [_Store_](/en/api/effector/Store) as `source`: + +```js +import { createStore, createEvent, sample } from "effector"; + +const trigger = createEvent(); + +const $a = createStore("A"); +const $b = createStore(1); + +// Target has type `Event<{ a: string, b: number }>` +const target = sample({ + clock: trigger, + source: { a: $a, b: $b }, +}); + +target.watch((obj) => { + console.log("sampled object", obj); +}); + +trigger(); +// => sampled object {a: 'A', b: 1} +``` + +[Try it](https://share.effector.dev/Wp9nq14k) + +### Array of Stores (#sample-source-array-stores) + +:::info{title="since"} +[effector 20.8.0](https://changelog.effector.dev/#effector-20-8-0) +::: + +`sample` can be called with an array of [_Store_](/en/api/effector/Store) as `source`: + +> Note: Typescript requires adding `as const` after the array is entered. + +```ts +import { createStore, createEvent, sample } from "effector"; + +const trigger = createEvent(); + +const $a = createStore("A"); +const $b = createStore(1); + +// Target has type `Event<[string, number]>` +const target = sample({ + clock: trigger, + source: [$a, $b] as const, +}); + +target.watch((obj) => { + console.log("sampled array", obj); +}); + +// You can easily destructure arguments to set explicit names +target.watch(([a, b]) => { + console.log("explicit names", a, b); +}); + +trigger(); +// => sampled array ["A", 1] +// => explicit names "A" 1 +``` + +[Try it](https://share.effector.dev/duqTwRgT) + +### Array of _Units_ in `sample({ clock })` (#sample-clock-array) + +:::info{title="since"} +[effector 21.2.0](https://changelog.effector.dev/#effector-21-2-0) +::: + +`clock` field in `sample` supports passing arrays of units, acting similarly to a `merge` call. + +```js +import {createStore, createEvent, createEffect, sample, merge} from 'effector' + +const showNotification = createEvent() +const trigger = createEvent() +const fx = createEffect() +const $store = createStore('') + +// array of units in `clock` +sample({ + clock: [trigger, fx.doneData], + source: $store, + target: showNotification, +}) + +// merged unit in `clock` +sample({ + clock: merge([trigger, fx.doneData]), + source: $store, + target: showNotification, +}) +``` + +[Try it](https://share.effector.dev/1YEHUFs7) + +## Filtering updates with `sample({ filter })` (#sample-filter) + +:::info{title="since"} +[effector 22.2.0](https://changelog.effector.dev/#effector-22-2-0) +::: + +The new variant of the `sample` works the same but with one extra method `filter`. Whenever `filter` returns `true` continue execution otherwise cancel. Let's see an example below. + +Henry wants to send money to William. Henry – sender and William – recipient. To send money, sender should know the recipient address, besides sender has to sign the transaction. This example shows how exactly the `sample` works with a `filter`. The main points are: + +1. Make sure balance is positive and more than sending amount +2. Having recipient address +3. Signed transaction +4. Make sure sender balance has been changed + +```js +import { createStore, createEvent, createEffect, sample } from "effector"; + +const sign = createEvent(); +const sentMoney = createEvent(); +const $recipientAddress = createStore("a23x3xd"); +const $balance = createStore(20000); +const $isSigned = createStore(false); +const transactionFx = createEffect( + ({ amountToSend, recipientAddress }) => + new Promise((res) => + setTimeout(res, 3000, { + amount: amountToSend, + recipientAddress, + }), + ), +); + +$isSigned.on(sign, () => true).reset(transactionFx); +$balance.on(transactionFx.doneData, (balance, { amount }) => balance - amount); + +sample({ + source: { + recipientAddress: $recipientAddress, + isSigned: $isSigned, + balance: $balance, + }, + clock: sentMoney, + filter: ({ isSigned, balance }, amountToSend) => isSigned && balance > amountToSend, + fn({ recipientAddress }, amountToSend) { + return { recipientAddress, amountToSend }; + }, + target: transactionFx, +}); + +$balance.watch((balance) => console.log("balance: ", balance)); +$isSigned.watch((isSigned) => console.log("is signed: ", isSigned)); + +sign(); +sentMoney(1000); +``` + +[Try it](https://share.effector.dev/XTxkCYC0) + + diff --git a/src/content/docs/en/api/effector/scopeBind.md b/src/content/docs/en/api/effector/scopeBind.md new file mode 100644 index 0000000..6b0aa99 --- /dev/null +++ b/src/content/docs/en/api/effector/scopeBind.md @@ -0,0 +1,115 @@ +--- +title: scopeBind +description: scopeBind is a method to bind unit to scope +redirectFrom: + - /api/effector/scopeBind + - /docs/api/effector/scopeBind +--- + +```ts +import { scopeBind } from "effector"; +``` + +`scopeBind` is a method to bind a unit (an Event or Effect) to a [Scope](/en/api/effector/Scope) to be called later. Effector supports imperative calling of events within watchers, however, there are instances where you must explicitly bind events to the scope, such as when triggering events from within `setTimeout` or `setInterval` callbacks. + +# Methods (#scopeBind-methods) + +## `scopeBind(event, options?)` (#scopeBind-methods-scopeBind-event) + +### Formulae (#scopeBind-methods-scopeBind-event-formulae) + +```ts +scopeBind(event: EventCallable): (payload: T) => void +scopeBind(event: EventCallable, options?: {scope?: Scope, safe?: boolean}): (payload: T) => void +``` + +### Arguments (#scopeBind-methods-scopeBind-event-arguments) + +1. `event` [_EventCallable_](/en/api/effector/Event) or [_Effect_](/en/api/effector/Effect) to be bound to the scope. +2. `options` (_Object_): Optional configuration. + - `scope` (_Scope_): Scope to bind event to. + - `safe` (_Boolean_): Flag for exception suppression if there is no scope. + +### Returns (#scopeBind-methods-scopeBind-event-returns) + +`(payload: T) => void` — A function with the same types as `event`. + +### Examples (#scopeBind-methods-scopeBind-event-examples) + +#### Basic Usage (#scopeBind-methods-scopeBind-event-examples-basic-usage) + +We are going to call `changeLocation` inside `history.listen` callback so there is no way for effector to associate event with corresponding scope, and we should explicitly bind event to scope using `scopeBind`. + +```ts +const $history = createStore(history); +const initHistory = createEvent(); +const changeLocation = createEvent(); + +const installHistoryFx = attach({ + source: $history, + effect: (history) => { + const locationUpdate = scopeBind(changeLocation); + + history.listen((location) => { + locationUpdate(location); + }); + }, +}); + +sample({ + clock: initHistory, + target: installHistoryFx, +}); +``` + +[See full example](https://share.effector.dev/nJo1zRil) + +## `scopeBind(callback, options?)` (#scopeBind-methods-scopeBind-callback) + +Binds arbitrary callback to a scope to be called later. The bound version of the function retains all properties of the original, e.g., if the original function would throw when called with a certain argument, the bound version will also throw under the same circumstances. + +:::info{title="since"} +Feature is available since `effector 23.1.0` release. +::: + +:::warning +To be compatible with the Fork API, callbacks **must** adhere to the same rules as `Effect` handlers: + +- Synchronous functions can be used as they are. +- Asynchronous functions must follow the [rules described in "Imperative Effect calls with scope"](/en/api/effector/scope/). + +::: + +### Formulae (#scopeBind-methods-scopeBind-callback-formulae) + +```ts +scopeBind(callback: T, options?: { scope?: Scope; safe?: boolean }): (payload: T) => void; +``` + +### Arguments (#scopeBind-methods-scopeBind-callback-arguments) + +1. `callback` (_Function_): Any function to be bound to the scope. +2. `options` (_Object_): Optional configuration. + - `scope` (_Scope_): Scope to bind the event to. + - `safe` (_Boolean_): Flag for exception suppression if there is no scope. + +### Returns (#scopeBind-methods-scopeBind-callback-returns) + +`(payload: T) => void` — A function with the same types as `callback`. + +### Examples (#scopeBind-methods-scopeBind-callback-examples) + +```ts +const locationChanged = createEvent(); + +const listenToHistoryFx = attach({ + source: $history, + effect: (history) => { + return history.listen( + scopeBind((location) => { + locationChanged(location); + }), + ); + }, +}); +``` diff --git a/src/content/docs/en/api/effector/serialize.md b/src/content/docs/en/api/effector/serialize.md new file mode 100644 index 0000000..4a282ce --- /dev/null +++ b/src/content/docs/en/api/effector/serialize.md @@ -0,0 +1,107 @@ +--- +title: serialize +description: serialize is a method for serializing application states within a scope +redirectFrom: + - /api/effector/serialize + - /docs/api/effector/serialize +--- + +```ts +import { serialize, type Scope } from "effector"; +``` + +# Methods (#methods) + +## `serialize(scope, params)` (#methods-serialize) + +A companion method for [_fork_](/en/api/effector/fork). It allows us to get a serialized value for all the store states within a [scope](/en/api/effector/Scope). The main purpose is an application state serialization on the server side during SSR. + +:::warning{title="Requirements"} +[_Babel plugin_](/en/api/effector/babel-plugin) or [_SWC plugin_](https://github.com/effector/swc-plugin) is required for using this method, as these plugins provide the SIDs for stores, which are required for stable state serialization. + +You can find deep-dive [explanation here](/en/explanation/sids) +::: + +### Formulae (#methods-serialize-formulae) + +```ts +serialize(scope: Scope, { ignore?: Array>; onlyChanges?: boolean }): {[sid: string]: any} +``` + +### Arguments (#methods-serialize-arguments) + +1. `scope` [_Scope_](/en/api/effector/Scope): a scope object (forked instance) +2. `ignore` Optional array of [_Store_](/en/api/effector/Store) to be omitted during serialization (added 20.14.0) +3. `onlyChanges` Optional boolean flag to ignore stores which didn't change in fork (prevent default values from being carried over network) + +:::warning{title="Deprecated"} +Since [effector 23.0.0](https://changelog.effector.dev/#effector-23-0-0) property `onlyChanges` is deprecated. +::: + +### Returns (#methods-serialize-returns) + +An object with store values using sids as a keys + +:::warning{title="Reminder"} +If a store [does not have a sid](/en/api/effector/babel-plugin#sid), its value will be omitted during serialization. +::: + +### Examples (#methods-serialize-examples) + +#### Serialize forked instance state (#methods-serialize-examples-serializeForkedInstanceState) + +```js +import { createDomain, fork, serialize } from "effector"; + +const domain = createDomain(); +const $store = domain.createStore(42); +const scope = fork(domain); + +console.log(serialize(scope)); // => {[sid]: 42} +``` + +[Try it](https://share.effector.dev/zlRJbjei) + +#### Using with `onlyChanges` (#methods-serialize-examples-usingWithOnlyChanges) + +With `onlyChanges`, this method will serialize only stores which were changed by some trigger during work or defined in `values` field by [fork](/en/api/effector/fork) or [hydrate(scope)](/en/api/effector/hydrate). Once being changed, a store will stay marked as changed in given scope even if it was turned back to the default state during work, otherwise client will not update that store on its side, which is unexpected and inconsistent. +This allows us to hydrate client state several times, for example, during route changes in next.js + +```js +import { createDomain, fork, serialize, hydrate } from "effector"; + +const app = createDomain(); + +/** store which we want to hydrate by server */ +const $title = app.createStore("dashboard"); + +/** store which is not used by server */ +const $clientTheme = app.createStore("light"); + +/** scope in client app */ +const clientScope = fork(app, { + values: new Map([ + [$clientTheme, "dark"], + [$title, "profile"], + ]), +}); + +/** server side scope of chats page created for each request */ +const chatsPageScope = fork(app, { + values: new Map([[$title, "chats"]]), +}); + +/** this object will contain only $title data + * as $clientTheme never changed in server scope */ +const chatsPageData = serialize(chatsPageScope, { onlyChanges: true }); +console.log(chatsPageData); +// => {'-l644hw': 'chats'} + +/** thereby, filling values from a server will touch only relevant stores */ +hydrate(clientScope, { values: chatsPageData }); + +console.log(clientScope.getState($clientTheme)); +// => dark +``` + +[Try it](https://share.effector.dev/BQhzISFV) diff --git a/src/content/docs/en/api/effector/split.md b/src/content/docs/en/api/effector/split.md new file mode 100644 index 0000000..bf7c365 --- /dev/null +++ b/src/content/docs/en/api/effector/split.md @@ -0,0 +1,398 @@ +--- +title: split +redirectFrom: + - /api/effector/split + - /docs/api/effector/split +--- + +```ts +import { split } from "effector"; +``` + +Choose one of cases by given conditions. It "splits" source unit into several events, which fires when payload matches their conditions. Works like pattern matching for payload values and external stores + +# Concepts (#concepts) + +## Case mode (#concepts-case-mode) + +Mode in which target case is selected by the name of its field. Case could be selected from data in `source` by [case function](/en/api/effector/split#case-function) or from external [case store](/en/api/effector/split#case-store) which kept current case name. After selection data from `source` will be sent to corresponding `cases[fieldName]` (if there is one), if none of the fields matches, then the data will be sent to `cases.__` (if there is one). + +**See also**: + +- [case store](/en/api/effector/split#case-store) +- [case function](/en/api/effector/split#case-function) + +## Matching mode (#concepts-matching-mode) + +Mode in which each case is sequentially matched by stores and functions in fields of `match` object. +If one of the fields got `true` from store value or return of function, then the data from `source` will be sent to corresponding `cases[fieldName]` (if there is one), if none of the fields matches, then the data will be sent to `cases.__` (if there is one) + +**See also**: + +- [matcher store](/en/api/effector/split#matcher-store) +- [matcher function](/en/api/effector/split#matcher-function) + +## Case store (#concepts-case-store) + +Store with a string which will be used to choose the case by its name. Placed directly in `match` field. + +```ts +split({ + source: Unit + // case store + match: Store<'first' | 'second'>, + cases: { + first: Unit, + second: Unit, + __?: Unit + } +}) +``` + +## Case function (#concepts-case-function) + +String-returning function which will be called with value from `source` to choose the case by its name. Placed directly in `match` field, [should be **pure**](/en/explanation/glossary#purity) + +```ts +split({ + source: Unit + // case function + match: (value: T) => 'first' | 'second', + cases: { + first: Unit, + second: Unit, + __?: Unit + } +}) +``` + +## Matcher store (#concepts-matcher-store) + +Boolean store which indicates whether to choose the particular case or try the next one. Placed in fields of `match` object, might be mixed with [matcher functions](/en/api/effector/split#matcher-function) + +```ts +split({ + source: Unit + match: { + // matcher store + first: Store, + second: Store + }, + cases: { + first: Unit, + second: Unit, + __?: Unit + } +}) +``` + +## Matcher function (#concepts-matcher-function) + +:::info +Case store, case function and matcher store are supported since [effector 21.8.0](https://changelog.effector.dev/#effector-21-8-0) +::: + +Boolean-returning function which indicates whether to choose the particular case or try the next one. Placed in fields of `match` object, might be mixed with [matcher stores](/en/api/effector/split#matcher-store), [should be **pure**](/en/explanation/glossary#purity) + +```ts +split({ + source: Unit + match: { + // matcher function + first: (value: T) => boolean, + second: (value: T) => boolean + }, + cases: { + first: Unit, + second: Unit, + __?: Unit + } +}) +``` + +# Methods (#methods) + +## `split({ source, match, cases })` (#methods-split-source-match-cases) + +:::info{title="since"} +[effector 21.0.0](https://changelog.effector.dev/#effector-21-0-0) +::: + +### Formulae (#methods-split-source-match-cases-formulae) + +```ts +split({ source, match, cases }); +``` + +```ts +split({ + source: Unit + // case function + match: (data: T) => 'a' | 'b', + cases: { + a: Unit, + b: Unit, + __?: Unit + } +}) +split({ + source: Unit + // case store + match: Store<'a' | 'b'>, + cases: { + a: Unit, + b: Unit, + __?: Unit + } +}) +split({ + source: Unit + match: { + // matcher function + a: (data: T) => boolean, + // matcher store + b: Store + }, + cases: { + a: Unit, + b: Unit, + __?: Unit + } +}) +``` + +### Arguments (#methods-split-source-match-cases-arguments) + +- `source`: [Unit](/en/explanation/glossary#common-unit) which will trigger computation in `split` +- `match`: Single [store with string](/en/api/effector/split#case-store), single [function which returns string](/en/api/effector/split#case-function) or object with [boolean stores](/en/api/effector/split#matching-store) and [functions which returns boolean](/en/api/effector/split#matching-function) +- `cases`: Object with [units](/en/explanation/glossary#common-unit) to which data will be passed from `source` after case selection + +### Returns (#methods-split-source-match-cases-returns) + +`void` + +### Examples (#methods-split-source-match-cases-examples) + +#### Basic (#methods-split-source-match-cases-examples-basic) + +```js +import { split, createEffect, createEvent } from "effector"; +const messageReceived = createEvent(); +const showTextPopup = createEvent(); +const playAudio = createEvent(); +const reportUnknownMessageTypeFx = createEffect(({ type }) => { + console.log("unknown message:", type); +}); + +split({ + source: messageReceived, + match: { + text: (msg) => msg.type === "text", + audio: (msg) => msg.type === "audio", + }, + cases: { + text: showTextPopup, + audio: playAudio, + __: reportUnknownMessageTypeFx, + }, +}); + +showTextPopup.watch(({ value }) => { + console.log("new message:", value); +}); + +messageReceived({ + type: "text", + value: "Hello", +}); +// => new message: Hello +messageReceived({ + type: "image", + imageUrl: "...", +}); +// => unknown message: image +``` + +[Try it](https://share.effector.dev/W6VYZbfH) + +#### Direct match (#methods-split-source-match-cases-examples-direct-match) + +You can match directly to store api as well: + +```js +import { split, createStore, createEvent, createApi } from "effector"; + +const messageReceived = createEvent(); + +const $textContent = createStore([]); + +split({ + source: messageReceived, + match: { + text: (msg) => msg.type === "text", + audio: (msg) => msg.type === "audio", + }, + cases: createApi($textContent, { + text: (list, { value }) => [...list, value], + audio: (list, { duration }) => [...list, `audio ${duration} ms`], + __: (list) => [...list, "unknown message"], + }), +}); + +$textContent.watch((messages) => { + console.log(messages); +}); + +messageReceived({ + type: "text", + value: "Hello", +}); +// => ['Hello'] +messageReceived({ + type: "image", + imageUrl: "...", +}); +// => ['Hello', 'unknown message'] +messageReceived({ + type: "audio", + duration: 500, +}); +// => ['Hello', 'unknown message', 'audio 500 ms'] +``` + +[Try it](https://share.effector.dev/32FNNk8H) + +## `split(source, match)` (#methods-split-source-match) + +:::info{title="since"} +[effector 20.0.0](https://changelog.effector.dev/#effector-20-0-0) +::: + +### Formulae (#methods-split-source-match-formulae) + +```ts +split(source, match); +``` + +### Arguments (#methods-split-source-match-arguments) + +1. `source`: [Unit](/en/explanation/glossary#common-unit) which will trigger computation in `split` +2. `match` (_Object_): Schema of cases, which uses names of resulting events as keys, and matching function*((value) => Boolean)* + +### Returns (#methods-split-source-match-returns) + +(Object) – Object, having keys, defined in `match` argument, plus `__`(two underscores) – which stands for `default` (no matches met) case. + +### Examples (#methods-split-source-match-examples) + +#### Basic (#methods-split-source-match-examples-basic) + +```js +import { createEvent, split } from "effector"; + +const message = createEvent(); + +const messageByAuthor = split(message, { + bob: ({ user }) => user === "bob", + alice: ({ user }) => user === "alice", +}); +messageByAuthor.bob.watch(({ text }) => { + console.log("[bob]: ", text); +}); +messageByAuthor.alice.watch(({ text }) => { + console.log("[alice]: ", text); +}); + +message({ user: "bob", text: "Hello" }); +// => [bob]: Hello +message({ user: "alice", text: "Hi bob" }); +// => [alice]: Hi bob + +/* default case, triggered if no one condition met */ +const { __: guest } = messageByAuthor; +guest.watch(({ text }) => { + console.log("[guest]: ", text); +}); +message({ user: "unregistered", text: "hi" }); +// => [guest]: hi +``` + +[Try it](https://share.effector.dev/QXZsR5yM) + +:::info +Only the first met match will trigger resulting event +::: + +#### Another (#methods-split-source-match-examples-another) + +```js +import { createEvent, split } from "effector"; + +const message = createEvent(); + +const { short, long, medium } = split(message, { + short: (m) => m.length <= 5, + medium: (m) => m.length > 5 && m.length <= 10, + long: (m) => m.length > 10, +}); + +short.watch((m) => console.log(`short message '${m}'`)); +medium.watch((m) => console.log(`medium message '${m}'`)); +long.watch((m) => console.log(`long message '${m}'`)); + +message("Hello, Bob!"); +// => long message 'Hello, Bob!' + +message("Hi!"); +// => short message 'Hi!' +``` + +[Try it](https://share.effector.dev/ke2tM78I) + +## `split({ source, clock?, match, cases })` (#methods-split-source-clock-match-cases) + +:::info{title="since"} +[effector 22.2.0](https://changelog.effector.dev/#effector-22-2-0) +::: + +It works the same as [split with cases](/en/api/effector/split#methods-split-source-match-cases), however computations in `split` will be started after `clock` is triggered. + +### Formulae (#methods-split-source-clock-match-cases-formulae) + +```js +split({source, clock?, match, cases}) +``` + +### Arguments (#methods-split-source-clock-match-cases-arguments) + +TBD + +### Examples (#methods-split-source-clock-match-cases-examples) + +```js +const options = ["save", "delete", "forward"]; +const $message = createStore({ id: 1, text: "Bring me a cup of coffee, please!" }); +const $mode = createStore(""); +const selectedMessageOption = createEvent(); +const saveMessageFx = createEffect(() => "save"); +const forwardMessageFx = createEffect(() => "forward"); +const deleteMessageFx = createEffect(() => "delete"); + +$mode.on(selectedMessageOption, (_, opt) => options.find((item) => item === opt)); + +split({ + source: $message, + clock: selectedMessageOption, + match: $mode, + cases: { + save: saveMessageFx, + delete: deleteMessageFx, + forward: forwardMessageFx, + }, +}); + +selectedMessageOption("delet"); // nothing happens +selectedMessageOption("delete"); +``` + +[Try it](https://share.effector.dev/VJmD5KdN) diff --git a/src/content/docs/en/api/effector/withRegion.md b/src/content/docs/en/api/effector/withRegion.md new file mode 100644 index 0000000..24ac878 --- /dev/null +++ b/src/content/docs/en/api/effector/withRegion.md @@ -0,0 +1,62 @@ +--- +title: withRegion +redirectFrom: + - /api/effector/withRegion + - /docs/api/effector/withRegion +--- + +```ts +import { withRegion } from "effector"; +``` + +The method is based on the idea of region-based memory management (see [Region-based memory management](https://en.wikipedia.org/wiki/Region-based_memory_management) for reference). + +# Methods (#methods) + +## `withRegion(unit, callback)` (#methods-withRegion) + +:::info{title="since"} +[effector 20.11.0](https://changelog.effector.dev/#effector-20-11-0) +::: + +The method allows to explicitly transfer ownership of all units (including links created with `sample`, `forward`, etc...) defined in the callback to `unit`. As an implication, all the created links will be erased as soon as `clearNode` is called on [_Unit_](/en/explanation/glossary#unit). + +### Formulae (#methods-withRegion-unit-callback-formulae) + +```ts +withRegion(unit: Unit | Step, callback: () => void): void +``` + +### Arguments (#methods-withRegion-unit-callback-arguments) + +1. `unit`: _Unit_ | _Step_ — which will serve as "local area" or "region" owning all the units created within the provided callback. +2. `callback`: `() => void` — The callback where all the relevant units should be defined. + +### Examples (#methods-withRegion-unit-callback-examples) + +```js +import { createDomain, createEvent, restore, withRegion, clearNode } from "effector"; + +const first = createEvent(); +const second = createEvent(); +const $store = restore(first, ""); +const domain = createDomain(); + +withRegion(domain, () => { + // Following links created with `forward` or `sample` are owned by the provided unit `domain` + // and will be disposed as soon as `clearNode` is called on `domain`. + forward({ + from: second, + to: first, + }); +}); + +$store.watch(console.log); + +first("hello"); +second("world"); + +clearNode(domain); + +second("will not trigger updates of `$store`"); +``` diff --git a/src/content/docs/en/api/index.mdx b/src/content/docs/en/api/index.mdx new file mode 100644 index 0000000..3762232 --- /dev/null +++ b/src/content/docs/en/api/index.mdx @@ -0,0 +1,49 @@ +--- +title: API Reference +--- + +import FeatureCard from "@components/FeatureCard.astro"; +import IconReact from "@icons/React.astro"; +import IconVue from "@icons/Vue.astro"; +import IconSolid from "@icons/Solid.astro"; +import IconEffector from "@icons/Effector.astro"; + +# Effector Packages + +Effector provides a range of packages to accommodate your specific needs. There are packages supported by the core team. + +
    + + + + + + + + + + + + + + + + + +
    diff --git a/src/content/docs/en/conventions/naming.md b/src/content/docs/en/conventions/naming.md new file mode 100644 index 0000000..adb3a3f --- /dev/null +++ b/src/content/docs/en/conventions/naming.md @@ -0,0 +1,41 @@ +--- +title: Naming +description: Naming conventions for stores, events and effects +redirectFrom: + - /docs/conventions/naming + - /conventions/naming +--- + +First of all, to avoid any misconceptions and get better developer experience for all of us. This document contains several pretty simple rules to keep consistency between different projects written with effector. + +## Stores naming + +Your stores should be distinguished by a `$`. The choice between prefix or postfix is mostly a matter of personal preference. This should be done to have a better search experience in your IDE. + +```js +const $user = createStore({}); +``` + +## Effect naming + +It is recommended to add the `Fx` postfix to the end of your effects. It will let you differentiate your effects from the events. + +```js +const fetchUserFx = createEffect(async () => { + const res = await fetch("my pretty url"); + return res.json(); +}); +``` + +## Event naming + +There are no any specific rules for this. However, the proposal is to name events that directly trigger store updates, as like as they have already happened. + +```js +const emailChanged = createEvent(); + +$user.on(emailChanged, (state, email) => ({ + ...state, + email, +})); +``` diff --git a/src/content/docs/en/core-principles/own-your-data.md b/src/content/docs/en/core-principles/own-your-data.md new file mode 100644 index 0000000..aa24bdf --- /dev/null +++ b/src/content/docs/en/core-principles/own-your-data.md @@ -0,0 +1,11 @@ +--- +title: Own your data principle +redirectFrom: + - /core-principles/own-your-data +--- + +Effector will not force you to turn your data into abstract or custom entities – your data are your data and effector only provides infrastructure to handle it. + +That means that you should not use effector's entities (Store, Event, etc) to represent your data – only to describe the surrounding logic. + +- Effector Store with an array of other Stores inside – anti-pattern, use key-value pattern instead. diff --git a/src/content/docs/en/core-principles/releases.md b/src/content/docs/en/core-principles/releases.md new file mode 100644 index 0000000..278fdb5 --- /dev/null +++ b/src/content/docs/en/core-principles/releases.md @@ -0,0 +1,26 @@ +--- +title: Releases policy +redirectFrom: + - /core-principles/releases +--- + +The main goal of effector is to **make developer experience better**, as a part of this strategy we are committing to some rules of effector releases. + +## No breaking changes without prior deprecation + +Before each breaking change, the effector must provide a deprecation warning for **at least a year before.** + +For example: + +- When version 22 was released, feature "A" was marked as deprecated. The library gives a warning to the console when it is used. +- A year later, in version 23 release, feature "A" is removed. + +## Release cycle + +Major updates (i.e. with breaking changes) of the effector are released **no more than once a year.** + +Minor and patch updates (i.e., with fixes and new features) are released when ready. If a new feature requires breaking changes – it is also released in a major update. + +This is necessary to allow developers to plan their work smoothly, taking into account possible changes in effector. + +It also obliges effector maintainers to be extremely careful when designing new features and breaking changes to old library features, because the opportunity to remove or heavily modify something in the public API only appears once every two years. diff --git a/src/content/docs/en/core-principles/testing.md b/src/content/docs/en/core-principles/testing.md new file mode 100644 index 0000000..9e0a399 --- /dev/null +++ b/src/content/docs/en/core-principles/testing.md @@ -0,0 +1,13 @@ +--- +title: Testing +redirectFrom: + - /core-principles/testing +--- + +We believe that the only way to write good code is to test it. + +This has the following consequences: + +- Every effector function is created with testing in mind, so users don't have to come up with complicated ways to test their code. +- Effector is heavily tested itself. +- Any fixes or features without proper testing **will not be accepted**. diff --git a/src/content/docs/en/core-principles/typings.md b/src/content/docs/en/core-principles/typings.md new file mode 100644 index 0000000..a5e7833 --- /dev/null +++ b/src/content/docs/en/core-principles/typings.md @@ -0,0 +1,17 @@ +--- +title: Typings +redirectFrom: + - /core-principles/typing +--- + +We believe that good type support is a necessary part of good DX. + +## TypeScript + +Effector is aiming to provide first-class support of TypeScript types. +Effector is written in TypeScript, and there are [a lot of type-tests in the codebase, including auto-generated ones.](https://github.com/effector/effector/tree/master/src/types) + +However, TypeScript itself does not aim for [applying a sound or "provably correct" type system](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals#non-goals), instead it strikes a balance between correctness and productivity. +So, we cannot guarantee that all the types are correct, but we are going to do our best to provide the best possible type inference. + +Public typings of effector are [located here](https://github.com/effector/effector/blob/master/packages/effector/index.d.ts), separate from the main code of the library. diff --git a/src/content/docs/en/ecosystem-development/unit-shape-protocol.md b/src/content/docs/en/ecosystem-development/unit-shape-protocol.md new file mode 100644 index 0000000..211e8aa --- /dev/null +++ b/src/content/docs/en/ecosystem-development/unit-shape-protocol.md @@ -0,0 +1,69 @@ +--- +title: Protocol @@unitShape +description: Re-use UI-library bindings for your own effector-based libraries +redirectFrom: + - /docs/ecosystem-development/unit-shape-protocol + - /ecosystem-development/unit-shape-protocol +--- + +:::info +Available since [effector-react 22.4.0](https://changelog.effector.dev/#effector-react-22-4-0), effector-solid 0.22.7 +::: + +Effector provides a way to use units ([Stores](/en/api/effector/Store), [Events](/en/api/effector/Event), [Effects](/en/api/effector/Effect)) in UI libraries with a special bindings like `effector-react`, `effector-solid`, etc. Normally, they allow binding any shape of units to a UI-framework: + +```ts +import { createStore } from "effector"; +import { useUnit } from "effector-react"; + +const $value = createStore("Hello!"); + +const Component = () => { + const { value } = useUnit({ value: $value }); + + return

    {value}

    ; +}; +``` + +But what if you want to create your own library on top of effector with some custom entities? For example, you want to create a router library with a custom `Route` entity, and you want to allow users to use it with `effector-react` bindings: + +```ts +import { createRoute } from "my-router-library"; +import { useUnit } from "effector-react"; + +const mainPageRoute = createRoute(/* ... */); + +const Component = () => { + const { params } = useUnit(mainPageRoute); + + return

    {params.name}

    ; +}; +``` + +It is possible with the `@@unitShape` protocol. It allows defining the shape of a unit in the custom entity and then using it in UI libraries. Just add field `@@unitShape` with a function that return shape of units to your entity: + +```ts +function createRoute(/* ... */) { + const $params = createStore(/* ... */); + + return { + "@@unitShape": () => ({ + params: $params, + }), + }; +} +``` + +## FAQ + +--- + +**Q**: How frequently `@@unitShape`-function is called? + +**A**: As many times as `useUnit` itself is called – it depends on a UI-library. For example, `effector-react` calls it as any other hook – once per component render, but `effector-solid` calls `useUnit` once per component mount. + +--- + +**Q**: How can I know what UI-library is used for particular `@@unitShape` call? + +**A**: You cannot. `@@unitShape` has to be universal for all UI-libraries either has to check what UI-library is used inside by UI-library methods (like `Context` in React or Solid). diff --git a/src/content/docs/en/explanation/computation-priority.md b/src/content/docs/en/explanation/computation-priority.md new file mode 100644 index 0000000..7cd9654 --- /dev/null +++ b/src/content/docs/en/explanation/computation-priority.md @@ -0,0 +1,70 @@ +--- +title: Computation priority +description: How effector calculates units +redirectFrom: + - /en/advanced-guide/computation-priority + - /explanation/computation-priority + - /docs/advanced-guide/computation-priority + - /docs/explanation/computation-priority +--- + +For sure, you've noticed that function [should be pure](/en/explanation/glossary#purity)... or watch if there is a place +for side effect. We will talk about this in the current section – **Computation priority** + +A real example of queue priority — people waiting for medical treatment in a hospital, extreme emergency cases will have +the highest priority and move to the start of the queue and less significant to the end. + +Computation priority allows us to have side effects, and it's one of the main reasons to create this concept: + +- Letting pure functions to execute first. +- Side effects can follow a consistent state of the application. + +Actually, pure computation cannot be observed out of the scope, therefore, the definition of **_pure computation_** used +in this library gives us an opportunity to optimize grouping. + +Priority: + +[Source code](https://github.com/effector/effector/blob/master/src/effector/kernel.ts#L169) + +``` +1. child -> forward +2. pure -> map, on +3. sampler -> sample, guard, combine +4. effect -> watch, effect handler +``` + +> Whenever you allow side effects in pure computations, the library will work by the worst scenario. Thereby, increasing non-consistency of application and breaking pure computations. Don't ignore that. + +Let's consider prioritizing in the example below. + +```js +let count = 0; +const fx = createEffect(() => { + // side effect 1 + count += 1; +}); + +fx.done.watch(() => { + // side effect 1 already executed + console.log("expect count to be 1", count === 1); + // side effect 2 + count += 1; +}); + +fx(); +// side effect 1 already executed +// side effect 2 already executed as well +// that's what we expected to happen +// that's watchmen effect +console.log("expect count to be 2", count === 2); +// example which violated that agreement: setState in react +// which defer any side effect long after setState call itself +``` + +[Try it](https://share.effector.dev/IRAmt9Uq) + +:::info +Whenever a library notices side effect in a pure function it moves it to the end of the [**priority queue**](https://en.wikipedia.org/wiki/Priority_queue). +::: + +We hope that this information cleared some things on how the library works. diff --git a/src/content/docs/en/explanation/events.md b/src/content/docs/en/explanation/events.md new file mode 100644 index 0000000..59c84ab --- /dev/null +++ b/src/content/docs/en/explanation/events.md @@ -0,0 +1,183 @@ +--- +title: Events +keywords: + - event + - unit +description: How events works, where to use +--- + +The **Event** in effector represents a user action, a step in the application process, a command to execute, or an intention to make modifications, among other things. This unit is designed to be a carrier of information/intention/state within the application, not the holder of a state. + +In most situations, it is recommended to create events directly within the module, rather than placing them within conditional statements or classes, in order to maintain simplicity and readability. An exception to this recommendation is the use of factory functions; however, these should also be invoked at the root level of the module. + +:::info +Event instances persist throughout the entire runtime of the application and inherently represent a portion of the business logic. + +Attempting to delete instances and clear memory for the purpose of saving resources is not advised, as it may adversely impact the functionality and performance of the application. +::: + +## Calling the event (#event-calling) + +There are two ways to trigger event: imperative and declarative. + +The **imperative** method involves invoking the event as if it were a function: + +```ts +import { createEvent } from "effector"; + +const callHappened = createEvent(); + +callHappened(); // event triggered +``` + +The **declarative** approach utilizes the event as a target for operators, such as sample, or as an argument when passed into factory functions: + +```ts +import { createEvent, sample } from "effector"; + +const firstTriggered = createEvent(); +const secondTriggered = createEvent(); + +sample({ + clock: firstTriggered, + target: secondTriggered, +}); +``` + +> When the `firstTriggered` event is invoked, the `secondTriggered` event will be subsequently called, creating a sequence of events. + +This method is employed to link various units within a single event chain. In most cases, the chain will have multiple branches, allowing for diverse interactions and processes within the application logic. + +**You need to know:** In Effector, any event supports only **a single argument**. +It is not possible to call an event with two or more arguments, as in `someEvent(first, second)`. + +All arguments beyond the first will be ignored. +The core team has implemented this rule for specific reasons related to the design and functionality. +This approach enables the argument to be accessed in any situation without adding types complexity. + +If multiple arguments need to be passed, encapsulate them within an object: + +```ts +import { createEvent } from "effector"; +const requestReceived = createEvent<{ id: number; title: string }>(); +requestReceived({ id: 1, title: "example" }); +``` + +This rule also contributes to the clarity of each argument's meaning, both at the call side and subscription side. It promotes clean and organized code, making it easier to understand and maintain. + +## Watching the event (#event-watch) + +To ascertain when an event is called, effector and its ecosystem offer various methods with distinct capabilities. Debugging is the primary use case for this purpose, and we highly recommend using [`patronum/debug`](https://patronum.effector.dev/methods/debug/) to display when an event is triggered and the argument it carries. + +```ts +import { createEvent, sample } from "effector"; +import { debug } from "patronum"; + +const firstTriggered = createEvent(); +const secondTriggered = createEvent(); + +sample({ + clock: firstTriggered, + target: secondTriggered, +}); + +debug(firstTriggered, secondTriggered); + +firstTriggered(); +// => [event] firstTriggered undefined +// => [event] secondTriggered undefined +``` + +However, if your environment does not permit the addition of further dependencies, you may use the `.watch()` method with caution. + +```ts +import { createEvent, sample } from "effector"; + +const firstTriggered = createEvent(); +const secondTriggered = createEvent(); + +sample({ + clock: firstTriggered, + target: secondTriggered, +}); + +firstTriggered.watch(() => console.info("[event] firstTriggered")); +secondTriggered.watch(() => console.info("[event] secondTriggered")); + +firstTriggered(); +// => [event] firstTriggered +// => [event] secondTriggered +``` + +:::tip{title="Keep in mind"} +The `watch` method neither handles nor reports exceptions, manages the completion of asynchronous operations, nor addresses data race issues. + +Its primary intended use is for short-term debugging and logging purposes. +::: + +## Working with TypeScript (#typescript) + +When an event is invoked, TypeScript will verify that the type of the argument passed matches the type defined in the event, ensuring consistency and type safety within the code. + +This is also works for operators like `sample` or `split`: + +```ts +import { sample, createEvent } from "effector"; + +const someHappened = createEvent(); +const anotherHappened = createEvent(); + +sample({ + // @ts-expect-error error: "clock should extend target type"; targets: { clockType: number; targetType: string; } + clock: someHappened, + target: anotherHappened, +}); +``` + +[Try it](https://tsplay.dev/WyoPKN) + +To specify the argument type for an event, it is essential to first determine the intended purpose, what do you want to do with this event: + +- If you intend to **invoke** an event or use it as a target, you should utilize the `EventCallable` type. +- If your goal is to **subscribe** to updates, or use the event as a `clock` or `source`, you should employ the `Event` type. + +_Where `T` represents the type of the event's argument._ + +```ts +import { type Event, createStore, createEvent } from "effector"; + +function createCounter(counterChanged: Event) { + const $counter = createStore(0); + sample({ + clock: $counter, + target: counterChanged, + }); +} + +const whenCounterChanged = createEvent(); +createCounter(whenCounterChanged); +``` + +## Using `Event` (#event-using) + +A `Event` is a super type of `EventCallable` with different approach. Firstly, invoking a `Event` is not allowed, and it cannot be used as a target in the sample operator, and so on. + +The primary purpose of a `Event` is to be triggered by internal code withing the effector library or ecosystem. For instance, the `.map()` method returns a `Event`, which is subsequently called by the `.map()` method itself. + +To utilize the `Event` type, simply import it from the `"effector"` package and integrate it into your code as needed: + +```ts +import { type Event, createStore, createEvent } from "effector"; + +function createCounter(increment: Event) { + const $counter = createStore(0); + + $counter.on(increment, (count) => count + 1); +} + +const incrementCounter = createEvent(); +createCounter(incrementCounter); +incrementCounter(); +``` + +It is allowed to pass `EventCallable` into argument with type `Event`. The primary purpose of Event is to accept any event type while explicitly signaling the code's intention to listen to the event, rather than invoking it. diff --git a/src/content/docs/en/explanation/glossary.md b/src/content/docs/en/explanation/glossary.md new file mode 100644 index 0000000..f09aebc --- /dev/null +++ b/src/content/docs/en/explanation/glossary.md @@ -0,0 +1,162 @@ +--- +title: Glossary +description: Glossary of basic terms in effector +redirectFrom: + - /docs/glossary + - /docs/explanation/glossary + - /explanation/glossary + - /en/glossary +--- + +Glossary of basic terms in effector. + +## Event (#event) + +_Event_ is a function you can subscribe to. It can be an intention to change the store, indication of something happening in the application, a command to be executed, aggregated analytics trigger and so on. + +[Event](/en/api/effector/Event) in api documentation + +## Store (#store) + +_Store_ is an object that holds state. +There can be multiple stores. + +[Store](/en/api/effector/Store) in api documentation + +## Effect (#effect) + +_Effect_ is a container for (possibly async) side effects. +It exposes special events and stores, such as `.pending`, `.done`, `.fail`, `.finally`, etc... + +It can be safely used in place of the original async function. + +It returns promise with the result of a function call. + +The only requirement for the function: + +- **Must** have zero or one argument + +[Effect](/en/api/effector/Effect) in api documentation + +## Domain (#domain) + +_Domain_ is a namespace for your events, stores and effects. + +Domains are notified when events, stores, effects, or nested domains are created via `.onCreateEvent`, `.onCreateStore`, `.onCreateEffect`, `.onCreateDomain` methods. + +It is useful for logging or other side effects. + +[Domain](/en/api/effector/Domain) in api documentation + +## Unit (#unit) + +Data type used to describe business logic of applications. Most of the effector methods deal with unit processing. +There are five unit types: [Store](/en/api/effector/Store), [Event](/en/api/effector/Event), [Effect](/en/api/effector/Effect), [Domain](/en/api/effector/Domain) and [Scope](/en/api/effector/Scope). + +## Common unit (#common-unit) + +Common units can be used to trigger updates of other units. There are three common unit types: [Store](/en/api/effector/Store), [Event](/en/api/effector/Event) and [Effect](/en/api/effector/Effect). **When a method accepts units, it means that it accepts events, effects, and stores** as a source of reactive updates. + +## Purity (#purity) + +Most of the functions in api must not call other events or effects: it's easier to reason about application's data flow when imperative triggers are grouped inside watchers and effect handlers rather than spread across entire business logic. + +**Correct**, imperative: + +```js +import { createStore, createEvent } from "effector"; + +const submitLoginSize = createEvent(); + +const $login = createStore("guest"); +const $loginSize = $login.map((login) => login.length); + +$loginSize.watch((size) => { + submitLoginSize(size); +}); +``` + +[Try it](https://share.effector.dev/D5hV8C70) + +Reference: [Store.map](/en/api/effector/Store#map-fn), [Store.watch](/en/api/effector/Store#watch-watcher) + +**Better**, declarative: + +```js +import { createStore, createEvent, sample } from "effector"; + +const submitLoginSize = createEvent(); + +const $login = createStore("guest"); +const $loginSize = $login.map((login) => login.length); + +sample({ + clock: $loginSize, + target: submitLoginSize, +}); +``` + +[Try it](https://share.effector.dev/it0gXQLI) + +Reference: [sample](/en/api/effector/sample) + +**Incorrect**: + +```js +import { createStore, createEvent } from "effector"; + +const submitLoginSize = createEvent(); + +const $login = createStore("guest"); +const $loginSize = $login.map((login) => { + // no! use `sample` instead + submitLoginSize(login.length); + return login.length; +}); +``` + +## Reducer (#reducer) + +```typescript +type StoreReducer = (state: State, payload: E) => State | void; +type EventOrEffectReducer = (state: T, payload: E) => T; +``` + +_Reducer_ calculates a new state given the previous state and an event's payload. For stores, if reducer returns undefined or the same state (`===`), then there will be no update for a given store. + +## Watcher (#watcher) + +```typescript +type Watcher = (update: T) => any; +``` + +_Watcher_ is used for **side effects**. Accepted by [Event.watch](/en/api/effector/Event#watch-watcher), [Store.watch](/en/api/effector/Store#watchwatcher) and [Domain.onCreate\* hooks](/en/api/effector/Domain#oncreateeventhook). Return value of a watcher is ignored. + +## Subscription (#subscription) + +```ts +import { type Subscription } from "effector"; +``` + +Looks like: + +```typescript +type Subscription = { + (): void; + unsubscribe(): void; +}; +``` + +**Function**, returned by [forward](/en/api/effector/forward), [Event.watch](/en/api/effector/Event#event-watch-watcher), [Store.watch](/en/api/effector/Store#methods-watch-watcher) and some other methods. Used for cancelling a subscription. After the first call, subscription will do nothing. + +:::warning +**Managing subscriptions manually distracts from business logic improvements.** +

    +Effector provides a wide range of features to minimize the need to remove subscriptions. This sets it apart from most other reactive libraries. +::: + +[effect]: /en/api/effector/Effect +[store]: /en/api/effector/Store +[event]: /en/api/effector/Event +[domain]: /en/api/effector/Domain +[scope]: /en/api/effector/Scope diff --git a/src/content/docs/en/explanation/prior-art.md b/src/content/docs/en/explanation/prior-art.md new file mode 100644 index 0000000..b2c7df0 --- /dev/null +++ b/src/content/docs/en/explanation/prior-art.md @@ -0,0 +1,33 @@ +--- +id: prior-art +title: Prior Art +redirectFrom: + - /en/advanced-guide/prior-art + - /explanation/prior-art + - /docs/explanation/prior-art + - /advanced-guide/prior-art +--- + +## Papers + +- **Functional Pearl. Weaving a Web** [[pdf]](https://zero-bias-papers.s3-eu-west-1.amazonaws.com/weaver+zipper.pdf) _Ralf Hinze and Johan Jeuring_ +- **A graph model of data and workflow provenance** [[pdf]](https://zero-bias-papers.s3-eu-west-1.amazonaws.com/A+graph+model+of+data+and+workflow+provenance.pdf)
    _Umut Acar, Peter Buneman, James Cheney, Jan Van den Bussche, Natalia Kwasnikowska and Stijn Vansummeren_ +- **An Applicative Control-Flow Graph Based on Huet’s Zipper** [[pdf]](http://zero-bias-papers.s3-website-eu-west-1.amazonaws.com/zipcfg.pdf)
    _Norman Ramsey and Joao Dias_ +- **Elm: Concurrent FRP for Functional GUIs** [[pdf]](https://zero-bias-papers.s3-eu-west-1.amazonaws.com/elm-concurrent-frp.pdf)
    _Evan Czaplicki_ +- **Inductive Graphs and Functional Graph Algorithms** [[pdf]](https://zero-bias-papers.s3-eu-west-1.amazonaws.com/Inductive+Graphs+and+Functional+Graph+Algorithms.pdf)
    _Martin Erwig_ +- **Notes on Graph Algorithms Used in Optimizing Compilers** [[pdf]](https://zero-bias-papers.s3-eu-west-1.amazonaws.com/Graph+Algorithms+Used+in+Optimizing+Compilers.pdf)
    _Carl D. Offner_ +- **Backtracking, Interleaving, and Terminating Monad Transformers** [[pdf]](https://zero-bias-papers.s3-eu-west-1.amazonaws.com/Backtracking%2C+Interleaving%2C+and+Terminating+Monad+Transformers.pdf)
    _Oleg Kiselyov, Chung-chieh Shan, Daniel P. Friedman and Amr Sabry_ +- **Typed Tagless Final Interpreters** [[pdf]](https://zero-bias-papers.s3-eu-west-1.amazonaws.com/Typed+Tagless+Final+Interpreters.pdf) _Oleg Kiselyov_ + +## Books + +- **Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions** [[book]](https://www.amazon.com/o/asin/0321200683/ref=nosim/enterpriseint-20), [[messaging patterns overview]](https://www.enterpriseintegrationpatterns.com/patterns/messaging/)
    _Gregor Hohpe and Bobby Woolf_ + +## API + +- [re-frame](https://github.com/day8/re-frame) +- [flux](https://facebook.github.io/flux/) +- [redux](https://redux.js.org/) +- [redux-act](https://github.com/pauldijou/redux-act) +- [most](https://github.com/cujojs/most) +- nodejs [events](https://nodejs.org/dist/latest-v12.x/docs/api/events.html#events_emitter_on_eventname_listener) diff --git a/src/content/docs/en/explanation/sids.md b/src/content/docs/en/explanation/sids.md new file mode 100644 index 0000000..4d158ff --- /dev/null +++ b/src/content/docs/en/explanation/sids.md @@ -0,0 +1,322 @@ +--- +title: SIDs +description: Stable IDentifiers +redirectFrom: + - /en/explanation/sids + - /explanation/sids + - /docs/explanation/sids +--- + +The SID is a Stable IDentifier of an effector unit. It can be used for different purposes, but the main use case is Server Side Rendering. + +The SIDs have two important properties: + +1. They are **unique** – each SID for each unit should be unique. +2. They are **stable** between different environments – each SID of each unit in some environment (e.g. server code) should be equal to a SID of this unit in any other environment (e.g. client code). + +## How to add SIDs (#how-to-add-sids) + +The SIDs can be added manually and automatically, but it is important that they are set before any bundling happens — otherwise, there is no way to guarantee stability. + +### Manual way (#how-to-add-sids-manual) + +```tsx +import { createStore } from "effector"; + +export const $myStore = createStore(42, { sid: "my-stable-id" }); +``` + +It is important, that all SIDs are also **unique**. If you are using the manual way, you have to guarantee that by yourself. + +### Automatic way (#how-to-add-sids-automatic) + +For sure, manually creating unique ids is a quite boring job. + +Thankfully, there are [`effector/babel-plugin`](/api/effector/babel-plugin) and [`@effector/swc-plugin`](https://github.com/effector/swc-plugin), which will provide SIDs automatically. + +Because code-transpilation tools are working at the file level and are run before bundling happens – it is possible to make SIDs **stable** for every environment. + +:::tip +It is preferable to use [`effector/babel-plugin`](/api/effector/babel-plugin) or [`@effector/swc-plugin`](https://github.com/effector/swc-plugin) instead of adding SIDs manually. +::: + +## Why do we need Stable Identifiers (#why-do-we-need-sids) + +Because of **multi-store** architecture, Effector code in the applications is written in **atomic** and **distributed** way and there is no single central "root store" or "controller", which needs to be notified about all stores/reducers/etc, created anywhere in the app. + +And since there is no central "root store" – no additional setup (like Reducer Manager, etc) is required to support micro-frontends and code-splitting, everything works out of the box. + +**Code example** + +Notice, that there is no central point at all – any event of any "feature" can be triggered from anywhere and the rest of them will react accordingly. + +```tsx +// src/features/first-name/model.ts +import { createStore, createEvent } from "effector"; + +export const firstNameChanged = createEvent(); +export const $firstName = createStore(""); + +$firstName.on(firstNameChanged, (_, firstName) => firstName); + +// src/features/last-name/model.ts +import { createStore, createEvent } from "effector"; + +export const lastNameChanged = createEvent(); +export const $lastName = createStore(""); + +$lastName.on(lastNameChanged, (_, lastName) => lastName); + +// src/features/form/model.ts +import { createEvent, sample, combine } from "effector"; + +import { $firstName, firstNameChanged } from "@/features/first-name"; +import { $lastName, lastNameChanged } from "@/features/last-name"; + +export const formValuesFilled = createEvent<{ firstName: string; lastName: string }>(); + +export const $fullName = combine($firstName, $lastName, (first, last) => `${first} ${last}`); + +sample({ + clock: formValuesFilled, + fn: (values) => values.firstName, + target: firstNameChanged, +}); + +sample({ + clock: formValuesFilled, + fn: (values) => values.lastName, + target: lastNameChanged, +}); +``` + +If this application was a SPA or any other kind of client-only app — this would be the end of the article. + +### Serialization boundary (#serialization-boundary) + +But in the case of Server Side Rendering, there is always a **serialization boundary** — a point, where all state is stringified, added to a server response, and sent to a client browser. + +#### Problem (#serialization-boundary-problem) + +And at this point we **still need to collect the states of all stores of the app** somehow! + +Also, after the client browser has received a page — we need to "hydrate" everything back: unpack these values at the client and add this "server-calculated" state to client-side instances of all stores. + +#### Solution (#serialization-boundary-solution) + +This is a hard problem and to solve this, `effector` needs a way to connect the "server-calculated" state of some store with its client-side instance. + +While **it could be** done by introducing a "root store" or something like that, which would manage store instances and their state for us, it would also bring to us all the downsides of this approach, e.g. much more complicated code-splitting – so this is still undesirable. + +This is where SIDs will help us a lot. +Because SID is, by definition, the same for the same store in any environment, `effector` can simply rely on it to handle state serializing and hydration. + +#### Example (#serialization-boundary-example) + +This is a generic server-side rendering handler. The `renderHtmlToString` function is an implementation detail, which will depend on the framework you use. + +```tsx +// src/server/handler.ts +import { fork, allSettled, serialize } from "effector"; + +import { formValuesFilled } from "@/features/form"; + +async function handleServerRequest(req) { + const scope = fork(); // creates isolated container for application state + + // calculates the state of the app in this scope + await allSettled(formValuesFilled, { + scope, + params: { + firstName: "John", + lastName: "Doe", + }, + }); + + // extract scope values to simple js object of `{[storeSid]: storeState}` + const values = serialize(scope); + + const serializedState = JSON.stringify(values); + + return renderHtmlToString({ + scripts: [ + ` + + `, + ], + }); +} +``` + +Notice, that there are no direct imports of any stores of the application here. +The state is collected automatically and its serialized version already has all the information, which will be needed for hydration. + +When the generated response arrives in a client browser, the server state must be hydrated to the client stores. +Thanks to SIDs, state hydration also works automatically: + +```tsx +// src/client/index.ts +import { Provider } from "effector-react"; + +const serverState = window._SERVER_STATE_; + +const clientScope = fork({ + values: serverState, // simply assign server state to scope +}); + +clientScope.getState($lastName); // "Doe" + +hydrateApp( + + + , +); +``` + +At this point, the state of all stores in the `clientScope` is the same, as it was at the server and there was **zero** manual work to do it. + +## Unique SIDs (#unique-sids) + +The stability of SIDs is ensured by the fact, that they are added to the code before any bundling has happened. + +But since both `babel` and `swc` plugins are able "to see" contents of one file at each moment, there is a case, where SIDs will be stable, but **might not be unique** + +To understand why, we need to dive a bit deeper into plugin internals. + +Both `effector` plugins use the same approach to code transformation. Basically, they do two things: + +1. Add `sid`-s and any other meta-information to raw Effector's factories calls, like `createStore` or `createEvent`. +2. Wrap any custom factories with `withFactory` helper that allows you to make `sid`-s of inner units unique as well. + +### Built-in unit factories (#built-in-factories) + +Let's take a look at the first case. For the following source code: + +```ts +const $name = createStore(null); +``` + +The plugin will apply these transformations: + +```ts +const $name = createStore(null, { sid: "j3l44" }); +``` + +:::tip +Plugins create `sid`-s as a hash of the location in the source code of a unit. It allows making `sid`-s unique and stable. +::: + +### Custom factories (#custom-factories) + +The second case is about custom factories. These are usually created to abstract away some common pattern. + +Examples of custom factories: + +- `createQuery`, `createMutation` from [`farfetched`](https://farfetched.pages.dev/) +- `debounce`, `throttle`, etc from [`patronum`](https://patronum.effector.dev/) +- Any custom factory in your code, e.g. factory of a [feature-flag entity](https://farfetched.pages.dev/recipes/feature_flags.html) + +For this explanation, we will create a very simple factory: + +```ts +// src/shared/lib/create-name/index.ts +export function createName() { + const updateName = createEvent(); + const $name = createStore(null); + + $name.on(updateName, (_, nextName) => nextName); + + return { $name }; +} + +// src/feature/persons/model.ts +import { createName } from "@/shared/lib/create-name"; + +const personOne = createName(); +const personTwo = createName(); +``` + +First, the plugin will add `sid` to the inner stores of the factory + +```ts +// src/shared/lib/create-name/index.ts +export function createName() { + const updateName = createEvent(); + const $name = createStore(null, { sid: "ffds2" }); + + $name.on(updateName, (_, nextName) => nextName); + + return { $name }; +} + +// src/feature/persons/model.ts +import { createName } from "@/shared/lib/create-name"; + +const personOne = createName(); +const personTwo = createName(); +``` + +But it's not enough, because we can create two instances of `createName` and internal stores of both of these instances will have the same SIDs! +These SIDs will be stable, but not unique. + +To fix it we need to inform the plugin about our custom factory: + +```json +// .babelrc +{ + "plugins": [ + [ + "effector/babel-plugin", + { + "factories": ["@/shared/lib/create-name"] + } + ] + ] +} +``` + +Since the plugin "sees" only one file at a time, we need to provide it with the actual import path used in the module. + +:::tip +If relative import paths are used in the module, then the full path from the project root must be added to the `factories` list, so the plugin could resolve it. + +If absolute or aliased (like in the example) paths are used, then specifically this aliased path must be added to the `factories` list. + +Most of the popular ecosystem projects are already included in plugin's default settings. +::: + +Now the plugin knows about our factory and it will wrap `createName` with the internal `withFactory` helper: + +```ts +// src/shared/lib/create-name/index.ts +export function createName() { + const updateName = createEvent(); + const $name = createStore(null, { sid: "ffds2" }); + + $name.on(updateName, (_, nextName) => nextName); + + return { $name }; +} + +// src/feature/persons/model.ts +import { withFactory } from "effector"; +import { createName } from "@/shared/lib/create-name"; + +const personOne = withFactory({ + sid: "gre24f", + fn: () => createName(), +}); +const personTwo = withFactory({ + sid: "lpefgd", + fn: () => createName(), +}); +``` + +Thanks to that `sid`-s of inner units of a factory are also unique, and we can safely serialize and deserialize them. + +```ts +personOne.$name.sid; // gre24f|ffds2 +personTwo.$name.sid; // lpefgd|ffds2 +``` diff --git a/src/content/docs/en/guides/migration-guide-v23.md b/src/content/docs/en/guides/migration-guide-v23.md new file mode 100644 index 0000000..7d67e1a --- /dev/null +++ b/src/content/docs/en/guides/migration-guide-v23.md @@ -0,0 +1,115 @@ +--- +title: Migration guide +redirectFrom: + - /guides/migration-guide-v23 + - /guides/migration-guide + - /en/guides/migration-guide +--- + +This guide covers the steps required to migrate to Effector 23 from a previous version. +Several features were declared deprecated in this release: + +- `forward` and `guard` operators +- `greedy` option of `sample` was renamed into `batch` +- "derived" and "callable" unit types are officially separated now +- the ability to use `undefined` as a magic "skip" value in reducers + +## Deprecation of `forward` and `guard` + +Those operators are pretty old and lived through many releases of Effector. +But all of their use-cases are already covered by `sample` now, so it is their time to go. You will see a deprecation warning in console for every call of those operators in your code. + +:::tip +You can migrate from both of them by using the official [Effector's ESLint plugin](https://eslint.effector.dev/), which has `no-forward` and `no-guard` rules with built-in [auto-fix feature](https://eslint.org/docs/latest/use/command-line-interface#fix-problems). +::: + +## `greedy` to `batch` + +The `sample` operator had `greedy` option to disable updates batching in rare edge-cases. +But the name "greedy" wasn't that obvious for the users, so it is renamed into `batch` and it's signature is reversed. + +You will see a deprecation warning in console for every usage of `greedy` option in your code. + +:::tip +You can migrate from one to the other by simply running "Find and Replace" from `greedy: true` to `batch: false` in your favorite IDE. +::: + +## Separate types for derived and callable units + +Derived units now fully separated from "callable/writable" ones: + +- Main factories `createEvent` and `createStore` now return types `EventCallable` and `StoreWritable` (because you can call and write to these units at any moment). +- Methods and operators like `unit.map(...)` or `combine(...)` now return types `Event` and `Store`, which are "read-only" i.e. you can only use them as `clock` or `source`, but not as a `target`. +- `EventCallable` type is assignable to `Event`, but not the other way around, same for stores. +- There are also runtime exceptions for types mismatch. + +Most likely you will not need to do anything, you will just get better types. + +But you might have issues with external libraries, **which are not updated to Effector 23 yet**: + +- Most of the libraries are just _accepting_ units as clocks and sources – those cases are ok. +- If some operator from the external library is accepting some unit as a `target`, you still will see an good-old `Event` type in this case, so you will not have a type error here even if there is actually an issue. +- If some _factory_ returns an event, which you are expected to call in your own code, then you will get a type error and you will need to typecast this event to `EventCallable`. + +:::tip +If you run into any of these cases, just create an issue in the repo of this library with a request to support Effector 23 version. +Owners of the project will see relevant type errors in their own source code and tests, once they update Effector in their repo. +::: + +If you have these issues in your own custom factories or libraries, then you should already see a relevant type errors in the source code of your library. +Just replace `Event` with `EventCallable`, `Store` with `StoreWritable` or `Unit` with `UnitTargetable` everywhere it is relevant (i.e. you are going to call or write into these units somehow). + +## Magic `undefined` skip is deprecated + +There is an old feature in Effector: `undefined` is used as a "magic" value to skip updates in reducers in rare cases, e.g. + +```ts +const $value = createStore(0).on(newValueReceived, (_oldValue, newValue) => newValue); +``` + +☝️ if `newValue` is `undefined`, then update will be skipped. + +The idea of making each mapper and reducer work as a sort of `filterMap` was considered useful in early Effector, but is very rarely used properly, and is confusing and distracting, so it should be deprecated and removed. + +To do so each and every store factory now supports special `skipVoid` configuration setting, which controls, how specifically store should handle `undefined` value. If set to `false` – store will use `undefined` as a value. +If set to `true` (deprecated), store will read `undefined` as a "skip update" command and will do nothing. + +You will see a warning for each return of undefined in your mappers or reducers in your code, with a requirement to provide an explicit `skipVoid` setting on your store. + +:::tip +If you do want to skip store update in certain cases, then it is better to explicitly return previous state, when possible. +::: + +It is recommended to use `{skipVoid: false}` at all times, so you are able to use an `undefined` as a normal value. + +If you do need `undefined` as a "magic skip" value – then you can use `{skipVoid: true}` to preserve current behavior. You still will get a deprecation warning though, but only one for declaration instead of one for every such update. + +The `skipVoid` setting is temporary and only needed as a way to properly deprecate this feature from Effector. In Effector 24 `skipVoid` itself will be deprecated and then removed. + +## `useStore` and `useEvent` to `useUnit` in `effector-react` + +We merged two old hooks into one, its advantage is that you can pass many units to it at once and it batches all the stores' updates into one single update. + +It's safe to just swap the calls of the old hooks with the new one: + +```ts +const Component = () => { + const foo = useStore($foo) + const bar = useStore($bar) + const onSubmit = useEvent(triggerSubmit) +} +``` +Becomes: +```ts +const Component = () => { + const foo = useUnit($foo) + const bar = useUnit($bar) + const onSubmit = useUnit(triggerSubmit) +} +``` +Or shorter: +```ts +const Component = () => { + const [foo, bar, onSubmit] = useUnit([$foo, $bar, triggerSubmit]) +} +``` diff --git a/src/content/docs/en/guides/server-side-rendering.md b/src/content/docs/en/guides/server-side-rendering.md new file mode 100644 index 0000000..57fbd52 --- /dev/null +++ b/src/content/docs/en/guides/server-side-rendering.md @@ -0,0 +1,311 @@ +--- +title: Server Side Rendering +redirectFrom: + - /guides/ssr + - /guides/server-side-rendering + - /en/guides/ssr + - /en/guides/server-side-rendering +--- + +Server-side rendering (SSR) means that the content of your site is generated on the server and then sent to the browser – which these days is achieved in very different ways and forms. + +:::info +Generally, if the rendering happens at the runtime – it is called SSR. If the rendering happens at the build-time – it is usually called Server Side Generation (SSG), which in fact is basically a subset of SSR. + +This difference it is not important for this guide, everything said applies both to SSR and SSG. +::: + +In this guide we will cover two main kinds of Server Side Rendering patterns and how effector should be used in these cases. + +## Non-Isomorphic SSR + +You don't need to do anything special to support non-isomorphic SSR/SSG workflow. + +This way initial HTML is usually generated separately, by using some sort of template engine, which is quite often run with different (not JS) programming language. +The frontend code in this case works only at the client browser and **is not used in any way** to generate the server response. + +This approach works for effector, as well as any javascript code. Any SPA application is basically an edge-case of it, as its HTML template does not contain any content, except for ` + +