├── .all-contributorsrc ├── .editorconfig ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml └── workflows │ └── test.yml ├── .gitignore ├── .husky ├── pre-commit └── pre-push ├── .prettierrc ├── .vercelignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── api ├── list │ └── domain │ │ └── [query].ts └── services │ ├── appstore │ └── [query].ts │ ├── archlinux │ └── [query].ts │ ├── chrome-web-store │ └── [query].ts │ ├── debian │ └── [query].ts │ ├── dns │ └── [query].ts │ ├── domain │ └── [query].ts │ ├── existence │ └── [query].ts │ ├── firefox-addons │ └── [query].ts │ ├── gitlab │ └── [query].ts │ ├── jsorg │ └── [query].ts │ ├── launchpad │ └── [query].ts │ ├── npm-org │ └── [query].ts │ ├── npm │ └── [query].ts │ ├── nta │ └── [query].ts │ ├── playstore │ └── [query].ts │ ├── reddit │ └── [query].ts │ ├── slack │ └── [query].ts │ └── twitter │ └── [query].ts ├── jest.config.js ├── package.json ├── public ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── index.html ├── locales │ ├── de │ │ └── translation.json │ ├── en │ │ └── translation.json │ ├── es │ │ └── translation.json │ ├── fr │ │ └── translation.json │ ├── id │ │ └── translation.json │ ├── it │ │ └── translation.json │ ├── ja │ │ └── translation.json │ ├── nl │ │ └── translation.json │ ├── pt-BR │ │ └── translation.json │ ├── ru │ │ └── translation.json │ ├── zh-Hans │ │ └── translation.json │ └── zh-Hant │ │ └── translation.json ├── logo.svg ├── manifest.json ├── mstile-150x150.png ├── opensearch.xml ├── robots.txt ├── safari-pinned-tab.svg ├── site.webmanifest └── social.png ├── setupJest.ts ├── src ├── App.test.tsx ├── App.tsx ├── components │ ├── Contributors.tsx │ ├── Footer.tsx │ ├── Form.tsx │ ├── Icons.tsx │ ├── Suggestion.tsx │ ├── Welcome.tsx │ └── cards │ │ ├── core.tsx │ │ ├── index.tsx │ │ └── providers │ │ ├── AppStore.tsx │ │ ├── ChromeWebStore.tsx │ │ ├── Cloudflare.tsx │ │ ├── Cratesio.tsx │ │ ├── Docker.tsx │ │ ├── Domains.tsx │ │ ├── Firebase.tsx │ │ ├── FirefoxAddons.tsx │ │ ├── FlyIo.tsx │ │ ├── GitHubOrganization.tsx │ │ ├── GitHubSearch.tsx │ │ ├── GitLab.tsx │ │ ├── Heroku.tsx │ │ ├── HexPm.tsx │ │ ├── Homebrew.tsx │ │ ├── Instagram.tsx │ │ ├── JsOrg.tsx │ │ ├── Linux.tsx │ │ ├── ModLand.tsx │ │ ├── Netlify.tsx │ │ ├── Npm.tsx │ │ ├── Nta.tsx │ │ ├── Ocaml.tsx │ │ ├── PlayStore.tsx │ │ ├── ProductHunt.tsx │ │ ├── PyPI.tsx │ │ ├── RubyGems.tsx │ │ ├── S3.tsx │ │ ├── Slack.tsx │ │ ├── Subreddit.tsx │ │ ├── Twitter.tsx │ │ ├── Vercel.tsx │ │ └── YouTube.tsx ├── index.tsx ├── pages │ ├── Home.tsx │ └── Search.tsx ├── react-app-env.d.ts ├── serviceWorker.ts ├── setupTests.ts ├── store.tsx ├── theme │ └── index.tsx └── util │ ├── analytics.ts │ ├── array.ts │ ├── crisp.ts │ ├── css.ts │ ├── hooks.test.tsx │ ├── hooks.tsx │ ├── i18n.ts │ ├── pwa.test.ts │ ├── pwa.ts │ ├── suspense.tsx │ ├── text.test.ts │ ├── text.ts │ └── zones.ts ├── tests └── existense.test.ts ├── tsconfig.json ├── types ├── react-tippy.d.ts └── whois-json.d.ts ├── util ├── http.ts └── testHelpers.ts ├── vercel.json └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "namae", 3 | "projectOwner": "uetchy", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "imageSize": 100, 10 | "commit": false, 11 | "commitConvention": "angular", 12 | "contributors": [ 13 | { 14 | "login": "uetchy", 15 | "name": "Yasuaki Uechi", 16 | "avatar_url": "https://avatars0.githubusercontent.com/u/431808?v=4", 17 | "profile": "https://uechi.io", 18 | "contributions": [ 19 | "code", 20 | "translation" 21 | ] 22 | }, 23 | { 24 | "login": "flawyte", 25 | "name": "Mickaël Allonneau", 26 | "avatar_url": "https://avatars3.githubusercontent.com/u/1585006?v=4", 27 | "profile": "https://github.com/flawyte", 28 | "contributions": [ 29 | "code" 30 | ] 31 | }, 32 | { 33 | "login": "3x", 34 | "name": "Example", 35 | "avatar_url": "https://avatars1.githubusercontent.com/u/18331588?v=4", 36 | "profile": "https://github.com/3x", 37 | "contributions": [ 38 | "code" 39 | ] 40 | }, 41 | { 42 | "login": "nixiesquid", 43 | "name": "NixieSquid", 44 | "avatar_url": "https://avatars2.githubusercontent.com/u/21212032?v=4", 45 | "profile": "https://scrapbox.io/rustacean/", 46 | "contributions": [ 47 | "code", 48 | "translation" 49 | ] 50 | }, 51 | { 52 | "login": "ZeProf2Code", 53 | "name": "Arnaud Lier", 54 | "avatar_url": "https://avatars3.githubusercontent.com/u/32982428?v=4", 55 | "profile": "https://www.zeprof2coding.me", 56 | "contributions": [ 57 | "translation", 58 | "code" 59 | ] 60 | }, 61 | { 62 | "login": "raikasdev", 63 | "name": "Raikas", 64 | "avatar_url": "https://avatars.githubusercontent.com/u/29684625?v=4", 65 | "profile": "https://mikroni.fi", 66 | "contributions": [ 67 | "code" 68 | ] 69 | }, 70 | { 71 | "login": "jonahsnider", 72 | "name": "Jonah Snider", 73 | "avatar_url": "https://avatars.githubusercontent.com/u/7608555?v=4", 74 | "profile": "https://jonahsnider.com/", 75 | "contributions": [ 76 | "code" 77 | ] 78 | }, 79 | { 80 | "login": "miguelsmuller", 81 | "name": "Miguel Müller", 82 | "avatar_url": "https://avatars.githubusercontent.com/u/4589909?v=4", 83 | "profile": "https://github.com/miguelsmuller", 84 | "contributions": [ 85 | "translation" 86 | ] 87 | }, 88 | { 89 | "login": "z3ro0k", 90 | "name": "Azurnex", 91 | "avatar_url": "https://avatars.githubusercontent.com/u/32654584?v=4", 92 | "profile": "https://atzu.ml", 93 | "contributions": [ 94 | "translation" 95 | ] 96 | }, 97 | { 98 | "login": "Snazzah", 99 | "name": "Snazzah", 100 | "avatar_url": "https://avatars.githubusercontent.com/u/7025343?v=4", 101 | "profile": "http://snazzah.com", 102 | "contributions": [ 103 | "code" 104 | ] 105 | }, 106 | { 107 | "login": "Thebigbot0000", 108 | "name": "Thebigbot", 109 | "avatar_url": "https://avatars.githubusercontent.com/u/77632836?v=4", 110 | "profile": "http://thebigbot.mod.land", 111 | "contributions": [ 112 | "translation" 113 | ] 114 | }, 115 | { 116 | "login": "BlackdestinyXX", 117 | "name": "BlackdestinyXX", 118 | "avatar_url": "https://avatars.githubusercontent.com/u/65021823?v=4", 119 | "profile": "https://github.com/BlackdestinyXX", 120 | "contributions": [ 121 | "code" 122 | ] 123 | }, 124 | { 125 | "login": "DaniruKun", 126 | "name": "Daniils Petrovs", 127 | "avatar_url": "https://avatars.githubusercontent.com/u/5202322?v=4", 128 | "profile": "http://danpetrov.xyz", 129 | "contributions": [ 130 | "review" 131 | ] 132 | }, 133 | { 134 | "login": "Fyxren", 135 | "name": "Ben", 136 | "avatar_url": "https://avatars.githubusercontent.com/u/68126277?v=4", 137 | "profile": "http://fyxren.site", 138 | "contributions": [ 139 | "translation" 140 | ] 141 | }, 142 | { 143 | "login": "MadProbe", 144 | "name": "Evgeniy Istomin", 145 | "avatar_url": "https://avatars.githubusercontent.com/u/49519179?v=4", 146 | "profile": "https://github.com/MadProbe", 147 | "contributions": [ 148 | "translation" 149 | ] 150 | }, 151 | { 152 | "login": "LukasPurbaW", 153 | "name": "LukasPurbaW", 154 | "avatar_url": "https://avatars.githubusercontent.com/u/72651891?v=4", 155 | "profile": "http://lukaspaw.wordpress.com", 156 | "contributions": [ 157 | "translation" 158 | ] 159 | } 160 | ], 161 | "contributorsPerLine": 7, 162 | "skipCi": true 163 | } 164 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @uetchy 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [uetchy] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: uetchy 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms 2 | 3 | name: Bug report 4 | description: Create a report to help us improve 5 | title: 'Bug: ' 6 | labels: ['bug'] 7 | # assignees: '' 8 | 9 | body: 10 | - type: textarea 11 | attributes: 12 | label: Description 13 | description: >- 14 | A clear and concise description of what the bug is. 15 | validations: 16 | required: true 17 | 18 | - type: textarea 19 | attributes: 20 | label: To Reproduce 21 | description: >- 22 | Steps to reproduce the behavior. 23 | placeholder: | 24 | 1. Go to '...' 25 | 2. Click on '....' 26 | 3. Scroll down to '....' 27 | 4. See error 28 | validations: 29 | required: true 30 | 31 | - type: input 32 | attributes: 33 | label: What's My Browser Link 34 | description: Copy and paste a link generated on [What's My Browser](https://www.whatsmybrowser.org/) 35 | validations: 36 | required: true 37 | 38 | - type: textarea 39 | attributes: 40 | label: Expected behavior 41 | description: >- 42 | A clear and concise description of what you expected to happen. 43 | 44 | - type: textarea 45 | attributes: 46 | label: Log output 47 | render: Shell 48 | description: Copy and paste relevant log outputs in Developer Console 49 | 50 | - type: textarea 51 | attributes: 52 | label: Additional context 53 | description: >- 54 | Add any other context about the problem here. 55 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | # contact_links: 3 | # - name: GitHub Community Support 4 | # url: https://github.community/ 5 | # about: Please ask and answer questions here. 6 | # - name: GitHub Security Bug Bounty 7 | # url: https://bounty.github.com/ 8 | # about: Please report security vulnerabilities here. 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms 2 | 3 | name: Feature request 4 | description: Suggest an idea for this project 5 | title: 'Feature request: ' 6 | labels: ['enhancement'] 7 | # assignees: '' 8 | 9 | body: 10 | - type: textarea 11 | attributes: 12 | label: Is your feature request related to a problem? Please describe. 13 | description: >- 14 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 15 | validations: 16 | required: true 17 | 18 | - type: textarea 19 | attributes: 20 | label: Describe the solution you'd like 21 | description: >- 22 | A clear and concise description of what you want to happen. 23 | validations: 24 | required: true 25 | 26 | - type: textarea 27 | attributes: 28 | label: Describe alternatives you've considered 29 | description: A clear and concise description of any alternative solutions or features you've considered. 30 | 31 | - type: textarea 32 | attributes: 33 | label: Additional context 34 | description: >- 35 | Add any other context or screenshots about the feature request here. 36 | 37 | - type: dropdown 38 | attributes: 39 | label: Commitment 40 | description: How far can you commit to this feature request? 41 | multiple: true 42 | options: 43 | - Join discussions 44 | - Send a pull-request 45 | validations: 46 | required: true 47 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Test 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - uses: actions/setup-node@v2 13 | with: 14 | node-version: '16' 15 | 16 | - name: Install dependencies 17 | run: yarn install --frozen-lockfile 18 | 19 | - name: Test 20 | run: yarn test 21 | 22 | - name: Upload coverage 23 | if: ${{ github.event_name != 'pull_request' }} 24 | run: | 25 | cat ./coverage/lcov.info | yarn codacy-coverage 26 | env: 27 | CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | /build 3 | /tmp 4 | 5 | # Created by https://www.gitignore.io/api/node 6 | # Edit at https://www.gitignore.io/?templates=node 7 | 8 | ### Node ### 9 | # Logs 10 | logs 11 | *.log 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | lerna-debug.log* 16 | 17 | # Diagnostic reports (https://nodejs.org/api/report.html) 18 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 19 | 20 | # Runtime data 21 | pids 22 | *.pid 23 | *.seed 24 | *.pid.lock 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | *.lcov 32 | 33 | # nyc test coverage 34 | .nyc_output 35 | 36 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | bower_components 41 | 42 | # node-waf configuration 43 | .lock-wscript 44 | 45 | # Compiled binary addons (https://nodejs.org/api/addons.html) 46 | build/Release 47 | 48 | # Dependency directories 49 | node_modules/ 50 | jspm_packages/ 51 | 52 | # TypeScript v1 declaration files 53 | typings/ 54 | 55 | # TypeScript cache 56 | *.tsbuildinfo 57 | 58 | # Optional npm cache directory 59 | .npm 60 | 61 | # Optional eslint cache 62 | .eslintcache 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | 80 | # next.js build output 81 | .next 82 | 83 | # nuxt.js build output 84 | .nuxt 85 | 86 | # vuepress build output 87 | .vuepress/dist 88 | 89 | # Serverless directories 90 | .serverless/ 91 | 92 | # FuseBox cache 93 | .fusebox/ 94 | 95 | # DynamoDB Local files 96 | .dynamodb/ 97 | 98 | # End of https://www.gitignore.io/api/node 99 | 100 | .now 101 | .vercel 102 | .sentryclirc 103 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx pretty-quick --staged 5 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm test 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.vercelignore: -------------------------------------------------------------------------------- 1 | /build 2 | /tests 3 | /.envrc 4 | /.env 5 | /.github 6 | /.vscode 7 | coverage 8 | *.md 9 | *.log 10 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at y@uechi.io. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | ## Setup 4 | 5 | Install `vercel`: 6 | 7 | ``` 8 | npm i -g vercel 9 | ``` 10 | 11 | then install deps and fire up a dev server. 12 | 13 | ``` 14 | yarn install 15 | vc dev 16 | ``` 17 | 18 | Run tests before creating a pull request: 19 | 20 | ``` 21 | yarn test 22 | ``` 23 | 24 | ## Adding new provider 25 | 26 | Create `src/components/cards/providers/.tsx`. Here is the example card for checking GitHub namespaces. 27 | 28 | ```tsx 29 | import React from 'react'; 30 | import { useTranslation } from 'react-i18next'; 31 | import { FaGithub } from 'react-icons/fa'; 32 | 33 | import { Card, Repeater, DedicatedAvailability } from '../core'; 34 | 35 | const GithubCard: React.FC<{ query: string }> = ({ name }) => { 36 | const { t } = useTranslation(); 37 | const lowerCase = name.toLowerCase(); 38 | 39 | const names = [name]; 40 | const moreNames = [ 41 | `${lowerCase}hq`, 42 | `${lowerCase}-team`, 43 | `${lowerCase}-org`, 44 | `${lowerCase}-js`, 45 | ]; 46 | 47 | return ( 48 | 49 | 50 | {(name) => ( 51 | which is /api/services/github/[query].ts on GitHub 54 | link={`https://github.com/${name}`} 55 | prefix="github.com/" 56 | icon={} 57 | /> 58 | )} 59 | 60 | 61 | ); 62 | }; 63 | ``` 64 | 65 | and add the card to `src/components/cards/index.tsx`: 66 | 67 | ```jsx 68 | import NewCard from './providers/NewCard'; 69 | ``` 70 | 71 | ```patch 72 | <> 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | + 85 | 86 | 87 | ``` 88 | 89 | ### ExistentialAvailability 90 | 91 | `ExistentialAvailability` check if the response from passed URL returns `404` or not. 92 | For example, `` will send a request to `target` and see if it returns with 404. For security reasons, `target` must send back `Access-Control-Allow-Origin: *` header for bridging across cross-site requests. If they don't support `Access-Control-Allow-Origin`, you might want to use `DedicatedAvailability` for server-side handling. 93 | 94 | ### DedicatedAvailability 95 | 96 | `DedicatedAvailability` is for interacting with defined API endpoint to check availability. 97 | For example, `` will send a request to `https://namae.dev/availability//` which is routed to `/api/services/.js`. 98 | 99 | ## Adding a new language 100 | 101 | Suppose we'll add a support for Esperanto. First, copy `public/locales/en` folder and rename to `public/locales/eo` which is a language code for Esperanto. 102 | 103 | ```bash 104 | cd public/locales 105 | cp -r en eo 106 | ``` 107 | 108 | Then translate `eo/translation.json`. 109 | 110 | After that, edit `src/util/i18n.ts`: 111 | 112 | ```patch 113 | - const TRANSLATION_VERSION = '2'; 114 | + const TRANSLATION_VERSION = '3'; 115 | 116 | i18n 117 | .use(Backend) 118 | .use(LanguageDetector) 119 | .use(initReactI18next) 120 | .init({ 121 | backend: { 122 | backends: [LocalStorageBackend, XHR], 123 | backendOptions: [ 124 | { 125 | versions: { 126 | en: TRANSLATION_VERSION, 127 | ja: TRANSLATION_VERSION, 128 | 'zh-Hans': TRANSLATION_VERSION, 129 | 'zh-Hant': TRANSLATION_VERSION, 130 | + eo: TRANSLATION_VERSION, 131 | }, 132 | }, 133 | ], 134 | }, 135 | fallbackLng: 'en', 136 | debug: false, 137 | interpolation: { 138 | escapeValue: false, // not needed for react as it escapes by default 139 | }, 140 | }); 141 | ``` 142 | 143 | and `src/components/Footer.tsx`: 144 | 145 | ```patch 146 | const Languages = () => { 147 | const { t } = useTranslation(); 148 | 149 | return ( 150 | 151 | {t('language')} 152 | 178 | 179 | ); 180 | }; 181 | ``` 182 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # namae 2 | 3 | [![Actions Status](https://github.com/uetchy/namae/workflows/test/badge.svg)](https://github.com/uetchy/namae/actions) 4 | [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/0b8abd28e8c04affb2aac6d907ffa149)](https://www.codacy.com/manual/uetchy/namae?utm_source=github.com&utm_medium=referral&utm_content=uetchy/namae&utm_campaign=Badge_Coverage) 5 | [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/uetchy/namae.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/uetchy/namae/context:javascript) 6 | [![Total alerts](https://img.shields.io/lgtm/alerts/g/uetchy/namae.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/uetchy/namae/alerts/) 7 | ![hatena bookmark](https://badge.vercel.app/hatena/b/namae.dev) 8 | 9 | > name new project. 10 | 11 | namae saves your time searching around registries and checking if the desired name is ready for use. 12 | 13 | ## Contributing 14 | 15 | PRs welcome! 16 | 17 | See [Contribution Guide](./CONTRIBUTING.md) for the detailed instruction. 18 | 19 | ### Sponsors ⚡️ 20 | 21 | 22 | 23 | [](https://github.com/Naturalclar) [](https://github.com/Lierin8oracle) 24 | 25 | 26 | 27 | ### Contributors ✨ 28 | 29 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |

Yasuaki Uechi

💻 🌍

Mickaël Allonneau

💻

Example

💻

NixieSquid

💻 🌍

Arnaud Lier

🌍 💻

Raikas

💻

Jonah Snider

💻

Miguel Müller

🌍

Azurnex

🌍

Snazzah

💻

Thebigbot

🌍

BlackdestinyXX

💻

Daniils Petrovs

👀

Ben

🌍

Evgeniy Istomin

🌍

LukasPurbaW

🌍
58 | 59 | 60 | 61 | 62 | 63 | 64 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 65 | -------------------------------------------------------------------------------- /api/list/domain/[query].ts: -------------------------------------------------------------------------------- 1 | import { VercelRequest, VercelResponse } from '@vercel/node'; 2 | import assert from 'assert'; 3 | import fetch from 'cross-fetch'; 4 | import { send, sendError } from '../../../util/http'; 5 | 6 | export default async function handler( 7 | req: VercelRequest, 8 | res: VercelResponse 9 | ): Promise { 10 | const { query } = req.query; 11 | 12 | assert(process.env.DOMAINR_API_KEY); 13 | 14 | if (!query || typeof query !== 'string') { 15 | return sendError(res, new Error('No query given')); 16 | } 17 | 18 | try { 19 | const response = await fetch( 20 | `https://domainr.p.rapidapi.com/v2/search?query=${encodeURIComponent( 21 | query 22 | )}`, 23 | { 24 | headers: { 25 | 'X-RapidAPI-Key': process.env.DOMAINR_API_KEY, 26 | }, 27 | } 28 | ).then((res) => res.json()); 29 | send(res, response); 30 | } catch (err: any) { 31 | sendError(res, err); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /api/services/appstore/[query].ts: -------------------------------------------------------------------------------- 1 | import { send, sendError, fetch } from '../../../util/http'; 2 | import { VercelRequest, VercelResponse } from '@vercel/node'; 3 | 4 | interface App { 5 | trackId: string; 6 | trackName: string; 7 | kind: string; 8 | version: string; 9 | price: string; 10 | trackViewUrl: string; 11 | sellerName: string; 12 | formattedPrice: string; 13 | } 14 | 15 | interface AppStoreResponse { 16 | results: App[]; 17 | } 18 | 19 | export default async function handler( 20 | req: VercelRequest, 21 | res: VercelResponse 22 | ): Promise { 23 | const { query, country } = req.query; 24 | 25 | if (!query || typeof query !== 'string') { 26 | return sendError(res, new Error('No query given')); 27 | } 28 | 29 | const term = encodeURIComponent(query); 30 | const countryCode = country || 'us'; 31 | const limit = 5; 32 | 33 | try { 34 | const response = await fetch( 35 | `https://itunes.apple.com/search?media=software&entity=software,iPadSoftware,macSoftware&country=${countryCode}&limit=${limit}&term=${term}`, 36 | 'GET' 37 | ); 38 | const body: AppStoreResponse = await response.json(); 39 | const apps = body.results.map((app) => ({ 40 | id: app.trackId, 41 | name: app.trackName, 42 | author: app.sellerName, 43 | viewURL: app.trackViewUrl, 44 | })); 45 | send(res, { result: apps }); 46 | } catch (err: any) { 47 | sendError(res, err); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /api/services/archlinux/[query].ts: -------------------------------------------------------------------------------- 1 | import { send, sendError, fetch } from '../../../util/http'; 2 | import { VercelRequest, VercelResponse } from '@vercel/node'; 3 | 4 | export default async function handler( 5 | req: VercelRequest, 6 | res: VercelResponse 7 | ): Promise { 8 | const { query } = req.query; 9 | 10 | if (!query || typeof query !== 'string') { 11 | return sendError(res, new Error('No query given')); 12 | } 13 | 14 | if (/[^a-zA-Z0-9_-]/.test(query)) { 15 | return sendError(res, new Error('Invalid characters')); 16 | } 17 | 18 | try { 19 | const response = await fetch( 20 | `https://archlinux.org/packages/?sort=&q=${encodeURIComponent(query)}`, 21 | 'GET' 22 | ).then((res) => res.text()); 23 | const availability = !/
{ 8 | const { query } = req.query; 9 | 10 | if (!query || typeof query !== 'string') { 11 | return sendError(res, new Error('No query given')); 12 | } 13 | 14 | const term = encodeURIComponent(query); 15 | 16 | try { 17 | const response = await fetch( 18 | `https://chrome.google.com/webstore/ajax/item?hl=en&pv=20210820&count=112&category=extensions&searchTerm=${term}`, 19 | 'POST' 20 | ); 21 | const body = await response.text(); 22 | const json = JSON.parse(body.slice(5)); 23 | 24 | const items = json[1][1].map((item: any) => ({ 25 | id: item[0], 26 | name: item[1], 27 | description: item[6], 28 | url: item[37], 29 | })); 30 | send(res, { result: items.slice(0, 5) }); 31 | } catch (err: any) { 32 | sendError(res, err); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /api/services/debian/[query].ts: -------------------------------------------------------------------------------- 1 | import { send, sendError, fetch } from '../../../util/http'; 2 | import { VercelRequest, VercelResponse } from '@vercel/node'; 3 | 4 | export default async function handler( 5 | req: VercelRequest, 6 | res: VercelResponse 7 | ): Promise { 8 | const { query } = req.query; 9 | 10 | if (!query || typeof query !== 'string') { 11 | return sendError(res, new Error('No query given')); 12 | } 13 | 14 | if (/[^a-zA-Z0-9_-]/.test(query)) { 15 | return sendError(res, new Error('Invalid characters')); 16 | } 17 | 18 | try { 19 | const response = await fetch( 20 | `https://packages.debian.org/buster/${encodeURIComponent(query)}`, 21 | 'GET' 22 | ); 23 | const body = await response.text(); 24 | const availability = body.includes('No such package'); 25 | send(res, { availability }); 26 | } catch (err: any) { 27 | sendError(res, err); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/services/dns/[query].ts: -------------------------------------------------------------------------------- 1 | import dns from 'dns'; 2 | import { send, sendError } from '../../../util/http'; 3 | import { VercelRequest, VercelResponse } from '@vercel/node'; 4 | 5 | function resolvePromise(hostname: string): Promise { 6 | return new Promise((resolve, reject) => { 7 | dns.resolve4(hostname, function (err, addresses) { 8 | if (err) return reject(err); 9 | resolve(addresses); 10 | }); 11 | }); 12 | } 13 | 14 | export default async function handler( 15 | req: VercelRequest, 16 | res: VercelResponse 17 | ): Promise { 18 | const { query } = req.query; 19 | 20 | if (!query || typeof query !== 'string') { 21 | return sendError(res, new Error('No query given')); 22 | } 23 | 24 | try { 25 | const response = await resolvePromise(query); 26 | const availability = response && response.length > 0 ? false : true; 27 | send(res, { availability }); 28 | } catch (err: any) { 29 | if (err.code === 'ENODATA' || err.code === 'ENOTFOUND') { 30 | return send(res, { availability: true }); 31 | } 32 | sendError(res, err); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /api/services/domain/[query].ts: -------------------------------------------------------------------------------- 1 | import { VercelRequest, VercelResponse } from '@vercel/node'; 2 | import 'cross-fetch'; 3 | import whoiser from 'whoiser'; 4 | import { send, sendError } from '../../../util/http'; 5 | 6 | export default async function handler( 7 | req: VercelRequest, 8 | res: VercelResponse 9 | ): Promise { 10 | const { query } = req.query; 11 | 12 | if (!query || typeof query !== 'string') { 13 | return sendError(res, new Error('No query given')); 14 | } 15 | 16 | try { 17 | const response = await whoiser(query, { follow: 1, timeout: 5000 }); 18 | const first = Object.values(response)[0]; 19 | if (first.error) { 20 | throw new Error(`Got error while querying for ${query}: ${first.error}`); 21 | } 22 | try { 23 | const availability = first['Domain Status'].length > 0 ? false : true; 24 | send(res, { availability }); 25 | } catch (err) { 26 | console.log(response); 27 | } 28 | } catch (err: any) { 29 | sendError(res, err); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /api/services/existence/[query].ts: -------------------------------------------------------------------------------- 1 | import isURL from 'validator/lib/isURL'; 2 | import { send, sendError, fetch } from '../../../util/http'; 3 | import { VercelRequest, VercelResponse } from '@vercel/node'; 4 | 5 | export default async function handler( 6 | req: VercelRequest, 7 | res: VercelResponse 8 | ): Promise { 9 | const { query, existIf = '404' } = req.query; 10 | 11 | const availableStatus = (existIf as string).split(',').map((s) => s.trim()); 12 | 13 | if (!query || typeof query !== 'string') { 14 | return sendError(res, new Error('no query given')); 15 | } 16 | 17 | if (!isURL(query)) { 18 | return sendError(res, new Error('Invalid URL: ' + query)); 19 | } 20 | 21 | try { 22 | const response = await fetch(`https://${query}`); 23 | const availability = availableStatus.includes(response.status.toString()); 24 | send(res, { availability }); 25 | } catch (err: any) { 26 | if ((err as any).code === 'ENOTFOUND') { 27 | return send(res, { availability: true }); 28 | } 29 | sendError(res, err); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /api/services/firefox-addons/[query].ts: -------------------------------------------------------------------------------- 1 | import { send, sendError, fetch } from '../../../util/http'; 2 | import { VercelRequest, VercelResponse } from '@vercel/node'; 3 | 4 | export default async function handler( 5 | req: VercelRequest, 6 | res: VercelResponse 7 | ): Promise { 8 | const { query } = req.query; 9 | 10 | if (!query || typeof query !== 'string') { 11 | return sendError(res, new Error('No query given')); 12 | } 13 | 14 | const term = encodeURIComponent(query); 15 | 16 | try { 17 | const response = await fetch( 18 | `https://addons.mozilla.org/api/v5/addons/search/?app=firefox&appversion=100.0&type=extension&lang=en-US&q=${term}`, 19 | 'GET' 20 | ); 21 | const json = await response.json(); 22 | 23 | const items = json.results.map((item: any) => { 24 | const locale = item.name._default || 'en-US'; 25 | return { 26 | id: item.id, 27 | name: item.name[locale], 28 | description: item.summary[locale], 29 | url: item.url, 30 | author: item.authors[0].name, 31 | }; 32 | }); 33 | send(res, { result: items.slice(0, 5) }); 34 | } catch (err: any) { 35 | sendError(res, err); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/services/gitlab/[query].ts: -------------------------------------------------------------------------------- 1 | import { VercelRequest, VercelResponse } from '@vercel/node'; 2 | import nodeFetch from 'cross-fetch'; 3 | import { send, sendError } from '../../../util/http'; 4 | 5 | export default async function handler( 6 | req: VercelRequest, 7 | res: VercelResponse 8 | ): Promise { 9 | const { query } = req.query; 10 | 11 | if (!query || typeof query !== 'string') { 12 | return sendError(res, new Error('No query given')); 13 | } 14 | 15 | if (/[^a-zA-Z0-9_-]/.test(query)) { 16 | return sendError(res, new Error('Invalid characters')); 17 | } 18 | 19 | try { 20 | const response = await nodeFetch(`https://gitlab.com/${query}`, { 21 | redirect: 'manual', 22 | }); 23 | const availability = response.status === 302; 24 | send(res, { availability }); 25 | } catch (err: any) { 26 | sendError(res, err); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /api/services/jsorg/[query].ts: -------------------------------------------------------------------------------- 1 | import { VercelRequest, VercelResponse } from '@vercel/node'; 2 | import { fetch, send, sendError } from '../../../util/http'; 3 | 4 | export default async function handler( 5 | req: VercelRequest, 6 | res: VercelResponse 7 | ): Promise { 8 | const { query } = req.query; 9 | 10 | if (!query || typeof query !== 'string') { 11 | return sendError(res, new Error('No query given')); 12 | } 13 | 14 | if (!/([a-zA-Z0-9_-])\.js\.org/.test(query)) { 15 | return sendError(res, new Error('Invalid format')); 16 | } 17 | 18 | const cname = query.replace(/\.js\.org$/, ''); 19 | 20 | try { 21 | // Get cnames from js.org repo 22 | const source = await fetch( 23 | `https://raw.githubusercontent.com/js-org/js.org/master/cnames_active.js`, 24 | 'GET' 25 | ).then((res) => res.text()); 26 | const cnames = Array.from(source.matchAll(/^ "(.+)":/gm)).map((m) => m[1]); 27 | 28 | if (cnames.includes(cname)) { 29 | send(res, { availability: false }); 30 | } else { 31 | send(res, { availability: true }); 32 | } 33 | } catch (err: any) { 34 | console.log(err); 35 | sendError(res, err); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/services/launchpad/[query].ts: -------------------------------------------------------------------------------- 1 | import { send, sendError, fetch } from '../../../util/http'; 2 | import { VercelRequest, VercelResponse } from '@vercel/node'; 3 | 4 | export default async function handler( 5 | req: VercelRequest, 6 | res: VercelResponse 7 | ): Promise { 8 | const { query } = req.query; 9 | 10 | if (!query || typeof query !== 'string') { 11 | return sendError(res, new Error('No query given')); 12 | } 13 | 14 | if (/[^a-zA-Z0-9_-]/.test(query)) { 15 | return sendError(res, new Error('Invalid characters')); 16 | } 17 | 18 | try { 19 | const response = await fetch( 20 | `https://api.launchpad.net/devel/ubuntu/+source/${encodeURIComponent( 21 | query 22 | )}`, 23 | 'GET' 24 | ); 25 | const availability = response.status !== 200; 26 | send(res, { availability }); 27 | } catch (err: any) { 28 | sendError(res, err); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /api/services/npm-org/[query].ts: -------------------------------------------------------------------------------- 1 | import npmName from 'npm-name'; 2 | import { send, sendError } from '../../../util/http'; 3 | import { VercelRequest, VercelResponse } from '@vercel/node'; 4 | 5 | export default async function handler( 6 | req: VercelRequest, 7 | res: VercelResponse 8 | ): Promise { 9 | const { query } = req.query; 10 | 11 | if (!query || typeof query !== 'string') { 12 | return sendError(res, new Error('No query given')); 13 | } 14 | 15 | try { 16 | const availability = await npmName(`@${query}`); 17 | send(res, { availability }); 18 | } catch (err: any) { 19 | if (err.code === 'ENOTFOUND') { 20 | return send(res, { availability: true }); 21 | } 22 | sendError(res, err); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /api/services/npm/[query].ts: -------------------------------------------------------------------------------- 1 | import npmName from 'npm-name'; 2 | import { send, sendError } from '../../../util/http'; 3 | import { VercelRequest, VercelResponse } from '@vercel/node'; 4 | 5 | export default async function handler( 6 | req: VercelRequest, 7 | res: VercelResponse 8 | ): Promise { 9 | const { query } = req.query; 10 | 11 | if (!query || typeof query !== 'string') { 12 | return sendError(res, new Error('No query given')); 13 | } 14 | 15 | try { 16 | const availability = await npmName(query); 17 | send(res, { availability }); 18 | } catch (err: any) { 19 | sendError(res, err); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /api/services/nta/[query].ts: -------------------------------------------------------------------------------- 1 | import { send, sendError, fetch } from '../../../util/http'; 2 | import { VercelRequest, VercelResponse } from '@vercel/node'; 3 | 4 | const APPLICATION_ID = process.env.NTA_APPLICATION_ID; 5 | 6 | export default async function handler( 7 | req: VercelRequest, 8 | res: VercelResponse 9 | ): Promise { 10 | const { query } = req.query; 11 | 12 | if (!query || typeof query !== 'string') { 13 | return sendError(res, new Error('No query given')); 14 | } 15 | 16 | const encodedQuery = encodeURIComponent( 17 | query.replace(/[A-Za-z0-9]/g, (str) => 18 | String.fromCharCode(str.charCodeAt(0) + 0xfee0) 19 | ) 20 | ); 21 | 22 | try { 23 | const response = await fetch( 24 | `https://api.houjin-bangou.nta.go.jp/4/name?id=${APPLICATION_ID}&name=${encodedQuery}&mode=1&target=1&type=02`, 25 | 'GET' 26 | ); 27 | const body: string[] = (await response.text()).split('\n').slice(0, -1); 28 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 29 | const header = body.shift()!.split(','); 30 | const result = body.map((csv) => { 31 | const entry = csv.split(',').map((item) => 32 | item 33 | .replace(/(^"|"$)/g, '') 34 | .replace(/[A-Za-z0-9]/g, (str) => 35 | String.fromCharCode(str.charCodeAt(0) - 0xfee0) 36 | ) 37 | // eslint-disable-next-line no-irregular-whitespace 38 | .replace(/ /g, ' ') 39 | ); 40 | 41 | return { 42 | index: entry[0], 43 | orgID: entry[1], 44 | name: entry[6], 45 | englishName: entry[24], 46 | phoneticName: entry[28], 47 | type: entry[8], 48 | created: entry[22], 49 | lastUpdate: entry[4], 50 | lastModified: entry[5], 51 | imageID: entry[7], 52 | location: { 53 | pref: entry[9], 54 | city: entry[10], 55 | address: entry[11], 56 | imageID: entry[12], 57 | prefCode: entry[13], 58 | cityCode: entry[14], 59 | postalCode: entry[15], 60 | englishPref: entry[25], 61 | englishAddress: entry[26], 62 | }, 63 | foreignLocation: { 64 | address: entry[16], 65 | imageID: entry[17], 66 | englishForeignLocation: entry[27], 67 | }, 68 | historyCount: entry[23], 69 | closedAt: entry[18], 70 | closedReason: entry[19], 71 | successorOrgID: entry[20], 72 | memo: entry[21], 73 | excluded: entry[29], 74 | processSection: entry[2], 75 | modifiedSection: entry[3], 76 | }; 77 | }); 78 | 79 | send(res, { 80 | meta: { 81 | lastUpdate: header[0], 82 | count: parseInt(header[1]), 83 | cursor: parseInt(header[2]), 84 | pages: parseInt(header[3]), 85 | }, 86 | result: 87 | result 88 | .map((entry) => ({ 89 | name: entry.name, 90 | phoneticName: entry.phoneticName, 91 | englishName: entry.englishName, 92 | })) 93 | .slice(0, 5) || [], 94 | }); 95 | } catch (err: any) { 96 | sendError(res, err); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /api/services/playstore/[query].ts: -------------------------------------------------------------------------------- 1 | import { VercelRequest, VercelResponse } from '@vercel/node'; 2 | import 'cross-fetch'; 3 | import { fetch, send, sendError } from '../../../util/http'; 4 | 5 | export default async function handler( 6 | req: VercelRequest, 7 | res: VercelResponse 8 | ): Promise { 9 | const { query } = req.query; 10 | 11 | if (!query || typeof query !== 'string') { 12 | return sendError(res, new Error('No query given')); 13 | } 14 | 15 | try { 16 | const responseText = await fetch( 17 | `https://play.google.com/store/search?c=apps&q=${encodeURIComponent( 18 | query 19 | )}`, 20 | 'GET' 21 | ).then((res) => res.text()); 22 | 23 | const response = JSON.parse( 24 | responseText.match( 25 | /AF_initDataCallback.+?hash: '7'.+?data:([\w\W]+?), sideChannel/m 26 | )?.[1] ?? '' 27 | ); 28 | 29 | console.log(JSON.stringify(response[0][1][0][22][0][0][0], null, 2)); 30 | 31 | const list = response[0][1][0][22][0]; 32 | 33 | const apps = list.map((entry: any) => ({ 34 | id: entry[0][0][0], 35 | name: entry[0][3], 36 | description: entry[0][13][1], 37 | author: entry[0][14], 38 | url: 'https://play.google.com' + entry[0][10][4][2], 39 | })); 40 | send(res, { result: apps.slice(0, 5) }); 41 | } catch (err: any) { 42 | sendError(res, err); 43 | } 44 | } 45 | 46 | class WeirdJSONReader { 47 | p = 0; 48 | c = 0; 49 | buf: string; 50 | 51 | constructor(buf: string) { 52 | this.buf = buf; 53 | } 54 | 55 | parse() { 56 | this.verify(); 57 | const jsons = []; 58 | while (this.c < this.buf.length) { 59 | jsons.push(JSON.parse(this.eatPayload())); 60 | } 61 | return jsons; 62 | } 63 | 64 | verify() { 65 | if (!this.buf.startsWith(")]}'")) { 66 | throw new Error('Invalid payload'); 67 | } 68 | this.c = 6; 69 | } 70 | 71 | eatHeader() { 72 | this.p = this.c; 73 | while (this.buf[this.c] !== '\n' && this.c < this.buf.length) { 74 | this.c += 1; 75 | } 76 | return Number(this.buf.slice(this.p, this.c)); 77 | } 78 | 79 | eatPayload() { 80 | const blen = this.eatHeader(); 81 | this.p = this.c; 82 | this.c = this.c + blen; 83 | return this.buf.slice(this.p, this.c); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /api/services/reddit/[query].ts: -------------------------------------------------------------------------------- 1 | import { send, sendError, fetch } from '../../../util/http'; 2 | import { VercelRequest, VercelResponse } from '@vercel/node'; 3 | 4 | export default async function handler( 5 | req: VercelRequest, 6 | res: VercelResponse 7 | ): Promise { 8 | const { query } = req.query; 9 | 10 | if (!query || typeof query !== 'string') { 11 | return sendError(res, new Error('No query given')); 12 | } 13 | 14 | if (/[^a-zA-Z0-9_]/.test(query)) { 15 | return sendError(res, new Error('Invalid characters')); 16 | } 17 | 18 | try { 19 | const response = await fetch(`https://reddit.com/r/${query}`, 'GET'); 20 | const body = await response.text(); 21 | const availability = body.includes( 22 | 'Sorry, there aren’t any communities on Reddit with that name.' 23 | ); 24 | send(res, { availability }); 25 | } catch (err: any) { 26 | sendError(res, err); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /api/services/slack/[query].ts: -------------------------------------------------------------------------------- 1 | import { send, sendError, fetch } from '../../../util/http'; 2 | import { VercelRequest, VercelResponse } from '@vercel/node'; 3 | 4 | export default async function handler( 5 | req: VercelRequest, 6 | res: VercelResponse 7 | ): Promise { 8 | const { query } = req.query; 9 | 10 | if (!query || typeof query !== 'string') { 11 | return sendError(res, new Error('No query given')); 12 | } 13 | 14 | if (/[^a-zA-Z0-9_-]/.test(query)) { 15 | return sendError(res, new Error('Invalid characters')); 16 | } 17 | 18 | try { 19 | const response = await fetch( 20 | `https://${encodeURIComponent(query)}.slack.com` 21 | ); 22 | const availability = response.status !== 200; 23 | send(res, { availability }); 24 | } catch (err: any) { 25 | sendError(res, err); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api/services/twitter/[query].ts: -------------------------------------------------------------------------------- 1 | import { VercelRequest, VercelResponse } from '@vercel/node'; 2 | import { fetch, send, sendError } from '../../../util/http'; 3 | 4 | export default async function handler( 5 | req: VercelRequest, 6 | res: VercelResponse 7 | ): Promise { 8 | const { query } = req.query; 9 | 10 | if (!query || typeof query !== 'string') { 11 | return sendError(res, new Error('No query given')); 12 | } 13 | 14 | if (/[^a-zA-Z0-9_]/.test(query)) { 15 | return sendError(res, new Error('Invalid characters')); 16 | } 17 | 18 | try { 19 | const response = await fetch( 20 | `https://api.twitter.com/i/users/username_available.json?username=${query}`, 21 | 'GET' 22 | ).then((res) => res.json()); 23 | const availability = response.valid; 24 | send(res, { availability }); 25 | } catch (err: any) { 26 | sendError(res, err); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | automock: false, 3 | setupFiles: ['./setupJest.ts'], 4 | testEnvironment: 'node', 5 | preset: 'ts-jest', 6 | testPathIgnorePatterns: ['/dist/', '/src/'], 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "namae", 3 | "description": "namae saves your time searching around registries and checking if the desired name is ready for use.", 4 | "author": "Yasuaki Uechi (https://uechi.io/)", 5 | "scripts": { 6 | "build": "NODE_ENV=production react-scripts build", 7 | "dev": "BROWSER=none react-scripts start", 8 | "eject": "react-scripts eject", 9 | "prepare": "husky install", 10 | "readme": "mdmod README.md --define.owner uetchy", 11 | "test": "tsc --noEmit && jest --coverage && CI=true react-scripts test --coverage" 12 | }, 13 | "dependencies": { 14 | "@sentry/browser": "^6.19.2", 15 | "cross-fetch": "^3.1.5", 16 | "easy-peasy": "^5.0.4", 17 | "fetch-suspense": "^1.2.2", 18 | "framer-motion": "^6.2.8", 19 | "i18next": ">=21.6.14", 20 | "i18next-browser-languagedetector": "^6.1.4", 21 | "i18next-chained-backend": "^3.0.2", 22 | "i18next-localstorage-backend": "^3.1.3", 23 | "i18next-xhr-backend": "^3.2.2", 24 | "mersennetwister": "^0.2.3", 25 | "npm-name": "6.0.1", 26 | "rc-tooltip": "^5.1.1", 27 | "react": "^17.0.2", 28 | "react-dom": "^17.0.2", 29 | "react-helmet": "^6.0.0", 30 | "react-i18next": "11.16.1", 31 | "react-icons": "^4.3.1", 32 | "react-router": "^6.2.2", 33 | "react-router-dom": "^6.2.2", 34 | "react-scripts": "5.0.0", 35 | "react-spinners": "^0.11.0", 36 | "react-toastify": "^8.2.0", 37 | "styled-components": "^5.3.5", 38 | "swr": "^1.2.2", 39 | "validator": "^13.7.0", 40 | "whoiser": "^1.13.1" 41 | }, 42 | "devDependencies": { 43 | "@sentry/cli": "^1.74.2", 44 | "@testing-library/jest-dom": "^5.16.3", 45 | "@testing-library/react": "^12.1.4", 46 | "@types/i18next-node-fs-backend": "^2.1.1", 47 | "@types/jest": "^27.4.1", 48 | "@types/mersennetwister": "^0.2.0", 49 | "@types/node": "^16.11.7", 50 | "@types/react-dom": "^17.0.14", 51 | "@types/react-helmet": "^6.1.5", 52 | "@types/react-router-dom": "^5.3.3", 53 | "@types/styled-components": "^5.1.24", 54 | "@types/validator": "^13.7.1", 55 | "@vercel/build-utils": "^2.15.0", 56 | "@vercel/node": "^1.14.0", 57 | "codacy-coverage": "^3.4.0", 58 | "husky": "^7.0.4", 59 | "i18next-node-fs-backend": "^2.1.3", 60 | "mdmod": "^2.0.0", 61 | "mdmod-plugin-github-sponsors": "^1.1.0", 62 | "mutationobserver-shim": "^0.3.5", 63 | "nock": "^13.2.4", 64 | "prettier": "^2.6.1", 65 | "pretty-quick": "^3.1.3", 66 | "ts-jest": "^27.1.4", 67 | "typescript": "^4.6.3" 68 | }, 69 | "license": "Apache-2.0", 70 | "browserslist": { 71 | "production": [ 72 | ">0.2%", 73 | "not dead", 74 | "not op_mini all" 75 | ], 76 | "development": [ 77 | "last 1 chrome version", 78 | "last 1 firefox version", 79 | "last 1 safari version" 80 | ] 81 | }, 82 | "engines": { 83 | "node": ">=14.0.0" 84 | }, 85 | "eslintConfig": { 86 | "extends": "react-app" 87 | }, 88 | "private": true 89 | } 90 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/namae/ad6052c29a21c8c80bb10047cf4e1536543701e9/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/namae/ad6052c29a21c8c80bb10047cf4e1536543701e9/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/namae/ad6052c29a21c8c80bb10047cf4e1536543701e9/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #6f3dff 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/namae/ad6052c29a21c8c80bb10047cf4e1536543701e9/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/namae/ad6052c29a21c8c80bb10047cf4e1536543701e9/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/namae/ad6052c29a21c8c80bb10047cf4e1536543701e9/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 30 | 36 | 41 | 42 | 43 | 44 | 48 | 49 | 50 | 51 | 52 | 53 | 57 | 63 | namae 64 | 65 | 66 | 67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /public/locales/de/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Zusammenfassung", 3 | "available": "Verfügbar", 4 | "blog": "Blog-Artikel", 5 | "community": "Gemeinschaft", 6 | "contributors": "Mitwirkende", 7 | "countryCode": "de", 8 | "description": "Überprüfen Sie sofort die Verfügbarkeit Ihres neuen Anwendungsnamens in allen wichtigen Registern.", 9 | "gettingStarted": "Fertig werden", 10 | "gettingStartedWithExample": "Untersuche namae", 11 | "issues": "Issues", 12 | "language": "Sprache", 13 | "noResult": "Kein Ergebnis", 14 | "placeholder": "suchen", 15 | "pressEnterToSearch": "Drücken Sie die Eingabetaste, um zu suchen", 16 | "providers": { 17 | "appStore": "App Store", 18 | "domains": "Domänen", 19 | "firebase": "Firebase", 20 | "github": "GitHub Organisation", 21 | "githubSearch": "GitHub Repository", 22 | "gitlab": "GitLab", 23 | "google": "Google-Suche", 24 | "heroku": "Heroku", 25 | "homebrew": "Homebrew", 26 | "instagram": "Instagram", 27 | "jsorg": "js.org", 28 | "modland": "mod.land", 29 | "linux": "Linux", 30 | "netlify": "Netlify", 31 | "now": "Vercel", 32 | "npm": "npm", 33 | "nta": "Firma (JP)", 34 | "ocaml": "OCaml", 35 | "pypi": "PyPI", 36 | "rubygems": "RubyGems", 37 | "rust": "Rust", 38 | "s3": "AWS S3", 39 | "slack": "Slack", 40 | "spectrum": "Spectrum", 41 | "reddit": "Reddit", 42 | "twitter": "Twitter" 43 | }, 44 | "showMore": "Zeig mehr", 45 | "title": "Schnapp dir einen guten Namen für deine neue App", 46 | "try": "Wie wäre es mit?", 47 | "unavailable": "Nicht verfügbar", 48 | "uniqueness": { 49 | "description": "UNIQ zeigt, wie eindeutig der Name ist (0 bis 100)", 50 | "high": "Ziemlich einzigartig", 51 | "low": "Typisch", 52 | "moderate": "Mäßig einzigartig" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/locales/en/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "About", 3 | "available": "Available", 4 | "blog": "Introduction", 5 | "community": "Community", 6 | "contributors": "Contributors", 7 | "countryCode": "us", 8 | "title": "Grab a slick name for your new project", 9 | "description": "Check availability of your new app/library/organization name ideas across all major registries at once.", 10 | "gettingStarted": "Getting Started", 11 | "gettingStartedWithExample": "Try with namae", 12 | "issues": "Issues", 13 | "language": "Language", 14 | "noResult": "No Result", 15 | "placeholder": "search", 16 | "pressEnterToSearch": "Press Enter to search", 17 | "providers": { 18 | "appStore": "App Store", 19 | "archlinux": "Arch Linux", 20 | "ubuntu": "Ubuntu", 21 | "debian": "Debian", 22 | "domains": "Domain", 23 | "firebase": "Firebase", 24 | "github": "GitHub Organization", 25 | "githubSearch": "GitHub Repository", 26 | "gitlab": "GitLab", 27 | "google": "Google Search", 28 | "heroku": "Heroku", 29 | "homebrew": "Homebrew", 30 | "instagram": "Instagram", 31 | "jsorg": "js.org", 32 | "modland": "mod.land", 33 | "linux": "Linux", 34 | "netlify": "Netlify", 35 | "now": "Vercel", 36 | "npm": "npm", 37 | "nta": "Company (Japan)", 38 | "ocaml": "OCaml", 39 | "playStore": "Google Play Store", 40 | "pypi": "PyPI", 41 | "rubygems": "RubyGems", 42 | "rust": "Rust", 43 | "s3": "AWS S3", 44 | "slack": "Slack", 45 | "spectrum": "Spectrum", 46 | "reddit": "Reddit", 47 | "twitter": "Twitter", 48 | "cloudflare": "Cloudflare Pages", 49 | "chromeWebStore": "Chrome Web Store", 50 | "firefoxAddons": "Firefox Add-ons", 51 | "youtube": "YouTube", 52 | "hexpm": "Hex", 53 | "fly": "Fly.io", 54 | "productHunt": "Product Hunt", 55 | "docker": "Docker Hub" 56 | }, 57 | "showMore": "show more", 58 | "try": "How about this?", 59 | "unavailable": "Unavailable", 60 | "uniqueness": { 61 | "description": "UNIQ shows how unique the name is (0 to 100)", 62 | "high": "Pretty unique", 63 | "low": "Common", 64 | "moderate": "Rare" 65 | }, 66 | "join-us": "Send <1>a pull request and become a contributor!", 67 | "section": { 68 | "starter": "Starter", 69 | "social": "Social Network", 70 | "package": "Package", 71 | "web": "Web Platform", 72 | "app": "App & Extension" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /public/locales/es/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Acerca de", 3 | "available": "Disponible", 4 | "blog": "Artículo de blog", 5 | "community": "Comunidad", 6 | "contributors": "Colaboradores", 7 | "countryCode": "mx", 8 | "description": "Verifique la disponibilidad de sus nuevas ideas de nombres de aplicaciones en los principales registros a la vez.", 9 | "gettingStarted": "Empezando", 10 | "gettingStartedWithExample": "Prueba con namae", 11 | "issues": "Issues", 12 | "language": "Idioma", 13 | "noResult": "Sin resultados", 14 | "placeholder": "Buscar", 15 | "pressEnterToSearch": "Presiona Enter para buscar", 16 | "providers": { 17 | "appStore": "App Store", 18 | "archlinux": "Arch Linux", 19 | "ubuntu": "Ubuntu", 20 | "debian": "Debian", 21 | "domains": "Dominios", 22 | "firebase": "Firebase", 23 | "github": "Organización de GitHub", 24 | "githubSearch": "Repositorio de GitHub", 25 | "gitlab": "GitLab", 26 | "google": "Google Search", 27 | "heroku": "Heroku", 28 | "homebrew": "Homebrew", 29 | "instagram": "Instagram", 30 | "jsorg": "js.org", 31 | "modland": "mod.land", 32 | "linux": "Linux", 33 | "netlify": "Netlify", 34 | "now": "Vercel", 35 | "npm": "npm", 36 | "nta": "Empresa (JP)", 37 | "ocaml": "OCaml", 38 | "playStore": "Google Play Store", 39 | "pypi": "PyPI", 40 | "rubygems": "RubyGems", 41 | "rust": "Rust", 42 | "s3": "AWS S3", 43 | "slack": "Slack", 44 | "spectrum": "Spectrum", 45 | "reddit": "Reddit", 46 | "twitter": "Twitter" 47 | }, 48 | "showMore": "Mostrar más", 49 | "title": "Escoge un nombre ingenioso para su nueva aplicación", 50 | "try": "¿Qué tal esto?", 51 | "unavailable": "Sin disponibilidad", 52 | "uniqueness": { 53 | "description": "UNIQ muestra cuán único es el nombre (0 a 100)", 54 | "high": "Bastante único", 55 | "low": "Típico", 56 | "moderate": "Moderadamente único" 57 | }, 58 | "join-us": "¡Envía <1>una pull request y conviértete en colaborador!" 59 | } 60 | -------------------------------------------------------------------------------- /public/locales/fr/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "À propos", 3 | "available": "Disponible", 4 | "blog": "Article de Blog", 5 | "community": "Communauté", 6 | "contributors": "Contributeurs", 7 | "countryCode": "fr", 8 | "description": "Vérifiez immédiatement la disponibilité de vos nouvelles idées de noms d'applications dans les principaux registres.", 9 | "gettingStarted": "Pour commencer", 10 | "gettingStartedWithExample": "Essayez avec namae", 11 | "issues": "Issues", 12 | "language": "Langue", 13 | "noResult": "Pas de résultat", 14 | "placeholder": "rechercher", 15 | "pressEnterToSearch": "Appuyez sur la touche Entrée pour effectuer une recherche", 16 | "providers": { 17 | "appStore": "App Store", 18 | "domains": "Domaines", 19 | "firebase": "Firebase", 20 | "github": "Organisation GitHub", 21 | "githubSearch": "Dépôt GitHub", 22 | "gitlab": "GitLab", 23 | "google": "Recherche Google", 24 | "heroku": "Heroku", 25 | "homebrew": "Homebrew", 26 | "instagram": "Instagram", 27 | "jsorg": "js.org", 28 | "modland": "mod.land", 29 | "linux": "Linux", 30 | "netlify": "Netlify", 31 | "now": "Vercel", 32 | "npm": "npm", 33 | "nta": "Company (JP)", 34 | "ocaml": "OCaml", 35 | "pypi": "PyPI", 36 | "rubygems": "RubyGems", 37 | "rust": "Rust", 38 | "s3": "AWS S3", 39 | "slack": "Slack", 40 | "spectrum": "Spectrum", 41 | "reddit": "Reddit", 42 | "twitter": "Twitter" 43 | }, 44 | "showMore": "afficher plus", 45 | "title": "Trouvez un nom original pour votre nouvelle application", 46 | "try": "Que pensez-vous de cela ?", 47 | "unavailable": "Indisponible", 48 | "uniqueness": { 49 | "description": "L'UNIQ indique à quel point le nom est unique (0 à 100)", 50 | "high": "Plutôt unique", 51 | "low": "Typique", 52 | "moderate": "Modérément unique" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/locales/id/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Tentang Kami", 3 | "available": "Tersedia", 4 | "blog": "Pengenalan", 5 | "community": "Komunitas", 6 | "contributors": "Kontributor", 7 | "countryCode": "id", 8 | "title": "Buat nama proyekmu jadi lebih keren", 9 | "description": "Cek ketersediaan nama aplikasi dan organisasimu di berbagai platform dengan mudah.", 10 | "gettingStarted": "Mulai", 11 | "gettingStartedWithExample": "Coba dengan namae", 12 | "issues": "Terbitan", 13 | "language": "Bahasa", 14 | "noResult": "Tidak ada hasil", 15 | "placeholder": "Cari", 16 | "pressEnterToSearch": "Tekan Enter untuk memulai pencarian", 17 | "providers": { 18 | "appStore": "App Store", 19 | "archlinux": "Arch Linux", 20 | "ubuntu": "Ubuntu", 21 | "debian": "Debian", 22 | "domains": "Domain", 23 | "firebase": "Firebase", 24 | "github": "Organisasi GitHub", 25 | "githubSearch": "Repositori Github", 26 | "gitlab": "GitLab", 27 | "google": "Pencarian Google", 28 | "heroku": "Heroku", 29 | "homebrew": "Homebrew", 30 | "instagram": "Instagram", 31 | "jsorg": "js.org", 32 | "modland": "mod.land", 33 | "linux": "Linux", 34 | "netlify": "Netlify", 35 | "now": "Vercel", 36 | "npm": "npm", 37 | "nta": "Perusahaan (JP)", 38 | "ocaml": "OCaml", 39 | "playStore": "Google Play Store", 40 | "pypi": "PyPI", 41 | "rubygems": "RubyGems", 42 | "rust": "Rust", 43 | "s3": "AWS S3", 44 | "slack": "Slack", 45 | "spectrum": "Spectrum", 46 | "reddit": "Reddit", 47 | "twitter": "Twitter", 48 | "cloudflare": "Halaman Cloudflare", 49 | "chromeWebStore": "Chrome Web Store", 50 | "firefoxAddons": "Firefox Add-ons", 51 | "youtube": "YouTube" 52 | }, 53 | "showMore": "Lihat lebih banyak", 54 | "try": "Coba nama-nama berikut", 55 | "unavailable": "Tidak tersedia", 56 | "uniqueness": { 57 | "description": "UNIQ memperlihatkan seberapa unik nama tersebut (skala 0 hingga 100)", 58 | "high": "Sangat Unik", 59 | "low": "Normal", 60 | "moderate": "Cukup Unik" 61 | }, 62 | "join-us": "Kirim <1>pull request dan jadilah bagian dari tim kontributor!" 63 | } 64 | -------------------------------------------------------------------------------- /public/locales/it/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Più informazioni su di noi", 3 | "available": "Disponibile", 4 | "blog": "Articoli del blog", 5 | "community": "Comunità", 6 | "contributors": "Contributori", 7 | "countryCode": "it", 8 | "description": "Verifica la disponibilità dei nomi che vuoi dare ai tuoi progetti in tutti i registri principali contemporaneamente.", 9 | "gettingStarted": "Inizia", 10 | "gettingStartedWithExample": "Prova con namae", 11 | "issues": "Problemi", 12 | "language": "Lingue", 13 | "noResult": "Nessun risultato", 14 | "placeholder": "Cerca", 15 | "pressEnterToSearch": "Premi Invio per cercare", 16 | "providers": { 17 | "appStore": "App Store", 18 | "archlinux": "Arch Linux", 19 | "ubuntu": "Ubuntu", 20 | "debian": "Debian", 21 | "domains": "Domini", 22 | "firebase": "Firebase", 23 | "github": "GitHub Organization", 24 | "githubSearch": "GitHub Repository", 25 | "gitlab": "GitLab", 26 | "google": "Google Search", 27 | "heroku": "Heroku", 28 | "homebrew": "Homebrew", 29 | "instagram": "Instagram", 30 | "jsorg": "js.org", 31 | "modland": "mod.land", 32 | "linux": "Linux", 33 | "netlify": "Netlify", 34 | "now": "Vercel", 35 | "npm": "npm", 36 | "nta": "Company (JP)", 37 | "ocaml": "OCaml", 38 | "playStore": "Google Play Store", 39 | "pypi": "PyPI", 40 | "rubygems": "RubyGems", 41 | "rust": "Rust", 42 | "s3": "AWS S3", 43 | "slack": "Slack", 44 | "spectrum": "Spectrum", 45 | "reddit": "Reddit", 46 | "twitter": "Twitter", 47 | "cloudflare": "Cloudflare Pages" 48 | }, 49 | "showMore": "mostra di più", 50 | "title": "Scegli un nome elegante per la tua nuova app", 51 | "try": "Cosa ne pensi di questi?", 52 | "unavailable": "Non disponibile", 53 | "uniqueness": { 54 | "description": "UNIQ mostra quanto il tuo nome è unico (da 0 a 100)", 55 | "high": "Molto raro", 56 | "low": "Comune", 57 | "moderate": "Raro" 58 | }, 59 | "join-us": "Invia <1>una pull request e diventa un contributore!" 60 | } 61 | -------------------------------------------------------------------------------- /public/locales/ja/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "概要", 3 | "available": "取得できます", 4 | "blog": "紹介", 5 | "community": "コミュニティ", 6 | "contributors": "貢献者", 7 | "countryCode": "jp", 8 | "description": "namae はあなたのアプリ、ライブラリ、組織に素敵な名前をつけるお手伝いをします。", 9 | "gettingStarted": "はじめる", 10 | "gettingStartedWithExample": "namae を調べる", 11 | "issues": "Issues", 12 | "language": "言語", 13 | "noResult": "該当なし", 14 | "placeholder": "調べる", 15 | "pressEnterToSearch": "エンターキーで検索", 16 | "providers": { 17 | "appStore": "App Store", 18 | "domains": "ドメイン", 19 | "firebase": "Firebase", 20 | "github": "GitHub Organization", 21 | "githubSearch": "GitHub リポジトリ", 22 | "gitlab": "GitLab", 23 | "google": "Google 検索", 24 | "heroku": "Heroku", 25 | "homebrew": "Homebrew", 26 | "instagram": "Instagram", 27 | "jsorg": "js.org", 28 | "modland": "mod.land", 29 | "linux": "Linux", 30 | "netlify": "Netlify", 31 | "now": "Vercel", 32 | "npm": "npm", 33 | "nta": "法人 (日本)", 34 | "ocaml": "OCaml", 35 | "pypi": "PyPI", 36 | "rubygems": "RubyGems", 37 | "rust": "Rust", 38 | "s3": "AWS S3", 39 | "slack": "Slack", 40 | "spectrum": "Spectrum", 41 | "reddit": "Reddit", 42 | "twitter": "Twitter" 43 | }, 44 | "showMore": "さらに表示", 45 | "title": "唯一無二の名前を見つける", 46 | "try": "これはどう?", 47 | "unavailable": "取得できません", 48 | "uniqueness": { 49 | "description": "名前がどれくらいユニークかを示しています(0→ありきたり 100→超ユニーク)", 50 | "high": "超ユニーク", 51 | "low": "普通", 52 | "moderate": "そこそこユニーク" 53 | }, 54 | "join-us": "<1>プルリクエストを送ってコントリビューターになりましょう!", 55 | "section": { 56 | "starter": "基本", 57 | "social": "SNS", 58 | "package": "パッケージ", 59 | "web": "Web", 60 | "app": "アプリとエクステンション" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /public/locales/nl/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Over", 3 | "available": "Beschikbaar", 4 | "blog": "Introductie", 5 | "community": "Gemeenschap", 6 | "contributors": "Bijdragers", 7 | "countryCode": "nl", 8 | "title": "Krijg een geweldige naam voor je nieuwe project", 9 | "description": "Controleer de beschikbaarheid van uw nieuwe app-/library-/organisatienaam ideeën in alle grote registers tegelijk.", 10 | "gettingStarted": "Ermee Beginnen", 11 | "gettingStartedWithExample": "Probeer met namae", 12 | "issues": "Problemen", 13 | "language": "Taal", 14 | "noResult": "Geen Resultaat", 15 | "placeholder": "zoek", 16 | "pressEnterToSearch": "Druk op Enter om te zoeken", 17 | "providers": { 18 | "appStore": "App Store", 19 | "archlinux": "Arch Linux", 20 | "ubuntu": "Ubuntu", 21 | "debian": "Debian", 22 | "domains": "Domeinen", 23 | "firebase": "Firebase", 24 | "github": "GitHub Organisaties", 25 | "githubSearch": "GitHub Repo's", 26 | "gitlab": "GitLab", 27 | "google": "Google Zoeken", 28 | "heroku": "Heroku", 29 | "homebrew": "Homebrew", 30 | "instagram": "Instagram", 31 | "jsorg": "js.org", 32 | "modland": "mod.land", 33 | "linux": "Linux", 34 | "netlify": "Netlify", 35 | "now": "Vercel", 36 | "npm": "npm", 37 | "nta": "Company (JP)", 38 | "ocaml": "OCaml", 39 | "playStore": "Google Play Store", 40 | "pypi": "PyPI", 41 | "rubygems": "RubyGems", 42 | "rust": "Rust", 43 | "s3": "AWS S3", 44 | "slack": "Slack", 45 | "spectrum": "Spectrum", 46 | "reddit": "Reddit", 47 | "twitter": "Twitter", 48 | "cloudflare": "Cloudflare Pages", 49 | "chromeWebStore": "Chrome Web Store", 50 | "firefoxAddons": "Firefox Add-ons", 51 | "youtube": "YouTube" 52 | }, 53 | "showMore": "laat meer zien", 54 | "try": "Wat dacht je hiervan?", 55 | "unavailable": "Onbeschikbaar", 56 | "uniqueness": { 57 | "description": "UNIQ laat zien hoe uniek de naam is (0 tot 100)", 58 | "high": "Vrij uniek", 59 | "low": "Vaak voorkomend", 60 | "moderate": "Zeldzaam" 61 | }, 62 | "join-us": "Stuur <1>een pull request en word een bijdrager!" 63 | } 64 | -------------------------------------------------------------------------------- /public/locales/pt-BR/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Sobre", 3 | "available": "Disponível", 4 | "blog": "Artigo do Blog", 5 | "community": "Comunidade", 6 | "contributors": "Contribuídores", 7 | "countryCode": "pt-BR", 8 | "description": "Verifique imediatamente a disponibilidade de nomes para suas ideias de apps nos principais serviços.", 9 | "gettingStarted": "Começando", 10 | "gettingStartedWithExample": "Tente com namae", 11 | "issues": "Issues", 12 | "language": "Idioma", 13 | "noResult": "Sem resultados", 14 | "placeholder": "busca", 15 | "pressEnterToSearch": "Pressione Enter para procurar", 16 | "providers": { 17 | "appStore": "App Store", 18 | "archlinux": "Arch Linux", 19 | "ubuntu": "Ubuntu", 20 | "debian": "Debian", 21 | "domains": "Domínios", 22 | "firebase": "Firebase", 23 | "github": "GitHub Organization", 24 | "githubSearch": "GitHub Repository", 25 | "gitlab": "GitLab", 26 | "google": "Google Search", 27 | "heroku": "Heroku", 28 | "homebrew": "Homebrew", 29 | "instagram": "Instagram", 30 | "jsorg": "js.org", 31 | "modland": "mod.land", 32 | "linux": "Linux", 33 | "netlify": "Netlify", 34 | "now": "Vercel", 35 | "npm": "npm", 36 | "nta": "Company (JP)", 37 | "ocaml": "OCaml", 38 | "playStore": "Google Play Store", 39 | "pypi": "PyPI", 40 | "rubygems": "RubyGems", 41 | "rust": "Rust", 42 | "s3": "AWS S3", 43 | "slack": "Slack", 44 | "spectrum": "Spectrum", 45 | "reddit": "Reddit", 46 | "twitter": "Twitter" 47 | }, 48 | "showMore": "Mostrar +", 49 | "title": "Encontre um nome original para seu novo aplicativo", 50 | "try": "Que tal agora?", 51 | "unavailable": "Indisponível", 52 | "uniqueness": { 53 | "description": "UNIQ mostra em uma escala de 0-10 quão único é nome", 54 | "high": "Muito único", 55 | "low": "Tipico", 56 | "moderate": "Moderadamente único" 57 | }, 58 | "join-us": "Envie <1>uma pull request e seja um contribuidor!" 59 | } 60 | -------------------------------------------------------------------------------- /public/locales/ru/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "О сайте", 3 | "available": "Доступно", 4 | "blog": "Описание работы сайта", 5 | "community": "Сообщество", 6 | "contributors": "Контрибьюторы", 7 | "countryCode": "ru", 8 | "title": "Выберите новое уникальное имя для Вашего проекта", 9 | "description": "Проверьте доступность идей для названия Вашего нового приложения/библиотеки/организации сразу во всех самых крупных регистрах.", 10 | "gettingStarted": "Начать", 11 | "gettingStartedWithExample": "Попробовать сначала с namae", 12 | "issues": "Issues", 13 | "language": "Язык", 14 | "noResult": "Результаты отсутствуют", 15 | "placeholder": "Поиск", 16 | "pressEnterToSearch": "Нажмите Enter, чтобы начать поиск", 17 | "providers": { 18 | "appStore": "App Store", 19 | "archlinux": "Arch Linux", 20 | "ubuntu": "Ubuntu", 21 | "debian": "Debian", 22 | "domains": "Домены", 23 | "firebase": "Firebase", 24 | "github": "Организация GitHub", 25 | "githubSearch": "Репозиторий GitHub", 26 | "gitlab": "GitLab", 27 | "google": "Google Search", 28 | "heroku": "Heroku", 29 | "homebrew": "Homebrew", 30 | "instagram": "Instagram", 31 | "jsorg": "js.org", 32 | "modland": "mod.land", 33 | "linux": "Linux", 34 | "netlify": "Netlify", 35 | "now": "Vercel", 36 | "npm": "npm", 37 | "nta": "Компания (Япония)", 38 | "ocaml": "OCaml", 39 | "playStore": "Магазин Google Play", 40 | "pypi": "PyPI", 41 | "rubygems": "RubyGems", 42 | "rust": "Rust", 43 | "s3": "AWS S3", 44 | "slack": "Slack", 45 | "spectrum": "Spectrum", 46 | "reddit": "Reddit", 47 | "twitter": "Twitter", 48 | "cloudflare": "Cloudflare Pages", 49 | "chromeWebStore": "Chrome Web Store", 50 | "firefoxAddons": "Аддоны Firefox", 51 | "youtube": "YouTube" 52 | }, 53 | "showMore": "Показать ещё", 54 | "try": "Как насчёт этого?", 55 | "unavailable": "Недоступно", 56 | "uniqueness": { 57 | "description": "Шкала УНИК показывает, насколько уникально данное имя (от 0 до 100)", 58 | "high": "Довольно-таки уникальное", 59 | "low": "Часто используемое", 60 | "moderate": "Редко используемое" 61 | }, 62 | "join-us": "Создайте <1>пулл риквест и станьте контрибьютором!" 63 | } 64 | -------------------------------------------------------------------------------- /public/locales/zh-Hans/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "关于", 3 | "available": "可获得", 4 | "blog": "博客", 5 | "community": "社区", 6 | "contributors": "贡献者", 7 | "countryCode": "jp", 8 | "description": "namae可让您给您的应用程序、Web服务或组织起一个好名字。", 9 | "gettingStarted": "起步", 10 | "gettingStartedWithExample": "试试 namae", 11 | "issues": "Issues", 12 | "language": "语言", 13 | "noResult": "无结果", 14 | "placeholder": "检验", 15 | "pressEnterToSearch": "按Enter键搜索", 16 | "providers": { 17 | "appStore": "App Store", 18 | "domains": "域名", 19 | "firebase": "Firebase", 20 | "github": "GitHub 组织", 21 | "githubSearch": "GitHub 仓库", 22 | "gitlab": "GitLab", 23 | "google": "谷歌", 24 | "heroku": "Heroku", 25 | "homebrew": "Homebrew", 26 | "instagram": "Instagram", 27 | "jsorg": "js.org", 28 | "modland": "mod.land", 29 | "linux": "Linux", 30 | "netlify": "Netlify", 31 | "now": "Vercel", 32 | "npm": "npm", 33 | "nta": "在日本的公司", 34 | "ocaml": "OCaml", 35 | "pypi": "PyPI", 36 | "rubygems": "RubyGems", 37 | "rust": "Rust", 38 | "s3": "AWS S3", 39 | "slack": "Slack", 40 | "spectrum": "Spectrum", 41 | "reddit": "Reddit", 42 | "twitter": "推特" 43 | }, 44 | "showMore": "更多", 45 | "title": "为您的新应用取个好听的名字", 46 | "try": "这个呢?", 47 | "unavailable": "不可获得", 48 | "uniqueness": { 49 | "description": "UNIQ表示名称的独特性(0-100)", 50 | "high": "超级独特", 51 | "low": "普通", 52 | "moderate": "有点独特" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/locales/zh-Hant/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "關於", 3 | "available": "可獲得", 4 | "blog": "博客", 5 | "community": "社區", 6 | "contributors": "貢獻者", 7 | "countryCode": "jp", 8 | "description": "namae可讓您給您的應用程序、Web服務或組織起一個好名字。", 9 | "gettingStarted": "起步", 10 | "gettingStartedWithExample": "試試 namae", 11 | "issues": "Issues", 12 | "language": "語言", 13 | "noResult": "無結果", 14 | "placeholder": "檢驗", 15 | "pressEnterToSearch": "按Enter鍵搜索", 16 | "providers": { 17 | "appStore": "App Store", 18 | "domains": "域名", 19 | "firebase": "Firebase", 20 | "github": "GitHub 組織", 21 | "githubSearch": "GitHub 倉庫", 22 | "gitlab": "GitLab", 23 | "google": "谷歌", 24 | "heroku": "Heroku", 25 | "homebrew": "Homebrew", 26 | "instagram": "Instagram", 27 | "jsorg": "js.org", 28 | "modland": "mod.land", 29 | "linux": "Linux", 30 | "netlify": "Netlify", 31 | "now": "Vercel", 32 | "npm": "npm", 33 | "nta": "在日本的公司", 34 | "ocaml": "OCaml", 35 | "pypi": "PyPI", 36 | "rubygems": "RubyGems", 37 | "rust": "Rust", 38 | "s3": "AWS S3", 39 | "slack": "Slack", 40 | "spectrum": "Spectrum", 41 | "reddit": "Reddit", 42 | "twitter": "推特" 43 | }, 44 | "showMore": "更多", 45 | "title": "為您的新應用取個好聽的名字", 46 | "try": "這個呢?", 47 | "unavailable": "不可獲得", 48 | "uniqueness": { 49 | "description": "UNIQ表示名稱的獨特性(0-100)", 50 | "high": "超級獨特", 51 | "low": "普通", 52 | "moderate": "有點獨特" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "namae", 3 | "name": "namae", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "/android-chrome-192x192.png", 12 | "sizes": "192x192", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "/android-chrome-512x512.png", 17 | "sizes": "512x512", 18 | "type": "image/png" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#ffffff", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/namae/ad6052c29a21c8c80bb10047cf4e1536543701e9/public/mstile-150x150.png -------------------------------------------------------------------------------- /public/opensearch.xml: -------------------------------------------------------------------------------- 1 | 3 | namae 4 | Inter-platform name availability checker 5 | UTF-8 6 | [https://namae.dev/favicon.ico] 7 | 8 | https://namae.dev 9 | 10 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / -------------------------------------------------------------------------------- /public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 25 | 30 | 34 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "namae", 3 | "short_name": "namae", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#6f3dff", 17 | "background_color": "#6f3dff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /public/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uetchy/namae/ad6052c29a21c8c80bb10047cf4e1536543701e9/public/social.png -------------------------------------------------------------------------------- /setupJest.ts: -------------------------------------------------------------------------------- 1 | import nock from 'nock'; 2 | 3 | nock.disableNetConnect(); 4 | 5 | // nock.recorder.rec() 6 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | import { StoreProvider } from 'easy-peasy'; 3 | import 'mutationobserver-shim'; 4 | import React from 'react'; 5 | import { BrowserRouter } from 'react-router-dom'; 6 | import { ToastContainer } from 'react-toastify'; 7 | import App from './App'; 8 | import { store } from './store'; 9 | import { FullScreenSuspense } from './util/suspense'; 10 | 11 | it('renders welcome message', async () => { 12 | const { findByText } = render( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | const text = await findByText('Grab a slick name for your new project'); 23 | expect(text).toBeTruthy(); 24 | }); 25 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Navigate, Route, Routes } from 'react-router-dom'; 3 | import Footer from './components/Footer'; 4 | import Home from './pages/Home'; 5 | import Search from './pages/Search'; 6 | import { GlobalStyle } from './theme'; 7 | import { useOpenSearch } from './util/hooks'; 8 | 9 | export default function App() { 10 | const OpenSearch = useOpenSearch('/opensearch.xml'); 11 | 12 | return ( 13 | <> 14 | 15 | 16 | 17 | 18 | } /> 19 | } /> 20 | } /> 21 | 22 | 23 |