├── .eslintrc.json ├── .prettierrc.js ├── .prettierrc ├── postcss.config.js ├── src ├── app │ ├── favicon.ico │ ├── layout.tsx │ ├── page.tsx │ └── globals.css └── components │ ├── HitList.tsx │ ├── ImageWithFallback.tsx │ ├── types.ts │ ├── Markdown.tsx │ ├── SearchBox.tsx │ ├── Results.tsx │ ├── Hit.tsx │ └── Search.tsx ├── public ├── discordFallback.png ├── vercel.svg ├── icon-stack.svg ├── icon-convex.svg ├── icon-twitter-white.svg ├── icon-discord.svg ├── icon-discord-white.svg ├── next.svg ├── icon-github-white.svg └── logo.svg ├── .editorconfig ├── next.config.js ├── renovate.json ├── .gitignore ├── tsconfig.json ├── package.json ├── README.md └── LICENSE.txt /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | proseWrap: 'always', 3 | arrowParens: 'avoid', 4 | }; 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "proseWrap": "never", 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/convex-resource-search/main/src/app/favicon.ico -------------------------------------------------------------------------------- /public/discordFallback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/convex-resource-search/main/public/discordFallback.png -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | remotePatterns: [ 5 | { 6 | protocol: 'https', 7 | hostname: 'cdn.discordapp.com', 8 | }, 9 | ], 10 | }, 11 | }; 12 | 13 | module.exports = nextConfig; 14 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import './globals.css'; 2 | 3 | export const metadata = { 4 | title: 'Convex Developer Search', 5 | description: 'Search Docs, Stack, Discord all at once', 6 | }; 7 | 8 | export default function RootLayout({ 9 | children, 10 | }: { 11 | children: React.ReactNode; 12 | }) { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:recommended"], 4 | "packageRules": [ 5 | { 6 | "matchUpdateTypes": ["minor", "patch"], 7 | "groupName": "minor and patch dependencies", 8 | "schedule": ["* 0-3 * * 1"], 9 | "minimumReleaseAge": "3 days" 10 | }, 11 | { 12 | "matchUpdateTypes": ["major"], 13 | "schedule": ["* 0-3 * * 1"], 14 | "minimumReleaseAge": "7 days" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /src/components/HitList.tsx: -------------------------------------------------------------------------------- 1 | import { useHits } from 'react-instantsearch'; 2 | import Hit from './Hit'; 3 | import { SearchHit } from './types'; 4 | 5 | export default function HitList() { 6 | const { hits } = useHits(); 7 | 8 | return ( 9 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Search from '@/components/Search'; 4 | import { liteClient } from 'algoliasearch/lite'; 5 | import { InstantSearch } from 'react-instantsearch'; 6 | 7 | // Search-only API key, safe to use in frontend code. See: 8 | // https://www.algolia.com/doc/guides/security/api-keys/#search-only-api-key 9 | const searchClient = liteClient( 10 | '1KIE511890', 11 | '07096f4c927e372785f8453f177afb16' 12 | ); 13 | 14 | export default function Home() { 15 | return ( 16 | 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ImageWithFallback.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Image, { ImageProps } from 'next/image'; 4 | import { useState } from 'react'; 5 | 6 | interface ImageWithFallbackProps extends Omit { 7 | src: string; 8 | fallbackSrc: string; 9 | } 10 | 11 | export default function ImageWithFallback({ 12 | src, 13 | fallbackSrc, 14 | ...props 15 | }: ImageWithFallbackProps) { 16 | const [imgSrc, setImgSrc] = useState(src); 17 | const [hasError, setHasError] = useState(false); 18 | 19 | const handleError = () => { 20 | if (!hasError) { 21 | setHasError(true); 22 | setImgSrc(fallbackSrc); 23 | } 24 | }; 25 | 26 | return ; 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./src/*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "search", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-icons": "^1.3.0", 13 | "@tailwindcss/line-clamp": "^0.4.4", 14 | "@tailwindcss/postcss": "^4.1.11", 15 | "@types/node": "24.10.3", 16 | "@types/react": "18.3.27", 17 | "@types/react-dom": "18.3.7", 18 | "algoliasearch": "^5.0.0", 19 | "classnames": "^2.3.2", 20 | "eslint": "^9.0.0", 21 | "eslint-config-next": "16.0.10", 22 | "next": "16.0.10", 23 | "postcss": "8.5.6", 24 | "react": "18.3.1", 25 | "react-dom": "18.3.1", 26 | "react-instantsearch": "^7.13.0", 27 | "react-markdown": "^10.0.0", 28 | "tailwindcss": "^4.1.11", 29 | "typescript": "5.9.3" 30 | }, 31 | "devDependencies": { 32 | "prettier": "^3.0.0", 33 | "prettier-plugin-tailwindcss": "^0.7.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /public/icon-stack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/types.ts: -------------------------------------------------------------------------------- 1 | export type BaseHit = { 2 | title: string; 3 | objectID: string; 4 | __position: number; 5 | }; 6 | 7 | export type DocsHit = BaseHit & { 8 | contents: string; 9 | }; 10 | 11 | export type StackHit = BaseHit & { 12 | summary: string; 13 | content: string; 14 | tags: string[]; 15 | }; 16 | 17 | export type DiscordHit = BaseHit & { 18 | channel: string; 19 | url: string; 20 | date: number; 21 | messages: { 22 | author: { 23 | avatar: string; 24 | convexer: boolean; 25 | name: string; 26 | }; 27 | body: string; 28 | }[]; 29 | }; 30 | 31 | export type SearchHit = DocsHit | StackHit | DiscordHit; 32 | 33 | export function isDocsHit(hit: SearchHit): hit is DocsHit { 34 | return (hit as DocsHit).contents !== undefined; 35 | } 36 | 37 | export function isStackHit(hit: SearchHit): hit is StackHit { 38 | return (hit as StackHit).summary !== undefined; 39 | } 40 | 41 | export function isDiscordHit(hit: SearchHit): hit is DiscordHit { 42 | return (hit as DiscordHit).messages !== undefined; 43 | } 44 | -------------------------------------------------------------------------------- /src/components/Markdown.tsx: -------------------------------------------------------------------------------- 1 | import ReactMarkdown from 'react-markdown'; 2 | 3 | interface MarkdownProps { 4 | text: string; 5 | } 6 | 7 | export default function Markdown({ text }: MarkdownProps) { 8 | return ( 9 |

{children}

, 12 | pre: ({ children }) => ( 13 |
14 |
{children}
15 |
16 | ), 17 | code: ({ children }) => ( 18 | 19 | {children} 20 | 21 | ), 22 | ol: ({ children }) => ( 23 |
    24 | {children} 25 |
26 | ), 27 | li: ({ children }) =>
  • {children}
  • , 28 | }} 29 | > 30 | {text} 31 |
    32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /public/icon-convex.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/icon-twitter-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/icon-discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/icon-discord-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/SearchBox.tsx: -------------------------------------------------------------------------------- 1 | import { Cross2Icon } from '@radix-ui/react-icons'; 2 | import { useEffect, useRef } from 'react'; 3 | 4 | interface SearchBoxProps { 5 | value: string; 6 | onChange: (event: React.ChangeEvent) => void; 7 | onClear: () => void; 8 | } 9 | 10 | export default function SearchBox({ 11 | value, 12 | onChange, 13 | onClear, 14 | }: SearchBoxProps) { 15 | const inputRef = useRef(null); 16 | 17 | const focusInput = () => { 18 | if (inputRef.current) { 19 | inputRef.current.focus(); 20 | } 21 | }; 22 | 23 | const handleClear = () => { 24 | onClear(); 25 | focusInput(); 26 | }; 27 | 28 | useEffect(() => { 29 | focusInput(); 30 | }, []); 31 | 32 | return ( 33 |
    34 | 42 | {value !== '' && ( 43 | 49 | )} 50 |
    51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 18 | 19 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /public/icon-github-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/components/Results.tsx: -------------------------------------------------------------------------------- 1 | import { Index } from 'react-instantsearch'; 2 | import HitList from './HitList'; 3 | import { useState } from 'react'; 4 | import classnames from 'classnames'; 5 | 6 | const indexes = [ 7 | { 8 | name: 'docs', 9 | title: 'Docs', 10 | link: 'https://docs.convex.dev', 11 | }, 12 | { 13 | name: 'stack', 14 | title: 'Stack', 15 | link: 'https://stack.convex.dev', 16 | }, 17 | { 18 | name: 'discord', 19 | title: 'Discord', 20 | link: 'https://discord.com/invite/nk6C2qTeCq', 21 | }, 22 | ]; 23 | 24 | export default function Results() { 25 | const [selectedIndexName, setSelectedIndexName] = useState(indexes[0].name); 26 | 27 | return ( 28 |
    29 |
    30 | {indexes.map(({ name, title }) => ( 31 | 45 | ))} 46 |
    47 |
    48 | {indexes.map(({ name, title, link }) => ( 49 |
    50 | 55 | {title} 56 | 57 |
    63 | 64 | 65 | 66 |
    67 |
    68 | ))} 69 |
    70 |
    71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Roboto+Flex:wdth,wght@25..125,100..1000&display=swap') 2 | layer(base); 3 | 4 | @import 'tailwindcss'; 5 | 6 | /* Custom theme variables for Tailwind v4 */ 7 | @theme { 8 | --color-neutral-white: #ffffff; 9 | --color-neutral-n1: #f6f6f6; 10 | --color-neutral-n2: #f1f1f1; 11 | --color-neutral-n3: #e5e5e5; 12 | --color-neutral-n4: #d7d7d7; 13 | --color-neutral-n5: #c2c2c2; 14 | --color-neutral-n6: #adadad; 15 | --color-neutral-n7: #939393; 16 | --color-neutral-n8: #797979; 17 | --color-neutral-n9: #6e6e6e; 18 | --color-neutral-n10: #3f3f3f; 19 | --color-neutral-n11: #292929; 20 | --color-neutral-n12: #141414; 21 | --color-neutral-n13: #111111; 22 | --color-neutral-black: #000000; 23 | 24 | --color-plum-p1: #f4e9f1; 25 | --color-plum-p2: #e3d0df; 26 | --color-plum-p3: #d7b3cf; 27 | --color-plum-p4: #8d2676; 28 | --color-plum-p5: #711e5e; 29 | --color-plum-p6: #47133b; 30 | 31 | --color-yellow-y1: #fdefd2; 32 | --color-yellow-y2: #f8d077; 33 | --color-yellow-y3: #f3b01c; 34 | --color-yellow-y4: #e7a71b; 35 | 36 | --color-red-r1: #fcd6d5; 37 | --color-red-r2: #f15d59; 38 | --color-red-r3: #ee342f; 39 | --color-red-r4: #d62f2a; 40 | 41 | --color-green-g1: #e5f3dc; 42 | --color-green-g2: #72c043; 43 | --color-green-g3: #4fb014; 44 | --color-green-g4: #479e12; 45 | 46 | --color-accent-a1: #07bfe8; 47 | --color-accent-a2: #074ee8; 48 | --color-accent-a3: #7651e0; 49 | --color-accent-a4: #ee9f73; 50 | 51 | --color-discord: #5865f2; 52 | 53 | --font-family-display: 54 | 'Roboto Flex', system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 55 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; 56 | --font-family-body: 57 | 'Inter', system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 58 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; 59 | --font-family-mono: monospace; 60 | 61 | --font-size-xs: 0.6875rem; 62 | --font-size-sm: 0.875rem; 63 | --font-size-base: 1rem; 64 | --font-size-lg: 1.125rem; 65 | --font-size-xl: 1.25rem; 66 | --font-size-2xl: 2.375rem; 67 | --font-size-3xl: 2.5rem; 68 | --font-size-4xl: 2.625rem; 69 | --font-size-5xl: 2.875rem; 70 | --font-size-6xl: 3.125rem; 71 | --font-size-7xl: 3.5rem; 72 | --font-size-8xl: 3.75rem; 73 | } 74 | 75 | /* Custom utilities for font stretch (Roboto Flex variable font) */ 76 | @layer utilities { 77 | .stretch-min { 78 | font-stretch: 25%; 79 | } 80 | 81 | .stretch-thin { 82 | font-stretch: 75%; 83 | } 84 | 85 | .stretch-default { 86 | font-stretch: 100%; 87 | } 88 | 89 | .stretch-wide { 90 | font-stretch: 125%; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/components/Hit.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import { Highlight } from 'react-instantsearch'; 3 | import ImageWithFallback from './ImageWithFallback'; 4 | import Markdown from './Markdown'; 5 | import { SearchHit, isDiscordHit, isDocsHit, isStackHit } from './types'; 6 | 7 | type HitProps = { 8 | hit: SearchHit; 9 | }; 10 | 11 | export default function Hit({ hit }: HitProps) { 12 | console.log('hit', hit); 13 | 14 | return ( 15 |
    16 | {isDocsHit(hit) && ( 17 | 37 | )} 38 | {isStackHit(hit) && ( 39 | 59 | )} 60 | {isDiscordHit(hit) && ( 61 |
    62 | 67 | Discord logo 73 | {hit.title} 74 | 75 |
      76 | {hit.messages.slice(0, 3).map((message, index) => ( 77 |
    1. 81 | {/* Uses a fallback image, if the avatar we indexed is no longer available. */} 82 | 90 |
      91 | 92 | {message.author.name} 93 | {message.author.convexer && ( 94 | Convex team member 100 | )} 101 | 102 | 103 |
      104 |
    2. 105 | ))} 106 |
    107 |
    108 | )} 109 |
    110 | ); 111 | } 112 | -------------------------------------------------------------------------------- /src/components/Search.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { InfoCircledIcon } from '@radix-ui/react-icons'; 4 | import Image from 'next/image'; 5 | import { useEffect, useState } from 'react'; 6 | import { useSearchBox } from 'react-instantsearch'; 7 | import Results from './Results'; 8 | import SearchBox from './SearchBox'; 9 | 10 | const queryParam = 'q'; 11 | 12 | export default function Search() { 13 | const { refine } = useSearchBox(); 14 | const [query, setQuery] = useState(''); 15 | 16 | const handleChange = (event: React.ChangeEvent) => { 17 | const value = event.target.value; 18 | setQuery(value); 19 | refine(value); 20 | updateQueryParam(value); 21 | }; 22 | 23 | const handleClear = () => { 24 | setQuery(''); 25 | refine(''); 26 | updateQueryParam(''); 27 | }; 28 | 29 | const updateQueryParam = (value: string) => { 30 | const urlParams = new URLSearchParams(window.location.search); 31 | if (value) { 32 | urlParams.set(queryParam, value); 33 | } else { 34 | urlParams.delete(queryParam); 35 | } 36 | 37 | // Construct the updated URL. 38 | const baseUrl = `${window.location.origin}${window.location.pathname}`; 39 | const newUrl = urlParams.toString() 40 | ? `${baseUrl}?${urlParams.toString()}` 41 | : baseUrl; 42 | 43 | // Update the URL without reloading. 44 | window.history.pushState({}, '', newUrl); 45 | }; 46 | 47 | useEffect(() => { 48 | const urlParams = new URLSearchParams(window.location.search); 49 | const searchParam = urlParams.get(queryParam); 50 | if (searchParam) { 51 | setQuery(searchParam); 52 | refine(searchParam); 53 | } 54 | }, [refine]); 55 | 56 | return ( 57 |
    58 |
    59 | Convex logo 60 | 65 |
    66 |
    67 | {query === '' ? ( 68 |
    69 | 70 | 71 | Use the input above to search across Docs, Stack, and Discord. 72 | 73 |
    74 | ) : ( 75 | 76 | )} 77 |
    78 | 121 |
    122 | ); 123 | } 124 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2024 Convex, Inc. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------