├── .nvmrc
├── packages
├── pliny
│ ├── .eslintrc.cjs
│ ├── tsup.config.ts
│ ├── tsconfig.json
│ ├── src
│ │ ├── utils
│ │ │ ├── formatDate.ts
│ │ │ ├── htmlEscaper.ts
│ │ │ └── contentlayer.ts
│ │ ├── ui
│ │ │ ├── BlogNewsletterForm.tsx
│ │ │ ├── Bleed.tsx
│ │ │ ├── Pre.tsx
│ │ │ ├── NewsletterForm.tsx
│ │ │ └── TOCInline.tsx
│ │ ├── newsletter
│ │ │ ├── mailchimp.ts
│ │ │ ├── buttondown.ts
│ │ │ ├── convertkit.ts
│ │ │ ├── emailOctopus.ts
│ │ │ ├── beehiiv.ts
│ │ │ ├── klaviyo.ts
│ │ │ └── index.ts
│ │ ├── mdx-plugins
│ │ │ ├── remark-extract-frontmatter.ts
│ │ │ ├── index.ts
│ │ │ ├── remark-code-title.ts
│ │ │ ├── remark-toc-headings.ts
│ │ │ └── remark-img-to-jsx.ts
│ │ ├── search
│ │ │ ├── KBarButton.tsx
│ │ │ ├── AlgoliaButton.tsx
│ │ │ ├── index.tsx
│ │ │ ├── KBar.tsx
│ │ │ ├── KBarModal.tsx
│ │ │ └── Algolia.tsx
│ │ ├── analytics
│ │ │ ├── MicrosoftClarity.tsx
│ │ │ ├── SimpleAnalytics.tsx
│ │ │ ├── GoogleAnalytics.tsx
│ │ │ ├── Plausible.tsx
│ │ │ ├── Posthog.tsx
│ │ │ ├── Umami.tsx
│ │ │ └── index.tsx
│ │ ├── comments
│ │ │ ├── Disqus.tsx
│ │ │ ├── Giscus.tsx
│ │ │ ├── Utterances.tsx
│ │ │ └── index.tsx
│ │ ├── mdx-components.tsx
│ │ └── config.ts
│ ├── tsup.ui.config.ts
│ ├── add-use-client.mjs
│ ├── package.json
│ ├── CHANGELOG.md
│ └── public
│ │ └── algolia.css
└── config
│ ├── package.json
│ ├── tsconfig.base.json
│ └── eslint.js
├── pliny-sketch.png
├── commitlint.config.js
├── .husky
├── pre-commit
└── _
│ └── husky.sh
├── .editorconfig
├── .github
├── changeset-version.cjs
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ └── ci.yml
├── prettier.config.js
├── .eslintignore
├── vitest-exclude-recipe.config.ts
├── .changeset
├── config.json
└── README.md
├── .yarnrc.yml
├── turbo.json
├── LICENSE
├── .gitignore
├── package.json
├── README.md
└── .yarn
└── plugins
└── @yarnpkg
├── plugin-typescript.cjs
└── plugin-workspace-tools.cjs
/.nvmrc:
--------------------------------------------------------------------------------
1 | v18
2 |
--------------------------------------------------------------------------------
/packages/pliny/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = require('@pliny/config/eslint')
2 |
--------------------------------------------------------------------------------
/pliny-sketch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timlrx/pliny/HEAD/pliny-sketch.png
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = { extends: ['@commitlint/config-conventional'] }
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx --no-install lint-staged
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.github/changeset-version.cjs:
--------------------------------------------------------------------------------
1 | const { execSync } = require('node:child_process')
2 |
3 | execSync('npx changeset version')
4 | execSync('YARN_ENABLE_IMMUTABLE_INSTALLS=false node .yarn/releases/yarn-3.6.1.cjs')
5 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: false,
3 | singleQuote: true,
4 | printWidth: 100,
5 | tabWidth: 2,
6 | useTabs: false,
7 | trailingComma: 'es5',
8 | bracketSpacing: true,
9 | }
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | .github/
2 | .idea/
3 | .vscode/
4 | .git/
5 | commitlint.config.js
6 | package.json
7 | craco.config.js
8 | node_modules/
9 | webpack.config.js
10 | jest.config.ts
11 |
12 | dist/
13 | coverage/
14 | cypress/
15 |
--------------------------------------------------------------------------------
/vitest-exclude-recipe.config.ts:
--------------------------------------------------------------------------------
1 | import { configDefaults, defineConfig } from 'vitest/config'
2 |
3 | export default defineConfig({
4 | test: {
5 | exclude: [...configDefaults.exclude, '**/new.test.ts', '**/install-recipe.test.ts'],
6 | },
7 | })
8 |
--------------------------------------------------------------------------------
/packages/pliny/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 |
3 | export default defineConfig({
4 | entry: ['src/**/*'],
5 | format: 'esm',
6 | clean: true,
7 | splitting: true,
8 | treeshake: true,
9 | dts: true,
10 | silent: true,
11 | })
12 |
--------------------------------------------------------------------------------
/packages/pliny/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@pliny/config/tsconfig.base.json",
3 | "compilerOptions": {
4 | "rootDir": "src/",
5 | "baseUrl": "./",
6 | "outDir": "dist",
7 | "module": "ES2020",
8 | "moduleResolution": "node"
9 | },
10 | "exclude": ["dist", "node_modules", "public"]
11 | }
12 |
--------------------------------------------------------------------------------
/packages/pliny/src/utils/formatDate.ts:
--------------------------------------------------------------------------------
1 | export const formatDate = (date: string, locale = 'en-US') => {
2 | const options: Intl.DateTimeFormatOptions = {
3 | year: 'numeric',
4 | month: 'long',
5 | day: 'numeric',
6 | }
7 | const now = new Date(date).toLocaleDateString(locale, options)
8 |
9 | return now
10 | }
11 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json",
3 | "changelog": "@changesets/cli/changelog",
4 | "commit": false,
5 | "fixed": [],
6 | "linked": [],
7 | "access": "public",
8 | "baseBranch": "main",
9 | "updateInternalDependencies": "patch",
10 | "ignore": [
11 | "@pliny/config"
12 | ]
13 | }
--------------------------------------------------------------------------------
/packages/pliny/tsup.ui.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 |
3 | // Do not split UI files as we want to use default export
4 | // https://github.com/vercel/next.js/issues/52415
5 | export default defineConfig({
6 | entry: ['src/ui/*'],
7 | format: 'esm',
8 | outDir: 'ui',
9 | clean: true,
10 | splitting: false,
11 | treeshake: true,
12 | dts: true,
13 | silent: true,
14 | })
15 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
3 | plugins:
4 | - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
5 | spec: "@yarnpkg/plugin-typescript"
6 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
7 | spec: "@yarnpkg/plugin-workspace-tools"
8 | - path: .yarn/plugins/@yarnpkg/plugin-version.cjs
9 | spec: "@yarnpkg/plugin-version"
10 |
11 | yarnPath: .yarn/releases/yarn-3.6.1.cjs
12 |
--------------------------------------------------------------------------------
/packages/config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pliny/config",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "private": true,
6 | "devDependencies": {
7 | "@typescript-eslint/eslint-plugin": "^6.0.0",
8 | "eslint": "^8.45.0",
9 | "eslint-config-next": "13.4.10",
10 | "eslint-config-prettier": "^8.8.0",
11 | "eslint-plugin-jsx-a11y": "^6.7.1",
12 | "eslint-plugin-prettier": "^5.0.0",
13 | "typescript": "^5.1.6"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/pliny/src/ui/BlogNewsletterForm.tsx:
--------------------------------------------------------------------------------
1 | import NewsletterForm, { NewsletterFormProps } from './NewsletterForm'
2 |
3 | const BlogNewsletterForm = ({ title, apiUrl }: NewsletterFormProps) => (
4 |
9 | )
10 |
11 | export default BlogNewsletterForm
12 |
--------------------------------------------------------------------------------
/packages/pliny/src/ui/Bleed.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 |
3 | export interface BleedProps {
4 | full?: boolean
5 | children: ReactNode
6 | }
7 |
8 | const Bleed = ({ full, children }: BleedProps) => {
9 | return (
10 |
15 | {children}
16 |
17 | )
18 | }
19 |
20 | export default Bleed
21 |
--------------------------------------------------------------------------------
/packages/pliny/src/newsletter/mailchimp.ts:
--------------------------------------------------------------------------------
1 | import mailchimp from '@mailchimp/mailchimp_marketing'
2 |
3 | mailchimp.setConfig({
4 | apiKey: process.env.MAILCHIMP_API_KEY,
5 | server: process.env.MAILCHIMP_API_SERVER, // E.g. us1
6 | })
7 |
8 | export const mailchimpSubscribe = async (email: string) => {
9 | const response = await mailchimp.lists.addListMember(process.env.MAILCHIMP_AUDIENCE_ID, {
10 | email_address: email,
11 | status: 'subscribed',
12 | })
13 | return response
14 | }
15 |
--------------------------------------------------------------------------------
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/packages/pliny/src/mdx-plugins/remark-extract-frontmatter.ts:
--------------------------------------------------------------------------------
1 | import { Parent } from 'unist'
2 | import { VFile } from 'vfile'
3 | import { visit } from 'unist-util-visit'
4 | import yaml from 'js-yaml'
5 |
6 | /**
7 | * Extracts frontmatter from markdown file and adds it to the file's data object.
8 | *
9 | */
10 | export function remarkExtractFrontmatter() {
11 | return (tree: Parent, file: VFile) => {
12 | visit(tree, 'yaml', (node: Parent) => {
13 | //@ts-ignore
14 | file.data.frontmatter = yaml.load(node.value)
15 | })
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/pliny/src/search/KBarButton.tsx:
--------------------------------------------------------------------------------
1 | import { DetailedHTMLProps, HTMLAttributes } from 'react'
2 | import { useKBar } from 'kbar'
3 |
4 | /**
5 | * Button wrapper component that triggers the KBar modal on click.
6 | *
7 | * @return {*}
8 | */
9 | export const KBarButton = ({
10 | children,
11 | ...rest
12 | }: DetailedHTMLProps, HTMLButtonElement>) => {
13 | const { query } = useKBar()
14 |
15 | return (
16 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/packages/pliny/src/newsletter/buttondown.ts:
--------------------------------------------------------------------------------
1 | export const buttondownSubscribe = async (email: string) => {
2 | const API_KEY = process.env.BUTTONDOWN_API_KEY
3 | const API_URL = 'https://api.buttondown.email/v1/'
4 | const buttondownRoute = `${API_URL}subscribers`
5 |
6 | const data = { email_address: email }
7 |
8 | const response = await fetch(buttondownRoute, {
9 | body: JSON.stringify(data),
10 | headers: {
11 | Authorization: `Token ${API_KEY}`,
12 | 'Content-Type': 'application/json',
13 | },
14 | method: 'POST',
15 | })
16 | return response
17 | }
18 |
--------------------------------------------------------------------------------
/packages/config/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": true,
4 | "declarationMap": true,
5 | "isolatedModules": true,
6 | "module": "commonjs",
7 | "target": "es2015",
8 | "lib": ["dom", "dom.iterable", "ES2018"],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "strict": false,
12 | "forceConsistentCasingInFileNames": true,
13 | "esModuleInterop": true,
14 | "sourceMap": true,
15 | "moduleResolution": "node",
16 | "jsx": "react-jsx",
17 | "resolveJsonModule": true
18 | },
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/pliny/src/mdx-plugins/index.ts:
--------------------------------------------------------------------------------
1 | import { remarkExtractFrontmatter } from './remark-extract-frontmatter'
2 | import { remarkCodeTitles } from './remark-code-title'
3 | import type { Toc } from './remark-toc-headings'
4 | import { remarkTocHeadings, extractTocHeadings } from './remark-toc-headings'
5 | import type { ImageNode } from './remark-img-to-jsx'
6 | import { remarkImgToJsx } from './remark-img-to-jsx'
7 |
8 | export type { Toc, ImageNode }
9 |
10 | export {
11 | remarkExtractFrontmatter,
12 | remarkCodeTitles,
13 | remarkImgToJsx,
14 | remarkTocHeadings,
15 | extractTocHeadings,
16 | }
17 |
--------------------------------------------------------------------------------
/packages/pliny/src/newsletter/convertkit.ts:
--------------------------------------------------------------------------------
1 | export const convertkitSubscribe = async (email: string) => {
2 | const FORM_ID = process.env.CONVERTKIT_FORM_ID
3 | const API_KEY = process.env.CONVERTKIT_API_KEY
4 | const API_URL = 'https://api.convertkit.com/v3/'
5 |
6 | // Send request to ConvertKit
7 | const data = { email, api_key: API_KEY }
8 |
9 | const response = await fetch(`${API_URL}forms/${FORM_ID}/subscribe`, {
10 | body: JSON.stringify(data),
11 | headers: {
12 | 'Content-Type': 'application/json',
13 | },
14 | method: 'POST',
15 | })
16 |
17 | return response
18 | }
19 |
--------------------------------------------------------------------------------
/packages/pliny/src/search/AlgoliaButton.tsx:
--------------------------------------------------------------------------------
1 | import { DetailedHTMLProps, HTMLAttributes, useContext } from 'react'
2 | import { AlgoliaSearchContext } from './Algolia'
3 |
4 | /**
5 | * Button wrapper component that triggers the Algolia modal on click.
6 | *
7 | * @return {*}
8 | */
9 | export const AlgoliaButton = ({
10 | children,
11 | ...rest
12 | }: DetailedHTMLProps, HTMLButtonElement>) => {
13 | const { query } = useContext(AlgoliaSearchContext)
14 |
15 | return (
16 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/packages/pliny/src/newsletter/emailOctopus.ts:
--------------------------------------------------------------------------------
1 | export const emailOctopusSubscribe = async (email: string) => {
2 | const API_KEY = process.env.EMAILOCTOPUS_API_KEY
3 | const LIST_ID = process.env.EMAILOCTOPUS_LIST_ID
4 | const API_URL = 'https://emailoctopus.com/api/1.6/'
5 |
6 | const data = { email_address: email, api_key: API_KEY }
7 |
8 | const API_ROUTE = `${API_URL}lists/${LIST_ID}/contacts`
9 |
10 | const response = await fetch(API_ROUTE, {
11 | body: JSON.stringify(data),
12 | headers: {
13 | 'Content-Type': 'application/json',
14 | },
15 | method: 'POST',
16 | })
17 |
18 | return response
19 | }
20 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turborepo.org/schema.json",
3 | "tasks": {
4 | "build": {
5 | "dependsOn": [
6 | "^build"
7 | ],
8 | "outputs": [
9 | "dist/**",
10 | ".next/**"
11 | ]
12 | },
13 | "build-publishable-release": {
14 | "dependsOn": [
15 | "^build-publishable-release"
16 | ],
17 | "outputs": [
18 | "dist/**",
19 | ".next/**"
20 | ]
21 | },
22 | "lint": {
23 | "outputs": []
24 | },
25 | "dev": {
26 | "cache": false
27 | },
28 | "clean": {
29 | "cache": false
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/packages/pliny/src/utils/htmlEscaper.ts:
--------------------------------------------------------------------------------
1 | const { replace } = ''
2 |
3 | // escape
4 | const ca = /[&<>'"]/g
5 |
6 | const esca = {
7 | '&': '&',
8 | '<': '<',
9 | '>': '>',
10 | "'": ''',
11 | '"': '"',
12 | }
13 | const pe = (m: keyof typeof esca) => esca[m]
14 |
15 | /**
16 | * Safely escape HTML entities such as `&`, `<`, `>`, `"`, and `'`.
17 | * @param {string} es the input to safely escape
18 | * @returns {string} the escaped input, and it **throws** an error if
19 | * the input type is unexpected, except for boolean and numbers,
20 | * converted as string.
21 | */
22 | export const escape = (es: string): string => replace.call(es, ca, pe)
23 |
--------------------------------------------------------------------------------
/packages/pliny/src/newsletter/beehiiv.ts:
--------------------------------------------------------------------------------
1 | export const beehiivSubscribe = async (email: string) => {
2 | const API_KEY = process.env.BEEHIIV_API_KEY
3 | const PUBLICATION_ID = process.env.BEEHIIV_PUBLICATION_ID
4 | const API_URL = `https://api.beehiiv.com/v2/publications/${PUBLICATION_ID}/subscriptions`
5 |
6 | const data = {
7 | email,
8 | reactivate_existing: false,
9 | send_welcome_email: true,
10 | }
11 |
12 | const response = await fetch(API_URL, {
13 | method: 'POST',
14 | headers: {
15 | 'Content-Type': 'application/json',
16 | 'Authorization': `Bearer ${API_KEY}`,
17 | },
18 | body: JSON.stringify(data),
19 | })
20 |
21 | return response
22 | }
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: 'enhancement'
6 | assignees: ''
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.**
10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11 |
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 |
15 | **Describe alternatives you've considered**
16 | A clear and concise description of any alternative solutions or features you've considered.
17 |
18 | **Additional context**
19 | Add any other context or screenshots about the feature request here.
20 |
--------------------------------------------------------------------------------
/.husky/_/husky.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | if [ -z "$husky_skip_init" ]; then
3 | debug () {
4 | [ "$HUSKY_DEBUG" = "1" ] && echo "husky (debug) - $1"
5 | }
6 |
7 | readonly hook_name="$(basename "$0")"
8 | debug "starting $hook_name..."
9 |
10 | if [ "$HUSKY" = "0" ]; then
11 | debug "HUSKY env variable is set to 0, skipping hook"
12 | exit 0
13 | fi
14 |
15 | if [ -f ~/.huskyrc ]; then
16 | debug "sourcing ~/.huskyrc"
17 | . ~/.huskyrc
18 | fi
19 |
20 | export readonly husky_skip_init=1
21 | sh -e "$0" "$@"
22 | exitCode="$?"
23 |
24 | if [ $exitCode != 0 ]; then
25 | echo "husky - $hook_name hook exited with code $exitCode (error)"
26 | exit $exitCode
27 | fi
28 |
29 | exit 0
30 | fi
31 |
--------------------------------------------------------------------------------
/packages/pliny/src/analytics/MicrosoftClarity.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script.js'
2 |
3 | export interface ClarityProps {
4 | ClarityWebsiteId: string
5 | }
6 |
7 | // https://clarity.microsoft.com/
8 | // https://learn.microsoft.com/en-us/clarity/setup-and-installation/clarity-setup
9 | export const Clarity = ({ ClarityWebsiteId }: ClarityProps) => {
10 | return (
11 | <>
12 |
21 | >
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/packages/pliny/src/analytics/SimpleAnalytics.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script.js'
2 |
3 | export interface SimpleAnalyticsProps {
4 | src?: string
5 | }
6 |
7 | export const SimpleAnalytics = ({
8 | src = 'https://scripts.simpleanalyticscdn.com/latest.js',
9 | }: SimpleAnalyticsProps) => {
10 | return (
11 | <>
12 |
17 |
18 | >
19 | )
20 | }
21 |
22 | // https://docs.simpleanalytics.com/events
23 | export const logEvent = (eventName, callback) => {
24 | if (callback) {
25 | return window.sa_event?.(eventName, callback)
26 | } else {
27 | return window.sa_event?.(eventName)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **System Info (if dev / build issue):**
27 | - OS: [e.g. iOS]
28 | - Node version (please ensure you are using 14+)
29 | - Npm version
30 |
31 | **Browser Info (if display / formatting issue):**
32 | - Device [e.g. Desktop, iPhone6]
33 | - Browser [e.g. chrome, safari]
34 | - Version [e.g. 22]
35 |
36 | **Additional context**
37 | Add any other context about the problem here.
38 |
--------------------------------------------------------------------------------
/packages/pliny/src/analytics/GoogleAnalytics.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script.js'
2 |
3 | export interface GoogleAnalyticsProps {
4 | googleAnalyticsId: string
5 | }
6 |
7 | export const GA = ({ googleAnalyticsId }: GoogleAnalyticsProps) => {
8 | return (
9 | <>
10 |
14 |
15 |
23 | >
24 | )
25 | }
26 |
27 | // https://developers.google.com/analytics/devguides/collection/gtagjs/events
28 | export const logEvent = (action, category, label, value) => {
29 | // @ts-ignore
30 | window.gtag?.('event', action, {
31 | event_category: category,
32 | event_label: label,
33 | value: value,
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Timothy Lin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # package-lock
8 | package-lock.json
9 |
10 | # builds
11 | build
12 | dist
13 | .rpt2_cache
14 | packages/pliny/analytics
15 | packages/pliny/comments
16 | packages/pliny/config
17 | packages/pliny/mdx-components
18 | packages/pliny/mdx-plugins
19 | packages/pliny/newsletter
20 | packages/pliny/search
21 | packages/pliny/seo
22 | packages/pliny/ui
23 | packages/pliny/utils
24 | packages/pliny/chunk-*
25 | packages/pliny/*.js
26 | packages/pliny/*.d.ts
27 |
28 | # misc
29 | .DS_Store
30 | .env
31 | .env.local
32 | .env.development.local
33 | .env.test.local
34 | .env.production.local
35 |
36 | .tmp*
37 | /tmp
38 |
39 | /.yarn/*
40 | !/.yarn/releases
41 | !/.yarn/plugins
42 | !/.yarn/sdks
43 |
44 | npm-debug.log*
45 | yarn-debug.log*
46 | yarn-error.log*
47 | lerna-debug.log*
48 | .turbo
49 | /_release
50 | oclif.manifest.json
51 |
52 | # ide's setting folder
53 | .vscode/
54 | .idea/
55 |
56 | # jest code coverage reports
57 | coverage/
58 |
59 | # cypress miscellaneous
60 | cypress/screenshots
61 | cypress/videos
62 |
63 | # rollup
64 | stats.html
65 |
66 | # Contentlayer
67 | .contentlayer
68 |
--------------------------------------------------------------------------------
/packages/pliny/src/mdx-plugins/remark-code-title.ts:
--------------------------------------------------------------------------------
1 | import { Parent } from 'unist'
2 | import { visit } from 'unist-util-visit'
3 |
4 | /**
5 | * Parses title from code block and inserts it as a sibling title node.
6 | *
7 | */
8 | export function remarkCodeTitles() {
9 | return (tree: Parent & { lang?: string }) =>
10 | visit(tree, 'code', (node: Parent & { lang?: string }, index, parent: Parent) => {
11 | const nodeLang = node.lang || ''
12 | let language = ''
13 | let title = ''
14 |
15 | if (nodeLang.includes(':')) {
16 | language = nodeLang.slice(0, nodeLang.search(':'))
17 | title = nodeLang.slice(nodeLang.search(':') + 1, nodeLang.length)
18 | }
19 |
20 | if (!title) {
21 | return
22 | }
23 |
24 | const className = 'remark-code-title'
25 |
26 | const titleNode = {
27 | type: 'mdxJsxFlowElement',
28 | name: 'div',
29 | attributes: [{ type: 'mdxJsxAttribute', name: 'className', value: className }],
30 | children: [{ type: 'text', value: title }],
31 | data: { _xdmExplicitJsx: true },
32 | }
33 |
34 | parent.children.splice(index, 0, titleNode)
35 | node.lang = language
36 | })
37 | }
38 |
--------------------------------------------------------------------------------
/packages/pliny/src/comments/Disqus.tsx:
--------------------------------------------------------------------------------
1 | import { useTheme } from 'next-themes'
2 | import { useEffect, useCallback } from 'react'
3 |
4 | export interface DisqusConfig {
5 | provider: 'disqus'
6 | disqusConfig: {
7 | shortname: string
8 | }
9 | }
10 |
11 | export type DisqusProps = DisqusConfig['disqusConfig'] & {
12 | slug?: string
13 | }
14 |
15 | export const Disqus = ({ shortname, slug }: DisqusProps) => {
16 | const { theme } = useTheme()
17 |
18 | const COMMENTS_ID = 'disqus_thread'
19 |
20 | const LoadComments = useCallback(() => {
21 | window.disqus_config = function () {
22 | this.page.url = window.location.href
23 | this.page.identifier = slug
24 | }
25 | // Reset Disqus does not reset the style
26 | // As an ugly workaround we just reload it
27 | const script = document.createElement('script')
28 | script.src = 'https://' + shortname + '.disqus.com/embed.js'
29 | script.setAttribute('data-timestamp', Date.now().toString())
30 | script.async = true
31 | document.body.appendChild(script)
32 | }, [shortname, slug, theme])
33 |
34 | useEffect(() => {
35 | LoadComments()
36 | }, [LoadComments])
37 |
38 | return
39 | }
40 |
--------------------------------------------------------------------------------
/packages/pliny/src/analytics/Plausible.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script.js'
2 |
3 | export interface PlausibleProps {
4 | plausibleDataDomain: string
5 | dataApi?: string
6 | src?: string
7 | }
8 |
9 | /**
10 | * Plausible analytics component.
11 | * To proxy the requests through your own domain, you can use the dataApi and src attribute.
12 | * See [Plausible docs](https://plausible.io/docs/proxy/guides/nextjs#step-2-adjust-your-deployed-script)
13 | * for more information.
14 | *
15 | */
16 | export const Plausible = ({
17 | plausibleDataDomain,
18 | dataApi = undefined,
19 | src = 'https://plausible.io/js/plausible.js',
20 | }: PlausibleProps) => {
21 | return (
22 | <>
23 |
29 |
34 | >
35 | )
36 | }
37 |
38 | // https://plausible.io/docs/custom-event-goals
39 | export const logEvent = (eventName, ...rest) => {
40 | return window.plausible?.(eventName, ...rest)
41 | }
42 |
--------------------------------------------------------------------------------
/packages/pliny/src/mdx-components.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/display-name */
2 | import React from 'react'
3 | import * as _jsx_runtime from 'react/jsx-runtime'
4 | import ReactDOM from 'react-dom'
5 | import type { MDXComponents } from 'mdx/types'
6 |
7 | export interface MDXLayoutRenderer {
8 | code: string
9 | components?: MDXComponents
10 | [key: string]: unknown
11 | }
12 |
13 | const getMDXComponent = (
14 | code: string,
15 | globals: Record = {}
16 | ): React.ComponentType => {
17 | const scope = { React, ReactDOM, _jsx_runtime, ...globals }
18 | const fn = new Function(...Object.keys(scope), code)
19 | return fn(...Object.values(scope)).default
20 | }
21 |
22 | // TS transpile it to a require which causes ESM error
23 | // Copying the function from contentlayer as a workaround
24 | // Copy of https://github.com/contentlayerdev/contentlayer/blob/main/packages/next-contentlayer/src/hooks/useMDXComponent.ts
25 | export const useMDXComponent = (
26 | code: string,
27 | globals: Record = {}
28 | ): React.ComponentType => {
29 | return React.useMemo(() => getMDXComponent(code, globals), [code, globals])
30 | }
31 |
32 | export const MDXLayoutRenderer = ({ code, components, ...rest }: MDXLayoutRenderer) => {
33 | const Mdx = useMDXComponent(code)
34 |
35 | return
36 | }
37 |
--------------------------------------------------------------------------------
/packages/pliny/src/mdx-plugins/remark-toc-headings.ts:
--------------------------------------------------------------------------------
1 | import { VFile } from 'vfile'
2 | import { Parent } from 'unist'
3 | import { visit } from 'unist-util-visit'
4 | import { Heading } from 'mdast'
5 | import GithubSlugger from 'github-slugger'
6 | import { toString } from 'mdast-util-to-string'
7 | import { remark } from 'remark'
8 |
9 | export type TocItem = {
10 | value: string
11 | url: string
12 | depth: number
13 | }
14 |
15 | export type Toc = TocItem[]
16 |
17 | /**
18 | * Extracts TOC headings from markdown file and adds it to the file's data object.
19 | */
20 | export function remarkTocHeadings() {
21 | const slugger = new GithubSlugger()
22 | return (tree: Parent, file: VFile) => {
23 | const toc: Toc = []
24 | visit(tree, 'heading', (node: Heading) => {
25 | const textContent = toString(node)
26 | toc.push({
27 | value: textContent,
28 | url: '#' + slugger.slug(textContent),
29 | depth: node.depth,
30 | })
31 | })
32 | file.data.toc = toc
33 | }
34 | }
35 |
36 | /**
37 | * Passes markdown file through remark to extract TOC headings
38 | *
39 | * @param {string} markdown
40 | * @return {*} {Promise}
41 | */
42 | export async function extractTocHeadings(markdown: string): Promise {
43 | const vfile = await remark().use(remarkTocHeadings).process(markdown)
44 | // @ts-ignore
45 | return vfile.data.toc
46 | }
47 |
--------------------------------------------------------------------------------
/packages/pliny/add-use-client.mjs:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | import globby from 'globby'
3 |
4 | // Append "use client" to all path chunks that contain "use" hooks
5 | // So these packages can be directly used in Next.js directly
6 | // This allows us to see support file splitting with easy next import
7 | ; (async () => {
8 | console.log('Added use client directive to the following files:')
9 | const chunkPaths = await globby('chunk*')
10 | for (const path of chunkPaths) {
11 | const data = fs.readFileSync(path, 'utf8')
12 | if (
13 | /useState|useEffect|useRef|useCallback|useContext|useMemo|useTheme|useRouter|useRegisterActions|useMatches|useKBar/.test(
14 | data
15 | )
16 | ) {
17 | console.log(path)
18 | const insert = Buffer.from('"use client"\n')
19 | fs.writeFileSync(path, insert + data)
20 | }
21 | }
22 | // Handle ui differently as they are not split
23 | const clientPaths = await globby([
24 | 'ui/NewsletterForm.js',
25 | 'ui/BlogNewsletterForm.js',
26 | 'ui/Pre.js',
27 | 'search/KBarButton.js',
28 | 'search/AlgoliaButton.js',
29 | ])
30 | for (const path of clientPaths) {
31 | console.log(path)
32 | const data = fs.readFileSync(path)
33 | const insert = Buffer.from('"use client"\n')
34 | fs.writeFileSync(path, insert + data)
35 | }
36 | })()
37 |
--------------------------------------------------------------------------------
/packages/pliny/src/newsletter/klaviyo.ts:
--------------------------------------------------------------------------------
1 | export const klaviyoSubscribe = async (email: string) => {
2 | const API_KEY = process.env.KLAVIYO_API_KEY
3 | const LIST_ID = process.env.KLAVIYO_LIST_ID
4 |
5 | const data = {
6 | data: {
7 | type: 'profile-subscription-bulk-create-job',
8 | attributes: {
9 | custom_source: 'Marketing Event',
10 | profiles: {
11 | data: [
12 | {
13 | type: 'profile',
14 | attributes: {
15 | email: email,
16 | subscriptions: {
17 | email: {
18 | marketing: {
19 | consent: 'SUBSCRIBED',
20 | },
21 | },
22 | },
23 | },
24 | },
25 | ],
26 | },
27 | },
28 | relationships: {
29 | list: {
30 | data: {
31 | type: 'list',
32 | id: LIST_ID,
33 | },
34 | },
35 | },
36 | },
37 | }
38 |
39 | const response = await fetch('https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs', {
40 | method: 'POST',
41 | headers: {
42 | Authorization: `Klaviyo-API-Key ${API_KEY}`,
43 | Accept: 'application/json',
44 | 'Content-Type': 'application/json',
45 | revision: '2024-07-15',
46 | },
47 | body: JSON.stringify(data),
48 | })
49 |
50 | return response
51 | }
52 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@pliny/monorepo",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "prepare": "husky install",
7 | "build": "turbo run build",
8 | "dev": "turbo run dev --parallel --no-cache",
9 | "build:starter": "turbo run build",
10 | "dev:starter": "turbo run dev --parallel --no-cache",
11 | "serve": "turbo run serve",
12 | "lint": "turbo run lint",
13 | "test": "vitest run",
14 | "clean": "turbo run clean && rm -rf node_modules",
15 | "format": "prettier --write \"**/*.{ts,tsx,md,mdx}\"",
16 | "changeset": "changeset",
17 | "version-packages": "changeset version",
18 | "release": "yarn run build && changeset publish"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/timlrx/pliny"
23 | },
24 | "lint-staged": {
25 | "./packages/*/src/**/*.+(js|jsx|ts|tsx)": [
26 | "eslint --fix"
27 | ],
28 | "./packages/*/src/**/*.+(js|jsx|ts|tsx|json|css|md|mdx)": [
29 | "prettier --write"
30 | ]
31 | },
32 | "devDependencies": {
33 | "@changesets/cli": "^2.26.0",
34 | "@commitlint/cli": "^17.0.0",
35 | "@commitlint/config-conventional": "^17.1.0",
36 | "@types/react": "18.3.3",
37 | "husky": "^8.0.0",
38 | "lint-staged": "^13.0.0",
39 | "prettier": "^3.0.0",
40 | "turbo": "2.0.3",
41 | "vitest": "1.4.0"
42 | },
43 | "workspaces": {
44 | "packages": [
45 | "packages/*"
46 | ]
47 | },
48 | "packageManager": "yarn@3.6.1"
49 | }
50 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | concurrency: ${{ github.workflow }}-${{ github.ref }}
9 |
10 | jobs:
11 | # test:
12 | # name: Unit Test
13 | # runs-on: ubuntu-latest
14 | # steps:
15 | # - uses: actions/checkout@v3
16 | # - uses: actions/setup-node@v3
17 | # with:
18 | # node-version: '18.x'
19 | # - name: Install dependencies
20 | # run: yarn
21 | # - name: Build
22 | # run: yarn build
23 | # - name: Run unit tests
24 | # run: yarn test
25 |
26 | release:
27 | name: Release
28 | runs-on: ubuntu-latest
29 | steps:
30 | - name: Checkout Repo
31 | uses: actions/checkout@v3
32 | with:
33 | # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
34 | fetch-depth: 0
35 |
36 | - name: Setup Node.js 18.x
37 | uses: actions/setup-node@v3
38 | with:
39 | node-version: 18.x
40 |
41 | - name: Install Dependencies
42 | run: yarn
43 |
44 | - name: Create Release Pull Request or Publish to npm
45 | id: changesets
46 | uses: changesets/action@v1
47 | with:
48 | # This expects you to have a script called release which does a build for your packages and calls changeset publish
49 | version: node .github/changeset-version.cjs
50 | publish: yarn release
51 | env:
52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
54 |
--------------------------------------------------------------------------------
/packages/pliny/src/analytics/Posthog.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script.js'
2 |
3 | export interface PosthogProps {
4 | posthogProjectApiKey: string
5 | apiHost?: string
6 | }
7 |
8 | /**
9 | * Posthog analytics component.
10 | * See [Posthog docs](https://posthog.com/docs/libraries/js#option-1-add-javascript-snippet-to-your-html-badgerecommendedbadge) for more information.
11 | *
12 | */
13 | export const Posthog = ({
14 | posthogProjectApiKey,
15 | apiHost = 'https://app.posthog.com',
16 | }: PosthogProps) => {
17 | return (
18 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/packages/pliny/src/comments/Giscus.tsx:
--------------------------------------------------------------------------------
1 | import { useTheme } from 'next-themes'
2 | import GiscusComponent from '@giscus/react'
3 | import type { Mapping, BooleanString, InputPosition } from '@giscus/react'
4 |
5 | // TODO: type optional fields
6 | export interface GiscusConfig {
7 | provider: 'giscus'
8 | giscusConfig: {
9 | themeURL?: string
10 | theme?: string
11 | darkTheme?: string
12 | mapping: Mapping
13 | repo: string
14 | repositoryId: string
15 | category: string
16 | categoryId: string
17 | reactions: BooleanString
18 | metadata: BooleanString
19 | inputPosition?: InputPosition
20 | lang?: string
21 | }
22 | }
23 |
24 | export type GiscusProps = GiscusConfig['giscusConfig']
25 |
26 | export const Giscus = ({
27 | themeURL,
28 | theme,
29 | darkTheme,
30 | repo,
31 | repositoryId,
32 | category,
33 | categoryId,
34 | reactions,
35 | metadata,
36 | inputPosition,
37 | lang,
38 | mapping,
39 | }: GiscusProps) => {
40 | const { theme: nextTheme, resolvedTheme } = useTheme()
41 | const commentsTheme =
42 | themeURL === ''
43 | ? nextTheme === 'dark' || resolvedTheme === 'dark'
44 | ? darkTheme
45 | : theme
46 | : themeURL
47 |
48 | const COMMENTS_ID = 'comments-container'
49 |
50 | return (
51 |
66 | )
67 | }
68 |
--------------------------------------------------------------------------------
/packages/config/eslint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | extends: [
4 | 'plugin:@typescript-eslint/recommended',
5 | 'plugin:react/recommended',
6 | 'plugin:prettier/recommended',
7 | 'prettier',
8 | ],
9 | plugins: ['@typescript-eslint'],
10 | env: {
11 | node: true,
12 | },
13 | parserOptions: {
14 | ecmaVersion: 2020,
15 | ecmaFeatures: {
16 | legacyDecorators: true,
17 | },
18 | },
19 | rules: {
20 | 'react/display-name': 0,
21 | 'react/require-default-props': 0,
22 | 'no-restricted-syntax': 0,
23 | 'import/prefer-default-export': 0,
24 | 'import/no-named-as-default': 0,
25 | 'import/named': 0,
26 | '@typescript-eslint/no-explicit-any': 0,
27 | '@typescript-eslint/explicit-module-boundary-types': 0,
28 | '@typescript-eslint/explicit-function-return-type': 0,
29 | '@typescript-eslint/ban-ts-comment': 0,
30 | '@typescript-eslint/ban-ts-ignore': 0,
31 | '@typescript-eslint/ban-types': 0,
32 | '@typescript-eslint/no-var-requires': 0,
33 | '@typescript-eslint/no-unused-vars': 'warn',
34 | '@typescript-eslint/no-use-before-define': ['error', { variables: false, functions: false }],
35 | 'space-before-function-paren': 0,
36 | 'no-plusplus': 0,
37 | 'react/prop-types': 'off',
38 | 'react/jsx-handler-names': 0,
39 | 'react/jsx-fragments': 0,
40 | 'react/react-in-jsx-scope': 'off',
41 | 'react/no-unused-prop-types': 0,
42 | 'react/jsx-props-no-spreading': 0,
43 | 'react/static-property-placement': 0,
44 | 'react/no-array-index-key': 0,
45 | 'arrow-parens': 1,
46 | '@typescript-eslint/no-shadow': 'off',
47 | 'no-useless-return': 'off',
48 | 'import/no-extraneous-dependencies': 'off',
49 | },
50 | }
51 |
--------------------------------------------------------------------------------
/packages/pliny/src/comments/Utterances.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useCallback } from 'react'
2 | import { useTheme } from 'next-themes'
3 |
4 | export interface UtterancesConfig {
5 | provider: 'utterances'
6 | utterancesConfig: {
7 | theme?: string
8 | darkTheme?: string
9 | repo?: string
10 | label?: string
11 | issueTerm?: string
12 | }
13 | }
14 |
15 | export type UtterancesProps = UtterancesConfig['utterancesConfig']
16 |
17 | export const Utterances = ({ theme, darkTheme, repo, label, issueTerm }: UtterancesProps) => {
18 | const { theme: nextTheme, resolvedTheme } = useTheme()
19 | const commentsTheme = nextTheme === 'dark' || resolvedTheme === 'dark' ? darkTheme : theme
20 |
21 | const COMMENTS_ID = 'comments-container'
22 |
23 | const LoadComments = useCallback(() => {
24 | const script = document.createElement('script')
25 | script.src = 'https://utteranc.es/client.js'
26 | script.setAttribute('repo', repo)
27 | script.setAttribute('issue-term', issueTerm)
28 | script.setAttribute('label', label)
29 | script.setAttribute('theme', commentsTheme)
30 | script.setAttribute('crossorigin', 'anonymous')
31 | script.async = true
32 |
33 | const comments = document.getElementById(COMMENTS_ID)
34 | if (comments) comments.appendChild(script)
35 |
36 | return () => {
37 | const comments = document.getElementById(COMMENTS_ID)
38 | if (comments) comments.innerHTML = ''
39 | }
40 | }, [commentsTheme, issueTerm, label, repo])
41 |
42 | // Reload on theme change
43 | useEffect(() => {
44 | LoadComments()
45 | }, [LoadComments])
46 |
47 | // Added `relative` to fix a weird bug with `utterances-frame` position
48 | return
49 | }
50 |
--------------------------------------------------------------------------------
/packages/pliny/src/search/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { AlgoliaSearchProvider } from './Algolia'
3 | import { KBarSearchProvider } from './KBar'
4 |
5 | import type { AlgoliaConfig } from './Algolia'
6 | import type { KBarConfig } from './KBar'
7 |
8 | export type SearchConfig = AlgoliaConfig | KBarConfig
9 | export interface SearchConfigProps {
10 | searchConfig: SearchConfig
11 | children: React.ReactNode
12 | }
13 |
14 | /**
15 | * Command palette like search component - `ctrl-k` to open the palette.
16 | * Or use the search context to bind toggle to an onOpen event.
17 | * Currently supports Algolia or Kbar (local search).
18 | *
19 | * To toggle the modal or search from child components, use the search context:
20 | *
21 | * For Algolia:
22 | * ```
23 | * import { AlgoliaSearchContext } from 'pliny/search/algolia'
24 | * const { query } = useContext(AlgoliaSearchContext)
25 | * ```
26 | *
27 | * For Kbar:
28 | * ```
29 | * import { useKBar } from 'kbar'
30 | * const { query } = useKBar()
31 | * ```
32 | *
33 | * @param {SearchConfig} searchConfig
34 | * @return {*}
35 | */
36 | export const SearchProvider = ({ searchConfig, children }: SearchConfigProps) => {
37 | if (searchConfig && searchConfig.provider) {
38 | switch (searchConfig.provider) {
39 | case 'algolia':
40 | return (
41 |
42 | {children}
43 |
44 | )
45 | case 'kbar':
46 | return (
47 | {children}
48 | )
49 | default:
50 | console.log('No suitable provider found. Please choose from algolia or kbar.')
51 | return <>{children}>
52 | }
53 | } else {
54 | return <>{children}>
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/packages/pliny/src/mdx-plugins/remark-img-to-jsx.ts:
--------------------------------------------------------------------------------
1 | import { Parent, Node, Literal } from 'unist'
2 | import { visit } from 'unist-util-visit'
3 | import { sync as sizeOf } from 'probe-image-size'
4 | import fs from 'fs'
5 |
6 | export type ImageNode = Parent & {
7 | url: string
8 | alt: string
9 | name: string
10 | attributes: (Literal & { name: string })[]
11 | }
12 |
13 | /**
14 | * Converts markdown image nodes to next/image jsx.
15 | *
16 | */
17 | export function remarkImgToJsx() {
18 | return (tree: Node) => {
19 | visit(
20 | tree,
21 | // only visit p tags that contain an img element
22 | (node: Parent): node is Parent =>
23 | node.type === 'paragraph' && node.children.some((n) => n.type === 'image'),
24 | (node: Parent) => {
25 | const imageNodeIndex = node.children.findIndex((n) => n.type === 'image')
26 | const imageNode = node.children[imageNodeIndex] as ImageNode
27 |
28 | // only local files
29 | if (fs.existsSync(`${process.cwd()}/public${imageNode.url}`)) {
30 | const dimensions = sizeOf(fs.readFileSync(`${process.cwd()}/public${imageNode.url}`))
31 |
32 | // Convert original node to next/image
33 | ;(imageNode.type = 'mdxJsxFlowElement'),
34 | (imageNode.name = 'Image'),
35 | (imageNode.attributes = [
36 | { type: 'mdxJsxAttribute', name: 'alt', value: imageNode.alt },
37 | { type: 'mdxJsxAttribute', name: 'src', value: imageNode.url },
38 | { type: 'mdxJsxAttribute', name: 'width', value: dimensions.width },
39 | { type: 'mdxJsxAttribute', name: 'height', value: dimensions.height },
40 | ])
41 | // Change node type from p to div to avoid nesting error
42 | node.type = 'div'
43 | node.children[imageNodeIndex] = imageNode
44 | }
45 | }
46 | )
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/pliny/src/ui/Pre.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useRef, ReactNode } from 'react'
2 |
3 | const Pre = ({ children }: { children: ReactNode }) => {
4 | const textInput = useRef(null)
5 | const [hovered, setHovered] = useState(false)
6 | const [copied, setCopied] = useState(false)
7 |
8 | const onEnter = () => {
9 | setHovered(true)
10 | }
11 | const onExit = () => {
12 | setHovered(false)
13 | setCopied(false)
14 | }
15 | const onCopy = () => {
16 | setCopied(true)
17 | navigator.clipboard.writeText(textInput.current.textContent)
18 | setTimeout(() => {
19 | setCopied(false)
20 | }, 2000)
21 | }
22 |
23 | return (
24 |
25 | {hovered && (
26 |
63 | )}
64 |
65 |
{children}
66 |
67 | )
68 | }
69 |
70 | export default Pre
71 |
--------------------------------------------------------------------------------
/packages/pliny/src/analytics/Umami.tsx:
--------------------------------------------------------------------------------
1 | import Script from 'next/script'
2 |
3 | /**
4 | * Props for the Umami component.
5 | */
6 | export interface UmamiProps {
7 | /** The unique Umami website ID. */
8 | umamiWebsiteId: string
9 | /** The Umami host URL. */
10 | umamiHostUrl?: string
11 | /** Tag to identify the script. */
12 | umamiTag?: string
13 | /** Enable or disable automatic tracking. Defaults to true. */
14 | umamiAutoTrack?: boolean
15 | /** Exclude URL query parameters from tracking. Defaults to false. */
16 | umamiExcludeSearch?: boolean
17 | /** A comma-separated list of domains to limit tracking to. */
18 | umamiDomains?: string
19 | /** Source URL for the Umami script. Defaults to the official CDN. */
20 | src?: string
21 | /** Additional data attributes for the script tag. */
22 | [key: `data${string}`]: any
23 | }
24 |
25 | const propToDataAttributeMap: { [key: string]: string } = {
26 | umamiWebsiteId: 'data-website-id',
27 | umamiHostUrl: 'data-host-url',
28 | umamiTag: 'data-tag',
29 | umamiAutoTrack: 'data-auto-track',
30 | umamiExcludeSearch: 'data-exclude-search',
31 | umamiDomains: 'data-domains',
32 | }
33 |
34 | /**
35 | * A React component that integrates Umami analytics via a script tag.
36 | *
37 | * @param props - The props for the Umami component.
38 | * @returns A Script element with the Umami analytics script and dynamic data attributes.
39 | */
40 | export const Umami = ({ src = 'https://analytics.umami.is/script.js', ...props }: UmamiProps) => {
41 | const dataAttributes: Record = {}
42 |
43 | // Map known Umami props to data attributes
44 | Object.entries(propToDataAttributeMap).forEach(([propName, dataAttrName]) => {
45 | const value = props[propName as keyof UmamiProps]
46 | if (value !== undefined) {
47 | dataAttributes[dataAttrName] = typeof value === 'boolean' ? String(value) : value
48 | }
49 | })
50 |
51 | // Include additional data attributes passed via props
52 | Object.entries(props).forEach(([key, value]) => {
53 | if (key.startsWith('data') && value !== undefined && !(key in propToDataAttributeMap)) {
54 | // Convert camelCase to kebab-case for HTML attributes
55 | const attributeName = key.replace(/([A-Z])/g, '-$1').toLowerCase()
56 | dataAttributes[attributeName] = value
57 | }
58 | })
59 |
60 | return
61 | }
62 |
--------------------------------------------------------------------------------
/packages/pliny/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pliny",
3 | "description": "Main entry point for pliny components",
4 | "homepage": "https://github.com/timlrx/pliny",
5 | "version": "0.4.1",
6 | "type": "module",
7 | "exports": {
8 | "./*": "./*",
9 | "./analytics/*": "./analytics/*",
10 | "./comments/*": "./comments/*",
11 | "./mdx-plugins/*": "./mdx-plugins/*",
12 | "./newsletter/*": "./newsletter/*",
13 | "./search/*": "./search/*",
14 | "./ui/*": "./ui/*",
15 | "./utils/*": "./utils/*"
16 | },
17 | "files": [
18 | "analytics",
19 | "comments",
20 | "mdx-plugins",
21 | "newsletter",
22 | "search",
23 | "ui",
24 | "utils",
25 | "mdx-components.js",
26 | "mdx-components.d.ts",
27 | "config.js",
28 | "config.d.ts",
29 | "chunk*.js"
30 | ],
31 | "scripts": {
32 | "dev": "tsup && cp -fR dist/* ./ && tsup --config tsup.ui.config.ts && rimraf dist && node add-use-client.mjs",
33 | "build": "tsup && cp -fR dist/* ./ && tsup --config tsup.ui.config.ts && rimraf dist && node add-use-client.mjs && yarn copyfiles",
34 | "copyfiles": "copyfiles -f public/algolia.css search"
35 | },
36 | "author": "Timothy Lin (https://timlrx.com)",
37 | "dependencies": {
38 | "@docsearch/react": "^3.6.2",
39 | "@giscus/react": "^3.0.0",
40 | "@mailchimp/mailchimp_marketing": "^3.0.80",
41 | "contentlayer2": "^0.5.3",
42 | "copyfiles": "^2.4.1",
43 | "github-slugger": "^2.0.0",
44 | "js-yaml": "4.1.0",
45 | "kbar": "0.1.0-beta.45",
46 | "next-contentlayer2": "^0.5.3",
47 | "next-themes": "^0.3.0",
48 | "probe-image-size": "^7.2.3",
49 | "remark": "^15.0.0",
50 | "unist-util-visit": "^5.0.0"
51 | },
52 | "peerDependencies": {
53 | "next": ">=13.0.0",
54 | "react": "^17.0.2 || ^18 || ^19 || ^19.0.0-rc",
55 | "react-dom": "^17.0.2 || ^18 || ^19 || ^19.0.0-rc"
56 | },
57 | "devDependencies": {
58 | "@types/copyfiles": "^2",
59 | "next": "14.2.3",
60 | "react": "18.3.1",
61 | "react-dom": "18.3.1",
62 | "rimraf": "^3.0.2",
63 | "tsup": "8.0.2",
64 | "typescript": "^5.1.6"
65 | },
66 | "lint-staged": {
67 | "*.+(js|jsx|ts|tsx)": [
68 | "eslint --fix"
69 | ],
70 | "*.+(js|jsx|ts|tsx|json|css|md|mdx)": [
71 | "prettier --write"
72 | ]
73 | },
74 | "publishConfig": {
75 | "access": "public"
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/packages/pliny/src/comments/index.tsx:
--------------------------------------------------------------------------------
1 | import { Disqus, DisqusConfig, DisqusProps } from './Disqus'
2 | import { Giscus, GiscusConfig, GiscusProps } from './Giscus'
3 | import { Utterances, UtterancesConfig, UtterancesProps } from './Utterances'
4 |
5 | declare global {
6 | interface Window {
7 | disqus_config?: (...args: any[]) => void
8 | DISQUS?: (...args: any[]) => void
9 | }
10 | }
11 |
12 | export type CommentsConfig = GiscusConfig | DisqusConfig | UtterancesConfig
13 |
14 | /**
15 | * @example
16 | * const comments: CommentsConfig = {
17 | * provider: 'giscus', // supported providers: giscus, utterances, disqus
18 | * giscusConfig: {
19 | * // Visit the link below, and follow the steps in the 'configuration' section
20 | * // https://giscus.app/
21 | * repo: process.env.NEXT_PUBLIC_GISCUS_REPO,
22 | * repositoryId: process.env.NEXT_PUBLIC_GISCUS_REPOSITORY_ID,
23 | * category: process.env.NEXT_PUBLIC_GISCUS_CATEGORY,
24 | * categoryId: process.env.NEXT_PUBLIC_GISCUS_CATEGORY_ID,
25 | * mapping: 'pathname', // supported options: pathname, url, title
26 | * reactions: '1', // Emoji reactions: 1 = enable / 0 = disable
27 | * // Send discussion metadata periodically to the parent window: 1 = enable / 0 = disable
28 | * metadata: '0',
29 | * // theme example: light, dark, dark_dimmed, dark_high_contrast
30 | * // transparent_dark, preferred_color_scheme, custom
31 | * theme: 'light',
32 | * // theme when dark mode
33 | * darkTheme: 'transparent_dark',
34 | * // If the theme option above is set to 'custom`
35 | * // please provide a link below to your custom theme css file.
36 | * // example: https://giscus.app/themes/custom_example.css
37 | * themeURL: '',
38 | * },
39 | * }
40 | *
41 | */
42 | export interface CommentsProps {
43 | commentsConfig: CommentsConfig
44 | slug?: string
45 | }
46 |
47 | /**
48 | * Supports Giscus, Utterances or Disqus
49 | * If you want to use a comments provider you have to add it to the
50 | * content security policy in the `next.config.js` file.
51 | * slug is used in disqus to identify the page
52 | *
53 | * @param {CommentsProps} { comments, slug }
54 | * @return {*}
55 | */
56 | export const Comments = ({ commentsConfig, slug }: CommentsProps) => {
57 | switch (commentsConfig.provider) {
58 | case 'giscus':
59 | return
60 | case 'utterances':
61 | return
62 | case 'disqus':
63 | return
64 | }
65 | }
66 |
67 | export { Disqus, Giscus, Utterances }
68 | export type {
69 | DisqusConfig,
70 | DisqusProps,
71 | GiscusConfig,
72 | GiscusProps,
73 | UtterancesConfig,
74 | UtterancesProps,
75 | }
76 |
--------------------------------------------------------------------------------
/packages/pliny/src/ui/NewsletterForm.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState } from 'react'
2 |
3 | export interface NewsletterFormProps {
4 | title?: string
5 | apiUrl?: string
6 | }
7 |
8 | const NewsletterForm = ({
9 | title = 'Subscribe to the newsletter',
10 | apiUrl = '/api/newsletter',
11 | }: NewsletterFormProps) => {
12 | const inputEl = useRef(null)
13 | const [error, setError] = useState(false)
14 | const [message, setMessage] = useState('')
15 | const [subscribed, setSubscribed] = useState(false)
16 |
17 | const subscribe = async (e: React.FormEvent) => {
18 | e.preventDefault()
19 |
20 | const res = await fetch(apiUrl, {
21 | body: JSON.stringify({
22 | email: inputEl.current.value,
23 | }),
24 | headers: {
25 | 'Content-Type': 'application/json',
26 | },
27 | method: 'POST',
28 | })
29 |
30 | const { error } = await res.json()
31 | if (error) {
32 | setError(true)
33 | setMessage('Your e-mail address is invalid or you are already subscribed!')
34 | return
35 | }
36 |
37 | inputEl.current.value = ''
38 | setError(false)
39 | setSubscribed(true)
40 | }
41 |
42 | return (
43 |
44 |
{title}
45 |
74 | {error && (
75 |
{message}
76 | )}
77 |
78 | )
79 | }
80 |
81 | export default NewsletterForm
82 |
--------------------------------------------------------------------------------
/packages/pliny/src/analytics/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | import { GA, GoogleAnalyticsProps } from './GoogleAnalytics'
3 | import { Plausible, PlausibleProps } from './Plausible'
4 | import { SimpleAnalytics, SimpleAnalyticsProps } from './SimpleAnalytics.js'
5 | import { Umami, UmamiProps } from './Umami'
6 | import { Posthog, PosthogProps } from './Posthog'
7 | import { Clarity, ClarityProps } from './MicrosoftClarity'
8 |
9 | declare global {
10 | interface Window {
11 | gtag?: (...args: any[]) => void
12 | plausible?: (...args: any[]) => void
13 | sa_event?: (...args: any[]) => void
14 | }
15 | }
16 |
17 | export interface AnalyticsConfig {
18 | googleAnalytics?: GoogleAnalyticsProps
19 | plausibleAnalytics?: PlausibleProps
20 | umamiAnalytics?: UmamiProps
21 | posthogAnalytics?: PosthogProps
22 | simpleAnalytics?: SimpleAnalyticsProps
23 | clarityAnalytics?: ClarityProps
24 | }
25 |
26 | /**
27 | * @example
28 | * const analytics: AnalyticsConfig = {
29 | * plausibleDataDomain: '', // e.g. tailwind-nextjs-starter-blog.vercel.app
30 | * simpleAnalytics: false, // true or false
31 | * umamiWebsiteId: '', // e.g. 123e4567-e89b-12d3-a456-426614174000
32 | * posthogProjectApiKey: '', // e.g. AhnJK8392ndPOav87as450xd
33 | * googleAnalyticsId: '', // e.g. UA-000000-2 or G-XXXXXXX
34 | * ClarityWebsiteId: '', // e.g. abcdefjhij
35 | * }
36 | */
37 | export interface AnalyticsProps {
38 | analyticsConfig: AnalyticsConfig
39 | }
40 |
41 | const isProduction = process.env.NODE_ENV === 'production'
42 |
43 | /**
44 | * Supports Plausible, Simple Analytics, Umami, Posthog or Google Analytics.
45 | * All components default to the hosted service, but can be configured to use a self-hosted
46 | * or proxied version of the script by providing the `src` / `apiHost` props.
47 | *
48 | * Note: If you want to use an analytics provider you have to add it to the
49 | * content security policy in the `next.config.js` file.
50 | * @param {AnalyticsProps} { analytics }
51 | * @return {*}
52 | */
53 | export const Analytics = ({ analyticsConfig }: AnalyticsProps) => {
54 | return (
55 | <>
56 | {isProduction && analyticsConfig.plausibleAnalytics && (
57 |
58 | )}
59 | {isProduction && analyticsConfig.simpleAnalytics && (
60 |
61 | )}
62 | {isProduction && analyticsConfig.posthogAnalytics && (
63 |
64 | )}
65 | {isProduction && analyticsConfig.umamiAnalytics && (
66 |
67 | )}
68 | {isProduction && analyticsConfig.googleAnalytics && (
69 |
70 | )}
71 | {isProduction && analyticsConfig.clarityAnalytics && (
72 |
73 | )}
74 | >
75 | )
76 | }
77 |
78 | export { GA, Plausible, SimpleAnalytics, Umami, Posthog, Clarity }
79 |
80 | export type { GoogleAnalyticsProps, PlausibleProps, UmamiProps, PosthogProps, SimpleAnalyticsProps, ClarityProps }
81 |
--------------------------------------------------------------------------------
/packages/pliny/src/search/KBar.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, FC, ReactNode } from 'react'
2 | import type { Action } from 'kbar'
3 | import { KBarProvider } from 'kbar'
4 | import { useRouter } from 'next/navigation.js'
5 | import { KBarModal } from './KBarModal'
6 | import { CoreContent, MDXDocument } from '../utils/contentlayer'
7 | import { formatDate } from '../utils/formatDate'
8 |
9 | export interface KBarSearchProps {
10 | searchDocumentsPath: string | false
11 | defaultActions?: Action[]
12 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
13 | onSearchDocumentsLoad?: (json: any) => Action[]
14 | }
15 |
16 | export interface KBarConfig {
17 | provider: 'kbar'
18 | kbarConfig: KBarSearchProps
19 | }
20 |
21 | /**
22 | * Command palette like search component with kbar - `ctrl-k` to open the palette.
23 | *
24 | * Default actions can be overridden by passing in an array of actions to `defaultActions`.
25 | * To load actions dynamically, pass in a `searchDocumentsPath` to a JSON file.
26 | * `onSearchDocumentsLoad` can be used to transform the JSON into actions.
27 | *
28 | * To toggle the modal or search from child components, use the search context:
29 | * ```
30 | * import { useKBar } from 'kbar'
31 | * const { query } = useKBar()
32 | * ```
33 | * See https://github.com/timc1/kbar/blob/main/src/types.ts#L98-L106 for typings.
34 | *
35 | * @param {*} { kbarConfig, children }
36 | * @return {*}
37 | */
38 | export const KBarSearchProvider: FC<{
39 | children: ReactNode
40 | kbarConfig: KBarSearchProps
41 | }> = ({ kbarConfig, children }) => {
42 | const router = useRouter()
43 | const { searchDocumentsPath, defaultActions, onSearchDocumentsLoad } = kbarConfig
44 | const [searchActions, setSearchActions] = useState([])
45 | const [dataLoaded, setDataLoaded] = useState(false)
46 |
47 | useEffect(() => {
48 | const mapPosts = (posts: CoreContent[]) => {
49 | const actions: Action[] = []
50 | for (const post of posts) {
51 | actions.push({
52 | id: post.path,
53 | name: post.title,
54 | keywords: post?.summary || '',
55 | section: 'Content',
56 | subtitle: formatDate(post.date, 'en-US'),
57 | perform: () => router.push('/' + post.path),
58 | })
59 | }
60 | return actions
61 | }
62 | async function fetchData() {
63 | if (searchDocumentsPath) {
64 | const url =
65 | searchDocumentsPath.indexOf('://') > 0 || searchDocumentsPath.indexOf('//') === 0
66 | ? searchDocumentsPath
67 | : new URL(searchDocumentsPath, window.location.origin)
68 | const res = await fetch(url)
69 | const json = await res.json()
70 | const actions = onSearchDocumentsLoad ? onSearchDocumentsLoad(json) : mapPosts(json)
71 | setSearchActions(actions)
72 | setDataLoaded(true)
73 | }
74 | }
75 | if (!dataLoaded && searchDocumentsPath) {
76 | fetchData()
77 | } else {
78 | setDataLoaded(true)
79 | }
80 | }, [defaultActions, dataLoaded, router, searchDocumentsPath, onSearchDocumentsLoad])
81 |
82 | return (
83 |
84 |
85 | {children}
86 |
87 | )
88 | }
89 |
--------------------------------------------------------------------------------
/packages/pliny/src/ui/TOCInline.tsx:
--------------------------------------------------------------------------------
1 | import { Toc, TocItem } from '../mdx-plugins/remark-toc-headings'
2 |
3 | export interface TOCInlineProps {
4 | toc: Toc
5 | fromHeading?: number
6 | toHeading?: number
7 | asDisclosure?: boolean
8 | exclude?: string | string[]
9 | collapse?: boolean
10 | ulClassName?: string
11 | liClassName?: string
12 | }
13 |
14 | export interface NestedTocItem extends TocItem {
15 | children?: NestedTocItem[]
16 | }
17 |
18 | const createNestedList = (items: TocItem[]): NestedTocItem[] => {
19 | const nestedList: NestedTocItem[] = []
20 | const stack: NestedTocItem[] = []
21 |
22 | items.forEach((item) => {
23 | const newItem: NestedTocItem = { ...item }
24 |
25 | while (stack.length > 0 && stack[stack.length - 1].depth >= newItem.depth) {
26 | stack.pop()
27 | }
28 |
29 | const parent = stack.length > 0 ? stack[stack.length - 1] : null
30 |
31 | if (parent) {
32 | parent.children = parent.children || []
33 | parent.children.push(newItem)
34 | } else {
35 | nestedList.push(newItem)
36 | }
37 |
38 | stack.push(newItem)
39 | })
40 |
41 | return nestedList
42 | }
43 |
44 | /**
45 | * Generates an inline table of contents
46 | * Exclude titles matching this string (new RegExp('^(' + string + ')$', 'i')).
47 | * If an array is passed the array gets joined with a pipe (new RegExp('^(' + array.join('|') + ')$', 'i')).
48 | *
49 | * `asDisclosure` will wrap the TOC in a `details` element with a `summary` element.
50 | * `collapse` will collapse the TOC when `AsDisclosure` is true.
51 | *
52 | * If you are using tailwind css and want to revert to the default HTML list style, set `ulClassName="[&_ul]:list-[revert]"`
53 | * @param {TOCInlineProps} {
54 | * toc,
55 | * fromHeading = 1,
56 | * toHeading = 6,
57 | * asDisclosure = false,
58 | * exclude = '',
59 | * collapse = false,
60 | * ulClassName = '',
61 | * liClassName = '',
62 | * }
63 | *
64 | */
65 | const TOCInline = ({
66 | toc,
67 | fromHeading = 1,
68 | toHeading = 6,
69 | asDisclosure = false,
70 | exclude = '',
71 | collapse = false,
72 | ulClassName = '',
73 | liClassName = '',
74 | }: TOCInlineProps) => {
75 | const re = Array.isArray(exclude)
76 | ? new RegExp('^(' + exclude.join('|') + ')$', 'i')
77 | : new RegExp('^(' + exclude + ')$', 'i')
78 |
79 | const filteredToc = toc.filter(
80 | (heading) =>
81 | heading.depth >= fromHeading && heading.depth <= toHeading && !re.test(heading.value)
82 | )
83 |
84 | const createList = (items: NestedTocItem[] | undefined) => {
85 | if (!items || items.length === 0) {
86 | return null
87 | }
88 |
89 | return (
90 |
91 | {items.map((item, index) => (
92 | -
93 | {item.value}
94 | {createList(item.children)}
95 |
96 | ))}
97 |
98 | )
99 | }
100 |
101 | const nestedList = createNestedList(filteredToc)
102 |
103 | return (
104 | <>
105 | {asDisclosure ? (
106 |
107 | Table of Contents
108 | {createList(nestedList)}
109 |
110 | ) : (
111 | createList(nestedList)
112 | )}
113 | >
114 | )
115 | }
116 |
117 | export default TOCInline
118 |
--------------------------------------------------------------------------------
/packages/pliny/src/utils/contentlayer.ts:
--------------------------------------------------------------------------------
1 | import type { Document, MDX } from 'contentlayer2/core'
2 |
3 | const isProduction = process.env.NODE_ENV === 'production'
4 |
5 | export type MDXDocument = Document & { body: MDX }
6 | export type MDXDocumentDate = MDXDocument & {
7 | date: string
8 | }
9 | export type MDXBlog = MDXDocumentDate & {
10 | tags?: string[]
11 | draft?: boolean
12 | }
13 |
14 | export type MDXAuthor = MDXDocument & {
15 | name: string
16 | }
17 |
18 | export function dateSortDesc(a: string, b: string) {
19 | if (a > b) return -1
20 | if (a < b) return 1
21 | return 0
22 | }
23 |
24 | /**
25 | * Sorts a list of MDX documents by date in descending order
26 | *
27 | * @param {MDXDocumentDate[]} allBlogs
28 | * @param {string} [dateKey='date']
29 | * @return {*}
30 | */
31 | export function sortPosts(allBlogs: T[], dateKey: string = 'date') {
32 | return allBlogs.sort((a, b) => dateSortDesc(a[dateKey], b[dateKey]))
33 | }
34 |
35 | /**
36 | * Kept for backwards compatibility
37 | * Please use `sortPosts` instead
38 | * @deprecated
39 | * @param {MDXBlog[]} allBlogs
40 | * @return {*}
41 | */
42 | export function sortedBlogPost(allBlogs: MDXDocumentDate[]) {
43 | return sortPosts(allBlogs)
44 | }
45 |
46 | type ConvertUndefined = OrNull<{
47 | [K in keyof T as undefined extends T[K] ? K : never]-?: T[K]
48 | }>
49 | type OrNull = { [K in keyof T]: Exclude | null }
50 | type PickRequired = {
51 | [K in keyof T as undefined extends T[K] ? never : K]: T[K]
52 | }
53 | type ConvertPick = ConvertUndefined & PickRequired
54 |
55 | /**
56 | * A typesafe omit helper function
57 | * @example pick(content, ['title', 'description'])
58 | *
59 | * @param {Obj} obj
60 | * @param {Keys[]} keys
61 | * @return {*} {ConvertPick<{ [K in Keys]: Obj[K] }>}
62 | */
63 | export const pick = (
64 | obj: Obj,
65 | keys: Keys[]
66 | ): ConvertPick<{ [K in Keys]: Obj[K] }> => {
67 | return keys.reduce((acc, key) => {
68 | acc[key] = obj[key] ?? null
69 | return acc
70 | }, {} as any)
71 | }
72 |
73 | /**
74 | * A typesafe omit helper function
75 | * @example omit(content, ['body', '_raw', '_id'])
76 | *
77 | * @param {Obj} obj
78 | * @param {Keys[]} keys
79 | * @return {*} {Omit}
80 | */
81 | export const omit = (obj: Obj, keys: Keys[]): Omit => {
82 | const result = Object.assign({}, obj)
83 | keys.forEach((key) => {
84 | delete result[key]
85 | })
86 | return result
87 | }
88 |
89 | export type CoreContent = Omit
90 |
91 | /**
92 | * Omit body, _raw, _id from MDX document and return only the core content
93 | *
94 | * @param {T} content
95 | * @return {*} {CoreContent}
96 | */
97 | export function coreContent(content: T): CoreContent {
98 | return omit(content, ['body', '_raw', '_id'])
99 | }
100 |
101 | /**
102 | * Omit body, _raw, _id from a list of MDX documents and returns only the core content
103 | * If `NODE_ENV` === "production", it will also filter out any documents with draft: true.
104 | *
105 | * @param {T[]} contents
106 | * @return {*} {CoreContent[]}
107 | */
108 | export function allCoreContent(contents: T[]): CoreContent[] {
109 | if (isProduction)
110 | return contents.map((c) => coreContent(c)).filter((c) => !('draft' in c && c.draft === true))
111 | return contents.map((c) => coreContent(c))
112 | }
113 |
--------------------------------------------------------------------------------
/packages/pliny/src/search/KBarModal.tsx:
--------------------------------------------------------------------------------
1 | // noinspection TypeScriptMissingConfigOption
2 |
3 | import {
4 | KBarPortal,
5 | KBarSearch,
6 | KBarAnimator,
7 | KBarPositioner,
8 | KBarResults,
9 | useMatches,
10 | Action,
11 | useRegisterActions,
12 | } from 'kbar'
13 |
14 | export const KBarModal = ({ actions, isLoading }: { actions: Action[]; isLoading: boolean }) => {
15 | useRegisterActions(actions, [actions])
16 |
17 | return (
18 |
19 |
20 |
21 |
22 |
23 |
24 |
38 |
39 |
40 |
41 | ESC
42 |
43 |
44 | {!isLoading &&
}
45 | {isLoading && (
46 |
47 | Loading...
48 |
49 | )}
50 |
51 |
52 |
53 |
54 | )
55 | }
56 |
57 | const RenderResults = () => {
58 | const { results } = useMatches()
59 |
60 | if (results.length) {
61 | return (
62 | (
65 |
66 | {typeof item === 'string' ? (
67 |
68 |
69 | {item}
70 |
71 |
72 | ) : (
73 |
80 |
81 | {item.icon &&
{item.icon}
}
82 |
83 | {item.subtitle && (
84 |
85 | {item.subtitle}
86 |
87 | )}
88 |
{item.name}
89 |
90 |
91 | {item.shortcut?.length ? (
92 |
93 | {item.shortcut.map((sc) => (
94 |
100 | {sc}
101 |
102 | ))}
103 |
104 | ) : null}
105 |
106 | )}
107 |
108 | )}
109 | />
110 | )
111 | } else {
112 | return (
113 |
114 | No results for your search...
115 |
116 | )
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/packages/pliny/src/config.ts:
--------------------------------------------------------------------------------
1 | import type { AnalyticsConfig } from './analytics'
2 | import type { CommentsConfig } from './comments'
3 | import type { NewsletterConfig } from './newsletter'
4 | import type { SearchConfig } from './search'
5 |
6 | export interface CoreConfig {
7 | title: string
8 | author: string
9 | headerTitle: string
10 | description: string
11 | language: string
12 | /** light and dark */
13 | theme: 'system' | 'dark' | 'light'
14 | siteUrl: string
15 | siteRepo: string
16 | siteLogo: string
17 | image: string
18 | socialBanner: string
19 | email: string
20 | github: string
21 | twitter?: string
22 | facebook?: string
23 | youtube?: string
24 | linkedin?: string
25 | locale: string
26 | }
27 |
28 | export type PlinyConfig = Record &
29 | CoreConfig & {
30 | analytics?: AnalyticsConfig
31 | comments?: CommentsConfig
32 | newsletter?: NewsletterConfig
33 | search?: SearchConfig
34 | }
35 |
36 | const sampleConfig: PlinyConfig = {
37 | title: 'Next.js Starter Blog',
38 | author: 'Tails Azimuth',
39 | headerTitle: 'TailwindBlog',
40 | description: 'A blog created with Next.js and Tailwind.css',
41 | language: 'en-us',
42 | theme: 'system',
43 | siteUrl: 'https://tailwind-nextjs-starter-blog.vercel.app',
44 | siteRepo: 'https://github.com/timlrx/tailwind-nextjs-starter-blog',
45 | siteLogo: '/static/images/logo.png',
46 | image: '/static/images/avatar.png',
47 | socialBanner: '/static/images/twitter-card.png',
48 | email: 'address@yoursite.com',
49 | github: 'https://github.com',
50 | twitter: 'https://twitter.com/Twitter',
51 | facebook: 'https://facebook.com',
52 | youtube: 'https://youtube.com',
53 | linkedin: 'https://www.linkedin.com',
54 | locale: 'en-US',
55 | analytics: {
56 | // If you want to use an analytics provider you have to add it to the
57 | // content security policy in the `next.config.js` file.
58 | // supports Plausible, Simple Analytics, Umami, Posthog or Google Analytics.
59 | plausibleAnalytics: {
60 | plausibleDataDomain: '', // e.g. tailwind-nextjs-starter-blog.vercel.app
61 | },
62 | simpleAnalytics: {},
63 | umamiAnalytics: {
64 | umamiWebsiteId: '', // e.g. 123e4567-e89b-12d3-a456-426614174000
65 | },
66 | posthogAnalytics: {
67 | posthogProjectApiKey: '', // e.g. 123e4567-e89b-12d3-a456-426614174000
68 | },
69 | googleAnalytics: {
70 | googleAnalyticsId: '', // e.g. G-XXXXXXX
71 | },
72 | clarityAnalytics: {
73 | ClarityWebsiteId: '', // e.g. abcdefjhij
74 | },
75 | },
76 | newsletter: {
77 | // supports mailchimp, buttondown, convertkit, klaviyo, emailOctopus
78 | // Please add your .env file and modify it according to your selection
79 | provider: 'buttondown',
80 | },
81 | comments: {
82 | // If you want to use an analytics provider you have to add it to the
83 | // content security policy in the `next.config.js` file.
84 | // Select a provider and use the environment variables associated to it
85 | // https://vercel.com/docs/environment-variables
86 | provider: 'giscus', // supported providers: giscus, utterances, disqus
87 | giscusConfig: {
88 | // Visit the link below, and follow the steps in the 'configuration' section
89 | // https://giscus.app/
90 | repo: process.env.NEXT_PUBLIC_GISCUS_REPO,
91 | repositoryId: process.env.NEXT_PUBLIC_GISCUS_REPOSITORY_ID,
92 | category: process.env.NEXT_PUBLIC_GISCUS_CATEGORY,
93 | categoryId: process.env.NEXT_PUBLIC_GISCUS_CATEGORY_ID,
94 | mapping: 'pathname', // supported options: pathname, url, title
95 | reactions: '1', // Emoji reactions: 1 = enable / 0 = disable
96 | // Send discussion metadata periodically to the parent window: 1 = enable / 0 = disable
97 | metadata: '0',
98 | // theme example: light, dark, dark_dimmed, dark_high_contrast
99 | // transparent_dark, preferred_color_scheme, custom
100 | theme: 'light',
101 | // theme when dark mode
102 | darkTheme: 'transparent_dark',
103 | // If the theme option above is set to 'custom`
104 | // please provide a link below to your custom theme css file.
105 | // example: https://giscus.app/themes/custom_example.css
106 | themeURL: '',
107 | },
108 | },
109 | search: {
110 | provider: 'kbar', // kbar or algolia
111 | kbarConfig: {
112 | searchDocumentsPath: 'search.json', // path to load documents to search relative to public folder
113 | },
114 | // algoliaConfig: {
115 | // // The application ID provided by Algolia
116 | // appId: 'YOUR_APP_ID',
117 | // // Public API key: it is safe to commit it
118 | // apiKey: 'YOUR_SEARCH_API_KEY',
119 | // indexName: 'YOUR_INDEX_NAME',
120 | // //... other Algolia params
121 | // },
122 | },
123 | }
124 |
--------------------------------------------------------------------------------
/packages/pliny/src/newsletter/index.ts:
--------------------------------------------------------------------------------
1 | import { NextApiRequest, NextApiResponse } from 'next'
2 | import { NextResponse } from 'next/server'
3 | import type { NextRequest } from 'next/server'
4 | import { buttondownSubscribe } from './buttondown'
5 | import { convertkitSubscribe } from './convertkit'
6 | import { mailchimpSubscribe } from './mailchimp'
7 | import { klaviyoSubscribe } from './klaviyo'
8 | import { emailOctopusSubscribe } from './emailOctopus'
9 | import { beehiivSubscribe } from './beehiiv'
10 |
11 | export interface NewsletterConfig {
12 | provider: 'buttondown' | 'convertkit' | 'klaviyo' | 'mailchimp' | 'emailoctopus' | 'beehiiv'
13 | }
14 |
15 | export interface NewsletterRequest extends NextApiRequest {
16 | options: NewsletterConfig
17 | }
18 |
19 | export type NewsletterResponse = NextApiResponse
20 |
21 | async function NewsletterAPIHandler(
22 | req: NextApiRequest,
23 | res: NextApiResponse,
24 | options: NewsletterConfig
25 | ) {
26 | const { email } = req.body
27 | if (!email) {
28 | return res.status(400).json({ error: 'Email is required' })
29 | }
30 | try {
31 | let response: Response
32 | switch (options.provider) {
33 | case 'buttondown':
34 | response = await buttondownSubscribe(email)
35 | break
36 | case 'convertkit':
37 | response = await convertkitSubscribe(email)
38 | break
39 | case 'mailchimp':
40 | response = await mailchimpSubscribe(email)
41 | break
42 | case 'klaviyo':
43 | response = await klaviyoSubscribe(email)
44 | break
45 | case 'emailoctopus':
46 | response = await emailOctopusSubscribe(email)
47 | break
48 | case 'beehiiv':
49 | response = await beehiivSubscribe(email)
50 | break
51 | default:
52 | res.status(500).json({ error: `${options.provider} not supported` })
53 | }
54 | if (response.status >= 400) {
55 | res.status(response.status).json({ error: `There was an error subscribing to the list.` })
56 | }
57 | res.status(201).json({ message: 'Successfully subscribed to the newsletter' })
58 | } catch (error) {
59 | res.status(500).json({ error: error.message || error.toString() })
60 | }
61 | }
62 |
63 | async function NewsletterRouteHandler(req: NextRequest, options: NewsletterConfig) {
64 | const { email } = await req.json()
65 | if (!email) {
66 | return NextResponse.json({ error: 'Email is required' }, { status: 400 })
67 | }
68 | try {
69 | let response: Response
70 | switch (options.provider) {
71 | case 'buttondown':
72 | response = await buttondownSubscribe(email)
73 | break
74 | case 'convertkit':
75 | response = await convertkitSubscribe(email)
76 | break
77 | case 'mailchimp':
78 | response = await mailchimpSubscribe(email)
79 | break
80 | case 'klaviyo':
81 | response = await klaviyoSubscribe(email)
82 | break
83 | case 'emailoctopus':
84 | response = await emailOctopusSubscribe(email)
85 | break
86 | case 'beehiiv':
87 | response = await beehiivSubscribe(email)
88 | break
89 | default:
90 | return NextResponse.json({ error: `${options.provider} not supported` }, { status: 500 })
91 | }
92 | if (response.status >= 400) {
93 | return NextResponse.json(
94 | { error: `There was an error subscribing to the list` },
95 | { status: response.status }
96 | )
97 | }
98 | return NextResponse.json(
99 | { message: 'Successfully subscribed to the newsletter' },
100 | { status: 201 }
101 | )
102 | } catch (error) {
103 | return NextResponse.json({ error: error.message || error.toString() }, { status: 500 })
104 | }
105 | }
106 |
107 | export function NewsletterAPI(options: NewsletterConfig): any
108 | export function NewsletterAPI(req: NextRequest, options: NewsletterConfig): any
109 | export function NewsletterAPI(
110 | req: NextApiRequest,
111 | res: NextApiResponse,
112 | options: NewsletterConfig
113 | ): any
114 |
115 | export function NewsletterAPI(
116 | ...args:
117 | | [NewsletterConfig]
118 | | [NextRequest, NewsletterConfig]
119 | | [NextApiRequest, NextApiResponse, NewsletterConfig]
120 | ) {
121 | if (args.length === 1) {
122 | return async (req: NewsletterRequest | NextRequest, res: NewsletterResponse) => {
123 | // For route handlers, 2nd argument contains the 'params' property instead of a response object
124 | if ('params' in res) {
125 | return await NewsletterRouteHandler(req as NextRequest, args[0])
126 | }
127 | return await NewsletterAPIHandler(req as NewsletterRequest, res, args[0])
128 | }
129 | }
130 |
131 | if (args.length === 2) {
132 | return NewsletterRouteHandler(...args)
133 | }
134 |
135 | return NewsletterAPIHandler(...args)
136 | }
137 |
--------------------------------------------------------------------------------
/packages/pliny/src/search/Algolia.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useRef, useCallback } from 'react'
2 | import { AnchorHTMLAttributes } from 'react'
3 | import { createPortal } from 'react-dom'
4 | import { useRouter } from 'next/navigation.js'
5 | import Link from 'next/link.js'
6 | import { useDocSearchKeyboardEvents } from '@docsearch/react'
7 | import type { LinkProps } from 'next/link'
8 | import type {
9 | DocSearchModal as DocSearchModalType,
10 | DocSearchProps,
11 | DocSearchModalProps,
12 | } from '@docsearch/react'
13 |
14 | export type AlgoliaSearchProps = {
15 | algoliaConfig: DocSearchProps
16 | }
17 |
18 | export interface AlgoliaConfig {
19 | provider: 'algolia'
20 | algoliaConfig: DocSearchProps
21 | }
22 |
23 | export interface AlgoliaSearchQuery {
24 | setSearch: (search: string) => void
25 | toggle: () => void
26 | }
27 |
28 | export interface AlgoliaSearchContext {
29 | query: AlgoliaSearchQuery
30 | }
31 |
32 | let DocSearchModal: typeof DocSearchModalType | null = null
33 |
34 | const CustomLink = ({ href, ...rest }: LinkProps & AnchorHTMLAttributes) => {
35 | const isInternalLink = href && href.startsWith('/')
36 | const isAnchorLink = href && href.startsWith('#')
37 |
38 | if (isInternalLink) {
39 | return
40 | }
41 |
42 | if (isAnchorLink) {
43 | return
44 | }
45 |
46 | return
47 | }
48 |
49 | function Hit({
50 | hit,
51 | children,
52 | }: {
53 | hit: Parameters>[0]['hit']
54 | children: React.ReactNode
55 | }) {
56 | return {children}
57 | }
58 |
59 | export const AlgoliaSearchContext = React.createContext(
60 | {} as AlgoliaSearchContext
61 | )
62 |
63 | /**
64 | * Command palette like search component for algolia - `ctrl-k` to open the palette.
65 | * To toggle the modal or search from child components, use the search context:
66 | * ```
67 | * import { AlgoliaSearchContext } from 'pliny/search/algolia'
68 | * const { query } = useContext(AlgoliaSearchContext)
69 | * ```
70 | *
71 | */
72 | export const AlgoliaSearchProvider: React.FC> = (
73 | props
74 | ) => {
75 | const { algoliaConfig } = props
76 |
77 | const router = useRouter()
78 | const [isOpen, setIsOpen] = useState(false)
79 | const [initialQuery, setInitialQuery] = useState(undefined)
80 |
81 | const importDocSearchModalIfNeeded = useCallback(() => {
82 | if (DocSearchModal) {
83 | return Promise.resolve()
84 | }
85 |
86 | return Promise.all([import('@docsearch/react')]).then(([{ DocSearchModal: Modal }]) => {
87 | DocSearchModal = Modal
88 | })
89 | }, [])
90 |
91 | const onOpen = useCallback(() => {
92 | importDocSearchModalIfNeeded().then(() => {
93 | setIsOpen(true)
94 | })
95 | }, [importDocSearchModalIfNeeded, setIsOpen])
96 |
97 | const onClose = useCallback(() => {
98 | setIsOpen(false)
99 | }, [setIsOpen])
100 |
101 | const onInput = useCallback(
102 | (event: KeyboardEvent) => {
103 | importDocSearchModalIfNeeded().then(() => {
104 | setIsOpen(true)
105 | setInitialQuery(event.key)
106 | })
107 | },
108 | [importDocSearchModalIfNeeded, setIsOpen, setInitialQuery]
109 | )
110 |
111 | const navigator = useRef({
112 | navigate({ itemUrl }: { itemUrl?: string }) {
113 | // Algolia results could contain URL's from other domains which cannot
114 | // be served through history and should navigate with window.location
115 | const isInternalLink = itemUrl.startsWith('/')
116 | const isAnchorLink = itemUrl.startsWith('#')
117 | if (!isInternalLink && !isAnchorLink) {
118 | window.open(itemUrl, '_blank')
119 | } else {
120 | router.push(itemUrl)
121 | }
122 | },
123 | }).current
124 |
125 | const transformItems = useRef((items) =>
126 | items.map((item) => {
127 | // Assuming all entries indexed in algolia are internal domains
128 |
129 | const isInternalLink = item.url.startsWith('/')
130 | const isAnchorLink = item.url.startsWith('#')
131 | if (isInternalLink || isAnchorLink) {
132 | return item
133 | }
134 |
135 | // We transform the absolute URL into a relative URL.
136 | const url = new URL(item.url)
137 | return {
138 | ...item,
139 | // url: withBaseUrl(`${url.pathname}${url.hash}`),
140 | url: `${url.pathname}${url.hash}`,
141 | }
142 | })
143 | ).current
144 |
145 | useDocSearchKeyboardEvents({
146 | isOpen,
147 | onOpen,
148 | onClose,
149 | onInput,
150 | })
151 |
152 | return (
153 |
156 | {props.children}
157 | {isOpen &&
158 | DocSearchModal &&
159 | createPortal(
160 | ,
169 | document.body
170 | )}
171 |
172 | )
173 | }
174 |
--------------------------------------------------------------------------------
/packages/pliny/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # pliny
2 |
3 | ## 0.4.1
4 |
5 | ### Patch Changes
6 |
7 | - f45452b: change buttondown api params email to email_address
8 |
9 | ## 0.4.0
10 |
11 | ### Minor Changes
12 |
13 | - ef38b03: remove dependency on internal types
14 |
15 | ## 0.3.3
16 |
17 | ### Patch Changes
18 |
19 | - a2de4cb: add support for react 19 and next 15
20 | - 5dfaf6a: fix toc slugs are tracked across documents
21 | - 464eaf6: add z-index to kbar modal
22 | - 464eaf6: add umami options
23 |
24 | ## 0.3.2
25 |
26 | ### Patch Changes
27 |
28 | - 8cfb933: fix beehiiv url
29 |
30 | ## 0.3.1
31 |
32 | ### Patch Changes
33 |
34 | - 3b4fe2b: Added Beehiiv newsletter integration
35 |
36 | - Created new beehiiv.ts file with subscription functionality
37 | - Updated index.ts to include Beehiiv as a newsletter provider option
38 |
39 | ## 0.3.0
40 |
41 | ### Minor Changes
42 |
43 | - a4f7aa1: remove revue from newsletter list
44 |
45 | ### Patch Changes
46 |
47 | - c4a356a: update pliny to v0.5
48 | - f3f414e: update klaviyo api
49 |
50 | ## 0.2.2
51 |
52 | ### Patch Changes
53 |
54 | - 5618d7b: include only relevant files in dist
55 |
56 | ## 0.2.1
57 |
58 | ### Patch Changes
59 |
60 | - 45243a4: update contentlayer2 to v0.4.6
61 |
62 | ## 0.2.0
63 |
64 | ### Minor Changes
65 |
66 | - 949804e: Fix converting internal domains to relative paths in the Algolia Search component.
67 | - fde0d56: update dependencies
68 |
69 | ### Patch Changes
70 |
71 | - f0a7c10: Add liClassName prop for TOCInline
72 | - b0d057f: Add microsoft clarity analytics
73 |
74 | ## 0.1.7
75 |
76 | ### Patch Changes
77 |
78 | - 0af664f: update kbar and github slugger versions
79 | - 61fa6f0: support nested toc headings and ul classname
80 |
81 | ## 0.1.6
82 |
83 | ### Patch Changes
84 |
85 | - 0f6f3ab: fix disqus style when changing theme
86 | - 06973b1: Add option to collapse TOCInline
87 | - 2ad37da: Bump kbar to 0.1.0-beta.44
88 | - bc8f351: fix remark-img-to-jsx and only replace image node
89 |
90 | ## 0.1.5
91 |
92 | ### Patch Changes
93 |
94 | - f03903d: replace image-size with probe-image-size to support avif and other image formats
95 |
96 | ## 0.1.4
97 |
98 | ### Patch Changes
99 |
100 | - e9bcff8: Added icon support to KBar
101 |
102 | ## 0.1.3
103 |
104 | ### Patch Changes
105 |
106 | - 53a7617: Corrected cors error in disqus
107 |
108 | ## 0.1.2
109 |
110 | ### Patch Changes
111 |
112 | - a19ed9f: upgrade kbar version
113 | - 79ed1c1: Expose onSearchDocumentsLoad for kbar and allow searchDocumentsPath to be false
114 | - 9ff4d94: render shortcut key in kbar item
115 |
116 | ## 0.1.2-beta.1
117 |
118 | ### Patch Changes
119 |
120 | - 9ff4d94: render shortcut key in kbar item
121 |
122 | ## 0.1.2-beta.0
123 |
124 | ### Patch Changes
125 |
126 | - a19ed9f: upgrade kbar version
127 | - 79ed1c1: Expose onSearchDocumentsLoad for kbar and allow searchDocumentsPath to be false
128 |
129 | ## 0.1.1
130 |
131 | ### Patch Changes
132 |
133 | - 306917e: Improve kbar styling
134 | Simplify kbar component
135 | Add new KBarButton and AlgoliaButton wrapper which toggles their respective modals on click event
136 | - dbe5de2: add use client directive
137 |
138 | ## 0.1.1-beta.1
139 |
140 | ### Patch Changes
141 |
142 | - dbe5de2: add use client directive
143 |
144 | ## 0.1.1-beta.0
145 |
146 | ### Patch Changes
147 |
148 | - 306917e: Improve kbar styling
149 | Simplify kbar component
150 | Add new KBarButton and AlgoliaButton wrapper which toggles their respective modals on click event
151 |
152 | ## 0.1.0
153 |
154 | ### Minor Changes
155 |
156 | - 1faee5c: bump package dependencies
157 |
158 | ### Patch Changes
159 |
160 | - 245b6d3: make algolia search input focus style more specific
161 | - e669506: Split all files except ui components so that they are default exported
162 | - 23d9149: Remove dynamic load of comments
163 | - 72932a3: Use default export for components
164 |
165 | Named export breaks react server components. See https://github.com/vercel/next.js/issues/51593
166 |
167 | - 21080de: Fix newsletter route handlers
168 | - 44a6f19: Use official giscus component which fixes styling issues
169 | - 3533d4f: Fix algolia styles
170 | - 75533d5: fix bleed full mode
171 | - c41211e: Improve typing of contentlayer utility functions and filter posts only in prod
172 | - 1e82c4e: update google analytics to follow ga4 recommendations
173 | - 7f2539a: Remove functions superseded in next 13 or new set up
174 | - e0aa18c: Expose option to configure host for analytics scripts
175 | - 562605b: Fix algolia css specificity issues with tailwind
176 | - 7d2f6b3: update algolia component
177 | - 86b57e7: Export example css for algolia
178 | - 33d15e3: Remove next/dynamic from search component
179 | Remove redundant context components
180 | load kbar data in advance and refactor code
181 | - 7f71035: Fix newsletter typo
182 | - 459c5d8: Update to use next 13 app dir navigation router
183 | - 3e31a82: Add support for newsletter route handler
184 | - 9f87280: Fix kbar fetch to load relative to base url and add back starting actions
185 | - 1fe37b1: Add back code splitting and add use client at the chunk level
186 | - 4ba59d6: fix allCoreContent logic
187 |
188 | ## 0.1.0-beta.13
189 |
190 | ### Patch Changes
191 |
192 | - 245b6d3: make algolia search input focus style more specific
193 | - 4ba59d6: fix allCoreContent logic
194 |
195 | ## 0.1.0-beta.12
196 |
197 | ### Patch Changes
198 |
199 | - 75533d5: fix bleed full mode
200 | - c41211e: Improve typing of contentlayer utility functions and filter posts only in prod
201 |
202 | ## 0.1.0-beta.11
203 |
204 | ### Patch Changes
205 |
206 | - e0aa18c: Expose option to configure host for analytics scripts
207 | - 562605b: Fix algolia css specificity issues with tailwind
208 |
209 | ## 0.1.0-beta.10
210 |
211 | ### Patch Changes
212 |
213 | - 9f87280: Fix kbar fetch to load relative to base url and add back starting actions
214 |
215 | ## 0.1.0-beta.9
216 |
217 | ### Patch Changes
218 |
219 | - e669506: Split all files except ui components so that they are default exported
220 |
221 | ## 0.1.0-beta.8
222 |
223 | ### Patch Changes
224 |
225 | - 1fe37b1: Add back code splitting and add use client at the chunk level
226 |
227 | ## 0.1.0-beta.7
228 |
229 | ### Patch Changes
230 |
231 | - 33d15e3: Remove next/dynamic from search component
232 | Remove redundant context components
233 | load kbar data in advance and refactor code
234 | - 7f71035: Fix newsletter typo
235 |
236 | ## 0.1.0-beta.6
237 |
238 | ### Patch Changes
239 |
240 | - 21080de: Fix newsletter route handlers
241 |
242 | ## 0.1.0-beta.5
243 |
244 | ### Patch Changes
245 |
246 | - 3e31a82: Add support for newsletter route handler
247 |
248 | ## 0.1.0-beta.4
249 |
250 | ### Patch Changes
251 |
252 | - 44a6f19: Use official giscus component which fixes styling issues
253 | - 3533d4f: Fix algolia styles
254 |
255 | ## 0.1.0-beta.3
256 |
257 | ### Patch Changes
258 |
259 | - 23d9149: Remove dynamic load of comments
260 | - 86b57e7: Export example css for algolia
261 |
262 | ## 0.1.0-beta.2
263 |
264 | ### Patch Changes
265 |
266 | - 7d2f6b3: update algolia component
267 |
268 | ## 0.1.0-beta.1
269 |
270 | ### Minor Changes
271 |
272 | - 72932a3: Use default export for components
273 |
274 | Named export breaks react server components. See https://github.com/vercel/next.js/issues/51593
275 |
276 | - 7f2539a: Remove functions superseded in next 13 or new set up
277 | - 459c5d8: Update to use next 13 app dir navigation router
278 | - bump package dependencies
279 |
280 | ## 0.0.11-beta.0
281 |
282 | ### Patch Changes
283 |
284 | - 1e82c4e: update google analytics to follow ga4 recommendations
285 |
286 | ## 0.0.10
287 |
288 | ### Patch Changes
289 |
290 | - 15d4529: fix sitemap regex
291 | - cca397f: add use client directive
292 |
293 | ## 0.0.9
294 |
295 | ### Patch Changes
296 |
297 | - a82f661: update to next 13
298 | - e174c43: improve accessibility
299 |
300 | ## 0.0.9-beta.0
301 |
302 | ### Patch Changes
303 |
304 | - a82f661: update to next 13
305 | - e174c43: improve accessibility
306 |
307 | ## 0.0.8
308 |
309 | ### Patch Changes
310 |
311 | - 37f2f43: add mailchimp as dependency
312 |
313 | ## 0.0.7
314 |
315 | ### Patch Changes
316 |
317 | - 479ed29: release new version
318 |
319 | ## 0.0.6
320 |
321 | ### Patch Changes
322 |
323 | - 85377ed: module resolution
324 | - 1d9d815: update build config
325 | - 280d784: try publishing locally
326 | - 47009e9: revert kbar changes
327 | - 5f95b6e: modify package.json exports
328 | - a6ea8ac: upgrade to react 18
329 | - c7d833b: remove dynamic load for comments
330 | - 64ea789: fix react hoisting issues
331 |
332 | ## 0.0.6-beta.5
333 |
334 | ### Patch Changes
335 |
336 | - c7d833b: remove dynamic load for comments
337 |
338 | ## 0.0.6-beta.4
339 |
340 | ### Patch Changes
341 |
342 | - 280d784: try publishing locally
343 | - a6ea8ac: upgrade to react 18
344 |
345 | ## 0.0.6-beta.2
346 |
347 | ### Patch Changes
348 |
349 | - 64ea789: fix react hoisting issues
350 |
351 | ## 0.0.6-beta.1
352 |
353 | ### Patch Changes
354 |
355 | - 85377ed: module resolution
356 |
357 | ## 0.0.6
358 |
359 | ### Patch Changes
360 |
361 | - 6fa9098: export modules from root and use only named exports
362 | - 724b402: update dependencies
363 |
364 | ## 0.0.5
365 |
366 | ### Patch Changes
367 |
368 | - d8cada5: Fix sitemap glob and replace tsx file extension
369 |
370 | ## 0.0.4
371 |
372 | ### Patch Changes
373 |
374 | - b7a89c6: add posthog analytics integration
375 |
376 | ## 0.0.3
377 |
378 | ### Patch Changes
379 |
380 | - b59e129: add search component with algolia integration
381 | - 6b5d0a4: add kbar option to search component
382 | - a6215f8: fix issues with comments dynamic import and add back id tag
383 | - 9b9f193: update tsup to v6.1.2
384 |
385 | ## 0.0.2
386 |
387 | ### Patch Changes
388 |
389 | - 6f5b8c3: - Add contentlayer config to paths
390 | - Add utility function to detect contentlayer parts in installer
391 | - Add contentlayer document type transform helper codemod
392 | - Pass cli args to transformers
393 | - Add contentlayer transform codemod to add-blog recipe
394 | - Sync starter blog with v1.5.6 in secondary repo
395 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pliny
2 |
3 | Pliny provides out of the box components to enhance your static site:
4 |
5 | - Analytics
6 | - Google Analytics
7 | - Plausible Analytics
8 | - Simple Analytics
9 | - Umami Analytics
10 | - Posthog
11 | - Microsoft Clarity
12 | - Comments
13 | - Disqus
14 | - Giscus
15 | - Utterances
16 | - Newsletter (uses Next 13 API Routes)
17 | - Buttondown
18 | - Convertkit
19 | - Email Octopus
20 | - Klaviyo
21 | - Mailchimp
22 | - Beehiiv
23 | - Command palette search with tailwind style sheet
24 | - Algolia
25 | - Kbar (local search)
26 | - UI utility components
27 | - Bleed
28 | - Newsletter / Blog Newsletter
29 | - Pre / Code block
30 | - Table of Contents
31 |
32 | as well as a bunch of MDX and contentlayer utility functions which I use to build [Tailwind Nextjs Starter Blog][tnsb] and my own sites.
33 |
34 | It is based on [Next.js][nextjs], [Tailwind CSS][tailwindcss] and [Contentlayer][contentlayer]. For an example of how all the components can be used together, check out the [Tailwind Nextjs Starter Blog][tnsb].
35 |
36 | Note: The previous cli and starter template have been deprecated. Please use the new components directly in your favourite Next 13 websites.
37 |
38 | Note 2: The components are intended to be use within Next 13 app directory setup with [Contentlayer][contentlayer]. You might still be able to use the components in older websites but there's no official support for it, especially since many components are now using `next/navigation` instead of `next/router`.
39 |
40 | This project is still in beta. Please report any issues or feedbacks.
41 |
42 | ## Installation
43 |
44 | ```bash
45 | npm i pliny
46 | ```
47 |
48 | As many of the components are styled with tailwindcss, you will need to include the path to the library within the `content` section of your tailwind config file:
49 |
50 | ```js
51 | module.exports = {
52 | content: [
53 | './node_modules/pliny/**/*.js',
54 | './pages/**/*.{html,js}',
55 | './components/**/*.{html,js}',
56 | ],
57 | // ...
58 | }
59 | ```
60 |
61 | ## Components
62 |
63 | ### Analytics
64 |
65 | The `Analytics` component provides an easy interface to switch between different analytics providers. It might not be as feature rich as the official analytics providers but it should be sufficient for simple use cases.
66 |
67 | All components default to the hosted service, but can be configured to use a self-hosted or proxied version of the script by providing the `src` / `apiHost` props to the respective analytics component.
68 |
69 | Note: As an external script will be loaded, do ensure that `script-src` in the content security policy of `next.config.js` has been configured to whitelist the domain.
70 |
71 | ```tsx
72 | import { Analytics, AnalyticsConfig } from 'pliny/analytics'
73 |
74 | const analytics: AnalyticsConfig = {
75 | // If you want to use an analytics provider you have to add it to the
76 | // content security policy in the `next.config.js` file.
77 | // supports Plausible, Simple Analytics, Umami, Posthog or Google Analytics.
78 | plausibleAnalytics: {
79 | plausibleDataDomain: '', // e.g. tailwind-nextjs-starter-blog.vercel.app
80 | },
81 | simpleAnalytics: {},
82 | umamiAnalytics: {
83 | umamiWebsiteId: '', // e.g. 123e4567-e89b-12d3-a456-426614174000
84 | },
85 | posthogAnalytics: {
86 | posthogProjectApiKey: '', // e.g. 123e4567-e89b-12d3-a456-426614174000
87 | },
88 | googleAnalytics: {
89 | googleAnalyticsId: '', // e.g. G-XXXXXXX
90 | },
91 | clarityAnalytics: {
92 | ClarityWebsiteId: '', // e.g. abcdefjhij
93 | },
94 | }
95 |
96 | export default function Layout() {
97 | return (
98 | ...
99 |
100 | )
101 | }
102 | ```
103 |
104 | You can also use the individual analytics components directly.
105 |
106 | #### Google Analytics
107 |
108 | ```tsx
109 | import { GA } from 'pliny/analytics/GoogleAnalytics'
110 |
111 | const googleAnalyticsId = '' // e.g. UA-000000-2 or G-XXXXXXX
112 |
113 | export default function Layout() {
114 | return (
115 | ...
116 |
117 | )
118 | }
119 | ```
120 |
121 | #### Microsoft Clarity Analytics
122 |
123 | ```tsx
124 | import { GA } from 'pliny/analytics/MicrosoftClarity'
125 |
126 | const ClarityWebsiteId = '' // e.g. abcdefjhij
127 |
128 | export default function Layout() {
129 | return (
130 | ...
131 |
132 | )
133 | }
134 | ```
135 |
136 | #### Plausible Analytics
137 |
138 | ```tsx
139 | import { Plausible } from 'pliny/analytics/Plausible'
140 |
141 | const plausibleDataDomain = '' // e.g. tailwind-nextjs-starter-blog.vercel.app
142 |
143 | export default function Layout() {
144 | return (
145 | ...
146 |
147 | )
148 | }
149 | ```
150 |
151 | #### Simple Analytics
152 |
153 | ```tsx
154 | import { SimpleAnalytics } from 'pliny/analytics/SimpleAnalytics'
155 |
156 | export default function Layout() {
157 | return (
158 | ...
159 |
160 | )
161 | }
162 | ```
163 |
164 | #### Umami Analytics
165 |
166 | ```tsx
167 | import { Umami } from 'pliny/analytics/Umami'
168 |
169 | const umamiWebsiteId = '' // e.g. 123e4567-e89b-12d3-a456-426614174000
170 |
171 | export default function Layout() {
172 | return (
173 | ...
174 |
175 | )
176 | }
177 | ```
178 |
179 | #### Posthog
180 |
181 | ```tsx
182 | import { Posthog } from 'pliny/analytics/Posthog'
183 |
184 | const posthogProjectApiKey: '', // e.g. AhnJK8392ndPOav87as450xd
185 |
186 | export default function Layout() {
187 | return (
188 | ...
189 |
190 | )
191 | }
192 | ```
193 |
194 | ### Comments
195 |
196 | The `Comments` component provides an easy interface to switch between different comments providers.
197 |
198 | ```tsx
199 | import { Comments, CommentsConfig } from 'pliny/comments'
200 | import siteMetadata from '@/data/siteMetadata'
201 |
202 | export default function BlogComments({ slug }: { slug: string }) {
203 | return
204 | }
205 | ```
206 |
207 | You can also use the individual comments components directly.
208 |
209 | #### Giscus
210 |
211 | ```tsx
212 | import { Giscus, GiscusProps } from 'pliny/comments/Giscus'
213 |
214 | export default function BlogComments(props: GiscusProps) {
215 | return
216 | }
217 | ```
218 |
219 | #### Disqus
220 |
221 | ```tsx
222 | import { Disqus, DisqusProps } from 'pliny/comments/Disqus'
223 |
224 | export default function BlogComments(props: DisqusProps) {
225 | return
226 | }
227 | ```
228 |
229 | #### Utterances
230 |
231 | ```tsx
232 | import { Utterances, UtterancesProps } from 'pliny/comments/Utterances'
233 |
234 | export default function BlogComments(props: UtterancesProps) {
235 | return
236 | }
237 | ```
238 |
239 | ### Newsletter
240 |
241 | The `Newsletter` component provides a Next 13 API route to integrate a newsletter subscription API with various providers. E.g. in `app/api/newsletter/route.ts`
242 |
243 | ```tsx
244 | import { NewsletterAPI } from 'pliny/newsletter'
245 | import siteMetadata from '@/data/siteMetadata'
246 |
247 | const handler = NewsletterAPI({
248 | provider: '', // Use one of mailchimp, buttondown, convertkit, klaviyo emailOctopus
249 | })
250 |
251 | export { handler as GET, handler as POST }
252 | ```
253 |
254 | You can then send a `POST` request to the API route with a body with the email - `{ email: 'new_email@gmail.com' }`. See the `NewsletterForm` component in `pliny/ui/NewsletterForm` for an example.
255 |
256 | ### Search
257 |
258 | The `Search` component provides an easy interface to switch between different search providers. If you are using algolia, you will need to import the css file as well - `import 'pliny/search/algolia.css'`.
259 |
260 | ```tsx
261 | import { SearchProvider, SearchConfig } from 'pliny/search'
262 |
263 | export default function Layout() {
264 | return ...
265 | }
266 | ```
267 |
268 | You can also use the individual search components directly.
269 |
270 | #### Kbar
271 |
272 | You can pass in an optional `defaultActions` to `kbarConfig` to customize the default actions. See [Kbar documentation](https://kbar.vercel.app/docs/concepts/actions) for more details.
273 |
274 | ```tsx
275 | import { KBarSearchProvider } from 'pliny/search/KBar'
276 |
277 | export default function Layout() {
278 | return ...
279 | }
280 | ```
281 |
282 | Use `KBarButton` to add a button which toggles the command palette on click event.
283 |
284 | ```tsx
285 | import { KBarButton } from 'pliny/search/KBarButton'
286 |
287 | export default function SearchButton() {
288 | return (
289 |
290 |
304 |
305 | )
306 | }
307 | ```
308 |
309 | #### Algolia
310 |
311 | ```tsx
312 | import 'pliny/search/algolia.css'
313 | import { AlgoliaSearchProvider } from 'pliny/search/Algolia'
314 |
315 | export default function Layout() {
316 | return (
317 |
324 | ...
325 |
326 | )
327 | }
328 | ```
329 |
330 | Use `AlgoliaButton` to add a button which toggles the command palette on click event.
331 |
332 | ```tsx
333 | import { AlgoliaButton } from 'pliny/search/AlgoliaButton'
334 |
335 | export default function SearchButton() {
336 | return (
337 |
338 |
352 |
353 | )
354 | }
355 | ```
356 |
357 | ### MDX plugins
358 |
359 | Add the plugins to `remarkPlugins` in contentlayer or other MDX processors.
360 |
361 | #### Remark Extract Frontmatter
362 |
363 | Extracts frontmatter from markdown file and adds it to the file's data object. Used to pass frontmatter fields to subsequent remark / rehype plugins.
364 |
365 | #### Remark code title
366 |
367 | Parses title from code block and inserts it as a sibling title node.
368 |
369 | #### Remark Img To Jsx
370 |
371 | Converts markdown image nodes to next/image jsx.
372 |
373 | #### Remark TOC Headings
374 |
375 | Extracts TOC headings from markdown file and adds it to the file's data object. Alternatively, it also exports a `extractTocHeadings` function which can be used within contentlayer to create a `computedField` with the TOC headings.
376 |
377 | ### MDX components
378 |
379 | While these can be used in any React code, they can also be passed down as MDXComponents and used within MDX files.
380 |
381 | #### Bleed
382 |
383 | Useful component to break out of a constrained-width layout and fill the entire width.
384 |
385 | #### Pre / Code block
386 |
387 | Simple code block component with copy to clipboard button.
388 |
389 | #### TOCInline
390 |
391 | Table of contents component which can be used within a markdown file. `asDisclosure` will wrap the TOC in a `details` element with a `summary` element. `collapse` will collapse the TOC when `AsDisclosure` is true. Modify the list style by passing in a `ulClassName` and `liClassName` prop. For example, if you are using Tailwind css and want to revert to the default HTML list style set `ulClassName="[&_ul]:list-[revert]"` and you want to change styles of your list items `liClassName="underline decoration-sky-500"` .
392 |
393 | #### NewsletterForm / BlogNewsletterForm
394 |
395 | Newsletter form component to add a subscriber to your mailing list.
396 |
397 | [nextjs]: https://nextjs.org/
398 | [tailwindcss]: https://tailwindcss.com/
399 | [contentlayer]: https://github.com/contentlayerdev/contentlayer
400 | [rehype-prism-plus]: https://github.com/timlrx/rehype-prism-plus
401 | [katex]: https://katex.org/
402 | [rehype-citation]: https://github.com/timlrx/rehype-citation
403 | [tnsb]: https://github.com/timlrx/tailwind-nextjs-starter-blog
404 |
--------------------------------------------------------------------------------
/packages/pliny/public/algolia.css:
--------------------------------------------------------------------------------
1 | /* Original css taken from @docsearch/css */
2 | :root {
3 | --docsearch-primary-color: #5468ff;
4 | --docsearch-text-color: #1c1e21;
5 | --docsearch-spacing: 12px;
6 | --docsearch-icon-stroke-width: 1.4;
7 | --docsearch-highlight-color: var(--docsearch-primary-color);
8 | --docsearch-muted-color: #969faf;
9 | --docsearch-container-background: rgba(101, 108, 133, 0.8);
10 | --docsearch-logo-color: #5468ff;
11 | --docsearch-modal-width: 560px;
12 | --docsearch-modal-height: 600px;
13 | --docsearch-modal-background: #f5f6f7;
14 | --docsearch-modal-shadow: inset 1px 1px 0 0 hsla(0, 0%, 100%, 0.5), 0 3px 8px 0 #555a64;
15 | --docsearch-searchbox-height: 56px;
16 | --docsearch-searchbox-background: #ebedf0;
17 | --docsearch-searchbox-focus-background: #fff;
18 | --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);
19 | --docsearch-hit-height: 56px;
20 | --docsearch-hit-color: #444950;
21 | --docsearch-hit-active-color: #fff;
22 | --docsearch-hit-background: #fff;
23 | --docsearch-hit-shadow: 0 1px 3px 0 #d4d9e1;
24 | --docsearch-key-gradient: linear-gradient(-225deg, #d5dbe4, #f8f8f8);
25 | --docsearch-key-shadow: inset 0 -2px 0 0 #cdcde6, inset 0 0 1px 1px #fff,
26 | 0 1px 2px 1px rgba(30, 35, 90, 0.4);
27 | --docsearch-footer-height: 44px;
28 | --docsearch-footer-background: #fff;
29 | --docsearch-footer-shadow: 0 -1px 0 0 #e0e3e8, 0 -3px 6px 0 rgba(69, 98, 155, 0.12);
30 | }
31 |
32 | html[data-theme='dark'] {
33 | --docsearch-text-color: #f5f6f7;
34 | --docsearch-container-background: rgba(9, 10, 17, 0.8);
35 | --docsearch-modal-background: #15172a;
36 | --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
37 | --docsearch-searchbox-background: #090a11;
38 | --docsearch-searchbox-focus-background: #000;
39 | --docsearch-hit-color: #bec3c9;
40 | --docsearch-hit-shadow: none;
41 | --docsearch-hit-background: #090a11;
42 | --docsearch-key-gradient: linear-gradient(-26.5deg, #565872, #31355b);
43 | --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
44 | 0 2px 2px 0 rgba(3, 4, 9, 0.3);
45 | --docsearch-footer-background: #1e2136;
46 | --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5), 0 -4px 8px 0 rgba(0, 0, 0, 0.2);
47 | --docsearch-logo-color: #fff;
48 | --docsearch-muted-color: #7f8497;
49 | }
50 |
51 | .DocSearch-Button {
52 | align-items: center;
53 | background: var(--docsearch-searchbox-background);
54 | border: 0;
55 | border-radius: 40px;
56 | color: var(--docsearch-muted-color);
57 | cursor: pointer;
58 | display: flex;
59 | font-weight: 500;
60 | height: 36px;
61 | justify-content: space-between;
62 | margin: 0 0 0 16px;
63 | padding: 0 8px;
64 | user-select: none;
65 | }
66 |
67 | .DocSearch-Button:active,
68 | .DocSearch-Button:focus,
69 | .DocSearch-Button:hover {
70 | background: var(--docsearch-searchbox-focus-background);
71 | box-shadow: var(--docsearch-searchbox-shadow);
72 | color: var(--docsearch-text-color);
73 | outline: none;
74 | }
75 |
76 | .DocSearch-Button-Container {
77 | align-items: center;
78 | display: flex;
79 | }
80 |
81 | .DocSearch-Search-Icon {
82 | stroke-width: 1.6;
83 | }
84 |
85 | .DocSearch-Button .DocSearch-Search-Icon {
86 | color: var(--docsearch-text-color);
87 | }
88 |
89 | .DocSearch-Button-Placeholder {
90 | font-size: 1rem;
91 | padding: 0 12px 0 6px;
92 | }
93 |
94 | .DocSearch-Button-Keys {
95 | display: flex;
96 | min-width: calc(40px + 0.8em);
97 | }
98 |
99 | .DocSearch-Button-Key {
100 | align-items: center;
101 | background: var(--docsearch-key-gradient);
102 | border-radius: 3px;
103 | box-shadow: var(--docsearch-key-shadow);
104 | color: var(--docsearch-muted-color);
105 | display: flex;
106 | height: 18px;
107 | justify-content: center;
108 | margin-right: 0.4em;
109 | position: relative;
110 | padding: 0 0 2px;
111 | border: 0;
112 | top: -1px;
113 | width: 20px;
114 | }
115 |
116 | @media (max-width: 768px) {
117 | .DocSearch-Button-Keys,
118 | .DocSearch-Button-Placeholder {
119 | display: none;
120 | }
121 | }
122 |
123 | .DocSearch--active {
124 | overflow: hidden !important;
125 | }
126 |
127 | .DocSearch-Container,
128 | .DocSearch-Container * {
129 | box-sizing: border-box;
130 | }
131 |
132 | .DocSearch-Container {
133 | background-color: var(--docsearch-container-background);
134 | height: 100vh;
135 | left: 0;
136 | position: fixed;
137 | top: 0;
138 | width: 100vw;
139 | z-index: 200;
140 | }
141 |
142 | .DocSearch-Container a {
143 | text-decoration: none;
144 | }
145 |
146 | .DocSearch-Link {
147 | appearance: none;
148 | background: none;
149 | border: 0;
150 | color: var(--docsearch-highlight-color);
151 | cursor: pointer;
152 | font: inherit;
153 | margin: 0;
154 | padding: 0;
155 | }
156 |
157 | .DocSearch-Modal {
158 | background: var(--docsearch-modal-background);
159 | border-radius: 6px;
160 | box-shadow: var(--docsearch-modal-shadow);
161 | flex-direction: column;
162 | margin: 60px auto auto;
163 | max-width: var(--docsearch-modal-width);
164 | position: relative;
165 | }
166 |
167 | .DocSearch-SearchBar {
168 | display: flex;
169 | padding: var(--docsearch-spacing) var(--docsearch-spacing) 0;
170 | }
171 |
172 | .DocSearch-Form {
173 | align-items: center;
174 | background: var(--docsearch-searchbox-focus-background);
175 | border-radius: 4px;
176 | box-shadow: var(--docsearch-searchbox-shadow);
177 | display: flex;
178 | height: var(--docsearch-searchbox-height);
179 | margin: 0;
180 | padding: 0 var(--docsearch-spacing);
181 | position: relative;
182 | width: 100%;
183 | }
184 |
185 | .DocSearch-Input {
186 | appearance: none;
187 | background: transparent;
188 | border: 0;
189 | color: var(--docsearch-text-color);
190 | flex: 1;
191 | font: inherit;
192 | font-size: 1.2em;
193 | height: 100%;
194 | outline: none;
195 | padding: 0 0 0 8px;
196 | width: 80%;
197 | }
198 |
199 | .DocSearch-Input::placeholder {
200 | color: var(--docsearch-muted-color);
201 | opacity: 1;
202 | }
203 |
204 | .DocSearch-Input::-webkit-search-cancel-button,
205 | .DocSearch-Input::-webkit-search-decoration,
206 | .DocSearch-Input::-webkit-search-results-button,
207 | .DocSearch-Input::-webkit-search-results-decoration {
208 | display: none;
209 | }
210 |
211 | .DocSearch-LoadingIndicator,
212 | .DocSearch-MagnifierLabel,
213 | .DocSearch-Reset {
214 | margin: 0;
215 | padding: 0;
216 | }
217 |
218 | .DocSearch-MagnifierLabel,
219 | .DocSearch-Reset {
220 | align-items: center;
221 | color: var(--docsearch-highlight-color);
222 | display: flex;
223 | justify-content: center;
224 | }
225 |
226 | .DocSearch-Container--Stalled .DocSearch-MagnifierLabel,
227 | .DocSearch-LoadingIndicator {
228 | display: none;
229 | }
230 |
231 | .DocSearch-Container--Stalled .DocSearch-LoadingIndicator {
232 | align-items: center;
233 | color: var(--docsearch-highlight-color);
234 | display: flex;
235 | justify-content: center;
236 | }
237 |
238 | @media screen and (prefers-reduced-motion: reduce) {
239 | .DocSearch-Reset {
240 | animation: none;
241 | appearance: none;
242 | background: none;
243 | border: 0;
244 | border-radius: 50%;
245 | color: var(--docsearch-icon-color);
246 | cursor: pointer;
247 | right: 0;
248 | stroke-width: var(--docsearch-icon-stroke-width);
249 | }
250 | }
251 |
252 | .DocSearch-Reset {
253 | animation: fade-in 0.1s ease-in forwards;
254 | appearance: none;
255 | background: none;
256 | border: 0;
257 | border-radius: 50%;
258 | color: var(--docsearch-icon-color);
259 | cursor: pointer;
260 | padding: 2px;
261 | right: 0;
262 | stroke-width: var(--docsearch-icon-stroke-width);
263 | }
264 |
265 | .DocSearch-Reset[hidden] {
266 | display: none;
267 | }
268 |
269 | .DocSearch-Reset:hover {
270 | color: var(--docsearch-highlight-color);
271 | }
272 |
273 | .DocSearch-LoadingIndicator svg,
274 | .DocSearch-MagnifierLabel svg {
275 | height: 24px;
276 | width: 24px;
277 | }
278 |
279 | .DocSearch-Cancel {
280 | display: none;
281 | }
282 |
283 | .DocSearch-Dropdown {
284 | max-height: calc(
285 | var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) -
286 | var(--docsearch-footer-height)
287 | );
288 | min-height: var(--docsearch-spacing);
289 | overflow-y: auto;
290 | overflow-y: overlay;
291 | padding: 0 var(--docsearch-spacing);
292 | scrollbar-color: var(--docsearch-muted-color) var(--docsearch-modal-background);
293 | scrollbar-width: thin;
294 | }
295 |
296 | .DocSearch-Dropdown::-webkit-scrollbar {
297 | width: 12px;
298 | }
299 |
300 | .DocSearch-Dropdown::-webkit-scrollbar-track {
301 | background: transparent;
302 | }
303 |
304 | .DocSearch-Dropdown::-webkit-scrollbar-thumb {
305 | background-color: var(--docsearch-muted-color);
306 | border: 3px solid var(--docsearch-modal-background);
307 | border-radius: 20px;
308 | }
309 |
310 | .DocSearch-Dropdown ul {
311 | list-style: none;
312 | margin: 0;
313 | padding: 0;
314 | }
315 |
316 | .DocSearch-Label {
317 | font-size: 0.75em;
318 | line-height: 1.6em;
319 | }
320 |
321 | .DocSearch-Help,
322 | .DocSearch-Label {
323 | color: var(--docsearch-muted-color);
324 | }
325 |
326 | .DocSearch-Help {
327 | font-size: 0.9em;
328 | margin: 0;
329 | user-select: none;
330 | }
331 |
332 | .DocSearch-Title {
333 | font-size: 1.2em;
334 | }
335 |
336 | .DocSearch-Logo a {
337 | display: flex;
338 | }
339 |
340 | .DocSearch-Logo svg {
341 | color: var(--docsearch-logo-color);
342 | margin-left: 8px;
343 | }
344 |
345 | .DocSearch-Hits:last-of-type {
346 | margin-bottom: 24px;
347 | }
348 |
349 | .DocSearch-Hits mark {
350 | background: none;
351 | color: var(--docsearch-highlight-color);
352 | }
353 |
354 | .DocSearch-HitsFooter {
355 | color: var(--docsearch-muted-color);
356 | display: flex;
357 | font-size: 0.85em;
358 | justify-content: center;
359 | margin-bottom: var(--docsearch-spacing);
360 | padding: var(--docsearch-spacing);
361 | }
362 |
363 | .DocSearch-HitsFooter a {
364 | border-bottom: 1px solid;
365 | color: inherit;
366 | }
367 |
368 | .DocSearch-Hit {
369 | border-radius: 4px;
370 | display: flex;
371 | padding-bottom: 4px;
372 | position: relative;
373 | }
374 |
375 | @media screen and (prefers-reduced-motion: reduce) {
376 | .DocSearch-Hit--deleting {
377 | transition: none;
378 | }
379 | }
380 |
381 | .DocSearch-Hit--deleting {
382 | opacity: 0;
383 | transition: all 0.25s linear;
384 | }
385 |
386 | @media screen and (prefers-reduced-motion: reduce) {
387 | .DocSearch-Hit--favoriting {
388 | transition: none;
389 | }
390 | }
391 |
392 | .DocSearch-Hit--favoriting {
393 | transform: scale(0);
394 | transform-origin: top center;
395 | transition: all 0.25s linear;
396 | transition-delay: 0.25s;
397 | }
398 |
399 | .DocSearch-Hit a {
400 | background: var(--docsearch-hit-background);
401 | border-radius: 4px;
402 | box-shadow: var(--docsearch-hit-shadow);
403 | display: block;
404 | padding-left: var(--docsearch-spacing);
405 | width: 100%;
406 | }
407 |
408 | .DocSearch-Hit-source {
409 | background: var(--docsearch-modal-background);
410 | color: var(--docsearch-highlight-color);
411 | font-size: 0.85em;
412 | font-weight: 600;
413 | line-height: 32px;
414 | margin: 0 -4px;
415 | padding: 8px 4px 0;
416 | position: sticky;
417 | top: 0;
418 | z-index: 10;
419 | }
420 |
421 | .DocSearch-Hit-Tree {
422 | color: var(--docsearch-muted-color);
423 | height: var(--docsearch-hit-height);
424 | opacity: 0.5;
425 | stroke-width: var(--docsearch-icon-stroke-width);
426 | width: 24px;
427 | }
428 |
429 | .DocSearch-Hit[aria-selected='true'] a {
430 | background-color: var(--docsearch-highlight-color);
431 | }
432 |
433 | .DocSearch-Hit[aria-selected='true'] mark {
434 | text-decoration: underline;
435 | }
436 |
437 | .DocSearch-Hit-Container {
438 | align-items: center;
439 | color: var(--docsearch-hit-color);
440 | display: flex;
441 | flex-direction: row;
442 | height: var(--docsearch-hit-height);
443 | padding: 0 var(--docsearch-spacing) 0 0;
444 | }
445 |
446 | .DocSearch-Hit-icon {
447 | height: 20px;
448 | width: 20px;
449 | }
450 |
451 | .DocSearch-Hit-action,
452 | .DocSearch-Hit-icon {
453 | color: var(--docsearch-muted-color);
454 | stroke-width: var(--docsearch-icon-stroke-width);
455 | }
456 |
457 | .DocSearch-Hit-action {
458 | align-items: center;
459 | display: flex;
460 | height: 22px;
461 | width: 22px;
462 | }
463 |
464 | .DocSearch-Hit-action svg {
465 | display: block;
466 | height: 18px;
467 | width: 18px;
468 | }
469 |
470 | .DocSearch-Hit-action + .DocSearch-Hit-action {
471 | margin-left: 6px;
472 | }
473 |
474 | .DocSearch-Hit-action-button {
475 | appearance: none;
476 | background: none;
477 | border: 0;
478 | border-radius: 50%;
479 | color: inherit;
480 | cursor: pointer;
481 | padding: 2px;
482 | }
483 |
484 | svg.DocSearch-Hit-Select-Icon {
485 | display: none;
486 | }
487 |
488 | .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-Select-Icon {
489 | display: block;
490 | }
491 |
492 | .DocSearch-Hit-action-button:focus,
493 | .DocSearch-Hit-action-button:hover {
494 | background: rgba(0, 0, 0, 0.2);
495 | transition: background-color 0.1s ease-in;
496 | }
497 |
498 | @media screen and (prefers-reduced-motion: reduce) {
499 | .DocSearch-Hit-action-button:focus,
500 | .DocSearch-Hit-action-button:hover {
501 | transition: none;
502 | }
503 | }
504 |
505 | .DocSearch-Hit-action-button:focus path,
506 | .DocSearch-Hit-action-button:hover path {
507 | fill: #fff;
508 | }
509 |
510 | .DocSearch-Hit-content-wrapper {
511 | display: flex;
512 | flex: 1 1 auto;
513 | flex-direction: column;
514 | font-weight: 500;
515 | justify-content: center;
516 | line-height: 1.2em;
517 | margin: 0 8px;
518 | overflow-x: hidden;
519 | position: relative;
520 | text-overflow: ellipsis;
521 | white-space: nowrap;
522 | width: 80%;
523 | }
524 |
525 | .DocSearch-Hit-title {
526 | font-size: 0.9em;
527 | }
528 |
529 | .DocSearch-Hit-path {
530 | color: var(--docsearch-muted-color);
531 | font-size: 0.75em;
532 | }
533 |
534 | .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-action,
535 | .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-icon,
536 | .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-path,
537 | .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-text,
538 | .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-title,
539 | .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-Tree,
540 | .DocSearch-Hit[aria-selected='true'] mark {
541 | color: var(--docsearch-hit-active-color) !important;
542 | }
543 |
544 | @media screen and (prefers-reduced-motion: reduce) {
545 | .DocSearch-Hit-action-button:focus,
546 | .DocSearch-Hit-action-button:hover {
547 | background: rgba(0, 0, 0, 0.2);
548 | transition: none;
549 | }
550 | }
551 |
552 | .DocSearch-ErrorScreen,
553 | .DocSearch-NoResults,
554 | .DocSearch-StartScreen {
555 | font-size: 0.9em;
556 | margin: 0 auto;
557 | padding: 36px 0;
558 | text-align: center;
559 | width: 80%;
560 | }
561 |
562 | .DocSearch-Screen-Icon {
563 | color: var(--docsearch-muted-color);
564 | padding-bottom: 12px;
565 | }
566 |
567 | .DocSearch-NoResults-Prefill-List {
568 | display: inline-block;
569 | padding-bottom: 24px;
570 | text-align: left;
571 | }
572 |
573 | .DocSearch-NoResults-Prefill-List ul {
574 | display: inline-block;
575 | padding: 8px 0 0;
576 | }
577 |
578 | .DocSearch-NoResults-Prefill-List li {
579 | list-style-position: inside;
580 | list-style-type: '» ';
581 | }
582 |
583 | .DocSearch-Prefill {
584 | appearance: none;
585 | background: none;
586 | border: 0;
587 | border-radius: 1em;
588 | color: var(--docsearch-highlight-color);
589 | cursor: pointer;
590 | display: inline-block;
591 | font-size: 1em;
592 | font-weight: 700;
593 | padding: 0;
594 | }
595 |
596 | .DocSearch-Prefill:focus,
597 | .DocSearch-Prefill:hover {
598 | outline: none;
599 | text-decoration: underline;
600 | }
601 |
602 | .DocSearch-Footer {
603 | align-items: center;
604 | background: var(--docsearch-footer-background);
605 | border-radius: 0 0 8px 8px;
606 | box-shadow: var(--docsearch-footer-shadow);
607 | display: flex;
608 | flex-direction: row-reverse;
609 | flex-shrink: 0;
610 | height: var(--docsearch-footer-height);
611 | justify-content: space-between;
612 | padding: 0 var(--docsearch-spacing);
613 | position: relative;
614 | user-select: none;
615 | width: 100%;
616 | z-index: 300;
617 | }
618 |
619 | .DocSearch-Commands {
620 | color: var(--docsearch-muted-color);
621 | display: flex;
622 | list-style: none;
623 | margin: 0;
624 | padding: 0;
625 | }
626 |
627 | .DocSearch-Commands li {
628 | align-items: center;
629 | display: flex;
630 | }
631 |
632 | .DocSearch-Commands li:not(:last-of-type) {
633 | margin-right: 0.8em;
634 | }
635 |
636 | .DocSearch-Commands-Key {
637 | align-items: center;
638 | background: var(--docsearch-key-gradient);
639 | border-radius: 2px;
640 | box-shadow: var(--docsearch-key-shadow);
641 | display: flex;
642 | height: 18px;
643 | justify-content: center;
644 | margin-right: 0.4em;
645 | padding: 0 0 1px;
646 | color: var(--docsearch-muted-color);
647 | border: 0;
648 | width: 20px;
649 | }
650 |
651 | @media (max-width: 768px) {
652 | :root {
653 | --docsearch-spacing: 10px;
654 | --docsearch-footer-height: 40px;
655 | }
656 |
657 | .DocSearch-Dropdown {
658 | height: 100%;
659 | }
660 |
661 | .DocSearch-Container {
662 | height: 100vh;
663 | height: -webkit-fill-available;
664 | height: calc(var(--docsearch-vh, 1vh) * 100);
665 | position: absolute;
666 | }
667 |
668 | .DocSearch-Footer {
669 | border-radius: 0;
670 | bottom: 0;
671 | position: absolute;
672 | }
673 |
674 | .DocSearch-Hit-content-wrapper {
675 | display: flex;
676 | position: relative;
677 | width: 80%;
678 | }
679 |
680 | .DocSearch-Modal {
681 | border-radius: 0;
682 | box-shadow: none;
683 | height: 100vh;
684 | height: -webkit-fill-available;
685 | height: calc(var(--docsearch-vh, 1vh) * 100);
686 | margin: 0;
687 | max-width: 100%;
688 | width: 100%;
689 | }
690 |
691 | .DocSearch-Dropdown {
692 | max-height: calc(
693 | var(--docsearch-vh, 1vh) * 100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) -
694 | var(--docsearch-footer-height)
695 | );
696 | }
697 |
698 | .DocSearch-Cancel {
699 | appearance: none;
700 | background: none;
701 | border: 0;
702 | color: var(--docsearch-highlight-color);
703 | cursor: pointer;
704 | display: inline-block;
705 | flex: none;
706 | font: inherit;
707 | font-size: 1em;
708 | font-weight: 500;
709 | margin-left: var(--docsearch-spacing);
710 | outline: none;
711 | overflow: hidden;
712 | padding: 0;
713 | user-select: none;
714 | white-space: nowrap;
715 | }
716 |
717 | .DocSearch-Commands,
718 | .DocSearch-Hit-Tree {
719 | display: none;
720 | }
721 | }
722 |
723 | @keyframes fade-in {
724 | 0% {
725 | opacity: 0;
726 | }
727 |
728 | to {
729 | opacity: 1;
730 | }
731 | }
732 |
733 | /* Custom overrides to work with a primary tailwind css theme */
734 | .light .DocSearch {
735 | --docsearch-primary-color: theme(colors.primary.600);
736 | --docsearch-highlight-color: theme(colors.primary.600);
737 | --docsearch-searchbox-shadow: inset 0 0 0 2px theme(colors.primary.600);
738 | --docsearch-muted-color: theme(colors.gray.500);
739 | --docsearch-container-background: theme(colors.gray.400 / 80%);
740 | /* Modal */
741 | --docsearch-modal-background: theme(colors.gray.200);
742 | /* Search box */
743 | --docsearch-searchbox-background: theme(colors.gray.100);
744 | --docsearch-searchbox-focus-background: theme(colors.gray.100);
745 | /* Hit */
746 | --docsearch-hit-color: theme(colors.gray.700);
747 | --docsearch-hit-shadow: none;
748 | --docsearch-hit-active-color: theme(colors.gray.800);
749 | --docsearch-hit-background: theme(colors.gray.100);
750 | /* Footer */
751 | --docsearch-footer-background: theme(colors.gray.100);
752 | }
753 |
754 | .dark .DocSearch {
755 | --docsearch-primary-color: theme(colors.primary.600);
756 | --docsearch-highlight-color: theme(colors.primary.600);
757 | --docsearch-searchbox-shadow: inset 0 0 0 2px theme(colors.primary.600);
758 | --docsearch-text-color: theme(colors.gray.300);
759 | --docsearch-muted-color: theme(colors.gray.400);
760 | --docsearch-container-background: theme(colors.gray.900 / 80%);
761 | /* Modal */
762 | --docsearch-modal-background: theme(colors.gray.900);
763 | --docsearch-modal-shadow: inset 1px 1px 0 0 rgb(44, 46, 64), 0 3px 8px 0 rgb(0, 3, 9);
764 | /* Search box */
765 | --docsearch-searchbox-background: theme(colors.gray.800);
766 | --docsearch-searchbox-focus-background: theme(colors.gray.800);
767 | /* Hit */
768 | --docsearch-hit-color: theme(colors.gray.200);
769 | --docsearch-hit-shadow: none;
770 | --docsearch-hit-active-color: theme(colors.gray.100);
771 | --docsearch-hit-background: theme(colors.gray.800);
772 | /* Footer */
773 | --docsearch-footer-background: theme(colors.gray.900);
774 | --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5), 0 -4px 8px 0 rgba(0, 0, 0, 0.2);
775 | --docsearch-key-gradient: linear-gradient(
776 | -26.5deg,
777 | theme(colors.gray.800) 0%,
778 | theme(colors.gray.900) 100%
779 | );
780 | --docsearch-key-shadow: inset 0 -2px 0 0 rgb(40, 45, 85), inset 0 0 1px 1px rgb(81, 87, 125),
781 | 0 2px 2px 0 rgba(3, 4, 9, 0.3);
782 | --docsearch-logo-color: theme(colors.gray.300);
783 | }
784 |
785 | .light .DocSearch-Input,
786 | .dark .DocSearch-Input,
787 | .light .DocSearch-Input:focus,
788 | .dark .DocSearch-Input:focus {
789 | box-shadow: 0 0 #0000;
790 | background: transparent;
791 | }
792 |
--------------------------------------------------------------------------------
/.yarn/plugins/@yarnpkg/plugin-typescript.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | //prettier-ignore
3 | module.exports = {
4 | name: "@yarnpkg/plugin-typescript",
5 | factory: function (require) {
6 | var plugin=(()=>{var Ft=Object.create,H=Object.defineProperty,Bt=Object.defineProperties,Kt=Object.getOwnPropertyDescriptor,zt=Object.getOwnPropertyDescriptors,Gt=Object.getOwnPropertyNames,Q=Object.getOwnPropertySymbols,$t=Object.getPrototypeOf,ne=Object.prototype.hasOwnProperty,De=Object.prototype.propertyIsEnumerable;var Re=(e,t,r)=>t in e?H(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,u=(e,t)=>{for(var r in t||(t={}))ne.call(t,r)&&Re(e,r,t[r]);if(Q)for(var r of Q(t))De.call(t,r)&&Re(e,r,t[r]);return e},g=(e,t)=>Bt(e,zt(t)),Lt=e=>H(e,"__esModule",{value:!0});var R=(e,t)=>{var r={};for(var s in e)ne.call(e,s)&&t.indexOf(s)<0&&(r[s]=e[s]);if(e!=null&&Q)for(var s of Q(e))t.indexOf(s)<0&&De.call(e,s)&&(r[s]=e[s]);return r};var I=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Vt=(e,t)=>{for(var r in t)H(e,r,{get:t[r],enumerable:!0})},Qt=(e,t,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of Gt(t))!ne.call(e,s)&&s!=="default"&&H(e,s,{get:()=>t[s],enumerable:!(r=Kt(t,s))||r.enumerable});return e},C=e=>Qt(Lt(H(e!=null?Ft($t(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var xe=I(J=>{"use strict";Object.defineProperty(J,"__esModule",{value:!0});function _(e){let t=[...e.caches],r=t.shift();return r===void 0?ve():{get(s,n,a={miss:()=>Promise.resolve()}){return r.get(s,n,a).catch(()=>_({caches:t}).get(s,n,a))},set(s,n){return r.set(s,n).catch(()=>_({caches:t}).set(s,n))},delete(s){return r.delete(s).catch(()=>_({caches:t}).delete(s))},clear(){return r.clear().catch(()=>_({caches:t}).clear())}}}function ve(){return{get(e,t,r={miss:()=>Promise.resolve()}){return t().then(n=>Promise.all([n,r.miss(n)])).then(([n])=>n)},set(e,t){return Promise.resolve(t)},delete(e){return Promise.resolve()},clear(){return Promise.resolve()}}}J.createFallbackableCache=_;J.createNullCache=ve});var Ee=I(($s,qe)=>{qe.exports=xe()});var Te=I(ae=>{"use strict";Object.defineProperty(ae,"__esModule",{value:!0});function Jt(e={serializable:!0}){let t={};return{get(r,s,n={miss:()=>Promise.resolve()}){let a=JSON.stringify(r);if(a in t)return Promise.resolve(e.serializable?JSON.parse(t[a]):t[a]);let o=s(),d=n&&n.miss||(()=>Promise.resolve());return o.then(y=>d(y)).then(()=>o)},set(r,s){return t[JSON.stringify(r)]=e.serializable?JSON.stringify(s):s,Promise.resolve(s)},delete(r){return delete t[JSON.stringify(r)],Promise.resolve()},clear(){return t={},Promise.resolve()}}}ae.createInMemoryCache=Jt});var we=I((Vs,Me)=>{Me.exports=Te()});var Ce=I(M=>{"use strict";Object.defineProperty(M,"__esModule",{value:!0});function Xt(e,t,r){let s={"x-algolia-api-key":r,"x-algolia-application-id":t};return{headers(){return e===oe.WithinHeaders?s:{}},queryParameters(){return e===oe.WithinQueryParameters?s:{}}}}function Yt(e){let t=0,r=()=>(t++,new Promise(s=>{setTimeout(()=>{s(e(r))},Math.min(100*t,1e3))}));return e(r)}function ke(e,t=(r,s)=>Promise.resolve()){return Object.assign(e,{wait(r){return ke(e.then(s=>Promise.all([t(s,r),s])).then(s=>s[1]))}})}function Zt(e){let t=e.length-1;for(t;t>0;t--){let r=Math.floor(Math.random()*(t+1)),s=e[t];e[t]=e[r],e[r]=s}return e}function er(e,t){return Object.keys(t!==void 0?t:{}).forEach(r=>{e[r]=t[r](e)}),e}function tr(e,...t){let r=0;return e.replace(/%s/g,()=>encodeURIComponent(t[r++]))}var rr="4.2.0",sr=e=>()=>e.transporter.requester.destroy(),oe={WithinQueryParameters:0,WithinHeaders:1};M.AuthMode=oe;M.addMethods=er;M.createAuth=Xt;M.createRetryablePromise=Yt;M.createWaitablePromise=ke;M.destroy=sr;M.encode=tr;M.shuffle=Zt;M.version=rr});var F=I((Js,Ue)=>{Ue.exports=Ce()});var Ne=I(ie=>{"use strict";Object.defineProperty(ie,"__esModule",{value:!0});var nr={Delete:"DELETE",Get:"GET",Post:"POST",Put:"PUT"};ie.MethodEnum=nr});var B=I((Ys,We)=>{We.exports=Ne()});var Ze=I(A=>{"use strict";Object.defineProperty(A,"__esModule",{value:!0});var He=B();function ce(e,t){let r=e||{},s=r.data||{};return Object.keys(r).forEach(n=>{["timeout","headers","queryParameters","data","cacheable"].indexOf(n)===-1&&(s[n]=r[n])}),{data:Object.entries(s).length>0?s:void 0,timeout:r.timeout||t,headers:r.headers||{},queryParameters:r.queryParameters||{},cacheable:r.cacheable}}var X={Read:1,Write:2,Any:3},U={Up:1,Down:2,Timeouted:3},_e=2*60*1e3;function ue(e,t=U.Up){return g(u({},e),{status:t,lastUpdate:Date.now()})}function Fe(e){return e.status===U.Up||Date.now()-e.lastUpdate>_e}function Be(e){return e.status===U.Timeouted&&Date.now()-e.lastUpdate<=_e}function le(e){return{protocol:e.protocol||"https",url:e.url,accept:e.accept||X.Any}}function ar(e,t){return Promise.all(t.map(r=>e.get(r,()=>Promise.resolve(ue(r))))).then(r=>{let s=r.filter(d=>Fe(d)),n=r.filter(d=>Be(d)),a=[...s,...n],o=a.length>0?a.map(d=>le(d)):t;return{getTimeout(d,y){return(n.length===0&&d===0?1:n.length+3+d)*y},statelessHosts:o}})}var or=({isTimedOut:e,status:t})=>!e&&~~t==0,ir=e=>{let t=e.status;return e.isTimedOut||or(e)||~~(t/100)!=2&&~~(t/100)!=4},cr=({status:e})=>~~(e/100)==2,ur=(e,t)=>ir(e)?t.onRetry(e):cr(e)?t.onSucess(e):t.onFail(e);function Qe(e,t,r,s){let n=[],a=$e(r,s),o=Le(e,s),d=r.method,y=r.method!==He.MethodEnum.Get?{}:u(u({},r.data),s.data),b=u(u(u({"x-algolia-agent":e.userAgent.value},e.queryParameters),y),s.queryParameters),f=0,p=(h,S)=>{let O=h.pop();if(O===void 0)throw Ve(de(n));let P={data:a,headers:o,method:d,url:Ge(O,r.path,b),connectTimeout:S(f,e.timeouts.connect),responseTimeout:S(f,s.timeout)},x=j=>{let T={request:P,response:j,host:O,triesLeft:h.length};return n.push(T),T},v={onSucess:j=>Ke(j),onRetry(j){let T=x(j);return j.isTimedOut&&f++,Promise.all([e.logger.info("Retryable failure",pe(T)),e.hostsCache.set(O,ue(O,j.isTimedOut?U.Timeouted:U.Down))]).then(()=>p(h,S))},onFail(j){throw x(j),ze(j,de(n))}};return e.requester.send(P).then(j=>ur(j,v))};return ar(e.hostsCache,t).then(h=>p([...h.statelessHosts].reverse(),h.getTimeout))}function lr(e){let{hostsCache:t,logger:r,requester:s,requestsCache:n,responsesCache:a,timeouts:o,userAgent:d,hosts:y,queryParameters:b,headers:f}=e,p={hostsCache:t,logger:r,requester:s,requestsCache:n,responsesCache:a,timeouts:o,userAgent:d,headers:f,queryParameters:b,hosts:y.map(h=>le(h)),read(h,S){let O=ce(S,p.timeouts.read),P=()=>Qe(p,p.hosts.filter(j=>(j.accept&X.Read)!=0),h,O);if((O.cacheable!==void 0?O.cacheable:h.cacheable)!==!0)return P();let v={request:h,mappedRequestOptions:O,transporter:{queryParameters:p.queryParameters,headers:p.headers}};return p.responsesCache.get(v,()=>p.requestsCache.get(v,()=>p.requestsCache.set(v,P()).then(j=>Promise.all([p.requestsCache.delete(v),j]),j=>Promise.all([p.requestsCache.delete(v),Promise.reject(j)])).then(([j,T])=>T)),{miss:j=>p.responsesCache.set(v,j)})},write(h,S){return Qe(p,p.hosts.filter(O=>(O.accept&X.Write)!=0),h,ce(S,p.timeouts.write))}};return p}function dr(e){let t={value:`Algolia for JavaScript (${e})`,add(r){let s=`; ${r.segment}${r.version!==void 0?` (${r.version})`:""}`;return t.value.indexOf(s)===-1&&(t.value=`${t.value}${s}`),t}};return t}function Ke(e){try{return JSON.parse(e.content)}catch(t){throw Je(t.message,e)}}function ze({content:e,status:t},r){let s=e;try{s=JSON.parse(e).message}catch(n){}return Xe(s,t,r)}function pr(e,...t){let r=0;return e.replace(/%s/g,()=>encodeURIComponent(t[r++]))}function Ge(e,t,r){let s=Ye(r),n=`${e.protocol}://${e.url}/${t.charAt(0)==="/"?t.substr(1):t}`;return s.length&&(n+=`?${s}`),n}function Ye(e){let t=r=>Object.prototype.toString.call(r)==="[object Object]"||Object.prototype.toString.call(r)==="[object Array]";return Object.keys(e).map(r=>pr("%s=%s",r,t(e[r])?JSON.stringify(e[r]):e[r])).join("&")}function $e(e,t){if(e.method===He.MethodEnum.Get||e.data===void 0&&t.data===void 0)return;let r=Array.isArray(e.data)?e.data:u(u({},e.data),t.data);return JSON.stringify(r)}function Le(e,t){let r=u(u({},e.headers),t.headers),s={};return Object.keys(r).forEach(n=>{let a=r[n];s[n.toLowerCase()]=a}),s}function de(e){return e.map(t=>pe(t))}function pe(e){let t=e.request.headers["x-algolia-api-key"]?{"x-algolia-api-key":"*****"}:{};return g(u({},e),{request:g(u({},e.request),{headers:u(u({},e.request.headers),t)})})}function Xe(e,t,r){return{name:"ApiError",message:e,status:t,transporterStackTrace:r}}function Je(e,t){return{name:"DeserializationError",message:e,response:t}}function Ve(e){return{name:"RetryError",message:"Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.",transporterStackTrace:e}}A.CallEnum=X;A.HostStatusEnum=U;A.createApiError=Xe;A.createDeserializationError=Je;A.createMappedRequestOptions=ce;A.createRetryError=Ve;A.createStatefulHost=ue;A.createStatelessHost=le;A.createTransporter=lr;A.createUserAgent=dr;A.deserializeFailure=ze;A.deserializeSuccess=Ke;A.isStatefulHostTimeouted=Be;A.isStatefulHostUp=Fe;A.serializeData=$e;A.serializeHeaders=Le;A.serializeQueryParameters=Ye;A.serializeUrl=Ge;A.stackFrameWithoutCredentials=pe;A.stackTraceWithoutCredentials=de});var K=I((en,et)=>{et.exports=Ze()});var tt=I(w=>{"use strict";Object.defineProperty(w,"__esModule",{value:!0});var N=F(),mr=K(),z=B(),hr=e=>{let t=e.region||"us",r=N.createAuth(N.AuthMode.WithinHeaders,e.appId,e.apiKey),s=mr.createTransporter(g(u({hosts:[{url:`analytics.${t}.algolia.com`}]},e),{headers:u(g(u({},r.headers()),{"content-type":"application/json"}),e.headers),queryParameters:u(u({},r.queryParameters()),e.queryParameters)})),n=e.appId;return N.addMethods({appId:n,transporter:s},e.methods)},yr=e=>(t,r)=>e.transporter.write({method:z.MethodEnum.Post,path:"2/abtests",data:t},r),gr=e=>(t,r)=>e.transporter.write({method:z.MethodEnum.Delete,path:N.encode("2/abtests/%s",t)},r),fr=e=>(t,r)=>e.transporter.read({method:z.MethodEnum.Get,path:N.encode("2/abtests/%s",t)},r),br=e=>t=>e.transporter.read({method:z.MethodEnum.Get,path:"2/abtests"},t),Pr=e=>(t,r)=>e.transporter.write({method:z.MethodEnum.Post,path:N.encode("2/abtests/%s/stop",t)},r);w.addABTest=yr;w.createAnalyticsClient=hr;w.deleteABTest=gr;w.getABTest=fr;w.getABTests=br;w.stopABTest=Pr});var st=I((rn,rt)=>{rt.exports=tt()});var at=I(G=>{"use strict";Object.defineProperty(G,"__esModule",{value:!0});var me=F(),jr=K(),nt=B(),Or=e=>{let t=e.region||"us",r=me.createAuth(me.AuthMode.WithinHeaders,e.appId,e.apiKey),s=jr.createTransporter(g(u({hosts:[{url:`recommendation.${t}.algolia.com`}]},e),{headers:u(g(u({},r.headers()),{"content-type":"application/json"}),e.headers),queryParameters:u(u({},r.queryParameters()),e.queryParameters)}));return me.addMethods({appId:e.appId,transporter:s},e.methods)},Ir=e=>t=>e.transporter.read({method:nt.MethodEnum.Get,path:"1/strategies/personalization"},t),Ar=e=>(t,r)=>e.transporter.write({method:nt.MethodEnum.Post,path:"1/strategies/personalization",data:t},r);G.createRecommendationClient=Or;G.getPersonalizationStrategy=Ir;G.setPersonalizationStrategy=Ar});var it=I((nn,ot)=>{ot.exports=at()});var jt=I(i=>{"use strict";Object.defineProperty(i,"__esModule",{value:!0});var l=F(),q=K(),m=B(),Sr=require("crypto");function Y(e){let t=r=>e.request(r).then(s=>{if(e.batch!==void 0&&e.batch(s.hits),!e.shouldStop(s))return s.cursor?t({cursor:s.cursor}):t({page:(r.page||0)+1})});return t({})}var Dr=e=>{let t=e.appId,r=l.createAuth(e.authMode!==void 0?e.authMode:l.AuthMode.WithinHeaders,t,e.apiKey),s=q.createTransporter(g(u({hosts:[{url:`${t}-dsn.algolia.net`,accept:q.CallEnum.Read},{url:`${t}.algolia.net`,accept:q.CallEnum.Write}].concat(l.shuffle([{url:`${t}-1.algolianet.com`},{url:`${t}-2.algolianet.com`},{url:`${t}-3.algolianet.com`}]))},e),{headers:u(g(u({},r.headers()),{"content-type":"application/x-www-form-urlencoded"}),e.headers),queryParameters:u(u({},r.queryParameters()),e.queryParameters)})),n={transporter:s,appId:t,addAlgoliaAgent(a,o){s.userAgent.add({segment:a,version:o})},clearCache(){return Promise.all([s.requestsCache.clear(),s.responsesCache.clear()]).then(()=>{})}};return l.addMethods(n,e.methods)};function ct(){return{name:"MissingObjectIDError",message:"All objects must have an unique objectID (like a primary key) to be valid. Algolia is also able to generate objectIDs automatically but *it's not recommended*. To do it, use the `{'autoGenerateObjectIDIfNotExist': true}` option."}}function ut(){return{name:"ObjectNotFoundError",message:"Object not found."}}function lt(){return{name:"ValidUntilNotFoundError",message:"ValidUntil not found in given secured api key."}}var Rr=e=>(t,r)=>{let d=r||{},{queryParameters:s}=d,n=R(d,["queryParameters"]),a=u({acl:t},s!==void 0?{queryParameters:s}:{}),o=(y,b)=>l.createRetryablePromise(f=>$(e)(y.key,b).catch(p=>{if(p.status!==404)throw p;return f()}));return l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:"1/keys",data:a},n),o)},vr=e=>(t,r,s)=>{let n=q.createMappedRequestOptions(s);return n.queryParameters["X-Algolia-User-ID"]=t,e.transporter.write({method:m.MethodEnum.Post,path:"1/clusters/mapping",data:{cluster:r}},n)},xr=e=>(t,r,s)=>e.transporter.write({method:m.MethodEnum.Post,path:"1/clusters/mapping/batch",data:{users:t,cluster:r}},s),Z=e=>(t,r,s)=>{let n=(a,o)=>L(e)(t,{methods:{waitTask:D}}).waitTask(a.taskID,o);return l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/operation",t),data:{operation:"copy",destination:r}},s),n)},qr=e=>(t,r,s)=>Z(e)(t,r,g(u({},s),{scope:[ee.Rules]})),Er=e=>(t,r,s)=>Z(e)(t,r,g(u({},s),{scope:[ee.Settings]})),Tr=e=>(t,r,s)=>Z(e)(t,r,g(u({},s),{scope:[ee.Synonyms]})),Mr=e=>(t,r)=>{let s=(n,a)=>l.createRetryablePromise(o=>$(e)(t,a).then(o).catch(d=>{if(d.status!==404)throw d}));return l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Delete,path:l.encode("1/keys/%s",t)},r),s)},wr=()=>(e,t)=>{let r=q.serializeQueryParameters(t),s=Sr.createHmac("sha256",e).update(r).digest("hex");return Buffer.from(s+r).toString("base64")},$=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Get,path:l.encode("1/keys/%s",t)},r),kr=e=>t=>e.transporter.read({method:m.MethodEnum.Get,path:"1/logs"},t),Cr=()=>e=>{let t=Buffer.from(e,"base64").toString("ascii"),r=/validUntil=(\d+)/,s=t.match(r);if(s===null)throw lt();return parseInt(s[1],10)-Math.round(new Date().getTime()/1e3)},Ur=e=>t=>e.transporter.read({method:m.MethodEnum.Get,path:"1/clusters/mapping/top"},t),Nr=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Get,path:l.encode("1/clusters/mapping/%s",t)},r),Wr=e=>t=>{let n=t||{},{retrieveMappings:r}=n,s=R(n,["retrieveMappings"]);return r===!0&&(s.getClusters=!0),e.transporter.read({method:m.MethodEnum.Get,path:"1/clusters/mapping/pending"},s)},L=e=>(t,r={})=>{let s={transporter:e.transporter,appId:e.appId,indexName:t};return l.addMethods(s,r.methods)},Hr=e=>t=>e.transporter.read({method:m.MethodEnum.Get,path:"1/keys"},t),_r=e=>t=>e.transporter.read({method:m.MethodEnum.Get,path:"1/clusters"},t),Fr=e=>t=>e.transporter.read({method:m.MethodEnum.Get,path:"1/indexes"},t),Br=e=>t=>e.transporter.read({method:m.MethodEnum.Get,path:"1/clusters/mapping"},t),Kr=e=>(t,r,s)=>{let n=(a,o)=>L(e)(t,{methods:{waitTask:D}}).waitTask(a.taskID,o);return l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/operation",t),data:{operation:"move",destination:r}},s),n)},zr=e=>(t,r)=>{let s=(n,a)=>Promise.all(Object.keys(n.taskID).map(o=>L(e)(o,{methods:{waitTask:D}}).waitTask(n.taskID[o],a)));return l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:"1/indexes/*/batch",data:{requests:t}},r),s)},Gr=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Post,path:"1/indexes/*/objects",data:{requests:t}},r),$r=e=>(t,r)=>{let s=t.map(n=>g(u({},n),{params:q.serializeQueryParameters(n.params||{})}));return e.transporter.read({method:m.MethodEnum.Post,path:"1/indexes/*/queries",data:{requests:s},cacheable:!0},r)},Lr=e=>(t,r)=>Promise.all(t.map(s=>{let d=s.params,{facetName:n,facetQuery:a}=d,o=R(d,["facetName","facetQuery"]);return L(e)(s.indexName,{methods:{searchForFacetValues:dt}}).searchForFacetValues(n,a,u(u({},r),o))})),Vr=e=>(t,r)=>{let s=q.createMappedRequestOptions(r);return s.queryParameters["X-Algolia-User-ID"]=t,e.transporter.write({method:m.MethodEnum.Delete,path:"1/clusters/mapping"},s)},Qr=e=>(t,r)=>{let s=(n,a)=>l.createRetryablePromise(o=>$(e)(t,a).catch(d=>{if(d.status!==404)throw d;return o()}));return l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/keys/%s/restore",t)},r),s)},Jr=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Post,path:"1/clusters/mapping/search",data:{query:t}},r),Xr=e=>(t,r)=>{let s=Object.assign({},r),f=r||{},{queryParameters:n}=f,a=R(f,["queryParameters"]),o=n?{queryParameters:n}:{},d=["acl","indexes","referers","restrictSources","queryParameters","description","maxQueriesPerIPPerHour","maxHitsPerQuery"],y=p=>Object.keys(s).filter(h=>d.indexOf(h)!==-1).every(h=>p[h]===s[h]),b=(p,h)=>l.createRetryablePromise(S=>$(e)(t,h).then(O=>y(O)?Promise.resolve():S()));return l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Put,path:l.encode("1/keys/%s",t),data:o},a),b)},pt=e=>(t,r)=>{let s=(n,a)=>D(e)(n.taskID,a);return l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/batch",e.indexName),data:{requests:t}},r),s)},Yr=e=>t=>Y(g(u({},t),{shouldStop:r=>r.cursor===void 0,request:r=>e.transporter.read({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/browse",e.indexName),data:r},t)})),Zr=e=>t=>{let r=u({hitsPerPage:1e3},t);return Y(g(u({},r),{shouldStop:s=>s.hits.lengthg(u({},n),{hits:n.hits.map(a=>(delete a._highlightResult,a))}))}}))},es=e=>t=>{let r=u({hitsPerPage:1e3},t);return Y(g(u({},r),{shouldStop:s=>s.hits.lengthg(u({},n),{hits:n.hits.map(a=>(delete a._highlightResult,a))}))}}))},te=e=>(t,r,s)=>{let y=s||{},{batchSize:n}=y,a=R(y,["batchSize"]),o={taskIDs:[],objectIDs:[]},d=(b=0)=>{let f=[],p;for(p=b;p({action:r,body:h})),a).then(h=>(o.objectIDs=o.objectIDs.concat(h.objectIDs),o.taskIDs.push(h.taskID),p++,d(p)))};return l.createWaitablePromise(d(),(b,f)=>Promise.all(b.taskIDs.map(p=>D(e)(p,f))))},ts=e=>t=>l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/clear",e.indexName)},t),(r,s)=>D(e)(r.taskID,s)),rs=e=>t=>{let a=t||{},{forwardToReplicas:r}=a,s=R(a,["forwardToReplicas"]),n=q.createMappedRequestOptions(s);return r&&(n.queryParameters.forwardToReplicas=1),l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/rules/clear",e.indexName)},n),(o,d)=>D(e)(o.taskID,d))},ss=e=>t=>{let a=t||{},{forwardToReplicas:r}=a,s=R(a,["forwardToReplicas"]),n=q.createMappedRequestOptions(s);return r&&(n.queryParameters.forwardToReplicas=1),l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/synonyms/clear",e.indexName)},n),(o,d)=>D(e)(o.taskID,d))},ns=e=>(t,r)=>l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/deleteByQuery",e.indexName),data:t},r),(s,n)=>D(e)(s.taskID,n)),as=e=>t=>l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Delete,path:l.encode("1/indexes/%s",e.indexName)},t),(r,s)=>D(e)(r.taskID,s)),os=e=>(t,r)=>l.createWaitablePromise(yt(e)([t],r).then(s=>({taskID:s.taskIDs[0]})),(s,n)=>D(e)(s.taskID,n)),yt=e=>(t,r)=>{let s=t.map(n=>({objectID:n}));return te(e)(s,k.DeleteObject,r)},is=e=>(t,r)=>{let o=r||{},{forwardToReplicas:s}=o,n=R(o,["forwardToReplicas"]),a=q.createMappedRequestOptions(n);return s&&(a.queryParameters.forwardToReplicas=1),l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Delete,path:l.encode("1/indexes/%s/rules/%s",e.indexName,t)},a),(d,y)=>D(e)(d.taskID,y))},cs=e=>(t,r)=>{let o=r||{},{forwardToReplicas:s}=o,n=R(o,["forwardToReplicas"]),a=q.createMappedRequestOptions(n);return s&&(a.queryParameters.forwardToReplicas=1),l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Delete,path:l.encode("1/indexes/%s/synonyms/%s",e.indexName,t)},a),(d,y)=>D(e)(d.taskID,y))},us=e=>t=>gt(e)(t).then(()=>!0).catch(r=>{if(r.status!==404)throw r;return!1}),ls=e=>(t,r)=>{let y=r||{},{query:s,paginate:n}=y,a=R(y,["query","paginate"]),o=0,d=()=>ft(e)(s||"",g(u({},a),{page:o})).then(b=>{for(let[f,p]of Object.entries(b.hits))if(t(p))return{object:p,position:parseInt(f,10),page:o};if(o++,n===!1||o>=b.nbPages)throw ut();return d()});return d()},ds=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Get,path:l.encode("1/indexes/%s/%s",e.indexName,t)},r),ps=()=>(e,t)=>{for(let[r,s]of Object.entries(e.hits))if(s.objectID===t)return parseInt(r,10);return-1},ms=e=>(t,r)=>{let o=r||{},{attributesToRetrieve:s}=o,n=R(o,["attributesToRetrieve"]),a=t.map(d=>u({indexName:e.indexName,objectID:d},s?{attributesToRetrieve:s}:{}));return e.transporter.read({method:m.MethodEnum.Post,path:"1/indexes/*/objects",data:{requests:a}},n)},hs=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Get,path:l.encode("1/indexes/%s/rules/%s",e.indexName,t)},r),gt=e=>t=>e.transporter.read({method:m.MethodEnum.Get,path:l.encode("1/indexes/%s/settings",e.indexName),data:{getVersion:2}},t),ys=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Get,path:l.encode("1/indexes/%s/synonyms/%s",e.indexName,t)},r),bt=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Get,path:l.encode("1/indexes/%s/task/%s",e.indexName,t.toString())},r),gs=e=>(t,r)=>l.createWaitablePromise(Pt(e)([t],r).then(s=>({objectID:s.objectIDs[0],taskID:s.taskIDs[0]})),(s,n)=>D(e)(s.taskID,n)),Pt=e=>(t,r)=>{let o=r||{},{createIfNotExists:s}=o,n=R(o,["createIfNotExists"]),a=s?k.PartialUpdateObject:k.PartialUpdateObjectNoCreate;return te(e)(t,a,n)},fs=e=>(t,r)=>{let O=r||{},{safe:s,autoGenerateObjectIDIfNotExist:n,batchSize:a}=O,o=R(O,["safe","autoGenerateObjectIDIfNotExist","batchSize"]),d=(P,x,v,j)=>l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/operation",P),data:{operation:v,destination:x}},j),(T,V)=>D(e)(T.taskID,V)),y=Math.random().toString(36).substring(7),b=`${e.indexName}_tmp_${y}`,f=he({appId:e.appId,transporter:e.transporter,indexName:b}),p=[],h=d(e.indexName,b,"copy",g(u({},o),{scope:["settings","synonyms","rules"]}));p.push(h);let S=(s?h.wait(o):h).then(()=>{let P=f(t,g(u({},o),{autoGenerateObjectIDIfNotExist:n,batchSize:a}));return p.push(P),s?P.wait(o):P}).then(()=>{let P=d(b,e.indexName,"move",o);return p.push(P),s?P.wait(o):P}).then(()=>Promise.all(p)).then(([P,x,v])=>({objectIDs:x.objectIDs,taskIDs:[P.taskID,...x.taskIDs,v.taskID]}));return l.createWaitablePromise(S,(P,x)=>Promise.all(p.map(v=>v.wait(x))))},bs=e=>(t,r)=>ye(e)(t,g(u({},r),{clearExistingRules:!0})),Ps=e=>(t,r)=>ge(e)(t,g(u({},r),{replaceExistingSynonyms:!0})),js=e=>(t,r)=>l.createWaitablePromise(he(e)([t],r).then(s=>({objectID:s.objectIDs[0],taskID:s.taskIDs[0]})),(s,n)=>D(e)(s.taskID,n)),he=e=>(t,r)=>{let o=r||{},{autoGenerateObjectIDIfNotExist:s}=o,n=R(o,["autoGenerateObjectIDIfNotExist"]),a=s?k.AddObject:k.UpdateObject;if(a===k.UpdateObject){for(let d of t)if(d.objectID===void 0)return l.createWaitablePromise(Promise.reject(ct()))}return te(e)(t,a,n)},Os=e=>(t,r)=>ye(e)([t],r),ye=e=>(t,r)=>{let d=r||{},{forwardToReplicas:s,clearExistingRules:n}=d,a=R(d,["forwardToReplicas","clearExistingRules"]),o=q.createMappedRequestOptions(a);return s&&(o.queryParameters.forwardToReplicas=1),n&&(o.queryParameters.clearExistingRules=1),l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/rules/batch",e.indexName),data:t},o),(y,b)=>D(e)(y.taskID,b))},Is=e=>(t,r)=>ge(e)([t],r),ge=e=>(t,r)=>{let d=r||{},{forwardToReplicas:s,replaceExistingSynonyms:n}=d,a=R(d,["forwardToReplicas","replaceExistingSynonyms"]),o=q.createMappedRequestOptions(a);return s&&(o.queryParameters.forwardToReplicas=1),n&&(o.queryParameters.replaceExistingSynonyms=1),l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/synonyms/batch",e.indexName),data:t},o),(y,b)=>D(e)(y.taskID,b))},ft=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/query",e.indexName),data:{query:t},cacheable:!0},r),dt=e=>(t,r,s)=>e.transporter.read({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/facets/%s/query",e.indexName,t),data:{facetQuery:r},cacheable:!0},s),mt=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/rules/search",e.indexName),data:{query:t}},r),ht=e=>(t,r)=>e.transporter.read({method:m.MethodEnum.Post,path:l.encode("1/indexes/%s/synonyms/search",e.indexName),data:{query:t}},r),As=e=>(t,r)=>{let o=r||{},{forwardToReplicas:s}=o,n=R(o,["forwardToReplicas"]),a=q.createMappedRequestOptions(n);return s&&(a.queryParameters.forwardToReplicas=1),l.createWaitablePromise(e.transporter.write({method:m.MethodEnum.Put,path:l.encode("1/indexes/%s/settings",e.indexName),data:t},a),(d,y)=>D(e)(d.taskID,y))},D=e=>(t,r)=>l.createRetryablePromise(s=>bt(e)(t,r).then(n=>n.status!=="published"?s():void 0)),Ss={AddObject:"addObject",Analytics:"analytics",Browser:"browse",DeleteIndex:"deleteIndex",DeleteObject:"deleteObject",EditSettings:"editSettings",ListIndexes:"listIndexes",Logs:"logs",Recommendation:"recommendation",Search:"search",SeeUnretrievableAttributes:"seeUnretrievableAttributes",Settings:"settings",Usage:"usage"},k={AddObject:"addObject",UpdateObject:"updateObject",PartialUpdateObject:"partialUpdateObject",PartialUpdateObjectNoCreate:"partialUpdateObjectNoCreate",DeleteObject:"deleteObject"},ee={Settings:"settings",Synonyms:"synonyms",Rules:"rules"},Ds={None:"none",StopIfEnoughMatches:"stopIfEnoughMatches"},Rs={Synonym:"synonym",OneWaySynonym:"oneWaySynonym",AltCorrection1:"altCorrection1",AltCorrection2:"altCorrection2",Placeholder:"placeholder"};i.ApiKeyACLEnum=Ss;i.BatchActionEnum=k;i.ScopeEnum=ee;i.StrategyEnum=Ds;i.SynonymEnum=Rs;i.addApiKey=Rr;i.assignUserID=vr;i.assignUserIDs=xr;i.batch=pt;i.browseObjects=Yr;i.browseRules=Zr;i.browseSynonyms=es;i.chunkedBatch=te;i.clearObjects=ts;i.clearRules=rs;i.clearSynonyms=ss;i.copyIndex=Z;i.copyRules=qr;i.copySettings=Er;i.copySynonyms=Tr;i.createBrowsablePromise=Y;i.createMissingObjectIDError=ct;i.createObjectNotFoundError=ut;i.createSearchClient=Dr;i.createValidUntilNotFoundError=lt;i.deleteApiKey=Mr;i.deleteBy=ns;i.deleteIndex=as;i.deleteObject=os;i.deleteObjects=yt;i.deleteRule=is;i.deleteSynonym=cs;i.exists=us;i.findObject=ls;i.generateSecuredApiKey=wr;i.getApiKey=$;i.getLogs=kr;i.getObject=ds;i.getObjectPosition=ps;i.getObjects=ms;i.getRule=hs;i.getSecuredApiKeyRemainingValidity=Cr;i.getSettings=gt;i.getSynonym=ys;i.getTask=bt;i.getTopUserIDs=Ur;i.getUserID=Nr;i.hasPendingMappings=Wr;i.initIndex=L;i.listApiKeys=Hr;i.listClusters=_r;i.listIndices=Fr;i.listUserIDs=Br;i.moveIndex=Kr;i.multipleBatch=zr;i.multipleGetObjects=Gr;i.multipleQueries=$r;i.multipleSearchForFacetValues=Lr;i.partialUpdateObject=gs;i.partialUpdateObjects=Pt;i.removeUserID=Vr;i.replaceAllObjects=fs;i.replaceAllRules=bs;i.replaceAllSynonyms=Ps;i.restoreApiKey=Qr;i.saveObject=js;i.saveObjects=he;i.saveRule=Os;i.saveRules=ye;i.saveSynonym=Is;i.saveSynonyms=ge;i.search=ft;i.searchForFacetValues=dt;i.searchRules=mt;i.searchSynonyms=ht;i.searchUserIDs=Jr;i.setSettings=As;i.updateApiKey=Xr;i.waitTask=D});var It=I((on,Ot)=>{Ot.exports=jt()});var At=I(re=>{"use strict";Object.defineProperty(re,"__esModule",{value:!0});function vs(){return{debug(e,t){return Promise.resolve()},info(e,t){return Promise.resolve()},error(e,t){return Promise.resolve()}}}var xs={Debug:1,Info:2,Error:3};re.LogLevelEnum=xs;re.createNullLogger=vs});var Dt=I((un,St)=>{St.exports=At()});var xt=I(fe=>{"use strict";Object.defineProperty(fe,"__esModule",{value:!0});var Rt=require("http"),vt=require("https"),qs=require("url");function Es(){let e={keepAlive:!0},t=new Rt.Agent(e),r=new vt.Agent(e);return{send(s){return new Promise(n=>{let a=qs.parse(s.url),o=a.query===null?a.pathname:`${a.pathname}?${a.query}`,d=u({agent:a.protocol==="https:"?r:t,hostname:a.hostname,path:o,method:s.method,headers:s.headers},a.port!==void 0?{port:a.port||""}:{}),y=(a.protocol==="https:"?vt:Rt).request(d,h=>{let S="";h.on("data",O=>S+=O),h.on("end",()=>{clearTimeout(f),clearTimeout(p),n({status:h.statusCode||0,content:S,isTimedOut:!1})})}),b=(h,S)=>setTimeout(()=>{y.abort(),n({status:0,content:S,isTimedOut:!0})},h*1e3),f=b(s.connectTimeout,"Connection timeout"),p;y.on("error",h=>{clearTimeout(f),clearTimeout(p),n({status:0,content:h.message,isTimedOut:!1})}),y.once("response",()=>{clearTimeout(f),p=b(s.responseTimeout,"Socket timeout")}),s.data!==void 0&&y.write(s.data),y.end()})},destroy(){return t.destroy(),r.destroy(),Promise.resolve()}}}fe.createNodeHttpRequester=Es});var Et=I((dn,qt)=>{qt.exports=xt()});var kt=I((pn,Tt)=>{"use strict";var Mt=Ee(),Ts=we(),W=st(),be=F(),Pe=it(),c=It(),Ms=Dt(),ws=Et(),ks=K();function wt(e,t,r){let s={appId:e,apiKey:t,timeouts:{connect:2,read:5,write:30},requester:ws.createNodeHttpRequester(),logger:Ms.createNullLogger(),responsesCache:Mt.createNullCache(),requestsCache:Mt.createNullCache(),hostsCache:Ts.createInMemoryCache(),userAgent:ks.createUserAgent(be.version).add({segment:"Node.js",version:process.versions.node})};return c.createSearchClient(g(u(u({},s),r),{methods:{search:c.multipleQueries,searchForFacetValues:c.multipleSearchForFacetValues,multipleBatch:c.multipleBatch,multipleGetObjects:c.multipleGetObjects,multipleQueries:c.multipleQueries,copyIndex:c.copyIndex,copySettings:c.copySettings,copyRules:c.copyRules,copySynonyms:c.copySynonyms,moveIndex:c.moveIndex,listIndices:c.listIndices,getLogs:c.getLogs,listClusters:c.listClusters,multipleSearchForFacetValues:c.multipleSearchForFacetValues,getApiKey:c.getApiKey,addApiKey:c.addApiKey,listApiKeys:c.listApiKeys,updateApiKey:c.updateApiKey,deleteApiKey:c.deleteApiKey,restoreApiKey:c.restoreApiKey,assignUserID:c.assignUserID,assignUserIDs:c.assignUserIDs,getUserID:c.getUserID,searchUserIDs:c.searchUserIDs,listUserIDs:c.listUserIDs,getTopUserIDs:c.getTopUserIDs,removeUserID:c.removeUserID,hasPendingMappings:c.hasPendingMappings,generateSecuredApiKey:c.generateSecuredApiKey,getSecuredApiKeyRemainingValidity:c.getSecuredApiKeyRemainingValidity,destroy:be.destroy,initIndex:n=>a=>c.initIndex(n)(a,{methods:{batch:c.batch,delete:c.deleteIndex,getObject:c.getObject,getObjects:c.getObjects,saveObject:c.saveObject,saveObjects:c.saveObjects,search:c.search,searchForFacetValues:c.searchForFacetValues,waitTask:c.waitTask,setSettings:c.setSettings,getSettings:c.getSettings,partialUpdateObject:c.partialUpdateObject,partialUpdateObjects:c.partialUpdateObjects,deleteObject:c.deleteObject,deleteObjects:c.deleteObjects,deleteBy:c.deleteBy,clearObjects:c.clearObjects,browseObjects:c.browseObjects,getObjectPosition:c.getObjectPosition,findObject:c.findObject,exists:c.exists,saveSynonym:c.saveSynonym,saveSynonyms:c.saveSynonyms,getSynonym:c.getSynonym,searchSynonyms:c.searchSynonyms,browseSynonyms:c.browseSynonyms,deleteSynonym:c.deleteSynonym,clearSynonyms:c.clearSynonyms,replaceAllObjects:c.replaceAllObjects,replaceAllSynonyms:c.replaceAllSynonyms,searchRules:c.searchRules,getRule:c.getRule,deleteRule:c.deleteRule,saveRule:c.saveRule,saveRules:c.saveRules,replaceAllRules:c.replaceAllRules,browseRules:c.browseRules,clearRules:c.clearRules}}),initAnalytics:()=>n=>W.createAnalyticsClient(g(u(u({},s),n),{methods:{addABTest:W.addABTest,getABTest:W.getABTest,getABTests:W.getABTests,stopABTest:W.stopABTest,deleteABTest:W.deleteABTest}})),initRecommendation:()=>n=>Pe.createRecommendationClient(g(u(u({},s),n),{methods:{getPersonalizationStrategy:Pe.getPersonalizationStrategy,setPersonalizationStrategy:Pe.setPersonalizationStrategy}}))}}))}wt.version=be.version;Tt.exports=wt});var Ut=I((mn,je)=>{var Ct=kt();je.exports=Ct;je.exports.default=Ct});var Ws={};Vt(Ws,{default:()=>Ks});var Oe=C(require("@yarnpkg/core")),E=C(require("@yarnpkg/core")),Ie=C(require("@yarnpkg/plugin-essentials")),Ht=C(require("semver"));var se=C(require("@yarnpkg/core")),Nt=C(Ut()),Cs="e8e1bd300d860104bb8c58453ffa1eb4",Us="OFCNCOG2CU",Wt=async(e,t)=>{var a;let r=se.structUtils.stringifyIdent(e),n=Ns(t).initIndex("npm-search");try{return((a=(await n.getObject(r,{attributesToRetrieve:["types"]})).types)==null?void 0:a.ts)==="definitely-typed"}catch(o){return!1}},Ns=e=>(0,Nt.default)(Us,Cs,{requester:{async send(r){try{let s=await se.httpUtils.request(r.url,r.data||null,{configuration:e,headers:r.headers});return{content:s.body,isTimedOut:!1,status:s.statusCode}}catch(s){return{content:s.response.body,isTimedOut:!1,status:s.response.statusCode}}}}});var _t=e=>e.scope?`${e.scope}__${e.name}`:`${e.name}`,Hs=async(e,t,r,s)=>{if(r.scope==="types")return;let{project:n}=e,{configuration:a}=n,o=a.makeResolver(),d={project:n,resolver:o,report:new E.ThrowReport};if(!await Wt(r,a))return;let b=_t(r),f=E.structUtils.parseRange(r.range).selector;if(!E.semverUtils.validRange(f)){let P=await o.getCandidates(r,new Map,d);f=E.structUtils.parseRange(P[0].reference).selector}let p=Ht.default.coerce(f);if(p===null)return;let h=`${Ie.suggestUtils.Modifier.CARET}${p.major}`,S=E.structUtils.makeDescriptor(E.structUtils.makeIdent("types",b),h),O=E.miscUtils.mapAndFind(n.workspaces,P=>{var T,V;let x=(T=P.manifest.dependencies.get(r.identHash))==null?void 0:T.descriptorHash,v=(V=P.manifest.devDependencies.get(r.identHash))==null?void 0:V.descriptorHash;if(x!==r.descriptorHash&&v!==r.descriptorHash)return E.miscUtils.mapAndFind.skip;let j=[];for(let Ae of Oe.Manifest.allDependencies){let Se=P.manifest[Ae].get(S.identHash);typeof Se!="undefined"&&j.push([Ae,Se])}return j.length===0?E.miscUtils.mapAndFind.skip:j});if(typeof O!="undefined")for(let[P,x]of O)e.manifest[P].set(x.identHash,x);else{try{if((await o.getCandidates(S,new Map,d)).length===0)return}catch{return}e.manifest[Ie.suggestUtils.Target.DEVELOPMENT].set(S.identHash,S)}},_s=async(e,t,r)=>{if(r.scope==="types")return;let s=_t(r),n=E.structUtils.makeIdent("types",s);for(let a of Oe.Manifest.allDependencies)typeof e.manifest[a].get(n.identHash)!="undefined"&&e.manifest[a].delete(n.identHash)},Fs=(e,t)=>{t.publishConfig&&t.publishConfig.typings&&(t.typings=t.publishConfig.typings),t.publishConfig&&t.publishConfig.types&&(t.types=t.publishConfig.types)},Bs={hooks:{afterWorkspaceDependencyAddition:Hs,afterWorkspaceDependencyRemoval:_s,beforeWorkspacePacking:Fs}},Ks=Bs;return Ws;})();
7 | return plugin;
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | //prettier-ignore
3 | module.exports = {
4 | name: "@yarnpkg/plugin-workspace-tools",
5 | factory: function (require) {
6 | var plugin=(()=>{var wr=Object.create,ge=Object.defineProperty,Sr=Object.defineProperties,vr=Object.getOwnPropertyDescriptor,Hr=Object.getOwnPropertyDescriptors,$r=Object.getOwnPropertyNames,Je=Object.getOwnPropertySymbols,kr=Object.getPrototypeOf,et=Object.prototype.hasOwnProperty,Tr=Object.prototype.propertyIsEnumerable;var tt=(e,t,r)=>t in e?ge(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,I=(e,t)=>{for(var r in t||(t={}))et.call(t,r)&&tt(e,r,t[r]);if(Je)for(var r of Je(t))Tr.call(t,r)&&tt(e,r,t[r]);return e},F=(e,t)=>Sr(e,Hr(t)),Lr=e=>ge(e,"__esModule",{value:!0});var K=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Or=(e,t)=>{for(var r in t)ge(e,r,{get:t[r],enumerable:!0})},Nr=(e,t,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of $r(t))!et.call(e,n)&&n!=="default"&&ge(e,n,{get:()=>t[n],enumerable:!(r=vr(t,n))||r.enumerable});return e},Q=e=>Nr(Lr(ge(e!=null?wr(kr(e)):{},"default",e&&e.__esModule&&"default"in e?{get:()=>e.default,enumerable:!0}:{value:e,enumerable:!0})),e);var He=K(ee=>{"use strict";ee.isInteger=e=>typeof e=="number"?Number.isInteger(e):typeof e=="string"&&e.trim()!==""?Number.isInteger(Number(e)):!1;ee.find=(e,t)=>e.nodes.find(r=>r.type===t);ee.exceedsLimit=(e,t,r=1,n)=>n===!1||!ee.isInteger(e)||!ee.isInteger(t)?!1:(Number(t)-Number(e))/Number(r)>=n;ee.escapeNode=(e,t=0,r)=>{let n=e.nodes[t];!n||(r&&n.type===r||n.type==="open"||n.type==="close")&&n.escaped!==!0&&(n.value="\\"+n.value,n.escaped=!0)};ee.encloseBrace=e=>e.type!=="brace"?!1:e.commas>>0+e.ranges>>0==0?(e.invalid=!0,!0):!1;ee.isInvalidBrace=e=>e.type!=="brace"?!1:e.invalid===!0||e.dollar?!0:e.commas>>0+e.ranges>>0==0||e.open!==!0||e.close!==!0?(e.invalid=!0,!0):!1;ee.isOpenOrClose=e=>e.type==="open"||e.type==="close"?!0:e.open===!0||e.close===!0;ee.reduce=e=>e.reduce((t,r)=>(r.type==="text"&&t.push(r.value),r.type==="range"&&(r.type="text"),t),[]);ee.flatten=(...e)=>{let t=[],r=n=>{for(let s=0;s{"use strict";var at=He();st.exports=(e,t={})=>{let r=(n,s={})=>{let a=t.escapeInvalid&&at.isInvalidBrace(s),i=n.invalid===!0&&t.escapeInvalid===!0,o="";if(n.value)return(a||i)&&at.isOpenOrClose(n)?"\\"+n.value:n.value;if(n.value)return n.value;if(n.nodes)for(let h of n.nodes)o+=r(h);return o};return r(e)}});var ot=K((os,it)=>{"use strict";it.exports=function(e){return typeof e=="number"?e-e==0:typeof e=="string"&&e.trim()!==""?Number.isFinite?Number.isFinite(+e):isFinite(+e):!1}});var mt=K((us,ut)=>{"use strict";var ct=ot(),pe=(e,t,r)=>{if(ct(e)===!1)throw new TypeError("toRegexRange: expected the first argument to be a number");if(t===void 0||e===t)return String(e);if(ct(t)===!1)throw new TypeError("toRegexRange: expected the second argument to be a number.");let n=I({relaxZeros:!0},r);typeof n.strictZeros=="boolean"&&(n.relaxZeros=n.strictZeros===!1);let s=String(n.relaxZeros),a=String(n.shorthand),i=String(n.capture),o=String(n.wrap),h=e+":"+t+"="+s+a+i+o;if(pe.cache.hasOwnProperty(h))return pe.cache[h].result;let m=Math.min(e,t),f=Math.max(e,t);if(Math.abs(m-f)===1){let y=e+"|"+t;return n.capture?`(${y})`:n.wrap===!1?y:`(?:${y})`}let R=pt(e)||pt(t),p={min:e,max:t,a:m,b:f},v=[],_=[];if(R&&(p.isPadded=R,p.maxLen=String(p.max).length),m<0){let y=f<0?Math.abs(f):1;_=lt(y,Math.abs(m),p,n),m=p.a=0}return f>=0&&(v=lt(m,f,p,n)),p.negatives=_,p.positives=v,p.result=Ir(_,v,n),n.capture===!0?p.result=`(${p.result})`:n.wrap!==!1&&v.length+_.length>1&&(p.result=`(?:${p.result})`),pe.cache[h]=p,p.result};function Ir(e,t,r){let n=Pe(e,t,"-",!1,r)||[],s=Pe(t,e,"",!1,r)||[],a=Pe(e,t,"-?",!0,r)||[];return n.concat(a).concat(s).join("|")}function Mr(e,t){let r=1,n=1,s=ft(e,r),a=new Set([t]);for(;e<=s&&s<=t;)a.add(s),r+=1,s=ft(e,r);for(s=ht(t+1,n)-1;e1&&o.count.pop(),o.count.push(f.count[0]),o.string=o.pattern+dt(o.count),i=m+1;continue}r.isPadded&&(R=Ur(m,r,n)),f.string=R+f.pattern+dt(f.count),a.push(f),i=m+1,o=f}return a}function Pe(e,t,r,n,s){let a=[];for(let i of e){let{string:o}=i;!n&&!gt(t,"string",o)&&a.push(r+o),n&>(t,"string",o)&&a.push(r+o)}return a}function Pr(e,t){let r=[];for(let n=0;nt?1:t>e?-1:0}function gt(e,t,r){return e.some(n=>n[t]===r)}function ft(e,t){return Number(String(e).slice(0,-t)+"9".repeat(t))}function ht(e,t){return e-e%Math.pow(10,t)}function dt(e){let[t=0,r=""]=e;return r||t>1?`{${t+(r?","+r:"")}}`:""}function Dr(e,t,r){return`[${e}${t-e==1?"":"-"}${t}]`}function pt(e){return/^-?(0+)\d/.test(e)}function Ur(e,t,r){if(!t.isPadded)return e;let n=Math.abs(t.maxLen-String(e).length),s=r.relaxZeros!==!1;switch(n){case 0:return"";case 1:return s?"0?":"0";case 2:return s?"0{0,2}":"00";default:return s?`0{0,${n}}`:`0{${n}}`}}pe.cache={};pe.clearCache=()=>pe.cache={};ut.exports=pe});var Ue=K((cs,At)=>{"use strict";var qr=require("util"),Rt=mt(),yt=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),Kr=e=>t=>e===!0?Number(t):String(t),De=e=>typeof e=="number"||typeof e=="string"&&e!=="",Ae=e=>Number.isInteger(+e),Ge=e=>{let t=`${e}`,r=-1;if(t[0]==="-"&&(t=t.slice(1)),t==="0")return!1;for(;t[++r]==="0";);return r>0},Wr=(e,t,r)=>typeof e=="string"||typeof t=="string"?!0:r.stringify===!0,jr=(e,t,r)=>{if(t>0){let n=e[0]==="-"?"-":"";n&&(e=e.slice(1)),e=n+e.padStart(n?t-1:t,"0")}return r===!1?String(e):e},_t=(e,t)=>{let r=e[0]==="-"?"-":"";for(r&&(e=e.slice(1),t--);e.length{e.negatives.sort((i,o)=>io?1:0),e.positives.sort((i,o)=>io?1:0);let r=t.capture?"":"?:",n="",s="",a;return e.positives.length&&(n=e.positives.join("|")),e.negatives.length&&(s=`-(${r}${e.negatives.join("|")})`),n&&s?a=`${n}|${s}`:a=n||s,t.wrap?`(${r}${a})`:a},bt=(e,t,r,n)=>{if(r)return Rt(e,t,I({wrap:!1},n));let s=String.fromCharCode(e);if(e===t)return s;let a=String.fromCharCode(t);return`[${s}-${a}]`},Et=(e,t,r)=>{if(Array.isArray(e)){let n=r.wrap===!0,s=r.capture?"":"?:";return n?`(${s}${e.join("|")})`:e.join("|")}return Rt(e,t,r)},xt=(...e)=>new RangeError("Invalid range arguments: "+qr.inspect(...e)),Ct=(e,t,r)=>{if(r.strictRanges===!0)throw xt([e,t]);return[]},Qr=(e,t)=>{if(t.strictRanges===!0)throw new TypeError(`Expected step "${e}" to be a number`);return[]},Xr=(e,t,r=1,n={})=>{let s=Number(e),a=Number(t);if(!Number.isInteger(s)||!Number.isInteger(a)){if(n.strictRanges===!0)throw xt([e,t]);return[]}s===0&&(s=0),a===0&&(a=0);let i=s>a,o=String(e),h=String(t),m=String(r);r=Math.max(Math.abs(r),1);let f=Ge(o)||Ge(h)||Ge(m),R=f?Math.max(o.length,h.length,m.length):0,p=f===!1&&Wr(e,t,n)===!1,v=n.transform||Kr(p);if(n.toRegex&&r===1)return bt(_t(e,R),_t(t,R),!0,n);let _={negatives:[],positives:[]},y=H=>_[H<0?"negatives":"positives"].push(Math.abs(H)),b=[],E=0;for(;i?s>=a:s<=a;)n.toRegex===!0&&r>1?y(s):b.push(jr(v(s,E),R,p)),s=i?s-r:s+r,E++;return n.toRegex===!0?r>1?Fr(_,n):Et(b,null,I({wrap:!1},n)):b},Zr=(e,t,r=1,n={})=>{if(!Ae(e)&&e.length>1||!Ae(t)&&t.length>1)return Ct(e,t,n);let s=n.transform||(p=>String.fromCharCode(p)),a=`${e}`.charCodeAt(0),i=`${t}`.charCodeAt(0),o=a>i,h=Math.min(a,i),m=Math.max(a,i);if(n.toRegex&&r===1)return bt(h,m,!1,n);let f=[],R=0;for(;o?a>=i:a<=i;)f.push(s(a,R)),a=o?a-r:a+r,R++;return n.toRegex===!0?Et(f,null,{wrap:!1,options:n}):f},ke=(e,t,r,n={})=>{if(t==null&&De(e))return[e];if(!De(e)||!De(t))return Ct(e,t,n);if(typeof r=="function")return ke(e,t,1,{transform:r});if(yt(r))return ke(e,t,0,r);let s=I({},n);return s.capture===!0&&(s.wrap=!0),r=r||s.step||1,Ae(r)?Ae(e)&&Ae(t)?Xr(e,t,r,s):Zr(e,t,Math.max(Math.abs(r),1),s):r!=null&&!yt(r)?Qr(r,s):ke(e,t,1,r)};At.exports=ke});var vt=K((ls,wt)=>{"use strict";var Yr=Ue(),St=He(),zr=(e,t={})=>{let r=(n,s={})=>{let a=St.isInvalidBrace(s),i=n.invalid===!0&&t.escapeInvalid===!0,o=a===!0||i===!0,h=t.escapeInvalid===!0?"\\":"",m="";if(n.isOpen===!0||n.isClose===!0)return h+n.value;if(n.type==="open")return o?h+n.value:"(";if(n.type==="close")return o?h+n.value:")";if(n.type==="comma")return n.prev.type==="comma"?"":o?n.value:"|";if(n.value)return n.value;if(n.nodes&&n.ranges>0){let f=St.reduce(n.nodes),R=Yr(...f,F(I({},t),{wrap:!1,toRegex:!0}));if(R.length!==0)return f.length>1&&R.length>1?`(${R})`:R}if(n.nodes)for(let f of n.nodes)m+=r(f,n);return m};return r(e)};wt.exports=zr});var kt=K((ps,Ht)=>{"use strict";var Vr=Ue(),$t=$e(),he=He(),fe=(e="",t="",r=!1)=>{let n=[];if(e=[].concat(e),t=[].concat(t),!t.length)return e;if(!e.length)return r?he.flatten(t).map(s=>`{${s}}`):t;for(let s of e)if(Array.isArray(s))for(let a of s)n.push(fe(a,t,r));else for(let a of t)r===!0&&typeof a=="string"&&(a=`{${a}}`),n.push(Array.isArray(a)?fe(s,a,r):s+a);return he.flatten(n)},Jr=(e,t={})=>{let r=t.rangeLimit===void 0?1e3:t.rangeLimit,n=(s,a={})=>{s.queue=[];let i=a,o=a.queue;for(;i.type!=="brace"&&i.type!=="root"&&i.parent;)i=i.parent,o=i.queue;if(s.invalid||s.dollar){o.push(fe(o.pop(),$t(s,t)));return}if(s.type==="brace"&&s.invalid!==!0&&s.nodes.length===2){o.push(fe(o.pop(),["{}"]));return}if(s.nodes&&s.ranges>0){let R=he.reduce(s.nodes);if(he.exceedsLimit(...R,t.step,r))throw new RangeError("expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.");let p=Vr(...R,t);p.length===0&&(p=$t(s,t)),o.push(fe(o.pop(),p)),s.nodes=[];return}let h=he.encloseBrace(s),m=s.queue,f=s;for(;f.type!=="brace"&&f.type!=="root"&&f.parent;)f=f.parent,m=f.queue;for(let R=0;R{"use strict";Tt.exports={MAX_LENGTH:1024*64,CHAR_0:"0",CHAR_9:"9",CHAR_UPPERCASE_A:"A",CHAR_LOWERCASE_A:"a",CHAR_UPPERCASE_Z:"Z",CHAR_LOWERCASE_Z:"z",CHAR_LEFT_PARENTHESES:"(",CHAR_RIGHT_PARENTHESES:")",CHAR_ASTERISK:"*",CHAR_AMPERSAND:"&",CHAR_AT:"@",CHAR_BACKSLASH:"\\",CHAR_BACKTICK:"`",CHAR_CARRIAGE_RETURN:"\r",CHAR_CIRCUMFLEX_ACCENT:"^",CHAR_COLON:":",CHAR_COMMA:",",CHAR_DOLLAR:"$",CHAR_DOT:".",CHAR_DOUBLE_QUOTE:'"',CHAR_EQUAL:"=",CHAR_EXCLAMATION_MARK:"!",CHAR_FORM_FEED:"\f",CHAR_FORWARD_SLASH:"/",CHAR_HASH:"#",CHAR_HYPHEN_MINUS:"-",CHAR_LEFT_ANGLE_BRACKET:"<",CHAR_LEFT_CURLY_BRACE:"{",CHAR_LEFT_SQUARE_BRACKET:"[",CHAR_LINE_FEED:`
7 | `,CHAR_NO_BREAK_SPACE:"\xA0",CHAR_PERCENT:"%",CHAR_PLUS:"+",CHAR_QUESTION_MARK:"?",CHAR_RIGHT_ANGLE_BRACKET:">",CHAR_RIGHT_CURLY_BRACE:"}",CHAR_RIGHT_SQUARE_BRACKET:"]",CHAR_SEMICOLON:";",CHAR_SINGLE_QUOTE:"'",CHAR_SPACE:" ",CHAR_TAB:" ",CHAR_UNDERSCORE:"_",CHAR_VERTICAL_LINE:"|",CHAR_ZERO_WIDTH_NOBREAK_SPACE:"\uFEFF"}});var Mt=K((hs,Ot)=>{"use strict";var en=$e(),{MAX_LENGTH:Nt,CHAR_BACKSLASH:qe,CHAR_BACKTICK:tn,CHAR_COMMA:rn,CHAR_DOT:nn,CHAR_LEFT_PARENTHESES:sn,CHAR_RIGHT_PARENTHESES:an,CHAR_LEFT_CURLY_BRACE:on,CHAR_RIGHT_CURLY_BRACE:un,CHAR_LEFT_SQUARE_BRACKET:It,CHAR_RIGHT_SQUARE_BRACKET:Bt,CHAR_DOUBLE_QUOTE:cn,CHAR_SINGLE_QUOTE:ln,CHAR_NO_BREAK_SPACE:pn,CHAR_ZERO_WIDTH_NOBREAK_SPACE:fn}=Lt(),hn=(e,t={})=>{if(typeof e!="string")throw new TypeError("Expected a string");let r=t||{},n=typeof r.maxLength=="number"?Math.min(Nt,r.maxLength):Nt;if(e.length>n)throw new SyntaxError(`Input length (${e.length}), exceeds max characters (${n})`);let s={type:"root",input:e,nodes:[]},a=[s],i=s,o=s,h=0,m=e.length,f=0,R=0,p,v={},_=()=>e[f++],y=b=>{if(b.type==="text"&&o.type==="dot"&&(o.type="text"),o&&o.type==="text"&&b.type==="text"){o.value+=b.value;return}return i.nodes.push(b),b.parent=i,b.prev=o,o=b,b};for(y({type:"bos"});f0){if(i.ranges>0){i.ranges=0;let b=i.nodes.shift();i.nodes=[b,{type:"text",value:en(i)}]}y({type:"comma",value:p}),i.commas++;continue}if(p===nn&&R>0&&i.commas===0){let b=i.nodes;if(R===0||b.length===0){y({type:"text",value:p});continue}if(o.type==="dot"){if(i.range=[],o.value+=p,o.type="range",i.nodes.length!==3&&i.nodes.length!==5){i.invalid=!0,i.ranges=0,o.type="text";continue}i.ranges++,i.args=[];continue}if(o.type==="range"){b.pop();let E=b[b.length-1];E.value+=o.value+p,o=E,i.ranges--;continue}y({type:"dot",value:p});continue}y({type:"text",value:p})}do if(i=a.pop(),i.type!=="root"){i.nodes.forEach(H=>{H.nodes||(H.type==="open"&&(H.isOpen=!0),H.type==="close"&&(H.isClose=!0),H.nodes||(H.type="text"),H.invalid=!0)});let b=a[a.length-1],E=b.nodes.indexOf(i);b.nodes.splice(E,1,...i.nodes)}while(a.length>0);return y({type:"eos"}),s};Ot.exports=hn});var Gt=K((ds,Pt)=>{"use strict";var Dt=$e(),dn=vt(),gn=kt(),mn=Mt(),z=(e,t={})=>{let r=[];if(Array.isArray(e))for(let n of e){let s=z.create(n,t);Array.isArray(s)?r.push(...s):r.push(s)}else r=[].concat(z.create(e,t));return t&&t.expand===!0&&t.nodupes===!0&&(r=[...new Set(r)]),r};z.parse=(e,t={})=>mn(e,t);z.stringify=(e,t={})=>typeof e=="string"?Dt(z.parse(e,t),t):Dt(e,t);z.compile=(e,t={})=>(typeof e=="string"&&(e=z.parse(e,t)),dn(e,t));z.expand=(e,t={})=>{typeof e=="string"&&(e=z.parse(e,t));let r=gn(e,t);return t.noempty===!0&&(r=r.filter(Boolean)),t.nodupes===!0&&(r=[...new Set(r)]),r};z.create=(e,t={})=>e===""||e.length<3?[e]:t.expand!==!0?z.compile(e,t):z.expand(e,t);Pt.exports=z});var Re=K((gs,Ut)=>{"use strict";var An=require("path"),se="\\\\/",qt=`[^${se}]`,ue="\\.",Rn="\\+",yn="\\?",Te="\\/",_n="(?=.)",Kt="[^/]",Ke=`(?:${Te}|$)`,Wt=`(?:^|${Te})`,We=`${ue}{1,2}${Ke}`,bn=`(?!${ue})`,En=`(?!${Wt}${We})`,xn=`(?!${ue}{0,1}${Ke})`,Cn=`(?!${We})`,wn=`[^.${Te}]`,Sn=`${Kt}*?`,jt={DOT_LITERAL:ue,PLUS_LITERAL:Rn,QMARK_LITERAL:yn,SLASH_LITERAL:Te,ONE_CHAR:_n,QMARK:Kt,END_ANCHOR:Ke,DOTS_SLASH:We,NO_DOT:bn,NO_DOTS:En,NO_DOT_SLASH:xn,NO_DOTS_SLASH:Cn,QMARK_NO_DOT:wn,STAR:Sn,START_ANCHOR:Wt},vn=F(I({},jt),{SLASH_LITERAL:`[${se}]`,QMARK:qt,STAR:`${qt}*?`,DOTS_SLASH:`${ue}{1,2}(?:[${se}]|$)`,NO_DOT:`(?!${ue})`,NO_DOTS:`(?!(?:^|[${se}])${ue}{1,2}(?:[${se}]|$))`,NO_DOT_SLASH:`(?!${ue}{0,1}(?:[${se}]|$))`,NO_DOTS_SLASH:`(?!${ue}{1,2}(?:[${se}]|$))`,QMARK_NO_DOT:`[^.${se}]`,START_ANCHOR:`(?:^|[${se}])`,END_ANCHOR:`(?:[${se}]|$)`}),Hn={alnum:"a-zA-Z0-9",alpha:"a-zA-Z",ascii:"\\x00-\\x7F",blank:" \\t",cntrl:"\\x00-\\x1F\\x7F",digit:"0-9",graph:"\\x21-\\x7E",lower:"a-z",print:"\\x20-\\x7E ",punct:"\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~",space:" \\t\\r\\n\\v\\f",upper:"A-Z",word:"A-Za-z0-9_",xdigit:"A-Fa-f0-9"};Ut.exports={MAX_LENGTH:1024*64,POSIX_REGEX_SOURCE:Hn,REGEX_BACKSLASH:/\\(?![*+?^${}(|)[\]])/g,REGEX_NON_SPECIAL_CHARS:/^[^@![\].,$*+?^{}()|\\/]+/,REGEX_SPECIAL_CHARS:/[-*+?.^${}(|)[\]]/,REGEX_SPECIAL_CHARS_BACKREF:/(\\?)((\W)(\3*))/g,REGEX_SPECIAL_CHARS_GLOBAL:/([-*+?.^${}(|)[\]])/g,REGEX_REMOVE_BACKSLASH:/(?:\[.*?[^\\]\]|\\(?=.))/g,REPLACEMENTS:{"***":"*","**/**":"**","**/**/**":"**"},CHAR_0:48,CHAR_9:57,CHAR_UPPERCASE_A:65,CHAR_LOWERCASE_A:97,CHAR_UPPERCASE_Z:90,CHAR_LOWERCASE_Z:122,CHAR_LEFT_PARENTHESES:40,CHAR_RIGHT_PARENTHESES:41,CHAR_ASTERISK:42,CHAR_AMPERSAND:38,CHAR_AT:64,CHAR_BACKWARD_SLASH:92,CHAR_CARRIAGE_RETURN:13,CHAR_CIRCUMFLEX_ACCENT:94,CHAR_COLON:58,CHAR_COMMA:44,CHAR_DOT:46,CHAR_DOUBLE_QUOTE:34,CHAR_EQUAL:61,CHAR_EXCLAMATION_MARK:33,CHAR_FORM_FEED:12,CHAR_FORWARD_SLASH:47,CHAR_GRAVE_ACCENT:96,CHAR_HASH:35,CHAR_HYPHEN_MINUS:45,CHAR_LEFT_ANGLE_BRACKET:60,CHAR_LEFT_CURLY_BRACE:123,CHAR_LEFT_SQUARE_BRACKET:91,CHAR_LINE_FEED:10,CHAR_NO_BREAK_SPACE:160,CHAR_PERCENT:37,CHAR_PLUS:43,CHAR_QUESTION_MARK:63,CHAR_RIGHT_ANGLE_BRACKET:62,CHAR_RIGHT_CURLY_BRACE:125,CHAR_RIGHT_SQUARE_BRACKET:93,CHAR_SEMICOLON:59,CHAR_SINGLE_QUOTE:39,CHAR_SPACE:32,CHAR_TAB:9,CHAR_UNDERSCORE:95,CHAR_VERTICAL_LINE:124,CHAR_ZERO_WIDTH_NOBREAK_SPACE:65279,SEP:An.sep,extglobChars(e){return{"!":{type:"negate",open:"(?:(?!(?:",close:`))${e.STAR})`},"?":{type:"qmark",open:"(?:",close:")?"},"+":{type:"plus",open:"(?:",close:")+"},"*":{type:"star",open:"(?:",close:")*"},"@":{type:"at",open:"(?:",close:")"}}},globChars(e){return e===!0?vn:jt}}});var ye=K(X=>{"use strict";var $n=require("path"),kn=process.platform==="win32",{REGEX_BACKSLASH:Tn,REGEX_REMOVE_BACKSLASH:Ln,REGEX_SPECIAL_CHARS:On,REGEX_SPECIAL_CHARS_GLOBAL:Nn}=Re();X.isObject=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);X.hasRegexChars=e=>On.test(e);X.isRegexChar=e=>e.length===1&&X.hasRegexChars(e);X.escapeRegex=e=>e.replace(Nn,"\\$1");X.toPosixSlashes=e=>e.replace(Tn,"/");X.removeBackslashes=e=>e.replace(Ln,t=>t==="\\"?"":t);X.supportsLookbehinds=()=>{let e=process.version.slice(1).split(".").map(Number);return e.length===3&&e[0]>=9||e[0]===8&&e[1]>=10};X.isWindows=e=>e&&typeof e.windows=="boolean"?e.windows:kn===!0||$n.sep==="\\";X.escapeLast=(e,t,r)=>{let n=e.lastIndexOf(t,r);return n===-1?e:e[n-1]==="\\"?X.escapeLast(e,t,n-1):`${e.slice(0,n)}\\${e.slice(n)}`};X.removePrefix=(e,t={})=>{let r=e;return r.startsWith("./")&&(r=r.slice(2),t.prefix="./"),r};X.wrapOutput=(e,t={},r={})=>{let n=r.contains?"":"^",s=r.contains?"":"$",a=`${n}(?:${e})${s}`;return t.negated===!0&&(a=`(?:^(?!${a}).*$)`),a}});var er=K((As,Ft)=>{"use strict";var Qt=ye(),{CHAR_ASTERISK:je,CHAR_AT:In,CHAR_BACKWARD_SLASH:_e,CHAR_COMMA:Bn,CHAR_DOT:Fe,CHAR_EXCLAMATION_MARK:Xt,CHAR_FORWARD_SLASH:Zt,CHAR_LEFT_CURLY_BRACE:Qe,CHAR_LEFT_PARENTHESES:Xe,CHAR_LEFT_SQUARE_BRACKET:Mn,CHAR_PLUS:Pn,CHAR_QUESTION_MARK:Yt,CHAR_RIGHT_CURLY_BRACE:Dn,CHAR_RIGHT_PARENTHESES:zt,CHAR_RIGHT_SQUARE_BRACKET:Gn}=Re(),Vt=e=>e===Zt||e===_e,Jt=e=>{e.isPrefix!==!0&&(e.depth=e.isGlobstar?Infinity:1)},Un=(e,t)=>{let r=t||{},n=e.length-1,s=r.parts===!0||r.scanToEnd===!0,a=[],i=[],o=[],h=e,m=-1,f=0,R=0,p=!1,v=!1,_=!1,y=!1,b=!1,E=!1,H=!1,L=!1,k=!1,J=0,ie,g,w={value:"",depth:0,isGlob:!1},D=()=>m>=n,W=()=>h.charCodeAt(m+1),l=()=>(ie=g,h.charCodeAt(++m));for(;m0&&(T=h.slice(0,f),h=h.slice(f),R-=f),x&&_===!0&&R>0?(x=h.slice(0,R),U=h.slice(R)):_===!0?(x="",U=h):x=h,x&&x!==""&&x!=="/"&&x!==h&&Vt(x.charCodeAt(x.length-1))&&(x=x.slice(0,-1)),r.unescape===!0&&(U&&(U=Qt.removeBackslashes(U)),x&&H===!0&&(x=Qt.removeBackslashes(x)));let u={prefix:T,input:e,start:f,base:x,glob:U,isBrace:p,isBracket:v,isGlob:_,isExtglob:y,isGlobstar:b,negated:L};if(r.tokens===!0&&(u.maxDepth=0,Vt(g)||i.push(w),u.tokens=i),r.parts===!0||r.tokens===!0){let c;for(let $=0;${"use strict";var Le=Re(),V=ye(),{MAX_LENGTH:Oe,POSIX_REGEX_SOURCE:qn,REGEX_NON_SPECIAL_CHARS:Kn,REGEX_SPECIAL_CHARS_BACKREF:Wn,REPLACEMENTS:rr}=Le,jn=(e,t)=>{if(typeof t.expandRange=="function")return t.expandRange(...e,t);e.sort();let r=`[${e.join("-")}]`;try{new RegExp(r)}catch(n){return e.map(s=>V.escapeRegex(s)).join("..")}return r},de=(e,t)=>`Missing ${e}: "${t}" - use "\\\\${t}" to match literal characters`,nr=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");e=rr[e]||e;let r=I({},t),n=typeof r.maxLength=="number"?Math.min(Oe,r.maxLength):Oe,s=e.length;if(s>n)throw new SyntaxError(`Input length: ${s}, exceeds maximum allowed length: ${n}`);let a={type:"bos",value:"",output:r.prepend||""},i=[a],o=r.capture?"":"?:",h=V.isWindows(t),m=Le.globChars(h),f=Le.extglobChars(m),{DOT_LITERAL:R,PLUS_LITERAL:p,SLASH_LITERAL:v,ONE_CHAR:_,DOTS_SLASH:y,NO_DOT:b,NO_DOT_SLASH:E,NO_DOTS_SLASH:H,QMARK:L,QMARK_NO_DOT:k,STAR:J,START_ANCHOR:ie}=m,g=A=>`(${o}(?:(?!${ie}${A.dot?y:R}).)*?)`,w=r.dot?"":b,D=r.dot?L:k,W=r.bash===!0?g(r):J;r.capture&&(W=`(${W})`),typeof r.noext=="boolean"&&(r.noextglob=r.noext);let l={input:e,index:-1,start:0,dot:r.dot===!0,consumed:"",output:"",prefix:"",backtrack:!1,negated:!1,brackets:0,braces:0,parens:0,quotes:0,globstar:!1,tokens:i};e=V.removePrefix(e,l),s=e.length;let x=[],T=[],U=[],u=a,c,$=()=>l.index===s-1,B=l.peek=(A=1)=>e[l.index+A],Y=l.advance=()=>e[++l.index],re=()=>e.slice(l.index+1),oe=(A="",O=0)=>{l.consumed+=A,l.index+=O},xe=A=>{l.output+=A.output!=null?A.output:A.value,oe(A.value)},xr=()=>{let A=1;for(;B()==="!"&&(B(2)!=="("||B(3)==="?");)Y(),l.start++,A++;return A%2==0?!1:(l.negated=!0,l.start++,!0)},Ce=A=>{l[A]++,U.push(A)},ce=A=>{l[A]--,U.pop()},C=A=>{if(u.type==="globstar"){let O=l.braces>0&&(A.type==="comma"||A.type==="brace"),d=A.extglob===!0||x.length&&(A.type==="pipe"||A.type==="paren");A.type!=="slash"&&A.type!=="paren"&&!O&&!d&&(l.output=l.output.slice(0,-u.output.length),u.type="star",u.value="*",u.output=W,l.output+=u.output)}if(x.length&&A.type!=="paren"&&!f[A.value]&&(x[x.length-1].inner+=A.value),(A.value||A.output)&&xe(A),u&&u.type==="text"&&A.type==="text"){u.value+=A.value,u.output=(u.output||"")+A.value;return}A.prev=u,i.push(A),u=A},we=(A,O)=>{let d=F(I({},f[O]),{conditions:1,inner:""});d.prev=u,d.parens=l.parens,d.output=l.output;let S=(r.capture?"(":"")+d.open;Ce("parens"),C({type:A,value:O,output:l.output?"":_}),C({type:"paren",extglob:!0,value:Y(),output:S}),x.push(d)},Cr=A=>{let O=A.close+(r.capture?")":"");if(A.type==="negate"){let d=W;A.inner&&A.inner.length>1&&A.inner.includes("/")&&(d=g(r)),(d!==W||$()||/^\)+$/.test(re()))&&(O=A.close=`)$))${d}`),A.prev.type==="bos"&&(l.negatedExtglob=!0)}C({type:"paren",extglob:!0,value:c,output:O}),ce("parens")};if(r.fastpaths!==!1&&!/(^[*!]|[/()[\]{}"])/.test(e)){let A=!1,O=e.replace(Wn,(d,S,M,j,q,Me)=>j==="\\"?(A=!0,d):j==="?"?S?S+j+(q?L.repeat(q.length):""):Me===0?D+(q?L.repeat(q.length):""):L.repeat(M.length):j==="."?R.repeat(M.length):j==="*"?S?S+j+(q?W:""):W:S?d:`\\${d}`);return A===!0&&(r.unescape===!0?O=O.replace(/\\/g,""):O=O.replace(/\\+/g,d=>d.length%2==0?"\\\\":d?"\\":"")),O===e&&r.contains===!0?(l.output=e,l):(l.output=V.wrapOutput(O,l,t),l)}for(;!$();){if(c=Y(),c==="\0")continue;if(c==="\\"){let d=B();if(d==="/"&&r.bash!==!0||d==="."||d===";")continue;if(!d){c+="\\",C({type:"text",value:c});continue}let S=/^\\+/.exec(re()),M=0;if(S&&S[0].length>2&&(M=S[0].length,l.index+=M,M%2!=0&&(c+="\\")),r.unescape===!0?c=Y()||"":c+=Y()||"",l.brackets===0){C({type:"text",value:c});continue}}if(l.brackets>0&&(c!=="]"||u.value==="["||u.value==="[^")){if(r.posix!==!1&&c===":"){let d=u.value.slice(1);if(d.includes("[")&&(u.posix=!0,d.includes(":"))){let S=u.value.lastIndexOf("["),M=u.value.slice(0,S),j=u.value.slice(S+2),q=qn[j];if(q){u.value=M+q,l.backtrack=!0,Y(),!a.output&&i.indexOf(u)===1&&(a.output=_);continue}}}(c==="["&&B()!==":"||c==="-"&&B()==="]")&&(c=`\\${c}`),c==="]"&&(u.value==="["||u.value==="[^")&&(c=`\\${c}`),r.posix===!0&&c==="!"&&u.value==="["&&(c="^"),u.value+=c,xe({value:c});continue}if(l.quotes===1&&c!=='"'){c=V.escapeRegex(c),u.value+=c,xe({value:c});continue}if(c==='"'){l.quotes=l.quotes===1?0:1,r.keepQuotes===!0&&C({type:"text",value:c});continue}if(c==="("){Ce("parens"),C({type:"paren",value:c});continue}if(c===")"){if(l.parens===0&&r.strictBrackets===!0)throw new SyntaxError(de("opening","("));let d=x[x.length-1];if(d&&l.parens===d.parens+1){Cr(x.pop());continue}C({type:"paren",value:c,output:l.parens?")":"\\)"}),ce("parens");continue}if(c==="["){if(r.nobracket===!0||!re().includes("]")){if(r.nobracket!==!0&&r.strictBrackets===!0)throw new SyntaxError(de("closing","]"));c=`\\${c}`}else Ce("brackets");C({type:"bracket",value:c});continue}if(c==="]"){if(r.nobracket===!0||u&&u.type==="bracket"&&u.value.length===1){C({type:"text",value:c,output:`\\${c}`});continue}if(l.brackets===0){if(r.strictBrackets===!0)throw new SyntaxError(de("opening","["));C({type:"text",value:c,output:`\\${c}`});continue}ce("brackets");let d=u.value.slice(1);if(u.posix!==!0&&d[0]==="^"&&!d.includes("/")&&(c=`/${c}`),u.value+=c,xe({value:c}),r.literalBrackets===!1||V.hasRegexChars(d))continue;let S=V.escapeRegex(u.value);if(l.output=l.output.slice(0,-u.value.length),r.literalBrackets===!0){l.output+=S,u.value=S;continue}u.value=`(${o}${S}|${u.value})`,l.output+=u.value;continue}if(c==="{"&&r.nobrace!==!0){Ce("braces");let d={type:"brace",value:c,output:"(",outputIndex:l.output.length,tokensIndex:l.tokens.length};T.push(d),C(d);continue}if(c==="}"){let d=T[T.length-1];if(r.nobrace===!0||!d){C({type:"text",value:c,output:c});continue}let S=")";if(d.dots===!0){let M=i.slice(),j=[];for(let q=M.length-1;q>=0&&(i.pop(),M[q].type!=="brace");q--)M[q].type!=="dots"&&j.unshift(M[q].value);S=jn(j,r),l.backtrack=!0}if(d.comma!==!0&&d.dots!==!0){let M=l.output.slice(0,d.outputIndex),j=l.tokens.slice(d.tokensIndex);d.value=d.output="\\{",c=S="\\}",l.output=M;for(let q of j)l.output+=q.output||q.value}C({type:"brace",value:c,output:S}),ce("braces"),T.pop();continue}if(c==="|"){x.length>0&&x[x.length-1].conditions++,C({type:"text",value:c});continue}if(c===","){let d=c,S=T[T.length-1];S&&U[U.length-1]==="braces"&&(S.comma=!0,d="|"),C({type:"comma",value:c,output:d});continue}if(c==="/"){if(u.type==="dot"&&l.index===l.start+1){l.start=l.index+1,l.consumed="",l.output="",i.pop(),u=a;continue}C({type:"slash",value:c,output:v});continue}if(c==="."){if(l.braces>0&&u.type==="dot"){u.value==="."&&(u.output=R);let d=T[T.length-1];u.type="dots",u.output+=c,u.value+=c,d.dots=!0;continue}if(l.braces+l.parens===0&&u.type!=="bos"&&u.type!=="slash"){C({type:"text",value:c,output:R});continue}C({type:"dot",value:c,output:R});continue}if(c==="?"){if(!(u&&u.value==="(")&&r.noextglob!==!0&&B()==="("&&B(2)!=="?"){we("qmark",c);continue}if(u&&u.type==="paren"){let S=B(),M=c;if(S==="<"&&!V.supportsLookbehinds())throw new Error("Node.js v10 or higher is required for regex lookbehinds");(u.value==="("&&!/[!=<:]/.test(S)||S==="<"&&!/<([!=]|\w+>)/.test(re()))&&(M=`\\${c}`),C({type:"text",value:c,output:M});continue}if(r.dot!==!0&&(u.type==="slash"||u.type==="bos")){C({type:"qmark",value:c,output:k});continue}C({type:"qmark",value:c,output:L});continue}if(c==="!"){if(r.noextglob!==!0&&B()==="("&&(B(2)!=="?"||!/[!=<:]/.test(B(3)))){we("negate",c);continue}if(r.nonegate!==!0&&l.index===0){xr();continue}}if(c==="+"){if(r.noextglob!==!0&&B()==="("&&B(2)!=="?"){we("plus",c);continue}if(u&&u.value==="("||r.regex===!1){C({type:"plus",value:c,output:p});continue}if(u&&(u.type==="bracket"||u.type==="paren"||u.type==="brace")||l.parens>0){C({type:"plus",value:c});continue}C({type:"plus",value:p});continue}if(c==="@"){if(r.noextglob!==!0&&B()==="("&&B(2)!=="?"){C({type:"at",extglob:!0,value:c,output:""});continue}C({type:"text",value:c});continue}if(c!=="*"){(c==="$"||c==="^")&&(c=`\\${c}`);let d=Kn.exec(re());d&&(c+=d[0],l.index+=d[0].length),C({type:"text",value:c});continue}if(u&&(u.type==="globstar"||u.star===!0)){u.type="star",u.star=!0,u.value+=c,u.output=W,l.backtrack=!0,l.globstar=!0,oe(c);continue}let A=re();if(r.noextglob!==!0&&/^\([^?]/.test(A)){we("star",c);continue}if(u.type==="star"){if(r.noglobstar===!0){oe(c);continue}let d=u.prev,S=d.prev,M=d.type==="slash"||d.type==="bos",j=S&&(S.type==="star"||S.type==="globstar");if(r.bash===!0&&(!M||A[0]&&A[0]!=="/")){C({type:"star",value:c,output:""});continue}let q=l.braces>0&&(d.type==="comma"||d.type==="brace"),Me=x.length&&(d.type==="pipe"||d.type==="paren");if(!M&&d.type!=="paren"&&!q&&!Me){C({type:"star",value:c,output:""});continue}for(;A.slice(0,3)==="/**";){let Se=e[l.index+4];if(Se&&Se!=="/")break;A=A.slice(3),oe("/**",3)}if(d.type==="bos"&&$()){u.type="globstar",u.value+=c,u.output=g(r),l.output=u.output,l.globstar=!0,oe(c);continue}if(d.type==="slash"&&d.prev.type!=="bos"&&!j&&$()){l.output=l.output.slice(0,-(d.output+u.output).length),d.output=`(?:${d.output}`,u.type="globstar",u.output=g(r)+(r.strictSlashes?")":"|$)"),u.value+=c,l.globstar=!0,l.output+=d.output+u.output,oe(c);continue}if(d.type==="slash"&&d.prev.type!=="bos"&&A[0]==="/"){let Se=A[1]!==void 0?"|$":"";l.output=l.output.slice(0,-(d.output+u.output).length),d.output=`(?:${d.output}`,u.type="globstar",u.output=`${g(r)}${v}|${v}${Se})`,u.value+=c,l.output+=d.output+u.output,l.globstar=!0,oe(c+Y()),C({type:"slash",value:"/",output:""});continue}if(d.type==="bos"&&A[0]==="/"){u.type="globstar",u.value+=c,u.output=`(?:^|${v}|${g(r)}${v})`,l.output=u.output,l.globstar=!0,oe(c+Y()),C({type:"slash",value:"/",output:""});continue}l.output=l.output.slice(0,-u.output.length),u.type="globstar",u.output=g(r),u.value+=c,l.output+=u.output,l.globstar=!0,oe(c);continue}let O={type:"star",value:c,output:W};if(r.bash===!0){O.output=".*?",(u.type==="bos"||u.type==="slash")&&(O.output=w+O.output),C(O);continue}if(u&&(u.type==="bracket"||u.type==="paren")&&r.regex===!0){O.output=c,C(O);continue}(l.index===l.start||u.type==="slash"||u.type==="dot")&&(u.type==="dot"?(l.output+=E,u.output+=E):r.dot===!0?(l.output+=H,u.output+=H):(l.output+=w,u.output+=w),B()!=="*"&&(l.output+=_,u.output+=_)),C(O)}for(;l.brackets>0;){if(r.strictBrackets===!0)throw new SyntaxError(de("closing","]"));l.output=V.escapeLast(l.output,"["),ce("brackets")}for(;l.parens>0;){if(r.strictBrackets===!0)throw new SyntaxError(de("closing",")"));l.output=V.escapeLast(l.output,"("),ce("parens")}for(;l.braces>0;){if(r.strictBrackets===!0)throw new SyntaxError(de("closing","}"));l.output=V.escapeLast(l.output,"{"),ce("braces")}if(r.strictSlashes!==!0&&(u.type==="star"||u.type==="bracket")&&C({type:"maybe_slash",value:"",output:`${v}?`}),l.backtrack===!0){l.output="";for(let A of l.tokens)l.output+=A.output!=null?A.output:A.value,A.suffix&&(l.output+=A.suffix)}return l};nr.fastpaths=(e,t)=>{let r=I({},t),n=typeof r.maxLength=="number"?Math.min(Oe,r.maxLength):Oe,s=e.length;if(s>n)throw new SyntaxError(`Input length: ${s}, exceeds maximum allowed length: ${n}`);e=rr[e]||e;let a=V.isWindows(t),{DOT_LITERAL:i,SLASH_LITERAL:o,ONE_CHAR:h,DOTS_SLASH:m,NO_DOT:f,NO_DOTS:R,NO_DOTS_SLASH:p,STAR:v,START_ANCHOR:_}=Le.globChars(a),y=r.dot?R:f,b=r.dot?p:f,E=r.capture?"":"?:",H={negated:!1,prefix:""},L=r.bash===!0?".*?":v;r.capture&&(L=`(${L})`);let k=w=>w.noglobstar===!0?L:`(${E}(?:(?!${_}${w.dot?m:i}).)*?)`,J=w=>{switch(w){case"*":return`${y}${h}${L}`;case".*":return`${i}${h}${L}`;case"*.*":return`${y}${L}${i}${h}${L}`;case"*/*":return`${y}${L}${o}${h}${b}${L}`;case"**":return y+k(r);case"**/*":return`(?:${y}${k(r)}${o})?${b}${h}${L}`;case"**/*.*":return`(?:${y}${k(r)}${o})?${b}${L}${i}${h}${L}`;case"**/.*":return`(?:${y}${k(r)}${o})?${i}${h}${L}`;default:{let D=/^(.*?)\.(\w+)$/.exec(w);if(!D)return;let W=J(D[1]);return W?W+i+D[2]:void 0}}},ie=V.removePrefix(e,H),g=J(ie);return g&&r.strictSlashes!==!0&&(g+=`${o}?`),g};tr.exports=nr});var ir=K((ys,ar)=>{"use strict";var Fn=require("path"),Qn=er(),Ze=sr(),Ye=ye(),Xn=Re(),Zn=e=>e&&typeof e=="object"&&!Array.isArray(e),P=(e,t,r=!1)=>{if(Array.isArray(e)){let f=e.map(p=>P(p,t,r));return p=>{for(let v of f){let _=v(p);if(_)return _}return!1}}let n=Zn(e)&&e.tokens&&e.input;if(e===""||typeof e!="string"&&!n)throw new TypeError("Expected pattern to be a non-empty string");let s=t||{},a=Ye.isWindows(t),i=n?P.compileRe(e,t):P.makeRe(e,t,!1,!0),o=i.state;delete i.state;let h=()=>!1;if(s.ignore){let f=F(I({},t),{ignore:null,onMatch:null,onResult:null});h=P(s.ignore,f,r)}let m=(f,R=!1)=>{let{isMatch:p,match:v,output:_}=P.test(f,i,t,{glob:e,posix:a}),y={glob:e,state:o,regex:i,posix:a,input:f,output:_,match:v,isMatch:p};return typeof s.onResult=="function"&&s.onResult(y),p===!1?(y.isMatch=!1,R?y:!1):h(f)?(typeof s.onIgnore=="function"&&s.onIgnore(y),y.isMatch=!1,R?y:!1):(typeof s.onMatch=="function"&&s.onMatch(y),R?y:!0)};return r&&(m.state=o),m};P.test=(e,t,r,{glob:n,posix:s}={})=>{if(typeof e!="string")throw new TypeError("Expected input to be a string");if(e==="")return{isMatch:!1,output:""};let a=r||{},i=a.format||(s?Ye.toPosixSlashes:null),o=e===n,h=o&&i?i(e):e;return o===!1&&(h=i?i(e):e,o=h===n),(o===!1||a.capture===!0)&&(a.matchBase===!0||a.basename===!0?o=P.matchBase(e,t,r,s):o=t.exec(h)),{isMatch:Boolean(o),match:o,output:h}};P.matchBase=(e,t,r,n=Ye.isWindows(r))=>(t instanceof RegExp?t:P.makeRe(t,r)).test(Fn.basename(e));P.isMatch=(e,t,r)=>P(t,r)(e);P.parse=(e,t)=>Array.isArray(e)?e.map(r=>P.parse(r,t)):Ze(e,F(I({},t),{fastpaths:!1}));P.scan=(e,t)=>Qn(e,t);P.compileRe=(e,t,r=!1,n=!1)=>{if(r===!0)return e.output;let s=t||{},a=s.contains?"":"^",i=s.contains?"":"$",o=`${a}(?:${e.output})${i}`;e&&e.negated===!0&&(o=`^(?!${o}).*$`);let h=P.toRegex(o,t);return n===!0&&(h.state=e),h};P.makeRe=(e,t,r=!1,n=!1)=>{if(!e||typeof e!="string")throw new TypeError("Expected a non-empty string");let s=t||{},a={negated:!1,fastpaths:!0},i="",o;return e.startsWith("./")&&(e=e.slice(2),i=a.prefix="./"),s.fastpaths!==!1&&(e[0]==="."||e[0]==="*")&&(o=Ze.fastpaths(e,t)),o===void 0?(a=Ze(e,t),a.prefix=i+(a.prefix||"")):a.output=o,P.compileRe(a,t,r,n)};P.toRegex=(e,t)=>{try{let r=t||{};return new RegExp(e,r.flags||(r.nocase?"i":""))}catch(r){if(t&&t.debug===!0)throw r;return/$^/}};P.constants=Xn;ar.exports=P});var ur=K((_s,or)=>{"use strict";or.exports=ir()});var hr=K((bs,cr)=>{"use strict";var lr=require("util"),pr=Gt(),ae=ur(),ze=ye(),fr=e=>typeof e=="string"&&(e===""||e==="./"),N=(e,t,r)=>{t=[].concat(t),e=[].concat(e);let n=new Set,s=new Set,a=new Set,i=0,o=f=>{a.add(f.output),r&&r.onResult&&r.onResult(f)};for(let f=0;f!n.has(f));if(r&&m.length===0){if(r.failglob===!0)throw new Error(`No matches found for "${t.join(", ")}"`);if(r.nonull===!0||r.nullglob===!0)return r.unescape?t.map(f=>f.replace(/\\/g,"")):t}return m};N.match=N;N.matcher=(e,t)=>ae(e,t);N.isMatch=(e,t,r)=>ae(t,r)(e);N.any=N.isMatch;N.not=(e,t,r={})=>{t=[].concat(t).map(String);let n=new Set,s=[],a=o=>{r.onResult&&r.onResult(o),s.push(o.output)},i=N(e,t,F(I({},r),{onResult:a}));for(let o of s)i.includes(o)||n.add(o);return[...n]};N.contains=(e,t,r)=>{if(typeof e!="string")throw new TypeError(`Expected a string: "${lr.inspect(e)}"`);if(Array.isArray(t))return t.some(n=>N.contains(e,n,r));if(typeof t=="string"){if(fr(e)||fr(t))return!1;if(e.includes(t)||e.startsWith("./")&&e.slice(2).includes(t))return!0}return N.isMatch(e,t,F(I({},r),{contains:!0}))};N.matchKeys=(e,t,r)=>{if(!ze.isObject(e))throw new TypeError("Expected the first argument to be an object");let n=N(Object.keys(e),t,r),s={};for(let a of n)s[a]=e[a];return s};N.some=(e,t,r)=>{let n=[].concat(e);for(let s of[].concat(t)){let a=ae(String(s),r);if(n.some(i=>a(i)))return!0}return!1};N.every=(e,t,r)=>{let n=[].concat(e);for(let s of[].concat(t)){let a=ae(String(s),r);if(!n.every(i=>a(i)))return!1}return!0};N.all=(e,t,r)=>{if(typeof e!="string")throw new TypeError(`Expected a string: "${lr.inspect(e)}"`);return[].concat(t).every(n=>ae(n,r)(e))};N.capture=(e,t,r)=>{let n=ze.isWindows(r),a=ae.makeRe(String(e),F(I({},r),{capture:!0})).exec(n?ze.toPosixSlashes(t):t);if(a)return a.slice(1).map(i=>i===void 0?"":i)};N.makeRe=(...e)=>ae.makeRe(...e);N.scan=(...e)=>ae.scan(...e);N.parse=(e,t)=>{let r=[];for(let n of[].concat(e||[]))for(let s of pr(String(n),t))r.push(ae.parse(s,t));return r};N.braces=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");return t&&t.nobrace===!0||!/\{.*\}/.test(e)?[e]:pr(e,t)};N.braceExpand=(e,t)=>{if(typeof e!="string")throw new TypeError("Expected a string");return N.braces(e,F(I({},t),{expand:!0}))};cr.exports=N});var gr=K((Es,dr)=>{"use strict";dr.exports=(e,...t)=>new Promise(r=>{r(e(...t))})});var Ar=K((xs,Ve)=>{"use strict";var Yn=gr(),mr=e=>{if(e<1)throw new TypeError("Expected `concurrency` to be a number from 1 and up");let t=[],r=0,n=()=>{r--,t.length>0&&t.shift()()},s=(o,h,...m)=>{r++;let f=Yn(o,...m);h(f),f.then(n,n)},a=(o,h,...m)=>{rnew Promise(m=>a(o,m,...h));return Object.defineProperties(i,{activeCount:{get:()=>r},pendingCount:{get:()=>t.length}}),i};Ve.exports=mr;Ve.exports.default=mr});var Vn={};Or(Vn,{default:()=>es});var ve=Q(require("@yarnpkg/cli")),ne=Q(require("@yarnpkg/core")),rt=Q(require("@yarnpkg/core")),le=Q(require("clipanion")),me=class extends ve.BaseCommand{constructor(){super(...arguments);this.json=le.Option.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.production=le.Option.Boolean("--production",!1,{description:"Only install regular dependencies by omitting dev dependencies"});this.all=le.Option.Boolean("-A,--all",!1,{description:"Install the entire project"});this.workspaces=le.Option.Rest()}async execute(){let t=await ne.Configuration.find(this.context.cwd,this.context.plugins),{project:r,workspace:n}=await ne.Project.find(t,this.context.cwd),s=await ne.Cache.find(t);await r.restoreInstallState({restoreResolutions:!1});let a;if(this.all)a=new Set(r.workspaces);else if(this.workspaces.length===0){if(!n)throw new ve.WorkspaceRequiredError(r.cwd,this.context.cwd);a=new Set([n])}else a=new Set(this.workspaces.map(o=>r.getWorkspaceByIdent(rt.structUtils.parseIdent(o))));for(let o of a)for(let h of this.production?["dependencies"]:ne.Manifest.hardDependencies)for(let m of o.manifest.getForScope(h).values()){let f=r.tryWorkspaceByDescriptor(m);f!==null&&a.add(f)}for(let o of r.workspaces)a.has(o)?this.production&&o.manifest.devDependencies.clear():(o.manifest.installConfig=o.manifest.installConfig||{},o.manifest.installConfig.selfReferences=!1,o.manifest.dependencies.clear(),o.manifest.devDependencies.clear(),o.manifest.peerDependencies.clear(),o.manifest.scripts.clear());return(await ne.StreamReport.start({configuration:t,json:this.json,stdout:this.context.stdout,includeLogs:!0},async o=>{await r.install({cache:s,report:o,persistProject:!1})})).exitCode()}};me.paths=[["workspaces","focus"]],me.usage=le.Command.Usage({category:"Workspace-related commands",description:"install a single workspace and its dependencies",details:"\n This command will run an install as if the specified workspaces (and all other workspaces they depend on) were the only ones in the project. If no workspaces are explicitly listed, the active one will be assumed.\n\n Note that this command is only very moderately useful when using zero-installs, since the cache will contain all the packages anyway - meaning that the only difference between a full install and a focused install would just be a few extra lines in the `.pnp.cjs` file, at the cost of introducing an extra complexity.\n\n If the `-A,--all` flag is set, the entire project will be installed. Combine with `--production` to replicate the old `yarn install --production`.\n "});var nt=me;var Ne=Q(require("@yarnpkg/cli")),Ie=Q(require("@yarnpkg/core")),be=Q(require("@yarnpkg/core")),Z=Q(require("@yarnpkg/core")),Rr=Q(require("@yarnpkg/plugin-git")),G=Q(require("clipanion")),Be=Q(hr()),yr=Q(require("os")),_r=Q(Ar()),te=Q(require("typanion")),Ee=class extends Ne.BaseCommand{constructor(){super(...arguments);this.recursive=G.Option.Boolean("-R,--recursive",!1,{description:"Find packages via dependencies/devDependencies instead of using the workspaces field"});this.from=G.Option.Array("--from",[],{description:"An array of glob pattern idents from which to base any recursion"});this.all=G.Option.Boolean("-A,--all",!1,{description:"Run the command on all workspaces of a project"});this.verbose=G.Option.Boolean("-v,--verbose",!1,{description:"Prefix each output line with the name of the originating workspace"});this.parallel=G.Option.Boolean("-p,--parallel",!1,{description:"Run the commands in parallel"});this.interlaced=G.Option.Boolean("-i,--interlaced",!1,{description:"Print the output of commands in real-time instead of buffering it"});this.jobs=G.Option.String("-j,--jobs",{description:"The maximum number of parallel tasks that the execution will be limited to; or `unlimited`",validator:te.isOneOf([te.isEnum(["unlimited"]),te.applyCascade(te.isNumber(),[te.isInteger(),te.isAtLeast(1)])])});this.topological=G.Option.Boolean("-t,--topological",!1,{description:"Run the command after all workspaces it depends on (regular) have finished"});this.topologicalDev=G.Option.Boolean("--topological-dev",!1,{description:"Run the command after all workspaces it depends on (regular + dev) have finished"});this.include=G.Option.Array("--include",[],{description:"An array of glob pattern idents; only matching workspaces will be traversed"});this.exclude=G.Option.Array("--exclude",[],{description:"An array of glob pattern idents; matching workspaces won't be traversed"});this.publicOnly=G.Option.Boolean("--no-private",{description:"Avoid running the command on private workspaces"});this.since=G.Option.String("--since",{description:"Only include workspaces that have been changed since the specified ref.",tolerateBoolean:!0});this.commandName=G.Option.String();this.args=G.Option.Proxy()}async execute(){let t=await Ie.Configuration.find(this.context.cwd,this.context.plugins),{project:r,workspace:n}=await Ie.Project.find(t,this.context.cwd);if(!this.all&&!n)throw new Ne.WorkspaceRequiredError(r.cwd,this.context.cwd);let s=this.cli.process([this.commandName,...this.args]),a=s.path.length===1&&s.path[0]==="run"&&typeof s.scriptName!="undefined"?s.scriptName:null;if(s.path.length===0)throw new G.UsageError("Invalid subcommand name for iteration - use the 'run' keyword if you wish to execute a script");let i=this.all?r.topLevelWorkspace:n,o=this.since?Array.from(await Rr.gitUtils.fetchChangedWorkspaces({ref:this.since,project:r})):[i,...this.from.length>0?i.getRecursiveWorkspaceChildren():[]],h=g=>Be.default.isMatch(Z.structUtils.stringifyIdent(g.locator),this.from),m=this.from.length>0?o.filter(h):o,f=new Set([...m,...m.map(g=>[...this.recursive?this.since?g.getRecursiveWorkspaceDependents():g.getRecursiveWorkspaceDependencies():g.getRecursiveWorkspaceChildren()]).flat()]),R=[],p=!1;if(a==null?void 0:a.includes(":")){for(let g of r.workspaces)if(g.manifest.scripts.has(a)&&(p=!p,p===!1))break}for(let g of f)a&&!g.manifest.scripts.has(a)&&!p||a===process.env.npm_lifecycle_event&&g.cwd===n.cwd||this.include.length>0&&!Be.default.isMatch(Z.structUtils.stringifyIdent(g.locator),this.include)||this.exclude.length>0&&Be.default.isMatch(Z.structUtils.stringifyIdent(g.locator),this.exclude)||this.publicOnly&&g.manifest.private===!0||R.push(g);let v=this.parallel?this.jobs==="unlimited"?Infinity:this.jobs||Math.max(1,(0,yr.cpus)().length/2):1,_=v===1?!1:this.parallel,y=_?this.interlaced:!0,b=(0,_r.default)(v),E=new Map,H=new Set,L=0,k=null,J=!1,ie=await be.StreamReport.start({configuration:t,stdout:this.context.stdout},async g=>{let w=async(D,{commandIndex:W})=>{if(J)return-1;!_&&this.verbose&&W>1&&g.reportSeparator();let l=zn(D,{configuration:t,verbose:this.verbose,commandIndex:W}),[x,T]=br(g,{prefix:l,interlaced:y}),[U,u]=br(g,{prefix:l,interlaced:y});try{this.verbose&&g.reportInfo(null,`${l} Process started`);let c=Date.now(),$=await this.cli.run([this.commandName,...this.args],{cwd:D.cwd,stdout:x,stderr:U})||0;x.end(),U.end(),await T,await u;let B=Date.now();if(this.verbose){let Y=t.get("enableTimers")?`, completed in ${Z.formatUtils.pretty(t,B-c,Z.formatUtils.Type.DURATION)}`:"";g.reportInfo(null,`${l} Process exited (exit code ${$})${Y}`)}return $===130&&(J=!0,k=$),$}catch(c){throw x.end(),U.end(),await T,await u,c}};for(let D of R)E.set(D.anchoredLocator.locatorHash,D);for(;E.size>0&&!g.hasErrors();){let D=[];for(let[x,T]of E){if(H.has(T.anchoredDescriptor.descriptorHash))continue;let U=!0;if(this.topological||this.topologicalDev){let u=this.topologicalDev?new Map([...T.manifest.dependencies,...T.manifest.devDependencies]):T.manifest.dependencies;for(let c of u.values()){let $=r.tryWorkspaceByDescriptor(c);if(U=$===null||!E.has($.anchoredLocator.locatorHash),!U)break}}if(!!U&&(H.add(T.anchoredDescriptor.descriptorHash),D.push(b(async()=>{let u=await w(T,{commandIndex:++L});return E.delete(x),H.delete(T.anchoredDescriptor.descriptorHash),u})),!_))break}if(D.length===0){let x=Array.from(E.values()).map(T=>Z.structUtils.prettyLocator(t,T.anchoredLocator)).join(", ");g.reportError(be.MessageName.CYCLIC_DEPENDENCIES,`Dependency cycle detected (${x})`);return}let l=(await Promise.all(D)).find(x=>x!==0);k===null&&(k=typeof l!="undefined"?1:k),(this.topological||this.topologicalDev)&&typeof l!="undefined"&&g.reportError(be.MessageName.UNNAMED,"The command failed for workspaces that are depended upon by other workspaces; can't satisfy the dependency graph")}});return k!==null?k:ie.exitCode()}};Ee.paths=[["workspaces","foreach"]],Ee.usage=G.Command.Usage({category:"Workspace-related commands",description:"run a command on all workspaces",details:"\n This command will run a given sub-command on current and all its descendant workspaces. Various flags can alter the exact behavior of the command:\n\n - If `-p,--parallel` is set, the commands will be ran in parallel; they'll by default be limited to a number of parallel tasks roughly equal to half your core number, but that can be overridden via `-j,--jobs`, or disabled by setting `-j unlimited`.\n\n - If `-p,--parallel` and `-i,--interlaced` are both set, Yarn will print the lines from the output as it receives them. If `-i,--interlaced` wasn't set, it would instead buffer the output from each process and print the resulting buffers only after their source processes have exited.\n\n - If `-t,--topological` is set, Yarn will only run the command after all workspaces that it depends on through the `dependencies` field have successfully finished executing. If `--topological-dev` is set, both the `dependencies` and `devDependencies` fields will be considered when figuring out the wait points.\n\n - If `-A,--all` is set, Yarn will run the command on all the workspaces of a project. By default yarn runs the command only on current and all its descendant workspaces.\n\n - If `-R,--recursive` is set, Yarn will find workspaces to run the command on by recursively evaluating `dependencies` and `devDependencies` fields, instead of looking at the `workspaces` fields.\n\n - If `--from` is set, Yarn will use the packages matching the 'from' glob as the starting point for any recursive search.\n\n - If `--since` is set, Yarn will only run the command on workspaces that have been modified since the specified ref. By default Yarn will use the refs specified by the `changesetBaseRefs` configuration option.\n\n - The command may apply to only some workspaces through the use of `--include` which acts as a whitelist. The `--exclude` flag will do the opposite and will be a list of packages that mustn't execute the script. Both flags accept glob patterns (if valid Idents and supported by [micromatch](https://github.com/micromatch/micromatch)). Make sure to escape the patterns, to prevent your own shell from trying to expand them.\n\n Adding the `-v,--verbose` flag will cause Yarn to print more information; in particular the name of the workspace that generated the output will be printed at the front of each line.\n\n If the command is `run` and the script being run does not exist the child workspace will be skipped without error.\n ",examples:[["Publish current and all descendant packages","yarn workspaces foreach npm publish --tolerate-republish"],["Run build script on current and all descendant packages","yarn workspaces foreach run build"],["Run build script on current and all descendant packages in parallel, building package dependencies first","yarn workspaces foreach -pt run build"],["Run build script on several packages and all their dependencies, building dependencies first","yarn workspaces foreach -ptR --from '{workspace-a,workspace-b}' run build"]]});var Er=Ee;function br(e,{prefix:t,interlaced:r}){let n=e.createStreamReporter(t),s=new Z.miscUtils.DefaultStream;s.pipe(n,{end:!1}),s.on("finish",()=>{n.end()});let a=new Promise(o=>{n.on("finish",()=>{o(s.active)})});if(r)return[s,a];let i=new Z.miscUtils.BufferStream;return i.pipe(s,{end:!1}),i.on("finish",()=>{s.end()}),[i,a]}function zn(e,{configuration:t,commandIndex:r,verbose:n}){if(!n)return null;let s=Z.structUtils.convertToIdent(e.locator),i=`[${Z.structUtils.stringifyIdent(s)}]:`,o=["#2E86AB","#A23B72","#F18F01","#C73E1D","#CCE2A3"],h=o[r%o.length];return Z.formatUtils.pretty(t,i,h)}var Jn={commands:[nt,Er]},es=Jn;return Vn;})();
8 | /*!
9 | * fill-range
10 | *
11 | * Copyright (c) 2014-present, Jon Schlinkert.
12 | * Licensed under the MIT License.
13 | */
14 | /*!
15 | * is-number
16 | *
17 | * Copyright (c) 2014-present, Jon Schlinkert.
18 | * Released under the MIT License.
19 | */
20 | /*!
21 | * to-regex-range
22 | *
23 | * Copyright (c) 2015-present, Jon Schlinkert.
24 | * Released under the MIT License.
25 | */
26 | return plugin;
27 | }
28 | };
29 |
--------------------------------------------------------------------------------