[
  {
    "path": ".archives/console/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# vercel\n.vercel\n"
  },
  {
    "path": ".archives/console/README.md",
    "content": "# [cors.sh/console](https://cors.sh/console) (`8824`)\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n```\n\nOpen [http://localhost:8824](http://localhost:8824) with your browser to see the result.\n\n## Scheduled Migration\n\nThe cors.sh/console is scheduled to be migrated to grida console (console.grida.co).\n"
  },
  {
    "path": ".archives/console/babel.config.js",
    "content": "module.exports = {\n  presets: [\"next/babel\", \"linaria/babel\"],\n  plugins: [],\n};\n"
  },
  {
    "path": ".archives/console/logo/index.tsx",
    "content": "import React from \"react\";\n\nexport function Logo({\n  color = \"black\",\n  width = 104,\n}: {\n  width?: React.CSSProperties[\"width\"];\n  color?: React.CSSProperties[\"color\"];\n}) {\n  return (\n    <svg\n      width={width}\n      height=\"19\"\n      viewBox=\"0 0 104 19\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        d=\"M16.4205 7.09091H11.6136C11.5795 6.69318 11.4886 6.33239 11.3409 6.00852C11.1989 5.68466 11 5.40625 10.7443 5.17329C10.4943 4.93466 10.1903 4.75284 9.83239 4.62784C9.47443 4.49716 9.06818 4.43182 8.61364 4.43182C7.81818 4.43182 7.14489 4.625 6.59375 5.01136C6.0483 5.39773 5.63352 5.9517 5.34943 6.6733C5.07102 7.39489 4.93182 8.26136 4.93182 9.27273C4.93182 10.3409 5.07386 11.2358 5.35795 11.9574C5.64773 12.6733 6.06534 13.2131 6.6108 13.5767C7.15625 13.9347 7.8125 14.1136 8.57955 14.1136C9.01705 14.1136 9.40909 14.0597 9.75568 13.9517C10.1023 13.8381 10.4034 13.6761 10.6591 13.4659C10.9148 13.2557 11.1222 13.0028 11.2812 12.7074C11.446 12.4062 11.5568 12.0682 11.6136 11.6932L16.4205 11.7273C16.3636 12.4659 16.1563 13.2187 15.7983 13.9858C15.4403 14.7472 14.9318 15.4517 14.2727 16.0994C13.6193 16.7415 12.8097 17.2585 11.8438 17.6506C10.8778 18.0426 9.75568 18.2386 8.47727 18.2386C6.875 18.2386 5.4375 17.8949 4.16477 17.2074C2.89773 16.5199 1.89489 15.5085 1.15625 14.1733C0.423295 12.8381 0.0568182 11.2045 0.0568182 9.27273C0.0568182 7.32955 0.431818 5.69318 1.18182 4.36364C1.93182 3.02841 2.94318 2.01989 4.21591 1.33807C5.48864 0.650568 6.90909 0.306818 8.47727 0.306818C9.57955 0.306818 10.5938 0.457386 11.5199 0.758522C12.446 1.05966 13.2585 1.5 13.9574 2.07954C14.6563 2.65341 15.2188 3.3608 15.6449 4.2017C16.071 5.04261 16.3295 6.00568 16.4205 7.09091ZM34.6394 9.27273C34.6394 11.2159 34.2616 12.8551 33.5059 14.1903C32.7502 15.5199 31.7303 16.5284 30.4462 17.2159C29.1621 17.8977 27.7303 18.2386 26.1508 18.2386C24.5599 18.2386 23.1224 17.8949 21.8383 17.2074C20.5599 16.5142 19.5428 15.5028 18.7871 14.1733C18.0371 12.8381 17.6621 11.2045 17.6621 9.27273C17.6621 7.32955 18.0371 5.69318 18.7871 4.36364C19.5428 3.02841 20.5599 2.01989 21.8383 1.33807C23.1224 0.650568 24.5599 0.306818 26.1508 0.306818C27.7303 0.306818 29.1621 0.650568 30.4462 1.33807C31.7303 2.01989 32.7502 3.02841 33.5059 4.36364C34.2616 5.69318 34.6394 7.32955 34.6394 9.27273ZM29.7644 9.27273C29.7644 8.22727 29.6252 7.34659 29.3468 6.63068C29.0741 5.90909 28.6678 5.36364 28.128 4.99432C27.5939 4.61932 26.9349 4.43182 26.1508 4.43182C25.3667 4.43182 24.7047 4.61932 24.165 4.99432C23.6309 5.36364 23.2246 5.90909 22.9462 6.63068C22.6735 7.34659 22.5371 8.22727 22.5371 9.27273C22.5371 10.3182 22.6735 11.2017 22.9462 11.9233C23.2246 12.6392 23.6309 13.1847 24.165 13.5597C24.7047 13.929 25.3667 14.1136 26.1508 14.1136C26.9349 14.1136 27.5939 13.929 28.128 13.5597C28.6678 13.1847 29.0741 12.6392 29.3468 11.9233C29.6252 11.2017 29.7644 10.3182 29.7644 9.27273ZM35.9919 18V0.545454H43.526C44.8214 0.545454 45.9549 0.78125 46.9265 1.25284C47.8981 1.72443 48.6538 2.40341 49.1936 3.28977C49.7334 4.17614 50.0032 5.23864 50.0032 6.47727C50.0032 7.72727 49.7248 8.78125 49.168 9.6392C48.6169 10.4972 47.8413 11.1449 46.8413 11.5824C45.847 12.0199 44.6851 12.2386 43.3555 12.2386H38.8555V8.55682H42.401C42.9578 8.55682 43.4322 8.48864 43.8243 8.35227C44.222 8.21023 44.526 7.9858 44.7362 7.67898C44.9521 7.37216 45.0601 6.97159 45.0601 6.47727C45.0601 5.97727 44.9521 5.57102 44.7362 5.25852C44.526 4.94034 44.222 4.70739 43.8243 4.55966C43.4322 4.40625 42.9578 4.32954 42.401 4.32954H40.7305V18H35.9919ZM46.2191 9.98864L50.5828 18H45.4351L41.1737 9.98864H46.2191ZM60.5922 6C60.5468 5.43182 60.3337 4.98864 59.953 4.67045C59.578 4.35227 59.007 4.19318 58.2399 4.19318C57.7513 4.19318 57.3507 4.25284 57.0382 4.37216C56.7314 4.4858 56.5041 4.64205 56.3564 4.84091C56.2087 5.03977 56.132 5.26705 56.1263 5.52273C56.1149 5.73295 56.1519 5.9233 56.2371 6.09375C56.328 6.25852 56.4701 6.40909 56.6632 6.54545C56.8564 6.67614 57.1036 6.79545 57.4047 6.90341C57.7059 7.01136 58.0638 7.10795 58.4786 7.19318L59.9104 7.5C60.8763 7.70455 61.703 7.97443 62.3905 8.30966C63.078 8.64489 63.6405 9.03977 64.078 9.49432C64.5155 9.94318 64.8365 10.4489 65.0411 11.0114C65.2513 11.5739 65.3593 12.1875 65.3649 12.8523C65.3593 14 65.0723 14.9716 64.5041 15.767C63.936 16.5625 63.1235 17.1676 62.0666 17.5824C61.0155 17.9972 59.7513 18.2045 58.274 18.2045C56.757 18.2045 55.4331 17.9801 54.3024 17.5312C53.1774 17.0824 52.3024 16.392 51.6774 15.4602C51.0581 14.5227 50.7456 13.3239 50.7399 11.8636H55.2399C55.2684 12.3977 55.4019 12.8466 55.6405 13.2102C55.8791 13.5739 56.2144 13.8494 56.6462 14.0369C57.0837 14.2244 57.6036 14.3182 58.2059 14.3182C58.7115 14.3182 59.1348 14.2557 59.4757 14.1307C59.8166 14.0057 60.0752 13.8324 60.2513 13.6108C60.4274 13.3892 60.5184 13.1364 60.524 12.8523C60.5184 12.5852 60.4303 12.3523 60.2598 12.1534C60.0951 11.9489 59.8223 11.767 59.4416 11.608C59.061 11.4432 58.5468 11.2898 57.899 11.1477L56.1604 10.7727C54.6149 10.4375 53.3962 9.87784 52.5041 9.09375C51.6178 8.30398 51.1774 7.22727 51.1831 5.86364C51.1774 4.75568 51.4729 3.78693 52.0695 2.95739C52.6718 2.12216 53.5041 1.47159 54.5666 1.00568C55.6348 0.539772 56.8593 0.306818 58.2399 0.306818C59.649 0.306818 60.8678 0.542613 61.8962 1.0142C62.9246 1.4858 63.7172 2.15057 64.274 3.00852C64.8365 3.8608 65.1206 4.85795 65.1263 6H60.5922ZM68.9525 18.2727C68.2934 18.2727 67.7281 18.0426 67.2565 17.5824C66.7906 17.1165 66.5605 16.5511 66.5661 15.8864C66.5605 15.2386 66.7906 14.6847 67.2565 14.2244C67.7281 13.7642 68.2934 13.5341 68.9525 13.5341C69.5775 13.5341 70.1286 13.7642 70.6059 14.2244C71.0889 14.6847 71.3332 15.2386 71.3389 15.8864C71.3332 16.3295 71.2167 16.733 70.9894 17.0966C70.7678 17.4545 70.4781 17.7415 70.1201 17.9574C69.7622 18.1676 69.373 18.2727 68.9525 18.2727ZM82.3966 6C82.3511 5.43182 82.1381 4.98864 81.7574 4.67045C81.3824 4.35227 80.8114 4.19318 80.0443 4.19318C79.5557 4.19318 79.1551 4.25284 78.8426 4.37216C78.5358 4.4858 78.3085 4.64205 78.1608 4.84091C78.0131 5.03977 77.9364 5.26705 77.9307 5.52273C77.9193 5.73295 77.9563 5.9233 78.0415 6.09375C78.1324 6.25852 78.2744 6.40909 78.4676 6.54545C78.6608 6.67614 78.908 6.79545 79.2091 6.90341C79.5102 7.01136 79.8682 7.10795 80.283 7.19318L81.7148 7.5C82.6807 7.70455 83.5074 7.97443 84.1949 8.30966C84.8824 8.64489 85.4449 9.03977 85.8824 9.49432C86.3199 9.94318 86.6409 10.4489 86.8455 11.0114C87.0557 11.5739 87.1636 12.1875 87.1693 12.8523C87.1636 14 86.8767 14.9716 86.3085 15.767C85.7403 16.5625 84.9278 17.1676 83.871 17.5824C82.8199 17.9972 81.5557 18.2045 80.0784 18.2045C78.5614 18.2045 77.2375 17.9801 76.1068 17.5312C74.9818 17.0824 74.1068 16.392 73.4818 15.4602C72.8625 14.5227 72.55 13.3239 72.5443 11.8636H77.0443C77.0727 12.3977 77.2063 12.8466 77.4449 13.2102C77.6835 13.5739 78.0188 13.8494 78.4506 14.0369C78.8881 14.2244 79.408 14.3182 80.0102 14.3182C80.5159 14.3182 80.9392 14.2557 81.2801 14.1307C81.621 14.0057 81.8795 13.8324 82.0557 13.6108C82.2318 13.3892 82.3227 13.1364 82.3284 12.8523C82.3227 12.5852 82.2347 12.3523 82.0642 12.1534C81.8994 11.9489 81.6267 11.767 81.246 11.608C80.8653 11.4432 80.3511 11.2898 79.7034 11.1477L77.9648 10.7727C76.4193 10.4375 75.2006 9.87784 74.3085 9.09375C73.4222 8.30398 72.9818 7.22727 72.9875 5.86364C72.9818 4.75568 73.2773 3.78693 73.8739 2.95739C74.4761 2.12216 75.3085 1.47159 76.371 1.00568C77.4392 0.539772 78.6636 0.306818 80.0443 0.306818C81.4534 0.306818 82.6722 0.542613 83.7006 1.0142C84.729 1.4858 85.5216 2.15057 86.0784 3.00852C86.6409 3.8608 86.925 4.85795 86.9307 6H82.3966ZM88.1319 18V0.545454H92.8705V7.36364H99.1432V0.545454H103.882V18H99.1432V11.1818H92.8705V18H88.1319Z\"\n        fill={color}\n      />\n    </svg>\n  );\n}\n"
  },
  {
    "path": ".archives/console/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/basic-features/typescript for more information.\n"
  },
  {
    "path": ".archives/console/next.config.js",
    "content": "const withLinaria = require(\"next-linaria\");\nconst withTM = require(\"next-transpile-modules\")([\n  \"@app/ui\",\n  \"@cors.sh/service-api\",\n  \"@editor-ui/console\",\n]);\n\n/**\n * @type {import('next').NextConfig}\n */\nconst nextconfig = {\n  basePath: \"/console\",\n};\n\nmodule.exports = withTM(withLinaria(nextconfig));\n"
  },
  {
    "path": ".archives/console/package.json",
    "content": "{\n  \"name\": \"console.cors.sh\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev -p 8824\",\n    \"build\": \"next build\",\n    \"start\": \"next start -p 8824\"\n  },\n  \"dependencies\": {\n    \"@cors.sh/service-api\": \"0.0.0\",\n    \"@editor-ui/console\": \"0.0.0\",\n    \"@radix-ui/react-icons\": \"^1.1.1\",\n    \"copy-to-clipboard\": \"^3.3.3\",\n    \"framer-motion\": \"^8.1.3\",\n    \"linaria\": \"^4.1.2\",\n    \"next\": \"12.2.5\",\n    \"next-linaria\": \"^0.11.0\",\n    \"react\": \"18.2.0\",\n    \"react-dom\": \"18.2.0\",\n    \"react-hot-toast\": \"^2.4.0\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^18.7.15\",\n    \"@types/react\": \"18.0.18\",\n    \"next-transpile-modules\": \"^9.0.0\",\n    \"typescript\": \"4.8.2\"\n  }\n}"
  },
  {
    "path": ".archives/console/pages/[id].tsx",
    "content": "import React, { useEffect } from \"react\";\nimport styled from \"@emotion/styled\";\nimport { Pencil1Icon } from \"@radix-ui/react-icons\";\nimport client, { ApplicationWithApiKey } from \"@cors.sh/service-api\";\nimport { FormPageLayout, PageCloseButton } from \"@app/ui/layouts\";\nimport { Button, TextFormField } from \"@editor-ui/console\";\nimport { Logo } from \"logo\";\nimport { UnderlineButton } from \"@app/ui/components\";\nimport { ApiKeyReveal } from \"@app/ui/components\";\n\nexport default function ApplicationDetailPage({\n  application,\n}: {\n  application: ApplicationWithApiKey;\n}) {\n  return (\n    <FormPageLayout>\n      <PageCloseButton />\n      <Logo />\n      <div style={{ height: 80 }} />\n      <EditableTitle initialValue={application.name} />\n      <div className=\"form\">\n        <ApiKeyReveal\n          keys={{\n            test: application.apikey_test,\n            prod: application.apikey_live,\n          }}\n        />\n\n        <TextFormField\n          readonly\n          label=\"Application origin URL\"\n          placeholder=\"http://localhost:3000, https://my-site.com\"\n          helpText=\"You can add up to 3 urls of your site\"\n          value={application.allowedOrigins?.join(\", \")}\n          // onChange={setAllowedOrigins}\n        />\n        <TextFormField\n          readonly\n          label=\"Restrict Targets (Optional)\"\n          placeholder=\"http://localhost:3000, https://my-site.com\"\n          helpText=\"You can restrict target urls for extra security\"\n          value={application.allowedOrigins?.join(\", \")}\n          // onChange={setAllowedOrigins}\n        />\n\n        <Button height={36}>Save</Button>\n        <UnderlineButton>Archive application</UnderlineButton>\n      </div>\n    </FormPageLayout>\n  );\n}\n\nfunction EditableTitle({ initialValue = \"\" }: { initialValue?: string }) {\n  const [editing, setEditing] = React.useState(false);\n  const [text, setText] = React.useState(initialValue);\n  const ref = React.useRef<HTMLInputElement>(null);\n\n  useEffect(() => {\n    if (editing) {\n      // select all\n      ref.current?.focus();\n      ref.current?.setSelectionRange(0, text.length);\n    }\n  }, [editing]);\n\n  return (\n    <TitleInputWrapper>\n      <input\n        style={{\n          cursor: editing ? \"text\" : \"pointer\",\n        }}\n        onDoubleClick={() => setEditing(true)}\n        onBlur={() => setEditing(false)}\n        ref={ref}\n        readOnly={!editing}\n        contentEditable\n        onChange={(e) => {\n          setText(e.target.value);\n        }}\n        onKeyDown={(e) => {\n          if (e.key === \"Enter\") {\n            setEditing(false);\n          }\n        }}\n        value={text}\n      />\n      <button\n        className=\"edit-button\"\n        style={{\n          visibility: editing ? \"hidden\" : \"visible\",\n        }}\n        onClick={() => setEditing(true)}\n      >\n        <Pencil1Icon />\n      </button>\n    </TitleInputWrapper>\n  );\n}\n\nconst TitleInputWrapper = styled.div`\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 8px;\n\n  input {\n    width: 100%;\n    box-sizing: border-box;\n    font-size: 24px;\n    font-weight: bold;\n    border: none;\n    text-align: center;\n    outline: none;\n  }\n\n  .edit-button {\n    position: absolute;\n    right: 0;\n    top: 0;\n    bottom: 0;\n    margin: auto;\n    opacity: 0;\n    cursor: pointer;\n    border: none;\n    background: none;\n    outline: none;\n    transition: opacity 0.2s ease;\n  }\n\n  &:hover {\n    .edit-button {\n      opacity: 1;\n    }\n  }\n`;\n\nexport async function getServerSideProps(context: any) {\n  const { id } = context.query;\n  // const application = await client.getApplication(id);\n  // return {\n  //   props: {\n  //     application,\n  //   },\n  // };\n\n  return {\n    props: {\n      application: {\n        id,\n        name: \"my-portfolio-website\",\n        apikey_live: \"prod_1223-xasx-xxe2\",\n        apikey_test: \"test_xxasdj-xxd9-x2hx\",\n      },\n    },\n  };\n}\n"
  },
  {
    "path": ".archives/console/pages/_app.tsx",
    "content": "import React from \"react\";\nimport { Toaster } from \"react-hot-toast\";\nimport \"../styles/globals.css\";\nimport type { AppProps } from \"next/app\";\n\nfunction App({ Component, pageProps }: AppProps) {\n  return (\n    <>\n      <Toaster position=\"bottom-center\" />\n      <Component {...pageProps} />\n    </>\n  );\n}\nexport default App;\n"
  },
  {
    "path": ".archives/console/pages/index.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport Link from \"next/link\";\nimport { Logo } from \"logo\";\nimport { FormPageLayout } from \"@app/ui/layouts\";\nimport Head from \"next/head\";\nimport { ArrowRightIcon } from \"@radix-ui/react-icons\";\nimport { UnderlineButton } from \"@app/ui/components\";\n\nexport default function ConsoleIndex({\n  applications,\n}: {\n  applications: any[];\n}) {\n  return (\n    <>\n      <Head>\n        <title>CORS.SH - Console</title>\n      </Head>\n      <FormPageLayout>\n        <Logo />\n        <div style={{ height: 80 }} />\n        <ApplicationList>\n          {applications.map((application) => (\n            <ApplicationItem key={application.id} {...application} />\n          ))}\n        </ApplicationList>\n        <div style={{ height: 80 }} />\n        <div\n          style={{\n            display: \"flex\",\n            flexDirection: \"column\",\n            gap: 8,\n          }}\n        >\n          <UnderlineButton>\n            <Link href=\"/new\">Create new application</Link>\n          </UnderlineButton>\n          <UnderlineButton>\n            <Link href=\"/subscriptions\">Manage subscription</Link>\n          </UnderlineButton>\n        </div>\n      </FormPageLayout>\n    </>\n  );\n}\n\nconst ApplicationList = styled.div`\n  display: flex;\n  flex-direction: column;\n  align-items: stretch;\n  width: 100%;\n  gap: 8px;\n`;\n\nfunction ApplicationItem({\n  id,\n  name,\n  allowedOrigins,\n}: {\n  id: string;\n  name: string;\n  allowedOrigins: string[];\n}) {\n  return (\n    <Link href={`/${id}`}>\n      <ItemWrap>\n        <span>\n          {name} ({id})\n        </span>\n        <ArrowRightIcon />\n      </ItemWrap>\n    </Link>\n  );\n}\n\nconst ItemWrap = styled.div`\n  flex: 1;\n  display: flex;\n  cursor: pointer;\n  justify-content: space-between;\n  flex-direction: row;\n  align-items: center;\n  flex: none;\n  border-radius: 4px;\n  border: solid 1px rgba(0, 0, 0, 0.1);\n  box-sizing: border-box;\n  padding: 21px;\n\n  &:hover {\n    border: solid 1px rgba(0, 0, 0, 0.1);\n    background-color: rgba(0, 0, 0, 0.02);\n    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);\n  }\n\n  transition: all 0.2s ease-in-out;\n`;\n\nexport async function getServerSideProps(context: any) {\n  // fetch all my applications\n  return {\n    props: {\n      applications: [\n        {\n          id: \"1\",\n          name: \"My app\",\n        },\n        {\n          id: \"2\",\n          name: \"My app 2\",\n        },\n      ],\n    },\n  };\n}\n"
  },
  {
    "path": ".archives/console/pages/new.tsx",
    "content": "import React, { useEffect } from \"react\";\nimport { Button, TextFormField } from \"@editor-ui/console\";\nimport client from \"@cors.sh/service-api\";\nimport { useRouter } from \"next/router\";\nimport { FormPageLayout } from \"@app/ui/layouts\";\nexport default function NewApplicationPage() {\n  const router = useRouter();\n  const [name, setName] = React.useState(\"\");\n  const [allowedOrigins, setAllowedOrigins] = React.useState(\"\");\n  const [isBusy, setIsBusy] = React.useState(false);\n  const [isValid, setIsValid] = React.useState(false);\n\n  const validateUrls = (urls: string) => {\n    const lines = urls.split(\",\").map((line) => line.trim());\n    for (const line of lines) {\n      try {\n        new URL(line);\n      } catch (e) {\n        return false;\n      }\n    }\n    return true;\n  };\n\n  const onCreateNewClick = () => {\n    setIsBusy(true);\n    client\n      .createApplication({\n        name: name,\n        allowedOrigins: allowedOrigins\n          .split(\",\")\n          .map((origin) => origin.trim()),\n      })\n      .then((r) => {\n        router.push({\n          pathname: \"[id]\",\n          query: { id: r.id },\n        });\n      })\n      .finally(() => {\n        setIsBusy(false);\n      });\n  };\n\n  useEffect(() => {\n    setIsValid(name.length > 0 && validateUrls(allowedOrigins));\n  }, [name, allowedOrigins]);\n\n  return (\n    <FormPageLayout>\n      <h1>Create new application</h1>\n      <div className=\"form\">\n        <TextFormField\n          label=\"Project Name\"\n          placeholder=\"my-portfolio-website\"\n          onChange={setName}\n        />\n        <TextFormField\n          label=\"Your site\"\n          placeholder=\"http://localhost:3000, https://my-site.com\"\n          helpText=\"You can add up to 3 urls of your site\"\n          onChange={setAllowedOrigins}\n        />\n        <div style={{ height: 16 }} />\n        <Button\n          disabled={!isValid || isBusy}\n          onClick={onCreateNewClick}\n          height={\"32px\"}\n        >\n          Create Project\n        </Button>\n      </div>\n    </FormPageLayout>\n  );\n}\n"
  },
  {
    "path": ".archives/console/styles/Home.module.css",
    "content": ".container {\n  min-height: 100vh;\n  padding: 0 0.5rem;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  height: 100vh;\n}\n\n.main {\n  padding: 5rem 0;\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n}\n\n.footer {\n  width: 100%;\n  height: 100px;\n  border-top: 1px solid #eaeaea;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.footer a {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  flex-grow: 1;\n}\n\n.title a {\n  color: #0070f3;\n  text-decoration: none;\n}\n\n.title a:hover,\n.title a:focus,\n.title a:active {\n  text-decoration: underline;\n}\n\n.title {\n  margin: 0;\n  line-height: 1.15;\n  font-size: 4rem;\n}\n\n.title,\n.description {\n  text-align: center;\n}\n\n.description {\n  line-height: 1.5;\n  font-size: 1.5rem;\n}\n\n.code {\n  background: #fafafa;\n  border-radius: 5px;\n  padding: 0.75rem;\n  font-size: 1.1rem;\n  font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,\n    Bitstream Vera Sans Mono, Courier New, monospace;\n}\n\n.grid {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-wrap: wrap;\n  max-width: 800px;\n  margin-top: 3rem;\n}\n\n.card {\n  margin: 1rem;\n  padding: 1.5rem;\n  text-align: left;\n  color: inherit;\n  text-decoration: none;\n  border: 1px solid #eaeaea;\n  border-radius: 10px;\n  transition: color 0.15s ease, border-color 0.15s ease;\n  width: 45%;\n}\n\n.card:hover,\n.card:focus,\n.card:active {\n  color: #0070f3;\n  border-color: #0070f3;\n}\n\n.card h2 {\n  margin: 0 0 1rem 0;\n  font-size: 1.5rem;\n}\n\n.card p {\n  margin: 0;\n  font-size: 1.25rem;\n  line-height: 1.5;\n}\n\n.logo {\n  height: 1em;\n  margin-left: 0.5rem;\n}\n\n@media (max-width: 600px) {\n  .grid {\n    width: 100%;\n    flex-direction: column;\n  }\n}\n"
  },
  {
    "path": ".archives/console/styles/globals.css",
    "content": "html,\nbody {\n  padding: 0;\n  margin: 0;\n  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,\n    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;\n}\n\na {\n  color: inherit;\n  text-decoration: none;\n}\n\n* {\n  box-sizing: border-box;\n}\n"
  },
  {
    "path": ".archives/console/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": false,\n    \"noImplicitAny\": false,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"baseUrl\": \".\"\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": ".archives/homepage/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# vercel\n.vercel\n\n# grida\n.env\n.grida"
  },
  {
    "path": ".archives/homepage/README.md",
    "content": "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).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.\n\n[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.\n\nThe `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe 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.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.\n"
  },
  {
    "path": ".archives/homepage/babel.config.js",
    "content": "module.exports = {\n  presets: [\"next/babel\", \"linaria/babel\"],\n  plugins: [],\n};\n"
  },
  {
    "path": ".archives/homepage/components/appbar/appbar-parent-site.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport Image from \"next/image\";\n/**\n * `<AppbarParentSite>` ('appbar-parent-site')\n * - [Open in Figma](https://figma.com/file/aPfdtNb1aGFIN9p05cmmVY?node-id=19:1937)\n * - [Open in Grida](https://code.grida.co/files/aPfdtNb1aGFIN9p05cmmVY?node=19:1937)\n *\n *\n * ---\n * @example\n * ```tsx\n * import React from \"react\";\n *\n * export default function () {\n *   return (\n *     <>\n *       👇 instanciate widget like below. 👇\n *       <AppbarParentSite/>\n *     </>\n *   )\n * }\n * ```\n * ---\n * @params {any} props - this widget does not requires props. you can pass custom dynamic props to the widget as you want (on typescript, it will raise type check issues).\n * ---\n * @preview\n * ![](https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/1f5abd8c-95d5-4bd8-a931-e935de073cf7)\n * ---\n * @remarks\n * @see {@link https://grida.co/docs} for more information.\n * ---\n * Code generated by grida.co | engine 0.0.1 (Apache-2.0) | Generated code under CC0 (public domain) *This code is free to use, modify, and redistribute. (aknowledgment is not required)*\n *\n *\n * ![Made with Grida](https://bridged-service-static.s3.us-west-1.amazonaws.com/branding/logo/32.png)\n * <!-- Info: Please do not remove this comment unless intended. removing this section will break grida integrations. -->\n * <!-- grida.meta.widget_declaration | engine : 0.0.1 | source : figma://aPfdtNb1aGFIN9p05cmmVY/19:1937 -->\n */\nexport function AppbarParentSite() {\n  return (\n    <RootWrapperAppbarParentSite href=\"https://grida.co\" target=\"_blank\">\n      <Home>\n        <Logo64>\n          <Image\n            src=\"/grida-logo-white-128.png\"\n            alt=\"Grida Logo White\"\n            width={25}\n            height={25}\n          />\n        </Logo64>\n        <Grida>Grida</Grida>\n      </Home>\n    </RootWrapperAppbarParentSite>\n  );\n}\n\nconst RootWrapperAppbarParentSite = styled.a`\n  cursor: pointer;\n  background-color: black;\n  position: relative;\n  height: 50px;\n`;\n\nconst Home = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  gap: 8px;\n  box-sizing: border-box;\n  position: absolute;\n  left: calc((calc((50% + -484px)) - 37px));\n  top: 12px;\n  width: 73px;\n  height: 26px;\n`;\n\nconst Logo64 = styled.div`\n  width: 25px;\n  height: 26px;\n  overflow: hidden;\n  position: relative;\n`;\n\nconst LogoShapeOnly = styled.div`\n  width: 25px;\n  height: 25px;\n  position: absolute;\n  left: 0px;\n  top: 0px;\n`;\n\nconst Union = styled.img`\n  width: 25px;\n  height: 25px;\n  object-fit: cover;\n  position: absolute;\n  left: 0px;\n  top: 0px;\n`;\n\nconst Grida = styled.span`\n  color: white;\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: Helvetica, sans-serif;\n  font-weight: 700;\n  letter-spacing: -1px;\n  text-align: left;\n`;\n"
  },
  {
    "path": ".archives/homepage/components/appbar/appbar.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { useRouter } from \"next/router\";\n/**\n * `<Appbar>` ('appbar')\n * - [Open in Figma](https://figma.com/file/aPfdtNb1aGFIN9p05cmmVY?node-id=18:1889)\n * - [Open in Grida](https://code.grida.co/files/aPfdtNb1aGFIN9p05cmmVY?node=18:1889)\n *\n *\n * ---\n * @example\n * ```tsx\n * import React from \"react\";\n *\n * export default function () {\n *   return (\n *     <>\n *       👇 instanciate widget like below. 👇\n *       <Appbar/>\n *     </>\n *   )\n * }\n * ```\n * ---\n * @params {any} props - this widget does not requires props. you can pass custom dynamic props to the widget as you want (on typescript, it will raise type check issues).\n * ---\n * @preview\n * ![](https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/d8000780-f44d-4ccf-b5da-f132856fc5aa)\n * ---\n * @remarks\n * @see {@link https://grida.co/docs} for more information.\n * ---\n * Code generated by grida.co | engine 0.0.1 (Apache-2.0) | Generated code under CC0 (public domain) *This code is free to use, modify, and redistribute. (aknowledgment is not required)*\n *\n *\n * ![Made with Grida](https://bridged-service-static.s3.us-west-1.amazonaws.com/branding/logo/32.png)\n * <!-- Info: Please do not remove this comment unless intended. removing this section will break grida integrations. -->\n * <!-- grida.meta.widget_declaration | engine : 0.0.1 | source : figma://aPfdtNb1aGFIN9p05cmmVY/18:1889 -->\n */\nexport function Appbar() {\n  const router = useRouter();\n  const onGetStartedClick = () => {\n    router.push(\"/get-started\");\n  };\n\n  return (\n    <RootWrapperAppbar>\n      <ContentFrame>\n        <Leading>\n          <Home>\n            <Image\n              alt=\"CORS.SH Logo (svg)\"\n              src={\"/logo.svg\"}\n              height={20}\n              width={100}\n            />\n          </Home>\n          <Menus>\n            <MenuItem label=\"Usage\" href=\"#usage\" />\n            <MenuItem label=\"Playground\" href=\"/playground\" />\n            <MenuItem label=\"Pricing\" href=\"#pricing\" />\n            <MenuItem\n              label=\"Github\"\n              href=\"https://github.com/gridaco/cors.sh\"\n            />\n          </Menus>\n        </Leading>\n        <HeaderCtaSignupPlain>\n          <HeaderCtaSignupPlain_0001>\n            <GetStarted onClick={onGetStartedClick}>Get started</GetStarted>\n          </HeaderCtaSignupPlain_0001>\n        </HeaderCtaSignupPlain>\n      </ContentFrame>\n    </RootWrapperAppbar>\n  );\n}\n\nconst MenuItem = ({ label, href }: { label: string; href: string }) => {\n  return (\n    <MenuContainer href={href}>\n      <BaseHeaderPrimaryMenu>\n        {/* <MenuMargin /> */}\n        <MenuTextContainer>\n          <MenuText>{label}</MenuText>\n        </MenuTextContainer>\n        {/* <Indicator /> */}\n      </BaseHeaderPrimaryMenu>\n    </MenuContainer>\n  );\n};\n\nconst RootWrapperAppbar = styled.header`\n  background-color: white;\n  position: relative;\n  z-index: 9;\n  height: 80px;\n`;\n\nconst ContentFrame = styled.div`\n  width: 1040px;\n  overflow: hidden;\n  position: absolute;\n  left: calc((calc((50% + 0px)) - 520px));\n  top: 0px;\n  bottom: 0px;\n`;\n\nconst Leading = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  gap: 120px;\n  box-sizing: border-box;\n  position: absolute;\n  left: 0px;\n  top: calc((calc((50% + 0px)) - 30px));\n  width: 732px;\n  height: 60px;\n`;\n\nconst Home = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  flex: none;\n  gap: 8px;\n  box-sizing: border-box;\n`;\n\nconst Menus = styled.nav`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  flex: none;\n  gap: 36px;\n  height: 60px;\n  box-sizing: border-box;\n`;\n\nconst MenuContainer = styled(Link)`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  flex: none;\n  box-sizing: border-box;\n`;\n\nconst BaseHeaderPrimaryMenu = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: flex-start;\n  flex: none;\n  gap: 16px;\n  box-sizing: border-box;\n`;\n\nconst MenuMargin = styled.div`\n  height: 2px;\n  background-color: white;\n  align-self: stretch;\n  flex-shrink: 0;\n  opacity: 0;\n`;\n\nconst MenuTextContainer = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  flex: none;\n  box-sizing: border-box;\n  cursor: pointer;\n`;\n\nconst MenuText = styled.span`\n  color: rgb(139, 139, 139);\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 500;\n  text-align: left;\n`;\n\nconst HeaderCtaSignupPlain = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  position: absolute;\n  top: calc((calc((50% + 0px)) - 16px));\n  right: 0px;\n  min-width: 80px;\n  height: 32px;\n`;\n\nconst HeaderCtaSignupPlain_0001 = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  gap: 10px;\n  border-radius: 35px;\n  box-sizing: border-box;\n`;\n\nconst GetStarted = styled.span`\n  cursor: pointer;\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: Roboto, sans-serif;\n  font-weight: 500;\n  text-align: right;\n`;\n"
  },
  {
    "path": ".archives/homepage/components/appbar/index.ts",
    "content": "export * from \"./appbar\";\nexport * from \"./appbar-parent-site\";\n"
  },
  {
    "path": ".archives/homepage/components/button/index.tsx",
    "content": "import React, { forwardRef } from \"react\";\nimport styled from \"@emotion/styled\";\n\ntype Props = React.PropsWithRef<\n  {\n    onClick?: () => void;\n  } & React.PropsWithChildren\n>;\n\nfunction _Button({\n  children,\n  // @ts-ignore\n  ref,\n  onClick,\n}: Props) {\n  return (\n    <ButtonBase ref={ref} onClick={onClick}>\n      {children}\n    </ButtonBase>\n  );\n}\n\nexport const Button = forwardRef(_Button);\n\nconst ButtonBase = styled.button`\n  background-color: black;\n  border-radius: 2px;\n  padding: 10px;\n  color: white;\n  font-size: 14px;\n  font-family: Inter, sans-serif;\n  font-weight: 400;\n  border: none;\n  outline: none;\n  cursor: pointer;\n\n  :hover {\n    opacity: 0.8;\n  }\n\n  :disabled {\n    opacity: 0.5;\n  }\n\n  :active {\n    opacity: 1;\n  }\n\n  :focus {\n  }\n`;\n"
  },
  {
    "path": ".archives/homepage/components/chatwood/index.jsx",
    "content": "import React from \"react\";\n\nclass ChatwootWidget extends React.Component {\n  componentDidMount() {\n    // Add Chatwoot Settings\n    window.chatwootSettings = {\n      hideMessageBubble: false,\n      position: \"right\", // This can be left or right\n      locale: \"en\", // Language to be set\n      type: \"standard\", // [standard, expanded_bubble]\n    };\n\n    // Paste the script from inbox settings except the <script> tag\n    (function (d, t) {\n      var BASE_URL = \"https://app.chatwoot.com\";\n      var g = d.createElement(t),\n        s = d.getElementsByTagName(t)[0];\n      g.src = BASE_URL + \"/packs/js/sdk.js\";\n      s.parentNode.insertBefore(g, s);\n      g.async = !0;\n      g.onload = function () {\n        window.chatwootSDK.run({\n          websiteToken: \"uYszA87RExPbaCa66i6w9c8i\",\n          baseUrl: BASE_URL,\n        });\n      };\n    })(document, \"script\");\n  }\n\n  render() {\n    return null;\n  }\n}\n\nexport default ChatwootWidget;\n"
  },
  {
    "path": ".archives/homepage/components/collabsible-info-card/index.tsx",
    "content": "import React, { useState } from \"react\";\nimport styled from \"@emotion/styled\";\nimport * as Collapsible from \"@radix-ui/react-collapsible\";\nimport { CaretDownIcon, CaretUpIcon } from \"@radix-ui/react-icons\";\n\nexport function CollapsibleInfoCard({\n  title,\n  children,\n}: React.PropsWithChildren<{ title: string }>) {\n  const [open, setOpen] = useState(false);\n\n  return (\n    <Container open={open} onOpenChange={setOpen}>\n      <Collapsible.Trigger className=\"trigger\">\n        <h2>{title}</h2>\n        <div className=\"icon\">{open ? <CaretUpIcon /> : <CaretDownIcon />}</div>\n      </Collapsible.Trigger>\n      <Collapsible.Content className=\"content\">{children}</Collapsible.Content>\n    </Container>\n  );\n}\n\nconst Container = styled(Collapsible.Root)`\n  display: flex;\n  flex-direction: column;\n  .trigger {\n    display: flex;\n    align-items: center;\n    flex: 1;\n    gap: 8px;\n    justify-content: space-between;\n    cursor: pointer;\n    padding: 12px 16px;\n    border-radius: 4px;\n    background: #f5f5f5;\n    border: 1px solid #e5e5e5;\n    margin-bottom: 8px;\n    h2 {\n      font-size: 16px;\n      font-weight: 500;\n      margin: 0;\n    }\n    .icon {\n      width: 24px;\n      height: 24px;\n      display: flex;\n      align-items: center;\n\n      svg {\n        width: 100%;\n        height: 100%;\n      }\n    }\n  }\n\n  .content {\n    padding: 16px;\n    border-radius: 4px;\n    background: rgba(0, 0, 0, 0.02);\n    border: 1px solid #e5e5e5;\n    margin-bottom: 16px;\n\n    font-size: 14px;\n\n    /* no default padding for ul */\n    ul {\n      margin: 0;\n      padding: 16px;\n    }\n\n    li {\n      padding: 0;\n    }\n  }\n\n  /* motion */\n  .content[data-state=\"open\"] {\n    animation: slideDown 0.2s ease-out;\n  }\n  .content[data-state=\"closed\"] {\n    animation: slideUp 0.2s ease-out;\n  }\n\n  @keyframes slideDown {\n    from {\n      opacity: 0;\n      height: 0;\n    }\n    to {\n      opacity: 1;\n      height: var(--radix-collapsible-content-height);\n    }\n  }\n\n  @keyframes slideUp {\n    from {\n      opacity: 1;\n      height: var(--radix-collapsible-content-height);\n    }\n    to {\n      opacity: 0;\n      height: 0;\n    }\n  }\n`;\n"
  },
  {
    "path": ".archives/homepage/components/cta-footer/index.tsx",
    "content": "import styled from \"@emotion/styled\";\nimport React from \"react\";\nimport { OnboardingCta } from \"../cta-onboarding\";\n\nexport function FooterCtaSection({}: {}) {\n  return (\n    <Wrapper>\n      <HeadingAsH3>Say bye to cors errors</HeadingAsH3>\n      <ScribbleGuide>\n        <WeLlSendYouAApiKeyToGetStarted>\n          We’ll send you an Api key to get started\n        </WeLlSendYouAApiKeyToGetStarted>\n        <PointerArtwork\n          src=\"/assets/scribble-pointer.png\"\n          alt=\"image of PointerArtwork\"\n        />\n      </ScribbleGuide>\n      <CtaContainer>\n        <OnboardingCta />\n      </CtaContainer>\n    </Wrapper>\n  );\n}\n\nconst Wrapper = styled.div`\n  position: relative;\n  display: flex;\n  justify-content: center;\n  flex-direction: column;\n  align-items: center;\n  gap: 51px;\n  box-shadow: 0px 4px 48px 24px rgba(0, 0, 0, 0.04);\n  border: solid 1px rgba(0, 0, 0, 0.1);\n  border-radius: 8px;\n  min-height: 60vh;\n  background-color: white;\n  box-sizing: border-box;\n`;\n\nconst HeadingAsH3 = styled.h3`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 64px;\n  font-family: \"Inter\", sans-serif;\n  font-weight: 700;\n  letter-spacing: -1px;\n  text-align: center;\n  align-self: stretch;\n  flex-shrink: 0;\n  padding-left: 16px;\n  padding-right: 16px;\n`;\n\nconst CtaContainer = styled.div`\n  display: flex;\n  z-index: 1;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  flex: none;\n  gap: 16px;\n  border-radius: 0;\n  margin-top: 100px;\n  box-sizing: border-box;\n`;\n\nconst ScribbleGuide = styled.div`\n  z-index: 0;\n  width: 217px;\n  height: 112px;\n  position: absolute;\n  left: -10px;\n  top: 280px;\n`;\n\nconst WeLlSendYouAApiKeyToGetStarted = styled.span`\n  color: rgba(180, 180, 180, 0.7);\n  text-overflow: ellipsis;\n  font-size: 24px;\n  font-family: \"Nanum Pen Script\", sans-serif;\n  font-weight: 400;\n  line-height: 98%;\n  text-align: left;\n  position: absolute;\n  top: 25px;\n  transform: translateX(-50px) translateY(40px) rotate(-20deg);\n  transform-origin: top left;\n`;\n\nconst PointerArtwork = styled.img`\n  width: 53px;\n  height: 69px;\n  object-fit: cover;\n  position: absolute;\n  left: calc((calc((50% + 59px)) - 26px));\n  top: 49px;\n  transform: rotate(-23deg);\n  transform-origin: top left;\n`;\n"
  },
  {
    "path": ".archives/homepage/components/cta-onboarding/index.tsx",
    "content": "import React, { useState } from \"react\";\nimport styled from \"@emotion/styled\";\nimport client from \"@cors.sh/service-api\";\nimport { toast } from \"react-hot-toast\";\n\nexport function OnboardingCta() {\n  const [isBusy, setIsBusy] = useState(false);\n  const [email, setEmail] = useState(\"\");\n\n  const onsend = async () => {\n    // validate email\n    if (!validateEmail(email)) {\n      toast.error(<p>Invalid Email</p>);\n      return;\n    }\n\n    // send email\n    setIsBusy(true);\n\n    client\n      .onboardingWithEmail({ email: email })\n      .then((data) => {\n        toast.success(\n          <p>\n            API Key sent to your email.\n            <br />\n            Please check your <b>spam folder</b> as well\n          </p>\n        );\n\n        // log conversion (signup)\n        // @ts-ignore\n        window.gtag(\"event\", \"sign_up\", {\n          method: \"email\",\n        });\n      })\n      .catch((e) => {\n        toast.error(<p>Something went wrong</p>);\n      })\n      .finally(() => {\n        setIsBusy(false);\n      });\n  };\n\n  return (\n    <RootWrapperCta>\n      <InputGroup>\n        <RequestInputAsInput\n          type=\"email\"\n          autoComplete=\"email\"\n          placeholder=\"alice@acme.com\"\n          value={email}\n          disabled={isBusy}\n          onKeyDown={(e) => {\n            if (e.key === \"Enter\") {\n              onsend();\n            }\n          }}\n          onChange={(e) => {\n            setEmail(e.target.value);\n          }}\n        />\n      </InputGroup>\n      <ButtonAsButton disabled={isBusy} onClick={onsend}>\n        Send me an API Key\n      </ButtonAsButton>\n    </RootWrapperCta>\n  );\n}\n\nconst RootWrapperCta = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  flex: none;\n  gap: 14px;\n  box-sizing: border-box;\n  height: 64px;\n`;\n\nconst InputGroup = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  box-shadow: 0px 4px 48px 0px rgba(0, 0, 0, 0.12);\n  border: solid 1px rgba(0, 0, 0, 0.1);\n  border-radius: 4px;\n  align-self: stretch;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst RequestInputAsInput = styled.input`\n  width: 400px;\n  background-color: white;\n  border-radius: 4px;\n  padding: 12px 21px;\n  box-sizing: border-box;\n  border: none;\n  color: black;\n  font-size: 18px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  line-height: 98%;\n  text-align: start;\n  align-self: stretch;\n  flex-shrink: 0;\n  flex: 1;\n  outline: none;\n\n  ::placeholder {\n    color: rgb(181, 181, 181);\n    font-size: 18px;\n    font-family: \"Helvetica Neue\", sans-serif;\n    font-weight: 400;\n  }\n`;\n\nconst ButtonAsButton = styled.button`\n  padding: 0 24px;\n  box-shadow: 0px 4px 48px 0px rgba(0, 0, 0, 0.12);\n  background-color: black;\n  border: solid 1px rgb(210, 210, 210);\n  border-radius: 4px;\n  color: white;\n  font-size: 18px;\n  font-family: \"Inter\", sans-serif;\n  font-weight: 400;\n  line-height: 98%;\n  outline: none;\n  cursor: pointer;\n  align-self: stretch;\n  flex-shrink: 0;\n\n  :hover {\n    opacity: 0.8;\n  }\n\n  :disabled {\n    opacity: 0.5;\n  }\n\n  :active {\n    opacity: 1;\n  }\n\n  :focus {\n  }\n`;\n\nconst validateEmail = (email: string) => {\n  return String(email)\n    .toLowerCase()\n    .match(\n      /^(([^<>()[\\]\\\\.,;:\\s@\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/\n    );\n};\n"
  },
  {
    "path": ".archives/homepage/components/cta-test-it/index.tsx",
    "content": "import React, { useEffect, useState } from \"react\";\nimport styled from \"@emotion/styled\";\nimport Select from \"react-select\";\nimport Axios, { Method } from \"axios\";\nimport { toast } from \"react-hot-toast\";\n\ntype TMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\nfunction WarningIcon() {\n  return (\n    <svg\n      width=\"24\"\n      height=\"24\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <g clip-path=\"url(#clip0_31_2300)\">\n        <path\n          d=\"M1 21H23L12 2L1 21ZM13 18H11V16H13V18ZM13 14H11V10H13V14Z\"\n          fill=\"black\"\n        />\n      </g>\n      <defs>\n        <clipPath id=\"clip0_31_2300\">\n          <rect width=\"24\" height=\"24\" fill=\"white\" />\n        </clipPath>\n      </defs>\n    </svg>\n  );\n}\n\nconst options: { value: TMethod; label: string }[] = [\n  { value: \"GET\", label: \"GET\" },\n  { value: \"POST\", label: \"POST\" },\n  { value: \"PUT\", label: \"PUT\" },\n  { value: \"DELETE\", label: \"DELETE\" },\n];\n\nconst style = {\n  control: (base: any) => ({\n    ...base,\n    border: 0,\n    // This line disable the blue border\n    boxShadow: \"none\",\n    backgroundColor: \"transparent\",\n    fontFamily: `\"Source Code Pro\", monospace`,\n  }),\n};\n\nexport function TestItOutCta({\n  onChange,\n}: {\n  onChange?: (value: string) => void;\n}) {\n  const [url, setUrl] = useState(\"\");\n  const [selectedOption, setSelectedOption] = useState({\n    value: \"GET\",\n    label: \"GET\",\n  });\n\n  const [isValid, setIsValid] = useState(true);\n\n  useEffect(() => {\n    if (url === \"\") {\n      return;\n    }\n\n    try {\n      new URL(url);\n      setIsValid(true);\n    } catch (e) {\n      setIsValid(false);\n    }\n  }, [url]);\n\n  const onsend = () => {\n    Axios.request({\n      method: selectedOption.value as Method,\n      url: \"https://proxy.cors.sh/\" + url,\n    })\n      .then(() => {\n        toast.success(\n          <>\n            Request {selectedOption.value} to {url} succeeded.\n            <br />\n            use{\" \"}\n            <a\n              href=\"/playground\"\n              style={{\n                fontWeight: \"bold\",\n                color: \"blue\",\n              }}\n            >\n              playground\n            </a>{\" \"}\n            for more details\n          </>\n        );\n      })\n      .catch((e) => {\n        toast.error(\n          <>\n            Request {selectedOption.value} to {url}{\" \"}\n            <i\n              style={{\n                color: \"red\",\n              }}\n            >\n              failed with {e.response?.status}\n            </i>\n          </>,\n          {\n            icon: <WarningIcon />,\n          }\n        );\n      });\n  };\n\n  return (\n    <RootWrapperCta>\n      <InputGroup>\n        <MethodSelect>\n          <Select\n            styles={style}\n            defaultValue={selectedOption}\n            id=\"method-select\"\n            instanceId=\"method-select\"\n            // @ts-ignore\n            onChange={setSelectedOption}\n            options={options}\n          />\n          {/* <Type>\n              <Label>GET</Label>\n            </Type>\n            <Menu>\n              <Line />\n              <IconsMdiArrowDropDown src=\"grida://assets-reservation/images/31:2296\" />\n            </Menu> */}\n        </MethodSelect>\n        <RequestInputAsInput\n          type=\"text\"\n          placeholder=\"https://acme.com\"\n          value={url}\n          onKeyDown={(e) => {\n            if (e.key === \"Enter\") {\n              onsend();\n            }\n          }}\n          onChange={(e) => {\n            setUrl(e.target.value);\n            onChange?.(e.target.value);\n          }}\n        />\n      </InputGroup>\n      <ButtonAsButton disabled={!isValid} onClick={onsend}>\n        Send\n      </ButtonAsButton>\n    </RootWrapperCta>\n  );\n}\n\nconst RootWrapperCta = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  flex: none;\n  gap: 14px;\n  box-sizing: border-box;\n  height: 48px;\n`;\n\nconst InputGroup = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  box-shadow: 0px 4px 48px 0px rgba(0, 0, 0, 0.12);\n  border: solid 1px rgba(0, 0, 0, 0.1);\n  border-radius: 4px;\n  align-self: stretch;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst MethodSelect = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: column;\n  align-items: center;\n  gap: 10px;\n  border-right: solid 1px rgba(0, 0, 0, 0.1);\n  border-top-left-radius: 4px;\n  border-top-right-radius: 0px;\n  border-bottom-right-radius: 0px;\n  border-bottom-left-radius: 4px;\n  align-self: stretch;\n  width: 123px;\n  background-color: transparent;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst RequestInputAsInput = styled.input`\n  flex: 1;\n  width: 320px;\n  background-color: transparent;\n  border-top-left-radius: 0px;\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 4px;\n  border-bottom-left-radius: 0px;\n  padding: 12px 21px;\n  box-sizing: border-box;\n  border: none;\n  color: black;\n  font-size: 18px;\n  font-family: \"Source Code Pro\", monospace;\n  font-weight: 400;\n  line-height: 98%;\n  text-align: start;\n  align-self: stretch;\n  flex-shrink: 0;\n  flex: 1;\n  outline: none;\n\n  ::placeholder {\n    color: rgb(181, 181, 181);\n    font-size: 18px;\n    font-weight: 400;\n  }\n`;\n\nconst ButtonAsButton = styled.button`\n  padding: 0 21px;\n  box-shadow: 0px 4px 48px 0px rgba(0, 0, 0, 0.12);\n  background-color: black;\n  border: solid 1px rgb(210, 210, 210);\n  border-radius: 4px;\n  color: white;\n  font-size: 18px;\n  font-family: \"Source Code Pro\", monospace;\n  font-weight: 500;\n  line-height: 98%;\n  outline: none;\n  cursor: pointer;\n  align-self: stretch;\n  flex-shrink: 0;\n\n  :hover {\n    opacity: 0.8;\n  }\n\n  :disabled {\n    opacity: 0.5;\n  }\n\n  :active {\n    opacity: 1;\n  }\n\n  :focus {\n  }\n`;\n"
  },
  {
    "path": ".archives/homepage/components/demo-terminal/index.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport { useRouter } from \"next/router\";\nimport { Prism as SyntaxHighlighter } from \"react-syntax-highlighter\";\nimport { examples } from \"k\";\n\nexport function DemoTerminal({\n  target = \"https://acme.com\",\n}: {\n  target: string;\n}) {\n  const [example, setExample] = React.useState<\"fetch\" | \"axios\">(\"fetch\");\n  const router = useRouter();\n  return (\n    <Wrapper>\n      <Content>\n        <SyntaxHighlighter language=\"js\">\n          {examples[example](target)}\n        </SyntaxHighlighter>\n      </Content>\n      <Tabs>\n        {Object.keys(examples).map((key) => {\n          return (\n            <TabItem\n              onClick={() => {\n                setExample(key as any);\n              }}\n              key={key}\n              selected={key === example}\n            >\n              {key}\n            </TabItem>\n          );\n        })}\n      </Tabs>\n      <ViewAllExamples href=\"/docs/category/guides\">\n        View all examples\n      </ViewAllExamples>\n    </Wrapper>\n  );\n}\n\nconst Wrapper = styled.div`\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n  height: 100%;\n  background-color: #f5f2f0;\n  border: solid 1px rgba(0, 0, 0, 0.05);\n  border-radius: 4px;\n  box-shadow: 0px 4px 32px rgba(0, 0, 0, 0.04);\n`;\n\nconst Content = styled.span`\n  color: rgb(68, 68, 68);\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 500;\n  text-align: left;\n  position: absolute;\n  left: 40px;\n  top: 98px;\n`;\n\nconst Tabs = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  gap: 17px;\n  box-sizing: border-box;\n  position: absolute;\n  left: 40px;\n  top: 40px;\n  width: 127px;\n  height: 19px;\n`;\n\nconst TabItem = styled.span<{ selected?: boolean }>`\n  cursor: pointer;\n  color: rgba(0, 0, 0, ${(p) => (p.selected ? \"0.8\" : \"0.2\")});\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: Inter, sans-serif;\n  font-weight: 400;\n  text-align: left;\n`;\n\nconst ViewAllExamples = styled.a`\n  cursor: pointer;\n  color: rgba(0, 0, 0, 0.3);\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: Inter, sans-serif;\n  font-weight: 400;\n  text-align: left;\n  position: absolute;\n  right: 40px;\n  top: 40px;\n`;\n"
  },
  {
    "path": ".archives/homepage/components/icons/check-filled.tsx",
    "content": "import { Check } from \"@mui/icons-material\";\n\nexport default function CheckFilled({\n  color = \"black\",\n  size = 24,\n}: {\n  size?: number;\n  color?: string;\n}) {\n  return (\n    <span\n      style={{\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        width: size,\n        height: size,\n        borderRadius: \"50%\",\n        backgroundColor: color,\n      }}\n    >\n      <Check\n        sx={{\n          fontSize: size * 0.8,\n          color: \"white\",\n        }}\n      />\n    </span>\n  );\n}\n"
  },
  {
    "path": ".archives/homepage/components/index.ts",
    "content": "export * from \"./appbar\";\nexport * from \"./cta-test-it\";\nexport * from \"./cta-onboarding\";\nexport * from \"./collabsible-info-card\";\n"
  },
  {
    "path": ".archives/homepage/components/logo/index.tsx",
    "content": "import React from \"react\";\nimport Image from \"next/image\";\nimport { useRouter } from \"next/router\";\n\nexport function Logo({ moveToHome = false }: { moveToHome?: boolean }) {\n  const router = useRouter();\n  const onClick = () => {\n    if (moveToHome) {\n      router.push(\"/\");\n    }\n  };\n  return <Image onClick={onClick} src={\"/logo.svg\"} height={20} width={100} />;\n}\n"
  },
  {
    "path": ".archives/homepage/components/pricing/card.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport Check from \"@mui/icons-material/Check\";\n/**\n * `<PricingCard>` ('pricing-card')\n * - [Open in Figma](https://figma.com/file/aPfdtNb1aGFIN9p05cmmVY?node-id=44:1495)\n * - [Open in Grida](https://code.grida.co/files/aPfdtNb1aGFIN9p05cmmVY?node=44:1495)\n *\n *\n * ---\n * @example\n * ```tsx\n * import React from \"react\";\n *\n * export default function () {\n *   return (\n *     <>\n *       👇 instanciate widget like below. 👇\n *       <PricingCard/>\n *     </>\n *   )\n * }\n * ```\n * ---\n * @params {any} props - this widget does not requires props. you can pass custom dynamic props to the widget as you want (on typescript, it will raise type check issues).\n * ---\n * @preview\n * ![](https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/96e79c0e-a4b8-44cd-969a-41b7793ccdbb)\n * ---\n * @remarks\n * @see {@link https://grida.co/docs} for more information.\n * ---\n * Code generated by grida.co | engine 0.0.1 (Apache-2.0) | Generated code under CC0 (public domain) *This code is free to use, modify, and redistribute. (aknowledgment is not required)*\n *\n *\n * ![Made with Grida](https://bridged-service-static.s3.us-west-1.amazonaws.com/branding/logo/32.png)\n * <!-- Info: Please do not remove this comment unless intended. removing this section will break grida integrations. -->\n * <!-- grida.meta.widget_declaration | engine : 0.0.1 | source : figma://aPfdtNb1aGFIN9p05cmmVY/44:1495 -->\n */\nexport function PricingCard({\n  onStartClick,\n  startLabel = \"Get Started\",\n  features,\n  price,\n  description,\n  name,\n  unit,\n  selected,\n  enableAction = true,\n  onClick,\n}: {\n  enableAction?: boolean;\n  onClick?: () => void;\n  selected?: boolean;\n  unit?: string;\n  description?: string;\n  name: string;\n  price: string;\n  onStartClick: () => void;\n  startLabel?: string;\n  features: string[];\n}) {\n  return (\n    <RootWrapperPricingCard\n      onClick={onClick}\n      border={selected ? \"black\" : \"transparent\"}\n    >\n      <Content>\n        <ReadingArea>\n          <PriceArea>\n            <Name>{name}</Name>\n            <PriceContainer direction={description ? \"column\" : \"row\"}>\n              <Price>{price}</Price>\n              {unit && <Sub>/ {unit}</Sub>}\n              {description && <Sub>{description}</Sub>}\n            </PriceContainer>\n          </PriceArea>\n          <FeaturesArea>\n            {features.map((feature) => (\n              <PricelistFeatureItem key={feature}>\n                <Check\n                  sx={{\n                    color: \"#A9A9A9\",\n                  }}\n                />\n                <FeatureName>{feature}</FeatureName>\n              </PricelistFeatureItem>\n            ))}\n          </FeaturesArea>\n        </ReadingArea>\n        {enableAction && (\n          <ButtonAsButton data-highlighted={selected} onClick={onStartClick}>\n            {startLabel}\n          </ButtonAsButton>\n        )}\n      </Content>\n    </RootWrapperPricingCard>\n  );\n}\n\nconst RootWrapperPricingCard = styled.div<{ border: string }>`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  gap: 10px;\n  box-shadow: 0px 4px 128px 32px rgba(0, 0, 0, 0.08);\n  border-radius: 8px;\n  background-color: white;\n  box-sizing: border-box;\n  border: 4px solid ${(p) => p.border};\n  padding: 40px;\n`;\n\nconst Content = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: flex-start;\n  flex: 1;\n  gap: 142px;\n  width: 300px;\n  box-sizing: border-box;\n`;\n\nconst ReadingArea = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: flex-start;\n  gap: 72px;\n  align-self: stretch;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst PriceArea = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: flex-start;\n  gap: 36px;\n  align-self: stretch;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst Name = styled.span`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 18px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  line-height: 135%;\n  text-align: left;\n  align-self: stretch;\n  flex-shrink: 0;\n`;\n\nconst PriceContainer = styled.div<{ direction: \"column\" | \"row\" }>`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: ${(p) => p.direction};\n  align-items: flex-start;\n  align-items: ${(p) => (p.direction === \"column\" ? \"flex-start\" : \"center\")};\n  gap: 16px;\n  align-self: stretch;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst Price = styled.span`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 36px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 500;\n  line-height: 135%;\n  text-align: left;\n`;\n\nconst Sub = styled.span`\n  color: rgba(109, 109, 109, 0.83);\n  text-overflow: ellipsis;\n  font-size: 21px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  text-align: left;\n`;\n\nconst FeaturesArea = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: flex-start;\n  gap: 16px;\n  align-self: stretch;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst PricelistFeatureItem = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  gap: 8px;\n  align-self: stretch;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst IconsMdiCheck = styled.img`\n  width: 24px;\n  height: 24px;\n  object-fit: cover;\n`;\n\nconst FeatureName = styled.span`\n  color: rgb(83, 83, 83);\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  text-align: left;\n  width: 268px;\n  flex: 1;\n`;\n\nconst FeatureName_0001 = styled.span`\n  color: rgb(83, 83, 83);\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  text-align: left;\n  width: 270px;\n  flex: 1;\n`;\n\nconst ButtonAsButton = styled.button`\n  background-color: black;\n  border: solid 1px rgb(28, 28, 28);\n  border-radius: 4px;\n  padding: 12px 89px;\n  color: white;\n  font-size: 18px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 700;\n  outline: none;\n  cursor: pointer;\n  align-self: stretch;\n  flex-shrink: 0;\n\n  :hover {\n    opacity: 0.8;\n  }\n\n  :disabled {\n    opacity: 0.5;\n  }\n\n  :active {\n    opacity: 1;\n  }\n\n  :focus {\n  }\n\n  &[data-highlighted=\"true\"] {\n    background-color: #2562ff;\n    border: none;\n  }\n`;\n"
  },
  {
    "path": ".archives/homepage/components/pricing/free-for-opensource.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport { useRouter } from \"next/router\";\nimport * as k from \"../../k\";\nimport CheckFilled from \"../icons/check-filled\";\n\nconst features = [\n  \"1,000,000 requests / month\",\n  \"5mb per request\",\n  \"Unlimited requests per hour\",\n  \"No down time\",\n];\n\nexport function FreeForOpenSource() {\n  const router = useRouter();\n  return (\n    <FreePricingHeroCardLarge>\n      <Frame315>\n        <Frame247>\n          <Frame246>\n            {features.map((feature, i) => (\n              <PricelistFeatureItemFeatured key={i}>\n                <CheckFilled />\n                <FeatureName_0019>{feature}</FeatureName_0019>\n              </PricelistFeatureItemFeatured>\n            ))}\n          </Frame246>\n        </Frame247>\n        <Frame314>\n          <Frame313>\n            <TextLayout>\n              <Free>Free</Free>\n              <ForOpenSource>for Open Source</ForOpenSource>\n            </TextLayout>\n            <Frame312>\n              <Button\n                onClick={() => {\n                  router.push(k.LINK_APPLY_FOR_OSS_PLAN);\n                }}\n              >\n                <Label>Apply</Label>\n              </Button>\n              <LimitedToPublicProjectsThatAreOnGithub>\n                *Limited to Public Projects that are on Github\n              </LimitedToPublicProjectsThatAreOnGithub>\n            </Frame312>\n          </Frame313>\n        </Frame314>\n      </Frame315>\n    </FreePricingHeroCardLarge>\n  );\n}\n\nconst FreePricingHeroCardLarge = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: column;\n  align-items: center;\n  gap: 10px;\n  box-shadow: 0px 4px 64px 12px rgba(0, 0, 0, 0.08);\n  border-radius: 8px;\n  background-color: white;\n  box-sizing: border-box;\n`;\n\nconst Label = styled.span`\n  color: white;\n  text-overflow: ellipsis;\n  font-size: 18px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 700;\n  text-align: left;\n`;\n\nconst Frame315 = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  align-self: stretch;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst Frame247 = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: column;\n  align-items: flex-start;\n  flex: 1;\n  gap: 10px;\n  border-radius: 8px;\n  width: 520px;\n  background-color: rgb(252, 252, 252);\n  box-sizing: border-box;\n  padding: 56px;\n`;\n\nconst Frame246 = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: flex-start;\n  gap: 24px;\n  align-self: stretch;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst PricelistFeatureItemFeatured = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  gap: 16px;\n  align-self: stretch;\n  height: 32px;\n  box-sizing: border-box;\n  flex-shrink: 0;\n`;\n\nconst Frame245 = styled.img`\n  width: 24px;\n  height: 24px;\n  object-fit: cover;\n`;\n\nconst FeatureName_0019 = styled.span`\n  color: rgb(94, 94, 94);\n  text-overflow: ellipsis;\n  font-size: 18px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  text-align: left;\n`;\n\nconst Frame314 = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: column;\n  align-items: center;\n  flex: 1;\n  gap: 10px;\n  width: 520px;\n  background-color: white;\n  box-sizing: border-box;\n  padding: 31px 72px;\n`;\n\nconst Frame313 = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: center;\n  flex: none;\n  gap: 24px;\n  box-sizing: border-box;\n`;\n\nconst TextLayout = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: flex-end;\n  flex: none;\n  gap: 12px;\n  box-sizing: border-box;\n`;\n\nconst Free = styled.span`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 42px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 700;\n  text-align: left;\n`;\n\nconst ForOpenSource = styled.span`\n  color: rgb(99, 99, 99);\n  text-overflow: ellipsis;\n  font-size: 32px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  text-align: left;\n`;\n\nconst Frame312 = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: column;\n  align-items: center;\n  flex: none;\n  gap: 14px;\n  box-sizing: border-box;\n`;\n\nconst Button = styled.div`\n  cursor: pointer;\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  flex: none;\n  gap: 10px;\n  border: solid 1px rgb(28, 28, 28);\n  border-radius: 4px;\n  width: 337px;\n  background-color: black;\n  box-sizing: border-box;\n  padding: 12px 89px;\n`;\n\nconst LimitedToPublicProjectsThatAreOnGithub = styled.span`\n  color: rgb(154, 154, 154);\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  line-height: 135%;\n  text-align: left;\n`;\n"
  },
  {
    "path": ".archives/homepage/components/pricing/index.ts",
    "content": "export * from \"./card\";\n"
  },
  {
    "path": ".archives/homepage/grida/.gitkeep",
    "content": "This directory is reserved for Grida. you may remove this .gitkeep file once the directory is fullfilled."
  },
  {
    "path": ".archives/homepage/grida/AppbarGroup.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport { Appbar, AppbarParentSite } from \"../components\";\n/**\n * `<AppbarGroup>` ('appbar-group')\n * - [Open in Figma](https://figma.com/file/aPfdtNb1aGFIN9p05cmmVY?node-id=15:1833)\n * - [Open in Grida](https://code.grida.co/files/aPfdtNb1aGFIN9p05cmmVY?node=15:1833)\n *\n *\n * ---\n * @example\n * ```tsx\n * import React from \"react\";\n *\n * export default function () {\n *   return (\n *     <>\n *       👇 instanciate widget like below. 👇\n *       <AppbarGroup/>\n *     </>\n *   )\n * }\n * ```\n * ---\n * @params {any} props - this widget does not requires props. you can pass custom dynamic props to the widget as you want (on typescript, it will raise type check issues).\n * ---\n * @preview\n * ![](https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/fa0a7d06-3956-4a2f-862e-72868c8f9a85)\n * ---\n * @remarks\n * @see {@link https://grida.co/docs} for more information.\n * ---\n * Code generated by grida.co | engine 0.0.1 (Apache-2.0) | Generated code under CC0 (public domain) *This code is free to use, modify, and redistribute. (aknowledgment is not required)*\n *\n *\n * ![Made with Grida](https://bridged-service-static.s3.us-west-1.amazonaws.com/branding/logo/32.png)\n * <!-- Info: Please do not remove this comment unless intended. removing this section will break grida integrations. -->\n * <!-- grida.meta.widget_declaration | engine : 0.0.1 | source : figma://aPfdtNb1aGFIN9p05cmmVY/15:1833 -->\n */\nexport function AppbarGroup() {\n  return (\n    <RootWrapperAppbarGroup>\n      <AppbarParentSite />\n      <Appbar />\n    </RootWrapperAppbarGroup>\n  );\n}\n\nconst RootWrapperAppbarGroup = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: stretch;\n  box-sizing: border-box;\n`;\n\nconst Frame565 = styled.div`\n  height: 50px;\n  overflow: hidden;\n  background-color: black;\n  position: relative;\n  align-self: stretch;\n  flex-shrink: 0;\n`;\n\nconst Home = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  gap: 8px;\n  box-sizing: border-box;\n  position: absolute;\n  left: 200px;\n  top: 12px;\n  width: 171px;\n  height: 26px;\n`;\n\nconst Logo64 = styled.div`\n  width: 25px;\n  height: 26px;\n  overflow: hidden;\n  position: relative;\n`;\n\nconst LogoShapeOnly = styled.div`\n  width: 25px;\n  height: 25px;\n  position: absolute;\n  left: 0px;\n  top: 0px;\n`;\n\nconst Union = styled.img`\n  width: 25px;\n  height: 25px;\n  object-fit: cover;\n  position: absolute;\n  left: 0px;\n  top: 0px;\n`;\n\nconst GridaAppServices = styled.span`\n  color: white;\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: Helvetica, sans-serif;\n  font-weight: 700;\n  letter-spacing: -1px;\n  text-align: left;\n`;\n"
  },
  {
    "path": ".archives/homepage/grida/SectionCtaLast.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\n/**\n * `<SectionCtaLast>` ('section-cta-last')\n * - [Open in Figma](https://figma.com/file/aPfdtNb1aGFIN9p05cmmVY?node-id=15:1600)\n * - [Open in Grida](https://code.grida.co/files/aPfdtNb1aGFIN9p05cmmVY?node=15:1600)\n *\n *\n * ---\n * @example\n * ```tsx\n * import React from \"react\";\n *\n * export default function () {\n *   return (\n *     <>\n *       👇 instanciate widget like below. 👇\n *       <SectionCtaLast/>\n *     </>\n *   )\n * }\n * ```\n * ---\n * @params {any} props - this widget does not requires props. you can pass custom dynamic props to the widget as you want (on typescript, it will raise type check issues).\n * ---\n * @preview\n * ![](https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/9f8bb5c4-af20-4f88-86de-1641944601f4)\n * ---\n * @remarks\n * @see {@link https://grida.co/docs} for more information.\n * ---\n * Code generated by grida.co | engine 0.0.1 (Apache-2.0) | Generated code under CC0 (public domain) *This code is free to use, modify, and redistribute. (aknowledgment is not required)*\n *\n *\n * ![Made with Grida](https://bridged-service-static.s3.us-west-1.amazonaws.com/branding/logo/32.png)\n * <!-- Info: Please do not remove this comment unless intended. removing this section will break grida integrations. -->\n * <!-- grida.meta.widget_declaration | engine : 0.0.1 | source : figma://aPfdtNb1aGFIN9p05cmmVY/15:1600 -->\n */\nexport function SectionCtaLast() {\n  return (\n    <RootWrapperSectionCtaLast>\n      <Frame270>\n        <SayByeToCorsErrors>Say bye to cors errors</SayByeToCorsErrors>\n        <AndFocusOnYouReDevelopment>\n          And focus on you’re development\n        </AndFocusOnYouReDevelopment>\n      </Frame270>\n      <ScribbleGuide>\n        <WeLlSendYouAApiKeyToGetStarted>\n          We’ll send you a Api key to get started\n        </WeLlSendYouAApiKeyToGetStarted>\n        <PointerArtwork\n          src=\"/assets/scribble-pointer.png\"\n          alt=\"image of PointerArtwork\"\n        />\n      </ScribbleGuide>\n      <Frame572>\n        <Frame549>\n          <EnterYourEmailAddress>\n            Enter your email address\n          </EnterYourEmailAddress>\n        </Frame549>\n        <Frame550>\n          <Frame556>\n            <SendMeAnApiKey>Send me an API Key</SendMeAnApiKey>\n          </Frame556>\n        </Frame550>\n      </Frame572>\n    </RootWrapperSectionCtaLast>\n  );\n}\n\nconst RootWrapperSectionCtaLast = styled.div`\n  min-height: 100vh;\n  background-color: white;\n  position: relative;\n`;\n\nconst Frame270 = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: flex-start;\n  gap: 36px;\n  box-sizing: border-box;\n  position: absolute;\n  left: calc((calc((50% + -1px)) - 337px));\n  top: 105px;\n  width: 673px;\n  height: 137px;\n`;\n\nconst SayByeToCorsErrors = styled.span`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 64px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 700;\n  line-height: 98%;\n  text-align: center;\n`;\n\nconst AndFocusOnYouReDevelopment = styled.span`\n  color: rgb(68, 69, 69);\n  text-overflow: ellipsis;\n  font-size: 24px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 500;\n  line-height: 160%;\n  text-align: center;\n  align-self: stretch;\n  flex-shrink: 0;\n`;\n\nconst ScribbleGuide = styled.div`\n  width: 217px;\n  height: 112px;\n  position: absolute;\n  left: 235px;\n  top: 302px;\n`;\n\nconst WeLlSendYouAApiKeyToGetStarted = styled.span`\n  color: rgb(164, 164, 164);\n  text-overflow: ellipsis;\n  font-size: 24px;\n  font-family: \"Nanum Pen Script\", sans-serif;\n  font-weight: 400;\n  line-height: 98%;\n  text-align: left;\n  width: 213px;\n  position: absolute;\n  left: calc((calc((50% + -2px)) - 107px));\n  top: 25px;\n  transform: rotate(-7deg);\n  transform-origin: top left;\n`;\n\nconst PointerArtwork = styled.img`\n  width: 53px;\n  height: 69px;\n  object-fit: cover;\n  position: absolute;\n  left: calc((calc((50% + 59px)) - 26px));\n  top: 49px;\n  transform: rotate(-23deg);\n  transform-origin: top left;\n`;\n\nconst Frame572 = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  gap: 14px;\n  box-sizing: border-box;\n  position: absolute;\n  left: calc((calc((50% + -1px)) - 373px));\n  top: 414px;\n  width: 745px;\n  height: 66px;\n`;\n\nconst Frame549 = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  flex: none;\n  gap: 10px;\n  box-shadow: 0px 4px 48px rgba(0, 0, 0, 0.12);\n  border: solid 1px rgb(210, 210, 210);\n  border-radius: 4px;\n  width: 508px;\n  background-color: white;\n  box-sizing: border-box;\n  padding: 24px;\n`;\n\nconst EnterYourEmailAddress = styled.span`\n  color: rgb(181, 181, 181);\n  text-overflow: ellipsis;\n  font-size: 18px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  letter-spacing: -1px;\n  line-height: 98%;\n  text-align: left;\n`;\n\nconst Frame550 = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: column;\n  align-items: center;\n  flex: none;\n  gap: 10px;\n  box-shadow: 0px 4px 48px rgba(0, 0, 0, 0.12);\n  border: solid 1px rgb(210, 210, 210);\n  border-radius: 4px;\n  height: 66px;\n  background-color: black;\n  box-sizing: border-box;\n  padding: 0px 21px;\n`;\n\nconst Frame556 = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  gap: 10px;\n  align-self: stretch;\n  height: 66px;\n  box-sizing: border-box;\n  padding: 10px;\n  flex-shrink: 0;\n`;\n\nconst SendMeAnApiKey = styled.span`\n  color: white;\n  text-overflow: ellipsis;\n  font-size: 18px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 500;\n  letter-spacing: -1px;\n  line-height: 98%;\n  text-align: left;\n`;\n"
  },
  {
    "path": ".archives/homepage/grida/SectionDisclaimer.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport { motion } from \"framer-motion\";\n/**\n *\n */\nfunction useSeoNotImportant() {\n  const [load, setLoad] = React.useState(false);\n\n  React.useEffect(() => {\n    // load after 5 seconds\n    setTimeout(() => {\n      setLoad(true);\n    }, 5000);\n  }, []);\n\n  return load;\n}\n\n/**\n * `<SectionDisclaimer>` ('section-disclaimer')\n * - [Open in Figma](https://figma.com/file/aPfdtNb1aGFIN9p05cmmVY?node-id=20:1965)\n * - [Open in Grida](https://code.grida.co/files/aPfdtNb1aGFIN9p05cmmVY?node=20:1965)\n *\n *\n * ---\n * @example\n * ```tsx\n * import React from \"react\";\n *\n * export default function () {\n *   return (\n *     <>\n *       👇 instanciate widget like below. 👇\n *       <SectionDisclaimer/>\n *     </>\n *   )\n * }\n * ```\n * ---\n * @params {any} props - this widget does not requires props. you can pass custom dynamic props to the widget as you want (on typescript, it will raise type check issues).\n * ---\n * @preview\n * ![](https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/60bed68c-21d3-462e-a55a-003e642798e3)\n * ---\n * @remarks\n * @see {@link https://grida.co/docs} for more information.\n * ---\n * Code generated by grida.co | engine 0.0.1 (Apache-2.0) | Generated code under CC0 (public domain) *This code is free to use, modify, and redistribute. (aknowledgment is not required)*\n *\n *\n * ![Made with Grida](https://bridged-service-static.s3.us-west-1.amazonaws.com/branding/logo/32.png)\n * <!-- Info: Please do not remove this comment unless intended. removing this section will break grida integrations. -->\n * <!-- grida.meta.widget_declaration | engine : 0.0.1 | source : figma://aPfdtNb1aGFIN9p05cmmVY/20:1965 -->\n */\nexport function SectionDisclaimer() {\n  const load = useSeoNotImportant();\n\n  if (!load) {\n    return <></>;\n  }\n\n  return (\n    <RootWrapperSectionDisclaimer\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      transition={{ duration: 0.5 }}\n    >\n      <Disclaimer>DISCLAIMER</Disclaimer>\n      <Text>\n        1. This project's intend is to serve developers a reliable cors proxy\n        service with fast response for their development. Using a cors proxy\n        service to connect to your own server is not a best practice. We'll\n        consistently optimize our service infra to keep the paid version\n        affordable as possible.\n        <br />\n        <br />\n        2. The original code behind cors proxy is by Rob wu's cors-anywhere and\n        the playground is forked from hoppscotch. both licensed under MIT, and\n        our project cors.sh is licensed under Apache 2.0.\n      </Text>\n    </RootWrapperSectionDisclaimer>\n  );\n}\n\nconst RootWrapperSectionDisclaimer = styled(motion.div)`\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 40px;\n`;\n\nconst Disclaimer = styled.span`\n  color: white;\n  opacity: 0.5;\n  text-overflow: ellipsis;\n  font-size: 18px;\n  font-family: \"Roboto Mono\", sans-serif;\n  font-weight: 500;\n  text-align: center;\n`;\n\nconst Text = styled.span`\n  color: white;\n  opacity: 0.35;\n  text-overflow: ellipsis;\n  font-size: 14px;\n  font-family: \"Roboto Mono\", sans-serif;\n  text-transform: uppercase;\n  font-weight: 400;\n  text-align: left;\n  width: 400px;\n`;\n"
  },
  {
    "path": ".archives/homepage/grida/SectionHero.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport { OnboardingCta } from \"../components\";\nimport CheckFilled from \"../components/icons/check-filled\";\nimport { Close } from \"@mui/icons-material\";\n\nexport function TryPlayground() {\n  return (\n    <RootWrapperTryPlayground>\n      Try it out on playground for requests with body 👉{\" \"}\n      <a className=\"underline\" href=\"/playground\">\n        cors.sh/playground\n      </a>\n    </RootWrapperTryPlayground>\n  );\n}\n\nconst RootWrapperTryPlayground = styled.span`\n  color: rgba(0, 0, 0, 0.6);\n  text-overflow: ellipsis;\n  font-size: 14px;\n  font-family: Inter, sans-serif;\n  font-weight: 500;\n  line-height: 160%;\n  text-align: left;\n  width: 356px;\n\n  .underline {\n    text-decoration: underline;\n  }\n`;\n\n/**\n * `<SectionHero>` ('section-hero')\n * - [Open in Figma](https://figma.com/file/aPfdtNb1aGFIN9p05cmmVY?node-id=15:1784)\n * - [Open in Grida](https://code.grida.co/files/aPfdtNb1aGFIN9p05cmmVY?node=15:1784)\n *\n *\n * ---\n * @example\n * ```tsx\n * import React from \"react\";\n *\n * export default function () {\n *   return (\n *     <>\n *       👇 instanciate widget like below. 👇\n *       <SectionHero/>\n *     </>\n *   )\n * }\n * ```\n * ---\n * @params {any} props - this widget does not requires props. you can pass custom dynamic props to the widget as you want (on typescript, it will raise type check issues).\n * ---\n * @preview\n * ![](https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/9ad3b248-98c3-46dc-9668-e4bee71c948d)\n * ---\n * @remarks\n * @see {@link https://grida.co/docs} for more information.\n * ---\n * Code generated by grida.co | engine 0.0.1 (Apache-2.0) | Generated code under CC0 (public domain) *This code is free to use, modify, and redistribute. (aknowledgment is not required)*\n *\n *\n * ![Made with Grida](https://bridged-service-static.s3.us-west-1.amazonaws.com/branding/logo/32.png)\n * <!-- Info: Please do not remove this comment unless intended. removing this section will break grida integrations. -->\n * <!-- grida.meta.widget_declaration | engine : 0.0.1 | source : figma://aPfdtNb1aGFIN9p05cmmVY/15:1784 -->\n */\nexport function SectionHero() {\n  return (\n    <RootWrapperSectionHero>\n      <Headings>\n        <HeadingAsH1>\n          Sick of\n          <br />\n          cors errors?\n        </HeadingAsH1>\n        <Description>\n          A Fast & Reliable CORS Proxy for your websites\n        </Description>\n      </Headings>\n      <Cta>\n        <OnboardingCta />\n        <div style={{ height: 20 }} />\n        <TryPlayground />\n      </Cta>\n      <FeatuersList>\n        <Item>\n          <CheckFilled />\n          <ItemText>Secure</ItemText>\n        </Item>\n        <Item>\n          <CheckFilled />\n          <ItemText>Reliable</ItemText>\n        </Item>\n        <Item>\n          <CheckFilled />\n          <ItemText>Fast</ItemText>\n        </Item>\n        <Item>\n          <CheckFilled />\n          <ItemText>A cors-anywhere mirror</ItemText>\n        </Item>\n      </FeatuersList>\n      <Example>\n        <X>\n          <Close sx={{ fontSize: 12 }} />\n        </X>\n        <Text>\n          Access to XMLHttpRequest at 'https://target.domain' from origin\n          'https://my.site' has been blocked by CORS policy: No\n          'Access-Control-Allow-Origin' header is present on the requested\n          resource.\n        </Text>\n      </Example>\n      <FixThisInMinutes>Fix this in minutes</FixThisInMinutes>\n      <PointerArtwork\n        src=\"/assets/scribble-pointer.png\"\n        alt=\"image of PointerArtwork\"\n      />\n    </RootWrapperSectionHero>\n  );\n}\n\nconst RootWrapperSectionHero = styled.div`\n  min-height: 100vh;\n  background-color: white;\n  position: relative;\n`;\n\nconst Headings = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: flex-start;\n  gap: 32px;\n  box-sizing: border-box;\n  position: absolute;\n  left: calc((calc((50% + -245px)) - 276px));\n  top: 171px;\n  width: 551px;\n  height: 256px;\n`;\n\nconst HeadingAsH1 = styled.h1`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 80px;\n  font-family: Inter, sans-serif;\n  font-weight: 700;\n  letter-spacing: -2px;\n  line-height: 98%;\n  text-align: left;\n`;\n\nconst Description = styled.h2`\n  color: rgba(0, 0, 0, 0.6);\n  text-overflow: ellipsis;\n  font-size: 21px;\n  font-family: Inter, sans-serif;\n  font-weight: 500;\n  line-height: 160%;\n  text-align: left;\n`;\n\nconst Cta = styled.div`\n  position: absolute;\n  left: calc((calc((50% + -184px)) - 337px));\n  top: 517px;\n  height: 66px;\n`;\n\nconst FeatuersList = styled.ul`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  gap: 138px;\n  box-sizing: border-box;\n  position: absolute;\n  left: calc((calc((50% + -42px)) - 478px));\n  top: 703px;\n  height: 24px;\n  margin: 0;\n  padding: 0;\n`;\n\nconst Item = styled.li`\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  flex: none;\n  gap: 15px;\n  box-sizing: border-box;\n`;\n\nconst Icon = styled.img`\n  width: 24px;\n  height: 24px;\n  object-fit: cover;\n`;\n\nconst ItemText = styled.span`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 16px;\n  font-family: Monaco, sans-serif;\n  font-weight: 400;\n  text-align: left;\n`;\n\nconst Example = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  gap: 10px;\n  box-shadow: 0px 4px 32px rgba(255, 0, 0, 0.08);\n  border: solid 1px rgb(255, 46, 46);\n  border-radius: 4px;\n  background-color: rgb(252, 240, 240);\n  box-sizing: border-box;\n  padding: 10px;\n  position: absolute;\n  left: calc((calc((50% + 375px)) - 269px));\n  top: 247px;\n  width: 537px;\n  word-break: break-all;\n`;\n\nconst X = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  flex: none;\n  background-color: rgb(233, 22, 6);\n  border-radius: 50%;\n  width: 16px;\n  height: 16px;\n  gap: 10px;\n  color: white;\n  box-sizing: border-box;\n  margin: 4px 0px 0px;\n`;\n\nconst Text = styled.span`\n  color: rgb(233, 22, 6);\n  text-overflow: ellipsis;\n  font-size: 18px;\n  font-family: \"Roboto Mono\", sans-serif;\n  font-weight: 400;\n  letter-spacing: -1px;\n  line-height: 119%;\n  text-align: left;\n  width: 492px;\n  flex: 1;\n`;\n\nconst FixThisInMinutes = styled.span`\n  color: rgb(164, 164, 164);\n  text-overflow: ellipsis;\n  font-size: 24px;\n  font-family: \"Nanum Pen Script\", sans-serif;\n  font-weight: 400;\n  line-height: 98%;\n  text-align: left;\n  position: absolute;\n  left: calc((calc((50% + 102px)) - 84px));\n  top: 123px;\n  transform: rotate(-7deg);\n  transform-origin: top left;\n`;\n\nconst PointerArtwork = styled.img`\n  width: 53px;\n  height: 69px;\n  object-fit: cover;\n  position: absolute;\n  left: calc((calc((50% + 148px)) - 26px));\n  top: 143px;\n  transform: rotate(-9deg);\n  transform-origin: top left;\n`;\n"
  },
  {
    "path": ".archives/homepage/grida/SectionPricing.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport { PricingCardsList } from \"../layouts/pricing-card-list\";\nimport { useRouter } from \"next/router\";\nimport { FreeForOpenSource } from \"../components/pricing/free-for-opensource\";\nimport { PRICE_PERSONAL_PRO_MONTHLY } from \"../k\";\n/**\n * `<SectionPricing>` ('section-pricing')\n * - [Open in Figma](https://figma.com/file/aPfdtNb1aGFIN9p05cmmVY?node-id=15:1613)\n * - [Open in Grida](https://code.grida.co/files/aPfdtNb1aGFIN9p05cmmVY?node=15:1613)\n *\n *\n * ---\n * @example\n * ```tsx\n * import React from \"react\";\n *\n * export default function () {\n *   return (\n *     <>\n *       👇 instanciate widget like below. 👇\n *       <SectionPricing/>\n *     </>\n *   )\n * }\n * ```\n * ---\n * @params {any} props - this widget does not requires props. you can pass custom dynamic props to the widget as you want (on typescript, it will raise type check issues).\n * ---\n * @preview\n * ![](https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/3b4fded0-7047-4710-8959-6fbef3fccb2b)\n * ---\n * @remarks\n * @see {@link https://grida.co/docs} for more information.\n * ---\n * Code generated by grida.co | engine 0.0.1 (Apache-2.0) | Generated code under CC0 (public domain) *This code is free to use, modify, and redistribute. (aknowledgment is not required)*\n *\n *\n * ![Made with Grida](https://bridged-service-static.s3.us-west-1.amazonaws.com/branding/logo/32.png)\n * <!-- Info: Please do not remove this comment unless intended. removing this section will break grida integrations. -->\n * <!-- grida.meta.widget_declaration | engine : 0.0.1 | source : figma://aPfdtNb1aGFIN9p05cmmVY/15:1613 -->\n */\nexport function SectionPricing() {\n  const router = useRouter();\n  return (\n    <RootWrapperSectionPricing id=\"pricing\">\n      <Pricing>Pricing</Pricing>\n      <div\n        style={{\n          display: \"flex\",\n          top: 300,\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          position: \"relative\",\n        }}\n      >\n        <PricingCardsList\n          initialSelection={PRICE_PERSONAL_PRO_MONTHLY}\n          disableSelection\n          enableIndividualActions\n          onGetStartedClick={(price) => {\n            router.push({ pathname: \"/get-started\", query: { price } });\n          }}\n        />\n      </div>\n      <FreeForOpensourcePosition>\n        <FreeForOpenSource />\n      </FreeForOpensourcePosition>\n      <Line />\n    </RootWrapperSectionPricing>\n  );\n}\n\nconst RootWrapperSectionPricing = styled.div`\n  height: 1632px;\n  background-color: white;\n  position: relative;\n`;\n\nconst Pricing = styled.span`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 64px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 700;\n  line-height: 98%;\n  text-align: center;\n  position: absolute;\n  left: calc((calc((50% + 1px)) - 108px));\n  top: 88px;\n`;\n\nconst FreeForOpensourcePosition = styled.div`\n  position: absolute;\n  left: calc((calc((50% + 1px)) - 520px));\n  top: 1184px;\n  width: 1040px;\n  height: 312px;\n`;\n\nconst Line = styled.div`\n  width: 1039px;\n  height: 0px;\n  border-top: solid 1px rgba(0, 0, 0, 0.1);\n  position: absolute;\n  left: calc((calc((50% + 1px)) - 520px));\n  top: 1108px;\n`;\n\nconst Frame587 = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: flex-start;\n  gap: 12px;\n  box-sizing: border-box;\n  position: absolute;\n  left: calc((calc((50% + 0px)) - 52px));\n  top: 229px;\n  width: 104px;\n  height: 14px;\n`;\n\nconst Monthly = styled.span`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 14px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 700;\n  line-height: 98%;\n  text-align: left;\n`;\n\nconst Yearly = styled.span`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 14px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  line-height: 98%;\n  text-align: left;\n`;\n"
  },
  {
    "path": ".archives/homepage/grida/SectionUsage.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport { useRouter } from \"next/router\";\nimport CheckFilled from \"../components/icons/check-filled\";\nimport { DemoTerminal } from \"../components/demo-terminal\";\nimport { TestItOutCta } from \"../components\";\n/**\n * `<SectionUsage>` ('section-usage')\n * - [Open in Figma](https://figma.com/file/aPfdtNb1aGFIN9p05cmmVY?node-id=27:2213)\n * - [Open in Grida](https://code.grida.co/files/aPfdtNb1aGFIN9p05cmmVY?node=27:2213)\n *\n *\n * ---\n * @example\n * ```tsx\n * import React from \"react\";\n *\n * export default function () {\n *   return (\n *     <>\n *       👇 instanciate widget like below. 👇\n *       <SectionUsage/>\n *     </>\n *   )\n * }\n * ```\n * ---\n * @params {any} props - this widget does not requires props. you can pass custom dynamic props to the widget as you want (on typescript, it will raise type check issues).\n * ---\n * @preview\n * ![](https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/02b521c4-1b43-4ea3-b374-71904e696e66)\n * ---\n * @remarks\n * @see {@link https://grida.co/docs} for more information.\n * ---\n * Code generated by grida.co | engine 0.0.1 (Apache-2.0) | Generated code under CC0 (public domain) *This code is free to use, modify, and redistribute. (aknowledgment is not required)*\n *\n *\n * ![Made with Grida](https://bridged-service-static.s3.us-west-1.amazonaws.com/branding/logo/32.png)\n * <!-- Info: Please do not remove this comment unless intended. removing this section will break grida integrations. -->\n * <!-- grida.meta.widget_declaration | engine : 0.0.1 | source : figma://aPfdtNb1aGFIN9p05cmmVY/27:2213 -->\n */\nexport function SectionUsage() {\n  const [target, setTarget] = React.useState(\"https://acme.com\");\n\n  return (\n    <RootWrapperSectionUsage id=\"usage\">\n      <Success>\n        <CheckFilled size={16} color=\"blue\" />\n        <FetchSuccess>Fetch success!</FetchSuccess>\n      </Success>\n      <Headings>\n        <HeadingAsH2>Simple usage</HeadingAsH2>\n        <Desc>\n          You can use the proxy service by simply adding https://proxy.cors.sh/\n          to your existing api\n        </Desc>\n      </Headings>\n      <Display>\n        <Line36 />\n        <Frame586>\n          <Rectangle865 />\n          <Rectangle867 />\n          <Rectangle866 />\n          <Ellipse42 />\n          <Ellipse43 />\n          <Ellipse44 />\n          <Vector xmlns=\"http://www.w3.org/2000/svg\">\n            <path\n              fill=\"rgb(206, 206, 206)\"\n              d=\"M19.9995 0C8.97168 0 0 8.9718 0 19.9996C0 31.0273 8.97168 39.9991 19.9995 39.9991C31.0273 39.9991 39.999 31.0275 39.999 19.9996C39.999 8.9718 31.0273 0 19.9995 0ZM16.9143 2.03209C15.2909 3.42189 13.7368 5.15207 12.4882 6.99386C11.9259 7.82338 11.4204 8.67991 10.9714 9.55839L5.06477 9.55839C7.80238 5.65431 12.0295 2.86833 16.9143 2.03209ZM3.96743 11.3281L10.1568 11.3281C9.12399 13.8402 8.53952 16.4995 8.43499 19.208L1.78799 19.208C1.90998 16.3658 2.68476 13.6901 3.96743 11.3281ZM1.79613 20.9777L8.43428 20.9777C8.53609 23.7286 9.11479 26.3636 10.1619 28.8576L4.07149 28.8576C2.7559 26.5013 1.94738 23.8254 1.79613 20.9777ZM5.19702 30.6272L10.9966 30.6272C11.4389 31.4706 11.9366 32.2966 12.4915 33.1033C13.9637 35.2438 15.546 36.836 16.8825 37.9616C12.0892 37.1323 7.93156 34.4251 5.19702 30.6272ZM19.1147 37.4689C17.138 36.0214 14.8111 33.7127 13.0283 30.6272L19.1147 30.6272L19.1147 37.4689ZM19.1147 28.8575L12.1099 28.8575C11.0559 26.573 10.328 23.9377 10.2057 20.9776L19.1145 20.9776L19.1147 28.8575ZM19.1147 19.208L10.2057 19.208C10.3159 16.4567 10.9418 13.8203 12.0738 11.3281L19.1147 11.3281L19.1147 19.208ZM19.1147 9.55839L12.9766 9.55839C13.2782 9.02725 13.6031 8.50318 13.953 7.9869C15.7749 5.29931 17.7719 3.5394 19.1147 2.54212L19.1147 9.55839ZM38.211 19.208L31.5639 19.208C31.4594 16.4995 30.8749 13.8402 29.8421 11.3281L36.0315 11.3281C37.3141 13.6901 38.089 16.3658 38.211 19.208ZM34.9343 9.55839L29.0277 9.55839C28.5788 8.67991 28.0731 7.82338 27.511 6.99386C26.2624 5.15195 24.7077 3.42225 23.0842 2.03197C27.9692 2.86821 32.1964 5.6542 34.9343 9.55839ZM20.8844 2.542C22.2271 3.53928 24.2241 5.29919 26.046 7.98678C26.3959 8.50306 26.7208 9.02713 27.0224 9.55827L20.8844 9.55827L20.8844 2.542ZM20.8844 11.3281L27.9252 11.3281C29.0572 13.8203 29.6831 16.4567 29.7933 19.208L20.8844 19.208L20.8844 11.3281ZM20.8844 20.9777L29.7932 20.9777C29.671 23.9378 28.943 26.5731 27.889 28.8576L20.8842 28.8576L20.8844 20.9777ZM20.8844 30.6272L26.9707 30.6272C25.1879 33.7126 22.861 36.0214 20.8844 37.4689L20.8844 30.6272ZM23.1168 37.9616C24.4531 36.8361 26.0354 35.2436 27.5074 33.1034C28.0623 32.2967 28.56 31.4707 29.0023 30.6273L34.8019 30.6273C32.0675 34.4249 27.91 37.1322 23.1168 37.9616ZM35.9275 28.8575L29.837 28.8575C30.8841 26.3635 31.4628 23.7286 31.5646 20.9776L38.2028 20.9776C38.0515 23.8254 37.2431 26.5013 35.9275 28.8575Z\"\n            />\n          </Vector>\n        </Frame586>\n        <Frame579>\n          <Image2 src=\"/logos-3p/instagram@2x.png\" alt=\"image of Image2\" />\n        </Frame579>\n        <Frame587>\n          <ProxyCorsSh>proxy.cors.sh</ProxyCorsSh>\n        </Frame587>\n      </Display>\n      <DemoContainer>\n        <ReqExample>\n          <TestItOutCta onChange={setTarget} />\n        </ReqExample>\n        <DemoTerminal target={target} />\n      </DemoContainer>\n      <Items>\n        <Item>\n          <CheckFilled size={16} />\n          <FetchImagesFromInstagram>\n            Fetch images from Tiktok\n          </FetchImagesFromInstagram>\n        </Item>\n        <Item>\n          <CheckFilled size={16} />\n          <ConnectToYourDevServer>\n            Connect to your dev server\n          </ConnectToYourDevServer>\n        </Item>\n        <Item>\n          <CheckFilled size={16} />\n          <FetchImagesFromInstagram>\n            Fetch images from Instagram\n          </FetchImagesFromInstagram>\n        </Item>\n      </Items>\n      {/* <ReqExample>GET https://proxy.cors.sh/https://instagram.com</ReqExample> */}\n      <Logos>\n        <Logo src=\"/logos-3p/instagram.png\" alt=\"instagram\" />\n        <Logo src=\"/logos-3p/tiktok.png\" alt=\"tiktok\" />\n        <Logo src=\"/logos-3p/twitter.png\" alt=\"twitter\" />\n        <Logo src=\"/logos-3p/github.png\" alt=\"github\" />\n      </Logos>\n    </RootWrapperSectionUsage>\n  );\n}\n\nconst RootWrapperSectionUsage = styled.div`\n  height: 1800px;\n  background-color: white;\n  position: relative;\n`;\n\nconst Success = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  gap: 10px;\n  box-shadow: 0px 4px 32px rgba(48, 94, 255, 0.1);\n  border: solid 1px rgb(0, 71, 255);\n  border-radius: 4px;\n  background-color: rgba(0, 56, 255, 0.81);\n  box-sizing: border-box;\n  padding: 10px;\n  position: absolute;\n  left: calc((calc((50% + 5px)) - 247px));\n  top: 604px;\n  width: 494px;\n  height: 41px;\n`;\n\nconst FetchSuccess = styled.span`\n  color: white;\n  text-overflow: ellipsis;\n  font-size: 18px;\n  font-family: \"Source Code Pro\", sans-serif;\n  font-weight: 500;\n  letter-spacing: -1px;\n  line-height: 119%;\n  text-align: left;\n  width: 451px;\n  flex: 1;\n`;\n\nconst Headings = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: column;\n  align-items: center;\n  gap: 32px;\n  box-sizing: border-box;\n  position: absolute;\n  left: calc((calc((50% + 1px)) - 245px));\n  top: 92px;\n  width: 489px;\n  height: 163px;\n`;\n\nconst HeadingAsH2 = styled.h2`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 64px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 700;\n  line-height: 98%;\n  text-align: center;\n`;\n\nconst Desc = styled.p`\n  color: rgba(0, 0, 0, 0.6);\n  text-overflow: ellipsis;\n  font-size: 21px;\n  font-family: Inter, sans-serif;\n  font-weight: 500;\n  line-height: 160%;\n  text-align: center;\n  width: 489px;\n`;\n\nconst Display = styled.div`\n  width: 486px;\n  height: 105px;\n  position: absolute;\n  left: calc((calc((50% + 0px)) - 243px));\n  top: 407px;\n`;\n\nconst Line36 = styled.div`\n  width: 230px;\n  height: 0px;\n  border-top: solid 1px rgb(236, 236, 236);\n  position: absolute;\n  left: calc((calc((50% + -5px)) - 115px));\n  top: 53px;\n`;\n\nconst Frame586 = styled.div`\n  width: 136px;\n  height: 105px;\n  background-color: white;\n  border: solid 1px rgba(0, 0, 0, 0.1);\n  border-radius: 4px;\n  position: absolute;\n  box-shadow: 0px 4px 24px rgba(0, 0, 0, 0.04);\n  left: calc((calc((50% + -175px)) - 68px));\n  top: 0px;\n`;\n\nconst Rectangle865 = styled.div`\n  width: 109px;\n  height: 8px;\n  background-color: rgb(239, 239, 239);\n  border-radius: 8px;\n  position: absolute;\n  left: 5px;\n  top: 10px;\n`;\n\nconst Rectangle867 = styled.div`\n  width: 15px;\n  height: 8px;\n  background-color: rgb(239, 239, 239);\n  border-radius: 8px;\n  position: absolute;\n  left: 116px;\n  top: 10px;\n`;\n\nconst Rectangle866 = styled.div`\n  width: 20px;\n  height: 6px;\n  background-color: rgb(217, 217, 217);\n  border-radius: 2px;\n  position: absolute;\n  left: 20px;\n  top: 3px;\n`;\n\nconst Ellipse42 = styled.div`\n  width: 3px;\n  height: 3px;\n  background-color: rgb(217, 217, 217);\n  border-radius: 3px / 3px;\n  position: absolute;\n  left: 6px;\n  top: 5px;\n`;\n\nconst Ellipse43 = styled.div`\n  width: 3px;\n  height: 3px;\n  background-color: rgb(217, 217, 217);\n  border-radius: 3px / 3px;\n  position: absolute;\n  left: 10px;\n  top: 5px;\n`;\n\nconst Ellipse44 = styled.div`\n  width: 3px;\n  height: 3px;\n  background-color: rgb(217, 217, 217);\n  border-radius: 3px / 3px;\n  position: absolute;\n  left: 14px;\n  top: 5px;\n`;\n\nconst Vector = styled.svg`\n  width: 40px;\n  height: 40px;\n  position: absolute;\n  left: 48px;\n  top: 40px;\n  right: 48px;\n  bottom: 25px;\n`;\n\nconst Frame579 = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  gap: 10px;\n  box-shadow: 0px 4px 24px rgba(0, 0, 0, 0.04);\n  border: solid 1px rgba(0, 0, 0, 0.1);\n  border-radius: 4px;\n  background-color: white;\n  box-sizing: border-box;\n  padding: 10px 20px;\n  position: absolute;\n  left: calc((calc((50% + 175px)) - 68px));\n  top: 0px;\n  width: 136px;\n  height: 105px;\n`;\n\nconst Image2 = styled.img`\n  width: 40px;\n  height: 40px;\n  object-fit: cover;\n`;\n\nconst Frame587 = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  gap: 10px;\n  box-shadow: 0px 4px 24px rgba(0, 0, 0, 0.04);\n  border: solid 1px rgba(0, 0, 0, 0.1);\n  border-radius: 4px;\n  background-color: white;\n  box-sizing: border-box;\n  padding: 10px 20px;\n  position: absolute;\n  left: calc((calc((50% + 0px)) - 51px));\n  top: 38px;\n  width: 102px;\n  height: 29px;\n`;\n\nconst ProxyCorsSh = styled.span`\n  color: rgba(0, 0, 0, 0.6);\n  text-overflow: ellipsis;\n  font-size: 11px;\n  font-family: Inter, sans-serif;\n  font-weight: 700;\n  line-height: 160%;\n  text-align: left;\n`;\n\nconst DemoContainer = styled.div`\n  position: absolute;\n  width: 948px;\n  height: 500px;\n  left: calc((calc((50% + 0px)) - 474px));\n  top: 968px;\n`;\n\nconst Items = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: flex-start;\n  gap: 12px;\n  box-sizing: border-box;\n  position: absolute;\n  left: calc((calc((50% + 5px)) - 247px));\n  top: 710px;\n  width: 494px;\n  height: 147px;\n`;\n\nconst Item = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: row;\n  align-items: center;\n  gap: 10px;\n  box-shadow: 0px 4px 24px rgba(0, 0, 0, 0.04);\n  border: solid 1px rgb(208, 208, 208);\n  border-radius: 4px;\n  align-self: stretch;\n  background-color: rgba(255, 255, 255, 0.08);\n  box-sizing: border-box;\n  padding: 10px;\n  flex-shrink: 0;\n`;\n\nconst FetchImagesFromInstagram = styled.span`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 18px;\n  font-family: \"Source Code Pro\", sans-serif;\n  font-weight: 500;\n  letter-spacing: -1px;\n  line-height: 119%;\n  text-align: left;\n  width: 451px;\n  flex: 1;\n`;\n\nconst ConnectToYourDevServer = styled.span`\n  color: black;\n  text-overflow: ellipsis;\n  font-size: 18px;\n  font-family: \"Source Code Pro\", sans-serif;\n  font-weight: 500;\n  letter-spacing: -1px;\n  line-height: 119%;\n  text-align: left;\n  width: 451px;\n  flex: 1;\n`;\n\nconst ReqExample = styled.span`\n  position: absolute;\n  display: flex;\n  z-index: 1;\n  top: 570px;\n  left: 0px;\n  right: 0px;\n  justify-content: center;\n`;\n\nconst Logos = styled.div`\n  display: flex;\n  justify-content: center;\n  flex-direction: row;\n  align-items: center;\n  gap: 27px;\n  box-sizing: border-box;\n  position: absolute;\n  left: calc((calc((50% + -1px)) - 81px));\n  top: 295px;\n  width: 161px;\n  height: 20px;\n`;\n\nconst Logo = styled.img`\n  width: 20px;\n  height: 20px;\n  object-fit: cover;\n`;\n\nconst Logo_0002 = styled.img`\n  width: 20px;\n  height: 16px;\n  object-fit: cover;\n`;\n"
  },
  {
    "path": ".archives/homepage/grida.config.js",
    "content": "// This file is auto-generated by Grida.\n\n/**\n * @type {import('grida').GridaConfig}\n */\nconst config = {\n  name: 'homepage',\n  type: 'project',\n  $schema: 'node_modules/@grida/grida-config-schema/v1.json',\n  designsource: {\n    provider: 'figma',\n    file: 'aPfdtNb1aGFIN9p05cmmVY',\n    client: 'api.figma.com',\n  },\n  fallbackDir: './grida',\n  framework: {\n    framework: 'react',\n    language: 'tsx',\n    styling: {\n      type: 'styled-components',\n      module: '@emotion/styled',\n    },\n    component_declaration_style: {\n      exporting_style: {\n        type: 'export-named-functional-component',\n        declaration_syntax_choice: 'function',\n        export_declaration_syntax_choice: 'export',\n        exporting_position: 'with-declaration',\n      },\n    },\n    packages: [\n      '@emotion/styled',\n      '@emotion/react',\n    ],\n  },\n};\n\nmodule.exports = config;"
  },
  {
    "path": ".archives/homepage/k/examples.ts",
    "content": "/**\n * usage code snippet\n */\nexport const examples = {\n  simplest: (\n    t: string,\n    key: string = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n  ) => `fetch('https://proxy.cors.sh/${t}', {\n  headers: {\n    'x-cors-api-key': '${key}',\n  }\n}).then(console.log);`,\n  fetch: (\n    t: string,\n    key: string = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n  ) => `fetch('https://proxy.cors.sh/${t}', {\n  headers: {\n    'x-cors-api-key': '${key}',\n  }\n});\n\n// or...\nfuncton fetchWithProxy(url, params){\n  return fetch(\\`https://proxy.cors.sh/\\${url}\\`, \n  { \n    ...params,\n    headers: \n    { \n      ...params.headers,\n      'x-cors-api-key': '${key}'\n    }\n  });\n}\n\nfetchWithProxy('${t}')\n  `,\n  axios: (\n    t: string,\n    key: string = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n  ) => `import Axios from \"axios\";\n\nAxios.get('https://proxy.cors.sh/${t}', {\n  headers: {\n    'x-cors-api-key': '${key}',\n  }\n})\n\n// or...\nconst client = Axios.create({\n  baseURL: 'https://proxy.cors.sh/' + '${t}',\n  headers: {\n    'x-cors-api-key': '${key}',\n  }\n})\n\nclient.get('/')\n`,\n} as const;\n"
  },
  {
    "path": ".archives/homepage/k/external-links.ts",
    "content": "export const LINK_APPLY_FOR_OSS_PLAN =\n  \"https://github.com/gridaco/cors.sh/issues/new?template=apply-for-oss-program.yml\";\n"
  },
  {
    "path": ".archives/homepage/k/host.ts",
    "content": "export const HOST =\n  process.env.NODE_ENV === \"production\"\n    ? \"https://cors.sh\"\n    : \"http://localhost:8823\";\n\nexport const SERVER_URL =\n  process.env.NODE_ENV === \"production\"\n    ? // TODO: this url is not live.\n      \"https://services.cors.sh\"\n    : \"http://localhost:4021\";\n"
  },
  {
    "path": ".archives/homepage/k/index.ts",
    "content": "export * from \"./price\";\nexport * from \"./external-links\";\nexport * from \"./host\";\nexport * from \"./examples\";\n"
  },
  {
    "path": ".archives/homepage/k/price.ts",
    "content": "const _PRICE_PERSONAL_PRO_MONTHLY = {\n  test: \"price_1Lda7UAvR3geCh5rVaajCSw6\",\n  live: \"price_1LbnTwAvR3geCh5rZm9v8CAy\",\n} as const;\n\nconst _PRICE_PERSONAL_PRO_YEARLY = {\n  test: \"price_1MMpznAvR3geCh5ro9O4Gdlt\",\n  live: \"price_1LbnV4AvR3geCh5rMEwJ5Zf1\",\n};\n\nconst _PRICE_ENTERPRISE_PRO_YEARLY = {\n  test: \"price_1MNTfvAvR3geCh5rV0KueWOk\",\n  live: \"price_1MNTlpAvR3geCh5r3gqzq2s7\",\n};\n\nconst _PRICE_FREE = \"price_1KeF2VAvR3geCh5rq6s4V2P1\";\n\nexport const PRICE_PERSONAL_PRO_MONTHLY =\n  process.env.NODE_ENV === \"production\"\n    ? _PRICE_PERSONAL_PRO_MONTHLY.live\n    : _PRICE_PERSONAL_PRO_MONTHLY.test;\n\nexport const PRICE_PERSONAL_PRO_YEARLY =\n  process.env.NODE_ENV === \"production\"\n    ? _PRICE_PERSONAL_PRO_YEARLY.live\n    : _PRICE_PERSONAL_PRO_YEARLY.test;\n\nexport const PRICE_ENTERPRISE_PRO_YEARLY =\n  process.env.NODE_ENV === \"production\"\n    ? _PRICE_ENTERPRISE_PRO_YEARLY.live\n    : _PRICE_ENTERPRISE_PRO_YEARLY.test;\n\nexport const PRICE_PAY_AS_YOU_GO = \"price_1LegsaAvR3geCh5rBCiuVmDt\";\n\nexport const PRICE_FREE_MONTHLY = _PRICE_FREE;\n\ninterface Price {\n  id: string;\n  name: string;\n  description?: string;\n  price: string;\n  unit?: string;\n  features: string[];\n}\n\nconst price_free: Price = {\n  id: PRICE_FREE_MONTHLY,\n  name: \"For Testing\",\n  price: \"Free\",\n  features: [\n    \"10,000 requests per month\",\n    \"5GB traffic per month\",\n    \"100 requests per hour\",\n    \"2 Projects\",\n    \"3mb per request\",\n  ],\n};\n\nconst price_pro_monthly: Price = {\n  id: PRICE_PERSONAL_PRO_MONTHLY,\n  name: \"Pro - Monthly\",\n  price: \"$4\",\n  unit: \"Month\",\n  features: [\n    \"Up to 500,000 requests per month\",\n    \"500GB Bandwidth\",\n    \"Unlimited Projects\",\n    \"No hourly request limit\",\n    \"Max 6mb per request\",\n  ],\n};\n\nconst price_pro_yearly: Price = {\n  id: PRICE_PERSONAL_PRO_YEARLY,\n  name: \"Pro - Save 25% with Annual billing\",\n  price: \"$36\",\n  unit: \"Year\",\n  features: [\n    \"Up to 500,000 requests per month\",\n    \"500GB Bandwidth\",\n    \"Unlimited Projects\",\n    \"No hourly request limit\",\n    \"Max 6mb per request\",\n  ],\n};\n\nconst enterprise_yearly: Price = {\n  id: PRICE_ENTERPRISE_PRO_YEARLY,\n  name: \"Enterprise\",\n  price: \"$499\",\n  unit: \"Year\",\n  features: [\n    \"Up to 10,000,000 requests per month\",\n    \"1TB Bandwidth\",\n    \"Unlimited Projects\",\n    \"No hourly request limit\",\n    \"Max 6mb per request\",\n  ],\n};\n\nconst price_pay_as_you_go: Price = {\n  id: PRICE_PAY_AS_YOU_GO,\n  name: \"For Production projects\",\n  description: \"From $10 / month\",\n  price: \"Pay as you go\",\n  features: [\n    \"5,000,000 requests / month included\",\n    \"Unlimited Bandwidth\",\n    \"$1 per every next 100,000 requests\",\n    \"Unlimited Projects\",\n    \"No hourly request limit\",\n    \"Max 6mb per request\",\n  ],\n};\n\nexport const prices: Price[] = [\n  // price_free,\n  price_pro_monthly,\n  price_pro_yearly,\n  // enterprise_yearly,\n  // price_pay_as_you_go,\n];\n"
  },
  {
    "path": ".archives/homepage/layouts/payment-required-page.tsx",
    "content": "import React from \"react\";\nexport function PaymentRequiredPage() {\n  return <></>;\n}\n"
  },
  {
    "path": ".archives/homepage/layouts/pricing-card-list.tsx",
    "content": "import React, { useState } from \"react\";\nimport { PricingCard } from \"../components/pricing\";\nimport * as k from \"../k\";\nexport function PricingCardsList({\n  enableIndividualActions,\n  onGetStartedClick,\n  initialSelection,\n  onPriceChange,\n  disableSelection = false,\n}: {\n  disableSelection?: boolean;\n  initialSelection?: string;\n  enableIndividualActions: boolean;\n  onPriceChange?: (price: string) => void;\n  onGetStartedClick?: (id: string) => void;\n}) {\n  const [selected, setSelected] = useState(initialSelection);\n  const onSelected = (id: string) => {\n    if (disableSelection) return;\n    setSelected(id);\n    onPriceChange?.(id);\n  };\n\n  return (\n    <div\n      style={{\n        display: \"flex\",\n        flexDirection: \"row\",\n        justifyContent: \"center\",\n        gap: 40,\n      }}\n    >\n      {k.prices.map((price) => (\n        <PricingCard\n          onClick={() => onSelected(price.id)}\n          selected={selected === price.id}\n          key={price.id}\n          name={price.name}\n          description={price.description}\n          price={price.price}\n          startLabel=\"Get Started\"\n          unit={price.unit}\n          features={price.features}\n          enableAction={enableIndividualActions}\n          onStartClick={() => {\n            onGetStartedClick?.(price.id);\n          }}\n        />\n      ))}\n    </div>\n  );\n}\n"
  },
  {
    "path": ".archives/homepage/layouts/step-layout.tsx",
    "content": "import styled from \"@emotion/styled\";\nimport Head from \"next/head\";\nimport React, { useEffect } from \"react\";\nimport { Button } from \"../components/button\";\nimport { Logo } from \"../components/logo\";\n\ntype Props = {\n  title: string;\n  description: string | React.ReactNode;\n  nextPromptLabel: string;\n  disableEnterForNext?: boolean;\n  onNextClick: () => void;\n} & React.PropsWithChildren;\n\nexport function StepLayout({\n  title,\n  description,\n  nextPromptLabel,\n  onNextClick,\n  children,\n  disableEnterForNext,\n}: Props) {\n  const nextbtnref = React.useRef<HTMLButtonElement>(null);\n\n  useEffect(() => {\n    const listener = (e: KeyboardEvent) => {\n      if (e.key === \"Enter\" && !disableEnterForNext) {\n        onNextClick();\n      }\n    };\n    document.addEventListener(\"keydown\", listener);\n    return () => {\n      document.removeEventListener(\"keydown\", listener);\n    };\n  }, []);\n\n  return (\n    <Layout id=\"layout\">\n      <Head>\n        <title>{title}</title>\n        <meta name=\"description\" content={title} />\n      </Head>\n      <HeadingContaienr>\n        <Logo moveToHome />\n        <div style={{ height: 8 }} />\n        <Heading>{title}</Heading>\n        <Description>{description}</Description>\n      </HeadingContaienr>\n      <div style={{ height: 40 }} />\n      {children}\n      <div style={{ height: 40 }} />\n      <FooterSticky>\n        {!disableEnterForNext && (\n          <PressEnterToContinue>Press enter to continue</PressEnterToContinue>\n        )}\n        <Button ref={nextbtnref} onClick={onNextClick}>\n          {nextPromptLabel}\n        </Button>\n      </FooterSticky>\n    </Layout>\n  );\n}\n\nconst Layout = styled.div`\n  display: flex;\n  flex-direction: column;\n  min-height: 100vh;\n  padding: 80px;\n  outline: none;\n  border: none;\n`;\n\nconst Heading = styled.h1`\n  max-width: 370px;\n`;\n\nconst Description = styled.p`\n  color: rgba(0, 0, 0, 0.7);\n  text-overflow: ellipsis;\n  font-size: 14px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  text-align: left;\n  max-width: 600px;\n`;\n\nconst HeadingContaienr = styled.div`\n  display: flex;\n  justify-content: flex-start;\n  flex-direction: column;\n  align-items: flex-start;\n  gap: 16px;\n`;\n\nconst PressEnterToContinue = styled.span`\n  color: rgba(0, 0, 0, 0.8);\n  text-overflow: ellipsis;\n  font-size: 14px;\n  font-family: \"Helvetica Neue\", sans-serif;\n  font-weight: 400;\n  text-align: left;\n`;\n\nconst FooterSticky = styled.div`\n  display: flex;\n  justify-content: flex-end;\n  align-items: center;\n  gap: 24px;\n  flex-direction: row;\n`;\n"
  },
  {
    "path": ".archives/homepage/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/basic-features/typescript for more information.\n"
  },
  {
    "path": ".archives/homepage/next.config.js",
    "content": "const withLinaria = require(\"next-linaria\");\nconst withTM = require(\"next-transpile-modules\")([\n  \"@app/ui\",\n  \"@cors.sh/service-api\",\n  \"@editor-ui/console\",\n]);\n\nconst CONSOLE_URL = process.env.CONSOLE_URL;\n/**\n * @type {import('next').NextConfig}\n */\nconst config = {\n  rewrites() {\n    return [\n      {\n        source: \"/playground\",\n        destination: \"https://playground.cors.sh\",\n      },\n      {\n        source: \"/playground/:path*\",\n        destination: \"https://playground.cors.sh/:path*\",\n      },\n      {\n        source: \"/console\",\n        destination: CONSOLE_URL,\n      },\n      {\n        source: \"/console/:path*\",\n        destination: CONSOLE_URL + \"/:path*\",\n      },\n      {\n        source: \"/docs\",\n        destination: \"https://docs.cors.sh/intro\",\n      },\n      {\n        source: \"/docs/:path*\",\n        destination: \"https://docs.cors.sh/:path*\",\n      },\n    ];\n  },\n  redirects() {\n    return [\n      {\n        source: \"/http\\\\://:path*\",\n        destination: \"https://proxy.cors.sh/http\\\\://:path*\",\n        basePath: false,\n        permanent: true,\n      },\n      {\n        source: \"/https\\\\://:path*\",\n        destination: \"https://proxy.cors.sh/https\\\\://:path*\",\n        basePath: false,\n        permanent: true,\n      },\n      {\n        // if pyament is canceled, go back to get started page.\n        source: \"/payments/canceled\",\n        destination: \"/get-started\",\n        permanent: true,\n      },\n      // {\n      //   source: \"/console\",\n      //   destination: \"https://console.grida.co/cors-proxy\",\n      //   permanent: true,\n      // },\n      {\n        source: \"/docs\",\n        destination: \"/docs/intro\",\n        permanent: true,\n      },\n    ];\n  },\n};\nmodule.exports = withTM(withLinaria(config));\n"
  },
  {
    "path": ".archives/homepage/package.json",
    "content": "{\n    \"name\": \"homepage\",\n    \"version\": \"0.1.0\",\n    \"private\": true,\n    \"scripts\": {\n        \"dev\": \"next dev -p 8823\",\n        \"build\": \"next build\",\n        \"start\": \"next start -p 8823\"\n    },\n    \"dependencies\": {\n        \"@cors.sh/service-api\": \"0.0.0\",\n        \"@emotion/react\": \"^11.10.0\",\n        \"@emotion/styled\": \"^11.10.0\",\n        \"@mui/icons-material\": \"^5.10.3\",\n        \"@mui/material\": \"^5.10.3\",\n        \"@radix-ui/react-collapsible\": \"^1.0.1\",\n        \"@types/react-select\": \"^5.0.1\",\n        \"framer-motion\": \"^8.5.0\",\n        \"nanoid\": \"^4.0.0\",\n        \"next\": \"12.2.5\",\n        \"react\": \"18.2.0\",\n        \"react-dom\": \"18.2.0\",\n        \"react-select\": \"^5.4.0\",\n        \"react-syntax-highlighter\": \"^15.5.0\",\n        \"sass\": \"^1.54.8\"\n    },\n    \"devDependencies\": {\n        \"@types/node\": \"^18.7.13\",\n        \"@types/react\": \"18.0.17\",\n        \"@types/react-syntax-highlighter\": \"^15.5.5\",\n        \"grida\": \"^0.0.21\",\n        \"next-linaria\": \"^0.11.0\",\n        \"next-transpile-modules\": \"^9.0.0\",\n        \"typescript\": \"4.8.2\"\n    }\n}\n"
  },
  {
    "path": ".archives/homepage/pages/_app.tsx",
    "content": "import \"../styles/globals.css\";\nimport type { AppProps } from \"next/app\";\nimport Head from \"next/head\";\nimport Script from \"next/script\";\nimport { Toaster } from \"react-hot-toast\";\n\nexport default function App({ Component, pageProps }: AppProps) {\n  return (\n    <>\n      {/* <!-- Google tag (gtag.js) --> */}\n      <Script\n        strategy=\"afterInteractive\"\n        src=\"https://www.googletagmanager.com/gtag/js?id=G-XG051N1VS3\"\n      />\n      <Script id=\"google-analytics\" strategy=\"afterInteractive\">\n        {`\n          window.dataLayer = window.dataLayer || [];\n          function gtag(){dataLayer.push(arguments);}\n          gtag('js', new Date());\n          gtag('config', 'G-XG051N1VS3');\n        `}\n      </Script>\n      <Head>\n        <SeoMeta />\n        <meta charSet=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n        <meta httpEquiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n        <meta name=\"theme-color\" content=\"#000000\" />\n        <link rel=\"icon\" href=\"/favicon.ico\" />\n      </Head>\n\n      <Toaster position=\"bottom-center\" />\n      <Component {...pageProps} />\n    </>\n  );\n}\n\nfunction SeoMeta() {\n  return (\n    <>\n      <meta name=\"description\" content=\"One CORS Proxy you'll ever need\" />\n      <meta\n        name=\"keywords\"\n        content=\"cors, cors proxy, cors proxy server, free cors proxy, soap, cors playground, cors-anywhere, cors anywhere alternative\"\n      />\n      <meta property=\"og:image\" content=\"/og:image.jpg\" />\n    </>\n  );\n}\n"
  },
  {
    "path": ".archives/homepage/pages/_document.tsx",
    "content": "import { Head, Html, Main, NextScript } from \"next/document\";\n\nexport default function _Document() {\n  return (\n    <Html lang=\"en\">\n      <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n      <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossOrigin=\"*\" />\n      {/* roboto mono */}\n      <link\n        href=\"https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500;600;700&display=swap\"\n        rel=\"stylesheet\"\n      />\n      {/* inter */}\n      <link\n        href=\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap\"\n        rel=\"stylesheet\"\n      />\n      {/* Nanum Pen Script */}\n      <link\n        href=\"https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&display=swap\"\n        rel=\"stylesheet\"\n      ></link>\n      <Head />\n\n      <body>\n        <Main />\n        <NextScript />\n      </body>\n    </Html>\n  );\n}\n"
  },
  {
    "path": ".archives/homepage/pages/disclaimer.tsx",
    "content": "import React from \"react\";\n\nexport default function DisclaimerPage() {\n  return <>Disclaimer</>;\n}\n"
  },
  {
    "path": ".archives/homepage/pages/get-started/index.tsx",
    "content": "import React, { useCallback, useRef, useEffect } from \"react\";\nimport { useRouter } from \"next/router\";\nimport { FormPageLayout } from \"@app/ui/layouts\";\nimport { validateUrls } from \"@app/ui/utils\";\nimport {\n  Button,\n  FormFieldBase,\n  FormFieldLabel,\n  TextFormField,\n} from \"@editor-ui/console\";\nimport Select from \"react-select\";\nimport client from \"@cors.sh/service-api\";\nimport * as k from \"../../k\";\nimport { toast } from \"react-hot-toast\";\n\nexport default function GetstartedPage({ price: _price }: { price: string }) {\n  const [name, setName] = React.useState(\"\");\n  const [price, setPrice] = React.useState(_price);\n  const [allowedOrigins, setAllowedOrigins] = React.useState(\"\");\n  const [valid, setValid] = React.useState(false);\n  const [isBusy, setIsBusy] = React.useState(false);\n  const [isValid, setIsValid] = React.useState(false);\n  const stateRef = useRef<string>(_price);\n\n  stateRef.current = price;\n\n  const router = useRouter();\n\n  const pricing_options = Object.values(pricing);\n\n  const onPriceChange = (price: string) => {\n    setPrice(price);\n  };\n\n  const onTypeKey = (key: string) => {\n    // todo: validate key via api\n    setValid(key.length > 10);\n  };\n\n  const onEnter = () => {\n    // this is required because onEnter can also be invoked from input's callback\n    if (valid) {\n      onNextClick();\n    }\n  };\n\n  useEffect(() => {\n    setIsValid(name.length > 0 && validateUrls(allowedOrigins));\n  }, [name, allowedOrigins]);\n\n  const onNextClick = useCallback(async () => {\n    setIsBusy(true);\n\n    try {\n      // log begin_checkout event\n      const pricedata = pricing[price];\n\n      // @ts-ignore\n      window.gtag(\"event\", \"begin_checkout\", {\n        value: pricedata.value,\n        currency: pricedata.currency,\n        items: [\n          {\n            item_id: pricedata.id,\n            item_name: pricedata.label,\n          },\n        ],\n      });\n    } catch (e) {}\n\n    try {\n      // create onboarding application\n      const form = {\n        name: name ? name : undefined,\n        allowedOrigins: allowedOrigins\n          .split(\",\")\n          .map((x) => x.trim())\n          .filter(Boolean),\n        priceId: price,\n      };\n\n      const application = await client.onboardingWithForm(form);\n\n      const onboarding_id = application.id;\n\n      // redirect user to payment page\n      let redirect;\n      switch (stateRef.current) {\n        case k.PRICE_PAY_AS_YOU_GO: {\n          // TODO: add stripe integration.\n          redirect = \"https://forms.gle/GXDGPAoM9fhZrQh77\";\n          break;\n        }\n        case k.PRICE_FREE_MONTHLY: {\n          // the free plan does not require payments, so we can skip to create new project right away.\n          redirect =\n            window.location.protocol + window.location.host + \"/console/new\";\n          break;\n        }\n        default: {\n          let params = new URLSearchParams();\n          params.append(\"onboarding_id\", onboarding_id);\n          // TODO: multiple search params not supported by accounts.grida.co?redirect_uri=x\n\n          redirect = k.SERVER_URL + \"/payments/checkout/new\" + \"?\" + params;\n\n          break;\n        }\n      }\n\n      router.replace(redirect);\n    } catch (e) {\n      toast.error(\"Oops. something went wrong. please try again.\");\n      setIsBusy(false);\n    }\n  }, [name, allowedOrigins, price]);\n\n  return (\n    <FormPageLayout>\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          gap: 8,\n        }}\n      >\n        <h1>Get started</h1>\n        <p>\n          Ready to use cors.sh? select your plan and let’s create your first\n          project. <br />\n        </p>\n        <p style={{ opacity: 0.5 }}>you can update the fields later</p>\n      </div>\n      <div className=\"form\">\n        <TextFormField\n          label=\"Application name\"\n          placeholder=\"my-portfolio-website\"\n          onEnter={onEnter}\n          onChange={setName}\n        />\n        <TextFormField\n          label=\"Application origin URL\"\n          placeholder=\"http://localhost:3000, https://my-site.com\"\n          helpText=\"Add up to 3 urls of your site\"\n          onEnter={onEnter}\n          onChange={setAllowedOrigins}\n        />\n        {/* pricing plan select */}\n\n        <FormFieldBase style={{ width: \"100%\" }}>\n          <FormFieldLabel>Plan</FormFieldLabel>\n          <div style={{ flex: 1, alignSelf: \"stretch\" }}>\n            <Select\n              id=\"pricing-select\"\n              instanceId=\"pricing-select\"\n              onKeyDown={(e) => {\n                if (e.key === \"Enter\") {\n                  onEnter();\n                }\n              }}\n              //\n              onChange={(e) => {\n                e && onPriceChange(e.id);\n              }}\n              value={pricing[price]}\n              options={pricing_options}\n            />\n          </div>\n        </FormFieldBase>\n        <div style={{ height: 16 }} />\n        <Button\n          disabled={!isValid || isBusy}\n          onClick={onNextClick}\n          height={\"32px\"}\n        >\n          Continue\n        </Button>\n      </div>\n    </FormPageLayout>\n\n    // <StepLayout\n    //   title=\"Get started\"\n    //   description=\"Ready to use cors.sh? select your plan and let’s create your first project.\"\n    //   onNextClick={onNextClick}\n    //   nextPromptLabel=\"Sign in with Grida and continue\"\n    // >\n    //   <PricingCardsList\n    //     onPriceChange={onPriceChange}\n    //     initialSelection={price}\n    //   />\n    // </StepLayout>\n  );\n}\n\nconst pricing = {\n  [k.PRICE_PERSONAL_PRO_MONTHLY]: {\n    id: k.PRICE_PERSONAL_PRO_MONTHLY,\n    label: \"Pro - $4/Mo\",\n    value: 4,\n    currency: \"USD\",\n  },\n  [k.PRICE_PERSONAL_PRO_YEARLY]: {\n    id: k.PRICE_PERSONAL_PRO_YEARLY,\n    label: \"Pro - $3/Mo (Pay annualy, save $12)\",\n    value: 36,\n    currency: \"USD\",\n  },\n  [k.PRICE_ENTERPRISE_PRO_YEARLY]: {\n    id: k.PRICE_ENTERPRISE_PRO_YEARLY,\n    label: \"Enterprise - $499/Yr\",\n    value: 499,\n    currency: \"USD\",\n  },\n  // [k.PRICE_FREE_MONTHLY]: {\n  //   id: k.PRICE_FREE_MONTHLY,\n  //   label: \"Free\",\n  // },\n  // [k.PRICE_PAY_AS_YOU_GO]: {},\n} as const;\n\nexport async function getServerSideProps(context: any) {\n  const { price } = context.query;\n\n  const validate_price_id = (price_id: string | undefined): boolean => {\n    return price_id?.startsWith(\"price_\") || false;\n  };\n\n  const _price = validate_price_id(price)\n    ? price\n    : k.PRICE_PERSONAL_PRO_MONTHLY;\n\n  return {\n    props: {\n      price: _price,\n    },\n  };\n}\n"
  },
  {
    "path": ".archives/homepage/pages/index.tsx",
    "content": "import React from \"react\";\nimport { AppbarGroup } from \"../grida/AppbarGroup\";\nimport { SectionDisclaimer } from \"../grida/SectionDisclaimer\";\nimport { SectionHero } from \"../grida/SectionHero\";\nimport { SectionPricing } from \"../grida/SectionPricing\";\nimport { SectionUsage } from \"../grida/SectionUsage\";\nimport ChatwootWidget from \"../components/chatwood\";\nimport { FooterCtaSection } from \"../components/cta-footer\";\nimport Head from \"next/head\";\n\nexport default function HomePage() {\n  return (\n    <>\n      <Head>\n        <title>CORS.SH - A Fast & Reliable CORS Proxy for your websites</title>\n      </Head>\n      <AppbarGroup />\n      <SectionHero />\n      <SectionUsage />\n      <SectionPricing />\n      {/* <SectionCtaLast /> */}\n      <div style={{ height: 120 }} />\n      <div\n        style={{\n          height: 800,\n          position: \"relative\",\n          // black background that starts from 60% to 100% of the height\n          background:\n            \"linear-gradient(180deg, white 0%, white 50%, #000000 50%, #000000 100%)\",\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            top: 40,\n            left: 0,\n            right: 0,\n            maxWidth: 1040,\n            margin: \"120px auto\",\n          }}\n        >\n          <FooterCtaSection />\n        </div>\n      </div>\n      <div\n        style={{\n          background: \"black\",\n          padding: \"400px 0\",\n        }}\n      >\n        <ClientOnly>\n          <SectionDisclaimer />\n        </ClientOnly>\n      </div>\n      <ChatwootWidget />\n    </>\n  );\n}\n\nfunction ClientOnly({ children, ...delegated }: React.PropsWithChildren) {\n  const [hasMounted, setHasMounted] = React.useState(false);\n  React.useEffect(() => {\n    setHasMounted(true);\n  }, []);\n\n  if (!hasMounted) {\n    return null;\n  }\n\n  return <div {...delegated}>{children}</div>;\n}\n"
  },
  {
    "path": ".archives/homepage/pages/oauth/callback.tsx",
    "content": "import React from \"react\";\nimport Axios from \"axios\";\n\nconst client = Axios.create({\n  baseURL: \"https://accounts.grida.ngrok.io/oauth\",\n});\n\nexport default function OAuthCallbackPage(props: any) {\n  return (\n    <>\n      <h1>Signed in</h1>\n      <p>You may now close this page</p>\n      <pre>\n        <code>{JSON.stringify(props, null, 2)}</code>\n      </pre>\n    </>\n  );\n}\n\nexport async function getServerSideProps(context: any) {\n  const { code, state } = context.query;\n\n  try {\n    // TODO:\n    const { data } = await client.get(\"/access_token\", {\n      params: {\n        client_id: \"cors.sh\",\n        client_secret: process.env.GRIDA_OAUTH_CLIENT_SECRET,\n        code,\n      },\n    });\n    return {\n      props: {\n        ...data,\n      },\n    };\n  } catch (e) {\n    console.error(e);\n  }\n\n  return {\n    props: {},\n  };\n}\n"
  },
  {
    "path": ".archives/homepage/pages/onboarding/[id].tsx",
    "content": "// TODO: rename this route to /onboarding/continue?id=\n\nimport Head from \"next/head\";\nimport React from \"react\";\n\nexport default function ContinueOnboardingWithVerification() {\n  return (\n    <>\n      <Head>\n        <title>Redirecting..</title>\n      </Head>\n      <h1>Redirecting..</h1>\n    </>\n  );\n}\n\nexport async function getServerSideProps() {\n  //\n  // TODO: fully implement.\n  // for now, redirect to get-started\n  return {\n    redirect: {\n      destination: \"/get-started\",\n      permanent: false,\n    },\n  };\n}\n"
  },
  {
    "path": ".archives/homepage/pages/onboarding/complete.tsx",
    "content": "import React, { useEffect } from \"react\";\nimport styled from \"@emotion/styled\";\nimport { Client, ApplicationWithApiKey } from \"@cors.sh/service-api\";\nimport Head from \"next/head\";\nimport { FormPageLayout, PageCloseButton } from \"@app/ui/layouts\";\nimport { CollapsibleInfoCard } from \"components\";\nimport { UnderlineButton } from \"@app/ui/components\";\nimport { Prism as SyntaxHighlighter } from \"react-syntax-highlighter\";\nimport { examples } from \"k\";\nimport { ApiKeyReveal } from \"@app/ui/components\";\n\nexport default function InitialOnboardingFinalPage({\n  application,\n}: {\n  application: ApplicationWithApiKey;\n}) {\n  const demo_target_url =\n    application.allowedOrigins[0] ?? \"https://example.com\";\n\n  return (\n    <>\n      <Head>\n        <title>CORS.SH - Complete</title>\n      </Head>\n      <FormPageLayout>\n        <PageCloseButton />\n        <>\n          <h1>\n            Extend your api call with <u>proxy.cors.sh</u>\n          </h1>\n          <p className=\"description\">\n            <small\n              style={{\n                opacity: 0.5,\n              }}\n            >\n              We've sent you an email with the api key.\n              <br />\n              Please check your inbox :)\n            </small>\n            <br />\n            <br />\n            Let’s get rid of the cors errors with proxy.cors.sh like below.\n          </p>\n          <div style={{ height: 16 }} />\n          <div className=\"body\">\n            {/* <VideoDemo /> */}\n\n            <CodeExamples\n              apikey={application.apikey_test}\n              target={demo_target_url}\n            />\n            <ApiKeyReveal\n              keys={{\n                test: application.apikey_test,\n                prod: application.apikey_live,\n              }}\n            />\n            <div\n              style={{\n                display: \"flex\",\n                flexDirection: \"column\",\n                gap: 8,\n              }}\n            >\n              <CollapsibleInfoCard title=\"View more examples\">\n                <MoreCodeExamples\n                  apikey={application.apikey_test}\n                  target={demo_target_url}\n                />\n              </CollapsibleInfoCard>\n              {/* <CollapsibleInfoCard title=\"What's Next?\">\n                <h5>Useful resources</h5>\n                <ul>\n                  <li>\n                    <a href=\"https://cors.sh/docs\">\n                      <u>Learn how to secure your api key</u>\n                    </a>\n                  </li>\n                  <li>\n                    <a href=\"https://cors.sh/docs\">\n                      <u>Before publishing your website to production</u>\n                    </a>\n                  </li>\n                  <li>\n                    <a href=\"https://cors.sh/docs\">\n                      <u>Create new application on console</u>\n                    </a>\n                  </li>\n                </ul>\n              </CollapsibleInfoCard> */}\n            </div>\n          </div>\n          <div style={{ height: 30 }} />\n          <div\n            style={{\n              display: \"flex\",\n              flexDirection: \"column\",\n              gap: 8,\n            }}\n          >\n            <i style={{ opacity: 0.5 }}>Thank you for using cors.sh 🙏</i>\n\n            {/* <UnderlineButton>Move to dashboard</UnderlineButton>\n            <UnderlineButton>I need help</UnderlineButton> */}\n          </div>\n        </>\n      </FormPageLayout>\n    </>\n  );\n}\n\nfunction CodeExamples({ target, apikey }: { target: string; apikey: string }) {\n  return (\n    <CodeBlock language=\"js\">{examples.simplest(target, apikey)}</CodeBlock>\n  );\n}\n\nfunction MoreCodeExamples({\n  target,\n  apikey,\n}: {\n  target: string;\n  apikey: string;\n}) {\n  return <CodeBlock language=\"js\">{examples.axios(target, apikey)}</CodeBlock>;\n}\n\nconst CodeBlock = styled(SyntaxHighlighter)`\n  max-height: 240px;\n  font-size: 12px !important;\n`;\n\n// <CodeBlock>\n//   <pre>\n//     GET https://proxy.corsh.sh/https://instragram.com/posts/123\n//     <br />\n//     -h x-cors-api-key {apikey}\n//   </pre>\n// </CodeBlock>\n// const CodeBlock = styled.code`\n//   background: black;\n//   color: white;\n//   border-radius: 4px;\n//   padding: 20px;\n//   display: block;\n//   font-size: 12px;\n//   line-height: 1.5;\n//   font-family: monospace;\n//   overflow: scroll;\n\n//   pre {\n//     margin: 0;\n//   }\n// `;\n\nfunction VideoDemo() {\n  return (\n    <div className=\"video-demo\">\n      <video\n        style={{\n          borderRadius: \"4px\",\n          overflow: \"hidden\",\n        }}\n        autoPlay\n        loop\n        muted\n        playsInline\n        width=\"100%\"\n        height=\"100%\"\n      >\n        <source\n          src=\"/console/demo-of-initial-api-key-configuration.mp4\"\n          type=\"video/mp4\"\n        />\n      </video>\n    </div>\n  );\n}\n\nexport async function getServerSideProps(context: any) {\n  const { app, checkout_session_id } = context.query;\n\n  if (!app) {\n    return {\n      redirect: {\n        destination: \"/console\",\n        permanent: false,\n      },\n    };\n  }\n\n  try {\n    const client = new Client({\n      \"x-cors-service-checkout-session-id\": checkout_session_id,\n    });\n\n    const application = await client.getApplication(app);\n\n    return {\n      props: {\n        application,\n      },\n    };\n  } catch (e) {\n    console.error(e);\n    // 404\n    return {\n      notFound: true,\n    };\n  }\n}\n"
  },
  {
    "path": ".archives/homepage/pages/onboarding/index.tsx",
    "content": "import React, { useEffect } from \"react\";\nimport {\n  Button,\n  FormFieldBase,\n  FormFieldLabel,\n  TextFormField,\n} from \"@editor-ui/console\";\nimport Select from \"react-select\";\nimport client from \"@cors.sh/service-api\";\nimport { useRouter } from \"next/router\";\nimport { Cross2Icon } from \"@radix-ui/react-icons\";\nimport Head from \"next/head\";\nimport { FormPageLayout } from \"@app/ui/layouts\";\nimport { validateUrls } from \"@app/ui/utils\";\nimport { motion } from \"framer-motion\";\n\nexport default function NewApplicationPage() {\n  const router = useRouter();\n  const [step, setStep] = React.useState<\"signin\" | \"setup\">(\"signin\");\n\n  function Body() {\n    switch (step) {\n      case \"signin\":\n        return (\n          <SigninForm\n            onComplete={() => {\n              setStep(\"setup\");\n            }}\n          />\n        );\n      case \"setup\":\n        return <SetupForm />;\n    }\n  }\n\n  return (\n    <>\n      <Head>\n        <title>CORS.SH - First App</title>\n      </Head>\n      <FormPageLayout>\n        <button\n          className=\"close\"\n          onClick={() => {\n            router.replace(\"/\");\n          }}\n        >\n          <Cross2Icon />\n        </button>\n        <Body />\n      </FormPageLayout>\n    </>\n  );\n}\n\nfunction SigninForm({ onComplete }: { onComplete: () => void }) {\n  const [tmpkey, setTmpkey] = React.useState(\"\");\n  const [valid, setValid] = React.useState(false);\n\n  const onTypeKey = (key: string) => {\n    // todo: validate key via api\n    setValid(key.length > 10);\n  };\n\n  const onEnter = () => {\n    // this is required because onEnter can also be invoked from input's callback\n    if (valid) {\n      onComplete();\n    }\n  };\n\n  return (\n    <>\n      <h1>Enter your API key from your inbox to get started</h1>\n      <div className=\"form\">\n        <TextFormField\n          label=\"API Key\"\n          placeholder=\"text_xxxx-xxxx-xxxx\"\n          onEnter={onEnter}\n          onChange={onTypeKey}\n        />\n      </div>\n      <div style={{ height: 16 }} />\n      <Button disabled={!valid} onClick={onEnter} height={\"32px\"}>\n        Continue\n      </Button>\n    </>\n  );\n}\n\nfunction SetupForm() {\n  const router = useRouter();\n  const [name, setName] = React.useState(\"\");\n  const [allowedOrigins, setAllowedOrigins] = React.useState(\"\");\n  const [isBusy, setIsBusy] = React.useState(false);\n  const [isValid, setIsValid] = React.useState(false);\n  const [isPricingVisible, setIsPricingVisible] = React.useState(false);\n\n  const onEnter = () => {\n    if (!isValid) {\n      return;\n    }\n    setIsBusy(true);\n    client\n      .createApplication({\n        name: name,\n        allowedOrigins: allowedOrigins\n          .split(\",\")\n          .map((origin) => origin.trim()),\n      })\n      .then((r) => {\n        router.push({\n          pathname: \"[id]\",\n          query: { id: r.id },\n        });\n      })\n      .finally(() => {\n        setIsBusy(false);\n      });\n  };\n\n  useEffect(() => {\n    setIsValid(name.length > 0 && validateUrls(allowedOrigins));\n  }, [name, allowedOrigins]);\n\n  useEffect(() => {\n    if (isValid) {\n      setTimeout(() => {\n        setIsPricingVisible(true);\n      }, 1800);\n    }\n  }, [isValid]);\n\n  return (\n    <>\n      <h1>Now, Let's configure your first application</h1>\n      <div className=\"form\">\n        <TextFormField\n          label=\"Application name\"\n          placeholder=\"my-portfolio-website\"\n          onEnter={onEnter}\n          onChange={setName}\n        />\n        <TextFormField\n          label=\"Application origin URL\"\n          placeholder=\"http://localhost:3000, https://my-site.com\"\n          helpText=\"You can add up to 3 urls of your site\"\n          onEnter={onEnter}\n          onChange={setAllowedOrigins}\n        />\n        {/* pricing plan select */}\n        <motion.div\n          initial={{ opacity: 0, y: 16 }}\n          animate={\n            isPricingVisible\n              ? {\n                  opacity: 1,\n                  y: 0,\n                }\n              : {}\n          }\n          transition={{ duration: 0.3, ease: \"easeOut\" }}\n        >\n          <FormFieldBase style={{ width: \"100%\" }}>\n            <FormFieldLabel>Plan</FormFieldLabel>\n            <div style={{ flex: 1, alignSelf: \"stretch\" }}>\n              <Select\n                onKeyDown={(e) => {\n                  if (e.key === \"Enter\") {\n                    onEnter();\n                  }\n                }}\n                options={[\n                  { value: \"pro-monthly\", label: \"Pro - $4/Mo\" },\n                  {\n                    value: \"pro-yearly\",\n                    label: \"Pro - $3/Mo (Pay annualy, save $12)\",\n                  },\n                ]}\n                defaultValue={{ value: \"pro\", label: \"Pro - $4/Mo\" }}\n              />\n            </div>\n          </FormFieldBase>\n        </motion.div>\n        <div style={{ height: 16 }} />\n        <Button disabled={!isValid || isBusy} onClick={onEnter} height={\"32px\"}>\n          Continue\n        </Button>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": ".archives/homepage/pages/onboarding/payment-success-with-issue.tsx",
    "content": "import React from \"react\";\nimport client from \"@cors.sh/service-api\";\nimport { Button } from \"@editor-ui/console\";\nimport Head from \"next/head\";\n\nexport default function PaymentSuccessButThereWasAProblem({\n  error,\n  message,\n  session,\n  application,\n}: {\n  error: \"identity_conflict\" | string;\n  message: string;\n  session: string;\n  application: {\n    id: string;\n    name: string;\n  };\n}) {\n  return (\n    <>\n      <Head>\n        <title>CORS.SH - Problem with your subscription</title>\n      </Head>\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          height: \"100vh\",\n          padding: 80,\n          textAlign: \"center\",\n        }}\n      >\n        <h1>There was a problem with your subscription - \"{error}\"</h1>\n\n        <div style={{ height: 40 }} />\n        <p>{message}</p>\n\n        <h5>Your Information</h5>\n        <p>Copy the data below when you contact customer support</p>\n        <div style={{ height: 40 }} />\n        <code>\n          <pre>session: {session}</pre>\n          <pre>\n            application: {application.id} ({application.name})\n          </pre>\n        </code>\n\n        <div style={{ height: 40 }} />\n        <a href={`mailto:hello@grida.co`}>\n          <Button>Contact Support</Button>\n        </a>\n      </div>\n    </>\n  );\n}\n\nexport async function getServerSideProps(context: any) {\n  const { error, message, session_id, application_id, onboarding_id } =\n    context.query;\n  if (!session_id || !application_id || !onboarding_id) {\n    // invalid entry\n    return {\n      redirect: {\n        destination: \"/\",\n        permanent: false,\n      },\n    };\n  }\n\n  try {\n    const application = await client.getOnboardingApplication(onboarding_id);\n    return {\n      props: {\n        error,\n        message,\n        session: session_id || null,\n        application,\n      },\n    };\n  } catch (e) {\n    // 404\n    return {\n      notFound: true,\n    };\n  }\n}\n"
  },
  {
    "path": ".archives/homepage/pages/onboarding/payment-success.tsx",
    "content": "import React, { useEffect } from \"react\";\nimport client from \"@cors.sh/service-api\";\nimport { useRouter } from \"next/router\";\nimport Head from \"next/head\";\nimport { Button, TextFormField } from \"@editor-ui/console\";\nimport { FormPageLayout, PageCloseButton } from \"@app/ui/layouts\";\nimport { toast } from \"react-hot-toast\";\n\n// page redirected from stripe once the payment is successful\nexport default function PaymentSuccessPage({\n  application,\n  session,\n  isOnboarding,\n}: {\n  session: string;\n  isOnboarding: boolean;\n  application: {\n    id: string;\n    name: string;\n    allowedOrigins: string[];\n  };\n}) {\n  const [isBusy, setBusy] = React.useState(false);\n  const router = useRouter();\n\n  useEffect(() => {\n    // GA4 conversion - Purchase\n    // @ts-ignore\n    window.gtag(\"event\", \"purchase\", {\n      transaction_id: session,\n      value: 4,\n      currency: \"USD\",\n      //\n    });\n  }, []);\n\n  const onNext = () => {\n    setBusy(true);\n    // convert to application.\n    client\n      .convertApplication(application.id, session)\n      .then((d) => {\n        // move to complete\n        router.push({\n          pathname: \"/onboarding/complete\",\n          query: {\n            app: d.id,\n            checkout_session_id: session,\n          },\n        });\n      })\n      .catch((e) => {\n        toast.error(\"Something went wrong. Please try again later.\");\n      })\n      .finally(() => {\n        setBusy(false);\n      });\n  };\n\n  return (\n    <>\n      <Head>\n        <title>CORS.SH - Complete</title>\n      </Head>\n      <FormPageLayout>\n        <PageCloseButton />\n        <>\n          <h1>Thank you for your subscription</h1>\n          <p className=\"description\">\n            You can now create as many project you want without unlimited hourly\n            rate :)\n            <br />\n            <br />\n            <b>Let’s finish up your first project.</b>\n          </p>\n          <div className=\"form\">\n            <TextFormField\n              label=\"Application name\"\n              placeholder=\"my-portfolio-website\"\n              value={application.name}\n              readonly\n            />\n            <TextFormField\n              label=\"Application origin URL\"\n              placeholder=\"http://localhost:3000, https://my-site.com\"\n              readonly\n              value={application.allowedOrigins.join(\", \")}\n              helpText=\"You can add up to 3 urls of your site\"\n            />\n            <div style={{ height: 16 }} />\n            <Button onClick={onNext} height={\"32px\"} disabled={isBusy}>\n              Continue\n            </Button>\n          </div>\n        </>\n      </FormPageLayout>\n    </>\n  );\n}\n\nexport async function getServerSideProps(context: any) {\n  const { session_id, application_id, customer_id, onboarding_id } =\n    context.query;\n  if (!session_id) {\n    // invalid entry\n    return {\n      redirect: {\n        destination: \"/\",\n        permanent: false,\n      },\n    };\n  }\n\n  if (!onboarding_id) {\n    return {\n      redirect: {\n        destination: \"/console\",\n        permanent: false,\n      },\n    };\n  }\n\n  try {\n    const application = await client.getOnboardingApplication(onboarding_id);\n    return {\n      props: {\n        session: session_id || null,\n        application,\n        customer_id,\n      },\n    };\n  } catch (e) {\n    // 404\n    return {\n      notFound: true,\n    };\n  }\n}\n"
  },
  {
    "path": ".archives/homepage/pages/too-many-requests.tsx",
    "content": "import React from \"react\";\nimport { PricingCardsList } from \"../layouts/pricing-card-list\";\nimport { StepLayout } from \"../layouts/step-layout\";\nimport * as k from \"../k\";\nexport default function TooMayRequestsPage() {\n  return (\n    <div>\n      <StepLayout\n        title=\"Oops. Too many requests.\"\n        description=\"We’re sorry to tell you that you’ve reached your hourly request limit for free-tier.\nUpgrade your plan to remove this limit.\"\n        onNextClick={() => {}}\n        nextPromptLabel=\"Sign in with Grida and continue\"\n      >\n        <PricingCardsList\n          enableIndividualActions={false}\n          // fallback to personal plan\n          initialSelection={k.PRICE_PERSONAL_PRO_MONTHLY}\n          onGetStartedClick={() => {}}\n        />\n      </StepLayout>\n    </div>\n  );\n}\n"
  },
  {
    "path": ".archives/homepage/public/robots.txt",
    "content": "# Block all crawlers for /console\nUser-agent: *\nDisallow: /console\n\n# Allow all crawlers\nUser-agent: *\nAllow: /"
  },
  {
    "path": ".archives/homepage/styles/globals.css",
    "content": "html,\nbody {\n  padding: 0;\n  margin: 0;\n  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,\n    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\np {\n  margin: 0;\n}\n\na {\n  color: inherit;\n  text-decoration: none;\n}\n\n* {\n  box-sizing: border-box;\n}\n"
  },
  {
    "path": ".archives/homepage/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"baseUrl\": \".\"\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/apply-for-oss-program.yml",
    "content": "name: Apply for OSS Program\ndescription: Apply your OSS Project for free pro-plan of cors.sh\ntitle: \"[Application] for my OSS Project\"\nlabels: [application]\nassignees: [softmarshmallow]\nbody:\n- type: checkboxes\n  attributes:\n    label: You are applying for validated OSS program\n    description: Please ensure your project is open to public.\n    options:\n    - label: My project is OSS licensed and open to public. (Must have LICENSE file)\n      required: true\n    - label: My project has more than **10 ⭐️stars** on Github, which is the required for this program.\n      required: true\n- type: textarea\n  attributes:\n    label: Repository URL to your project\n    description: Copy & paste url to your project repo\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: URL to your website\n    description: Please let us know the url to your website if you have one.\n  validations:\n    required: false\n- type: textarea\n  attributes:\n    label: Expected Quota of usage\n    description: How much API calls do you expect to occur via your site?\n  validations:\n    required: true\n- type: textarea\n  attributes:\n    label: Details\n    description: |\n      examples:\n        - **Category**: Blockchain\n        - **License**: MIT\n        - **Date of release**: 12/25/2023\n    value: |\n        - Category:\n        - License:\n        - Date of release:\n    render: markdown\n  validations:\n    required: false\n- type: textarea\n  attributes:\n    label: Anything else?\n    description: |\n      Links? References? Anything that will give us more context about the project!\n\n      Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.\n  validations:\n    required: false\n- type: markdown\n  attributes:\n    value: |\n      Before you continue..\n      \n      - I made my repo open to public\n      - My repo clearly states the license\n      - I will **star** cors.sh (this repo)\n      - I will follow Grida on Github\n    \n    \n  \n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\npackage-lock.json\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n\n# Next.js build output\n.next\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and *not* Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# .DS_Store\n.DS_Store"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"packages/editor-ui\"]\n\tpath = packages/editor-ui\n\turl = https://github.com/reflect-ui/reflect-editor-ui\n"
  },
  {
    "path": ".nvmrc",
    "content": "v20.9.0"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"files.exclude\": {\n    \"**/.git\": true,\n    \"**/.svn\": true,\n    \"**/.hg\": true,\n    \"**/CVS\": true,\n    \"**/.DS_Store\": true,\n    \"**/Thumbs.db\": true,\n    \"**/node_modules\": true\n  },\n  \"search.exclude\": {\n    \"**/node_modules\": true,\n    \"**/.build\": true,\n    \"**/.lock\": true,\n    \"**/*.code-search\": true\n  }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Grida\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# cors.sh\n\n![cors.sh](./branding/artwork_cors.sh.jpg)\n\nThe only cors proxy service all you'll ever need.\n\n| [Website](https://cors.sh) | [Proxy (proxy.cors.sh)](https://proxy.cors.sh) | [Playground](https://cors.sh/playground) | [Docs](https://cors.sh/docs) |\n\n## Usage\n\n**Quick testing**\nUse [cors.sh/playground](https://cors.sh/playground) for testing out your cors blocked request.\n\n**JS**\n\nAdd `proxy.cors.sh` to your request. For example,\n\n```js\nfetch(\"https://proxy.cors.sh/https://example.com/\");\n```\n\n## [`cors.sh/playground`](https://cors.sh/playground), The testing environment (forked from hoppscotch)\n\n- [cors.sh/playground](https://cors.sh/playground)\n- [gridaco/playground.cors.sh](https://github.com/gridaco/playground.cors.sh)\n- [hoppscotch/hoppscotch](https://github.com/hoppscotch/hoppscotch)\n\n\n## Projects using CORS.SH\n- https://github.com/IMGROOT2/algo\n- https://github.com/alejandroch1202/dollar-monitor\n- https://github.com/Iconem/search-satellite-imagery/\n- https://github.com/PavelLaptev/JSON-to-Figma\n- https://github.com/TomRadford/shootdrop\n\n\n\n## Contributing\n\n```bash\n# clone initially\ngit clone --recurse-submodules https://github.com/gridaco/cors.sh\n# updating submodules (once required)\ngit submodule update --init --recursive\n```\n\n## Disclaimer\n\n1. This project's intend is to serve developers a reliable cors proxy service with fast response for their development.\n   Using a cors proxy service to connect to your own server is not a best practice.\n   We'll consistently optimize our service infra to keep the paid version affordable as possible.\n\n2. The original code behind cors proxy is by Rob wu's cors-anywhere and the playground is forked from hoppscotch. both licensed under MIT, and our project cors.sh is also licensed under MIT License.\n\n\n## TODOs\n\n- Cost optimization - make it more cheap & provide free version to all.\n- Management console - Enable usesrs to create projects as much as they want.\n- OSS Application pipeline - Make OSS developers to get their api key right-on and get verified later.\n"
  },
  {
    "path": "branding/readme.md",
    "content": "# branding resources accross the site\n\n- favicon.ico - for `/`, `/docs`, `/playground`\n"
  },
  {
    "path": "cli/bin.ts",
    "content": "import yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { checkForUpdate } from \"./update\";\n\nexport default async function cli() {\n  await checkForUpdate();\n  yargs(hideBin(process.argv))\n    .option(\"cwd\", {\n      type: \"string\",\n      default: process.cwd(),\n      requiresArg: false,\n    })\n    .option(\"dry-run\", {\n      type: \"boolean\",\n      default: false,\n      requiresArg: false,\n    })\n    .global([\"cwd\", \"dry-run\"])\n    .command(\n      \"init\",\n      \"init grida project\",\n      () => {},\n      ({ cwd }) => {\n        // init(cwd);\n      }\n    )\n    .demandCommand(1)\n    .parse();\n}\n"
  },
  {
    "path": "cli/index.ts",
    "content": "#!/usr/bin/env node\n\nimport cli from \"./bin\";\n\nprocess.on(\"SIGINT\", () => {\n  process.exit(0); // now the \"exit\" event will fire\n});\n\n// if main\nif (require.main === module) {\n  cli();\n}\n"
  },
  {
    "path": "cli/jes.config.js",
    "content": "/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */\nmodule.exports = {\n  preset: \"ts-jest\",\n  testEnvironment: \"node\",\n};\n"
  },
  {
    "path": "cli/package.json",
    "content": "{\n  \"name\": \"@cors.sh/cli\",\n  \"version\": \"0.0.1\",\n  \"private\": false,\n  \"license\": \"Apache-2.0\",\n  \"description\": \"cors.sh cli\",\n  \"homepage\": \"https://cors.sh\",\n  \"repository\": \"https://github.com/gridaco/cors.sh\",\n  \"dependencies\": {\n    \"@base-sdk-fp/auth\": \"^0.1.4\",\n    \"boxen\": \"^7.0.0\",\n    \"dotenv\": \"^16.0.1\",\n    \"enquirer\": \"^2.3.6\",\n    \"keytar\": \"^7.9.0\",\n    \"node-fetch\": \"^3.2.10\",\n    \"node-machine-id\": \"^1.1.12\",\n    \"open\": \"^8.4.0\",\n    \"ora\": \"^5.4.0\",\n    \"yargs\": \"^17.2.1\"\n  },\n  \"scripts\": {\n    \"clean\": \"rimraf dist\",\n    \"dev\": \"ts-node index.ts\",\n    \"dev:watch\": \"ts-node-dev index.ts --watch\",\n    \"test\": \"jest\",\n    \"build\": \"ncc build index.ts -o dist -e keytar -e glob -e dotenv\",\n    \"prepack\": \"yarn test && yarn clean && yarn build\"\n  },\n  \"devDependencies\": {\n    \"@types/glob\": \"^7.2.0\",\n    \"@types/node\": \"^18.6.1\",\n    \"@types/semver\": \"^7.3.10\",\n    \"@types/which\": \"^2.0.1\",\n    \"@types/yargs\": \"^17.0.3\",\n    \"@vercel/ncc\": \"^0.34.0\",\n    \"jest\": \"^28.1.3\",\n    \"ts-jest\": \"^28.0.7\",\n    \"ts-node\": \"^10.9.1\",\n    \"ts-node-dev\": \"^2.0.0\",\n    \"typescript\": \"^4.7.4\"\n  },\n  \"bin\": {\n    \"cors\": \"./dist/index.js\"\n  },\n  \"files\": [\n    \"dist\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"publishConfig\": {\n    \"access\": \"public\"\n  }\n}"
  },
  {
    "path": "cli/readme.md",
    "content": "# cors.sh management cli\n\n- cors list project\n- cors new project\n- cors edit project `<id>`\n"
  },
  {
    "path": "cli/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2015\",\n    \"outDir\": \"dist\",\n    \"module\": \"CommonJS\",\n    \"strict\": false,\n    \"resolveJsonModule\": true,\n    \"esModuleInterop\": true,\n    \"moduleResolution\": \"node\"\n  },\n  \"exclude\": [\"dist\", \"node_modules\", \"**/*.spec.ts\", \"__test__\"]\n}\n"
  },
  {
    "path": "cli/update/index.ts",
    "content": "import { version, name } from \"../version\";\nimport boxen from \"boxen\";\nimport semver from \"semver\";\nimport fetch from \"node-fetch\";\nimport chalk from \"chalk\";\n\n/**\n *\n */\nexport async function checkForUpdate() {\n  // fetch latest version from npm\n  const { version: latestVersion } = (await (\n    await fetch(`https://registry.npmjs.org/${name}/latest`)\n  ).json()) as { version: string };\n\n  // compare versions\n  if (semver.gt(latestVersion, version)) {\n    // show update message\n    console.log(makeUpdateMessage({ latest: latestVersion }));\n  }\n}\n\nfunction makeUpdateMessage({ latest }: { latest: string }): string {\n  return boxen(\n    `${name} ${chalk.green(`v${latest}`)} is now available!\nRun \\`${chalk.green(`npm i -g ${name}`)}\\` to update.`,\n    {\n      padding: 1,\n      margin: 1,\n      borderStyle: \"classic\",\n      textAlignment: \"center\",\n    }\n  );\n}\n"
  },
  {
    "path": "cli/version/index.ts",
    "content": "import { version, name } from \"../package.json\";\n\nexport { version, name };\n"
  },
  {
    "path": "design/readme.md",
    "content": "# cors.sh design\n\n## Figma file\n\n- https://www.figma.com/file/aPfdtNb1aGFIN9p05cmmVY/cors.sh\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "# Dependencies\n/node_modules\n\n# Production\n/build\n\n# Generated files\n.docusaurus\n.cache-loader\n\n# Misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n"
  },
  {
    "path": "docs/README.md",
    "content": "# Website\n\nThis website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.\n\n### Installation\n\n```\n$ yarn\n```\n\n### Local Development\n\n```\n$ yarn start\n```\n\nThis command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.\n\n### Build\n\n```\n$ yarn build\n```\n\nThis command generates static content into the `build` directory and can be served using any static contents hosting service.\n\n### Deployment\n\nUsing SSH:\n\n```\n$ USE_SSH=true yarn deploy\n```\n\nNot using SSH:\n\n```\n$ GIT_USER=<Your GitHub username> yarn deploy\n```\n\nIf you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.\n"
  },
  {
    "path": "docs/babel.config.js",
    "content": "module.exports = {\n  presets: [require.resolve('@docusaurus/core/lib/babel/preset')],\n};\n"
  },
  {
    "path": "docs/blog/2019-05-28-first-blog-post.md",
    "content": "---\nslug: first-blog-post\ntitle: First Blog Post\nauthors:\n  name: Gao Wei\n  title: Docusaurus Core Team\n  url: https://github.com/wgao19\n  image_url: https://github.com/wgao19.png\ntags: [hola, docusaurus]\n---\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n"
  },
  {
    "path": "docs/blog/2019-05-29-long-blog-post.md",
    "content": "---\nslug: long-blog-post\ntitle: Long Blog Post\nauthors: endi\ntags: [hello, docusaurus]\n---\n\nThis is the summary of a very long blog post,\n\nUse a `<!--` `truncate` `-->` comment to limit blog post size in the list view.\n\n<!--truncate-->\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet\n"
  },
  {
    "path": "docs/blog/2021-08-01-mdx-blog-post.mdx",
    "content": "---\nslug: mdx-blog-post\ntitle: MDX Blog Post\nauthors: [slorber]\ntags: [docusaurus]\n---\n\nBlog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).\n\n:::tip\n\nUse the power of React to create interactive blog posts.\n\n```js\n<button onClick={() => alert('button clicked!')}>Click me!</button>\n```\n\n<button onClick={() => alert('button clicked!')}>Click me!</button>\n\n:::\n"
  },
  {
    "path": "docs/blog/2021-08-26-welcome/index.md",
    "content": "---\nslug: welcome\ntitle: Welcome\nauthors: [slorber, yangshun]\ntags: [facebook, hello, docusaurus]\n---\n\n[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).\n\nSimply add Markdown files (or folders) to the `blog` directory.\n\nRegular blog authors can be added to `authors.yml`.\n\nThe blog post date can be extracted from filenames, such as:\n\n- `2019-05-30-welcome.md`\n- `2019-05-30-welcome/index.md`\n\nA blog post folder can be convenient to co-locate blog post images:\n\n![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg)\n\nThe blog supports tags as well!\n\n**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.\n"
  },
  {
    "path": "docs/blog/authors.yml",
    "content": "endi:\n  name: Endilie Yacop Sucipto\n  title: Maintainer of Docusaurus\n  url: https://github.com/endiliey\n  image_url: https://github.com/endiliey.png\n\nyangshun:\n  name: Yangshun Tay\n  title: Front End Engineer @ Facebook\n  url: https://github.com/yangshun\n  image_url: https://github.com/yangshun.png\n\nslorber:\n  name: Sébastien Lorber\n  title: Docusaurus maintainer\n  url: https://sebastienlorber.com\n  image_url: https://github.com/slorber.png\n"
  },
  {
    "path": "docs/docs/guides/_category_.json",
    "content": "{\n  \"label\": \"Guides\",\n  \"position\": 4,\n  \"link\": {\n    \"type\": \"generated-index\",\n    \"description\": \"Popular use cases of cors proxy\"\n  }\n}"
  },
  {
    "path": "docs/docs/guides/api-instagram/index.md",
    "content": "---\nsidebar_position: 9\n---\n\n# For instagram api\n"
  },
  {
    "path": "docs/docs/guides/api-tiktok/index.md",
    "content": "---\nsidebar_position: 9\n---\n\n# For tiktok api\n"
  },
  {
    "path": "docs/docs/guides/figma-plugin/index.md",
    "content": "---\ntitle: For Figma plugin\nsidebar_position: 1\n---\n\n# CORS Proxy for developing your figma plugin\n\n## Examples\n\nFigma Assistant by Grida\n\nWe've initially made cors.sh for use of our own. The assistant uses cors.sh for requesting to our server.\n"
  },
  {
    "path": "docs/docs/guides/shopify-app/index.md",
    "content": "---\ntitle: For Shopify app\nsidebar_position: 2\n---\n\n# CORS Proxy for developing your shopify app\n\nShopify app\n"
  },
  {
    "path": "docs/docs/intro.md",
    "content": "---\nsidebar_position: 1\n---\n\n# Tutorial Intro\n\nLet's discover **Docusaurus in less than 5 minutes**.\n\n## Getting Started\n\nGet started by **creating a new site**.\n\nOr **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**.\n\n### What you'll need\n\n- [Node.js](https://nodejs.org/en/download/) version 16.14 or above:\n  - When installing Node.js, you are recommended to check all checkboxes related to dependencies.\n\n## Generate a new site\n\nGenerate a new Docusaurus site using the **classic template**.\n\nThe classic template will automatically be added to your project after you run the command:\n\n```bash\nnpm init docusaurus@latest my-website classic\n```\n\nYou can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor.\n\nThe command also installs all necessary dependencies you need to run Docusaurus.\n\n## Start your site\n\nRun the development server:\n\n```bash\ncd my-website\nnpm run start\n```\n\nThe `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there.\n\nThe `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/.\n\nOpen `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes.\n"
  },
  {
    "path": "docs/docs/migrate-from-cors.bridged.cc.md",
    "content": "# Migrate from cors.bridged.cc\n\nIf you are a `cors.bridged.cc` user, you will have api key in shape like this `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`\n\nYou can use the same api key for `cors.sh`, but this will no longer work from October 30, 2022.\n\nTo migrate, create new app from cors.sh console and replace the api key from your web app project.\n\n**ACTION REQUIRED:**\n\n- register new app on cors.sh\n- replace the existing api key in your web project issued from cors.sh on previous step\n- replace `cors.bridged.cc` to `api.cors.sh`\n\nFor example with cURL,\n\nPrevious\n\n```curl\nGET https://cors.bridged.cc/https://google.com\n-H 'cors-api-key: xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'\n```\n\nTo..\n\n```curl\nGET https://api.cors.sh/https://google.com\n-H 'cors-api-key: live_xxxx-xxxx-xxxx-xxxx'\n```\n"
  },
  {
    "path": "docs/docs/tutorial-basics/_category_.json",
    "content": "{\n  \"label\": \"Tutorial - Basics\",\n  \"position\": 2,\n  \"link\": {\n    \"type\": \"generated-index\",\n    \"description\": \"5 minutes to learn the most important Docusaurus concepts.\"\n  }\n}\n"
  },
  {
    "path": "docs/docs/tutorial-basics/congratulations.md",
    "content": "---\nsidebar_position: 6\n---\n\n# Congratulations!\n\nYou have just learned the **basics of Docusaurus** and made some changes to the **initial template**.\n\nDocusaurus has **much more to offer**!\n\nHave **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**.\n\nAnything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610)\n\n## What's next?\n\n- Read the [official documentation](https://docusaurus.io/).\n- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout)\n- Add a [search bar](https://docusaurus.io/docs/search)\n- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase)\n- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support)\n"
  },
  {
    "path": "docs/docs/tutorial-basics/create-a-blog-post.md",
    "content": "---\nsidebar_position: 3\n---\n\n# Create a Blog Post\n\nDocusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed...\n\n## Create your first Post\n\nCreate a file at `blog/2021-02-28-greetings.md`:\n\n```md title=\"blog/2021-02-28-greetings.md\"\n---\nslug: greetings\ntitle: Greetings!\nauthors:\n  - name: Joel Marcey\n    title: Co-creator of Docusaurus 1\n    url: https://github.com/JoelMarcey\n    image_url: https://github.com/JoelMarcey.png\n  - name: Sébastien Lorber\n    title: Docusaurus maintainer\n    url: https://sebastienlorber.com\n    image_url: https://github.com/slorber.png\ntags: [greetings]\n---\n\nCongratulations, you have made your first post!\n\nFeel free to play around and edit this post as much you like.\n```\n\nA new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).\n"
  },
  {
    "path": "docs/docs/tutorial-basics/create-a-document.md",
    "content": "---\nsidebar_position: 2\n---\n\n# Create a Document\n\nDocuments are **groups of pages** connected through:\n\n- a **sidebar**\n- **previous/next navigation**\n- **versioning**\n\n## Create your first Doc\n\nCreate a Markdown file at `docs/hello.md`:\n\n```md title=\"docs/hello.md\"\n# Hello\n\nThis is my **first Docusaurus document**!\n```\n\nA new document is now available at [http://localhost:3000/docs/hello](http://localhost:3000/docs/hello).\n\n## Configure the Sidebar\n\nDocusaurus automatically **creates a sidebar** from the `docs` folder.\n\nAdd metadata to customize the sidebar label and position:\n\n```md title=\"docs/hello.md\" {1-4}\n---\nsidebar_label: 'Hi!'\nsidebar_position: 3\n---\n\n# Hello\n\nThis is my **first Docusaurus document**!\n```\n\nIt is also possible to create your sidebar explicitly in `sidebars.js`:\n\n```js title=\"sidebars.js\"\nmodule.exports = {\n  tutorialSidebar: [\n    {\n      type: 'category',\n      label: 'Tutorial',\n      // highlight-next-line\n      items: ['hello'],\n    },\n  ],\n};\n```\n"
  },
  {
    "path": "docs/docs/tutorial-basics/create-a-page.md",
    "content": "---\nsidebar_position: 1\n---\n\n# Create a Page\n\nAdd **Markdown or React** files to `src/pages` to create a **standalone page**:\n\n- `src/pages/index.js` → `localhost:3000/`\n- `src/pages/foo.md` → `localhost:3000/foo`\n- `src/pages/foo/bar.js` → `localhost:3000/foo/bar`\n\n## Create your first React Page\n\nCreate a file at `src/pages/my-react-page.js`:\n\n```jsx title=\"src/pages/my-react-page.js\"\nimport React from 'react';\nimport Layout from '@theme/Layout';\n\nexport default function MyReactPage() {\n  return (\n    <Layout>\n      <h1>My React page</h1>\n      <p>This is a React page</p>\n    </Layout>\n  );\n}\n```\n\nA new page is now available at [http://localhost:3000/my-react-page](http://localhost:3000/my-react-page).\n\n## Create your first Markdown Page\n\nCreate a file at `src/pages/my-markdown-page.md`:\n\n```mdx title=\"src/pages/my-markdown-page.md\"\n# My Markdown page\n\nThis is a Markdown page\n```\n\nA new page is now available at [http://localhost:3000/my-markdown-page](http://localhost:3000/my-markdown-page).\n"
  },
  {
    "path": "docs/docs/tutorial-basics/deploy-your-site.md",
    "content": "---\nsidebar_position: 5\n---\n\n# Deploy your site\n\nDocusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**).\n\nIt builds your site as simple **static HTML, JavaScript and CSS files**.\n\n## Build your site\n\nBuild your site **for production**:\n\n```bash\nnpm run build\n```\n\nThe static files are generated in the `build` folder.\n\n## Deploy your site\n\nTest your production build locally:\n\n```bash\nnpm run serve\n```\n\nThe `build` folder is now served at [http://localhost:3000/](http://localhost:3000/).\n\nYou can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**).\n"
  },
  {
    "path": "docs/docs/tutorial-basics/markdown-features.mdx",
    "content": "---\nsidebar_position: 4\n---\n\n# Markdown Features\n\nDocusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**.\n\n## Front Matter\n\nMarkdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/):\n\n```text title=\"my-doc.md\"\n// highlight-start\n---\nid: my-doc-id\ntitle: My document title\ndescription: My document description\nslug: /my-custom-url\n---\n// highlight-end\n\n## Markdown heading\n\nMarkdown text with [links](./hello.md)\n```\n\n## Links\n\nRegular Markdown links are supported, using url paths or relative file paths.\n\n```md\nLet's see how to [Create a page](/create-a-page).\n```\n\n```md\nLet's see how to [Create a page](./create-a-page.md).\n```\n\n**Result:** Let's see how to [Create a page](./create-a-page.md).\n\n## Images\n\nRegular Markdown images are supported.\n\nYou can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`):\n\n```md\n![Docusaurus logo](/img/docusaurus.png)\n```\n\n![Docusaurus logo](/img/docusaurus.png)\n\nYou can reference images relative to the current file as well, as shown in [the extra guides](../tutorial-extras/manage-docs-versions.md).\n\n## Code Blocks\n\nMarkdown code blocks are supported with Syntax highlighting.\n\n    ```jsx title=\"src/components/HelloDocusaurus.js\"\n    function HelloDocusaurus() {\n        return (\n            <h1>Hello, Docusaurus!</h1>\n        )\n    }\n    ```\n\n```jsx title=\"src/components/HelloDocusaurus.js\"\nfunction HelloDocusaurus() {\n  return <h1>Hello, Docusaurus!</h1>;\n}\n```\n\n## Admonitions\n\nDocusaurus has a special syntax to create admonitions and callouts:\n\n    :::tip My tip\n\n    Use this awesome feature option\n\n    :::\n\n    :::danger Take care\n\n    This action is dangerous\n\n    :::\n\n:::tip My tip\n\nUse this awesome feature option\n\n:::\n\n:::danger Take care\n\nThis action is dangerous\n\n:::\n\n## MDX and React Components\n\n[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**:\n\n```jsx\nexport const Highlight = ({children, color}) => (\n  <span\n    style={{\n      backgroundColor: color,\n      borderRadius: '20px',\n      color: '#fff',\n      padding: '10px',\n      cursor: 'pointer',\n    }}\n    onClick={() => {\n      alert(`You clicked the color ${color} with label ${children}`)\n    }}>\n    {children}\n  </span>\n);\n\nThis is <Highlight color=\"#25c2a0\">Docusaurus green</Highlight> !\n\nThis is <Highlight color=\"#1877F2\">Facebook blue</Highlight> !\n```\n\nexport const Highlight = ({children, color}) => (\n  <span\n    style={{\n      backgroundColor: color,\n      borderRadius: '20px',\n      color: '#fff',\n      padding: '10px',\n      cursor: 'pointer',\n    }}\n    onClick={() => {\n      alert(`You clicked the color ${color} with label ${children}`);\n    }}>\n    {children}\n  </span>\n);\n\nThis is <Highlight color=\"#25c2a0\">Docusaurus green</Highlight> !\n\nThis is <Highlight color=\"#1877F2\">Facebook blue</Highlight> !\n"
  },
  {
    "path": "docs/docs/tutorial-extras/_category_.json",
    "content": "{\n  \"label\": \"Tutorial - Extras\",\n  \"position\": 3,\n  \"link\": {\n    \"type\": \"generated-index\"\n  }\n}\n"
  },
  {
    "path": "docs/docs/tutorial-extras/manage-docs-versions.md",
    "content": "---\nsidebar_position: 1\n---\n\n# Manage Docs Versions\n\nDocusaurus can manage multiple versions of your docs.\n\n## Create a docs version\n\nRelease a version 1.0 of your project:\n\n```bash\nnpm run docusaurus docs:version 1.0\n```\n\nThe `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created.\n\nYour docs now have 2 versions:\n\n- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs\n- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs**\n\n## Add a Version Dropdown\n\nTo navigate seamlessly across versions, add a version dropdown.\n\nModify the `docusaurus.config.js` file:\n\n```js title=\"docusaurus.config.js\"\nmodule.exports = {\n  themeConfig: {\n    navbar: {\n      items: [\n        // highlight-start\n        {\n          type: 'docsVersionDropdown',\n        },\n        // highlight-end\n      ],\n    },\n  },\n};\n```\n\nThe docs version dropdown appears in your navbar:\n\n![Docs Version Dropdown](./img/docsVersionDropdown.png)\n\n## Update an existing version\n\nIt is possible to edit versioned docs in their respective folder:\n\n- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello`\n- `docs/hello.md` updates `http://localhost:3000/docs/next/hello`\n"
  },
  {
    "path": "docs/docs/tutorial-extras/translate-your-site.md",
    "content": "---\nsidebar_position: 2\n---\n\n# Translate your site\n\nLet's translate `docs/intro.md` to French.\n\n## Configure i18n\n\nModify `docusaurus.config.js` to add support for the `fr` locale:\n\n```js title=\"docusaurus.config.js\"\nmodule.exports = {\n  i18n: {\n    defaultLocale: 'en',\n    locales: ['en', 'fr'],\n  },\n};\n```\n\n## Translate a doc\n\nCopy the `docs/intro.md` file to the `i18n/fr` folder:\n\n```bash\nmkdir -p i18n/fr/docusaurus-plugin-content-docs/current/\n\ncp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md\n```\n\nTranslate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French.\n\n## Start your localized site\n\nStart your site on the French locale:\n\n```bash\nnpm run start -- --locale fr\n```\n\nYour localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated.\n\n:::caution\n\nIn development, you can only use one locale at a same time.\n\n:::\n\n## Add a Locale Dropdown\n\nTo navigate seamlessly across languages, add a locale dropdown.\n\nModify the `docusaurus.config.js` file:\n\n```js title=\"docusaurus.config.js\"\nmodule.exports = {\n  themeConfig: {\n    navbar: {\n      items: [\n        // highlight-start\n        {\n          type: 'localeDropdown',\n        },\n        // highlight-end\n      ],\n    },\n  },\n};\n```\n\nThe locale dropdown now appears in your navbar:\n\n![Locale Dropdown](./img/localeDropdown.png)\n\n## Build your localized site\n\nBuild your site for a specific locale:\n\n```bash\nnpm run build -- --locale fr\n```\n\nOr build your site to include all the locales at once:\n\n```bash\nnpm run build\n```\n"
  },
  {
    "path": "docs/docs/what-is-cors.md",
    "content": "---\ntitle: What is CORS?\n---\n\n# What is `CORS` ?\n\n`CORS` Cross-Origin Resource Sharing\n\n## Do I need a cors proxy?\n\nIn short,\n\n- **NO:** For your own backend\n- **YES:** If..\n  - Your front end is a plugin that runs on top of other sites. (e.g. shopify, figma plugin)\n  - You are accessign third party api (e.g. instagram, twitter) and the api server responds with limited CORS headers.\n\n## Okay, it seems I don't need cors proxy service, What can I do?\n\nIf you're application is a general web app and you have control over the backend, here are some quick guides for each backend frameworks.\n\n- Express\n- Koa\n- Hapi\n- Fastify\n- NestJS\n- AWS Lambda\n"
  },
  {
    "path": "docs/docusaurus.config.js",
    "content": "// @ts-check\n// Note: type annotations allow type checking and IDEs autocompletion\n\nconst lightCodeTheme = require(\"prism-react-renderer/themes/github\");\nconst darkCodeTheme = require(\"prism-react-renderer/themes/dracula\");\n\n/** @type {import('@docusaurus/types').Config} */\nconst config = {\n  title: \"cors.sh\",\n  tagline: \"Get started with CORS.SH\",\n  url: \"https://docs.cors.sh\",\n  // @ts-ignore\n  baseUrl: process.env.BASE_URL,\n  onBrokenLinks: \"throw\",\n  onBrokenMarkdownLinks: \"warn\",\n  favicon: \"img/favicon.ico\",\n\n  // GitHub pages deployment config.\n  // If you aren't using GitHub pages, you don't need these.\n  organizationName: \"gridaco\", // Usually your GitHub org/user name.\n  projectName: \"cors.sh\", // Usually your repo name.\n\n  // Even if you don't use internalization, you can use this field to set useful\n  // metadata like html lang. For example, if your site is Chinese, you may want\n  // to replace \"en\" with \"zh-Hans\".\n  i18n: {\n    defaultLocale: \"en\",\n    locales: [\"en\"],\n  },\n\n  presets: [\n    [\n      \"classic\",\n      /** @type {import('@docusaurus/preset-classic').Options} */\n      ({\n        docs: {\n          breadcrumbs: false,\n          routeBasePath: \"/\",\n          path: \"docs\",\n          sidebarPath: require.resolve(\"./sidebars.js\"),\n          // Please change this to your repo.\n          // Remove this to remove the \"edit this page\" links.\n          editUrl: \"https://github.com/gridaco/cors.sh/tree/main/docs/\",\n        },\n        blog: {\n          showReadingTime: true,\n          // Please change this to your repo.\n          // Remove this to remove the \"edit this page\" links.\n          editUrl:\n            \"https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/\",\n        },\n        // tracking\n        googleAnalytics: {\n          trackingID: \"G-XG051N1VS3\",\n        },\n        theme: {\n          customCss: require.resolve(\"./src/css/custom.css\"),\n        },\n      }),\n    ],\n  ],\n\n  themeConfig:\n    /** @type {import('@docusaurus/preset-classic').ThemeConfig} */\n    ({\n      navbar: {\n        logo: {\n          alt: \"CORS.SH By Grida\",\n          src: \"img/logo.svg\",\n          width: 100,\n          href: \"https://cors.sh/\",\n        },\n        items: [\n          {\n            type: \"doc\",\n            docId: \"intro\",\n            position: \"left\",\n            label: \"Tutorial\",\n          },\n          { to: \"/blog\", label: \"Blog\", position: \"left\" },\n          {\n            href: \"https://github.com/facebook/docusaurus\",\n            label: \"GitHub\",\n            position: \"right\",\n          },\n        ],\n      },\n      footer: {\n        style: \"light\",\n        links: [\n          {\n            title: \"Docs\",\n            items: [\n              {\n                label: \"Tutorial\",\n                to: \"/docs/intro\",\n              },\n            ],\n          },\n          {\n            title: \"Community\",\n            items: [\n              {\n                label: \"Slack Channel\",\n                href: \"https://grida.co/join-slack\",\n              },\n              {\n                label: \"Twitter\",\n                href: \"https://twitter.com/grida_co\",\n              },\n            ],\n          },\n          {\n            title: \"More\",\n            items: [\n              {\n                label: \"Blog\",\n                to: \"/blog\",\n              },\n              {\n                label: \"GitHub\",\n                href: \"https://github.com/gridaco/cors.sh\",\n              },\n            ],\n          },\n        ],\n        copyright: `Copyright © ${new Date().getFullYear()} Grida, Inc. Built with Docusaurus.`,\n      },\n      prism: {\n        theme: lightCodeTheme,\n        darkTheme: darkCodeTheme,\n      },\n    }),\n};\n\nmodule.exports = config;\n"
  },
  {
    "path": "docs/package.json",
    "content": "{\n  \"name\": \"docs\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"docusaurus\": \"docusaurus\",\n    \"start\": \"docusaurus start\",\n    \"build\": \"docusaurus build\",\n    \"swizzle\": \"docusaurus swizzle\",\n    \"deploy\": \"docusaurus deploy\",\n    \"clear\": \"docusaurus clear\",\n    \"serve\": \"docusaurus serve\",\n    \"write-translations\": \"docusaurus write-translations\",\n    \"write-heading-ids\": \"docusaurus write-heading-ids\"\n  },\n  \"dependencies\": {\n    \"@docusaurus/core\": \"2.0.1\",\n    \"@docusaurus/preset-classic\": \"2.0.1\",\n    \"@mdx-js/react\": \"^1.6.22\",\n    \"clsx\": \"^1.2.1\",\n    \"prism-react-renderer\": \"^1.3.5\",\n    \"react\": \"^17.0.2\",\n    \"react-dom\": \"^17.0.2\"\n  },\n  \"devDependencies\": {\n    \"@docusaurus/module-type-aliases\": \"2.0.1\"\n  },\n  \"browserslist\": {\n    \"production\": [\n      \">0.5%\",\n      \"not dead\",\n      \"not op_mini all\"\n    ],\n    \"development\": [\n      \"last 1 chrome version\",\n      \"last 1 firefox version\",\n      \"last 1 safari version\"\n    ]\n  },\n  \"engines\": {\n    \"node\": \">=16.14\"\n  }\n}\n"
  },
  {
    "path": "docs/sidebars.js",
    "content": "/**\n * Creating a sidebar enables you to:\n - create an ordered group of docs\n - render a sidebar for each doc of that group\n - provide next/previous navigation\n\n The sidebars can be generated from the filesystem, or explicitly defined here.\n\n Create as many sidebars as you want.\n */\n\n// @ts-check\n\n/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */\nconst sidebars = {\n  // By default, Docusaurus generates a sidebar from the docs folder structure\n  tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],\n\n  // But you can create a sidebar manually\n  /*\n  tutorialSidebar: [\n    {\n      type: 'category',\n      label: 'Tutorial',\n      items: ['hello'],\n    },\n  ],\n   */\n};\n\nmodule.exports = sidebars;\n"
  },
  {
    "path": "docs/src/css/custom.css",
    "content": "/**\n * Any CSS included here will be global. The classic template\n * bundles Infima by default. Infima is a CSS framework designed to\n * work well for content-centric websites.\n */\n\n/* You can override the default Infima variables here. */\n:root {\n  --ifm-color-primary: #2e8555;\n  --ifm-color-primary-dark: #29784c;\n  --ifm-color-primary-darker: #277148;\n  --ifm-color-primary-darkest: #205d3b;\n  --ifm-color-primary-light: #33925d;\n  --ifm-color-primary-lighter: #359962;\n  --ifm-color-primary-lightest: #3cad6e;\n  --ifm-code-font-size: 95%;\n  --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);\n}\n\n/* For readability concerns, you should choose a lighter palette in dark mode. */\n[data-theme='dark'] {\n  --ifm-color-primary: #25c2a0;\n  --ifm-color-primary-dark: #21af90;\n  --ifm-color-primary-darker: #1fa588;\n  --ifm-color-primary-darkest: #1a8870;\n  --ifm-color-primary-light: #29d5b0;\n  --ifm-color-primary-lighter: #32d8b4;\n  --ifm-color-primary-lightest: #4fddbf;\n  --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);\n}\n"
  },
  {
    "path": "docs/static/.nojekyll",
    "content": ""
  },
  {
    "path": "docs/static/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta\n      http-equiv=\"refresh\"\n      content=\"0; url=docs/id-of-doc-to-land-on.html\"\n    />\n    <script type=\"text/javascript\">\n      window.location.href = \"docs/id-of-doc-to-land-on.html\";\n    </script>\n    <title>Your Site Title Here</title>\n  </head>\n  <body>\n    If you are not redirected automatically, follow this\n    <a href=\"docs/id-of-doc-to-land-on.html\">link</a>.\n  </body>\n</html>\n"
  },
  {
    "path": "examples/a-simple-demo/index.html",
    "content": ""
  },
  {
    "path": "examples/a-simple-demo/index.js",
    "content": ""
  },
  {
    "path": "examples/readme.md",
    "content": "# Examples directory\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"cors.sh\",\n  \"version\": \"0.0.0\",\n  \"description\": \"The only cors proxy service all you'll ever need.\",\n  \"repository\": \"https://github.com/gridaco/cors.sh.git\",\n  \"authors\": [\n    \"Rob-W\",\n    \"hoppscotch\",\n    \"softmarshmallow\"\n  ],\n  \"license\": \"MIT\",\n  \"private\": true,\n  \"workspaces\": [\n    \"web\",\n    \"homepage\",\n    \"console\",\n    \"packages/*\",\n    \"packages/editor-ui/packages/*\"\n  ],\n  \"scripts\": {\n    \"homepage\": \"yarn workspace homepage run dev\"\n  },\n  \"resolutions\": {\n    \"@emotion/react\": \"^11.11.1\",\n    \"@emotion/styled\": \"^11.11.0\"\n  }\n}\n"
  },
  {
    "path": "packages/api/index.ts",
    "content": "import Axios, { AxiosInstance, AxiosError } from \"axios\";\n\nconst HOST =\n  process.env.NODE_ENV === \"production\"\n    ? \"https://services.cors.sh\"\n    : \"http://localhost:4021\";\n\nexport class Client {\n  private _client: AxiosInstance;\n  constructor(credentials: { \"x-cors-service-checkout-session-id\"?: string }) {\n    this._client = Axios.create({\n      baseURL: HOST,\n      headers: {\n        ...credentials,\n      },\n    });\n  }\n\n  async onboardingWithEmail({ email }: { email: string }) {\n    try {\n      return (\n        await this._client.post<OnboardingApplication>(\n          \"/onboarding/with-email\",\n          { email }\n        )\n      ).data;\n    } catch (e) {\n      if (e instanceof AxiosError) {\n        switch (e.response?.status) {\n          case 409:\n            throw new AlreadySignedUp();\n          case 429:\n            throw new OnboardingApiKeyAlreadyRequested();\n        }\n      }\n    }\n  }\n\n  async onboardingWithForm({\n    name,\n    allowedOrigins = [],\n    priceId,\n  }: {\n    name?: string;\n    allowedOrigins?: string[];\n    priceId?: string;\n  }) {\n    return (\n      await this._client.post<OnboardingApplication>(\"/onboarding/with-form\", {\n        name: name,\n        allowedOrigins: allowedOrigins,\n        priceId: priceId,\n      })\n    ).data;\n  }\n\n  async getOnboardingApplication(id: string) {\n    return (await this._client.get<OnboardingApplication>(`/onboarding/${id}`))\n      .data;\n  }\n\n  async convertApplication(onboarding_id: string, checkout_session_id: string) {\n    return (\n      await this._client.post<ApplicationWithApiKey>(\n        `/onboarding/${onboarding_id}/convert`,\n        {\n          checkout_session_id,\n        }\n      )\n    ).data;\n  }\n\n  async createApplication(\n    payload: CreateApplicationRequest\n  ): Promise<CreateAplicationResponse> {\n    const { data } = await this._client.post<CreateAplicationResponse>(\n      \"/applications\",\n      payload\n    );\n    return data;\n  }\n\n  async getApplication(id: string): Promise<ApplicationDetail> {\n    return (await this._client.get<ApplicationDetail>(`/applications/${id}`))\n      .data;\n  }\n\n  async listApplications(): Promise<Application[]> {\n    return (await this._client.get<Application[]>(\"/applications\")).data;\n  }\n\n  async deleteApplication(id: string) {\n    await this._client.delete(`/applications/${id}`);\n  }\n\n  async updateApplication(id: string, payload: UpdateApplicationRequest) {\n    await this._client.put(`/applications/${id}`, payload);\n  }\n}\n\n/**\n * client that is used for signing in the user.\n */\nconst _auth_client = Axios.create({\n  baseURL: HOST,\n  withCredentials: false,\n});\n\n/**\n * sign-in to cors.sh service. the authentication flow is..\n *\n * 1. user sign-in to grida.co (with first pary proxy authentication)\n * 2. user calles this function with the token.\n * 3. services.cors.sh will set the secure http only cookie.\n * 4. use the _signed_client to make future requests.\n * @returns\n */\nexport async function signin({ grida_token }: { grida_token: string }) {\n  try {\n    return await _auth_client.post(\"/auth/signin\", {\n      headers: {\n        \"Proxy-Authorization\": `Bearer ${grida_token}`,\n      },\n    });\n  } catch (e) {\n    return false;\n  }\n}\n\nexport class AlreadySignedUp extends Error {\n  constructor() {\n    super(\"already signed up\");\n  }\n}\n\nexport class OnboardingApiKeyAlreadyRequested extends Error {\n  constructor() {\n    super(\"onboarding api key already requested\");\n  }\n}\n\nexport interface CreateApplicationRequest {\n  name: string;\n  allowedOrigins: string[];\n}\n\nexport interface Application {\n  name: string;\n  id: string;\n  allowedOrigins: string[];\n}\n\nexport interface OnboardingApplication {\n  id: string;\n  email: string;\n  name?: string;\n  allowedOrigins: string[];\n  priceId?: string;\n}\n\nexport type ApplicationWithApiKey = Application & {\n  apikey_test: string;\n  apikey_live: string;\n};\n\nexport type ApplicationDetail = ApplicationWithApiKey;\n\nexport type CreateAplicationResponse = ApplicationWithApiKey;\n\nexport type UpdateApplicationRequest = Application;\n\nexport default new Client({});\n"
  },
  {
    "path": "packages/api/package.json",
    "content": "{\n  \"name\": \"@cors.sh/service-api\",\n  \"version\": \"0.0.0\",\n  \"dependencies\": {\n    \"axios\": \"^0.27.2\"\n  }\n}\n"
  },
  {
    "path": "packages/app-ui/components/api-key-reveal.tsx",
    "content": "import React from \"react\";\nimport copy from \"copy-to-clipboard\";\nimport toast from \"react-hot-toast\";\nimport styled from \"@emotion/styled\";\nimport { EyeOpenIcon } from \"@radix-ui/react-icons\";\n\nexport function ApiKeyReveal({\n  keys,\n}: {\n  keys: { test: string; prod: string };\n}) {\n  const [masked, setMasked] = React.useState(true);\n\n  const onCopy = (text: string) => {\n    copy(text);\n    toast.success(\"Copied to clipboard\");\n  };\n\n  const keydisplay = (key: string, masked: boolean) => {\n    if (masked) {\n      // leave the first 5 characters as is\n      // replace all characters except \"-\" that with x\n      const first5 = key.slice(0, 5);\n      const target = key.slice(5);\n      // replace all characters except \"-\" with x\n      const masked = target.replace(/[^-]/g, \"x\");\n      return first5 + masked.slice(5);\n    } else {\n      return key;\n    }\n  };\n\n  const Item = ({ sign: key }: { sign: string }) => {\n    return (\n      <span key={key} className=\"key\" onClick={() => onCopy(key)}>\n        <u>{keydisplay(key, masked)}</u>\n        <br />\n      </span>\n    );\n  };\n\n  return (\n    <CodeBlock>\n      <div\n        className=\"reveal\"\n        onClick={() => {\n          setMasked(false);\n        }}\n        style={{\n          visibility: masked ? \"visible\" : \"hidden\",\n        }}\n      >\n        <EyeOpenIcon />\n      </div>\n      <pre>\n        API Keys\n        <br />\n        <br />\n        # for testing\n        <br />\n        <Item sign={keys.test} />\n        <br />\n        <br />\n        # for production\n        <br />\n        <Item sign={keys.prod} />\n      </pre>\n    </CodeBlock>\n  );\n}\n\nconst CodeBlock = styled.code`\n  position: relative;\n  display: block;\n  width: 100%;\n  padding: 21px;\n  background: black;\n  color: white;\n  border-radius: 4px;\n  font-size: 12px;\n  font-family: monospace;\n  font-weight: 400;\n  overflow-x: scroll;\n\n  .reveal {\n    position: absolute;\n    cursor: pointer;\n    top: 12px;\n    right: 12px;\n    opacity: 0;\n    transition: opacity 0.2s ease-in-out;\n  }\n\n  .key {\n    cursor: pointer;\n  }\n\n  pre {\n    margin: 0;\n  }\n\n  &:hover {\n    .reveal {\n      opacity: 1;\n    }\n  }\n`;\n"
  },
  {
    "path": "packages/app-ui/components/index.ts",
    "content": "export * from \"./underline-button\";\nexport * from \"./api-key-reveal\";\n"
  },
  {
    "path": "packages/app-ui/components/underline-button.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\n\nexport const UnderlineButton = React.forwardRef(function UnderlineButton({ children }: React.PropsWithChildren<{}>, forwaredRef: React.Ref<HTMLButtonElement>) {\n  return <_Button ref={forwaredRef}>{children}</_Button>;\n})\n\nconst _Button = styled.button`\n  background: none;\n  border: none;\n  padding: 0;\n  font: inherit;\n  cursor: pointer;\n  outline: inherit;\n  text-decoration: underline;\n  color: rgba(0, 0, 0, 0.5);\n`;\n"
  },
  {
    "path": "packages/app-ui/layouts/form-page-latout.ts",
    "content": "\"use client\";\nimport styled from \"@emotion/styled\";\n\nexport const FormPageLayout = styled.div`\n  font-family: sans-serif;\n  display: flex;\n  flex-direction: column;\n  margin: auto;\n  align-items: center;\n  justify-content: center;\n  max-width: 320px;\n  min-height: 100vh;\n\n  h1,\n  p {\n    text-align: center;\n  }\n\n  .description {\n    margin-top: 16px;\n  }\n\n  .body {\n    display: flex;\n    flex-direction: column;\n    gap: 21px;\n    width: 100%;\n  }\n\n  .form {\n    margin-top: 60px;\n    display: flex;\n    flex-direction: column;\n    gap: 21px;\n    width: 100%;\n  }\n\n  .close {\n    position: absolute;\n    top: 16px;\n    right: 16px;\n    margin: 0 !important;\n    padding: 4px !important;\n    background: none;\n    border: none;\n    cursor: pointer;\n    color: #ccc;\n\n    &:hover {\n      color: black;\n    }\n  }\n`;\n"
  },
  {
    "path": "packages/app-ui/layouts/index.ts",
    "content": "export * from \"./form-page-latout\";\nexport * from \"./page-close-button\";\n"
  },
  {
    "path": "packages/app-ui/layouts/page-close-button.tsx",
    "content": "import React from \"react\";\n// import { useRouter } from \"next/router\";\nimport { Cross2Icon } from \"@radix-ui/react-icons\";\n\nexport function PageCloseButton() {\n  // const router = useRouter();\n\n  return (\n    <button\n      className=\"close\"\n      onClick={() => {\n        // router.replace(\"/\");\n      }}\n    >\n      <Cross2Icon />\n    </button>\n  );\n}\n"
  },
  {
    "path": "packages/app-ui/package.json",
    "content": "{\n  \"name\": \"@app/ui\",\n  \"version\": \"0.0.0\"\n}"
  },
  {
    "path": "packages/app-ui/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"jsx\": \"react\",\n    \"esModuleInterop\": true\n  }\n}\n"
  },
  {
    "path": "packages/app-ui/utils/index.ts",
    "content": "export const validateUrls = (urls: string) => {\n  const lines = urls.split(\",\").map((line) => line.trim());\n  for (const line of lines) {\n    try {\n      new URL(line);\n    } catch (e) {\n      return false;\n    }\n  }\n  return true;\n};\n"
  },
  {
    "path": "playground/readme.md",
    "content": "# cors.sh/playground\n\nVisit [cors.sh/playground](https://cors.sh/playground) to try out the playground.\n\nSource code available at [gridaco/playground.cors.sh](https://github.com/gridaco/playground.cors.sh).\n"
  },
  {
    "path": "services/auth.proxy.cors.sh/.gitignore",
    "content": "# package directories\nnode_modules\njspm_packages\n\n# Serverless directories\n.serverless"
  },
  {
    "path": "services/auth.proxy.cors.sh/deploy/prod.sh",
    "content": "sls deploy --stage production \\\n  --param=\"domain=true\""
  },
  {
    "path": "services/auth.proxy.cors.sh/deploy/staging.sh",
    "content": "sls deploy --stage production \\\n  --param=\"domain=true\""
  },
  {
    "path": "services/auth.proxy.cors.sh/handler.ts",
    "content": "// this uses dynamodb to read api key signatures\n// signature types are..\n// tmp - this is not stored in db.\n// live / test - this is stored in db.\n\nconst CORS_API_KEY_HEADER = \"x-cors-api-key\";\n\n// API Gateway authorizer for serverless functions\nmodule.exports.authorize = async (event) => {\n  // get the api key from the header\n  const key = event.headers[CORS_API_KEY_HEADER];\n  // const { mode, signature } = keyinfo(key);\n\n  // switch (mode) {\n  //   case \"temp\": {\n  //     break;\n  //   }\n  //   case \"live\": {\n  //     break;\n  //   }\n  //   case \"test\": {\n  //     break;\n  //   }\n  //   case \"v2022\": {\n  //     //\n  //   }\n  // }\n\n  // Authorized\n  return {\n    principalId: \"user\",\n    policyDocument: {\n      Version: \"2012-10-17\",\n      Statement: [\n        {\n          Action: \"execute-api:Invoke\",\n          Effect: \"Allow\",\n          Resource: event.methodArn,\n        },\n      ],\n    },\n\n    // Optional context\n    context: {\n      key,\n    },\n\n    // Optional output with custom properties of the String, Number or Boolean type.\n    usageIdentifierKey: \"123456789\",\n  };\n};\n"
  },
  {
    "path": "services/auth.proxy.cors.sh/package.json",
    "content": "{\n  \"name\": \"auth.proxy.cors.sh\",\n  \"version\": \"0.0.0\",\n  \"dependencies\": {\n    \"aws-sdk\": \"^2.1290.0\"\n  }\n}"
  },
  {
    "path": "services/auth.proxy.cors.sh/readme.md",
    "content": "# API Authorizer for proxy.cors.sh\n\n## Architecture\n\nThis layer will use the synced key signatures direcly from the table, then proxy.cors.sh will use this as an authorizer.\n\n> The table is managed by the service layer, this layer only performs GetItem.\n\nAccess points are secured by domain, this service's source is safe to be public on github.\n"
  },
  {
    "path": "services/auth.proxy.cors.sh/serverless.yml",
    "content": "# Welcome to serverless. Read the docs\n# https://serverless.com/framework/docs/\n\nservice: cors-proxy-authorizer\nuseDotenv: true\ncustom:\n  customDomain:\n    domainName: auth.proxy.cors.sh\n    basePath: \"\"\n    stage: ${opt:stage, self:provider.stage}\n    createRoute53Record: false\n    # enabled only for production - configured by package.json script with --domain flag.\n    enabled: ${param:domain, false}\n\n  serverless-offline:\n    httpPort: 4022\n    noPrependStageInUrl: true\n\nprovider:\n  name: aws\n  memorySize: 128\n  runtime: nodejs14.x\n  apiGateway:\n    shouldStartNameWithService: true\n    binaryMediaTypes:\n      - \"*/*\"\n    endpointType: regional\n  region: us-west-1\n  environment:\n    # managed by service.cors.sh\n    DYNAMODB_TABLE_SERVICE_KEYS: \"$cors-proxy-service-keys-${opt:stage, self:provider.stage}\"\n  iamRoleStatements:\n    - Effect: Allow\n      Action:\n        # this service only needs to read the service keys\n        - dynamodb:GetItem\n      Resource: \"arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE_SERVICE_KEYS}\"\n\n# The `functions` block defines what code to deploy\nfunctions:\n  api:\n    handler: handler.authorize\n    maximumEventAge: 60\n    maximumRetryAttempts: 0\n    timeout: 12\n    events:\n      - http:\n          path: /{proxy+}\n          method: any\n      - http:\n          path: /\n          method: get\n\nplugins:\n  - serverless-plugin-typescript\n  - serverless-plugin-optimize\n  - serverless-offline\n  - serverless-domain-manager\n"
  },
  {
    "path": "services/lagacy/.gitignore",
    "content": "# compiled output\n/dist\n/node_modules\n.build\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# OS\n.DS_Store\n\n# Tests\n/coverage\n/.nyc_output\n\n# IDEs and editors\n/.idea\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json"
  },
  {
    "path": "services/lagacy/README.md",
    "content": "# Legacy cors.bridged.cc\n\nAll requests to cors.bridged.cc will be redirected to cors.sh\n\nThis is done by api-gateway in AWS console.\n\nKeep the code as original as the last state of active service\n"
  },
  {
    "path": "services/lagacy/lib/cors.ts",
    "content": "import * as httpProxy from \"http-proxy\";\nimport * as https from \"https\";\nimport * as http from \"http\";\nimport * as url from \"url\";\nimport { getProxyForUrl } from \"proxy-from-env\";\nimport { withCORS, isValidHostName, parseURL } from \"./utils\";\n\ninterface OptionParams {\n  httpProxyOptions: httpProxy.ServerOptions;\n  httpsOptions?: https.ServerOptions;\n  [x: string]: any;\n}\n\nfunction proxyRequest(req, res, proxy) {\n  var location = req.corsAnywhereRequestState.location;\n  req.url = location.path;\n\n  var proxyOptions: any = {\n    changeOrigin: false,\n    prependPath: false,\n    target: location,\n    headers: {\n      host: location.host,\n    },\n    buffer: {\n      pipe: function(proxyReq) {\n        var proxyReqOn = proxyReq.on;\n        proxyReq.on = function(eventName, listener) {\n          if (eventName !== \"response\") {\n            return proxyReqOn.call(this, eventName, listener);\n          }\n          return proxyReqOn.call(this, \"response\", function(proxyRes) {\n            if (onProxyResponse(proxy, proxyReq, proxyRes, req, res)) {\n              try {\n                listener(proxyRes);\n              } catch (err) {\n                proxyReq.emit(\"error\", err);\n              }\n            }\n          });\n        };\n        return req.pipe(proxyReq);\n      },\n    },\n  };\n\n  var proxyThroughUrl = req.corsAnywhereRequestState.getProxyForUrl(\n    location.href\n  );\n  if (proxyThroughUrl) {\n    proxyOptions.target = proxyThroughUrl;\n    proxyOptions.toProxy = true;\n    req.url = location.href;\n  }\n\n  try {\n    proxy.web(req, res, proxyOptions);\n  } catch (err) {\n    proxy.emit(\"error\", err, req, res);\n  }\n}\n\nfunction onProxyResponse(proxy, proxyReq, proxyRes, req, res) {\n  var requestState = req.corsAnywhereRequestState;\n\n  var statusCode = proxyRes.statusCode;\n\n  if (!requestState.redirectCount_) {\n    res.setHeader(\"x-request-url\", requestState.location.href);\n  }\n  if (\n    statusCode === 301 ||\n    statusCode === 302 ||\n    statusCode === 303 ||\n    statusCode === 307 ||\n    statusCode === 308\n  ) {\n    var locationHeader = proxyRes.headers.location;\n    var parsedLocation;\n    if (locationHeader) {\n      locationHeader = url.resolve(requestState.location.href, locationHeader);\n      parsedLocation = parseURL(locationHeader);\n    }\n    if (parsedLocation) {\n      if (statusCode === 301 || statusCode === 302 || statusCode === 303) {\n        requestState.redirectCount_ = requestState.redirectCount_ + 1 || 1;\n        if (requestState.redirectCount_ <= requestState.maxRedirects) {\n          res.setHeader(\n            \"X-CORS-Redirect-\" + requestState.redirectCount_,\n            statusCode + \" \" + locationHeader\n          );\n\n          req.method = \"GET\";\n          req.headers[\"content-length\"] = \"0\";\n          delete req.headers[\"content-type\"];\n          requestState.location = parsedLocation;\n          req.removeAllListeners();\n          proxyReq.removeAllListeners(\"error\");\n          proxyReq.once(\"error\", function catchAndIgnoreError() {});\n          proxyReq.abort();\n          proxyRequest(req, res, proxy);\n          return false;\n        }\n      }\n      proxyRes.headers.location =\n        requestState.proxyBaseUrl + \"/\" + locationHeader;\n    }\n  }\n\n  delete proxyRes.headers[\"set-cookie\"];\n  delete proxyRes.headers[\"set-cookie2\"];\n\n  proxyRes.headers[\"x-final-url\"] = requestState.location.href;\n  withCORS(proxyRes.headers, req);\n  return true;\n}\n\nfunction getHandler(options, proxy) {\n  var corsAnywhere: any = {\n    getProxyForUrl: getProxyForUrl, // Function that specifies the proxy to use\n    maxRedirects: 5, // Maximum number of redirects to be followed.\n    originBlacklist: [], // Requests from these origins will be blocked.\n    originWhitelist: [], // If non-empty, requests not from an origin in this list will be blocked.\n    checkRateLimit: null, // Function that may enforce a rate-limit by returning a non-empty string.\n    redirectSameOrigin: false, // Redirect the client to the requested URL for same-origin requests.\n    requireHeader: null, // Require a header to be set?\n    removeHeaders: [], // Strip these request headers.\n    setHeaders: {}, // Set these request headers.\n    corsMaxAge: 0, // If set, an Access-Control-Max-Age header with this value (in seconds) will be added.\n    helpFile: __dirname + \"/help.txt\",\n  };\n\n  Object.keys(corsAnywhere).forEach(function(option) {\n    if (Object.prototype.hasOwnProperty.call(options, option)) {\n      corsAnywhere[option] = options[option];\n    }\n  });\n  if (corsAnywhere.requireHeader) {\n    if (typeof corsAnywhere.requireHeader === \"string\") {\n      corsAnywhere.requireHeader = [corsAnywhere.requireHeader.toLowerCase()];\n    } else if (\n      !Array.isArray(corsAnywhere.requireHeader) ||\n      corsAnywhere.requireHeader.length === 0\n    ) {\n      corsAnywhere.requireHeader = null;\n    } else {\n      corsAnywhere.requireHeader = corsAnywhere.requireHeader.map(function(\n        headerName\n      ) {\n        return headerName.toLowerCase();\n      });\n    }\n  }\n  var hasRequiredHeaders = function(headers) {\n    return (\n      !corsAnywhere.requireHeader ||\n      corsAnywhere.requireHeader.some(function(headerName) {\n        return Object.hasOwnProperty.call(headers, headerName);\n      })\n    );\n  };\n\n  return function(req, res) {\n    req.corsAnywhereRequestState = {\n      getProxyForUrl: corsAnywhere.getProxyForUrl,\n      maxRedirects: corsAnywhere.maxRedirects,\n      corsMaxAge: corsAnywhere.corsMaxAge,\n    };\n\n    var cors_headers = withCORS({}, req);\n    if (req.method === \"OPTIONS\") {\n      res.writeHead(200, cors_headers);\n      res.end();\n      return;\n    }\n\n    var location: any = parseURL(req.url.slice(1));\n\n    if (location.host === \"iscorsneeded\") {\n      res.writeHead(200, { \"Content-Type\": \"text/plain\" });\n      res.end(\"no\");\n      return;\n    }\n\n    if (location.port > 65535) {\n      res.writeHead(400, \"Invalid port\", cors_headers);\n      res.end(\"Port number too large: \" + location.port);\n      return;\n    }\n\n    if (!/^\\/https?:/.test(req.url) && !isValidHostName(location.hostname)) {\n      res.writeHead(404, \"Invalid host\", cors_headers);\n      res.end(\"Invalid host: \" + location.hostname);\n      return;\n    }\n\n    if (!hasRequiredHeaders(req.headers)) {\n      res.writeHead(400, \"Header required\", cors_headers);\n      res.end(\n        \"Missing required request header. Must specify one of: \" +\n          corsAnywhere.requireHeader\n      );\n      return;\n    }\n\n    var origin = req.headers.origin || \"\";\n    if (corsAnywhere.originBlacklist.indexOf(origin) >= 0) {\n      res.writeHead(403, \"Forbidden\", cors_headers);\n      res.end(\n        'The origin \"' +\n          origin +\n          '\" was blacklisted by the operator of this proxy.'\n      );\n      return;\n    }\n\n    if (\n      corsAnywhere.originWhitelist.length &&\n      corsAnywhere.originWhitelist.indexOf(origin) === -1\n    ) {\n      res.writeHead(403, \"Forbidden\", cors_headers);\n      res.end(\n        'The origin \"' +\n          origin +\n          '\" was not whitelisted by the operator of this proxy.'\n      );\n      return;\n    }\n\n    var rateLimitMessage =\n      corsAnywhere.checkRateLimit && corsAnywhere.checkRateLimit(origin);\n    if (rateLimitMessage) {\n      res.writeHead(429, \"Too Many Requests\", cors_headers);\n      res.end(\n        'The origin \"' +\n          origin +\n          '\" has sent too many requests.\\n' +\n          rateLimitMessage\n      );\n      return;\n    }\n\n    if (\n      corsAnywhere.redirectSameOrigin &&\n      origin &&\n      location.href[origin.length] === \"/\" &&\n      location.href.lastIndexOf(origin, 0) === 0\n    ) {\n      cors_headers.vary = \"origin\";\n      cors_headers[\"cache-control\"] = \"private\";\n      cors_headers.location = location.href;\n      res.writeHead(301, \"Please use a direct request\", cors_headers);\n      res.end();\n      return;\n    }\n\n    var isRequestedOverHttps =\n      req.connection.encrypted ||\n      /^\\s*https/.test(req.headers[\"x-forwarded-proto\"]);\n    var proxyBaseUrl =\n      (isRequestedOverHttps ? \"https://\" : \"http://\") + req.headers.host;\n\n    corsAnywhere.removeHeaders.forEach(function(header) {\n      delete req.headers[header];\n    });\n\n    Object.keys(corsAnywhere.setHeaders).forEach(function(header) {\n      req.headers[header] = corsAnywhere.setHeaders[header];\n    });\n\n    req.corsAnywhereRequestState.location = location;\n    req.corsAnywhereRequestState.proxyBaseUrl = proxyBaseUrl;\n\n    proxyRequest(req, res, proxy);\n  };\n}\n\nexport const createServer = (options: OptionParams) => {\n  var server: https.Server | http.Server;\n  var httpProxyOptions: httpProxy.ServerOptions = {\n    xfwd: true, // Append X-Forwarded-* headers\n  };\n\n  options = options || {};\n\n  if (options.httpProxyOptions) {\n    Object.keys(options.httpProxyOptions).forEach(function(option) {\n      httpProxyOptions[option] = options.httpProxyOptions[option];\n    });\n  }\n\n  var proxy: httpProxy = httpProxy.createServer(httpProxyOptions);\n  var requestHandler = getHandler(options, proxy);\n  if (options.httpsOptions) {\n    server = https.createServer(options.httpsOptions, requestHandler);\n  } else {\n    server = http.createServer(requestHandler);\n  }\n\n  proxy.on(\"error\", function(\n    err: Error,\n    _: http.IncomingMessage,\n    res: http.ServerResponse\n  ) {\n    if (res.headersSent) {\n      if (res.writableEnded === false) {\n        res.end();\n      }\n      return;\n    }\n    var headerNames = res.getHeaderNames\n      ? res.getHeaderNames()\n      : //@ts-ignore\n        Object.keys(res._headers || {});\n    headerNames.forEach(function(name) {\n      res.removeHeader(name);\n    });\n\n    res.writeHead(404, { \"Access-Control-Allow-Origin\": \"*\" });\n    res.end(\"Not found because of proxy error: \" + err);\n  });\n  return server;\n};\n"
  },
  {
    "path": "services/lagacy/lib/utils.ts",
    "content": "import * as net from \"net\";\nimport * as url from \"url\";\n\nexport const regexp = /\\.(?:AAA|AARP|ABARTH|ABB|ABBOTT|ABBVIE|ABC|ABLE|ABOGADO|ABUDHABI|AC|ACADEMY|ACCENTURE|ACCOUNTANT|ACCOUNTANTS|ACO|ACTIVE|ACTOR|AD|ADAC|ADS|ADULT|AE|AEG|AERO|AETNA|AF|AFAMILYCOMPANY|AFL|AFRICA|AG|AGAKHAN|AGENCY|AI|AIG|AIGO|AIRBUS|AIRFORCE|AIRTEL|AKDN|AL|ALFAROMEO|ALIBABA|ALIPAY|ALLFINANZ|ALLSTATE|ALLY|ALSACE|ALSTOM|AM|AMERICANEXPRESS|AMERICANFAMILY|AMEX|AMFAM|AMICA|AMSTERDAM|ANALYTICS|ANDROID|ANQUAN|ANZ|AO|AOL|APARTMENTS|APP|APPLE|AQ|AQUARELLE|AR|ARAB|ARAMCO|ARCHI|ARMY|ARPA|ART|ARTE|AS|ASDA|ASIA|ASSOCIATES|AT|ATHLETA|ATTORNEY|AU|AUCTION|AUDI|AUDIBLE|AUDIO|AUSPOST|AUTHOR|AUTO|AUTOS|AVIANCA|AW|AWS|AX|AXA|AZ|AZURE|BA|BABY|BAIDU|BANAMEX|BANANAREPUBLIC|BAND|BANK|BAR|BARCELONA|BARCLAYCARD|BARCLAYS|BAREFOOT|BARGAINS|BASEBALL|BASKETBALL|BAUHAUS|BAYERN|BB|BBC|BBT|BBVA|BCG|BCN|BD|BE|BEATS|BEAUTY|BEER|BENTLEY|BERLIN|BEST|BESTBUY|BET|BF|BG|BH|BHARTI|BI|BIBLE|BID|BIKE|BING|BINGO|BIO|BIZ|BJ|BLACK|BLACKFRIDAY|BLANCO|BLOCKBUSTER|BLOG|BLOOMBERG|BLUE|BM|BMS|BMW|BN|BNL|BNPPARIBAS|BO|BOATS|BOEHRINGER|BOFA|BOM|BOND|BOO|BOOK|BOOKING|BOOTS|BOSCH|BOSTIK|BOSTON|BOT|BOUTIQUE|BOX|BR|BRADESCO|BRIDGESTONE|BROADWAY|BROKER|BROTHER|BRUSSELS|BS|BT|BUDAPEST|BUGATTI|BUILD|BUILDERS|BUSINESS|BUY|BUZZ|BV|BW|BY|BZ|BZH|CA|CAB|CAFE|CAL|CALL|CALVINKLEIN|CAM|CAMERA|CAMP|CANCERRESEARCH|CANON|CAPETOWN|CAPITAL|CAPITALONE|CAR|CARAVAN|CARDS|CARE|CAREER|CAREERS|CARS|CARTIER|CASA|CASE|CASEIH|CASH|CASINO|CAT|CATERING|CATHOLIC|CBA|CBN|CBRE|CBS|CC|CD|CEB|CENTER|CEO|CERN|CF|CFA|CFD|CG|CH|CHANEL|CHANNEL|CHASE|CHAT|CHEAP|CHINTAI|CHRISTMAS|CHROME|CHRYSLER|CHURCH|CI|CIPRIANI|CIRCLE|CISCO|CITADEL|CITI|CITIC|CITY|CITYEATS|CK|CL|CLAIMS|CLEANING|CLICK|CLINIC|CLINIQUE|CLOTHING|CLOUD|CLUB|CLUBMED|CM|CN|CO|COACH|CODES|COFFEE|COLLEGE|COLOGNE|COM|COMCAST|COMMBANK|COMMUNITY|COMPANY|COMPARE|COMPUTER|COMSEC|CONDOS|CONSTRUCTION|CONSULTING|CONTACT|CONTRACTORS|COOKING|COOKINGCHANNEL|COOL|COOP|CORSICA|COUNTRY|COUPON|COUPONS|COURSES|CR|CREDIT|CREDITCARD|CREDITUNION|CRICKET|CROWN|CRS|CRUISE|CRUISES|CSC|CU|CUISINELLA|CV|CW|CX|CY|CYMRU|CYOU|CZ|DABUR|DAD|DANCE|DATA|DATE|DATING|DATSUN|DAY|DCLK|DDS|DE|DEAL|DEALER|DEALS|DEGREE|DELIVERY|DELL|DELOITTE|DELTA|DEMOCRAT|DENTAL|DENTIST|DESI|DESIGN|DEV|DHL|DIAMONDS|DIET|DIGITAL|DIRECT|DIRECTORY|DISCOUNT|DISCOVER|DISH|DIY|DJ|DK|DM|DNP|DO|DOCS|DOCTOR|DODGE|DOG|DOHA|DOMAINS|DOT|DOWNLOAD|DRIVE|DTV|DUBAI|DUCK|DUNLOP|DUNS|DUPONT|DURBAN|DVAG|DVR|DZ|EARTH|EAT|EC|ECO|EDEKA|EDU|EDUCATION|EE|EG|EMAIL|EMERCK|ENERGY|ENGINEER|ENGINEERING|ENTERPRISES|EPOST|EPSON|EQUIPMENT|ER|ERICSSON|ERNI|ES|ESQ|ESTATE|ESURANCE|ET|ETISALAT|EU|EUROVISION|EUS|EVENTS|EVERBANK|EXCHANGE|EXPERT|EXPOSED|EXPRESS|EXTRASPACE|FAGE|FAIL|FAIRWINDS|FAITH|FAMILY|FAN|FANS|FARM|FARMERS|FASHION|FAST|FEDEX|FEEDBACK|FERRARI|FERRERO|FI|FIAT|FIDELITY|FIDO|FILM|FINAL|FINANCE|FINANCIAL|FIRE|FIRESTONE|FIRMDALE|FISH|FISHING|FIT|FITNESS|FJ|FK|FLICKR|FLIGHTS|FLIR|FLORIST|FLOWERS|FLY|FM|FO|FOO|FOOD|FOODNETWORK|FOOTBALL|FORD|FOREX|FORSALE|FORUM|FOUNDATION|FOX|FR|FREE|FRESENIUS|FRL|FROGANS|FRONTDOOR|FRONTIER|FTR|FUJITSU|FUJIXEROX|FUN|FUND|FURNITURE|FUTBOL|FYI|GA|GAL|GALLERY|GALLO|GALLUP|GAME|GAMES|GAP|GARDEN|GB|GBIZ|GD|GDN|GE|GEA|GENT|GENTING|GEORGE|GF|GG|GGEE|GH|GI|GIFT|GIFTS|GIVES|GIVING|GL|GLADE|GLASS|GLE|GLOBAL|GLOBO|GM|GMAIL|GMBH|GMO|GMX|GN|GODADDY|GOLD|GOLDPOINT|GOLF|GOO|GOODHANDS|GOODYEAR|GOOG|GOOGLE|GOP|GOT|GOV|GP|GQ|GR|GRAINGER|GRAPHICS|GRATIS|GREEN|GRIPE|GROCERY|GROUP|GS|GT|GU|GUARDIAN|GUCCI|GUGE|GUIDE|GUITARS|GURU|GW|GY|HAIR|HAMBURG|HANGOUT|HAUS|HBO|HDFC|HDFCBANK|HEALTH|HEALTHCARE|HELP|HELSINKI|HERE|HERMES|HGTV|HIPHOP|HISAMITSU|HITACHI|HIV|HK|HKT|HM|HN|HOCKEY|HOLDINGS|HOLIDAY|HOMEDEPOT|HOMEGOODS|HOMES|HOMESENSE|HONDA|HONEYWELL|HORSE|HOSPITAL|HOST|HOSTING|HOT|HOTELES|HOTELS|HOTMAIL|HOUSE|HOW|HR|HSBC|HT|HU|HUGHES|HYATT|HYUNDAI|IBM|ICBC|ICE|ICU|ID|IE|IEEE|IFM|IKANO|IL|IM|IMAMAT|IMDB|IMMO|IMMOBILIEN|IN|INDUSTRIES|INFINITI|INFO|ING|INK|INSTITUTE|INSURANCE|INSURE|INT|INTEL|INTERNATIONAL|INTUIT|INVESTMENTS|IO|IPIRANGA|IQ|IR|IRISH|IS|ISELECT|ISMAILI|IST|ISTANBUL|IT|ITAU|ITV|IVECO|IWC|JAGUAR|JAVA|JCB|JCP|JE|JEEP|JETZT|JEWELRY|JIO|JLC|JLL|JM|JMP|JNJ|JO|JOBS|JOBURG|JOT|JOY|JP|JPMORGAN|JPRS|JUEGOS|JUNIPER|KAUFEN|KDDI|KE|KERRYHOTELS|KERRYLOGISTICS|KERRYPROPERTIES|KFH|KG|KH|KI|KIA|KIM|KINDER|KINDLE|KITCHEN|KIWI|KM|KN|KOELN|KOMATSU|KOSHER|KP|KPMG|KPN|KR|KRD|KRED|KUOKGROUP|KW|KY|KYOTO|KZ|LA|LACAIXA|LADBROKES|LAMBORGHINI|LAMER|LANCASTER|LANCIA|LANCOME|LAND|LANDROVER|LANXESS|LASALLE|LAT|LATINO|LATROBE|LAW|LAWYER|LB|LC|LDS|LEASE|LECLERC|LEFRAK|LEGAL|LEGO|LEXUS|LGBT|LI|LIAISON|LIDL|LIFE|LIFEINSURANCE|LIFESTYLE|LIGHTING|LIKE|LILLY|LIMITED|LIMO|LINCOLN|LINDE|LINK|LIPSY|LIVE|LIVING|LIXIL|LK|LOAN|LOANS|LOCKER|LOCUS|LOFT|LOL|LONDON|LOTTE|LOTTO|LOVE|LPL|LPLFINANCIAL|LR|LS|LT|LTD|LTDA|LU|LUNDBECK|LUPIN|LUXE|LUXURY|LV|LY|MA|MACYS|MADRID|MAIF|MAISON|MAKEUP|MAN|MANAGEMENT|MANGO|MAP|MARKET|MARKETING|MARKETS|MARRIOTT|MARSHALLS|MASERATI|MATTEL|MBA|MC|MCKINSEY|MD|ME|MED|MEDIA|MEET|MELBOURNE|MEME|MEMORIAL|MEN|MENU|MEO|MERCKMSD|METLIFE|MG|MH|MIAMI|MICROSOFT|MIL|MINI|MINT|MIT|MITSUBISHI|MK|ML|MLB|MLS|MM|MMA|MN|MO|MOBI|MOBILE|MOBILY|MODA|MOE|MOI|MOM|MONASH|MONEY|MONSTER|MOPAR|MORMON|MORTGAGE|MOSCOW|MOTO|MOTORCYCLES|MOV|MOVIE|MOVISTAR|MP|MQ|MR|MS|MSD|MT|MTN|MTR|MU|MUSEUM|MUTUAL|MV|MW|MX|MY|MZ|NA|NAB|NADEX|NAGOYA|NAME|NATIONWIDE|NATURA|NAVY|NBA|NC|NE|NEC|NET|NETBANK|NETFLIX|NETWORK|NEUSTAR|NEW|NEWHOLLAND|NEWS|NEXT|NEXTDIRECT|NEXUS|NF|NFL|NG|NGO|NHK|NI|NICO|NIKE|NIKON|NINJA|NISSAN|NISSAY|NL|NO|NOKIA|NORTHWESTERNMUTUAL|NORTON|NOW|NOWRUZ|NOWTV|NP|NR|NRA|NRW|NTT|NU|NYC|NZ|OBI|OBSERVER|OFF|OFFICE|OKINAWA|OLAYAN|OLAYANGROUP|OLDNAVY|OLLO|OM|OMEGA|ONE|ONG|ONL|ONLINE|ONYOURSIDE|OOO|OPEN|ORACLE|ORANGE|ORG|ORGANIC|ORIGINS|OSAKA|OTSUKA|OTT|OVH|PA|PAGE|PANASONIC|PANERAI|PARIS|PARS|PARTNERS|PARTS|PARTY|PASSAGENS|PAY|PCCW|PE|PET|PF|PFIZER|PG|PH|PHARMACY|PHD|PHILIPS|PHONE|PHOTO|PHOTOGRAPHY|PHOTOS|PHYSIO|PIAGET|PICS|PICTET|PICTURES|PID|PIN|PING|PINK|PIONEER|PIZZA|PK|PL|PLACE|PLAY|PLAYSTATION|PLUMBING|PLUS|PM|PN|PNC|POHL|POKER|POLITIE|PORN|POST|PR|PRAMERICA|PRAXI|PRESS|PRIME|PRO|PROD|PRODUCTIONS|PROF|PROGRESSIVE|PROMO|PROPERTIES|PROPERTY|PROTECTION|PRU|PRUDENTIAL|PS|PT|PUB|PW|PWC|PY|QA|QPON|QUEBEC|QUEST|QVC|RACING|RADIO|RAID|RE|READ|REALESTATE|REALTOR|REALTY|RECIPES|RED|REDSTONE|REDUMBRELLA|REHAB|REISE|REISEN|REIT|RELIANCE|REN|RENT|RENTALS|REPAIR|REPORT|REPUBLICAN|REST|RESTAURANT|REVIEW|REVIEWS|REXROTH|RICH|RICHARDLI|RICOH|RIGHTATHOME|RIL|RIO|RIP|RMIT|RO|ROCHER|ROCKS|RODEO|ROGERS|ROOM|RS|RSVP|RU|RUGBY|RUHR|RUN|RW|RWE|RYUKYU|SA|SAARLAND|SAFE|SAFETY|SAKURA|SALE|SALON|SAMSCLUB|SAMSUNG|SANDVIK|SANDVIKCOROMANT|SANOFI|SAP|SAPO|SARL|SAS|SAVE|SAXO|SB|SBI|SBS|SC|SCA|SCB|SCHAEFFLER|SCHMIDT|SCHOLARSHIPS|SCHOOL|SCHULE|SCHWARZ|SCIENCE|SCJOHNSON|SCOR|SCOT|SD|SE|SEARCH|SEAT|SECURE|SECURITY|SEEK|SELECT|SENER|SERVICES|SES|SEVEN|SEW|SEX|SEXY|SFR|SG|SH|SHANGRILA|SHARP|SHAW|SHELL|SHIA|SHIKSHA|SHOES|SHOP|SHOPPING|SHOUJI|SHOW|SHOWTIME|SHRIRAM|SI|SILK|SINA|SINGLES|SITE|SJ|SK|SKI|SKIN|SKY|SKYPE|SL|SLING|SM|SMART|SMILE|SN|SNCF|SO|SOCCER|SOCIAL|SOFTBANK|SOFTWARE|SOHU|SOLAR|SOLUTIONS|SONG|SONY|SOY|SPACE|SPIEGEL|SPOT|SPREADBETTING|SR|SRL|SRT|ST|STADA|STAPLES|STAR|STARHUB|STATEBANK|STATEFARM|STATOIL|STC|STCGROUP|STOCKHOLM|STORAGE|STORE|STREAM|STUDIO|STUDY|STYLE|SU|SUCKS|SUPPLIES|SUPPLY|SUPPORT|SURF|SURGERY|SUZUKI|SV|SWATCH|SWIFTCOVER|SWISS|SX|SY|SYDNEY|SYMANTEC|SYSTEMS|SZ|TAB|TAIPEI|TALK|TAOBAO|TARGET|TATAMOTORS|TATAR|TATTOO|TAX|TAXI|TC|TCI|TD|TDK|TEAM|TECH|TECHNOLOGY|TEL|TELECITY|TELEFONICA|TEMASEK|TENNIS|TEVA|TF|TG|TH|THD|THEATER|THEATRE|TIAA|TICKETS|TIENDA|TIFFANY|TIPS|TIRES|TIROL|TJ|TJMAXX|TJX|TK|TKMAXX|TL|TM|TMALL|TN|TO|TODAY|TOKYO|TOOLS|TOP|TORAY|TOSHIBA|TOTAL|TOURS|TOWN|TOYOTA|TOYS|TR|TRADE|TRADING|TRAINING|TRAVEL|TRAVELCHANNEL|TRAVELERS|TRAVELERSINSURANCE|TRUST|TRV|TT|TUBE|TUI|TUNES|TUSHU|TV|TVS|TW|TZ|UA|UBANK|UBS|UCONNECT|UG|UK|UNICOM|UNIVERSITY|UNO|UOL|UPS|US|UY|UZ|VA|VACATIONS|VANA|VANGUARD|VC|VE|VEGAS|VENTURES|VERISIGN|VERSICHERUNG|VET|VG|VI|VIAJES|VIDEO|VIG|VIKING|VILLAS|VIN|VIP|VIRGIN|VISA|VISION|VISTA|VISTAPRINT|VIVA|VIVO|VLAANDEREN|VN|VODKA|VOLKSWAGEN|VOLVO|VOTE|VOTING|VOTO|VOYAGE|VU|VUELOS|WALES|WALMART|WALTER|WANG|WANGGOU|WARMAN|WATCH|WATCHES|WEATHER|WEATHERCHANNEL|WEBCAM|WEBER|WEBSITE|WED|WEDDING|WEIBO|WEIR|WF|WHOSWHO|WIEN|WIKI|WILLIAMHILL|WIN|WINDOWS|WINE|WINNERS|WME|WOLTERSKLUWER|WOODSIDE|WORK|WORKS|WORLD|WOW|WS|WTC|WTF|XBOX|XEROX|XFINITY|XIHUAN|XIN|XN--11B4C3D|XN--1CK2E1B|XN--1QQW23A|XN--2SCRJ9C|XN--30RR7Y|XN--3BST00M|XN--3DS443G|XN--3E0B707E|XN--3HCRJ9C|XN--3OQ18VL8PN36A|XN--3PXU8K|XN--42C2D9A|XN--45BR5CYL|XN--45BRJ9C|XN--45Q11C|XN--4GBRIM|XN--54B7FTA0CC|XN--55QW42G|XN--55QX5D|XN--5SU34J936BGSG|XN--5TZM5G|XN--6FRZ82G|XN--6QQ986B3XL|XN--80ADXHKS|XN--80AO21A|XN--80AQECDR1A|XN--80ASEHDB|XN--80ASWG|XN--8Y0A063A|XN--90A3AC|XN--90AE|XN--90AIS|XN--9DBQ2A|XN--9ET52U|XN--9KRT00A|XN--B4W605FERD|XN--BCK1B9A5DRE4C|XN--C1AVG|XN--C2BR7G|XN--CCK2B3B|XN--CG4BKI|XN--CLCHC0EA0B2G2A9GCD|XN--CZR694B|XN--CZRS0T|XN--CZRU2D|XN--D1ACJ3B|XN--D1ALF|XN--E1A4C|XN--ECKVDTC9D|XN--EFVY88H|XN--ESTV75G|XN--FCT429K|XN--FHBEI|XN--FIQ228C5HS|XN--FIQ64B|XN--FIQS8S|XN--FIQZ9S|XN--FJQ720A|XN--FLW351E|XN--FPCRJ9C3D|XN--FZC2C9E2C|XN--FZYS8D69UVGM|XN--G2XX48C|XN--GCKR3F0F|XN--GECRJ9C|XN--GK3AT1E|XN--H2BREG3EVE|XN--H2BRJ9C|XN--H2BRJ9C8C|XN--HXT814E|XN--I1B6B1A6A2E|XN--IMR513N|XN--IO0A7I|XN--J1AEF|XN--J1AMH|XN--J6W193G|XN--JLQ61U9W7B|XN--JVR189M|XN--KCRX77D1X4A|XN--KPRW13D|XN--KPRY57D|XN--KPU716F|XN--KPUT3I|XN--L1ACC|XN--LGBBAT1AD8J|XN--MGB9AWBF|XN--MGBA3A3EJT|XN--MGBA3A4F16A|XN--MGBA7C0BBN0A|XN--MGBAAKC7DVF|XN--MGBAAM7A8H|XN--MGBAB2BD|XN--MGBAI9AZGQP6J|XN--MGBAYH7GPA|XN--MGBB9FBPOB|XN--MGBBH1A|XN--MGBBH1A71E|XN--MGBC0A9AZCG|XN--MGBCA7DZDO|XN--MGBERP4A5D4AR|XN--MGBGU82A|XN--MGBI4ECEXP|XN--MGBPL2FH|XN--MGBT3DHD|XN--MGBTX2B|XN--MGBX4CD0AB|XN--MIX891F|XN--MK1BU44C|XN--MXTQ1M|XN--NGBC5AZD|XN--NGBE9E0A|XN--NGBRX|XN--NODE|XN--NQV7F|XN--NQV7FS00EMA|XN--NYQY26A|XN--O3CW4H|XN--OGBPF8FL|XN--P1ACF|XN--P1AI|XN--PBT977C|XN--PGBS0DH|XN--PSSY2U|XN--Q9JYB4C|XN--QCKA1PMC|XN--QXAM|XN--RHQV96G|XN--ROVU88B|XN--RVC1E0AM3E|XN--S9BRJ9C|XN--SES554G|XN--T60B56A|XN--TCKWE|XN--TIQ49XQYJ|XN--UNUP4Y|XN--VERMGENSBERATER-CTB|XN--VERMGENSBERATUNG-PWB|XN--VHQUV|XN--VUQ861B|XN--W4R85EL8FHU5DNRA|XN--W4RS40L|XN--WGBH1C|XN--WGBL6A|XN--XHQ521B|XN--XKC2AL3HYE2A|XN--XKC2DL3A5EE0H|XN--Y9A3AQ|XN--YFRO4I67O|XN--YGBI2AMMX|XN--ZFR164B|XPERIA|XXX|XYZ|YACHTS|YAHOO|YAMAXUN|YANDEX|YE|YODOBASHI|YOGA|YOKOHAMA|YOU|YOUTUBE|YT|YUN|ZA|ZAPPOS|ZARA|ZERO|ZIP|ZIPPO|ZM|ZONE|ZUERICH|ZW)$/i;\n\nexport const withCORS = (headers, request) => {\n  headers[\"access-control-allow-origin\"] = \"*\";\n\n\n  var corsMaxAge = request.corsAnywhereRequestState.corsMaxAge;\n  if (request.method === \"OPTIONS\" && corsMaxAge) {\n    headers[\"access-control-max-age\"] = corsMaxAge;\n  }\n  if (request.headers[\"access-control-request-method\"]) {\n    headers[\"access-control-allow-methods\"] =\n      request.headers[\"access-control-request-method\"];\n    delete request.headers[\"access-control-request-method\"];\n  }\n  if (request.headers[\"access-control-request-headers\"]) {\n    headers[\"access-control-allow-headers\"] =\n      request.headers[\"access-control-request-headers\"];\n    delete request.headers[\"access-control-request-headers\"];\n  }\n\n  headers[\"access-control-expose-headers\"] = Object.keys(headers).join(\",\");\n\n  return headers;\n};\n\nexport const isValidHostName = hostname => {\n  return !!(\n    regexp.test(hostname) ||\n    net.isIPv4(hostname) ||\n    net.isIPv6(hostname)\n  );\n};\n\nexport const parseURL = req_url => {\n  var match = req_url.match(\n    /^(?:(https?:)?\\/\\/)?(([^\\/?]+?)(?::(\\d{0,5})(?=[\\/?]|$))?)([\\/?][\\S\\s]*|$)/i\n  );\n  if (!match) {\n    return null;\n  }\n  if (!match[1]) {\n    if (/^https?:/i.test(req_url)) {\n      return null;\n    }\n    if (req_url.lastIndexOf(\"//\", 0) === -1) {\n      req_url = \"//\" + req_url;\n    }\n    req_url = (match[4] === \"443\" ? \"https:\" : \"http:\") + req_url;\n  }\n  var parsed = url.parse(req_url);\n  if (!parsed.hostname) {\n    return null;\n  }\n  return parsed;\n};\n"
  },
  {
    "path": "services/lagacy/package.json",
    "content": "{\n    \"name\": \"cors.bridged.cc\",\n    \"description\": \"free cors service for everyone\",\n    \"version\": \"0.1.0\",\n    \"scripts\": {\n        \"deploy:prod\": \"sls deploy\",\n        \"dev\": \"sls offline start\"\n    },\n    \"dependencies\": {\n        \"aws-serverless-express\": \"^3.4.0\",\n        \"dynamoose\": \"^2.7.3\",\n        \"express\": \"^4.17.1\",\n        \"express-useragent\": \"^1.0.15\",\n        \"http-proxy\": \"^1.18.1\",\n        \"moment\": \"^2.29.1\",\n        \"nanoid\": \"^3.1.23\",\n        \"proxy-from-env\": \"^1.1.0\",\n        \"response-time\": \"^2.3.2\",\n        \"serverless-http\": \"^2.7.0\"\n    },\n    \"devDependencies\": {\n        \"@hewmen/serverless-plugin-typescript\": \"^1.1.17\",\n        \"@types/aws-lambda\": \"^8.10.71\",\n        \"@types/express\": \"^4.17.11\",\n        \"@types/http-proxy\": \"^1.17.5\",\n        \"@types/node\": \"^14.14.25\",\n        \"@types/proxy-from-env\": \"^1.0.1\",\n        \"@types/response-time\": \"^2.3.4\",\n        \"serverless\": \"^2.22.0\",\n        \"serverless-domain-manager\": \"^5.1.0\",\n        \"serverless-dynamodb-local\": \"^0.2.39\",\n        \"serverless-offline\": \"^6.8.0\",\n        \"serverless-plugin-dynamo-autoscaling\": \"^1.0.1\",\n        \"serverless-plugin-optimize\": \"^4.1.4-rc.1\",\n        \"typescript\": \"^4.1.3\"\n    }\n}"
  },
  {
    "path": "services/lagacy/serverless.yml",
    "content": "# Welcome to serverless. Read the docs\n# https://serverless.com/framework/docs/\nframeworkVersion: \"2\"\nservice: cors\nuseDotenv: true\ncustom:\n  customDomain:\n    # legacy. won't support in the future\n    domainName: cors.bridged.cc\n    basePath: \"\"\n    stage: ${self:provider.stage}\n    createRoute53Record: true\n    # - http:\n    #   domainName: cors.grida.cc\n    #   basePath: \"\"\n    #   stage: \"production\"\n    #   createRoute53Record: true\n\n  serverless-offline:\n    httpPort: 4012\n\nprovider:\n  name: aws\n  memorySize: 128\n  runtime: nodejs12.x\n  apiGateway:\n    shouldStartNameWithService: true\n    binaryMediaTypes:\n      - \"*/*\"\n    endpointType: regional\n  region: us-west-1\n  environment:\n    AWS_NODEJS_CONNECTION_REUSE_ENABLED: \"1\"\n    DYNAMODB_TABLE_USAGE_LOG: \"${self:service}-usage-log-${opt:stage, self:provider.stage}\"\n  iamRoleStatements:\n    - Effect: Allow\n      Action:\n        - dynamodb:Query\n        - dynamodb:Scan\n        - dynamodb:GetItem\n        - dynamodb:PutItem\n        - dynamodb:UpdateItem\n        - dynamodb:DeleteItem\n        - dynamodb:DescribeTable\n      Resource: \"arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE_USAGE_LOG}\"\n\nresources:\n  Resources:\n    usageLogTable:\n      Type: AWS::DynamoDB::Table\n      Properties:\n        TableName: \"${self:provider.environment.DYNAMODB_TABLE_USAGE_LOG}\"\n        KeySchema:\n          - AttributeName: id\n            KeyType: HASH\n        AttributeDefinitions:\n          - AttributeName: id\n            AttributeType: S\n          - AttributeName: app\n            AttributeType: S\n        GlobalSecondaryIndexes:\n          - IndexName: appIndex\n            KeySchema:\n              - AttributeName: app\n                KeyType: HASH\n            Projection:\n              ProjectionType: \"ALL\"\n            ProvisionedThroughput:\n              ReadCapacityUnits: 10\n              WriteCapacityUnits: 10 # no write is required\n        ProvisionedThroughput:\n          ReadCapacityUnits: 10 # no read is required\n          WriteCapacityUnits: 10\n\n# The `functions` block defines what code to deploy\nfunctions:\n  api:\n    handler: src/index.handler\n    maximumEventAge: 60\n    maximumRetryAttempts: 0\n    timeout: 12\n    events:\n      - http:\n          path: /{proxy+}\n          method: any\n      - http:\n          path: /\n          method: get\n\nplugins:\n  - \"@hewmen/serverless-plugin-typescript\"\n  - serverless-plugin-optimize\n  - serverless-dynamodb-local\n  - serverless-offline\n  - serverless-domain-manager\n"
  },
  {
    "path": "services/lagacy/src/_util/size.ts",
    "content": "export const MB = 1048576;\n\n/**\n * 1 seconds in ms\n */\nexport const SEC = 1000;\n"
  },
  {
    "path": "services/lagacy/src/app.ts",
    "content": "import * as express from \"express\";\nimport * as useragent from \"express-useragent\";\nimport * as corsProxy from \"../lib/cors\";\nimport * as responsetime from \"response-time\";\n\nimport { logRequest } from \"./usage\";\nimport { blaklistoriginlimit, payloadlimit } from \"./limit\";\n// import { unauthorizedAppBlocking } from \"./auth\";\n\nconst app = express();\n\nconst cors_proxy = corsProxy.createServer({\n  // https://github.com/Rob--W/cors-anywhere/issues/39\n  requireHeader: [\"origin\", \"x-requested-with\"],\n  // requireHeader: [],\n  removeHeaders: [\n    \"cookie\",\n    \"cookie2\",\n    \"x-request-start\",\n    \"x-request-id\",\n    \"via\",\n    \"connect-time\",\n    \"total-route-time\",\n  ],\n  redirectSameOrigin: true,\n  httpProxyOptions: {\n    xfwd: false,\n  },\n});\n\napp.get(\"/\", function (req, res) {\n  res.redirect(\"https://app.cors.bridged.cc/\");\n});\n\napp.use(blaklistoriginlimit); // 1\napp.use(payloadlimit); // 2\n// disabling due to cors.sh migration\n// app.use(unauthorizedAppBlocking); // 3\n\napp.use(\n  responsetime({\n    suffix: false,\n  })\n);\napp.use(useragent.express());\n\n// -- execution order matters --\n// (1)\napp.use((req, res, next) => {\n  if (res.headersSent) {\n    return;\n  }\n\n  try {\n    cors_proxy.emit(\"request\", req, res);\n    next();\n  } catch (_) {}\n});\n\n// (2)\n/**\n * after response middleware\n */\napp.use((req, res) => {\n  res.on(\"finish\", () => {\n    logRequest(req, res);\n  });\n});\n// -- execution order matters --\n\n/**\n * global error handler\n */\napp.use(((err, req, res, next) => {\n  if (res.headersSent) {\n    return;\n  }\n  try {\n    return res.status(500).json({\n      message: \"Internal Server Error\",\n      error: err,\n      issue: \"https://github.com/bridgedxyz/base/issues\",\n    });\n  } catch (_) {}\n}) as express.ErrorRequestHandler);\n\nexport { app };\n"
  },
  {
    "path": "services/lagacy/src/auth/_tmp_static_api_keys/.gitignore",
    "content": "# manuall managed credentials (temporal)\nkeys.js"
  },
  {
    "path": "services/lagacy/src/auth/_tmp_static_api_keys/README.md",
    "content": "# Temporal manually managed api key store\n"
  },
  {
    "path": "services/lagacy/src/auth/_tmp_static_api_keys/index.js",
    "content": "export { default as keys } from \"./keys\";\n"
  },
  {
    "path": "services/lagacy/src/auth/index.ts",
    "content": "export { unauthorizedAppBlocking } from \"./static-account-api-key-auth\";\n"
  },
  {
    "path": "services/lagacy/src/auth/static-account-api-key-auth.ts",
    "content": "import * as express from \"express\";\nimport { keys } from \"./_tmp_static_api_keys\";\n/**\n * cors.grida.cc static api key header\n */\nconst STATIC_CORS_ACCOUNT_API_KEY_HEADER = \"x-cors-grida-api-key\";\n\nconst nokey401UnAuthorized = () => {\n  return \"https://bit.ly/2UnZSA8\";\n  // return {\n  //   message: `CORS Proxy request from origin \"${origin}\" is not allowed. Request is unauthorized`,\n  //   issue: \"https://github.com/bridgedxyz/base/issues/23\",\n  // };\n};\n\nexport const unauthorizedAppBlocking = (\n  req: express.Request,\n  res: express.Response,\n  next\n) => {\n  // skip api key check for preflight requests\n  if (req.method == \"OPTIONS\" || req.method == \"HEAD\") {\n    next();\n  }\n  const apikey: string = req.headers[\n    STATIC_CORS_ACCOUNT_API_KEY_HEADER\n  ] as string;\n  if (apikey && validate_api_key(apikey)) {\n    next();\n  } else {\n    res.status(401).send(nokey401UnAuthorized());\n    return;\n  }\n};\n\nfunction validate_api_key(apikey: string) {\n  if (!apikey || apikey == \"\") {\n    return false;\n  }\n  const found = (keys as string[]).find(s => s === apikey);\n  if (found) {\n    return true;\n  }\n  return false;\n}\n"
  },
  {
    "path": "services/lagacy/src/index.ts",
    "content": "import * as serverlessExpress from \"aws-serverless-express\";\nimport { APIGatewayProxyHandler } from \"aws-lambda\";\n\nimport { app } from \"./app\";\n\nconst binaryMimeTypes = [\n  '*/*'\n\n  // https://github.com/vendia/serverless-express/blob/master/examples/basic-starter/lambda.js\n  // 'application/javascript',\n  // 'application/json',\n  // 'application/octet-stream',\n  // 'application/xml',\n  // 'font/eot',\n  // 'font/opentype',\n  // 'font/otf',\n  // 'image/jpeg',\n  // 'image/png',\n  // 'image/svg+xml',\n  // 'text/comma-separated-values',\n  // 'text/css',\n  // 'text/html',\n  // 'text/javascript',\n  // 'text/plain',\n  // 'text/text',\n  // 'text/xml'\n]\n\nconst server = serverlessExpress.createServer(app, null, binaryMimeTypes)\n\nexport const handler: APIGatewayProxyHandler = (event, context) => {\n  serverlessExpress.proxy(server, event, context);\n};"
  },
  {
    "path": "services/lagacy/src/limit/README.md",
    "content": "# Limitation handler"
  },
  {
    "path": "services/lagacy/src/limit/blacklist-origin.ts",
    "content": "import * as express from \"express\";\n\n/**\n * explicitly black listed origins. these are not registered to use base.\n */\nconst blacklisted_origin: string[] = [\n  \"REJECTME\",\n  // WAITING FOR SERVICE PROVIDER'S ACTION\n  \"titronline.org\",\n  \"titr.online\",\n  \"showsport.xyz\",\n  \"cdn14.esp-cdn.xyz\",\n  \"digi-hdsport.com\",\n  \"siunus.github.io\",\n  // ILLEGAL OR AUDULT WEBSITE (PERMINANTLY BLOCKED)\n  \"twerktvnaija.com\",\n];\n\nconst blacked401UnAuthorized = (origin: string) => {\n  return \"https://bit.ly/2UnZSA8\";\n  // return {\n  //   message: `CORS Proxy request from origin \"${origin}\" is not allowed. Request is unauthorized`,\n  //   issue: \"https://github.com/bridgedxyz/base/issues/23\",\n  // };\n};\n\nexport const blaklistoriginlimit = (\n  req: express.Request,\n  res: express.Response,\n  next\n) => {\n  const origin = req.headers[\"origin\"];\n\n  if (origin) {\n    if (blacked(origin)) {\n      res.status(401).send(blacked401UnAuthorized(origin));\n      return;\n    }\n  }\n  next();\n};\n\nfunction blacked(origin: string): boolean {\n  // patterns\n  // 1. www.<origin>\n  // 2. http//<origin>\n  // 3. https//<origin>\n  // 4. http://<origin>\n  // 5. ....\n  try {\n    const u = new URL(origin);\n    return blacklisted_origin.some(o => {\n      return u.hostname == o || u.hostname == \"www.\" + o;\n    });\n  } catch (_) {\n    // we cannot handle url that is invalid (need better logic for this)\n    return false;\n  }\n}\n"
  },
  {
    "path": "services/lagacy/src/limit/index.ts",
    "content": "export * from \"./payload-limit\";\nexport * from \"./blacklist-origin\";\n"
  },
  {
    "path": "services/lagacy/src/limit/payload-limit.ts",
    "content": "import * as https from \"https\";\nimport * as http from \"http\";\nimport { MB } from \"../_util/size\";\n\n/**\n * this is to save data transfer cost. And basically we should not use CORS Proxy to load large files.\n *\n * https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html\n */\nconst MAX_TARGET_RESOURCE_MB = 2; // 6mb is max supported by api gateway. (supports up to 2mb on proxy)\n\nexport const payloadlimit = (req, res, next) => {\n  const requrl = req.originalUrl.substring(1);\n\n  const agent = requrl.startsWith(\"https:\") ? https : http;\n  agent\n    .request(requrl, { method: \"HEAD\" }, _resp => {\n      const len = Number(_resp.headers[\"content-length\"]);\n      if (len && len > MB * MAX_TARGET_RESOURCE_MB) {\n        // reject if data larger than 30mb.\n        res.status(413).send({\n          message: `Requested resource exceeds ${MAX_TARGET_RESOURCE_MB}mb`,\n          issue: \"https://github.com/bridgedxyz/base/issues/23\",\n        });\n      } else {\n        // if content-length is undefined or less than 30mb, procceed.\n        next();\n      }\n    })\n    .on(\"error\", err => {\n      // ignore error for this\n      // target, which is anonymous, might not support HEAD request.\n      next();\n    })\n    .end();\n};\n"
  },
  {
    "path": "services/lagacy/src/limit/unknown-host-limit.ts",
    "content": "import { MB, SEC } from \"../_util/size\";\n\nexport const unknownhostlimit = (req, res, next) => {\n  const limit = <SingleProxyRequestLimitPolicy>{\n    timeout: 6 * SEC,\n    size: 0.1 * MB,\n  };\n};\n\nfunction known(url: string): boolean {\n  try {\n    const u = new URL(url);\n    if (isIpAddress(u.hostname)) {\n      return false;\n    }\n    return true;\n  } catch (_) {\n    return false;\n  }\n}\n\n/**\n * pure host name e.g. 1.1.1.1 or www.grida.co\n * @param hostname\n * @returns\n */\nfunction isIpAddress(hostname: string): boolean {\n  return hostname.match(/[a-z]/i) ? false : true;\n}\n\ninterface SingleProxyRequestLimitPolicy {\n  timeout: number; // max time in ms\n  size: number; // max size in bytes\n}\n"
  },
  {
    "path": "services/lagacy/src/usage/README.md",
    "content": "# Usage Logging\n\n## Data we collect\n\n- client ip (for managing x-rate-limit)\n- client ua (for malicious usage preventation)\n- request url information (only host) (for understanding api usage)\n    - we do not collect request query\n    - we do not collect request body\n    - we do not collect response body\n- response result (for collecting success rate) (collection as http status code)\n- duration (for tracking x-rate-limit for execution time)\n- payload (for tracking x-rate-limit for data transfer)\n- app (for identifying the request) (this is determined by api key you are using. pretty obvious)"
  },
  {
    "path": "services/lagacy/src/usage/index.ts",
    "content": "import * as express from \"express\";\nimport * as dynamoose from \"dynamoose\";\nimport * as https from \"https\";\nconst agent = new https.Agent({\n  keepAlive: true,\n  rejectUnauthorized: true,\n  maxSockets: 1000,\n});\n\ndynamoose.aws.sdk.config.update({\n  httpOptions: {\n    agent: agent,\n  },\n});\n\nimport {\n  CorsProxyApiRequest,\n  CorsProxyApiRequestLog,\n  CorsRequestLogModel,\n} from \"./model\";\nimport { nanoid } from \"nanoid\";\n/**\n *\n * @param req\n * @param res\n */\nexport async function logRequest(req: express.Request, res: express.Response) {\n  // do not log the request if client error. (413 or 401 or 400)\n  if (500 > res.statusCode && res.statusCode >= 400) {\n    return;\n  }\n  const ip = (req.headers[\"x-forwarded-for\"] ||\n    req.socket.remoteAddress) as string;\n  const timestamp = new Date();\n  const origin = req.headers[\"origin\"] || undefined;\n  //@ts-ignore (useragent is provided by above useragent.express())\n  const _uaobj = req.useragent;\n  const ua = _uaobj.source; // gives the raw ua string\n  const url = res.get(\"x-request-url\");\n  const payload = Number(\n    (() => {\n      const _cl = res.get(\"content-length\");\n      return _cl ? _cl : 0;\n    })()\n  );\n  const duration = Number(\n    (() => {\n      const _rt = res.get(\"x-response-time\");\n      return _rt ? _rt : 0;\n    })()\n  );\n  await log({\n    ip: ip,\n    origin: origin,\n    ua: ua,\n    duration: duration,\n    size: payload,\n    at: timestamp,\n    target: url,\n    app: \"anonymous\",\n  });\n}\n\nasync function log(request: CorsProxyApiRequest) {\n  // console.log(\"request\", request);\n  const id = nanoid();\n  const billedduration = Math.ceil(request.duration / 100) * 100; // billed duration is stepped by 100ms\n  try {\n    const payload = new CorsRequestLogModel(<CorsProxyApiRequestLog>{\n      id: id,\n      billed_duration: billedduration,\n      ...request,\n    });\n\n    await payload.save();\n  } catch (_) {\n    // do nothing\n  }\n}\n"
  },
  {
    "path": "services/lagacy/src/usage/model.ts",
    "content": "import * as dynamoose from \"dynamoose\";\nimport { nanoid } from \"nanoid\";\n\nexport type AppId = string | \"anonymous\" | \"official-demo\";\n\nexport interface CorsProxyApiRequest {\n  /**\n   * ip address of request client (could be server / app / web)\n   */\n  ip?: string;\n  /**\n   * compressed / raw user agent data of the request\n   */\n  ua?: string;\n\n  /**\n   * request origin from request headers\n   */\n  origin?: string;\n\n  /**\n   * target resource url\n   */\n  target: string;\n  /**\n   * duration in ms\n   */\n  duration: number;\n  /**\n   * data payload\n   */\n  size: number;\n\n  /**\n   * request timestamp\n   */\n  at: Date;\n\n  /**\n   * the user/requester app\n   */\n  app: AppId;\n}\n\nexport interface CorsProxyApiRequestLog extends CorsProxyApiRequest {\n  /**\n   * unique id of the request\n   */\n  id: string;\n\n  /**\n   * billing duration in ms - ceils with 100ms\n   */\n  billed_duration: number;\n}\n\nexport const CorsRequestLogSchema = new dynamoose.Schema(\n  {\n    id: {\n      type: String,\n      required: true,\n      default: () => nanoid(),\n    },\n    app: {\n      type: String,\n      required: true,\n      index: {\n        name: \"appIndex\",\n      },\n    },\n    ua: {\n      type: String,\n      required: false,\n    },\n    at: {\n      type: Date,\n      required: true,\n    },\n    size: {\n      type: Number,\n      required: true,\n    },\n    ip: {\n      type: String,\n      required: false,\n    },\n    target: {\n      type: String,\n      required: true,\n    },\n    duration: {\n      type: Number,\n      required: true,\n    },\n    billed_duration: {\n      type: Number,\n      required: true,\n    },\n  },\n  {\n    saveUnknown: true,\n  }\n);\n\nconst CORS_REQUEST_LOG_TABLE_NAME = process.env\n  .DYNAMODB_TABLE_USAGE_LOG as string;\n\nexport const CorsRequestLogModel = dynamoose.model(\n  CORS_REQUEST_LOG_TABLE_NAME,\n  CorsRequestLogSchema,\n  {\n    create: true,\n  }\n);\n"
  },
  {
    "path": "services/lagacy/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"preserveConstEnums\": true,\n    \"strictNullChecks\": true,\n    \"sourceMap\": true,\n    \"allowJs\": true,\n    \"target\": \"es5\",\n    \"outDir\": \".build\",\n    \"moduleResolution\": \"node\",\n    \"lib\": [\"es2015\"],\n    \"rootDir\": \"./\"\n  }\n}\n"
  },
  {
    "path": "services/mail.cors.sh/onboarding.mjml",
    "content": "<mjml>\n  <mj-head>\n    <mj-font name=\"Source Code Pro\" href=\"https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;500;600;700&display=swap\" />\n    <mj-attributes>\n      <mj-text font-size=\"14px\" align=\"left\" color=\"black\" font-family=\"'Source Code Pro', monospace\" />\n      <mj-section background-color=\"#fff\" />\n      <mj-social-element icon-size=\"24px\" />\n      <mj-style inline=\"inline\">\n        .break {\n          word-break: break-all !important;\n        }\n      </mj-style>\n    </mj-attributes>\n  </mj-head>\n  <mj-body background-color=\"#eee\">\n    <mj-hero padding=\"24px 0px 0px 0px\">\n      <mj-text color=\"black\">\n        <h1>\n          Your API key for\n          </br>\n          proxy.cors.sh\n        </h1>\n      </mj-text>\n    </mj-hero>\n    <mj-section>\n      <mj-column>\n        <mj-text font-weight=\"bold\">\n          <h3>Your temporary API key is..</h3>\n        </mj-text>\n        <mj-text>\n          <code class=\"break\">\n            <p style=\"background-color: black; padding:12px; color:white;\">{{CODE}}</p>\n          </code>\n        </mj-text>\n        <mj-text>Copy &amp; Paste this api key to your api call (XHR) header.</mj-text>\n        <mj-text font-weight=\"bold\">\n          <h3>Example usage</h3>\n        </mj-text>\n        <mj-text>You can use the api key like below. For more example, visit <a href=\"https://cors.sh/#usage\">https://cors.sh/#usage</a>\n        </mj-text>\n        <mj-text>\n          <code css-class=\"overflow-scroll\">\n            <div style=\"background-color: #F4F2F0;overflow:auto;width:auto;padding:12px;\">\n              <div style=\"margin: 0; line-height: 125%;\">\n                <span style=\"color: #CC556A\">fetch</span><span style=\"color: #999999\">(</span><span style=\"color: #71982C\">&#39;https://proxy.cors.sh/https://acme.com&#39;</span><span style=\"color: #999999\">,</span> <span style=\"color: #999999\">{</span><br />\n                &nbsp;&nbsp;<span style=\"color: #CC556A\">headers</span><span style=\"color: #937042\">:</span> <span style=\"color: #999999\">{</span><br />\n                &nbsp;&nbsp;<span style=\"color: #71982C\">&#39;x-cors-api-key&#39;</span><span style=\"color: #937042\">:</span>&nbsp;<span style=\"color: #71982C\" class=\"break\">&#39;{{CODE}}&#39;</span><br />\n                &nbsp;&nbsp;<span style=\"color: #999999\">}</span><br />\n                <span style=\"color: #999999\">})</span><br />\n              </div>\n            </div>\n          </code>\n        </mj-text>\n        <mj-text font-weight=\"bold\">\n          <h3>Next (Action required)</h3>\n        </mj-text>\n        <mj-text> Since this key is a temporary key to get you started, you have to sign-in &amp; claim your account to get a permanent one. <b>This key will only be valid for 3 days.</b>\n        </mj-text>\n        <mj-button background-color=\"black\" color=\"white\" href=\"{{ONBOARDINGLINK}}\" align=\"left\" inner-padding=\"8px\">👉 Get the permanent key </mj-button>\n        <mj-text color=\"rgba(0, 0, 0, 0.5)\">\n          <small>button not working? copy &amp; paste this link to your browser</small>\n          <small>\n            <a href=\"{{ONBOARDINGLINK}}\">{{ONBOARDINGLINK}}</a>\n          </small>\n        </mj-text>\n      </mj-column>\n    </mj-section>\n    <!-- footer -->\n    <mj-section background-color=\"#fff\">\n      <mj-column>\n        <mj-image src=\"https://s3.us-west-1.amazonaws.com/cors.sh-public-resources/branding/CORS.SH-black.svg\" height=\"24px\" />\n        <mj-text color=\"rgba(0, 0, 0, 0.5)\">\n          <small>\n            <a href=\"https://grida.co\">A Grida Product</a> | <a href=\"https://cors.sh/contact\">Contact</a>\n          </small>\n        </mj-text>\n      </mj-column>\n  </mj-body>\n</mjml>"
  },
  {
    "path": "services/mail.cors.sh/onboarding_with_payment_success.mjml",
    "content": "<mjml>\n  <mj-head>\n    <mj-font name=\"Source Code Pro\" href=\"https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;500;600;700&display=swap\" />\n    <mj-attributes>\n      <mj-text font-size=\"14px\" align=\"left\" color=\"black\" font-family=\"'Source Code Pro', monospace\" />\n      <mj-section background-color=\"#fff\" />\n      <mj-social-element icon-size=\"24px\" />\n      <mj-style inline=\"inline\">\n        .break {\n          word-break: break-all !important;\n        }\n      </mj-style>\n    </mj-attributes>\n  </mj-head>\n  <mj-body background-color=\"#eee\">\n    <mj-hero padding=\"24px 0px 0px 0px\">\n      <mj-text color=\"black\">\n        <h1>\n          Thank you for\n          </br>\n          your subscription\n        </h1>\n      </mj-text>\n    </mj-hero>\n    <mj-section>\n      <mj-column>\n        <mj-text>We’re all set. Let’s get rid of the cors errors by extending your api call with proxy.cors.sh like below.</mj-text>\n        <mj-text font-weight=\"bold\">\n          <h3>Your API key for your first application “{{APPLICATIONNAME}}” is..</h3>\n          <code class=\"break\">\n            <p style=\"background-color: black; padding:12px; color:white;\">\n              # for testing\n              </br>\n              {{CODE_TEST}}\n              </br>\n              </br>\n              # for production\n              </br>\n              {{CODE_LIVE}}\n            </p>\n          </code>\n        </mj-text>\n        <mj-text>Copy &amp; Paste this api key to your api call (XHR) header.</mj-text>\n        <mj-text font-weight=\"bold\">\n          <h3>Example usage</h3>\n        </mj-text>\n        <mj-text>You can use the api key like below. For more example, visit <a href=\"https://cors.sh/#usage\">https://cors.sh/#usage</a>\n        </mj-text>\n        <mj-text>\n          <code css-class=\"overflow-scroll\">\n            <div style=\"background-color: #F4F2F0;overflow:auto;width:auto;padding:12px;\">\n              <div style=\"margin: 0; line-height: 125%;\">\n                <span style=\"color: #CC556A\">fetch</span><span style=\"color: #999999\">(</span><span style=\"color: #71982C\">&#39;https://proxy.cors.sh/https://acme.com&#39;</span><span style=\"color: #999999\">,</span> <span style=\"color: #999999\">{</span><br />\n                &nbsp;&nbsp;<span style=\"color: #CC556A\">headers</span><span style=\"color: #937042\">:</span> <span style=\"color: #999999\">{</span><br />\n                &nbsp;&nbsp;<span style=\"color: #71982C\">&#39;x-cors-api-key&#39;</span><span style=\"color: #937042\">:</span>&nbsp;<span style=\"color: #71982C\" class=\"break\">&#39;{{CODE_TEST}}&#39;</span><br />\n                &nbsp;&nbsp;<span style=\"color: #999999\">}</span><br />\n                <span style=\"color: #999999\">})</span><br />\n              </div>\n            </div>\n          </code>\n        </mj-text>\n\n        <mj-text>\n          <a href=\"https://cors.sh/docs\">Read the docs</a>\n          <!-- <h3>What’s Next?</h3> -->\n          <!-- <ul> -->\n          <!-- disabled temporary since we do not have a console yet. -->\n          <!-- <li><a href=\"https://cors.sh/docs\">Learn how to secure your api key</a></li> -->\n          <!-- <li><a href=\"https://cors.sh/docs\">Before publishing your website to production</a></li> -->\n          <!-- <li><a href=\"https://cors.sh/docs\">Create new application on console</a></li> -->\n          <!-- </ul> -->\n        </mj-text>\n      </mj-column>\n    </mj-section>\n    <!-- footer -->\n    <mj-section background-color=\"#fff\">\n      <mj-column>\n        <mj-image src=\"https://s3.us-west-1.amazonaws.com/cors.sh-public-resources/branding/CORS.SH-black.svg\" height=\"24px\" />\n        <mj-text color=\"rgba(0, 0, 0, 0.5)\">\n          <small>\n            <a href=\"https://grida.co\">A Grida Product</a> | <a href=\"https://cors.sh/contact\">Contact</a>\n          </small>\n        </mj-text>\n      </mj-column>\n  </mj-body>\n</mjml>"
  },
  {
    "path": "services/mail.cors.sh/render/onboarding.subject",
    "content": "CORS.SH | your API Key for cors.proxy.sh"
  },
  {
    "path": "services/mail.cors.sh/render/onboarding.template.html",
    "content": "<!doctype html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n  <head>\n    <title>\n      \n    </title>\n    <!--[if !mso]><!-->\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <!--<![endif]-->\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <style type=\"text/css\">\n      #outlook a { padding:0; }\n      body { margin:0;padding:0;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%; }\n      table, td { border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt; }\n      img { border:0;height:auto;line-height:100%; outline:none;text-decoration:none;-ms-interpolation-mode:bicubic; }\n      p { display:block;margin:13px 0; }\n    </style>\n    <!--[if mso]>\n    <noscript>\n    <xml>\n    <o:OfficeDocumentSettings>\n      <o:AllowPNG/>\n      <o:PixelsPerInch>96</o:PixelsPerInch>\n    </o:OfficeDocumentSettings>\n    </xml>\n    </noscript>\n    <![endif]-->\n    <!--[if lte mso 11]>\n    <style type=\"text/css\">\n      .mj-outlook-group-fix { width:100% !important; }\n    </style>\n    <![endif]-->\n    \n      <!--[if !mso]><!-->\n        <link href=\"https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700\" rel=\"stylesheet\" type=\"text/css\">\n<link href=\"https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;500;600;700&display=swap\" rel=\"stylesheet\" type=\"text/css\">\n        <style type=\"text/css\">\n          @import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);\n@import url(https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;500;600;700&display=swap);\n        </style>\n      <!--<![endif]-->\n\n    \n    \n    <style type=\"text/css\">\n      @media only screen and (min-width:480px) {\n        .mj-column-per-100 { width:100% !important; max-width: 100%; }\n      }\n    </style>\n    <style media=\"screen and (min-width:480px)\">\n      .moz-text-html .mj-column-per-100 { width:100% !important; max-width: 100%; }\n    </style>\n    \n  \n    <style type=\"text/css\">\n    \n    \n\n    @media only screen and (max-width:480px) {\n      table.mj-full-width-mobile { width: 100% !important; }\n      td.mj-full-width-mobile { width: auto !important; }\n    }\n  \n    </style>\n    <style type=\"text/css\">\n    \n    </style>\n    \n  </head>\n  <body style=\"word-spacing:normal;background-color:#eeeeee;\">\n    \n    \n      <div\n         style=\"background-color:#eeeeee;\"\n      >\n        \n      <!--[if mso | IE]><table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"width:600px;\" width=\"600\" ><tr><td style=\"line-height:0;font-size:0;mso-line-height-rule:exactly;\"><v:image style=\"border:0;mso-position-horizontal:center;position:absolute;top:0;width:600px;z-index:-3;\" xmlns:v=\"urn:schemas-microsoft-com:vml\" /><![endif]-->\n      <div\n         style=\"margin:0 auto;max-width:600px;\"\n      >\n        <table\n           border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"width:100%;\"\n        >\n          <tbody>\n            <tr\n               style=\"vertical-align:top;\"\n            >\n              \n          <td\n             style=\"background:#ffffff;background-position:center center;background-repeat:no-repeat;padding:24px 0px 0px 0px;vertical-align:top;\" height=\"-24\"\n          >\n            \n      <!--[if mso | IE]><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:600px;\" width=\"600\" ><tr><td style=\"\"><![endif]-->\n      <div\n         class=\"mj-hero-content\" style=\"margin:0px auto;\"\n      >\n        <table\n           border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"width:100%;margin:0px;\"\n        >\n          <tbody>\n            <tr>\n              <td  style=\"\" >\n                <table\n                   border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"width:100%;margin:0px;\"\n                >\n                  <tbody>\n                    \n                        <tr>\n                          <td\n                             align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                          >\n                            \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      ><h1>\n          Your API key for\n          </br>\n          proxy.cors.sh\n        </h1></div>\n    \n                          </td>\n                        </tr>\n                      \n                  </tbody>\n                </table>\n              </td>\n            </tr>\n          </tbody>\n        </table>\n      </div>\n      <!--[if mso | IE]></td></tr></table><![endif]-->\n    \n          </td>\n        \n            </tr>\n          </tbody>\n      </table>\n    </div>\n    <!--[if mso | IE]></td></tr></table><table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"\" role=\"presentation\" style=\"width:600px;\" width=\"600\" bgcolor=\"#ffffff\" ><tr><td style=\"line-height:0px;font-size:0px;mso-line-height-rule:exactly;\"><![endif]-->\n    \n      \n      <div  style=\"background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;\">\n        \n        <table\n           align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"background:#ffffff;background-color:#ffffff;width:100%;\"\n        >\n          <tbody>\n            <tr>\n              <td\n                 style=\"direction:ltr;font-size:0px;padding:20px 0;text-align:center;\"\n              >\n                <!--[if mso | IE]><table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"\" style=\"vertical-align:top;width:600px;\" ><![endif]-->\n            \n      <div\n         class=\"mj-column-per-100 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;\"\n      >\n        \n      <table\n         border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"vertical-align:top;\" width=\"100%\"\n      >\n        <tbody>\n          \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;font-weight:bold;line-height:1;text-align:left;color:black;\"\n      ><h3>Your temporary API key is..</h3></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      ><code class=\"break\">\n            <p style=\"background-color: black; padding:12px; color:white;\">{{CODE}}</p>\n          </code></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      >Copy &amp; Paste this api key to your api call (XHR) header.</div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;font-weight:bold;line-height:1;text-align:left;color:black;\"\n      ><h3>Example usage</h3></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      >You can use the api key like below. For more example, visit <a href=\"https://cors.sh/#usage\">https://cors.sh/#usage</a></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      ><code css-class=\"overflow-scroll\">\n            <div style=\"background-color: #F4F2F0;overflow:auto;width:auto;padding:12px;\">\n              <div style=\"margin: 0; line-height: 125%;\">\n                <span style=\"color: #CC556A\">fetch</span><span style=\"color: #999999\">(</span><span style=\"color: #71982C\">&#39;https://proxy.cors.sh/https://acme.com&#39;</span><span style=\"color: #999999\">,</span> <span style=\"color: #999999\">{</span><br />\n                &nbsp;&nbsp;<span style=\"color: #CC556A\">headers</span><span style=\"color: #937042\">:</span> <span style=\"color: #999999\">{</span><br />\n                &nbsp;&nbsp;<span style=\"color: #71982C\">&#39;x-cors-api-key&#39;</span><span style=\"color: #937042\">:</span>&nbsp;<span style=\"color: #71982C\" class=\"break\">&#39;{{CODE}}&#39;</span><br />\n                &nbsp;&nbsp;<span style=\"color: #999999\">}</span><br />\n                <span style=\"color: #999999\">})</span><br />\n              </div>\n            </div>\n          </code></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;font-weight:bold;line-height:1;text-align:left;color:black;\"\n      ><h3>Next (Action required)</h3></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      >Since this key is a temporary key to get you started, you have to sign-in &amp; claim your account to get a permanent one. <b>This key will only be valid for 3 days.</b></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" vertical-align=\"middle\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <table\n         border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"border-collapse:separate;line-height:100%;\"\n      >\n        <tbody>\n          <tr>\n            <td\n               align=\"center\" bgcolor=\"black\" role=\"presentation\" style=\"border:none;border-radius:3px;cursor:auto;mso-padding-alt:8px;background:black;\" valign=\"middle\"\n            >\n              <a\n                 href=\"{{ONBOARDINGLINK}}\" style=\"display:inline-block;background:black;color:white;font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;font-weight:normal;line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:8px;mso-padding-alt:0px;border-radius:3px;\" target=\"_blank\"\n              >\n                👉 Get the permanent key\n              </a>\n            </td>\n          </tr>\n        </tbody>\n      </table>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:rgba(0, 0, 0, 0.5);\"\n      ><small>button not working? copy &amp; paste this link to your browser</small>\n          <small>\n            <a href=\"{{ONBOARDINGLINK}}\">{{ONBOARDINGLINK}}</a>\n          </small></div>\n    \n                </td>\n              </tr>\n            \n        </tbody>\n      </table>\n    \n      </div>\n    \n          <!--[if mso | IE]></td></tr></table><![endif]-->\n              </td>\n            </tr>\n          </tbody>\n        </table>\n        \n      </div>\n    \n      \n      <!--[if mso | IE]></td></tr></table><![endif]-->\n    \n    <!-- footer -->\n      \n      <!--[if mso | IE]><table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"\" role=\"presentation\" style=\"width:600px;\" width=\"600\" bgcolor=\"#ffffff\" ><tr><td style=\"line-height:0px;font-size:0px;mso-line-height-rule:exactly;\"><![endif]-->\n    \n      \n      <div  style=\"background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;\">\n        \n        <table\n           align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"background:#ffffff;background-color:#ffffff;width:100%;\"\n        >\n          <tbody>\n            <tr>\n              <td\n                 style=\"direction:ltr;font-size:0px;padding:20px 0;text-align:center;\"\n              >\n                <!--[if mso | IE]><table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"\" style=\"vertical-align:top;width:600px;\" ><![endif]-->\n            \n      <div\n         class=\"mj-column-per-100 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;\"\n      >\n        \n      <table\n         border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"vertical-align:top;\" width=\"100%\"\n      >\n        <tbody>\n          \n              <tr>\n                <td\n                   align=\"center\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <table\n         border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"border-collapse:collapse;border-spacing:0px;\"\n      >\n        <tbody>\n          <tr>\n            <td  style=\"width:550px;\">\n              \n      <img\n         height=\"24\" src=\"https://s3.us-west-1.amazonaws.com/cors.sh-public-resources/branding/CORS.SH-black.svg\" style=\"border:0;display:block;outline:none;text-decoration:none;height:24px;width:100%;font-size:13px;\" width=\"550\"\n      />\n    \n            </td>\n          </tr>\n        </tbody>\n      </table>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:rgba(0, 0, 0, 0.5);\"\n      ><small>\n            <a href=\"https://grida.co\">A Grida Product</a> | <a href=\"mailto:hello@grida.co\">Contact</a>\n          </small></div>\n    \n                </td>\n              </tr>\n            \n        </tbody>\n      </table>\n    \n      </div>\n    \n          <!--[if mso | IE]></td></tr></table><![endif]-->\n              </td>\n            </tr>\n          </tbody>\n        </table>\n        \n      </div>\n    \n      \n      <!--[if mso | IE]></td></tr></table><![endif]-->\n    \n    \n      </div>\n    \n  </body>\n</html>\n  "
  },
  {
    "path": "services/mail.cors.sh/render/onboarding_with_payment_success.subject",
    "content": "CORS.SH | Your first project"
  },
  {
    "path": "services/mail.cors.sh/render/onboarding_with_payment_success.template.html",
    "content": "<!doctype html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n  <head>\n    <title>\n      \n    </title>\n    <!--[if !mso]><!-->\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <!--<![endif]-->\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <style type=\"text/css\">\n      #outlook a { padding:0; }\n      body { margin:0;padding:0;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%; }\n      table, td { border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt; }\n      img { border:0;height:auto;line-height:100%; outline:none;text-decoration:none;-ms-interpolation-mode:bicubic; }\n      p { display:block;margin:13px 0; }\n    </style>\n    <!--[if mso]>\n    <noscript>\n    <xml>\n    <o:OfficeDocumentSettings>\n      <o:AllowPNG/>\n      <o:PixelsPerInch>96</o:PixelsPerInch>\n    </o:OfficeDocumentSettings>\n    </xml>\n    </noscript>\n    <![endif]-->\n    <!--[if lte mso 11]>\n    <style type=\"text/css\">\n      .mj-outlook-group-fix { width:100% !important; }\n    </style>\n    <![endif]-->\n    \n      <!--[if !mso]><!-->\n        <link href=\"https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;500;600;700&display=swap\" rel=\"stylesheet\" type=\"text/css\">\n        <style type=\"text/css\">\n          @import url(https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;500;600;700&display=swap);\n        </style>\n      <!--<![endif]-->\n\n    \n    \n    <style type=\"text/css\">\n      @media only screen and (min-width:480px) {\n        .mj-column-per-100 { width:100% !important; max-width: 100%; }\n      }\n    </style>\n    <style media=\"screen and (min-width:480px)\">\n      .moz-text-html .mj-column-per-100 { width:100% !important; max-width: 100%; }\n    </style>\n    \n  \n    <style type=\"text/css\">\n    \n    \n\n    @media only screen and (max-width:480px) {\n      table.mj-full-width-mobile { width: 100% !important; }\n      td.mj-full-width-mobile { width: auto !important; }\n    }\n  \n    </style>\n    <style type=\"text/css\">\n    \n    </style>\n    \n  </head>\n  <body style=\"word-spacing:normal;background-color:#eeeeee;\">\n    \n    \n      <div\n         style=\"background-color:#eeeeee;\"\n      >\n        \n      <!--[if mso | IE]><table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"width:600px;\" width=\"600\" ><tr><td style=\"line-height:0;font-size:0;mso-line-height-rule:exactly;\"><v:image style=\"border:0;mso-position-horizontal:center;position:absolute;top:0;width:600px;z-index:-3;\" xmlns:v=\"urn:schemas-microsoft-com:vml\" /><![endif]-->\n      <div\n         style=\"margin:0 auto;max-width:600px;\"\n      >\n        <table\n           border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"width:100%;\"\n        >\n          <tbody>\n            <tr\n               style=\"vertical-align:top;\"\n            >\n              \n          <td\n             style=\"background:#ffffff;background-position:center center;background-repeat:no-repeat;padding:24px 0px 0px 0px;vertical-align:top;\" height=\"-24\"\n          >\n            \n      <!--[if mso | IE]><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width:600px;\" width=\"600\" ><tr><td style=\"\"><![endif]-->\n      <div\n         class=\"mj-hero-content\" style=\"margin:0px auto;\"\n      >\n        <table\n           border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"width:100%;margin:0px;\"\n        >\n          <tbody>\n            <tr>\n              <td  style=\"\" >\n                <table\n                   border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"width:100%;margin:0px;\"\n                >\n                  <tbody>\n                    \n                        <tr>\n                          <td\n                             align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                          >\n                            \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      ><h1>\n          Thank you for\n          </br>\n          your subscription\n        </h1></div>\n    \n                          </td>\n                        </tr>\n                      \n                  </tbody>\n                </table>\n              </td>\n            </tr>\n          </tbody>\n        </table>\n      </div>\n      <!--[if mso | IE]></td></tr></table><![endif]-->\n    \n          </td>\n        \n            </tr>\n          </tbody>\n      </table>\n    </div>\n    <!--[if mso | IE]></td></tr></table><table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"\" role=\"presentation\" style=\"width:600px;\" width=\"600\" bgcolor=\"#ffffff\" ><tr><td style=\"line-height:0px;font-size:0px;mso-line-height-rule:exactly;\"><![endif]-->\n    \n      \n      <div  style=\"background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;\">\n        \n        <table\n           align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"background:#ffffff;background-color:#ffffff;width:100%;\"\n        >\n          <tbody>\n            <tr>\n              <td\n                 style=\"direction:ltr;font-size:0px;padding:20px 0;text-align:center;\"\n              >\n                <!--[if mso | IE]><table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"\" style=\"vertical-align:top;width:600px;\" ><![endif]-->\n            \n      <div\n         class=\"mj-column-per-100 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;\"\n      >\n        \n      <table\n         border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"vertical-align:top;\" width=\"100%\"\n      >\n        <tbody>\n          \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      >We’re all set. Let’s get rid of the cors errors by extending your api call with proxy.cors.sh like below.</div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;font-weight:bold;line-height:1;text-align:left;color:black;\"\n      ><h3>Your API key for your first application “{{APPLICATIONNAME}}” is..</h3>\n          <code class=\"break\">\n            <p style=\"background-color: black; padding:12px; color:white;\">\n              # for testing\n              </br>\n              {{CODE_TEST}}\n              </br>\n              </br>\n              # for production\n              </br>\n              {{CODE_LIVE}}\n            </p>\n          </code></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      >Copy &amp; Paste this api key to your api call (XHR) header.</div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;font-weight:bold;line-height:1;text-align:left;color:black;\"\n      ><h3>Example usage</h3></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      >You can use the api key like below. For more example, visit <a href=\"https://cors.sh/#usage\">https://cors.sh/#usage</a></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      ><code css-class=\"overflow-scroll\">\n            <div style=\"background-color: #F4F2F0;overflow:auto;width:auto;padding:12px;\">\n              <div style=\"margin: 0; line-height: 125%;\">\n                <span style=\"color: #CC556A\">fetch</span><span style=\"color: #999999\">(</span><span style=\"color: #71982C\">&#39;https://proxy.cors.sh/https://acme.com&#39;</span><span style=\"color: #999999\">,</span> <span style=\"color: #999999\">{</span><br />\n                &nbsp;&nbsp;<span style=\"color: #CC556A\">headers</span><span style=\"color: #937042\">:</span> <span style=\"color: #999999\">{</span><br />\n                &nbsp;&nbsp;<span style=\"color: #71982C\">&#39;x-cors-api-key&#39;</span><span style=\"color: #937042\">:</span>&nbsp;<span style=\"color: #71982C\" class=\"break\">&#39;{{CODE_TEST}}&#39;</span><br />\n                &nbsp;&nbsp;<span style=\"color: #999999\">}</span><br />\n                <span style=\"color: #999999\">})</span><br />\n              </div>\n            </div>\n          </code></div>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:black;\"\n      ><a href=\"https://cors.sh/docs\">Read the docs</a>\n          <!-- <h3>What’s Next?</h3> -->\n          <!-- <ul> -->\n          <!-- disabled temporary since we do not have a console yet. -->\n          <!-- <li><a href=\"https://cors.sh/docs\">Learn how to secure your api key</a></li> -->\n          <!-- <li><a href=\"https://cors.sh/docs\">Before publishing your website to production</a></li> -->\n          <!-- <li><a href=\"https://cors.sh/docs\">Create new application on console</a></li> -->\n          <!-- </ul> --></div>\n    \n                </td>\n              </tr>\n            \n        </tbody>\n      </table>\n    \n      </div>\n    \n          <!--[if mso | IE]></td></tr></table><![endif]-->\n              </td>\n            </tr>\n          </tbody>\n        </table>\n        \n      </div>\n    \n      \n      <!--[if mso | IE]></td></tr></table><![endif]-->\n    \n    <!-- footer -->\n      \n      <!--[if mso | IE]><table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"\" role=\"presentation\" style=\"width:600px;\" width=\"600\" bgcolor=\"#ffffff\" ><tr><td style=\"line-height:0px;font-size:0px;mso-line-height-rule:exactly;\"><![endif]-->\n    \n      \n      <div  style=\"background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;\">\n        \n        <table\n           align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"background:#ffffff;background-color:#ffffff;width:100%;\"\n        >\n          <tbody>\n            <tr>\n              <td\n                 style=\"direction:ltr;font-size:0px;padding:20px 0;text-align:center;\"\n              >\n                <!--[if mso | IE]><table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"\" style=\"vertical-align:top;width:600px;\" ><![endif]-->\n            \n      <div\n         class=\"mj-column-per-100 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;\"\n      >\n        \n      <table\n         border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"vertical-align:top;\" width=\"100%\"\n      >\n        <tbody>\n          \n              <tr>\n                <td\n                   align=\"center\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <table\n         border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"border-collapse:collapse;border-spacing:0px;\"\n      >\n        <tbody>\n          <tr>\n            <td  style=\"width:550px;\">\n              \n      <img\n         height=\"24\" src=\"https://s3.us-west-1.amazonaws.com/cors.sh-public-resources/branding/CORS.SH-black.svg\" style=\"border:0;display:block;outline:none;text-decoration:none;height:24px;width:100%;font-size:13px;\" width=\"550\"\n      />\n    \n            </td>\n          </tr>\n        </tbody>\n      </table>\n    \n                </td>\n              </tr>\n            \n              <tr>\n                <td\n                   align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\"\n                >\n                  \n      <div\n         style=\"font-family:'Source Code Pro', monospace;font-size:14px;line-height:1;text-align:left;color:rgba(0, 0, 0, 0.5);\"\n      ><small>\n            <a href=\"https://grida.co\">A Grida Product</a> | <a href=\"mailto:hello@grida.co\">Contact</a>\n          </small></div>\n    \n                </td>\n              </tr>\n            \n        </tbody>\n      </table>\n    \n      </div>\n    \n          <!--[if mso | IE]></td></tr></table><![endif]-->\n              </td>\n            </tr>\n          </tbody>\n        </table>\n        \n      </div>\n    \n      \n      <!--[if mso | IE]></td></tr></table><![endif]-->\n    \n    \n      </div>\n    \n  </body>\n</html>\n  "
  },
  {
    "path": "services/proxy.cors.sh/.gitignore",
    "content": "# compiled output\n/dist\n/node_modules\n.build\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# OS\n.DS_Store\n\n# Tests\n/coverage\n/.nyc_output\n\n# IDEs and editors\n/.idea\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n\n\n# env\n.env\n.env.staging\n.env.production"
  },
  {
    "path": "services/proxy.cors.sh/README.md",
    "content": "# CORS.BRIDGED.CC\n\n![cors.sh cover artwork](./docs/artwork_cors.sh.jpg)\n\n## The Web deployment\n\napp.cors.bridged.cc is linked to repositoy https://github.com/bridgedxyz/proxy-client, deployed via aws amplify on `proxy-client`'s `app.cors.bridged.xyz` branch\n\n**other cd options**\nusing serverless-finch (dropped & not used)\n\n> this is an obsolete approach of deploying web to s3, which we are no longer using\n\nin serverless.yml\n\n```\ncustom:\n  scripts:\n    hooks:\n      'before:package:createDeploymentArtifacts': yarn --cwd web build && sls client deploy\n  client:\n    bucketName: app.cors.bridged.cc\n    distributionFolder: web/build\n    indexDocument: index.html\n    errorDocument: index.html\n\nplugins:\n    - serverless-finch\n```\n\n```\nyarn add --dev serverless-plugin-scripts serverless-finch\n```\n"
  },
  {
    "path": "services/proxy.cors.sh/contributing.md",
    "content": "## running redis server locally\n\n```\ndocker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server:latest\n```\n"
  },
  {
    "path": "services/proxy.cors.sh/deploy/prod.sh",
    "content": "sls deploy --stage production \\\n  --param=\"domain=true\""
  },
  {
    "path": "services/proxy.cors.sh/deploy/staging.sh",
    "content": "sls deploy --stage staging \\\n  --param=\"domain=false\""
  },
  {
    "path": "services/proxy.cors.sh/docs/README.md",
    "content": "# Docs & Image resources directory"
  },
  {
    "path": "services/proxy.cors.sh/lib/cors.ts",
    "content": "import * as httpProxy from \"http-proxy\";\nimport * as https from \"https\";\nimport * as http from \"http\";\nimport * as url from \"url\";\nimport { getProxyForUrl } from \"proxy-from-env\";\nimport { withCORS, isValidHostName, parseURL } from \"./utils\";\n\ninterface OptionParams {\n  httpProxyOptions: httpProxy.ServerOptions;\n  httpsOptions?: https.ServerOptions;\n  [x: string]: any;\n}\n\nfunction proxyRequest(req, res, proxy) {\n  var location = req.corsAnywhereRequestState.location;\n  req.url = location.path;\n\n  var proxyOptions: any = {\n    changeOrigin: false,\n    prependPath: false,\n    target: location,\n    headers: {\n      host: location.host,\n    },\n    buffer: {\n      pipe: function (proxyReq) {\n        var proxyReqOn = proxyReq.on;\n        proxyReq.on = function (eventName, listener) {\n          if (eventName !== \"response\") {\n            return proxyReqOn.call(this, eventName, listener);\n          }\n          return proxyReqOn.call(this, \"response\", function (proxyRes) {\n            if (onProxyResponse(proxy, proxyReq, proxyRes, req, res)) {\n              try {\n                listener(proxyRes);\n              } catch (err) {\n                proxyReq.emit(\"error\", err);\n              }\n            }\n          });\n        };\n        return req.pipe(proxyReq);\n      },\n    },\n  };\n\n  var proxyThroughUrl = req.corsAnywhereRequestState.getProxyForUrl(\n    location.href\n  );\n  if (proxyThroughUrl) {\n    proxyOptions.target = proxyThroughUrl;\n    proxyOptions.toProxy = true;\n    req.url = location.href;\n  }\n\n  try {\n    proxy.web(req, res, proxyOptions);\n  } catch (err) {\n    proxy.emit(\"error\", err, req, res);\n  }\n}\n\nfunction onProxyResponse(proxy, proxyReq, proxyRes, req, res) {\n  var requestState = req.corsAnywhereRequestState;\n\n  var statusCode = proxyRes.statusCode;\n\n  if (!requestState.redirectCount_) {\n    res.setHeader(\"x-request-url\", requestState.location.href);\n  }\n  if (\n    statusCode === 301 ||\n    statusCode === 302 ||\n    statusCode === 303 ||\n    statusCode === 307 ||\n    statusCode === 308\n  ) {\n    var locationHeader = proxyRes.headers.location;\n    var parsedLocation;\n    if (locationHeader) {\n      locationHeader = url.resolve(requestState.location.href, locationHeader);\n      parsedLocation = parseURL(locationHeader);\n    }\n    if (parsedLocation) {\n      if (statusCode === 301 || statusCode === 302 || statusCode === 303) {\n        requestState.redirectCount_ = requestState.redirectCount_ + 1 || 1;\n        if (requestState.redirectCount_ <= requestState.maxRedirects) {\n          res.setHeader(\n            \"X-CORS-Redirect-\" + requestState.redirectCount_,\n            statusCode + \" \" + locationHeader\n          );\n\n          req.method = \"GET\";\n          req.headers[\"content-length\"] = \"0\";\n          delete req.headers[\"content-type\"];\n          requestState.location = parsedLocation;\n          req.removeAllListeners();\n          proxyReq.removeAllListeners(\"error\");\n          proxyReq.once(\"error\", function catchAndIgnoreError() {});\n          proxyReq.abort();\n          proxyRequest(req, res, proxy);\n          return false;\n        }\n      }\n      proxyRes.headers.location =\n        requestState.proxyBaseUrl + \"/\" + locationHeader;\n    }\n  }\n\n  delete proxyRes.headers[\"set-cookie\"];\n  delete proxyRes.headers[\"set-cookie2\"];\n\n  proxyRes.headers[\"x-final-url\"] = requestState.location.href;\n  withCORS(proxyRes.headers, req);\n  return true;\n}\n\nfunction getHandler(options, proxy) {\n  var corsAnywhere: any = {\n    getProxyForUrl: getProxyForUrl, // Function that specifies the proxy to use\n    maxRedirects: 5, // Maximum number of redirects to be followed.\n    originBlacklist: [], // Requests from these origins will be blocked.\n    originWhitelist: [], // If non-empty, requests not from an origin in this list will be blocked.\n    checkRateLimit: null, // Function that may enforce a rate-limit by returning a non-empty string.\n    redirectSameOrigin: false, // Redirect the client to the requested URL for same-origin requests.\n    requireHeader: null, // Require a header to be set?\n    removeHeaders: [], // Strip these request headers.\n    setHeaders: {}, // Set these request headers.\n    corsMaxAge: 0, // If set, an Access-Control-Max-Age header with this value (in seconds) will be added.\n    helpFile: __dirname + \"/help.txt\",\n  };\n\n  Object.keys(corsAnywhere).forEach(function (option) {\n    if (Object.prototype.hasOwnProperty.call(options, option)) {\n      corsAnywhere[option] = options[option];\n    }\n  });\n  if (corsAnywhere.requireHeader) {\n    if (typeof corsAnywhere.requireHeader === \"string\") {\n      corsAnywhere.requireHeader = [corsAnywhere.requireHeader.toLowerCase()];\n    } else if (\n      !Array.isArray(corsAnywhere.requireHeader) ||\n      corsAnywhere.requireHeader.length === 0\n    ) {\n      corsAnywhere.requireHeader = null;\n    } else {\n      corsAnywhere.requireHeader = corsAnywhere.requireHeader.map(function (\n        headerName\n      ) {\n        return headerName.toLowerCase();\n      });\n    }\n  }\n  var hasRequiredHeaders = function (headers) {\n    return (\n      !corsAnywhere.requireHeader ||\n      corsAnywhere.requireHeader.some(function (headerName) {\n        return Object.hasOwnProperty.call(headers, headerName);\n      })\n    );\n  };\n\n  return function (req, res) {\n    req.corsAnywhereRequestState = {\n      getProxyForUrl: corsAnywhere.getProxyForUrl,\n      maxRedirects: corsAnywhere.maxRedirects,\n      corsMaxAge: corsAnywhere.corsMaxAge,\n    };\n\n    var cors_headers = withCORS({}, req);\n    if (req.method === \"OPTIONS\") {\n      res.writeHead(200, cors_headers);\n      res.end();\n      return;\n    }\n\n    var location: any = parseURL(req.url.slice(1));\n\n    if (location.host === \"iscorsneeded\") {\n      res.writeHead(200, { \"Content-Type\": \"text/plain\" });\n      res.end(\"no\");\n      return;\n    }\n\n    if (location.port > 65535) {\n      res.writeHead(400, \"Invalid port\", cors_headers);\n      res.end(\"Port number too large: \" + location.port);\n      return;\n    }\n\n    if (!/^\\/https?:/.test(req.url) && !isValidHostName(location.hostname)) {\n      res.writeHead(404, \"Invalid host\", cors_headers);\n      res.end(\"Invalid host: \" + location.hostname);\n      return;\n    }\n\n    if (!hasRequiredHeaders(req.headers)) {\n      res.writeHead(400, \"Header required\", cors_headers);\n      res.end(\n        \"Missing required request header. Must specify one of: \" +\n          corsAnywhere.requireHeader +\n          \"\\n\" +\n          \"If you are testing out, you can try at https://cors.sh/playground\"\n      );\n      return;\n    }\n\n    var origin = req.headers.origin || \"\";\n    if (corsAnywhere.originBlacklist.indexOf(origin) >= 0) {\n      res.writeHead(403, \"Forbidden\", cors_headers);\n      res.end(\n        'The origin \"' +\n          origin +\n          '\" was blacklisted by the operator of this proxy.'\n      );\n      return;\n    }\n\n    if (\n      corsAnywhere.originWhitelist.length &&\n      corsAnywhere.originWhitelist.indexOf(origin) === -1\n    ) {\n      res.writeHead(403, \"Forbidden\", cors_headers);\n      res.end(\n        'The origin \"' +\n          origin +\n          '\" was not whitelisted by the operator of this proxy.'\n      );\n      return;\n    }\n\n    var rateLimitMessage =\n      corsAnywhere.checkRateLimit && corsAnywhere.checkRateLimit(origin);\n    if (rateLimitMessage) {\n      res.writeHead(429, \"Too Many Requests\", cors_headers);\n      res.end(\n        'The origin \"' +\n          origin +\n          '\" has sent too many requests.\\n' +\n          rateLimitMessage\n      );\n      return;\n    }\n\n    if (\n      corsAnywhere.redirectSameOrigin &&\n      origin &&\n      location.href[origin.length] === \"/\" &&\n      location.href.lastIndexOf(origin, 0) === 0\n    ) {\n      cors_headers.vary = \"origin\";\n      cors_headers[\"cache-control\"] = \"private\";\n      cors_headers.location = location.href;\n      res.writeHead(301, \"Please use a direct request\", cors_headers);\n      res.end();\n      return;\n    }\n\n    var isRequestedOverHttps =\n      req.connection.encrypted ||\n      /^\\s*https/.test(req.headers[\"x-forwarded-proto\"]);\n    var proxyBaseUrl =\n      (isRequestedOverHttps ? \"https://\" : \"http://\") + req.headers.host;\n\n    corsAnywhere.removeHeaders.forEach(function (header) {\n      delete req.headers[header];\n    });\n\n    Object.keys(corsAnywhere.setHeaders).forEach(function (header) {\n      req.headers[header] = corsAnywhere.setHeaders[header];\n    });\n\n    req.corsAnywhereRequestState.location = location;\n    req.corsAnywhereRequestState.proxyBaseUrl = proxyBaseUrl;\n\n    proxyRequest(req, res, proxy);\n  };\n}\n\nexport const createServer = (options: OptionParams) => {\n  var server: https.Server | http.Server;\n  var httpProxyOptions: httpProxy.ServerOptions = {\n    xfwd: true, // Append X-Forwarded-* headers\n  };\n\n  options = options || {};\n\n  if (options.httpProxyOptions) {\n    Object.keys(options.httpProxyOptions).forEach(function (option) {\n      httpProxyOptions[option] = options.httpProxyOptions[option];\n    });\n  }\n\n  var proxy: httpProxy = httpProxy.createServer(httpProxyOptions);\n  var requestHandler = getHandler(options, proxy);\n  if (options.httpsOptions) {\n    server = https.createServer(options.httpsOptions, requestHandler);\n  } else {\n    server = http.createServer(requestHandler);\n  }\n\n  proxy.on(\n    \"error\",\n    function (err: Error, _: http.IncomingMessage, res: http.ServerResponse) {\n      if (process.env.NODE_ENV !== \"production\") {\n        console.error(\"Proxy error: \", err, res);\n      }\n\n      if (res.headersSent) {\n        if (res.writableEnded === false) {\n          res.end();\n        }\n        return;\n      }\n      var headerNames = res.getHeaderNames\n        ? res.getHeaderNames()\n        : //@ts-ignore\n          Object.keys(res._headers || {});\n      headerNames.forEach(function (name) {\n        res.removeHeader(name);\n      });\n\n      res.writeHead(404, { \"Access-Control-Allow-Origin\": \"*\" });\n      res.end(\"Not found because of proxy error: \" + err);\n    }\n  );\n  return server;\n};\n"
  },
  {
    "path": "services/proxy.cors.sh/lib/utils.ts",
    "content": "import * as net from \"net\";\nimport * as url from \"url\";\n\nexport const regexp = /\\.(?:AAA|AARP|ABARTH|ABB|ABBOTT|ABBVIE|ABC|ABLE|ABOGADO|ABUDHABI|AC|ACADEMY|ACCENTURE|ACCOUNTANT|ACCOUNTANTS|ACO|ACTIVE|ACTOR|AD|ADAC|ADS|ADULT|AE|AEG|AERO|AETNA|AF|AFAMILYCOMPANY|AFL|AFRICA|AG|AGAKHAN|AGENCY|AI|AIG|AIGO|AIRBUS|AIRFORCE|AIRTEL|AKDN|AL|ALFAROMEO|ALIBABA|ALIPAY|ALLFINANZ|ALLSTATE|ALLY|ALSACE|ALSTOM|AM|AMERICANEXPRESS|AMERICANFAMILY|AMEX|AMFAM|AMICA|AMSTERDAM|ANALYTICS|ANDROID|ANQUAN|ANZ|AO|AOL|APARTMENTS|APP|APPLE|AQ|AQUARELLE|AR|ARAB|ARAMCO|ARCHI|ARMY|ARPA|ART|ARTE|AS|ASDA|ASIA|ASSOCIATES|AT|ATHLETA|ATTORNEY|AU|AUCTION|AUDI|AUDIBLE|AUDIO|AUSPOST|AUTHOR|AUTO|AUTOS|AVIANCA|AW|AWS|AX|AXA|AZ|AZURE|BA|BABY|BAIDU|BANAMEX|BANANAREPUBLIC|BAND|BANK|BAR|BARCELONA|BARCLAYCARD|BARCLAYS|BAREFOOT|BARGAINS|BASEBALL|BASKETBALL|BAUHAUS|BAYERN|BB|BBC|BBT|BBVA|BCG|BCN|BD|BE|BEATS|BEAUTY|BEER|BENTLEY|BERLIN|BEST|BESTBUY|BET|BF|BG|BH|BHARTI|BI|BIBLE|BID|BIKE|BING|BINGO|BIO|BIZ|BJ|BLACK|BLACKFRIDAY|BLANCO|BLOCKBUSTER|BLOG|BLOOMBERG|BLUE|BM|BMS|BMW|BN|BNL|BNPPARIBAS|BO|BOATS|BOEHRINGER|BOFA|BOM|BOND|BOO|BOOK|BOOKING|BOOTS|BOSCH|BOSTIK|BOSTON|BOT|BOUTIQUE|BOX|BR|BRADESCO|BRIDGESTONE|BROADWAY|BROKER|BROTHER|BRUSSELS|BS|BT|BUDAPEST|BUGATTI|BUILD|BUILDERS|BUSINESS|BUY|BUZZ|BV|BW|BY|BZ|BZH|CA|CAB|CAFE|CAL|CALL|CALVINKLEIN|CAM|CAMERA|CAMP|CANCERRESEARCH|CANON|CAPETOWN|CAPITAL|CAPITALONE|CAR|CARAVAN|CARDS|CARE|CAREER|CAREERS|CARS|CARTIER|CASA|CASE|CASEIH|CASH|CASINO|CAT|CATERING|CATHOLIC|CBA|CBN|CBRE|CBS|CC|CD|CEB|CENTER|CEO|CERN|CF|CFA|CFD|CG|CH|CHANEL|CHANNEL|CHASE|CHAT|CHEAP|CHINTAI|CHRISTMAS|CHROME|CHRYSLER|CHURCH|CI|CIPRIANI|CIRCLE|CISCO|CITADEL|CITI|CITIC|CITY|CITYEATS|CK|CL|CLAIMS|CLEANING|CLICK|CLINIC|CLINIQUE|CLOTHING|CLOUD|CLUB|CLUBMED|CM|CN|CO|COACH|CODES|COFFEE|COLLEGE|COLOGNE|COM|COMCAST|COMMBANK|COMMUNITY|COMPANY|COMPARE|COMPUTER|COMSEC|CONDOS|CONSTRUCTION|CONSULTING|CONTACT|CONTRACTORS|COOKING|COOKINGCHANNEL|COOL|COOP|CORSICA|COUNTRY|COUPON|COUPONS|COURSES|CR|CREDIT|CREDITCARD|CREDITUNION|CRICKET|CROWN|CRS|CRUISE|CRUISES|CSC|CU|CUISINELLA|CV|CW|CX|CY|CYMRU|CYOU|CZ|DABUR|DAD|DANCE|DATA|DATE|DATING|DATSUN|DAY|DCLK|DDS|DE|DEAL|DEALER|DEALS|DEGREE|DELIVERY|DELL|DELOITTE|DELTA|DEMOCRAT|DENTAL|DENTIST|DESI|DESIGN|DEV|DHL|DIAMONDS|DIET|DIGITAL|DIRECT|DIRECTORY|DISCOUNT|DISCOVER|DISH|DIY|DJ|DK|DM|DNP|DO|DOCS|DOCTOR|DODGE|DOG|DOHA|DOMAINS|DOT|DOWNLOAD|DRIVE|DTV|DUBAI|DUCK|DUNLOP|DUNS|DUPONT|DURBAN|DVAG|DVR|DZ|EARTH|EAT|EC|ECO|EDEKA|EDU|EDUCATION|EE|EG|EMAIL|EMERCK|ENERGY|ENGINEER|ENGINEERING|ENTERPRISES|EPOST|EPSON|EQUIPMENT|ER|ERICSSON|ERNI|ES|ESQ|ESTATE|ESURANCE|ET|ETISALAT|EU|EUROVISION|EUS|EVENTS|EVERBANK|EXCHANGE|EXPERT|EXPOSED|EXPRESS|EXTRASPACE|FAGE|FAIL|FAIRWINDS|FAITH|FAMILY|FAN|FANS|FARM|FARMERS|FASHION|FAST|FEDEX|FEEDBACK|FERRARI|FERRERO|FI|FIAT|FIDELITY|FIDO|FILM|FINAL|FINANCE|FINANCIAL|FIRE|FIRESTONE|FIRMDALE|FISH|FISHING|FIT|FITNESS|FJ|FK|FLICKR|FLIGHTS|FLIR|FLORIST|FLOWERS|FLY|FM|FO|FOO|FOOD|FOODNETWORK|FOOTBALL|FORD|FOREX|FORSALE|FORUM|FOUNDATION|FOX|FR|FREE|FRESENIUS|FRL|FROGANS|FRONTDOOR|FRONTIER|FTR|FUJITSU|FUJIXEROX|FUN|FUND|FURNITURE|FUTBOL|FYI|GA|GAL|GALLERY|GALLO|GALLUP|GAME|GAMES|GAP|GARDEN|GB|GBIZ|GD|GDN|GE|GEA|GENT|GENTING|GEORGE|GF|GG|GGEE|GH|GI|GIFT|GIFTS|GIVES|GIVING|GL|GLADE|GLASS|GLE|GLOBAL|GLOBO|GM|GMAIL|GMBH|GMO|GMX|GN|GODADDY|GOLD|GOLDPOINT|GOLF|GOO|GOODHANDS|GOODYEAR|GOOG|GOOGLE|GOP|GOT|GOV|GP|GQ|GR|GRAINGER|GRAPHICS|GRATIS|GREEN|GRIPE|GROCERY|GROUP|GS|GT|GU|GUARDIAN|GUCCI|GUGE|GUIDE|GUITARS|GURU|GW|GY|HAIR|HAMBURG|HANGOUT|HAUS|HBO|HDFC|HDFCBANK|HEALTH|HEALTHCARE|HELP|HELSINKI|HERE|HERMES|HGTV|HIPHOP|HISAMITSU|HITACHI|HIV|HK|HKT|HM|HN|HOCKEY|HOLDINGS|HOLIDAY|HOMEDEPOT|HOMEGOODS|HOMES|HOMESENSE|HONDA|HONEYWELL|HORSE|HOSPITAL|HOST|HOSTING|HOT|HOTELES|HOTELS|HOTMAIL|HOUSE|HOW|HR|HSBC|HT|HU|HUGHES|HYATT|HYUNDAI|IBM|ICBC|ICE|ICU|ID|IE|IEEE|IFM|IKANO|IL|IM|IMAMAT|IMDB|IMMO|IMMOBILIEN|IN|INDUSTRIES|INFINITI|INFO|ING|INK|INSTITUTE|INSURANCE|INSURE|INT|INTEL|INTERNATIONAL|INTUIT|INVESTMENTS|IO|IPIRANGA|IQ|IR|IRISH|IS|ISELECT|ISMAILI|IST|ISTANBUL|IT|ITAU|ITV|IVECO|IWC|JAGUAR|JAVA|JCB|JCP|JE|JEEP|JETZT|JEWELRY|JIO|JLC|JLL|JM|JMP|JNJ|JO|JOBS|JOBURG|JOT|JOY|JP|JPMORGAN|JPRS|JUEGOS|JUNIPER|KAUFEN|KDDI|KE|KERRYHOTELS|KERRYLOGISTICS|KERRYPROPERTIES|KFH|KG|KH|KI|KIA|KIM|KINDER|KINDLE|KITCHEN|KIWI|KM|KN|KOELN|KOMATSU|KOSHER|KP|KPMG|KPN|KR|KRD|KRED|KUOKGROUP|KW|KY|KYOTO|KZ|LA|LACAIXA|LADBROKES|LAMBORGHINI|LAMER|LANCASTER|LANCIA|LANCOME|LAND|LANDROVER|LANXESS|LASALLE|LAT|LATINO|LATROBE|LAW|LAWYER|LB|LC|LDS|LEASE|LECLERC|LEFRAK|LEGAL|LEGO|LEXUS|LGBT|LI|LIAISON|LIDL|LIFE|LIFEINSURANCE|LIFESTYLE|LIGHTING|LIKE|LILLY|LIMITED|LIMO|LINCOLN|LINDE|LINK|LIPSY|LIVE|LIVING|LIXIL|LK|LOAN|LOANS|LOCKER|LOCUS|LOFT|LOL|LONDON|LOTTE|LOTTO|LOVE|LPL|LPLFINANCIAL|LR|LS|LT|LTD|LTDA|LU|LUNDBECK|LUPIN|LUXE|LUXURY|LV|LY|MA|MACYS|MADRID|MAIF|MAISON|MAKEUP|MAN|MANAGEMENT|MANGO|MAP|MARKET|MARKETING|MARKETS|MARRIOTT|MARSHALLS|MASERATI|MATTEL|MBA|MC|MCKINSEY|MD|ME|MED|MEDIA|MEET|MELBOURNE|MEME|MEMORIAL|MEN|MENU|MEO|MERCKMSD|METLIFE|MG|MH|MIAMI|MICROSOFT|MIL|MINI|MINT|MIT|MITSUBISHI|MK|ML|MLB|MLS|MM|MMA|MN|MO|MOBI|MOBILE|MOBILY|MODA|MOE|MOI|MOM|MONASH|MONEY|MONSTER|MOPAR|MORMON|MORTGAGE|MOSCOW|MOTO|MOTORCYCLES|MOV|MOVIE|MOVISTAR|MP|MQ|MR|MS|MSD|MT|MTN|MTR|MU|MUSEUM|MUTUAL|MV|MW|MX|MY|MZ|NA|NAB|NADEX|NAGOYA|NAME|NATIONWIDE|NATURA|NAVY|NBA|NC|NE|NEC|NET|NETBANK|NETFLIX|NETWORK|NEUSTAR|NEW|NEWHOLLAND|NEWS|NEXT|NEXTDIRECT|NEXUS|NF|NFL|NG|NGO|NHK|NI|NICO|NIKE|NIKON|NINJA|NISSAN|NISSAY|NL|NO|NOKIA|NORTHWESTERNMUTUAL|NORTON|NOW|NOWRUZ|NOWTV|NP|NR|NRA|NRW|NTT|NU|NYC|NZ|OBI|OBSERVER|OFF|OFFICE|OKINAWA|OLAYAN|OLAYANGROUP|OLDNAVY|OLLO|OM|OMEGA|ONE|ONG|ONL|ONLINE|ONYOURSIDE|OOO|OPEN|ORACLE|ORANGE|ORG|ORGANIC|ORIGINS|OSAKA|OTSUKA|OTT|OVH|PA|PAGE|PANASONIC|PANERAI|PARIS|PARS|PARTNERS|PARTS|PARTY|PASSAGENS|PAY|PCCW|PE|PET|PF|PFIZER|PG|PH|PHARMACY|PHD|PHILIPS|PHONE|PHOTO|PHOTOGRAPHY|PHOTOS|PHYSIO|PIAGET|PICS|PICTET|PICTURES|PID|PIN|PING|PINK|PIONEER|PIZZA|PK|PL|PLACE|PLAY|PLAYSTATION|PLUMBING|PLUS|PM|PN|PNC|POHL|POKER|POLITIE|PORN|POST|PR|PRAMERICA|PRAXI|PRESS|PRIME|PRO|PROD|PRODUCTIONS|PROF|PROGRESSIVE|PROMO|PROPERTIES|PROPERTY|PROTECTION|PRU|PRUDENTIAL|PS|PT|PUB|PW|PWC|PY|QA|QPON|QUEBEC|QUEST|QVC|RACING|RADIO|RAID|RE|READ|REALESTATE|REALTOR|REALTY|RECIPES|RED|REDSTONE|REDUMBRELLA|REHAB|REISE|REISEN|REIT|RELIANCE|REN|RENT|RENTALS|REPAIR|REPORT|REPUBLICAN|REST|RESTAURANT|REVIEW|REVIEWS|REXROTH|RICH|RICHARDLI|RICOH|RIGHTATHOME|RIL|RIO|RIP|RMIT|RO|ROCHER|ROCKS|RODEO|ROGERS|ROOM|RS|RSVP|RU|RUGBY|RUHR|RUN|RW|RWE|RYUKYU|SA|SAARLAND|SAFE|SAFETY|SAKURA|SALE|SALON|SAMSCLUB|SAMSUNG|SANDVIK|SANDVIKCOROMANT|SANOFI|SAP|SAPO|SARL|SAS|SAVE|SAXO|SB|SBI|SBS|SC|SCA|SCB|SCHAEFFLER|SCHMIDT|SCHOLARSHIPS|SCHOOL|SCHULE|SCHWARZ|SCIENCE|SCJOHNSON|SCOR|SCOT|SD|SE|SEARCH|SEAT|SECURE|SECURITY|SEEK|SELECT|SENER|SERVICES|SES|SEVEN|SEW|SEX|SEXY|SFR|SG|SH|SHANGRILA|SHARP|SHAW|SHELL|SHIA|SHIKSHA|SHOES|SHOP|SHOPPING|SHOUJI|SHOW|SHOWTIME|SHRIRAM|SI|SILK|SINA|SINGLES|SITE|SJ|SK|SKI|SKIN|SKY|SKYPE|SL|SLING|SM|SMART|SMILE|SN|SNCF|SO|SOCCER|SOCIAL|SOFTBANK|SOFTWARE|SOHU|SOLAR|SOLUTIONS|SONG|SONY|SOY|SPACE|SPIEGEL|SPOT|SPREADBETTING|SR|SRL|SRT|ST|STADA|STAPLES|STAR|STARHUB|STATEBANK|STATEFARM|STATOIL|STC|STCGROUP|STOCKHOLM|STORAGE|STORE|STREAM|STUDIO|STUDY|STYLE|SU|SUCKS|SUPPLIES|SUPPLY|SUPPORT|SURF|SURGERY|SUZUKI|SV|SWATCH|SWIFTCOVER|SWISS|SX|SY|SYDNEY|SYMANTEC|SYSTEMS|SZ|TAB|TAIPEI|TALK|TAOBAO|TARGET|TATAMOTORS|TATAR|TATTOO|TAX|TAXI|TC|TCI|TD|TDK|TEAM|TECH|TECHNOLOGY|TEL|TELECITY|TELEFONICA|TEMASEK|TENNIS|TEVA|TF|TG|TH|THD|THEATER|THEATRE|TIAA|TICKETS|TIENDA|TIFFANY|TIPS|TIRES|TIROL|TJ|TJMAXX|TJX|TK|TKMAXX|TL|TM|TMALL|TN|TO|TODAY|TOKYO|TOOLS|TOP|TORAY|TOSHIBA|TOTAL|TOURS|TOWN|TOYOTA|TOYS|TR|TRADE|TRADING|TRAINING|TRAVEL|TRAVELCHANNEL|TRAVELERS|TRAVELERSINSURANCE|TRUST|TRV|TT|TUBE|TUI|TUNES|TUSHU|TV|TVS|TW|TZ|UA|UBANK|UBS|UCONNECT|UG|UK|UNICOM|UNIVERSITY|UNO|UOL|UPS|US|UY|UZ|VA|VACATIONS|VANA|VANGUARD|VC|VE|VEGAS|VENTURES|VERISIGN|VERSICHERUNG|VET|VG|VI|VIAJES|VIDEO|VIG|VIKING|VILLAS|VIN|VIP|VIRGIN|VISA|VISION|VISTA|VISTAPRINT|VIVA|VIVO|VLAANDEREN|VN|VODKA|VOLKSWAGEN|VOLVO|VOTE|VOTING|VOTO|VOYAGE|VU|VUELOS|WALES|WALMART|WALTER|WANG|WANGGOU|WARMAN|WATCH|WATCHES|WEATHER|WEATHERCHANNEL|WEBCAM|WEBER|WEBSITE|WED|WEDDING|WEIBO|WEIR|WF|WHOSWHO|WIEN|WIKI|WILLIAMHILL|WIN|WINDOWS|WINE|WINNERS|WME|WOLTERSKLUWER|WOODSIDE|WORK|WORKS|WORLD|WOW|WS|WTC|WTF|XBOX|XEROX|XFINITY|XIHUAN|XIN|XN--11B4C3D|XN--1CK2E1B|XN--1QQW23A|XN--2SCRJ9C|XN--30RR7Y|XN--3BST00M|XN--3DS443G|XN--3E0B707E|XN--3HCRJ9C|XN--3OQ18VL8PN36A|XN--3PXU8K|XN--42C2D9A|XN--45BR5CYL|XN--45BRJ9C|XN--45Q11C|XN--4GBRIM|XN--54B7FTA0CC|XN--55QW42G|XN--55QX5D|XN--5SU34J936BGSG|XN--5TZM5G|XN--6FRZ82G|XN--6QQ986B3XL|XN--80ADXHKS|XN--80AO21A|XN--80AQECDR1A|XN--80ASEHDB|XN--80ASWG|XN--8Y0A063A|XN--90A3AC|XN--90AE|XN--90AIS|XN--9DBQ2A|XN--9ET52U|XN--9KRT00A|XN--B4W605FERD|XN--BCK1B9A5DRE4C|XN--C1AVG|XN--C2BR7G|XN--CCK2B3B|XN--CG4BKI|XN--CLCHC0EA0B2G2A9GCD|XN--CZR694B|XN--CZRS0T|XN--CZRU2D|XN--D1ACJ3B|XN--D1ALF|XN--E1A4C|XN--ECKVDTC9D|XN--EFVY88H|XN--ESTV75G|XN--FCT429K|XN--FHBEI|XN--FIQ228C5HS|XN--FIQ64B|XN--FIQS8S|XN--FIQZ9S|XN--FJQ720A|XN--FLW351E|XN--FPCRJ9C3D|XN--FZC2C9E2C|XN--FZYS8D69UVGM|XN--G2XX48C|XN--GCKR3F0F|XN--GECRJ9C|XN--GK3AT1E|XN--H2BREG3EVE|XN--H2BRJ9C|XN--H2BRJ9C8C|XN--HXT814E|XN--I1B6B1A6A2E|XN--IMR513N|XN--IO0A7I|XN--J1AEF|XN--J1AMH|XN--J6W193G|XN--JLQ61U9W7B|XN--JVR189M|XN--KCRX77D1X4A|XN--KPRW13D|XN--KPRY57D|XN--KPU716F|XN--KPUT3I|XN--L1ACC|XN--LGBBAT1AD8J|XN--MGB9AWBF|XN--MGBA3A3EJT|XN--MGBA3A4F16A|XN--MGBA7C0BBN0A|XN--MGBAAKC7DVF|XN--MGBAAM7A8H|XN--MGBAB2BD|XN--MGBAI9AZGQP6J|XN--MGBAYH7GPA|XN--MGBB9FBPOB|XN--MGBBH1A|XN--MGBBH1A71E|XN--MGBC0A9AZCG|XN--MGBCA7DZDO|XN--MGBERP4A5D4AR|XN--MGBGU82A|XN--MGBI4ECEXP|XN--MGBPL2FH|XN--MGBT3DHD|XN--MGBTX2B|XN--MGBX4CD0AB|XN--MIX891F|XN--MK1BU44C|XN--MXTQ1M|XN--NGBC5AZD|XN--NGBE9E0A|XN--NGBRX|XN--NODE|XN--NQV7F|XN--NQV7FS00EMA|XN--NYQY26A|XN--O3CW4H|XN--OGBPF8FL|XN--P1ACF|XN--P1AI|XN--PBT977C|XN--PGBS0DH|XN--PSSY2U|XN--Q9JYB4C|XN--QCKA1PMC|XN--QXAM|XN--RHQV96G|XN--ROVU88B|XN--RVC1E0AM3E|XN--S9BRJ9C|XN--SES554G|XN--T60B56A|XN--TCKWE|XN--TIQ49XQYJ|XN--UNUP4Y|XN--VERMGENSBERATER-CTB|XN--VERMGENSBERATUNG-PWB|XN--VHQUV|XN--VUQ861B|XN--W4R85EL8FHU5DNRA|XN--W4RS40L|XN--WGBH1C|XN--WGBL6A|XN--XHQ521B|XN--XKC2AL3HYE2A|XN--XKC2DL3A5EE0H|XN--Y9A3AQ|XN--YFRO4I67O|XN--YGBI2AMMX|XN--ZFR164B|XPERIA|XXX|XYZ|YACHTS|YAHOO|YAMAXUN|YANDEX|YE|YODOBASHI|YOGA|YOKOHAMA|YOU|YOUTUBE|YT|YUN|ZA|ZAPPOS|ZARA|ZERO|ZIP|ZIPPO|ZM|ZONE|ZUERICH|ZW)$/i;\n\nexport const withCORS = (headers, request) => {\n  headers[\"access-control-allow-origin\"] = \"*\";\n\n\n  var corsMaxAge = request.corsAnywhereRequestState.corsMaxAge;\n  if (request.method === \"OPTIONS\" && corsMaxAge) {\n    headers[\"access-control-max-age\"] = corsMaxAge;\n  }\n  if (request.headers[\"access-control-request-method\"]) {\n    headers[\"access-control-allow-methods\"] =\n      request.headers[\"access-control-request-method\"];\n    delete request.headers[\"access-control-request-method\"];\n  }\n  if (request.headers[\"access-control-request-headers\"]) {\n    headers[\"access-control-allow-headers\"] =\n      request.headers[\"access-control-request-headers\"];\n    delete request.headers[\"access-control-request-headers\"];\n  }\n\n  headers[\"access-control-expose-headers\"] = Object.keys(headers).join(\",\");\n\n  return headers;\n};\n\nexport const isValidHostName = hostname => {\n  return !!(\n    regexp.test(hostname) ||\n    net.isIPv4(hostname) ||\n    net.isIPv6(hostname)\n  );\n};\n\nexport const parseURL = req_url => {\n  var match = req_url.match(\n    /^(?:(https?:)?\\/\\/)?(([^\\/?]+?)(?::(\\d{0,5})(?=[\\/?]|$))?)([\\/?][\\S\\s]*|$)/i\n  );\n  if (!match) {\n    return null;\n  }\n  if (!match[1]) {\n    if (/^https?:/i.test(req_url)) {\n      return null;\n    }\n    if (req_url.lastIndexOf(\"//\", 0) === -1) {\n      req_url = \"//\" + req_url;\n    }\n    req_url = (match[4] === \"443\" ? \"https:\" : \"http:\") + req_url;\n  }\n  var parsed = url.parse(req_url);\n  if (!parsed.hostname) {\n    return null;\n  }\n  return parsed;\n};\n"
  },
  {
    "path": "services/proxy.cors.sh/package.json",
    "content": "{\n    \"name\": \"proxy.cors.sh\",\n    \"description\": \"free cors service for everyone\",\n    \"version\": \"0.1.0\",\n    \"scripts\": {\n        \"clean\": \"rimraf .build .serverless\",\n        \"deploy:prod\": \"yarn clean && source deploy/prod.sh\",\n        \"deploy:staging\": \"yarn clean && source deploy/staging.sh\",\n        \"dev\": \"sls offline start --stage development\",\n        \"build\": \"yarn clean && sls package --stage development\"\n    },\n    \"dependencies\": {\n        \"aws-serverless-express\": \"^3.4.0\",\n        \"dayjs\": \"^1.11.7\",\n        \"express\": \"^4.17.1\",\n        \"express-rate-limit\": \"^6.5.2\",\n        \"express-useragent\": \"^1.0.15\",\n        \"http-proxy\": \"^1.18.1\",\n        \"nanoid\": \"^3.1.23\",\n        \"proxy-from-env\": \"^1.1.0\",\n        \"rate-limit-redis\": \"^3.0.1\",\n        \"redis\": \"^4.3.0\",\n        \"response-time\": \"^2.3.2\",\n        \"serverless-http\": \"^2.7.0\"\n    },\n    \"devDependencies\": {\n        \"@types/aws-lambda\": \"^8.10.71\",\n        \"@types/express\": \"^4.17.11\",\n        \"@types/http-proxy\": \"^1.17.5\",\n        \"@types/node\": \"^18.7.14\",\n        \"@types/proxy-from-env\": \"^1.0.1\",\n        \"@types/response-time\": \"^2.3.4\",\n        \"rimraf\": \"^3.0.2\",\n        \"serverless\": \"^2.22.0\",\n        \"serverless-domain-manager\": \"^5.1.0\",\n        \"serverless-offline\": \"^6.8.0\",\n        \"serverless-plugin-dynamo-autoscaling\": \"^1.0.1\",\n        \"serverless-plugin-optimize\": \"^4.1.4-rc.1\",\n        \"serverless-plugin-typescript\": \"^2.1.2\",\n        \"typescript\": \"^4.1.3\"\n    }\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/serverless.yml",
    "content": "# Welcome to serverless. Read the docs\n# https://serverless.com/framework/docs/\n\nservice: cors-proxy\nuseDotenv: true\ncustom:\n  customDomain:\n    domainName: proxy.cors.sh\n    basePath: \"\"\n    stage: ${opt:stage, self:provider.stage}\n    createRoute53Record: false\n    # enabled only for production - configured by package.json script with --domain flag.\n    enabled: ${param:domain, false}\n\n  serverless-offline:\n    httpPort: 4020\n    lambdaPort: 3020\n    noPrependStageInUrl: true\n\nprovider:\n  name: aws\n  memorySize: 128\n  runtime: nodejs14.x\n  apiGateway:\n    shouldStartNameWithService: true\n    binaryMediaTypes:\n      - \"*/*\"\n    endpointType: regional\n  region: us-west-1\n  environment:\n    STAGE: ${opt:stage, self:provider.stage}\n    RATE_LIMIT_REDIS_URL: ${env:RATE_LIMIT_REDIS_URL}\n    RATE_LIMIT_REDIS_USERNAME: ${env:RATE_LIMIT_REDIS_USERNAME}\n    RATE_LIMIT_REDIS_PASSWORD: ${env:RATE_LIMIT_REDIS_PASSWORD}\n    API_KEY_TEMP_AES_KEY: ${env:API_KEY_TEMP_AES_KEY}\n    API_KEY_TEMP_AES_IV: ${env:API_KEY_TEMP_AES_IV}\n    AWS_NODEJS_CONNECTION_REUSE_ENABLED: \"1\"\n    # for auth, managed by service.cors.sh\n    DYNAMODB_TABLE_SERVICE_KEYS: \"cors-proxy-service-keys-${opt:stage, self:provider.stage}\"\n  iamRoleStatements:\n    - Effect: Allow\n      Action:\n        # this service only needs to read the service keys\n        - dynamodb:GetItem\n      Resource: \"arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE_SERVICE_KEYS}\"\n\n# The `functions` block defines what code to deploy\nfunctions:\n  api:\n    handler: src/index.handler\n    maximumEventAge: 60\n    maximumRetryAttempts: 0\n    timeout: 12\n    events:\n      - http:\n          path: /{proxy+}\n          method: any\n          # authorizer:\n          #   name: cors-proxy-authorizer\n          #   identitySource: \"method.request.header.x-cors-api-key\"\n          #   type: token\n          #   resultTtlInSeconds: 3600 # 1 hour\n          #   arn: ${env:CORS_PROXY_AUTHORIZER_ARN}\n      - http:\n          path: /\n          method: get\n\nplugins:\n  - serverless-plugin-typescript\n  - serverless-plugin-optimize\n  - serverless-offline\n  - serverless-domain-manager\n"
  },
  {
    "path": "services/proxy.cors.sh/src/_util/size.ts",
    "content": "export const MB = 1048576;\n\n/**\n * 1 seconds in ms\n */\nexport const SEC = 1000;\n"
  },
  {
    "path": "services/proxy.cors.sh/src/_util/x-header.ts",
    "content": "import { Request } from \"express\";\n\nexport function headerfrom(\n  headers: Request[\"headers\"],\n  key: string | string[]\n): string | null {\n  const keys = Array.isArray(key) ? key : [key];\n  for (const k of keys) {\n    const v = headers[k];\n    if (v) {\n      return v as string;\n    }\n  }\n  return null;\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/src/app.ts",
    "content": "import * as express from \"express\";\nimport * as useragent from \"express-useragent\";\nimport * as corsProxy from \"../lib/cors\";\nimport * as responsetime from \"response-time\";\n\nimport { logRequest } from \"./usage\";\nimport { blaklistoriginlimit, payloadlimit } from \"./limit\";\nimport limiter from \"./limit/rate-limit\";\nimport { authorization } from \"./auth\";\n\nconst app = express();\n\nconst cors_proxy = corsProxy.createServer({\n  // https://github.com/Rob--W/cors-anywhere/issues/39\n  requireHeader: [\"origin\", \"x-requested-with\"],\n  // requireHeader: [],\n  removeHeaders: [\n    \"cookie\",\n    \"cookie2\",\n    \"x-request-start\",\n    \"x-request-id\",\n    \"via\",\n    \"connect-time\",\n    \"total-route-time\",\n  ],\n  redirectSameOrigin: true,\n  httpProxyOptions: {\n    xfwd: false,\n  },\n});\n\napp.get(\"/\", function (req, res) {\n  res.redirect(\"https://cors.sh/\");\n});\n\napp.use(blaklistoriginlimit); // 1\napp.use(payloadlimit); // 2\napp.use(authorization); // 3\n\n// response time middleware\napp.use(\n  responsetime({\n    suffix: false,\n  }) as any\n);\n\n// user agent middleware\napp.use(useragent.express());\n\n// hourly rate limiter\napp.use(limiter.hourly);\n\n// monthly (28-day) rate limiter\napp.use(limiter.monthly);\n\n// -- execution order matters --\n// (0)\napp.use((req, res, next) => {\n  // support 'x-strict-request-url'\n  // refer issue: https://github.com/gridaco/cors.sh/issues/38\n  const x_strict_request_url = req.headers[\"x-strict-request-url\"];\n  if (x_strict_request_url && typeof x_strict_request_url === \"string\") {\n    // need prefix with '/' (required by cors_proxy to parse)\n    req.url = \"/\" + x_strict_request_url;\n    next();\n    return;\n  }\n\n  next();\n});\n\n// (1)\napp.use((req, res, next) => {\n  if (res.headersSent) {\n    return;\n  }\n\n  try {\n    cors_proxy.emit(\"request\", req, res);\n    next();\n  } catch (_) {}\n});\n\n// (2)\n/**\n * after response middleware\n */\napp.use((req, res) => {\n  res.on(\"finish\", () => {\n    logRequest(req, res);\n  });\n});\n// -- execution order matters --\n\n/**\n * global error handler\n */\napp.use(((err, req, res, next) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  try {\n    console.error(err);\n    res.status(500).json({\n      message: \"Internal Server Error\",\n      error: err,\n      issue: \"https://github.com/gridaco/cors.sh/issues\",\n    });\n  } catch (_) {}\n}) as express.ErrorRequestHandler);\n\nexport { app };\n"
  },
  {
    "path": "services/proxy.cors.sh/src/auth/_tmp_static_api_keys/.gitignore",
    "content": "# manuall managed credentials (temporal)\nkeys.js"
  },
  {
    "path": "services/proxy.cors.sh/src/auth/_tmp_static_api_keys/README.md",
    "content": "# Temporal manually managed api key store\n"
  },
  {
    "path": "services/proxy.cors.sh/src/auth/_tmp_static_api_keys/index.js",
    "content": "export { default as keys } from \"./keys\";\n"
  },
  {
    "path": "services/proxy.cors.sh/src/auth/index.ts",
    "content": "import * as express from \"express\";\nimport { validate_tmp_key } from \"./keys-temp\";\nimport { verify_synced_key } from \"./keys-synced\";\nimport { keyinfo } from \"./keys\";\nimport { headerfrom } from \"../_util/x-header\";\nimport { STATIC_CORS_ACCOUNT_API_KEY_HEADERS } from \"../k\";\nimport * as legacy from \"./legacy\";\n\nfunction parseApiKey(req: express.Request): string | undefined | null {\n  const apikey_from_header = headerfrom(\n    req.headers,\n    STATIC_CORS_ACCOUNT_API_KEY_HEADERS\n  );\n\n  const api_key_q = \"cors_api_key\";\n\n  // parsing from query requires parsing the url then, parsing the query, since the api call can contain the original url with their query.\n  let apikey_from_query;\n  try {\n    // since url starts with \"/\" we need to remove the first character, then decode the url\n    const url = decodeURI(req.url.slice(1));\n    const params = new URL(url).searchParams;\n    apikey_from_query = params.get(api_key_q);\n  } catch (e) {\n    apikey_from_query = req.query[api_key_q] as string;\n  }\n\n  return apikey_from_header || apikey_from_query;\n}\n\nexport async function authorization(\n  req: express.Request,\n  res: express.Response,\n  next\n) {\n  // 0. demo - no auth, allow all requests from https://cors.sh/playground  (https://playground.cors.sh), rate limited by fixed rate\n  //    - uses ip as id\n  // 1. legacy - static api key, rate limited by fixed rate\n  //    - uses api key as id\n  // 2. anonymous localhost - only allow requests from localhost, rate limited by fixed rate\n  //   - uses ip as id\n  // 3. temporary key - allow requests from anywhere (unless expired), rate limited by fixed rate\n  //   - uses api key\n  // 4. signed key - allow requests from anywhere (unless configured), rate limited by purchased plan\n  //   // TODO:\n  //   - uses api key as subscription id\n  //   - live key - allow requests from anywhere (unless configured), rate limited by purchased plan\n  //   - test key - allow requests from anywhere (unless configured), rate limited by fixed rate\n\n  // skip api key check for preflight requests\n  if (req.method == \"OPTIONS\" || req.method == \"HEAD\") {\n    next();\n  }\n\n  const apikey = parseApiKey(req);\n\n  if (!apikey) {\n    // no key, anonymous\n    const id = anynymous_request_identity(req);\n    const authorization: AuthorizationInfo = {\n      authorized: false,\n      id,\n      tier: \"anonymous\",\n    };\n    res.locals.authorization = authorization;\n    next();\n    return;\n  }\n\n  const { signature, mode } = keyinfo(apikey!);\n  const origin = req.headers.origin;\n  const { ip, hostname } = req;\n\n  if (\n    origin === \"https://cors.sh\" ||\n    origin === \"https://playground.cors.sh\" ||\n    origin === \"https://cors.sh/playground\"\n  ) {\n    const authorization: AuthorizationInfo = {\n      authorized: true,\n      id: ip,\n      tier: \"unlimited\",\n      skip_rate_limit: true,\n    };\n    res.locals.authorization = authorization;\n    next();\n    return;\n  }\n\n  switch (mode) {\n    case \"live\":\n    case \"test\": {\n      if (process.env.STAGE !== \"production\") {\n        // bypass auth check in dev\n        const authorization: AuthorizationInfo = {\n          authorized: true,\n          id: \"dev\",\n          tier: \"unlimited\",\n        };\n        res.locals.authorization = authorization;\n        next();\n        return;\n        //\n      }\n\n      // TODO: explicit handling for test / live key\n      const verified = await verify_synced_key(signature);\n      if (verified) {\n        const { plan, billing_group } = verified;\n        const authorization: AuthorizationInfo = {\n          authorized: true,\n          id: billing_group,\n          tier: plan,\n        };\n        res.locals.authorization = authorization;\n        next();\n        return;\n      } else {\n        res\n          .status(402)\n          .send(\n            \"Your account is suspended. Please make a payment at https://cors.sh\"\n          );\n        return;\n      }\n      break;\n    }\n    case \"temp\": {\n      const verified = validate_tmp_key(signature);\n      if (verified) {\n        const authorization: AuthorizationInfo = {\n          authorized: true,\n          id: signature,\n          tier: \"free\",\n        };\n        res.locals.authorization = authorization;\n        next();\n        return;\n      } else {\n        res\n          .status(401)\n          .send(\"Temporay key expired Get a new one from https://cors.sh\");\n        return;\n      }\n      break;\n    }\n    case \"v2022\": {\n      const verified = legacy.validate_api_key(apikey!);\n      if (verified) {\n        const authorization: AuthorizationInfo = {\n          authorized: true,\n          id: signature,\n          tier: \"2022.t1\",\n        };\n        res.locals.authorization = authorization;\n        next();\n        return;\n      } else {\n        res\n          .status(401)\n          .send(\"Unauthorized. Get a api key from https://cors.sh\");\n        return;\n      }\n      break;\n    }\n  }\n}\n\nexport interface AuthorizationInfo {\n  authorized: boolean;\n  id: string | \"demo\";\n  tier: \"anonymous\" | \"free\" | \"unlimited\" | \"2022.t1\" | \"2023.t1\" | \"2023.t2\";\n  skip_rate_limit?: boolean;\n}\n\nfunction anynymous_request_identity(req: express.Request) {\n  if (req.hostname == \"localhost\") {\n    return req.ip;\n  } else {\n    return req.hostname;\n  }\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/src/auth/keys-synced.ts",
    "content": "import { DynamoDB } from \"aws-sdk\";\nimport * as day from \"dayjs\";\nconst db = new DynamoDB.DocumentClient();\n\nconst TABLE = process.env.DYNAMODB_TABLE_SERVICE_KEYS!;\n\nexport async function verify_synced_key(key: string): Promise<\n  | {\n      plan: \"2023.t1\" | \"2023.t2\";\n      billing_group: string;\n    }\n  | false\n> {\n  const record = await find(key);\n\n  if (!record) {\n    return false;\n  }\n\n  const { active, expires_at, billing_group, plan } = record;\n  if (active && expires_at > day().unix()) {\n    return {\n      plan: plan as any,\n      billing_group,\n    };\n  }\n\n  return false;\n}\n\nasync function find(key: string): Promise<ServiceKeyInfo | null> {\n  const { Item } = await db\n    .get({\n      TableName: TABLE,\n      Key: {\n        key: key,\n      },\n    })\n    .promise();\n\n  if (!Item) {\n    return null;\n  }\n\n  return Item as ServiceKeyInfo;\n}\n\ninterface ServiceKeyInfo {\n  key: string;\n  plan: string;\n  config: {\n    allowed_origins: string[];\n    allowed_targets: string[];\n  };\n  active: boolean;\n  billing_group: string;\n  expires_at: number;\n  synced_at: number;\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/src/auth/keys-temp.ts",
    "content": "import * as crypto from \"crypto\";\nimport * as day from \"dayjs\";\n\nconst API_KEY_TEMP_AES_KEY: string = process.env.API_KEY_TEMP_AES_KEY!;\nconst API_KEY_TEMP_AES_IV: string = process.env.API_KEY_TEMP_AES_IV!;\nconst _aes_key = Buffer.from(API_KEY_TEMP_AES_KEY, \"hex\");\nconst _aes_iv = Buffer.from(API_KEY_TEMP_AES_IV, \"hex\");\nconst TMP_KEY_EXP_IN_DAYS = 3;\n\nexport function validate_tmp_key(signature: string) {\n  // decode signature that uses aes-256-cbc, then validate with totp.\n  let decipher = crypto.createDecipheriv(\"aes-256-cbc\", _aes_key, _aes_iv);\n\n  try {\n    let token =\n      decipher.update(signature, \"hex\", \"utf8\") + decipher.final(\"utf8\");\n\n    // token is a unix timestamp\n    // the token should match (now ~ now + TMP_KEY_EXP_IN_DAYS)\n    const now = day();\n    const token_day = day.unix(Number(token));\n    const diff = token_day.diff(now, \"day\");\n\n    if (diff > TMP_KEY_EXP_IN_DAYS) {\n      return false;\n    }\n\n    return true;\n  } catch (e) {\n    return false;\n  }\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/src/auth/keys.ts",
    "content": "export function keyinfo(key: string): {\n  signature: string;\n  mode: \"temp\" | \"live\" | \"test\" | \"v2022\";\n} {\n  const mode = key.split(\"_\")[0];\n  const signature = key.split(\"_\")[1];\n\n  switch (mode) {\n    case \"temp\":\n    case \"live\":\n    case \"test\": {\n      return {\n        mode,\n        signature,\n      };\n    }\n    default: {\n      // legacy key\n      return {\n        mode: \"v2022\",\n        signature: key,\n      };\n    }\n  }\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/src/auth/legacy/index.ts",
    "content": "export * from \"./static-account-api-key-auth\";\n"
  },
  {
    "path": "services/proxy.cors.sh/src/auth/legacy/static-account-api-key-auth.ts",
    "content": "import { keys } from \"../_tmp_static_api_keys\";\n\n// const nokey401UnAuthorized = () => {\n//   return \"https://bit.ly/2UnZSA8\";\n//   // return {\n//   //   message: `CORS Proxy request from origin \"${origin}\" is not allowed. Request is unauthorized`,\n//   //   issue: \"https://github.com/bridgedxyz/base/issues/23\",\n//   // };\n// };\n\n// export const unauthorizedAppBlocking = (\n//   req: express.Request,\n//   res: express.Response,\n//   next\n// ) => {\n//   // skip api key check for preflight requests\n//   if (req.method == \"OPTIONS\" || req.method == \"HEAD\") {\n//     next();\n//   }\n//   const apikey = headerfrom(req.headers, STATIC_CORS_ACCOUNT_API_KEY_HEADERS);\n//   if (apikey && validate_api_key(apikey)) {\n//     next();\n//   } else {\n//     res.status(401).send(nokey401UnAuthorized());\n//     return;\n//   }\n// };\n\nexport function validate_api_key(apikey: string) {\n  if (!apikey || apikey == \"\") {\n    return false;\n  }\n  const found = (keys as string[]).find((s) => s === apikey);\n  if (found) {\n    return true;\n  }\n  return false;\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/src/index.ts",
    "content": "import * as serverlessExpress from \"aws-serverless-express\";\nimport { APIGatewayProxyHandler } from \"aws-lambda\";\n\nimport { app } from \"./app\";\n\nconst binaryMimeTypes = [\n  '*/*'\n\n  // https://github.com/vendia/serverless-express/blob/master/examples/basic-starter/lambda.js\n  // 'application/javascript',\n  // 'application/json',\n  // 'application/octet-stream',\n  // 'application/xml',\n  // 'font/eot',\n  // 'font/opentype',\n  // 'font/otf',\n  // 'image/jpeg',\n  // 'image/png',\n  // 'image/svg+xml',\n  // 'text/comma-separated-values',\n  // 'text/css',\n  // 'text/html',\n  // 'text/javascript',\n  // 'text/plain',\n  // 'text/text',\n  // 'text/xml'\n]\n\nconst server = serverlessExpress.createServer(app, null, binaryMimeTypes)\n\nexport const handler: APIGatewayProxyHandler = (event, context) => {\n  serverlessExpress.proxy(server, event, context);\n};"
  },
  {
    "path": "services/proxy.cors.sh/src/k/index.ts",
    "content": "/**\n * proxy.cors.sh static api key header\n */\nexport const STATIC_CORS_ACCOUNT_API_KEY_HEADERS = [\n  \"x-cors-grida-api-key\", // legacy header for cors.bridged.cc\n  \"x-cors-api-key\", // header for cors.sh\n];\n\nexport const RATE_LIMIT_FREE_AUTHORIZED_PER_HOUR = 500; // 500\nexport const RATE_LIMIT_FREE_ANONYMOUS_PER_HOUR = 100; // 100\nexport const RATE_LIMIT_PAID_PER_MONTH_DEFAULT = 500000;\nexport const RATE_LIMIT_PAID_PER_MONTH_T1 = 500000; // $4 / Mo\nexport const RATE_LIMIT_PAID_PER_MONTH_T2 = 2500000; // $20 / Mo\n"
  },
  {
    "path": "services/proxy.cors.sh/src/limit/README.md",
    "content": "# Limitation handler"
  },
  {
    "path": "services/proxy.cors.sh/src/limit/blacklist-origin.ts",
    "content": "import * as express from \"express\";\n\n/**\n * explicitly black listed origins. these are not registered to use base.\n */\nconst blacklisted_origin: string[] = [\n  \"REJECTME\",\n  // WAITING FOR SERVICE PROVIDER'S ACTION\n  \"titronline.org\",\n  \"titr.online\",\n  \"showsport.xyz\",\n  \"cdn14.esp-cdn.xyz\",\n  \"digi-hdsport.com\",\n  \"siunus.github.io\",\n  // ILLEGAL OR AUDULT WEBSITE (PERMINANTLY BLOCKED)\n  \"twerktvnaija.com\",\n];\n\nconst blacked401UnAuthorized = (origin: string) => {\n  return \"https://bit.ly/2UnZSA8\";\n  // return {\n  //   message: `CORS Proxy request from origin \"${origin}\" is not allowed. Request is unauthorized`,\n  //   issue: \"https://github.com/bridgedxyz/base/issues/23\",\n  // };\n};\n\nexport const blaklistoriginlimit = (\n  req: express.Request,\n  res: express.Response,\n  next\n) => {\n  const origin = req.headers[\"origin\"];\n\n  if (origin) {\n    if (blacked(origin)) {\n      res.status(401).send(blacked401UnAuthorized(origin));\n      return;\n    }\n  }\n  next();\n};\n\nfunction blacked(origin: string): boolean {\n  // patterns\n  // 1. www.<origin>\n  // 2. http//<origin>\n  // 3. https//<origin>\n  // 4. http://<origin>\n  // 5. ....\n  try {\n    const u = new URL(origin);\n    return blacklisted_origin.some((o) => {\n      return u.hostname == o || u.hostname == \"www.\" + o;\n    });\n  } catch (_) {\n    // we cannot handle url that is invalid (need better logic for this)\n    return false;\n  }\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/src/limit/index.ts",
    "content": "export * from \"./payload-limit\";\nexport * from \"./blacklist-origin\";\n"
  },
  {
    "path": "services/proxy.cors.sh/src/limit/payload-limit.ts",
    "content": "import * as https from \"https\";\nimport * as http from \"http\";\nimport { MB } from \"../_util/size\";\n\n/**\n * this is to save data transfer cost. And basically we should not use CORS Proxy to load large files.\n *\n * https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html\n */\nconst MAX_TARGET_RESOURCE_MB = 2; // 6mb is max supported by api gateway. (supports up to 2mb on proxy)\n\nexport const payloadlimit = (req, res, next) => {\n  const requrl = req.originalUrl.substring(1);\n\n  const agent = requrl.startsWith(\"https:\") ? https : http;\n  agent\n    .request(requrl, { method: \"HEAD\" }, (_resp) => {\n      const len = Number(_resp.headers[\"content-length\"]);\n      if (len && len > MB * MAX_TARGET_RESOURCE_MB) {\n        // reject if data larger than 30mb.\n        res.status(413).send({\n          message: `Requested resource exceeds ${MAX_TARGET_RESOURCE_MB}mb`,\n          issue: \"https://github.com/bridgedxyz/base/issues/23\",\n        });\n      } else {\n        // if content-length is undefined or less than 30mb, procceed.\n        next();\n      }\n    })\n    .on(\"error\", (err) => {\n      // ignore error for this\n      // target, which is anonymous, might not support HEAD request.\n      next();\n    })\n    .end();\n};\n"
  },
  {
    "path": "services/proxy.cors.sh/src/limit/rate-limit.ts",
    "content": "import { Request, Response } from \"express\";\nimport rateLimit from \"express-rate-limit\";\nimport RedisStore from \"rate-limit-redis\";\nimport { createClient } from \"redis\";\nimport {\n  RATE_LIMIT_FREE_ANONYMOUS_PER_HOUR,\n  RATE_LIMIT_FREE_AUTHORIZED_PER_HOUR,\n  RATE_LIMIT_PAID_PER_MONTH_DEFAULT,\n  RATE_LIMIT_PAID_PER_MONTH_T1,\n  RATE_LIMIT_PAID_PER_MONTH_T2,\n} from \"../k\";\nimport type { AuthorizationInfo } from \"../auth\";\n\n// rather if locally ran by `sls offline`\nconst IS_OFFLINE = process.env.IS_OFFLINE;\n\nconst client = createClient({\n  url: process.env.RATE_LIMIT_REDIS_URL,\n  username: process.env.RATE_LIMIT_REDIS_USERNAME,\n  password: process.env.RATE_LIMIT_REDIS_PASSWORD,\n  socket: IS_OFFLINE\n    ? {\n        // 10 minutes\n        connectTimeout: 10 * 60 * 1000,\n        keepAlive: 10 * 60 * 1000,\n      }\n    : undefined,\n\n  // ... (see https://github.com/redis/node-redis/blob/master/docs/client-configuration.md)\n});\n\n// TODO: make sure connected\nclient.connect();\n\n/**\n * shared for all rate limiters\n * @returns\n */\nconst keygenerator = (req: Request, res: Response) => {\n  const { id } = (res.locals.authorization as AuthorizationInfo) ?? {};\n  // return ip by default\n  return id || req.ip;\n};\n\n/**\n * 429 handler\n * Used by all rate limiters\n */\nconst excess_handler = (req: Request, res: Response, next) => {\n  const { authorized } = (res.locals.authorization as AuthorizationInfo) ?? {};\n  // 429 handler\n  if (authorized) {\n    res\n      .status(429)\n      .send(\n        \"Too Many Requests.\\nYou have reached the maximum number of requests per hour for Free tier. Please upgrade your plan to remove this limit.\"\n      );\n  } else {\n    res\n      .status(429)\n      .send(\n        \"Too Many Requests.\\nThis request is anonymous and rate limited. To upgrade, please add an api key at https://cors.sh\"\n      );\n  }\n};\n\nconst limiter_free_per_hour = rateLimit({\n  // Rate limiter configuration\n  windowMs: 60 * 1000 * 60, // 1 hour\n  max: (req: Request, res: Response) => {\n    const { tier } = (res.locals.authorization as AuthorizationInfo) ?? {};\n\n    switch (tier) {\n      case \"unlimited\":\n        return Infinity;\n      case \"free\":\n        return RATE_LIMIT_FREE_AUTHORIZED_PER_HOUR; // per hour\n      case \"anonymous\":\n        return RATE_LIMIT_FREE_ANONYMOUS_PER_HOUR; // per hour\n      case \"2022.t1\":\n        // legacy, all free version\n        return RATE_LIMIT_FREE_AUTHORIZED_PER_HOUR; // per hour\n      case \"2023.t1\":\n      case \"2023.t2\": {\n        // paid version\n        // no limit, passed by skip()\n        return Infinity;\n      }\n      default:\n        // anonymous (free)\n        return RATE_LIMIT_FREE_ANONYMOUS_PER_HOUR; // per hour\n    }\n  },\n  standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers\n  legacyHeaders: false, // Disable the `X-RateLimit-*` headers\n  skip: (req: Request, res: Response) => {\n    if (req.method == \"OPTIONS\" || req.method == \"HEAD\") {\n      return true;\n    }\n\n    let force_skip = false;\n\n    const { skip_rate_limit, tier } =\n      (res.locals.authorization as AuthorizationInfo) ?? {};\n\n    if (tier === \"2023.t1\" || tier === \"2023.t2\") {\n      // since no monthly limit, skip the hourly limit for paid version\n      force_skip = true;\n    }\n\n    if (force_skip || skip_rate_limit) {\n      return true;\n    }\n\n    res.locals.rate_limit_handled = true;\n    return false;\n  },\n  keyGenerator: keygenerator,\n  handler: excess_handler,\n  // Redis store configuration\n  store: new RedisStore({\n    sendCommand: (...args: string[]) => client.sendCommand(args),\n  }),\n});\n\nconst MONTH28MS = 60 * 1000 * 60 * 24 * 28; // 28 days in ms\n\n// Consider: monthly rate limit might require bigger redis instance\nconst limiter_paid_per_month = rateLimit({\n  // 28-day month\n  windowMs: MONTH28MS,\n  // max: RATE_LIMIT_PAID_PER_MONTH_T1,\n  max: (req: Request, res: Response) => {\n    const { tier } = (res.locals.authorization as AuthorizationInfo) ?? {};\n\n    switch (tier) {\n      case \"2023.t1\": {\n        // paid version tier 1 ($4 / Mo)\n        return RATE_LIMIT_PAID_PER_MONTH_T1;\n      }\n      case \"2023.t2\": {\n        // paid version tier 2 ($20 / Mo)\n        return RATE_LIMIT_PAID_PER_MONTH_T2;\n      }\n      case \"unlimited\":\n        return Infinity;\n      case \"anonymous\": // anon, blocked by hourly limiter anyway\n      case \"free\": // free, blocked by hourly limiter anyway\n      case \"2022.t1\": // legacy, all free version\n      default:\n        // anonymous (free)\n        return RATE_LIMIT_PAID_PER_MONTH_DEFAULT; // per month\n    }\n  },\n  skip: (req: Request, res: Response) => {\n    if (req.method == \"OPTIONS\" || req.method == \"HEAD\") {\n      return true;\n    }\n\n    // if rate limit handled by hourly limiter, skip this one\n    return res.locals.rate_limit_handled;\n  },\n  keyGenerator: keygenerator,\n  handler: excess_handler,\n  store: new RedisStore({\n    sendCommand: (...args: string[]) => client.sendCommand(args),\n  }),\n});\n\n// FIXME: - We have to merge the two limiters into one - seems the skip does not do what we thought it does (It always uses the hourly limiter. even for paid plan, causing it to be infinite)\nexport default {\n  hourly: limiter_free_per_hour,\n  monthly: limiter_paid_per_month,\n};\n"
  },
  {
    "path": "services/proxy.cors.sh/src/usage/README.md",
    "content": "# Usage Logging\n\n## Data we collect\n\n- client ip (for managing x-rate-limit)\n- client ua (for malicious usage preventation)\n- request url information (only host) (for understanding api usage)\n    - we do not collect request query\n    - we do not collect request body\n    - we do not collect response body\n- response result (for collecting success rate) (collection as http status code)\n- duration (for tracking x-rate-limit for execution time)\n- payload (for tracking x-rate-limit for data transfer)\n- app (for identifying the request) (this is determined by api key you are using. pretty obvious)"
  },
  {
    "path": "services/proxy.cors.sh/src/usage/dynamo/log.ts",
    "content": "import type { CorsProxyApiRequest, CorsProxyApiRequestLog } from \"../type\";\nimport { CorsRequestLogModel } from \"./model\";\nimport { nanoid } from \"nanoid\";\nimport * as dynamoose from \"dynamoose\";\nimport * as https from \"https\";\n\nconst agent = new https.Agent({\n  keepAlive: true,\n  rejectUnauthorized: true,\n  maxSockets: 1000,\n});\n\ndynamoose.aws.sdk.config.update({\n  httpOptions: {\n    agent: agent,\n  },\n});\n\nasync function log(\n  request: CorsProxyApiRequest & {\n    billed_duration: number;\n  }\n) {\n  // console.log(\"request\", request);\n  const id = nanoid();\n  try {\n    const payload = new CorsRequestLogModel(<CorsProxyApiRequestLog>{\n      id: id,\n      ...request,\n    });\n\n    await payload.save();\n  } catch (_) {\n    // do nothing\n  }\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/src/usage/dynamo/model.ts",
    "content": "import * as dynamoose from \"dynamoose\";\nimport { nanoid } from \"nanoid\";\n\nexport const CorsRequestLogSchema = new dynamoose.Schema(\n  {\n    id: {\n      type: String,\n      required: true,\n      default: () => nanoid(),\n    },\n    app: {\n      type: String,\n      required: true,\n      index: {\n        name: \"appIndex\",\n      },\n    },\n    ua: {\n      type: String,\n      required: false,\n    },\n    at: {\n      type: Date,\n      required: true,\n    },\n    size: {\n      type: Number,\n      required: true,\n    },\n    ip: {\n      type: String,\n      required: false,\n    },\n    target: {\n      type: String,\n      required: true,\n    },\n    duration: {\n      type: Number,\n      required: true,\n    },\n    billed_duration: {\n      type: Number,\n      required: true,\n    },\n  },\n  {\n    saveUnknown: true,\n  }\n);\n\nconst CORS_REQUEST_LOG_TABLE_NAME = process.env\n  .DYNAMODB_TABLE_USAGE_LOG as string;\n\nexport const CorsRequestLogModel = dynamoose.model(\n  CORS_REQUEST_LOG_TABLE_NAME,\n  CorsRequestLogSchema,\n  {\n    create: true,\n  }\n);\n"
  },
  {
    "path": "services/proxy.cors.sh/src/usage/dynamo/readme.md",
    "content": "# dynamo logger (disabled)\n\nto enable,\n\n- update serverless.yml using table.yml\n- install dynamoose\n"
  },
  {
    "path": "services/proxy.cors.sh/src/usage/dynamo/table.yml",
    "content": "# use this configuration on serverless.yml to enable logging.\n\nprovider:\n  name: aws\n  environment:\n    DYNAMODB_TABLE_USAGE_LOG: \"${self:service}-usage-log-${opt:stage, self:provider.stage}\"\n\n  iamRoleStatements:\n    - Effect: Allow\n      Action:\n        - dynamodb:Query\n        - dynamodb:Scan\n        - dynamodb:GetItem\n        - dynamodb:PutItem\n        - dynamodb:UpdateItem\n        - dynamodb:DeleteItem\n        - dynamodb:DescribeTable\n      Resource: \"arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE_USAGE_LOG}\"\n\nresources:\n  Resources:\n    usageLogTable:\n      Type: AWS::DynamoDB::Table\n      Properties:\n        TableName: \"${self:provider.environment.DYNAMODB_TABLE_USAGE_LOG}\"\n        KeySchema:\n          - AttributeName: id\n            KeyType: HASH\n        AttributeDefinitions:\n          - AttributeName: id\n            AttributeType: S\n          - AttributeName: app\n            AttributeType: S\n        GlobalSecondaryIndexes:\n          - IndexName: appIndex\n            KeySchema:\n              - AttributeName: app\n                KeyType: HASH\n            Projection:\n              ProjectionType: \"ALL\"\n            ProvisionedThroughput:\n              ReadCapacityUnits: 10\n              WriteCapacityUnits: 10 # no write is required\n        ProvisionedThroughput:\n          ReadCapacityUnits: 10 # no read is required\n          WriteCapacityUnits: 10\n"
  },
  {
    "path": "services/proxy.cors.sh/src/usage/index.ts",
    "content": "import * as express from \"express\";\nimport type { CorsProxyApiRequestLog } from \"./type\";\n\ntype Log = Omit<CorsProxyApiRequestLog, \"id\">;\n\n/**\n *\n * @param req\n * @param res\n */\nexport async function logRequest(req: express.Request, res: express.Response) {\n  // do not log the request if client error. (413 or 401 or 400)\n  if (500 > res.statusCode && res.statusCode >= 400) {\n    return;\n  }\n  const ip = (req.headers[\"x-forwarded-for\"] ||\n    req.socket.remoteAddress) as string;\n  const timestamp = new Date();\n  const origin = req.headers[\"origin\"] || undefined;\n  //@ts-ignore (useragent is provided by above useragent.express())\n  const _uaobj = req.useragent;\n  const ua = _uaobj.source; // gives the raw ua string\n  const url = res.get(\"x-request-url\");\n  const payload = Number(\n    (() => {\n      const _cl = res.get(\"content-length\");\n      return _cl ? _cl : 0;\n    })()\n  );\n  const duration = Number(\n    (() => {\n      const _rt = res.get(\"x-response-time\");\n      return _rt ? _rt : 0;\n    })()\n  );\n\n  const billed_duration = Math.ceil(duration / 100) * 100; // billed duration is stepped by 100ms\n  // request id from api gateway\n  // const request_id = req.headers[\"x-request-id\"] as string;\n\n  const metric: Log = {\n    ip: ip,\n    origin: origin,\n    ua: ua,\n    duration: duration,\n    size: payload,\n    at: timestamp,\n    target: url,\n    app: \"anonymous\",\n    billed_duration,\n  };\n\n  systemlog(metric);\n  // await log(metric);\n}\n\nfunction systemlog(request: Log) {\n  console.log(request);\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/src/usage/type.ts",
    "content": "export type AppId = string | \"anonymous\" | \"official-demo\";\n\nexport interface CorsProxyApiRequest {\n  /**\n   * ip address of request client (could be server / app / web)\n   */\n  ip?: string;\n  /**\n   * compressed / raw user agent data of the request\n   */\n  ua?: string;\n\n  /**\n   * request origin from request headers\n   */\n  origin?: string;\n\n  /**\n   * target resource url\n   */\n  target?: string;\n  /**\n   * duration in ms\n   */\n  duration: number;\n  /**\n   * data payload\n   */\n  size: number;\n\n  /**\n   * request timestamp\n   */\n  at: Date;\n\n  /**\n   * the user/requester app\n   */\n  app: AppId;\n}\n\nexport interface CorsProxyApiRequestLog extends CorsProxyApiRequest {\n  /**\n   * unique id of the request\n   */\n  id: string;\n\n  /**\n   * billing duration in ms - ceils with 100ms\n   */\n  billed_duration: number;\n}\n"
  },
  {
    "path": "services/proxy.cors.sh/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"preserveConstEnums\": true,\n    \"strictNullChecks\": true,\n    \"sourceMap\": false,\n    \"allowJs\": true,\n    \"strict\": false,\n    \"target\": \"es5\",\n    \"outDir\": \".build\",\n    \"moduleResolution\": \"node\",\n    \"lib\": [\"es2015\"],\n    \"rootDir\": \"./\"\n  }\n}\n"
  },
  {
    "path": "services/readme.md",
    "content": "# service layers\n\n- proxy layer\n  - contributes only to proxying the request.\n  - shards part of the service layer db once a day.\n  - uses dynamo db & takes full serverless approach.\n  - handles millions of requests every hour.\n- service layer\n  - contributes to authorization and subscriptions management.\n  - uses mongodb\n  - uess prisma\n  - can be alted to ec2 instalce service.\n  - handles few thousand requests every hour.\n\n## Shared keys\n\n- `JWT_SECRET` the jwt secret is shared accross services, the token issueing can be only done by the service layer, and the proxy layer can only verify the token.\n"
  },
  {
    "path": "services/services.cors.sh/.gitignore",
    "content": ".build\n.webpack\n\n# we use yarn.\npackage-lock.json\n\n# env\n.env\n.env.staging\n.env.production\n.env.local\n.env.*.local"
  },
  {
    "path": "services/services.cors.sh/app.ts",
    "content": "import express from \"express\";\nimport useragent from \"express-useragent\";\nimport cors from \"cors\";\nimport bodyParser from \"body-parser\";\nimport router from \"./routes\";\n\nfunction logErrors(err, req, res, next) {\n  console.error(err.stack);\n  next(err);\n}\n\nconst app = express();\n\napp.get(\"/\", (req, res) => {\n  res.redirect(\"https://cors.sh\");\n});\n\napp.use(cors());\n\napp.use(\n  bodyParser.urlencoded({\n    extended: true,\n  })\n);\n\napp.use(useragent.express());\n\napp.use(bodyParser.json());\n\napp.use(logErrors);\n\napp.use(router);\n\nexport { app };\n"
  },
  {
    "path": "services/services.cors.sh/auth/index.ts",
    "content": "export * from \"./key\";\nexport * from \"./jwt\";\nexport * from \"./middleware\";\n"
  },
  {
    "path": "services/services.cors.sh/auth/jwt.ts",
    "content": "import jwt from \"jsonwebtoken\";\n\nconst _SECRET = process.env.SERVICE_JWT_SECRET as string;\n\nexport interface ServiceUserSignature {\n  type: \"customer\";\n  service: \"cors.sh\";\n  id: string;\n}\n\nexport function decode_jwt(token: string): ServiceUserSignature {\n  return jwt.verify(token, _SECRET) as ServiceUserSignature;\n}\n\nexport function encode_jwt(service_user_id: string): string {\n  const payload: ServiceUserSignature = {\n    type: \"customer\",\n    service: \"cors.sh\",\n    id: service_user_id,\n  };\n  return jwt.sign(payload, _SECRET);\n}\n\nexport function verify(token: string) {\n  const pl = jwt.verify(token, _SECRET) as ServiceUserSignature;\n  if (pl.service == \"cors.sh\" && pl.type == \"customer\") {\n    return pl.id;\n  } else {\n    throw new Error(\"invalid token\");\n  }\n}\n"
  },
  {
    "path": "services/services.cors.sh/auth/key.ts",
    "content": "export const SECURE_BROWSER_COOKIE_AUTH_KEY = \".cors.sh/auth-token\";\n"
  },
  {
    "path": "services/services.cors.sh/auth/middleware.ts",
    "content": "import { verify } from \"./jwt\";\nimport { Request, Response } from \"express\";\nimport { prisma, stripe } from \"../clients\";\nimport { SECURE_BROWSER_COOKIE_AUTH_KEY } from \"./key\";\n\n// since the secure cookie cannot be forged, we have no additional validation on the jwt from client's secure http only cookie.\n// the possible hacking scenario is..\n// 1. the hacker steals the jwt secret (impossible, but somehow.)\n// 2. the hacker finds out the customer id of services.cors.sh\n// 3. the hacker sets the cookie with the jwt token, and the customer id.\n// - seems secure enough for now.\nexport async function authMiddleware(req: Request, res: Response, next) {\n  const _1 = await standardAuthorizer(req);\n  if (_1) {\n    const { customer } = _1;\n    res.locals.customer = customer;\n    next();\n    return;\n  }\n\n  const _2 = await checoutSessionAuthorizer(req);\n  if (_2) {\n    const { customer } = _2;\n    res.locals.customer = customer;\n    next();\n    return;\n  }\n\n  return res.status(401).json({ error: \"Unauthorized\" });\n}\n\nasync function checoutSessionAuthorizer(req: Request) {\n  const checkout_session_id = req.headers[\"x-cors-service-checkout-session-id\"];\n  if (!checkout_session_id) {\n    return false;\n  }\n\n  const checkout_session = await stripe.checkout.sessions.retrieve(\n    checkout_session_id as string\n  );\n\n  const { customer: stripe_customer_id } = checkout_session;\n\n  const customer = await prisma.customer.findUnique({\n    where: {\n      stripeId: stripe_customer_id as string,\n    },\n  });\n\n  if (!customer) {\n    return false;\n  }\n\n  return {\n    customer,\n  };\n}\n\nasync function standardAuthorizer(req: Request): Promise<false | { customer }> {\n  const authorization = req.signedCookies?.[SECURE_BROWSER_COOKIE_AUTH_KEY];\n  if (!authorization) {\n    return false;\n  }\n\n  try {\n    const customerid = verify(authorization);\n    const customer = await prisma.customer.findUnique({\n      where: {\n        id: customerid,\n      },\n    });\n\n    return {\n      customer,\n    };\n  } catch (e) {\n    return false;\n  }\n}\n"
  },
  {
    "path": "services/services.cors.sh/auth/readme.md",
    "content": "# service auth (not proxy.cors.sh api key auth)\n\n## .env\n\n```\n...\nSERVICE_JWT_SECRET=...\n...\n```\n"
  },
  {
    "path": "services/services.cors.sh/clients/index.ts",
    "content": "export * from \"./prisma\";\nexport * from \"./stripe\";\nexport * from \"./ses\";\n"
  },
  {
    "path": "services/services.cors.sh/clients/prisma.ts",
    "content": "import { PrismaClient } from \"@prisma/client\";\n\nexport const prisma = new PrismaClient();\n"
  },
  {
    "path": "services/services.cors.sh/clients/ses.ts",
    "content": "import * as AWS from \"@aws-sdk/client-ses\";\n\nconst SENDER_EMAIL = \"no-reply@cors.sh\";\nconst ses = new AWS.SES({});\n\ninterface EmailBody_Raw {\n  subject: string;\n  text: string;\n}\n\ninterface EmailBody_Html {\n  subject: string;\n  html: string;\n}\n\ntype EmailContent = EmailBody_Raw | EmailBody_Html;\n\nexport function emailWithTemplate(to: string, template: string, data: object) {\n  return ses.sendTemplatedEmail({\n    Destination: {\n      ToAddresses: [to],\n    },\n    Source: SENDER_EMAIL,\n    Template: template,\n    TemplateData: JSON.stringify(data),\n  });\n}\n\nexport async function email(\n  to: string,\n  content: EmailContent\n): Promise<boolean> {\n  try {\n    const sendResp = await ses.sendEmail({\n      Destination: {\n        ToAddresses: [to],\n      },\n      Source: SENDER_EMAIL,\n      Message: {\n        Subject: {\n          Data: content.subject,\n        },\n        Body:\n          \"text\" in content\n            ? {\n                Text: {\n                  Data: content.text,\n                },\n              }\n            : {\n                Html: {\n                  Data: content.html,\n                },\n              },\n      },\n    });\n  } catch (error) {\n    console.error(error);\n    throw new Error(\"failed while sending email\");\n  }\n  return true;\n}\n"
  },
  {
    "path": "services/services.cors.sh/clients/slack.ts",
    "content": "// send message to slack for telementry\n\nimport axios from \"axios\";\nconst token = process.env.SLACK_TOKEN;\nconst channel = process.env.SLACK_CHANNEL;\n\nexport const blocks = ({\n  title,\n  data,\n}: {\n  title: string;\n  data: { [key: string]: any };\n}) => {\n  return [\n    {\n      type: \"section\",\n      text: {\n        type: \"mrkdwn\",\n        text: `*${title}*`,\n      },\n    },\n    ...Object.keys(data).reduce((acc, key) => {\n      const value = data[key];\n      if (value === undefined) {\n        return acc;\n      }\n\n      const valuetext = pretty_value(value);\n      const keytext = pretty_key(key);\n\n      return [\n        ...acc,\n        {\n          type: \"section\",\n          text: {\n            type: \"mrkdwn\",\n            text: `${keytext}: ${valuetext}`,\n          },\n        },\n      ];\n    }, []),\n  ];\n};\n\nconst pretty_value = (value: any) => {\n  switch (typeof value) {\n    case \"string\":\n      return value;\n    case \"number\":\n      return value.toString();\n    case \"object\":\n    default:\n      return JSON.stringify(value);\n  }\n};\n\n/**\n * pretty print the key string\n *\n * - replace `_` and `-` with ` `\n * - capitalize the first letter\n *\n * e.g.\n * - total_users_so_far -> Total users so far\n */\nfunction pretty_key(key: string) {\n  return key\n    .replace(/_/g, \" \")\n    .replace(/-/g, \" \")\n    .replace(/^\\w/, (c) => c.toUpperCase());\n}\n\nexport function slack({ blocks }: { blocks: Array<object> }) {\n  return axios.post(\n    \"https://slack.com/api/chat.postMessage\",\n    {\n      blocks: blocks,\n      // env Channel Id\n      channel: channel,\n    },\n    {\n      headers: {\n        // env TOKEN\n        Authorization: `Bearer ${token}`,\n      },\n    }\n  );\n}\n"
  },
  {
    "path": "services/services.cors.sh/clients/stripe.ts",
    "content": "import { Stripe } from \"stripe\";\n\nexport const stripe = new Stripe(process.env.STRIPE_API_KEY, {\n  apiVersion: \"2022-08-01\",\n});\n"
  },
  {
    "path": "services/services.cors.sh/controllers/_telemetry.ts",
    "content": "import type { OnboardingApplications, Application } from \"@prisma/client\";\nimport { prisma } from \"../clients\";\nimport { slack, blocks } from \"../clients/slack\";\n\nexport async function logNewOnboardingProc(data: OnboardingApplications) {\n  const total = await prisma.onboardingApplications.count();\n  const { id, name, email } = data;\n  const title = `New Onboarding Application`;\n  const dataToLog = {\n    name,\n    email,\n    total,\n  };\n  const message = blocks({ title, data: dataToLog });\n  await slack({ blocks: message });\n}\n\nexport async function logNewApplication(data: Application) {\n  const total = await prisma.application.count();\n  const { name } = data;\n  const title = `New Application`;\n  const dataToLog = {\n    name,\n    total,\n  };\n  const message = blocks({ title, data: dataToLog });\n  await slack({ blocks: message });\n}\n"
  },
  {
    "path": "services/services.cors.sh/controllers/applications.ts",
    "content": "import { prisma, stripe, emailWithTemplate } from \"../clients\";\nimport { sign_live_key, sign_temporary_key, sign_test_key } from \"../keygen\";\nimport { nanoid } from \"nanoid\";\nimport type { Application, OnboardingApplications } from \"@prisma/client\";\nimport sync from \"../sync\";\nimport { logNewOnboardingProc, logNewApplication } from \"./_telemetry\";\n\ntype CreateOnboardingApplicationBody =\n  | {\n      type: \"with-form\";\n      name: string;\n      allowedOrigins: string[];\n      priceId: string;\n    }\n  | {\n      type: \"with-email\";\n      email: string;\n    };\n\nexport async function createOnboardingApplication(\n  body: CreateOnboardingApplicationBody,\n  send_onboarding_email_if_possible: boolean = true\n) {\n  let email_available = false;\n  let email: string;\n  let name: string;\n  let allowedOrigins: string[];\n  let priceId: string;\n\n  switch (body.type) {\n    case \"with-email\": {\n      email = body.email;\n      name = \"My first cors.sh app\";\n      email_available = true;\n      break;\n    }\n    case \"with-form\": {\n      email = `${nanoid(10)}@unknown-users.cors.sh`;\n      name = body.name;\n      allowedOrigins = body.allowedOrigins;\n      priceId = body.priceId;\n      break;\n    }\n  }\n\n  const { key: tmpkey, expires_at } = sign_temporary_key();\n\n  let data: OnboardingApplications;\n\n  // check if email exists\n  let duplicated: OnboardingApplications | null = null;\n  if (email_available) {\n    duplicated = await prisma.onboardingApplications.findUnique({\n      where: { email: email },\n    });\n  }\n\n  // if duplicated, update the existing one with the new key.\n  if (duplicated) {\n    // if the last interaction is within 5 minutes, ignore the request\n    if (duplicated.updatedAt) {\n    }\n    data = await prisma.onboardingApplications.update({\n      where: { id: duplicated.id },\n      data: {\n        // this will be renewed since the signature contains the expiration time\n        key: tmpkey,\n      },\n    });\n  } else {\n    data = await prisma.onboardingApplications.create({\n      data: {\n        key: tmpkey,\n        email: email,\n        name: name,\n        allowedOrigins: allowedOrigins ?? [],\n        expiresAt: expires_at.toDate(),\n        priceId,\n      },\n    });\n  }\n\n  if (email_available && send_onboarding_email_if_possible) {\n    // send an email to the user\n    const res = await sendOnboardingEmail(email, data);\n    console.log(\"email sent\", res);\n  }\n\n  // log event to slack\n  try {\n    !duplicated && (await logNewOnboardingProc(data));\n  } catch (e) {\n    console.error(\"failed to log new onboarding application\", e); // not critical\n  }\n\n  return {\n    id: data.id,\n    // omit the key since it also works as a verification code for faster signup\n    // key: \"omitted\"\n    email: data.email,\n    name: data.name,\n    allowedOrigins: data.allowedOrigins,\n    priceId: data.priceId,\n  };\n}\n\nexport async function getOnboardingApplication(id: string) {\n  return await prisma.onboardingApplications.findUnique({\n    where: { id: id },\n    select: {\n      // omit private data\n      id: true,\n      name: true,\n      email: true,\n      allowedOrigins: true,\n      priceId: true,\n      createdAt: true,\n      updatedAt: true,\n    },\n  });\n}\n\nasync function sendOnboardingEmail(\n  email: string,\n  application?: OnboardingApplications\n) {\n  if (!application) {\n    application = await prisma.onboardingApplications.findUnique({\n      where: { email: email },\n    });\n  }\n\n  if (!application) {\n    return false;\n  }\n\n  const { key, emailSentAt } = application;\n\n  // if the last interaction is within 1 minutes, ignore the request\n  if (emailSentAt && emailSentAt.length > 0) {\n    const lastSentAt = emailSentAt[emailSentAt.length - 1];\n    if (lastSentAt) {\n      const diff = new Date().getTime() - lastSentAt.getTime();\n      if (diff < 60000) {\n        return false;\n      }\n    }\n  }\n\n  // sls stage\n\n  await emailWithTemplate(\n    email,\n    `mail_cors_sh_onboarding_${process.env.STAGE}`,\n    {\n      CODE: key,\n      ONBOARDINGLINK: `https://cors.sh/onboarding/${application.id}`,\n    }\n  );\n\n  // update the tmp app\n  await prisma.onboardingApplications.update({\n    where: { id: application.id },\n    data: {\n      emailSentAt: {\n        push: new Date(),\n      },\n    },\n  });\n\n  return true;\n}\n\nexport async function signApplication(application: Application) {\n  //\n  const { key: apikey_test } = sign_test_key(application.signature_test);\n  const { key: apikey_live } = sign_live_key(application.signature_live);\n\n  const payload = {\n    ...application,\n    apikey_test,\n    apikey_live,\n  };\n\n  return payload;\n}\n\nexport async function createApplication({\n  id,\n  name,\n  allowedOrigins,\n  owner,\n}: {\n  id?: string;\n  name: string;\n  allowedOrigins?: string[];\n  owner: { id: string };\n}) {\n  const placeholder_test = nanoid(10);\n  const placeholder_live = nanoid(10);\n\n  const application = await prisma.application.create({\n    data: {\n      id: id,\n      name: name,\n      signature_test: placeholder_test,\n      signature_live: placeholder_live,\n      allowedOrigins: allowedOrigins ?? [],\n      owner: {\n        connect: {\n          id: owner.id,\n        },\n      },\n    },\n  });\n\n  const salt_test = nanoid(10);\n  const salt_live = nanoid(10);\n  const signature_test = application.id + salt_test;\n  const signature_live = application.id + salt_live;\n\n  const updated = await prisma.application.update({\n    where: { id: application.id },\n    data: {\n      signature_test,\n      signature_live,\n    },\n  });\n\n  // once application is created and initially signed, sync to the keys table\n  await sync(updated);\n\n  return updated;\n}\n\nexport async function convertApplication({\n  onboarding_id,\n  checkout_session_id,\n}: {\n  onboarding_id: string;\n  checkout_session_id: string;\n}) {\n  const checkout_session = await stripe.checkout.sessions.retrieve(\n    checkout_session_id as string\n  );\n\n  const { customer: stripe_customer } = checkout_session;\n  const stripe_customer_id =\n    typeof stripe_customer === \"string\" ? stripe_customer : stripe_customer.id;\n\n  const customer = await prisma.customer.findUnique({\n    where: { stripeId: stripe_customer_id },\n  });\n\n  // place this right before application create since its a delete and cannot be undone. (reduce chance of error when it fails)\n  const tmp = await prisma.onboardingApplications.delete({\n    where: { id: onboarding_id as string },\n  });\n\n  const application = await createApplication({\n    id: tmp.id,\n    name: tmp.name || \"Untitled\",\n    allowedOrigins: tmp.allowedOrigins,\n    owner: { id: customer.id },\n  });\n\n  const signed = await signApplication(application);\n\n  // once application is created and initially signed, sync to the keys table\n  await sync(application);\n\n  // send email to the user\n  try {\n    await emailWithTemplate(\n      customer.email,\n      `mail_cors_sh_onboarding_with_payment_success_${process.env.STAGE}`,\n      {\n        APPLICATIONNAME: tmp.name,\n        CODE_LIVE: signed.apikey_live,\n        CODE_TEST: signed.apikey_test,\n      }\n    );\n    console.log(\"email sent\", true);\n    //   // set email verified to true?\n  } catch (e) {\n    console.error(\"email failed\", e);\n  }\n\n  // log event to slack\n  try {\n    await logNewApplication(signed);\n  } catch (e) {\n    console.error(\"failed to log new application\", e); // not critical\n  }\n\n  return application;\n}\n"
  },
  {
    "path": "services/services.cors.sh/deploy/dev.sh",
    "content": "sls deploy --stage development \\\n  --param=\"domain=false\" \\\n  --param=\"service-keys-rcu=1\" \\\n  --param=\"service-keys-wcu=1\""
  },
  {
    "path": "services/services.cors.sh/deploy/prod.sh",
    "content": "sls deploy --stage production \\\n  --param=\"domain=true\" \\\n  --param=\"service-keys-rcu=256\" \\\n  --param=\"service-keys-wcu=4\""
  },
  {
    "path": "services/services.cors.sh/deploy/staging.sh",
    "content": "sls deploy --stage staging \\\n  --param=\"domain=false\" \\\n  --param=\"service-keys-rcu=1\" \\\n  --param=\"service-keys-wcu=1\""
  },
  {
    "path": "services/services.cors.sh/index.ts",
    "content": "import { configure as serverlessExpress } from \"@codegenie/serverless-express\";\nimport { APIGatewayProxyHandler } from \"aws-lambda\";\n\nimport { app } from \"./app\";\n\nexport const handler: APIGatewayProxyHandler = serverlessExpress({ app });\n"
  },
  {
    "path": "services/services.cors.sh/jest.config.js",
    "content": "// jest config - ts\nmodule.exports = {\n  preset: \"ts-jest\",\n  setupFiles: [\"<rootDir>/test/setup-tests.ts\"],\n};\n"
  },
  {
    "path": "services/services.cors.sh/keygen/index.ts",
    "content": "import crypto from \"crypto\";\nimport day from \"dayjs\";\n\nconst API_KEY_TEMP_AES_KEY = process.env.API_KEY_TEMP_AES_KEY;\nconst _aes_key = Buffer.from(API_KEY_TEMP_AES_KEY, \"hex\");\nconst API_KEY_TEMP_AES_IV: string = process.env.API_KEY_TEMP_AES_IV!;\nconst _aes_iv = Buffer.from(API_KEY_TEMP_AES_IV, \"hex\");\n\nconst API_KEY_TEST_HASH_SECRET = process.env.API_KEY_TEST_HASH_SECRET;\nconst API_KEY_LIVE_HASH_SECRET = process.env.API_KEY_LIVE_HASH_SECRET;\n\nconst API_KEY_HASH_SECRET_BY_TYPE = {\n  test: API_KEY_TEST_HASH_SECRET,\n  live: API_KEY_LIVE_HASH_SECRET,\n} as const;\n\ninterface PermanentKey {\n  signature: string;\n  type: \"test\" | \"live\";\n}\n\nconst TMP_KEY_PREFIX = \"temp\";\nconst TEST_KEY_PREFIX = \"test\";\nconst LIVE_KEY_PREFIX = \"live\";\nconst TMP_KEY_EXP_IN_DAYS = 3;\n\nfunction test_key(signature: string): PermanentKey {\n  return {\n    signature,\n    type: \"test\",\n  };\n}\n\nfunction live_key(signature: string): PermanentKey {\n  return {\n    signature,\n    type: \"live\",\n  };\n}\n\nexport function sign_temporary_key() {\n  const expires_at = day().add(TMP_KEY_EXP_IN_DAYS, \"day\");\n\n  const token = expires_at.unix().toString();\n\n  // convert to complex string with aes\n  let cipher = crypto.createCipheriv(\"aes-256-cbc\", _aes_key, _aes_iv);\n\n  // this may be considered as insecure. yes. it may be vulnerable to ciphertext attack,\n  // even though it is fine because we have a rate-limit for temporary api keys and it does not have a clear usecase. for hackers to take benefits from this.\n  let encrypted = cipher.update(token, \"utf8\", \"hex\") + cipher.final(\"hex\");\n\n  return {\n    key: prefix(\"temp\") + \"_\" + encrypted,\n    expires_at,\n  };\n}\n\nexport function sign(signature: string, type: \"test\" | \"live\") {\n  switch (type) {\n    case \"test\":\n      return sign_test_key(signature);\n    case \"live\":\n      return sign_live_key(signature);\n  }\n}\n\nexport function sign_test_key(signature: string) {\n  const token = encrypt(test_key(signature), \"test\");\n  return {\n    key: prefix(\"test\") + \"_\" + token,\n    token,\n  };\n}\n\nexport function sign_live_key(signature: string) {\n  const token = encrypt(live_key(signature), \"live\");\n\n  return {\n    key: prefix(\"live\") + \"_\" + token,\n    token,\n  };\n}\n\n//\n// migrate this to cors.sh\nexport function validate_jwt(token: string) {\n  if (token.startsWith(TMP_KEY_PREFIX)) {\n    //\n  } else if (token.startsWith(TEST_KEY_PREFIX)) {\n    //\n  } else if (token.startsWith(LIVE_KEY_PREFIX)) {\n    //\n  } else {\n    throw \"Invalid token format\";\n  }\n}\n\nfunction prefix(type: \"test\" | \"live\" | \"temp\") {\n  switch (type) {\n    case \"test\":\n      return TEST_KEY_PREFIX;\n    case \"live\":\n      return LIVE_KEY_PREFIX;\n    case \"temp\":\n      return TMP_KEY_PREFIX;\n  }\n}\n\nfunction encrypt(data: string | object, type: \"test\" | \"live\") {\n  const key = API_KEY_HASH_SECRET_BY_TYPE[type];\n  const hmac = crypto.createHmac(\"sha256\", key);\n  hmac.update(JSON.stringify(data));\n  return hmac.digest(\"hex\");\n}\n"
  },
  {
    "path": "services/services.cors.sh/package.json",
    "content": "{\n  \"name\": \"@cors.sh/service-layer\",\n  \"version\": \"0.0.0\",\n  \"description\": \"CORS.SH's service layer for managing api keys & subscription\",\n  \"homepage\": \"https://cors.sh\",\n  \"scripts\": {\n    \"prisma:format\": \"npx prisma format\",\n    \"prisma:studio\": \"npx prisma studio\",\n    \"prisma:generate\": \"npx prisma generate\",\n    \"prisma:generate:watch\": \"npx prisma generate --watch\",\n    \"postinstall\": \"npm run prisma:generate && npm run prisma:format\",\n    \"db:push\": \"npx prisma db push\",\n    \"clean\": \"rimraf .build\",\n    \"dev\": \"sls offline start --stage development\",\n    \"deploy:staging\": \"yarn clean && source ./deploy/staging.sh\",\n    \"deploy:prod\": \"yarn clean && yarn db:push && source ./deploy/prod.sh\",\n    \"deploy:dev\": \"yarn clean && yarn db:push && source ./deploy/dev.sh\",\n    \"package\": \"sls package --stage development\",\n    \"sls:ses-template:deply\": \"sls ses-template deploy --stage development\",\n    \"test\": \"jest\"\n  },\n  \"dependencies\": {\n    \"@aws-sdk/client-ses\": \"^3.163.0\",\n    \"@codegenie/serverless-express\": \"^4.16.0\",\n    \"@prisma/client\": \"^4.8.0\",\n    \"axios\": \"^1.2.2\",\n    \"body-parser\": \"^1.20.0\",\n    \"cors\": \"^2.8.5\",\n    \"dayjs\": \"^1.11.5\",\n    \"express\": \"^4.18.1\",\n    \"express-useragent\": \"^1.0.15\",\n    \"jsonwebtoken\": \"^8.5.1\",\n    \"multer\": \"^1.4.4\",\n    \"nanoid\": \"^3.3.3\",\n    \"reading-time\": \"^1.5.0\",\n    \"serverless-http\": \"^3.0.1\",\n    \"stripe\": \"^10.7.0\"\n  },\n  \"devDependencies\": {\n    \"@haftahave/serverless-ses-template\": \"^4.0.5\",\n    \"@types/aws-lambda\": \"^8.10.95\",\n    \"@types/body-parser\": \"^1.19.2\",\n    \"@types/cors\": \"^2.8.12\",\n    \"@types/express\": \"^4.17.13\",\n    \"@types/jest\": \"^29.2.5\",\n    \"@types/jsonwebtoken\": \"^8.5.9\",\n    \"@types/multer\": \"^1.4.7\",\n    \"@types/node\": \"^17.0.29\",\n    \"jest\": \"^29.3.1\",\n    \"rimraf\": \"^3.0.2\",\n    \"serverless\": \"^3.17.0\",\n    \"serverless-domain-manager\": \"^6.0.3\",\n    \"serverless-offline\": \"^8.7.0\",\n    \"serverless-webpack\": \"^5.15.1\",\n    \"serverless-webpack-prisma\": \"^1.1.0\",\n    \"ts-jest\": \"^29.0.3\",\n    \"ts-loader\": \"^9.3.0\",\n    \"typescript\": \"^5.8.2\",\n    \"webpack\": \"^5.72.1\",\n    \"webpack-node-externals\": \"^3.0.0\"\n  }\n}\n"
  },
  {
    "path": "services/services.cors.sh/prisma/schema.prisma",
    "content": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\ndatasource db {\n  provider = \"mongodb\"\n  url      = env(\"DATABASE_URL\")\n}\n\ngenerator client {\n  provider      = \"prisma-client-js\"\n  binaryTargets = [\"native\", \"rhel-openssl-1.0.x\"]\n}\n\nmodel Application {\n  id             String   @id @default(auto()) @map(\"_id\") @db.ObjectId\n  name           String   @default(\"Untitled\")\n  allowedOrigins String[]\n  allowedTargets String[]\n\n  owner   Customer @relation(fields: [ownerId], references: [id])\n  ownerId String   @db.ObjectId\n\n  archivedAt DateTime?\n  createdAt  DateTime  @default(now())\n\n  // follows the user's plan + 1Mo\n  // if user is using a monthly plan, it will be now + 2Mo (initially)\n  // everytime the subscription is renewed, it will be end date + 1Mo\n  expiresAt DateTime?\n\n  // signature for live api key\n  signature_live String @unique\n  // signature for test api key\n  signature_test String @unique\n\n  @@map(\"applications\")\n}\n\nmodel OnboardingApplications {\n  id             String     @id @default(auto()) @map(\"_id\") @db.ObjectId\n  email          String     @unique\n  name           String?\n  allowedOrigins String[]   @default([])\n  priceId        String?\n  key            String     @unique\n  emailSentAt    DateTime[] @default([])\n  createdAt      DateTime   @default(now())\n  updatedAt      DateTime   @updatedAt\n\n  // now + n hours\n  expiresAt DateTime\n\n  @@map(\"applications_onboarding\")\n}\n\n// grida workspace <> stripe customer\nmodel Customer {\n  id            String        @id @default(auto()) @map(\"_id\") @db.ObjectId\n  email         String        @unique\n  emailVerified Boolean       @default(false)\n  stripeId      String        @unique\n  // grida workspace (user) id\n  // workspaceId   String         @unique\n  // subscriptions Subscription[]\n  applications  Application[]\n  createdAt     DateTime      @default(now())\n\n  @@map(\"customers\")\n}\n\n// model Subscription {\n//   @@map(\"subscriptions\")\n//   id             String   @id @default(auto()) @map(\"_id\") @db.ObjectId\n//   customer       Customer @relation(fields: [customerId], references: [id])\n//   customerId     String   @db.ObjectId\n//   // stripe subscription id\n//   subscriptionId String   @unique\n//   // price id from stripe (product is not saved in db.)\n//   priceId        String\n\n//   createdAt DateTime @default(now())\n// }\n"
  },
  {
    "path": "services/services.cors.sh/routes/applications/index.ts",
    "content": "import { Application } from \"@prisma/client\";\nimport * as express from \"express\";\nimport { prisma } from \"../../clients\";\nimport {\n  createApplication,\n  signApplication,\n} from \"../../controllers/applications\";\n\nconst router = express.Router();\n\n// list all my applications\nrouter.get(\"/\", async (req, res) => {\n  const applications = await prisma.application.findMany({\n    where: {\n      ownerId: res.locals.customer.id,\n    },\n  });\n\n  res.json(applications);\n});\n\n// get a single application\nrouter.get(\"/:id\", async (req, res) => {\n  console.info(\"fething application securely\", {\n    id: req.params.id,\n    customer: res.locals.customer,\n  });\n  const id = req.params.id;\n  const application = await prisma.application.findUnique({\n    where: {\n      id: id,\n    },\n  });\n\n  console.info(\"fetched application\", application);\n\n  if (!application) {\n    return res.status(404).json({ error: \"application not found\" });\n  }\n\n  if (application.ownerId !== res.locals.customer.id) {\n    return res.status(403).json({ error: \"application not found\" });\n  }\n\n  const signed = await signApplication(application);\n\n  res.json(msak(signed));\n});\n\n// create a new application\nrouter.post(\"/\", async (req, res) => {\n  //\n  const { name } = req.body;\n\n  const app = await createApplication({\n    name,\n    owner: res.locals.customer,\n  });\n\n  const signed = await signApplication(app);\n\n  res.json(msak(signed));\n});\n\nrouter.put(\"/:id\", async (req, res) => {\n  const id = req.params.id;\n  await prisma.application.update({\n    where: {\n      id: id,\n    },\n    data: {\n      ...req.body,\n    },\n  });\n\n  res.json({ success: true }); // or.. updated body?\n});\n\nfunction msak(\n  signed: Application & {\n    apikey_test: string;\n    apikey_live: string;\n  }\n) {\n  return {\n    id: signed.id,\n    name: signed.name,\n    allowedOrigins: signed.allowedOrigins,\n    allowedTargets: signed.allowedTargets,\n    // available once\n    apikey_test: signed.apikey_test,\n    apikey_live: signed.apikey_live,\n  };\n}\n\nexport default router;\n"
  },
  {
    "path": "services/services.cors.sh/routes/auth/index.ts",
    "content": "import * as express from \"express\";\nimport Axios from \"axios\";\nimport { prisma } from \"../../clients\";\nimport { encode_jwt, SECURE_BROWSER_COOKIE_AUTH_KEY } from \"../../auth\";\n\nconst router = express.Router();\n\nconst authclient = Axios.create({\n  baseURL: \"https://accounts.services.grida.co\",\n});\n\n// user signin with grida, retrieve oauth token from grida, sends it here,\n// this service will check the token, if valid, it will generate a new token for the user,\n// and send it back to the client with secure, http only cookie.\nrouter.post(\"signin\", async (req, res) => {\n  const authorization = req.headers[\"proxy-authorization\"];\n  if (!authorization) {\n    res.status(401).json({ error: \"no authorization header\" });\n    return;\n  }\n\n  // authenticate via accounts.grida.co\n  // request auth server for authentication.\n  const { data } = await authclient.get(\"/verify\", {\n    headers: {\n      // proxy the header, in form of \"Bearer <token>\"\n      authorization,\n    },\n  });\n\n  const customer = await prisma.customer.findUnique({\n    // @ts-ignore\n    where: {\n      // workspaceId: data.id,\n    },\n  });\n\n  // sign customer (user) signature\n  const jwt = encode_jwt(customer.id);\n\n  // set secure cookie when authorized.\n  res\n    .cookie(SECURE_BROWSER_COOKIE_AUTH_KEY, jwt, {\n      signed: true,\n      domain: \".cors.sh\",\n      secure: true,\n      httpOnly: true,\n      sameSite: \"strict\",\n      // no expiration. it is a secure cookie, that only has access to cors.sh. let's keep it this way for now.\n      // expires: null\n    })\n    .json({\n      success: true,\n      customer: customer,\n    });\n});\n\nexport default router;\n"
  },
  {
    "path": "services/services.cors.sh/routes/index.ts",
    "content": "import * as express from \"express\";\nimport router_payments from \"./payments\";\nimport router_stripe_webhooks from \"./webhooks-stripe\";\nimport router_applications from \"./applications\";\nimport router_onboarding from \"./onboarding\";\nimport router_auth from \"./auth\";\nimport cors from \"cors\";\nimport { authMiddleware } from \"../auth\";\n\nconst router = express.Router();\n\nconst cors_website_only = cors({\n  origin: process.env.NODE_ENV === \"production\" ? \"https://cors.sh\" : \"*\",\n});\n\n// router.use(\"/\", router_posts);\nrouter.use(\"/auth\", cors_website_only, router_auth);\nrouter.use(\"/payments\", router_payments);\nrouter.use(\"/webhooks/stripe\", router_stripe_webhooks);\nrouter.use(\n  \"/applications\",\n  cors_website_only,\n  authMiddleware,\n  router_applications\n);\nrouter.use(\"/onboarding\", cors_website_only, router_onboarding);\n\nexport default router;\n"
  },
  {
    "path": "services/services.cors.sh/routes/onboarding/index.ts",
    "content": "import * as express from \"express\";\nimport {\n  getOnboardingApplication,\n  createOnboardingApplication,\n  convertApplication,\n} from \"../../controllers/applications\";\n\nconst router = express.Router();\n\nconst blacklist = [\n  \"nqmo.com\",\n  \"qabq.com\",\n  \"mailinator.com\",\n  \"10minutemail.com\",\n  \"guerrillamail.com\",\n  \"temp-mail.org\",\n  \"yopmail.com\",\n  \"throwawaymail.com\",\n  \"dispostable.com\",\n  \"getnada.com\",\n  \"maildrop.cc\",\n  \"pastryofistanbul.com\",\n  \"litepax.com\",\n  \"raleigh-construction.com\",\n  \"questtechsystems.com\",\n  \"teihu.com\",\n  \"oakon.com\",\n  \"namestal.com\"\n];\n\n// create\nrouter.post(\"/with-email\", async (req, res) => {\n  // create new temporary application bind to the email (form is optional)\n\n  const { email } = req.body;\n\n  // @requires: email\n  if (!email) {\n    return res.status(400).json({ error: \"email is required\" });\n  }\n\n  //\n  if (blacklist.some((d) => email.includes(d))) {\n    return res.status(400).json({ error: \"bad email - do not hack us.\" });\n  }\n\n  const d = await createOnboardingApplication({\n    type: \"with-email\",\n    email,\n  });\n\n  res.status(201).json(d);\n});\n\nrouter.post(\"/with-form\", async (req, res) => {\n  // create new temporary application bind to the form (email is optional)\n\n  const { name, allowedOrigins, priceId } = req.body;\n\n  const d = await createOnboardingApplication({\n    type: \"with-form\",\n    name,\n    allowedOrigins,\n    priceId,\n  });\n\n  res.status(201).json(d);\n});\n\nrouter.get(\"/:id\", async (req, res) => {\n  // this route does not have a guard by design.\n  // get onboarding application (only public data)\n  const { id } = req.params;\n\n  const d = await getOnboardingApplication(id);\n\n  if (!d) {\n    return res.status(404).json({ error: \"application not found\" });\n  }\n\n  res.status(200).json(d);\n});\n\n// conversion\nrouter.post(\"/:id/convert\", async (req, res) => {\n  const { id: onboarding_id } = req.params;\n  const { checkout_session_id } = req.body;\n\n  const application = await convertApplication({\n    onboarding_id,\n    checkout_session_id,\n  });\n\n  res.status(201).json(application);\n});\n\nexport default router;\n"
  },
  {
    "path": "services/services.cors.sh/routes/payments/index.ts",
    "content": "import type { Customer } from \"@prisma/client\";\nimport * as express from \"express\";\nimport { prisma, stripe } from \"../../clients\";\nimport { getOnboardingApplication } from \"../../controllers/applications\";\n\nconst router = express.Router();\n\nconst WEBHOST = process.env.WEBHOST;\nconst WEBURL_CONSOLE = WEBHOST + \"/console\";\nconst PROTOCOL = process.env.NODE_ENV === \"production\" ? \"https\" : \"http\";\n// e.g.\n// http://localhost:4021/payments/checkout/new?price=price_1Lda7UAvR3geCh5rVaajCSw6&onboarding_id=63b9a40c02478a88364d7202\nrouter.get(\"/checkout/new\", async (req, res) => {\n  const host = req.headers.host;\n\n  const { onboarding_id: _q_onboarding } = req.query;\n\n  const onboarding = await getOnboardingApplication(_q_onboarding as string);\n\n  if (!onboarding) {\n    return res.status(400).json({ error: \"invalid session\" });\n  }\n\n  const { priceId } = onboarding;\n\n  const price = await stripe.prices.retrieve(priceId, {\n    expand: [\"product\"],\n  });\n\n  const extra_params = new URLSearchParams({\n    onboarding_id: _q_onboarding as string,\n  });\n\n  const session = await stripe.checkout.sessions.create({\n    billing_address_collection: \"auto\",\n    line_items: [\n      {\n        price: price.id,\n        // For metered billing, do not pass quantity\n        quantity: 1,\n      },\n    ],\n    mode: \"subscription\",\n    // e.g. http://localhost:8823/?success=true&session_id=cs_test_a1qQdhxwfS5kKZJ1kToxKqAr2K6yHneucfi65lIs1OPVkmoH14YNAev76S\n    success_url: `${PROTOCOL}://${host}/payments/success?session_id={CHECKOUT_SESSION_ID}&${extra_params}`,\n    cancel_url: `${PROTOCOL}://${host}/payments/canceled?session_id={CHECKOUT_SESSION_ID}&${extra_params}`,\n  });\n\n  res.redirect(303, session.url);\n});\n\nrouter.get(\"/success\", async (req, res) => {\n  const { session_id, onboarding_id } = req.query;\n\n  const checkout_session = await stripe.checkout.sessions.retrieve(\n    session_id as string\n  );\n\n  const {\n    customer: stripe_customer_id,\n    customer_email, // can be null if created on checkout page (unless explicitly specified by our side.)\n    customer_details,\n    subscription,\n  } = checkout_session;\n\n  // on success, convert onboarding app to a real app (and remove the temporary app)\n  // (if user has one.)\n\n  // remove\n  const tmp = await prisma.onboardingApplications.findUnique({\n    where: { id: onboarding_id as string },\n  });\n\n  // if onboarding's email is placeholded. get email from checkout via stripe\n  const email = tmp.email.endsWith(\"@unknown-users.cors.sh\")\n    ? customer_email ?? customer_details.email\n    : tmp.email;\n\n  // create customer\n  // TODO: what if already exists?\n\n  let customer_exists_with_same_email: Customer;\n  try {\n    customer_exists_with_same_email = await prisma.customer.findUnique({\n      where: { email },\n    });\n  } catch (e) {\n    console.error(\"Caught exeption while finding existing customer\", e);\n  }\n\n  if (customer_exists_with_same_email) {\n    // TODO: we'll need a better way to handle this since we need to test this with same email multiple times.\n    // we can't tell if the purchase is for the same user or not.\n    // yet, if the existing one is verified, we should protect it.\n    // if not, we should give the new one a chance.\n    const params = new URLSearchParams({\n      error: \"identity_conflict\",\n      message:\n        \"Your payment was successful, but we detected a suspicious activity. Please contact customer support.\",\n      session_id: session_id as string,\n      onboarding_id: tmp.id,\n      application_id: tmp.id,\n    });\n    const redirect_uri = `${WEBHOST}/onboarding/payment-success-with-issue?${params.toString()}`;\n    res.redirect(303, redirect_uri);\n  } else {\n    const customer = await prisma.customer.create({\n      data: {\n        stripeId: stripe_customer_id as string,\n        email: email,\n        emailVerified: false,\n      },\n    });\n\n    const _params = {\n      session_id: session_id as string,\n      onboarding_id: tmp?.id,\n      customer_id: customer.id,\n      application_id: tmp.id,\n    };\n    // prettier-ignore\n    Object.keys(_params).forEach((key) => _params[key] === undefined ? delete _params[key] : {});\n\n    const params = new URLSearchParams(_params);\n\n    const redirect_uri = `${WEBHOST}/onboarding/payment-success?${params.toString()}`;\n\n    res.redirect(303, redirect_uri);\n  }\n});\n\nrouter.get(\"/canceled\", async (req, res) => {\n  // remove the temporary app\n  const { session_id, onboarding_id } = req.query;\n\n  res.redirect(303, `https://cors.sh/`);\n});\n\nrouter.post(\"/portal-session\", async (req, res) => {\n  // For demonstration purposes, we're using the Checkout session to retrieve the customer ID.\n  // Typically this is stored alongside the authenticated user in your database.\n  const { session_id } = req.body;\n  const checkoutSession = await stripe.checkout.sessions.retrieve(session_id);\n\n  // This is the url to which the customer will be redirected when they are done\n  // managing their billing with the portal.\n  const returnUrl = WEBURL_CONSOLE;\n\n  const portalSession = await stripe.billingPortal.sessions.create({\n    customer: checkoutSession.customer as string,\n    return_url: returnUrl,\n  });\n\n  res.redirect(303, portalSession.url);\n});\n\nexport default router;\n"
  },
  {
    "path": "services/services.cors.sh/routes/payments/readme.md",
    "content": "# Payments with stripe\n\n## About\n\nThis subscirption checkout follows instruction from https://stripe.com/docs/billing/quickstart which uses the checkout api, webhooks and customer portal for subscription management. Most of the service relies on stripe's built in features.\n\n## Setting up webhook\n\n**Local testing (manual trigger)**\n\n[Install stripe cli](https://stripe.com/docs/stripe-cli)\n\n```bash\nbrew install stripe/stripe-cli/stripe\n```\n\nFollow the instructions at https://dashboard.stripe.com/webhooks/create?endpoint_location=local\n\n```bash\nstripe login\nstripe listen --forward-to localhost:4242/webhook\nstripe trigger payment_intent.succeeded # or other event as you want\n```\n\n**Local testing (production testing)**\n\nSetup ngrok to proxy request & register webhook at https://dashboard.stripe.com/webhooks/create?endpoint_location=hosted\n"
  },
  {
    "path": "services/services.cors.sh/routes/webhooks-stripe/index.ts",
    "content": "import * as express from \"express\";\nimport { stripe } from \"../../clients\";\n\nconst router = express.Router();\n\nrouter.post(\n  /**\n   * /webhooks/stripe - defined in routes/index.ts\n   */\n  \"/\",\n  express.raw({ type: \"application/json\" }),\n  (request, response) => {\n    let event = request.body;\n    // Replace this endpoint secret with your endpoint's unique secret\n    // If you are testing with the CLI, find the secret by running 'stripe listen'\n    // If you are using an endpoint defined with the API or dashboard, look in your webhook settings\n    // at https://dashboard.stripe.com/webhooks\n    const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;\n    // Only verify the event if you have an endpoint secret defined.\n    // Otherwise use the basic event deserialized with JSON.parse\n    if (endpointSecret) {\n      // Get the signature sent by Stripe\n      const signature = request.headers[\"stripe-signature\"];\n      try {\n        event = stripe.webhooks.constructEvent(\n          request.body,\n          signature,\n          endpointSecret\n        );\n      } catch (err) {\n        console.log(`⚠️  Webhook signature verification failed.`, err.message);\n        return response.sendStatus(400);\n      }\n    }\n    let subscription;\n    let status;\n    // Handle the event\n    switch (event.type) {\n      case \"customer.subscription.trial_will_end\":\n        subscription = event.data.object;\n        status = subscription.status;\n        console.log(`Subscription status is ${status}.`);\n        // Then define and call a method to handle the subscription trial ending.\n        // handleSubscriptionTrialEnding(subscription);\n        break;\n      case \"customer.subscription.deleted\":\n        subscription = event.data.object;\n        status = subscription.status;\n        console.log(`Subscription status is ${status}.`);\n        // Then define and call a method to handle the subscription deleted.\n        // handleSubscriptionDeleted(subscriptionDeleted);\n        break;\n      case \"customer.subscription.created\":\n        subscription = event.data.object;\n        status = subscription.status;\n        console.log(`Subscription status is ${status}.`);\n        // Then define and call a method to handle the subscription created.\n        // handleSubscriptionCreated(subscription);\n        break;\n      case \"customer.subscription.updated\":\n        subscription = event.data.object;\n        status = subscription.status;\n        console.log(`Subscription status is ${status}.`);\n        // Then define and call a method to handle the subscription update.\n        // handleSubscriptionUpdated(subscription);\n        break;\n      default:\n        // Unexpected event type\n        console.log(`Unhandled event type ${event.type}.`);\n    }\n    // Return a 200 response to acknowledge receipt of the event\n    response.send();\n  }\n);\n\nexport default router;\n"
  },
  {
    "path": "services/services.cors.sh/scripts/aes-256-cbc-creds.js",
    "content": "// used for generating AES-256-CBC credentials. which is used for generating temp api codes\n\nconst crypto = require(\"crypto\");\n\n// Generate a 256-bit (32-byte) key\nconst key = crypto.randomBytes(32);\nconsole.log(\"Key:\", key.toString(\"hex\"));\n\n// Generate a 128-bit (16-byte) IV\nconst iv = crypto.randomBytes(16);\nconsole.log(\"IV:\", iv.toString(\"hex\"));\n"
  },
  {
    "path": "services/services.cors.sh/serverless.yml",
    "content": "service: cors-proxy-service\nuseDotenv: true\nplugins:\n  - serverless-webpack\n  - serverless-webpack-prisma\n  - serverless-offline\n  - serverless-domain-manager\n  - \"@haftahave/serverless-ses-template\"\n\ncustom:\n  customDomain:\n    domainName: services.cors.sh\n    certificateName: \"services.cors.sh\"\n    basePath: \"\"\n    createRoute53Record: false\n    stage: production\n    # enabled only for production - configured by package.json script with --domain flag.\n    enabled: ${param:domain, false}\n  sesTemplates:\n    addStage: true\n  serverless-offline:\n    httpPort: 4021\n    lambdaPort: 3021\n    noPrependStageInUrl: true\n  webpack:\n    includeModules: true\n      \n\nprovider:\n  name: aws\n  runtime: nodejs14.x\n  region: us-west-1\n  environment:\n    DATABASE_URL: ${env:DATABASE_URL}\n    STRIPE_API_KEY: ${env:STRIPE_API_KEY}\n    STRIPE_WEBHOOK_SECRET: ${env:STRIPE_WEBHOOK_SECRET}\n    WEBHOST: ${env:WEBHOST}\n    API_KEY_TEMP_AES_KEY: ${env:API_KEY_TEMP_AES_KEY}\n    API_KEY_TEMP_AES_IV: ${env:API_KEY_TEMP_AES_IV}\n    API_KEY_TEST_HASH_SECRET: ${env:API_KEY_TEST_HASH_SECRET}\n    API_KEY_LIVE_HASH_SECRET: ${env:API_KEY_LIVE_HASH_SECRET}\n    SERVICE_JWT_SECRET: ${env:SERVICE_JWT_SECRET}\n    SLACK_CHANNEL: ${env:SLACK_CHANNEL}\n    SLACK_TOKEN: ${env:SLACK_TOKEN}\n    DYNAMODB_TABLE_SERVICE_KEYS: \"${self:service}-keys-${opt:stage, self:provider.stage}\"\n    STAGE: ${opt:stage, self:provider.stage}\n  apiGateway:\n    # https://stackoverflow.com/questions/61003311/serverless-i-image-upload-to-s3-broken-after-deploy-local-worked-only/61003498#61003498\n    binaryMediaTypes:\n      - \"*/*\"\n  iamRoleStatements:\n    # email with ses\n    - Effect: Allow\n      Action:\n        - ses:SendEmail\n        - ses:SendTemplatedEmail\n      Resource: \"*\"\n    - Effect: Allow\n      Action:\n        - dynamodb:Query\n        - dynamodb:Scan\n        - dynamodb:GetItem\n        - dynamodb:PutItem\n        - dynamodb:UpdateItem\n        - dynamodb:DeleteItem\n        - dynamodb:DescribeTable\n      Resource: \"arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE_SERVICE_KEYS}\"\n\nresources:\n  Resources:\n    serviceKeysTable:\n      Type: AWS::DynamoDB::Table\n      Properties:\n        TableName: \"${self:provider.environment.DYNAMODB_TABLE_SERVICE_KEYS}\"\n        AttributeDefinitions:\n          - AttributeName: key\n            AttributeType: S\n        KeySchema:\n          - AttributeName: key\n            KeyType: HASH\n        ProvisionedThroughput:\n          # $0.000145 per RCU\n          ReadCapacityUnits: ${param:service-keys-rcu, 1}\n          # $0.000725 per WCU\n          WriteCapacityUnits: ${param:service-keys-wcu, 1}\n\nfunctions:\n  main:\n    handler: index.handler\n    events:\n      - http:\n          method: any\n          path: /{proxy+}\n          cors:\n            origin: \"*\"\n      - http:\n          method: get\n          path: /\n          cors:\n            origin: \"*\"\n\npackage:\n  individually: true\n"
  },
  {
    "path": "services/services.cors.sh/ses-email-templates/index.js",
    "content": "const fs = require(\"fs\");\nconst path = require(\"path\");\n\nconst dir = path.join(__dirname, \"../../mail.cors.sh\", \"render\");\nconst templates = fs\n  .readdirSync(dir)\n  // list files ending with .template.html\n  .filter((file) => file.endsWith(\".template.html\"));\n\nconst prefix = \"mail_cors_sh_\";\n\nmodule.exports = async (serverless, options) =>\n  templates.map((file) => {\n    const fname = file.replace(\".template.html\", \"\");\n    const name = prefix + fname;\n    const subject = fs.readFileSync(path.join(dir, `${fname}.subject`), \"utf8\");\n    const html = fs.readFileSync(path.join(dir, file), \"utf8\");\n    return { name, subject, html };\n  });\n"
  },
  {
    "path": "services/services.cors.sh/sync/index.ts",
    "content": "import AWS from \"aws-sdk\";\nimport * as keygen from \"../keygen\";\nimport type { KeyInfo } from \"./type\";\nimport type { Application } from \"@prisma/client\";\nimport day from \"dayjs\";\n\nconst db = new AWS.DynamoDB.DocumentClient();\n\nexport default async function sync(application: Application) {\n  const { id, signature_live, signature_test, allowedOrigins, allowedTargets } =\n    application;\n\n  // TODO: we are using application.id as the billing group, but we should\n  // use the subscription id instead (in the future)\n  const billing_group = id;\n\n  const data = {\n    // TODO: get plan data\n    // for now, fixing it as \"2023.t1\", which is the pro plan\n    plan: \"2023.t1\",\n    allowedOrigins,\n    allowedTargets,\n  };\n\n  // TODO: get subscription data. to calculate expires_at\n  // for now, we are givving all keys a 3 year expiry\n  const expires_at = day().add(3, \"year\").unix();\n\n  // test\n  sync_record(signature_test, \"test\", billing_group, expires_at, data);\n\n  // live\n  sync_record(signature_live, \"live\", billing_group, expires_at, data);\n}\n\nfunction sync_record(\n  signature: string,\n  type: \"live\" | \"test\",\n  billing_group: string,\n  expires_at: number,\n  data: {\n    plan: string;\n    allowedOrigins: string[];\n    allowedTargets: string[];\n  }\n) {\n  const record: KeyInfo = {\n    key: keygen.sign(signature, type).token,\n    plan: data.plan,\n    config: {\n      allowed_origins: data.allowedOrigins,\n      allowed_targets: data.allowedTargets,\n    },\n    active: true,\n    billing_group,\n    expires_at,\n    synced_at: day().unix(),\n  };\n\n  // write to db\n  return db\n    .put({\n      TableName: process.env.DYNAMODB_TABLE_SERVICE_KEYS!,\n      Item: record,\n    })\n    .promise();\n}\n\nexport async function activate(id: string, active: boolean) {\n  // update \"active\" field of the key\n  // write to db\n\n  return db.update({\n    TableName: process.env.DYNAMODB_TABLE_SERVICE_KEYS!,\n    Key: {\n      id,\n    },\n    UpdateExpression: \"set active = :active\",\n    ExpressionAttributeValues: {\n      \":active\": active,\n    },\n  });\n}\n"
  },
  {
    "path": "services/services.cors.sh/sync/type.ts",
    "content": "// shared db\nexport interface KeyInfo {\n  key: string;\n  plan: string;\n  config: {\n    allowed_origins: string[];\n    allowed_targets: string[];\n  };\n  active: boolean;\n  billing_group: string;\n  expires_at: number;\n  synced_at: number;\n}\n"
  },
  {
    "path": "services/services.cors.sh/test/config.env.test",
    "content": "API_KEY_TEMP_OTP_SECRET=\"test\""
  },
  {
    "path": "services/services.cors.sh/test/setup-tests.ts",
    "content": "import dotenv from \"dotenv\";\n\ndotenv.config({ path: \"./config.env.test\" });\n"
  },
  {
    "path": "services/services.cors.sh/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"declaration\": true,\n    \"noImplicitAny\": false,\n    \"removeComments\": true,\n    \"noLib\": false,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"emitDecoratorMetadata\": true,\n    \"experimentalDecorators\": true,\n    \"target\": \"es6\",\n    \"sourceMap\": true,\n    \"outDir\": \"./dist\",\n    \"baseUrl\": \".\",\n    \"typeRoots\": [\"./node_modules/@types\"]\n  },\n  \"exclude\": [\"node_modules\", \"dist\"]\n}\n"
  },
  {
    "path": "services/services.cors.sh/webpack.config.js",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\nconst path = require(\"path\");\nconst nodeExternals = require(\"webpack-node-externals\");\nconst slsw = require(\"serverless-webpack\");\nconst { isLocal } = slsw.lib.webpack;\n\nmodule.exports = {\n  target: \"node\",\n  stats: \"normal\",\n  entry: slsw.lib.entries,\n  externals: [nodeExternals()],\n  mode: isLocal ? \"development\" : \"production\",\n  optimization: { concatenateModules: false },\n  resolve: { extensions: [\".js\", \".ts\"] },\n  module: {\n    rules: [\n      {\n        test: /\\.tsx?$/,\n        loader: \"ts-loader\",\n        exclude: /node_modules/,\n      },\n    ],\n  },\n  output: {\n    libraryTarget: \"commonjs\",\n    filename: \"[name].js\",\n    path: path.resolve(__dirname, \".webpack\"),\n  },\n};\n"
  },
  {
    "path": "web/.eslintrc.json",
    "content": "{\n  \"extends\": \"next/core-web-vitals\"\n}\n"
  },
  {
    "path": "web/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n.yarn/install-state.gz\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env*.local\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": "web/README.md",
    "content": "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).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.\n\nThis project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe 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.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.\n"
  },
  {
    "path": "web/app/(console)/console/[id]/page.tsx",
    "content": "'use client'\nimport React, { useEffect } from \"react\";\nimport styled from \"@emotion/styled\";\nimport { Pencil1Icon } from \"@radix-ui/react-icons\";\nimport client, { ApplicationWithApiKey } from \"@cors.sh/service-api\";\nimport { FormPageLayout, PageCloseButton } from \"@app/ui/layouts\";\nimport { Button, TextFormField } from \"@editor-ui/console\";\nimport { Logo } from \"@/components/logo\";\nimport { UnderlineButton } from \"@app/ui/components\";\nimport { ApiKeyReveal } from \"@app/ui/components\";\n\nexport default function ApplicationDetailPage({ params }: {\n  params: {\n    id: string;\n  }\n}) {\n\n  const application: ApplicationWithApiKey = {\n    id: params.id,\n    name: \"my-portfolio-website\",\n    apikey_live: \"prod_1223-xasx-xxe2\",\n    apikey_test: \"test_xxasdj-xxd9-x2hx\",\n    allowedOrigins: []\n  }\n\n  return (\n    <FormPageLayout>\n      <PageCloseButton />\n      <Logo />\n      <div style={{ height: 80 }} />\n      <EditableTitle initialValue={application.name} />\n      <div className=\"form\">\n        <ApiKeyReveal\n          keys={{\n            test: application.apikey_test,\n            prod: application.apikey_live,\n          }}\n        />\n\n        <TextFormField\n          readonly\n          label=\"Application origin URL\"\n          placeholder=\"http://localhost:3000, https://my-site.com\"\n          helpText=\"You can add up to 3 urls of your site\"\n          value={application.allowedOrigins?.join(\", \")}\n        // onChange={setAllowedOrigins}\n        />\n        <TextFormField\n          readonly\n          label=\"Restrict Targets (Optional)\"\n          placeholder=\"http://localhost:3000, https://my-site.com\"\n          helpText=\"You can restrict target urls for extra security\"\n          value={application.allowedOrigins?.join(\", \")}\n        // onChange={setAllowedOrigins}\n        />\n\n        <Button height={36}>Save</Button>\n        <UnderlineButton>Archive application</UnderlineButton>\n      </div>\n    </FormPageLayout>\n  );\n}\n\nfunction EditableTitle({ initialValue = \"\" }: { initialValue?: string }) {\n  const [editing, setEditing] = React.useState(false);\n  const [text, setText] = React.useState(initialValue);\n  const ref = React.useRef<HTMLInputElement>(null);\n\n  useEffect(() => {\n    if (editing) {\n      // select all\n      ref.current?.focus();\n      ref.current?.setSelectionRange(0, text.length);\n    }\n  }, [editing]);\n\n  return (\n    <TitleInputWrapper>\n      <input\n        style={{\n          cursor: editing ? \"text\" : \"pointer\",\n        }}\n        onDoubleClick={() => setEditing(true)}\n        onBlur={() => setEditing(false)}\n        ref={ref}\n        readOnly={!editing}\n        contentEditable\n        onChange={(e) => {\n          setText(e.target.value);\n        }}\n        onKeyDown={(e) => {\n          if (e.key === \"Enter\") {\n            setEditing(false);\n          }\n        }}\n        value={text}\n      />\n      <button\n        className=\"edit-button\"\n        style={{\n          visibility: editing ? \"hidden\" : \"visible\",\n        }}\n        onClick={() => setEditing(true)}\n      >\n        <Pencil1Icon />\n      </button>\n    </TitleInputWrapper>\n  );\n}\n\nconst TitleInputWrapper = styled.div`\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 8px;\n\n  input {\n    width: 100%;\n    box-sizing: border-box;\n    font-size: 24px;\n    font-weight: bold;\n    border: none;\n    text-align: center;\n    outline: none;\n  }\n\n  .edit-button {\n    position: absolute;\n    right: 0;\n    top: 0;\n    bottom: 0;\n    margin: auto;\n    opacity: 0;\n    cursor: pointer;\n    border: none;\n    background: none;\n    outline: none;\n    transition: opacity 0.2s ease;\n  }\n\n  &:hover {\n    .edit-button {\n      opacity: 1;\n    }\n  }\n`;"
  },
  {
    "path": "web/app/(console)/console/new/page.tsx",
    "content": "'use client'\n\nimport React, { useEffect } from \"react\";\nimport { Button, TextFormField } from \"@editor-ui/console\";\nimport client from \"@cors.sh/service-api\";\nimport { useRouter } from \"next/navigation\";\nimport { FormPageLayout } from \"@app/ui/layouts\";\n\nexport default function NewApplicationPage() {\n  const router = useRouter();\n  const [name, setName] = React.useState(\"\");\n  const [allowedOrigins, setAllowedOrigins] = React.useState(\"\");\n  const [isBusy, setIsBusy] = React.useState(false);\n  const [isValid, setIsValid] = React.useState(false);\n\n  const validateUrls = (urls: string) => {\n    const lines = urls.split(\",\").map((line) => line.trim());\n    for (const line of lines) {\n      try {\n        new URL(line);\n      } catch (e) {\n        return false;\n      }\n    }\n    return true;\n  };\n\n  const onCreateNewClick = () => {\n    setIsBusy(true);\n    client\n      .createApplication({\n        name: name,\n        allowedOrigins: allowedOrigins\n          .split(\",\")\n          .map((origin) => origin.trim()),\n      })\n      .then((r) => {\n        router.push(`/console/${r.id}`,);\n      })\n      .finally(() => {\n        setIsBusy(false);\n      });\n  };\n\n  useEffect(() => {\n    setIsValid(name.length > 0 && validateUrls(allowedOrigins));\n  }, [name, allowedOrigins]);\n\n  return (\n    <FormPageLayout>\n      <h1>Create new application</h1>\n      <div className=\"form\">\n        <TextFormField\n          label=\"Project Name\"\n          placeholder=\"my-portfolio-website\"\n          onChange={setName}\n        />\n        <TextFormField\n          label=\"Your site\"\n          placeholder=\"http://localhost:3000, https://my-site.com\"\n          helpText=\"You can add up to 3 urls of your site\"\n          onChange={setAllowedOrigins}\n        />\n        <div style={{ height: 16 }} />\n        <Button\n          disabled={!isValid || isBusy}\n          onClick={onCreateNewClick}\n          height={\"32px\"}\n        >\n          Create Project\n        </Button>\n      </div>\n    </FormPageLayout>\n  );\n}\n"
  },
  {
    "path": "web/app/(console)/console/page.tsx",
    "content": "import React from \"react\";\nimport Link from \"next/link\";\nimport type { Metadata } from 'next'\nimport { ApplicationItem, ApplicationList } from \"@/components/console/application-list\";\nimport { FormPageLayout } from \"@app/ui/layouts\";\nimport { UnderlineButton } from \"@app/ui/components\";\n\n\nexport const metadata: Metadata = {\n  title: \"Dashboard\",\n}\n\nexport default function ConsoleIndex() {\n  const applications = [\n    {\n      id: \"1\",\n      name: \"My app\",\n    },\n    {\n      id: \"2\",\n      name: \"My app 2\",\n    },\n  ]\n\n  return (\n    <>\n      <FormPageLayout>\n        {/* <Logo /> */}\n        <div style={{ height: 80 }} />\n        <ApplicationList>\n          {applications.map((application) => (\n            <ApplicationItem key={application.id} {...application} />\n          ))}\n        </ApplicationList>\n        <div style={{ height: 80 }} />\n        <div\n          style={{\n            display: \"flex\",\n            flexDirection: \"column\",\n            gap: 8,\n          }}\n        >\n          <Link href=\"/new\">\n            {/* <UnderlineButton> */}\n            Create new application\n            {/* </UnderlineButton> */}\n          </Link>\n          <Link href=\"/subscriptions\">\n            {/* <UnderlineButton> */}\n            Manage subscription\n            {/* </UnderlineButton> */}\n          </Link>\n        </div>\n      </FormPageLayout>\n    </>\n  );\n}\n"
  },
  {
    "path": "web/app/(console)/layout.tsx",
    "content": "import type { Metadata } from 'next'\nimport { Inter } from 'next/font/google'\nimport { Toaster } from \"react-hot-toast\";\nimport '../globals.css'\nimport GoogleAnalytics from '@/components/ga';\nimport ChatwootWidget from \"@/components/chatwoot\";\nimport { Heading, Link, Theme } from '@radix-ui/themes';\nimport { GearIcon, GitHubLogoIcon, MagnifyingGlassIcon, OpenInNewWindowIcon } from '@radix-ui/react-icons';\nconst inter = Inter({ subsets: ['latin'] })\n\nexport const metadata: Metadata = {\n  title: {\n    template: '%s - Console - CORS.SH',\n    default: 'Console - CORS.SH',\n  },\n  description: \"One CORS Proxy you'll ever need\",\n  metadataBase: new URL('https://cors.sh'),\n  openGraph: {\n    images: [\n      \"/og-image-01.jpg\",\n    ]\n  }\n}\n\nexport default function RootLayout({\n  children,\n}: {\n  children: React.ReactNode\n}) {\n  return (\n    <html lang=\"en\">\n      <body className={inter.className}>\n        {process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS ? (\n          <GoogleAnalytics gaid=\n            {process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS} />\n        ) : null}\n        <ChatwootWidget />\n        <div className='selection:bg-amber-500 selection:text-amber-900'>\n          <div suppressHydrationWarning>\n            <Toaster position=\"bottom-center\" />\n          </div>\n          <Theme hasBackground={false}>\n            <div className=\"w-screen h-screen flex flex-row\">\n              <Sidebar />\n              <ContentArea>\n                {children}\n              </ContentArea>\n            </div>\n          </Theme>\n        </div>\n      </body>\n    </html>\n  )\n}\n\n\nfunction Sidebar() {\n  return <div className=\"border-r border-black/5\">\n    <Heading className=\"p-4\">\n      CORS.SH\n    </Heading>\n    <div className=\"flex-1 p-4 flex flex-col gap-2 min-w-[240px]\">\n      <NavItem href=\"/console\">\n        <GearIcon />\n        Dashboard\n      </NavItem>\n      <NavItem href=\"/console/apps\">\n        <GearIcon />\n        Apps\n      </NavItem>\n      <NavItem href=\"/console/events\">\n        <GearIcon />\n        Events\n      </NavItem>\n      <NavItem href=\"https://github.com/gridaco/cors.sh\" target=\"_blank\">\n        <GitHubLogoIcon />\n        Github\n      </NavItem>\n    </div>\n  </div>\n}\n\nfunction NavItem({ children, ...props }: React.PropsWithChildren<React.ComponentProps<typeof Link>>) {\n  return (\n    <Link\n      className=\"flex flex-row items-center gap-2 px-4 py-2 hover:bg-gray-100 rounded-md transition-colors\"\n      {...props}\n    >\n      {children}\n    </Link>\n  )\n}\n\n\nfunction ContentArea({ children }: React.PropsWithChildren<{}>) {\n  return <div className=\"flex-1 flex flex-col\">\n    {children}\n  </div>\n\n}"
  },
  {
    "path": "web/app/(home)/contact/page.tsx",
    "content": "import Link from \"next/link\"\nimport React from \"react\"\n\nexport default function ContactPage() {\n  return <main className=\"max-w-screen-xl overflow-x-visible m-auto flex min-h-screen flex-col items-center md:items-stretch justify-center p-10 text-center md:text-start\">\n    <h1 className=\"text-5xl font-bold\">Contact</h1>\n    <div style={{ height: 40 }} />\n    <Link href=\"https://grida.co/join-slack\">\n      <button className=\"underline\">Join our Slack channel for Inqueries</button>\n    </Link>\n  </main>\n}"
  },
  {
    "path": "web/app/(home)/get-started/page.tsx",
    "content": "'use client'\n\nimport React, { useCallback, useRef, useEffect } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { FormPageLayout } from \"@app/ui/layouts\";\nimport { validateUrls } from \"@app/ui/utils\";\nimport {\n  Button,\n  FormFieldBase,\n  FormFieldLabel,\n  TextFormField,\n} from \"@editor-ui/console\";\nimport Select from \"react-select\";\nimport client from \"@cors.sh/service-api\";\nimport * as k from \"@/k\";\nimport { toast } from \"react-hot-toast\";\nimport { useSearchParams } from \"next/navigation\";\nimport { MiniPlanSelect } from \"@/components/pricing/mini\";\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\n\n// eslint-disable-next-line @next/next/no-async-client-component\nexport default function GetstartedPage() {\n  const searchParams = useSearchParams()\n  const _q_price = searchParams?.get(\"price\") as string\n\n  const validate_price_id = (price_id: string | undefined): boolean => {\n    return price_id?.startsWith(\"price_\") || false;\n  };\n\n  const _price = validate_price_id(_q_price)\n    ? _q_price\n    : k.plans.pro.id;\n\n\n  const [name, setName] = React.useState(\"\");\n  const [price, setPrice] = React.useState(_price);\n  const [allowedOrigins, setAllowedOrigins] = React.useState(\"\");\n  const [valid, setValid] = React.useState(false);\n  const [isBusy, setIsBusy] = React.useState(false);\n  const [isValid, setIsValid] = React.useState(false);\n  const stateRef = useRef<string>(_price);\n\n  stateRef.current = price;\n\n  const router = useRouter();\n\n  const onPriceChange = (price: string) => {\n    setPrice(price);\n  };\n\n  const onEnter = () => {\n    // this is required because onEnter can also be invoked from input's callback\n    if (valid) {\n      onNextClick();\n    }\n  };\n\n  useEffect(() => {\n    setIsValid(name.length > 0 && validateUrls(allowedOrigins));\n  }, [name, allowedOrigins]);\n\n  const onNextClick = useCallback(async () => {\n    setIsBusy(true);\n\n    try {\n      // log begin_checkout event\n      const pricedata = (k.plans as any)[price];\n\n      // @ts-ignore\n      window.gtag?.(\"event\", \"begin_checkout\", {\n        value: pricedata.value,\n        currency: pricedata.currency,\n        items: [\n          {\n            item_id: pricedata.id,\n            item_name: pricedata.label,\n          },\n        ],\n      });\n    } catch (e) { }\n\n    try {\n      // create onboarding application\n      const form = {\n        name: name ? name : undefined,\n        allowedOrigins: allowedOrigins\n          .split(\",\")\n          .map((x) => x.trim())\n          .filter(Boolean),\n        priceId: price,\n      };\n\n      const application = await client.onboardingWithForm(form);\n\n      const onboarding_id = application.id;\n\n      // switch (stateRef.current) {\n      //   case k.PRICE_FREE_MONTHLY: {\n      //     // the free plan does not require payments, so we can skip to create new project right away.\n      //     redirect =\n      //       window.location.protocol + window.location.host + \"/console/new\";\n      //     break;\n      //   }\n      // }\n\n      // redirect user to payment page\n      let params = new URLSearchParams();\n      params.append(\"onboarding_id\", onboarding_id);\n      // TODO: multiple search params not supported by accounts.grida.co?redirect_uri=x\n\n      const redirect = k.SERVER_URL + \"/payments/checkout/new\" + \"?\" + params;\n\n      router.replace(redirect);\n    } catch (e) {\n      toast.error(\"Oops. something went wrong. please try again.\");\n      setIsBusy(false);\n    }\n  }, [price, name, allowedOrigins, router]);\n\n  return (\n    <FormPageLayout>\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          gap: 8,\n        }}\n      >\n        <h1 className=\"text-3xl font-bold\">Get started</h1>\n        <p className=\"text-sm font-normal\">\n          Ready to use cors.sh? select your plan and let’s create your first\n          project. <br />\n        </p>\n        <p className=\"text-xs font-normal opacity-50\">*you can update the fields later</p>\n      </div>\n      <div className=\"form\">\n        <TextFormField\n          label=\"Application name\"\n          autoFocus\n          placeholder=\"my-portfolio-website\"\n          onEnter={onEnter}\n          onChange={setName}\n        />\n        <TextFormField\n          label=\"Application origin URL\"\n          placeholder=\"http://localhost:3000, https://my-site.com\"\n          helpText=\"Add up to 3 urls of your site\"\n          onEnter={onEnter}\n          onChange={setAllowedOrigins}\n        />\n        {/* pricing plan select */}\n\n        <motion.div\n          style={{\n            display: isValid ? \"block\" : \"none\",\n          }}\n          variants={{\n            hidden: { opacity: 0, y: 10 },\n            visible: { opacity: 1, y: 0 },\n          }}\n          initial={{ opacity: 0, y: 10 }}\n          animate={isValid ? \"visible\" : \"hidden\"}\n          transition={{ delay: 0.2, duration: 0.5 }}\n        >\n          <FormFieldBase style={{ width: \"100%\" }}>\n            <div className=\"flex items-center justify-between self-stretch\">\n              <FormFieldLabel>Plan</FormFieldLabel>\n              <Link className=\"underline text-xs\" target=\"_blank\" href=\"/pricing\">View Pricing</Link>\n            </div>\n            <div style={{ flex: 1, alignSelf: \"stretch\" }}>\n              <MiniPlanSelect\n                onChange={(id) => {\n                  onPriceChange(id);\n                }}\n                value={price}\n                options={[\n                  {\n                    id: k.plans.pro.id,\n                    label: <span>$3 / Mo<br />Annual Billing</span>,\n                    content: <>Save 25% with annual billing</>\n                  },\n                  {\n                    id: k.plans.pro2.id,\n                    label: <span>$4 / Mo<br />Monthly Billing</span>,\n                    content: <>-</>\n                  }\n                ]} />\n            </div>\n          </FormFieldBase>\n        </motion.div>\n        <div style={{ height: 16 }} />\n        <Button\n          color=\"white\"\n          disabled={!isValid || isBusy}\n          onClick={onNextClick}\n          height={\"32px\"}\n        >\n          Continue\n        </Button>\n      </div>\n    </FormPageLayout >\n  );\n}"
  },
  {
    "path": "web/app/(home)/layout.tsx",
    "content": "import type { Metadata } from 'next'\nimport { Inter } from 'next/font/google'\nimport { Header } from '@/components/header'\nimport { Toaster } from \"react-hot-toast\";\nimport GoogleAnalytics from '@/components/ga';\nimport ChatwootWidget from \"@/components/chatwoot\";\nimport '../globals.css'\n\nconst inter = Inter({ subsets: ['latin'] })\n\nexport const metadata: Metadata = {\n  title: 'CORS.SH - A Fast & Reliable CORS Proxy for your websites',\n  description: \"One CORS Proxy you'll ever need\",\n  metadataBase: new URL('https://cors.sh'),\n  openGraph: {\n    images: [\n      \"/og-image-01.jpg\",\n    ]\n  }\n}\n\nexport default function RootLayout({\n  children,\n}: {\n  children: React.ReactNode\n}) {\n  return (\n    <html lang=\"en\">\n      <body className={inter.className}>\n        {process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS ? (\n          <GoogleAnalytics gaid=\n            {process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS} />\n        ) : null}\n        <ChatwootWidget />\n        <div className='selection:bg-amber-500 selection:text-amber-900'>\n          <div suppressHydrationWarning>\n            <Toaster position=\"bottom-center\" />\n          </div>\n          <Header />\n          {children}\n        </div>\n      </body>\n    </html>\n  )\n}\n"
  },
  {
    "path": "web/app/(home)/onboarding/[id]/page.tsx",
    "content": "// TODO: rename this route to /onboarding/continue?id=\n\nimport Head from \"next/head\";\nimport { redirect } from \"next/navigation\";\nimport React from \"react\";\n\nexport default function ContinueOnboardingWithVerification() {\n  redirect(\"/get-started\");\n\n  return (\n    <>\n      <Head>\n        <title>Redirecting..</title>\n      </Head>\n      <h1>Redirecting..</h1>\n    </>\n  );\n}\n"
  },
  {
    "path": "web/app/(home)/onboarding/page.tsx",
    "content": "'use client'\nimport React, { useEffect } from \"react\";\nimport {\n  Button,\n  FormFieldBase,\n  FormFieldLabel,\n  TextFormField,\n} from \"@editor-ui/console\";\nimport Select from \"react-select\";\nimport client from \"@cors.sh/service-api\";\nimport { redirect, useRouter } from \"next/navigation\";\nimport { Cross2Icon } from \"@radix-ui/react-icons\";\nimport Head from \"next/head\";\nimport { FormPageLayout } from \"@app/ui/layouts\";\nimport { validateUrls } from \"@app/ui/utils\";\nimport { motion } from \"framer-motion\";\n\nexport default function NewApplicationPage() {\n  redirect(\"/\")\n  const router = useRouter();\n  const [step, setStep] = React.useState<\"signin\" | \"setup\">(\"signin\");\n\n  function Body() {\n    switch (step) {\n      case \"signin\":\n        return (\n          <SigninForm\n            onComplete={() => {\n              setStep(\"setup\");\n            }}\n          />\n        );\n      case \"setup\":\n        return <SetupForm />;\n    }\n  }\n\n  return (\n    <>\n      <Head>\n        <title>CORS.SH - First App</title>\n      </Head>\n      <FormPageLayout>\n        <button\n          className=\"close\"\n          onClick={() => {\n            router.replace(\"/\");\n          }}\n        >\n          <Cross2Icon />\n        </button>\n        <Body />\n      </FormPageLayout>\n    </>\n  );\n}\n\nfunction SigninForm({ onComplete }: { onComplete: () => void }) {\n  const [tmpkey, setTmpkey] = React.useState(\"\");\n  const [valid, setValid] = React.useState(false);\n\n  const onTypeKey = (key: string) => {\n    // todo: validate key via api\n    setValid(key.length > 10);\n  };\n\n  const onEnter = () => {\n    // this is required because onEnter can also be invoked from input's callback\n    if (valid) {\n      onComplete();\n    }\n  };\n\n  return (\n    <>\n      <h1>Enter your API key from your inbox to get started</h1>\n      <div className=\"form\">\n        <TextFormField\n          label=\"API Key\"\n          placeholder=\"text_xxxx-xxxx-xxxx\"\n          onEnter={onEnter}\n          onChange={onTypeKey}\n        />\n      </div>\n      <div style={{ height: 16 }} />\n      <Button disabled={!valid} onClick={onEnter} height={\"32px\"}>\n        Continue\n      </Button>\n    </>\n  );\n}\n\nfunction SetupForm() {\n  const router = useRouter();\n  const [name, setName] = React.useState(\"\");\n  const [allowedOrigins, setAllowedOrigins] = React.useState(\"\");\n  const [isBusy, setIsBusy] = React.useState(false);\n  const [isValid, setIsValid] = React.useState(false);\n  const [isPricingVisible, setIsPricingVisible] = React.useState(false);\n\n  const onEnter = () => {\n    if (!isValid) {\n      return;\n    }\n    setIsBusy(true);\n    client\n      .createApplication({\n        name: name,\n        allowedOrigins: allowedOrigins\n          .split(\",\")\n          .map((origin) => origin.trim()),\n      })\n      .then((r) => {\n        router.push(`/console/${r.id}`,);\n      })\n      .finally(() => {\n        setIsBusy(false);\n      });\n  };\n\n  useEffect(() => {\n    setIsValid(name.length > 0 && validateUrls(allowedOrigins));\n  }, [name, allowedOrigins]);\n\n  useEffect(() => {\n    if (isValid) {\n      setTimeout(() => {\n        setIsPricingVisible(true);\n      }, 1800);\n    }\n  }, [isValid]);\n\n  return (\n    <>\n      <h1>Now, Let&apos;s configure your first application</h1>\n      <div className=\"form\">\n        <TextFormField\n          label=\"Application name\"\n          placeholder=\"my-portfolio-website\"\n          onEnter={onEnter}\n          onChange={setName}\n        />\n        <TextFormField\n          label=\"Application origin URL\"\n          placeholder=\"http://localhost:3000, https://my-site.com\"\n          helpText=\"You can add up to 3 urls of your site\"\n          onEnter={onEnter}\n          onChange={setAllowedOrigins}\n        />\n        {/* pricing plan select */}\n        <motion.div\n          initial={{ opacity: 0, y: 16 }}\n          animate={\n            isPricingVisible\n              ? {\n                opacity: 1,\n                y: 0,\n              }\n              : {}\n          }\n          transition={{ duration: 0.3, ease: \"easeOut\" }}\n        >\n          <FormFieldBase style={{ width: \"100%\" }}>\n            <FormFieldLabel>Plan</FormFieldLabel>\n            <div style={{ flex: 1, alignSelf: \"stretch\" }}>\n              <Select\n                onKeyDown={(e) => {\n                  if (e.key === \"Enter\") {\n                    onEnter();\n                  }\n                }}\n                options={[\n                  { value: \"pro-monthly\", label: \"Pro - $4/Mo\" },\n                  {\n                    value: \"pro-yearly\",\n                    label: \"Pro - $3/Mo (Pay annualy, save $12)\",\n                  },\n                ]}\n                defaultValue={{ value: \"pro\", label: \"Pro - $4/Mo\" }}\n              />\n            </div>\n          </FormFieldBase>\n        </motion.div>\n        <div style={{ height: 16 }} />\n        <Button disabled={!isValid || isBusy} onClick={onEnter} height={\"32px\"}>\n          Continue\n        </Button>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "web/app/(home)/page.tsx",
    "content": "import React from 'react'\nimport { HomeCardExample } from '@/components/home-hover-card'\nimport { SendMeAnApiKeyForm } from '@/components/landing/send-me-an-api-key-form'\nimport { HomeBackground } from '@/components/home-background'\n\nexport default function Home() {\n  return (\n    <main className=\"max-w-screen-xl overflow-x-visible m-auto flex min-h-screen flex-col items-center md:items-stretch justify-center p-10 text-center md:text-start\">\n      <HomeBackground />\n      <div className='flex flex-col mb-40 mt-40 md:mb-0 md:mt-0 md:flex-row gap-20 justify-between items-center md:items-stretch'>\n        <div className='flex flex-col gap-16 justify-center max-w-lg'>\n          <div className='flex flex-col gap-5 max-w-lg'>\n            <h1 className='text-5xl lg:text-7xl font-black'>\n              <span className='bg-gradient-to-b from-white to-white/80 bg-clip-text text-transparent'\n                style={{\n                  display: 'inline-block'\n                }}\n              >\n                CORS.SH -<br />\n                CORS PROXY\n              </span>\n            </h1>\n            <p className='text-md lg:text-xl font-regular opacity-70' style={{\n              lineHeight: '137%'\n            }}>\n              Sick of cors errors? CORS.SH, A fast & reliable cors proxy for your websites here for the rescue.\n            </p>\n          </div>\n          <SendMeAnApiKeyForm />\n        </div>\n        <div className='flex'>\n          <HomeCardExample />\n        </div>\n      </div>\n    </main>\n  )\n}\n\n"
  },
  {
    "path": "web/app/(home)/pricing/page.tsx",
    "content": "\"use client\";\nimport React from \"react\";\nimport styled from \"@emotion/styled\";\nimport { FaqItem } from \"@/components/faq\";\nimport { PricingCard } from \"@/components/pricing\";\nimport { motion } from \"framer-motion\";\nimport { useRouter } from \"next/navigation\";\nimport { FreeForOpensourceCard } from \"@/components/free-for-opensource\";\nimport { plans } from \"@/k\";\nimport faqs from \"@/k/faq.json\";\nimport Link from \"next/link\";\n\n\nconst price_size = {\n  normal: { width: 280, height: 380 } as const,\n  highlighted: { width: 300, height: 420 } as const,\n} as const;\n\nexport default function Pricing() {\n  const router = useRouter();\n\n\n  return (\n    <>\n      <Main>\n        <motion.h1\n          initial={{ opacity: 0, y: 12 }}\n          animate={{ opacity: 1, y: 0 }}\n        >\n          Get started with $3/Mo\n        </motion.h1>\n        <motion.section\n          className=\"pricing-table section\"\n          initial={{ opacity: 0, y: 12 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ duration: 0.5, delay: 0.2 }}\n        >\n          <PricingCard\n            style={price_size.normal}\n            plan=\"Monthly Billing\"\n            price={{\n              value: plans.pro.price.value,\n              currency: plans.pro.price.symbol,\n              unit: \"/Mo\",\n            }}\n            features={\n              <>\n                {\n                  plans.pro.features.map((t, i) => (\n                    <li key={i}>{t}</li>\n                  ))\n                }\n              </>\n            }\n            action={\n              <Link href={`/get-started?price=${plans.pro.id}`}>\n                <button>\n                  Get Started\n                </button>\n              </Link>\n            }\n          />\n          <PricingCard\n            style={price_size.highlighted}\n            plan=\"Annual Billing\"\n            price={{\n              value: plans.pro2.price.value,\n              currency: plans.pro2.price.symbol,\n              unit: \"/Mo\",\n            }}\n            desc={\"Save 25% with Annual Billing\"}\n            features={\n              <>\n                {\n                  plans.pro2.features.map((t, i) => (\n                    <li key={i}>{t}</li>\n                  ))\n                }\n              </>\n            }\n            action={\n              <Link href={`/get-started?price=${plans.pro.id}`}>\n                <button>\n                  Get Started\n                </button>\n              </Link>\n            }\n          />\n          <PricingCard\n            style={price_size.normal}\n            plan=\"Enterprise\"\n            desc=\"Annual billing only\"\n            price={{\n              value: plans.enterprise.price.value,\n              currency: plans.enterprise.price.symbol,\n              unit: \"/Year\",\n            }}\n            features={\n              <>\n                {\n                  plans.enterprise.features.map((t, i) => (\n                    <li key={i}>{t}</li>\n                  ))\n                }\n              </>\n            }\n            action={\n              <Link href={`/get-started?price=${plans.pro.id}`}>\n                <button>\n                  Get Started\n                </button>\n              </Link>\n            }\n          />\n        </motion.section>\n        <motion.hr\n          initial={{ opacity: 0, y: 12 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ duration: 0.5, delay: 0.3 }}\n        />\n        <motion.div\n          id=\"free\"\n          initial={{ opacity: 0, y: 12 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ duration: 0.5, delay: 0.6 }}\n        >\n          <FreeForOpensourceCard />\n        </motion.div>\n        <motion.hr\n          initial={{ opacity: 0, y: 12 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ duration: 0.5, delay: 0.3 }}\n        />\n        <motion.section\n          className=\"faq section\"\n          initial={{ opacity: 0, y: 12 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ duration: 0.5, delay: 0.6 }}\n        >\n          <motion.h2\n            initial={{ opacity: 0, y: 12 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.5, delay: 0.8 }}\n          >\n            FAQ\n          </motion.h2>\n          <motion.div\n            className=\"list\"\n            initial={{ opacity: 0, y: 12 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.5, delay: 0.8 }}\n          >\n            {faqs.map((_: any, i: number) => (\n              <FaqItem key={i} {..._} />\n            ))}\n          </motion.div>\n        </motion.section>\n      </Main >\n    </>\n  );\n}\n\nconst Main = styled.main`\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  min-height: 100vh;\n\n  font-family: \"Inter\", sans-serif;\n\n  margin-top: 80px;\n  margin-bottom: 80px;\n\n  h1 {\n    margin-top: 80px;\n    font-size: 48px;\n    font-weight: 700;\n    text-align: center;\n  }\n\n  h2 {\n    margin-top: 32px;\n    text-align: center;\n  }\n\n  .pricing-table {\n    display: flex;\n    flex-direction: row;\n    justify-content: center;\n    align-items: center;\n    gap: 16px;\n    margin-top: 32px;\n  }\n\n  hr {\n    width: 100%;\n    border: 1px solid rgba(255, 255, 255, 0.1);\n    margin-top: 32px;\n    margin-bottom: 32px;\n  }\n\n  section.section {\n    padding: 32px;\n  }\n\n  section.faq {\n    display: flex;\n    flex-direction: column;\n    align-items: stretch;\n    gap: 32px;\n    width: 670px;\n\n    .list {\n      display: flex;\n      flex-direction: column;\n      align-items: stretch;\n      gap: 16px;\n    }\n  }\n`;\n"
  },
  {
    "path": "web/app/(home)/too-many-requests/page.tsx",
    "content": "'use client';\n\nimport React from \"react\";\nimport * as k from \"@/k\";\nimport Link from \"next/link\";\n\nexport default function TooMayRequestsPage() {\n  return (\n    <main className=\"max-w-screen-xl m-auto flex min-h-screen flex-col items-center justify-center p-10 text-center md:text-start\">\n      <div className=\"flex flex-col gap-6 items-center text-center\">\n        <h1\n          className=\"text-4xl lg:text-7xl font-black\"\n        >Too many requests.</h1>\n        <p className=\"max-w-screen-sm opacity-80\">\n          We’re sorry to tell you that you’ve reached your hourly request limit for free-tier.\n          Upgrade your plan to remove this limit.\n        </p>\n      </div>\n      <div className=\"h-20\" />\n      <Link href=\"/console\">\n        <button className=\"bg-neutral-50 text-neutral-950 p-5 rounded-sm\">Sign-in to continue</button>\n      </Link>\n    </main>\n  );\n}\n"
  },
  {
    "path": "web/app/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n/* :root {\n  --foreground-rgb: 0, 0, 0;\n  --background-start-rgb: 214, 219, 220;\n  --background-end-rgb: 255, 255, 255;\n} */\n\n/* @media (prefers-color-scheme: dark) { */\n/* } */\n\n:root {\n  --foreground-rgb: 255, 255, 255;\n  --background-start-rgb: 0, 0, 0;\n  --background-end-rgb: 0, 0, 0;\n}\n\nbody {\n  color: rgb(var(--foreground-rgb));\n  background: linear-gradient(\n      to bottom,\n      transparent,\n      rgb(var(--background-end-rgb))\n    )\n    rgb(var(--background-start-rgb));\n}\n"
  },
  {
    "path": "web/components/chatwoot/index.jsx",
    "content": "\"use client\";\nimport React from \"react\";\n\nclass ChatwootWidget extends React.Component {\n  componentDidMount() {\n    // Add Chatwoot Settings\n    window.chatwootSettings = {\n      hideMessageBubble: false,\n      position: \"right\", // This can be left or right\n      locale: \"en\", // Language to be set\n      type: \"standard\", // [standard, expanded_bubble]\n    };\n\n    // Paste the script from inbox settings except the <script> tag\n    (function (d, t) {\n      var BASE_URL = \"https://app.chatwoot.com\";\n      var g = d.createElement(t),\n        s = d.getElementsByTagName(t)[0];\n      g.src = BASE_URL + \"/packs/js/sdk.js\";\n      s.parentNode.insertBefore(g, s);\n      g.async = !0;\n      g.onload = function () {\n        window.chatwootSDK.run({\n          websiteToken: \"uYszA87RExPbaCa66i6w9c8i\",\n          baseUrl: BASE_URL,\n        });\n      };\n    })(document, \"script\");\n  }\n\n  render() {\n    return null;\n  }\n}\n\nexport default ChatwootWidget;\n"
  },
  {
    "path": "web/components/collapsible-info-card.tsx",
    "content": "import React, { useState } from \"react\";\nimport styled from \"@emotion/styled\";\nimport * as Collapsible from \"@radix-ui/react-collapsible\";\nimport { CaretDownIcon, CaretUpIcon } from \"@radix-ui/react-icons\";\n\nexport function CollapsibleInfoCard({\n  title,\n  children,\n}: React.PropsWithChildren<{ title: string }>) {\n  const [open, setOpen] = useState(false);\n\n  return (\n    <Container open={open} onOpenChange={setOpen}>\n      <Collapsible.Trigger className=\"trigger\">\n        <h2>{title}</h2>\n        <div className=\"icon\">{open ? <CaretUpIcon /> : <CaretDownIcon />}</div>\n      </Collapsible.Trigger>\n      <Collapsible.Content className=\"content\">{children}</Collapsible.Content>\n    </Container>\n  );\n}\n\nconst Container = styled(Collapsible.Root)`\n  display: flex;\n  flex-direction: column;\n  .trigger {\n    display: flex;\n    align-items: center;\n    flex: 1;\n    gap: 8px;\n    justify-content: space-between;\n    cursor: pointer;\n    padding: 12px 16px;\n    border-radius: 4px;\n    background: #f5f5f5;\n    border: 1px solid #e5e5e5;\n    margin-bottom: 8px;\n    h2 {\n      font-size: 16px;\n      font-weight: 500;\n      margin: 0;\n    }\n    .icon {\n      width: 24px;\n      height: 24px;\n      display: flex;\n      align-items: center;\n\n      svg {\n        width: 100%;\n        height: 100%;\n      }\n    }\n  }\n\n  .content {\n    padding: 16px;\n    border-radius: 4px;\n    background: rgba(0, 0, 0, 0.02);\n    border: 1px solid #e5e5e5;\n    margin-bottom: 16px;\n\n    font-size: 14px;\n\n    /* no default padding for ul */\n    ul {\n      margin: 0;\n      padding: 16px;\n    }\n\n    li {\n      padding: 0;\n    }\n  }\n\n  /* motion */\n  .content[data-state=\"open\"] {\n    animation: slideDown 0.2s ease-out;\n  }\n  .content[data-state=\"closed\"] {\n    animation: slideUp 0.2s ease-out;\n  }\n\n  @keyframes slideDown {\n    from {\n      opacity: 0;\n      height: 0;\n    }\n    to {\n      opacity: 1;\n      height: var(--radix-collapsible-content-height);\n    }\n  }\n\n  @keyframes slideUp {\n    from {\n      opacity: 1;\n      height: var(--radix-collapsible-content-height);\n    }\n    to {\n      opacity: 0;\n      height: 0;\n    }\n  }\n`;\n"
  },
  {
    "path": "web/components/console/application-list.tsx",
    "content": "'use client'\n\nimport styled from \"@emotion/styled\";\nimport { ArrowRightIcon } from \"@radix-ui/react-icons\";\nimport Link from \"next/link\";\n\nexport const ApplicationList = styled.div`\n  display: flex;\n  flex-direction: column;\n  align-items: stretch;\n  width: 100%;\n  gap: 8px;\n`;\n\nexport function ApplicationItem({\n  id,\n  name,\n  allowedOrigins,\n}: {\n  id: string;\n  name: string;\n  allowedOrigins?: string[];\n}) {\n  return (\n    <Link href={`/${id}`}>\n      <ItemWrap>\n        <span>\n          {name} ({id})\n        </span>\n        <ArrowRightIcon />\n      </ItemWrap>\n    </Link>\n  );\n}\n\nconst ItemWrap = styled.div`\n  flex: 1;\n  display: flex;\n  cursor: pointer;\n  justify-content: space-between;\n  flex-direction: row;\n  align-items: center;\n  flex: none;\n  border-radius: 4px;\n  border: solid 1px rgba(0, 0, 0, 0.1);\n  box-sizing: border-box;\n  padding: 21px;\n\n  &:hover {\n    border: solid 1px rgba(0, 0, 0, 0.1);\n    background-color: rgba(0, 0, 0, 0.02);\n    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);\n  }\n\n  transition: all 0.2s ease-in-out;\n`;\n"
  },
  {
    "path": "web/components/faq/index.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\n\nexport function FaqItem({ q, a }: { q: string; a: string }) {\n  return (\n    <FaqItemWrapper>\n      <details>\n        <summary className=\"question\">{q}</summary>\n        <p className=\"answer\">{a}</p>\n      </details>\n    </FaqItemWrapper>\n  );\n}\n\nconst FaqItemWrapper = styled.div`\n  display: flex;\n  flex-direction: column;\n  padding: 20px;\n  border: solid 2px rgba(var(--card-border-rgb), 0.1);\n  border-radius: 8px;\n  position: relative;\n\n  .question {\n    font-size: 14px;\n    font-weight: 500;\n  }\n\n  .answer {\n    margin-top: 16px;\n    font-size: 13px;\n    font-weight: 500;\n    text-align: left;\n    opacity: 0.5;\n  }\n`;\n"
  },
  {
    "path": "web/components/free-for-opensource/index.tsx",
    "content": "import Link from \"next/link\"\nimport React from \"react\"\nimport { CheckIcon } from \"@radix-ui/react-icons\"\n\nconst oss_features = [\n  \"1,000,000 requests / month\", \"5mb per request\", \"Unlimited requests per hour\", \"No down time\"\n]\n\nexport function FreeForOpensourceCard() {\n  return <div className=\"max-w-screen-lg border w-auto md:w-[940px] border-white/10 flex gap-20 flex-col-reverse justify-center md:flex-row md:justify-between md:gap-0 rounded-md p-12 pl-20 pr-20\">\n    <div className=\"flex-1 flex flex-col gap-7\">\n      {oss_features.map((t, i) => (\n        <li key={i} className=\"flex gap-4 items-center\">\n          <span className=\"rounded-full bg-white text-black w-6 h-6 flex items-center justify-center\">\n            <CheckIcon width={18} height={18} />\n          </span>\n          <span className=\"text-sm\">\n            {t}\n          </span>\n        </li>\n      ))}\n    </div>\n    <div className=\"flex-1 flex flex-col gap-7 items-stretch\">\n      <h2 className=\"text-4xl\"><strong>Free </strong><span className=\"opacity-80 font-light\">for Open Source</span></h2>\n      <div className=\"flex flex-col gap-2 items-stretch\">\n        <button className=\"bg-white rounded-sm text-lg text-black/80 font-semibold w-auto p-2\">\n          <Link href=\"https://github.com/gridaco/cors.sh/issues/new?template=apply-for-oss-program.yml\" target=\"_blank\">\n            Apply\n          </Link>\n        </button>\n        <span className=\"text-xs opacity-50 text-center\">*Limited to Public Projects that are on Github</span>\n      </div>\n    </div>\n  </div>\n}"
  },
  {
    "path": "web/components/ga.tsx",
    "content": "import Script from \"next/script\";\n\nconst GoogleAnalytics = ({ gaid }: { gaid: string }) => (\n  <>\n    <Script\n      async\n      src={`https://www.googletagmanager.com/gtag/js? \n      id=${gaid}`}\n    ></Script>\n    <Script\n      id=\"google-analytics\"\n      dangerouslySetInnerHTML={{\n        __html: `\n          window.dataLayer = window.dataLayer || [];\n          function gtag(){dataLayer.push(arguments);}\n          gtag('js', new Date());\n\n          gtag('config', '${gaid}');\n        `,\n      }}\n    ></Script>\n  </>\n);\n\nexport default GoogleAnalytics;"
  },
  {
    "path": "web/components/header.tsx",
    "content": "import React from \"react\"\nimport { GitHubLogoIcon } from \"@radix-ui/react-icons\"\nimport Link from \"next/link\"\nimport { SlashIcon } from \"@radix-ui/react-icons\"\n\n\nexport function Header() {\n  return <header className=\"fixed max-w-screen-xl m-auto left-0 right-0 top-0 p-10 pt-5 pb-5 z-10 w-full items-center justify-between font-mono text-sm md:flex\">\n    <span className=\"flex gap-2 items-center w-full justify-center border-b border-gray-800 bg-gradient-to-b from-black pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit md:static md:w-auto  md:rounded-xl md:border md:p-4 md:bg-gray-950 md:dark:bg-zinc-800/30\">\n      <Link href='https://grida.co' target=\"_blank\">\n        <svg width=\"16\" height=\"16\" viewBox=\"0 0 264 267\" fill=\"white\" xmlns=\"http://www.w3.org/2000/svg\">\n          <path fillRule=\"evenodd\" clipRule=\"evenodd\" d=\"M87 133C87 87.7448 121.161 50.4683 165.103 45.5503C150.12 18.3945 121.208 0 88 0C39.5653 0 0.269752 39.1298 0.00138286 87.5014L0 87.5V88V180L88.5 266.5L87 175V133ZM175 179.5L175.5 88C223.596 88 262.68 126.584 263.487 174.487L263.5 174.5V176V266L175 179.5Z\" />\n        </svg>\n      </Link>\n      <SlashIcon className=\"opacity-50\" />\n      <Link href=\"/\">\n        <svg width=\"64\" height=\"11\" viewBox=\"0 0 64 11\" fill=\"white\" xmlns=\"http://www.w3.org/2000/svg\">\n          <path d=\"M10.0869 4.1537H7.12387C7.10286 3.91018 7.04682 3.68928 6.95576 3.49099C6.8682 3.29269 6.74561 3.12223 6.58801 2.9796C6.4339 2.83349 6.24652 2.72217 6.02587 2.64564C5.80522 2.56562 5.5548 2.52562 5.27461 2.52562C4.78427 2.52562 4.36924 2.6439 4.02951 2.88046C3.69328 3.11701 3.4376 3.4562 3.26248 3.89801C3.09086 4.33982 3.00505 4.87034 3.00505 5.48956C3.00505 6.14358 3.09262 6.69149 3.26773 7.1333C3.44636 7.57163 3.70378 7.90212 4.04001 8.12476C4.37624 8.34393 4.78077 8.45351 5.25359 8.45351C5.52328 8.45351 5.76494 8.42046 5.97859 8.35436C6.19223 8.28479 6.37786 8.18564 6.53547 8.05693C6.69308 7.92821 6.82091 7.7734 6.91898 7.5925C7.02055 7.40813 7.08885 7.20114 7.12387 6.97154L10.0869 6.99241C10.0519 7.44466 9.92404 7.9056 9.70339 8.37524C9.48273 8.8414 9.16927 9.27277 8.76299 9.66936C8.36022 10.0625 7.86113 10.379 7.26572 10.6191C6.67031 10.8591 5.97859 10.9791 5.19055 10.9791C4.20287 10.9791 3.31677 10.7687 2.53223 10.3477C1.7512 9.92679 1.13302 9.30756 0.677713 8.49004C0.225904 7.67252 0 6.67236 0 5.48956C0 4.29981 0.231158 3.29791 0.693474 2.48387C1.15579 1.66635 1.77922 1.04886 2.56375 0.631404C3.34829 0.210468 4.22389 0 5.19055 0C5.87001 0 6.49519 0.0921884 7.06608 0.276565C7.63697 0.460942 8.13782 0.73055 8.56861 1.08539C8.9994 1.43675 9.34614 1.86986 9.60882 2.38472C9.8715 2.89959 10.0309 3.48925 10.0869 4.1537Z\" />\n          <path d=\"M21.3174 5.48956C21.3174 6.67932 21.0845 7.68295 20.6187 8.50047C20.1529 9.31452 19.5242 9.93201 18.7327 10.3529C17.9411 10.7704 17.0585 10.9791 16.0849 10.9791C15.1042 10.9791 14.2181 10.7687 13.4266 10.3477C12.6385 9.92331 12.0116 9.30408 11.5458 8.49004C11.0835 7.67252 10.8523 6.67236 10.8523 5.48956C10.8523 4.29981 11.0835 3.29791 11.5458 2.48387C12.0116 1.66635 12.6385 1.04886 13.4266 0.631404C14.2181 0.210468 15.1042 0 16.0849 0C17.0585 0 17.9411 0.210468 18.7327 0.631404C19.5242 1.04886 20.1529 1.66635 20.6187 2.48387C21.0845 3.29791 21.3174 4.29981 21.3174 5.48956ZM18.3124 5.48956C18.3124 4.84946 18.2266 4.31025 18.055 3.87192C17.8869 3.43011 17.6364 3.09614 17.3037 2.87002C16.9745 2.64042 16.5682 2.52562 16.0849 2.52562C15.6015 2.52562 15.1935 2.64042 14.8608 2.87002C14.5316 3.09614 14.2811 3.43011 14.1095 3.87192C13.9414 4.31025 13.8573 4.84946 13.8573 5.48956C13.8573 6.12967 13.9414 6.67062 14.1095 7.11243C14.2811 7.55076 14.5316 7.88473 14.8608 8.11433C15.1935 8.34045 15.6015 8.45351 16.0849 8.45351C16.5682 8.45351 16.9745 8.34045 17.3037 8.11433C17.6364 7.88473 17.8869 7.55076 18.055 7.11243C18.2266 6.67062 18.3124 6.12967 18.3124 5.48956Z\" />\n          <path d=\"M22.1511 10.833V0.14611H26.7953C27.5939 0.14611 28.2926 0.290481 28.8915 0.579222C29.4904 0.867964 29.9562 1.28368 30.289 1.82638C30.6217 2.36907 30.788 3.01961 30.788 3.77799C30.788 4.54333 30.6164 5.18865 30.2732 5.71395C29.9335 6.23925 29.4554 6.63583 28.839 6.9037C28.226 7.17157 27.5098 7.3055 26.6902 7.3055H23.9163V5.05123H26.1018C26.4451 5.05123 26.7375 5.00949 26.9792 4.926C27.2244 4.83903 27.4117 4.70161 27.5413 4.51376C27.6744 4.3259 27.741 4.08065 27.741 3.77799C27.741 3.47185 27.6744 3.22312 27.5413 3.03178C27.4117 2.83697 27.2244 2.69434 26.9792 2.60389C26.7375 2.50996 26.4451 2.463 26.1018 2.463H25.0721V10.833H22.1511ZM28.4555 5.92789L31.1453 10.833H27.9721L25.3453 5.92789H28.4555Z\" />\n          <path d=\"M37.3153 3.48577C37.2873 3.13789 37.156 2.86654 36.9213 2.67173C36.6901 2.47691 36.3382 2.37951 35.8653 2.37951C35.5641 2.37951 35.3172 2.41603 35.1246 2.48909C34.9354 2.55867 34.7953 2.65433 34.7043 2.77609C34.6132 2.89785 34.5659 3.037 34.5624 3.19355C34.5554 3.32226 34.5782 3.4388 34.6307 3.54317C34.6868 3.64405 34.7743 3.73624 34.8934 3.81973C35.0125 3.89975 35.1649 3.9728 35.3505 4.0389C35.5361 4.105 35.7568 4.16414 36.0124 4.21632L36.895 4.40417C37.4904 4.52941 38 4.69466 38.4238 4.89991C38.8476 5.10516 39.1944 5.34693 39.464 5.62524C39.7337 5.90006 39.9316 6.20968 40.0577 6.55408C40.1873 6.89848 40.2538 7.27419 40.2573 7.68121C40.2538 8.38393 40.077 8.97881 39.7267 9.46584C39.3765 9.95288 38.8756 10.3234 38.2242 10.5773C37.5762 10.8313 36.797 10.9583 35.8863 10.9583C34.9512 10.9583 34.1351 10.8208 33.4382 10.546C32.7447 10.2712 32.2053 9.84851 31.8201 9.27799C31.4383 8.70399 31.2457 7.96996 31.2422 7.0759H34.0161C34.0336 7.40291 34.1159 7.67774 34.263 7.90038C34.4101 8.12302 34.6167 8.29175 34.8829 8.40655C35.1526 8.52135 35.4731 8.57875 35.8443 8.57875C36.156 8.57875 36.417 8.54048 36.6271 8.46395C36.8372 8.38741 36.9966 8.28131 37.1052 8.14564C37.2138 8.00996 37.2698 7.85515 37.2733 7.68121C37.2698 7.51771 37.2155 7.37508 37.1104 7.25332C37.0089 7.12808 36.8407 7.01676 36.6061 6.91935C36.3714 6.81847 36.0545 6.72454 35.6552 6.63757L34.5835 6.40797C33.6308 6.20272 32.8795 5.86006 32.3297 5.37998C31.7833 4.89643 31.5119 4.23719 31.5154 3.40228C31.5119 2.72391 31.694 2.13077 32.0617 1.62287C32.433 1.11148 32.9461 0.713156 33.601 0.427894C34.2595 0.142631 35.0142 0 35.8653 0C36.7339 0 37.4852 0.144371 38.1191 0.433112C38.7531 0.721853 39.2416 1.12887 39.5849 1.65418C39.9316 2.176 40.1067 2.78653 40.1102 3.48577H37.3153Z\" />\n          <path d=\"M42.4688 11C42.0625 11 41.714 10.8591 41.4233 10.5773C41.1361 10.2921 40.9943 9.94592 40.9978 9.5389C40.9943 9.14232 41.1361 8.80313 41.4233 8.52135C41.714 8.23956 42.0625 8.09867 42.4688 8.09867C42.854 8.09867 43.1938 8.23956 43.488 8.52135C43.7857 8.80313 43.9363 9.14232 43.9398 9.5389C43.9363 9.81025 43.8645 10.0572 43.7244 10.2799C43.5878 10.4991 43.4092 10.6747 43.1885 10.8069C42.9679 10.9356 42.728 11 42.4688 11Z\" />\n          <path d=\"M50.756 3.48577C50.728 3.13789 50.5967 2.86654 50.362 2.67173C50.1308 2.47691 49.7788 2.37951 49.306 2.37951C49.0048 2.37951 48.7579 2.41603 48.5653 2.48909C48.3761 2.55867 48.236 2.65433 48.145 2.77609C48.0539 2.89785 48.0066 3.037 48.0031 3.19355C47.9961 3.32226 48.0189 3.4388 48.0714 3.54317C48.1275 3.64405 48.215 3.73624 48.3341 3.81973C48.4532 3.89975 48.6055 3.9728 48.7912 4.0389C48.9768 4.105 49.1974 4.16414 49.4531 4.21632L50.3357 4.40417C50.9311 4.52941 51.4407 4.69466 51.8645 4.89991C52.2883 5.10516 52.635 5.34693 52.9047 5.62524C53.1744 5.90006 53.3723 6.20968 53.4984 6.55408C53.628 6.89848 53.6945 7.27419 53.698 7.68121C53.6945 8.38393 53.5176 8.97881 53.1674 9.46584C52.8172 9.95288 52.3163 10.3234 51.6649 10.5773C51.0169 10.8313 50.2377 10.9583 49.327 10.9583C48.3919 10.9583 47.5758 10.8208 46.8789 10.546C46.1854 10.2712 45.646 9.84851 45.2608 9.27799C44.879 8.70399 44.6864 7.96996 44.6829 7.0759H47.4568C47.4743 7.40291 47.5566 7.67774 47.7037 7.90038C47.8508 8.12302 48.0574 8.29175 48.3236 8.40655C48.5933 8.52135 48.9138 8.57875 49.285 8.57875C49.5967 8.57875 49.8576 8.54048 50.0678 8.46395C50.2779 8.38741 50.4373 8.28131 50.5459 8.14564C50.6544 8.00996 50.7105 7.85515 50.714 7.68121C50.7105 7.51771 50.6562 7.37508 50.5511 7.25332C50.4496 7.12808 50.2814 7.01676 50.0468 6.91935C49.8121 6.81847 49.4952 6.72454 49.0959 6.63757L48.0241 6.40797C47.0715 6.20272 46.3202 5.86006 45.7704 5.37998C45.224 4.89643 44.9525 4.23719 44.956 3.40228C44.9525 2.72391 45.1347 2.13077 45.5024 1.62287C45.8737 1.11148 46.3868 0.713156 47.0417 0.427894C47.7002 0.142631 48.4549 0 49.306 0C50.1746 0 50.9259 0.144371 51.5598 0.433112C52.1937 0.721853 52.6823 1.12887 53.0256 1.65418C53.3723 2.176 53.5474 2.78653 53.5509 3.48577H50.756Z\" />\n          <path d=\"M54.2914 10.833V0.14611H57.2124V4.32068H61.079V0.14611H64V10.833H61.079V6.65844H57.2124V10.833H54.2914Z\" />\n        </svg>\n      </Link>\n    </span>\n    <div className='m-0 hidden md:flex gap-4 md:ml-10'>\n      {/* <Link href='#example'>\n        Example\n      </Link> */}\n      <Link href='/playground'>\n        Playground\n      </Link>\n      <Link href='/pricing'>\n        Pricing\n      </Link>\n    </div>\n    <div className=\"flex-1\" />\n    <div className=\"fixed bottom-0 left-0 p-5 flex h-48 w-full items-end justify-center bg-gradient-to-t from-black via-gray dark:from-black dark:via-black md:p-0 md:static md:h-auto md:w-auto md:bg-none\">\n      <div className='flex gap-4 items-center'>\n        {/* <Link href='/console'>\n          Console\n        </Link> */}\n        <Link href='/get-started'>\n          <button className=\"bg-none md:bg-neutral-50 text-inherit md:text-neutral-950 p-3 rounded-md\">\n            Get Started\n          </button>\n        </Link>\n\n        <div style={{ width: 16 }} />\n        <Link href=\"https://github.com/gridaco/cors.sh\" target=\"_blank\">\n          <GitHubLogoIcon width={24} height={24} />\n        </Link>\n      </div>\n    </div>\n  </header>\n}"
  },
  {
    "path": "web/components/home-background.tsx",
    "content": "'use client'\n\nimport React from \"react\"\nimport Image from 'next/image'\nimport { motion } from 'framer-motion';\n\nexport function HomeBackground() {\n  return (\n    <motion.div\n      id='background'\n      className='fixed -z-50 inset-0 select-none pointer-events-none'\n      transition={{\n        delay: 0.2,\n        duration: 2,\n        ease: 'easeOut'\n      }}\n      initial={{\n        opacity: 0.1,\n        scale: 1.3\n      }}\n      animate={{\n        opacity: 1,\n        scale: 1\n      }}\n    >\n      <div\n\n        className='inset-0 flex items-center justify-center'>\n        <Image src=\"/assets/background.svg\" layout=\"fill\" objectFit=\"cover\" alt='background graphic' />\n      </div>\n      <VignettingOverlay w={200} />\n    </motion.div>\n  )\n}\n\nfunction VignettingOverlay({ w }: { w: number }) {\n  return (\n    <div className=\"absolute inset-0\">\n      {/* Top edge */}\n      <div\n        style={{ height: w }}\n        className=\"absolute inset-x-0 top-0 bg-gradient-to-b from-black via-transparent to-transparent\" />\n\n      {/* Bottom edge */}\n      <div\n        style={{ height: w }}\n        className=\"absolute inset-x-0 bottom-0 bg-gradient-to-t from-black via-transparent to-transparent\" />\n\n      {/* Left edge */}\n      <div\n        style={{ width: w }}\n        className=\"absolute inset-y-0 left-0 bg-gradient-to-r from-black via-transparent to-transparent\" />\n\n      {/* Right edge */}\n      <div\n        style={{ width: w }}\n        className=\"absolute inset-y-0 right-0 bg-gradient-to-l from-black via-transparent to-transparent\" />\n    </div>\n  )\n}"
  },
  {
    "path": "web/components/home-hover-card.tsx",
    "content": "'use client'\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { atomDark as scheme } from 'react-syntax-highlighter/dist/esm/styles/prism';\n\n\nimport React from 'react';\nimport { useMove } from '@use-gesture/react'\nimport styled from \"@emotion/styled\"\nimport Image from 'next/image';\nimport { examples } from \"@/k\"\nimport Link from 'next/link';\nimport LineMotion from '@/motions/electron';\nimport { motion } from 'framer-motion';\n\nexport function HomeCardExample() {\n  return <HomeCardWrapper\n    style={{\n      willChange: 'transform'\n    }}\n    initial={{\n      opacity: 0.0,\n      transform: \"perspective(1000px) translateY(40px) rotateX(0deg) rotateY(0deg)\",\n    }}\n    animate={{\n      opacity: 1,\n      transform: \"perspective(1000px) translateY(0) rotateX(5deg) rotateY(-5deg)\",\n    }}\n    whileHover={{\n      transform: \"perspective(600px) rotateX(0deg) rotateY(0deg)\",\n    }}\n    className='max-w-lg w-auto p-4 border border-white/10 rounded-xl flex flex-col gap-4 bg-black items-stretch'>\n    <div className='relative flex mt-10 pl-8 pr-8 flex-row gap-2 justify-between select-none pointer-events-none'>\n      <div className='absolute inset-0 translate-y-1/2 ml-16 mr-16'>\n        <LineMotion />\n      </div>\n      <Image className='z-10' src='/assets/home-demo-0-illust-0-web.png' width={100} height={90} alt='' />\n      <Image className='z-10' src='/assets/home-demo-0-illust-1-cloud.png' width={100} height={90} alt='' />\n      <Image className='z-10' src='/assets/home-demo-0-illust-2-server.png' width={100} height={90} alt='' />\n    </div>\n\n    <div className='text-[10px] lg:text-xs rounded-lg overflow-scroll max-w-xs md:max-w-sm lg:max-w-2xl' style={{\n      background: '#08070E',\n      border: 'rgba(144, 175, 255, 0.1) 1px solid'\n    }}>\n      {/* @ts-ignore */}\n      <SyntaxHighlighter\n        customStyle={{\n          width: 'auto',\n          background: 'transparent',\n          opacity: 0.8\n        }}\n        language='typescript'\n        style={scheme}>\n        {examples.fetch('https://example.com')}\n      </SyntaxHighlighter>\n    </div>\n    <Link className='flex' href='/get-started'>\n      <button className='p-3 bg-neutral-50 text-orange-950 rounded-lg flex-1 text-sm font-semibold'>Get Started</button>\n    </Link>\n  </HomeCardWrapper >\n}\n\nconst HomeCardWrapper = styled(motion.div)`\n  box-shadow: 0px 4px 128px 4px rgba(94, 154, 223, 0.16);\n`\n\n\nexport function HomeHoverCardV2() {\n  const [{ x, y }, set] = React.useState({ x: 0, y: 0 });\n\n  const bind = useMove(({ movement: [mx, my], memo }) => {\n    const el = document.getElementById('card');\n    const rect = el!.getBoundingClientRect();\n    const halfWidth = rect.width / 2;\n    const halfHeight = rect.height / 2;\n\n    const rotateX = -1 * (my - halfHeight) / halfHeight * 10; // 10 is rotation strength\n    const rotateY = (mx - halfWidth) / halfWidth * 10; // 10 is rotation strength\n\n    set({ x: rotateY, y: rotateX });\n\n    return { x: mx, y: my };\n  });\n\n  const style = {\n    transform: `perspective(600px) rotateX(${y}deg) rotateY(${x}deg)`\n  };\n\n  return (\n    <div className='p-40' {...bind()}>\n      {/* @ts-ignore */}\n      <Card id=\"card\" className=\"card\" style={style}>\n        <Image\n          className='shadow-lg shadow-white'\n          src=\"/tmp-asset-1.png\" width={470} height={540} alt=\"\" />\n      </Card>\n    </div>\n  );\n}\n\n\nconst Card = styled.div`\n  width: 300px;\n  height: 300px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background-color: #e1e1e1;\n  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);\n  border-radius: 15px;\n  transition: transform 0.1s;\n  will-change: transform;\n\n`"
  },
  {
    "path": "web/components/landing/send-me-an-api-key-form.tsx",
    "content": "'use client'\n\nimport React, { useState } from \"react\";\nimport client from \"@cors.sh/service-api\";\nimport { toast } from \"react-hot-toast\";\nimport { validateEmail } from \"@/utils/email-validation\";\n\nexport function SendMeAnApiKeyForm() {\n  const [isBusy, setIsBusy] = useState(false);\n  const [email, setEmail] = useState(\"\");\n\n  const onsend = async () => {\n    // validate email\n    if (!validateEmail(email)) {\n      toast.error(<p>Invalid Email</p>);\n      return;\n    }\n\n    // send email\n    setIsBusy(true);\n\n    client\n      .onboardingWithEmail({ email: email })\n      .then((data) => {\n        toast.success(\n          <p>\n            API Key sent to your email.\n            <br />\n            Please check your <b>spam folder</b> as well\n          </p>\n        );\n\n        // log conversion (signup)\n        // @ts-ignore\n        window.gtag?.(\"event\", \"sign_up\", {\n          method: \"email\",\n        });\n      })\n      .catch((e) => {\n        toast.error(<p>Something went wrong</p>);\n      })\n      .finally(() => {\n        setIsBusy(false);\n      });\n  };\n\n  // \n  return (\n    <form className='flex flex-col md:flex-row gap-3' onSubmit={(e) => {\n      e.preventDefault();\n      onsend();\n    }}>\n      <input\n        className='p-5 text-neutral-100 bg-neutral-900 rounded-sm outline-none focus:outline-neutral-900 flex-1 min-w-[200px]'\n        type=\"email\"\n        autoComplete=\"email\"\n        placeholder=\"alice@acme.com\"\n        value={email}\n        disabled={isBusy}\n        onChange={(e) => {\n          setEmail(e.target.value);\n        }}\n      />\n      <button\n        className='p-5 pl-6 pr-6 border-neutral-900 border-2 rounded-sm text-xs lg:text-sm font-semibold bg-black'\n        disabled={isBusy} onClick={onsend}\n      >Send me an API Key</button>\n    </form>\n  )\n}"
  },
  {
    "path": "web/components/logo/index.tsx",
    "content": "import React from \"react\";\n\nexport function Logo({\n  color = \"black\",\n  width = 104,\n}: {\n  width?: React.CSSProperties[\"width\"];\n  color?: React.CSSProperties[\"color\"];\n}) {\n  return (\n    <svg\n      width={width}\n      height=\"19\"\n      viewBox=\"0 0 104 19\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        d=\"M16.4205 7.09091H11.6136C11.5795 6.69318 11.4886 6.33239 11.3409 6.00852C11.1989 5.68466 11 5.40625 10.7443 5.17329C10.4943 4.93466 10.1903 4.75284 9.83239 4.62784C9.47443 4.49716 9.06818 4.43182 8.61364 4.43182C7.81818 4.43182 7.14489 4.625 6.59375 5.01136C6.0483 5.39773 5.63352 5.9517 5.34943 6.6733C5.07102 7.39489 4.93182 8.26136 4.93182 9.27273C4.93182 10.3409 5.07386 11.2358 5.35795 11.9574C5.64773 12.6733 6.06534 13.2131 6.6108 13.5767C7.15625 13.9347 7.8125 14.1136 8.57955 14.1136C9.01705 14.1136 9.40909 14.0597 9.75568 13.9517C10.1023 13.8381 10.4034 13.6761 10.6591 13.4659C10.9148 13.2557 11.1222 13.0028 11.2812 12.7074C11.446 12.4062 11.5568 12.0682 11.6136 11.6932L16.4205 11.7273C16.3636 12.4659 16.1563 13.2187 15.7983 13.9858C15.4403 14.7472 14.9318 15.4517 14.2727 16.0994C13.6193 16.7415 12.8097 17.2585 11.8438 17.6506C10.8778 18.0426 9.75568 18.2386 8.47727 18.2386C6.875 18.2386 5.4375 17.8949 4.16477 17.2074C2.89773 16.5199 1.89489 15.5085 1.15625 14.1733C0.423295 12.8381 0.0568182 11.2045 0.0568182 9.27273C0.0568182 7.32955 0.431818 5.69318 1.18182 4.36364C1.93182 3.02841 2.94318 2.01989 4.21591 1.33807C5.48864 0.650568 6.90909 0.306818 8.47727 0.306818C9.57955 0.306818 10.5938 0.457386 11.5199 0.758522C12.446 1.05966 13.2585 1.5 13.9574 2.07954C14.6563 2.65341 15.2188 3.3608 15.6449 4.2017C16.071 5.04261 16.3295 6.00568 16.4205 7.09091ZM34.6394 9.27273C34.6394 11.2159 34.2616 12.8551 33.5059 14.1903C32.7502 15.5199 31.7303 16.5284 30.4462 17.2159C29.1621 17.8977 27.7303 18.2386 26.1508 18.2386C24.5599 18.2386 23.1224 17.8949 21.8383 17.2074C20.5599 16.5142 19.5428 15.5028 18.7871 14.1733C18.0371 12.8381 17.6621 11.2045 17.6621 9.27273C17.6621 7.32955 18.0371 5.69318 18.7871 4.36364C19.5428 3.02841 20.5599 2.01989 21.8383 1.33807C23.1224 0.650568 24.5599 0.306818 26.1508 0.306818C27.7303 0.306818 29.1621 0.650568 30.4462 1.33807C31.7303 2.01989 32.7502 3.02841 33.5059 4.36364C34.2616 5.69318 34.6394 7.32955 34.6394 9.27273ZM29.7644 9.27273C29.7644 8.22727 29.6252 7.34659 29.3468 6.63068C29.0741 5.90909 28.6678 5.36364 28.128 4.99432C27.5939 4.61932 26.9349 4.43182 26.1508 4.43182C25.3667 4.43182 24.7047 4.61932 24.165 4.99432C23.6309 5.36364 23.2246 5.90909 22.9462 6.63068C22.6735 7.34659 22.5371 8.22727 22.5371 9.27273C22.5371 10.3182 22.6735 11.2017 22.9462 11.9233C23.2246 12.6392 23.6309 13.1847 24.165 13.5597C24.7047 13.929 25.3667 14.1136 26.1508 14.1136C26.9349 14.1136 27.5939 13.929 28.128 13.5597C28.6678 13.1847 29.0741 12.6392 29.3468 11.9233C29.6252 11.2017 29.7644 10.3182 29.7644 9.27273ZM35.9919 18V0.545454H43.526C44.8214 0.545454 45.9549 0.78125 46.9265 1.25284C47.8981 1.72443 48.6538 2.40341 49.1936 3.28977C49.7334 4.17614 50.0032 5.23864 50.0032 6.47727C50.0032 7.72727 49.7248 8.78125 49.168 9.6392C48.6169 10.4972 47.8413 11.1449 46.8413 11.5824C45.847 12.0199 44.6851 12.2386 43.3555 12.2386H38.8555V8.55682H42.401C42.9578 8.55682 43.4322 8.48864 43.8243 8.35227C44.222 8.21023 44.526 7.9858 44.7362 7.67898C44.9521 7.37216 45.0601 6.97159 45.0601 6.47727C45.0601 5.97727 44.9521 5.57102 44.7362 5.25852C44.526 4.94034 44.222 4.70739 43.8243 4.55966C43.4322 4.40625 42.9578 4.32954 42.401 4.32954H40.7305V18H35.9919ZM46.2191 9.98864L50.5828 18H45.4351L41.1737 9.98864H46.2191ZM60.5922 6C60.5468 5.43182 60.3337 4.98864 59.953 4.67045C59.578 4.35227 59.007 4.19318 58.2399 4.19318C57.7513 4.19318 57.3507 4.25284 57.0382 4.37216C56.7314 4.4858 56.5041 4.64205 56.3564 4.84091C56.2087 5.03977 56.132 5.26705 56.1263 5.52273C56.1149 5.73295 56.1519 5.9233 56.2371 6.09375C56.328 6.25852 56.4701 6.40909 56.6632 6.54545C56.8564 6.67614 57.1036 6.79545 57.4047 6.90341C57.7059 7.01136 58.0638 7.10795 58.4786 7.19318L59.9104 7.5C60.8763 7.70455 61.703 7.97443 62.3905 8.30966C63.078 8.64489 63.6405 9.03977 64.078 9.49432C64.5155 9.94318 64.8365 10.4489 65.0411 11.0114C65.2513 11.5739 65.3593 12.1875 65.3649 12.8523C65.3593 14 65.0723 14.9716 64.5041 15.767C63.936 16.5625 63.1235 17.1676 62.0666 17.5824C61.0155 17.9972 59.7513 18.2045 58.274 18.2045C56.757 18.2045 55.4331 17.9801 54.3024 17.5312C53.1774 17.0824 52.3024 16.392 51.6774 15.4602C51.0581 14.5227 50.7456 13.3239 50.7399 11.8636H55.2399C55.2684 12.3977 55.4019 12.8466 55.6405 13.2102C55.8791 13.5739 56.2144 13.8494 56.6462 14.0369C57.0837 14.2244 57.6036 14.3182 58.2059 14.3182C58.7115 14.3182 59.1348 14.2557 59.4757 14.1307C59.8166 14.0057 60.0752 13.8324 60.2513 13.6108C60.4274 13.3892 60.5184 13.1364 60.524 12.8523C60.5184 12.5852 60.4303 12.3523 60.2598 12.1534C60.0951 11.9489 59.8223 11.767 59.4416 11.608C59.061 11.4432 58.5468 11.2898 57.899 11.1477L56.1604 10.7727C54.6149 10.4375 53.3962 9.87784 52.5041 9.09375C51.6178 8.30398 51.1774 7.22727 51.1831 5.86364C51.1774 4.75568 51.4729 3.78693 52.0695 2.95739C52.6718 2.12216 53.5041 1.47159 54.5666 1.00568C55.6348 0.539772 56.8593 0.306818 58.2399 0.306818C59.649 0.306818 60.8678 0.542613 61.8962 1.0142C62.9246 1.4858 63.7172 2.15057 64.274 3.00852C64.8365 3.8608 65.1206 4.85795 65.1263 6H60.5922ZM68.9525 18.2727C68.2934 18.2727 67.7281 18.0426 67.2565 17.5824C66.7906 17.1165 66.5605 16.5511 66.5661 15.8864C66.5605 15.2386 66.7906 14.6847 67.2565 14.2244C67.7281 13.7642 68.2934 13.5341 68.9525 13.5341C69.5775 13.5341 70.1286 13.7642 70.6059 14.2244C71.0889 14.6847 71.3332 15.2386 71.3389 15.8864C71.3332 16.3295 71.2167 16.733 70.9894 17.0966C70.7678 17.4545 70.4781 17.7415 70.1201 17.9574C69.7622 18.1676 69.373 18.2727 68.9525 18.2727ZM82.3966 6C82.3511 5.43182 82.1381 4.98864 81.7574 4.67045C81.3824 4.35227 80.8114 4.19318 80.0443 4.19318C79.5557 4.19318 79.1551 4.25284 78.8426 4.37216C78.5358 4.4858 78.3085 4.64205 78.1608 4.84091C78.0131 5.03977 77.9364 5.26705 77.9307 5.52273C77.9193 5.73295 77.9563 5.9233 78.0415 6.09375C78.1324 6.25852 78.2744 6.40909 78.4676 6.54545C78.6608 6.67614 78.908 6.79545 79.2091 6.90341C79.5102 7.01136 79.8682 7.10795 80.283 7.19318L81.7148 7.5C82.6807 7.70455 83.5074 7.97443 84.1949 8.30966C84.8824 8.64489 85.4449 9.03977 85.8824 9.49432C86.3199 9.94318 86.6409 10.4489 86.8455 11.0114C87.0557 11.5739 87.1636 12.1875 87.1693 12.8523C87.1636 14 86.8767 14.9716 86.3085 15.767C85.7403 16.5625 84.9278 17.1676 83.871 17.5824C82.8199 17.9972 81.5557 18.2045 80.0784 18.2045C78.5614 18.2045 77.2375 17.9801 76.1068 17.5312C74.9818 17.0824 74.1068 16.392 73.4818 15.4602C72.8625 14.5227 72.55 13.3239 72.5443 11.8636H77.0443C77.0727 12.3977 77.2063 12.8466 77.4449 13.2102C77.6835 13.5739 78.0188 13.8494 78.4506 14.0369C78.8881 14.2244 79.408 14.3182 80.0102 14.3182C80.5159 14.3182 80.9392 14.2557 81.2801 14.1307C81.621 14.0057 81.8795 13.8324 82.0557 13.6108C82.2318 13.3892 82.3227 13.1364 82.3284 12.8523C82.3227 12.5852 82.2347 12.3523 82.0642 12.1534C81.8994 11.9489 81.6267 11.767 81.246 11.608C80.8653 11.4432 80.3511 11.2898 79.7034 11.1477L77.9648 10.7727C76.4193 10.4375 75.2006 9.87784 74.3085 9.09375C73.4222 8.30398 72.9818 7.22727 72.9875 5.86364C72.9818 4.75568 73.2773 3.78693 73.8739 2.95739C74.4761 2.12216 75.3085 1.47159 76.371 1.00568C77.4392 0.539772 78.6636 0.306818 80.0443 0.306818C81.4534 0.306818 82.6722 0.542613 83.7006 1.0142C84.729 1.4858 85.5216 2.15057 86.0784 3.00852C86.6409 3.8608 86.925 4.85795 86.9307 6H82.3966ZM88.1319 18V0.545454H92.8705V7.36364H99.1432V0.545454H103.882V18H99.1432V11.1818H92.8705V18H88.1319Z\"\n        fill={color}\n      />\n    </svg>\n  );\n}\n"
  },
  {
    "path": "web/components/pricing/index.tsx",
    "content": "import React from \"react\";\nimport styled from \"@emotion/styled\";\nimport { motion } from \"framer-motion\";\n\nexport function PricingCard({\n  plan,\n  price,\n  desc,\n  features,\n  action,\n  style = {},\n}: {\n  plan: string;\n  price: {\n    value: number | string;\n    currency?: string;\n    unit?: string;\n  };\n  desc?: string;\n  features: React.ReactNode;\n  action?: React.ReactNode;\n  style?: React.CSSProperties;\n}) {\n  return (\n    <PricingCardWrapper style={style} whileHover={{ scale: 1.02 }}>\n      <span className=\"plan\">{plan}</span>\n      <section>\n        <span className=\"price\">\n          <span className=\"a\">\n            {typeof price.value === \"string\"\n              ? price.value\n              : `${price.currency}${price.value}`}\n          </span>\n          <span className=\"b\">{price.unit}</span>\n        </span>\n        {desc && (\n          <span className=\"unit\">\n            <span className=\"b\">{desc}</span>\n          </span>\n        )}\n      </section>\n      <section>\n        <span className=\"desc\">{features}</span>\n      </section>\n      <div style={{ minHeight: 20, flex: 1 }} />\n      <div className=\"self-start\">\n        {action}\n      </div>\n      <span className=\"dot\" />\n    </PricingCardWrapper>\n  );\n}\n\nconst PricingCardWrapper = styled(motion.div)`\n  --card-border-rgb: 200, 200, 200;\n\n  display: flex;\n  flex-direction: column;\n  padding: 20px;\n  border: solid 2px rgba(var(--card-border-rgb), 0.1);\n  border-radius: 8px;\n  position: relative;\n\n  font-family: Inter, sans-serif;\n  text-align: left;\n\n  &:hover {\n    border: solid 2px rgba(var(--card-border-rgb), 0.2);\n    box-shadow: 0px 0px 24px 2px rgba(var(--card-box-shadow-rgb), 0.1);\n  }\n\n  section {\n    margin-top: 32px;\n  }\n\n  .plan {\n    font-size: 14px;\n    font-weight: 500;\n  }\n\n  .price {\n    display: flex;\n    justify-content: flex-start;\n    flex-direction: row;\n    align-items: center;\n    flex: none;\n    gap: 2px;\n    box-sizing: border-box;\n\n    .a {\n      font-size: 32px;\n      font-weight: 800;\n    }\n\n    .b {\n      font-size: 13px;\n      font-weight: 500;\n      opacity: 0.5;\n    }\n  }\n\n  .unit {\n    display: flex;\n    justify-content: flex-start;\n    flex-direction: row;\n    align-items: center;\n    flex: none;\n    gap: 4px;\n    box-sizing: border-box;\n    opacity: 0.8;\n\n    .b {\n      font-size: 13px;\n      font-weight: 500;\n      opacity: 0.5;\n    }\n  }\n\n  .desc {\n    font-weight: 500;\n    opacity: 0.8;\n  }\n\n  button {\n    box-shadow: 0px 4px 24px 2px rgba(var(--card-box-shadow-rgb), 0.2);\n    background: white;\n    border: solid 1px rgba(120, 120, 120, 0.8);\n    border-radius: 4px;\n    padding: 8px 10px;\n    font-size: 12px;\n    font-weight: 600;\n    outline: none;\n    cursor: pointer;\n    color: black;\n\n    :hover {\n      opacity: 0.8;\n    }\n\n    :disabled {\n      opacity: 0.5;\n    }\n\n    :active {\n      opacity: 1;\n    }\n\n    :focus {\n    }\n  }\n\n  .dot {\n    width: 8px;\n    height: 8px;\n    background-color: rgb(var(--foreground-rgb));\n    border-radius: 50%;\n    position: absolute;\n    top: 20px;\n    right: 20px;\n  }\n`;\n"
  },
  {
    "path": "web/components/pricing/mini.tsx",
    "content": "\nexport function MiniPlanSelect({\n  onChange,\n  value,\n  options,\n}: {\n  onChange: (id: string) => void;\n  value?: string;\n  options: Array<{ id: string, label: React.ReactElement | string, content: React.ReactElement | string }>;\n}) {\n  return <div className=\"flex flex-row justify-evenly gap-4\">\n    {options.map(({ id, label, content }, i) => {\n      const selected = id === value;\n      return (\n        <MiniPriceCard key={i}\n          label={label}\n          content={content}\n          selected={selected}\n          onClick={() => {\n            onChange(id);\n          }} />\n      )\n    })}\n  </div>\n}\n\n\nexport function MiniPriceCard({\n  label,\n  content,\n  selected,\n  onClick\n}: {\n  label: React.ReactElement | string;\n  content: React.ReactElement | string;\n  selected: boolean;\n  onClick: () => void;\n}) {\n  return <button className=\"relative flex flex-col gap-2 items-center justify-center border rounded-sm border-neutral-50 border-opacity-10 flex-1 p-5 data-[selected=true]:border-opacity-80\"\n    data-selected={selected}\n    onClick={onClick}\n  >\n    <div\n      className=\"absolute right-4 top-4 w-2 h-2 rounded-full bg-neutral-50\"\n      style={{\n        visibility: selected ? \"visible\" : \"hidden\",\n      }} />\n    <h6 className=\"text-sm\">\n      {label}\n    </h6>\n    <p className=\"text-xs opacity-80 flex-1\">\n      {content}\n    </p>\n  </button>\n}"
  },
  {
    "path": "web/k/examples.ts",
    "content": "/**\n * usage code snippet\n */\nexport const examples = {\n  simplest: (\n    t: string,\n    key: string = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n  ) => `fetch('https://proxy.cors.sh/${t}', {\n  headers: {\n    'x-cors-api-key': '${key}',\n  }\n}).then(console.log);`,\n  fetch: (\n    t: string,\n    key: string = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n  ) => `fetch('https://proxy.cors.sh/${t}', {\n  headers: {\n    'x-cors-api-key': '${key}',\n  }\n});\n\n// or...\nfuncton fetchWithProxy(url, params){\n  return fetch(\\`https://proxy.cors.sh/\\${url}\\`, \n  { \n    ...params,\n    headers: \n    { \n      ...params.headers,\n      'x-cors-api-key': '${key}'\n    }\n  });\n}\n\nfetchWithProxy('${t}')\n  `,\n  axios: (\n    t: string,\n    key: string = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n  ) => `import Axios from \"axios\";\n\nAxios.get('https://proxy.cors.sh/${t}', {\n  headers: {\n    'x-cors-api-key': '${key}',\n  }\n})\n\n// or...\nconst client = Axios.create({\n  baseURL: 'https://proxy.cors.sh/' + '${t}',\n  headers: {\n    'x-cors-api-key': '${key}',\n  }\n})\n\nclient.get('/')\n`,\n} as const;\n"
  },
  {
    "path": "web/k/external-links.ts",
    "content": "export const LINK_APPLY_FOR_OSS_PLAN =\n  \"https://github.com/gridaco/cors.sh/issues/new?template=apply-for-oss-program.yml\";\n"
  },
  {
    "path": "web/k/faq.json",
    "content": "[\n  {\n    \"q\": \"Can I use CORS.SH Proxy for multiple websites?\",\n    \"a\": \"Yes, with single subscription, you are able to use CORS.SH Proxy for multiple websites unlimitedly, unless it exceeds the quota.\"\n  },\n  {\n    \"q\": \"How can I Apply to OSS Progam?\",\n    \"a\": \"Any Open-source projects with 5 stars is capable to apply to OSS Program.\"\n  }\n]\n"
  },
  {
    "path": "web/k/host.ts",
    "content": "export const HOST =\n  process.env.NODE_ENV === \"production\"\n    ? \"https://cors.sh\"\n    : \"http://localhost:8823\";\n\nexport const SERVER_URL =\n  process.env.NODE_ENV === \"production\"\n    ? // TODO: this url is not live.\n      \"https://services.cors.sh\"\n    : \"http://localhost:4021\";\n"
  },
  {
    "path": "web/k/index.ts",
    "content": "export * from \"./plans\";\nexport * from \"./external-links\";\nexport * from \"./host\";\nexport * from \"./examples\";\n"
  },
  {
    "path": "web/k/plans.json",
    "content": "{\n  \"pro\": {\n    \"id\": \"price_1LbnTwAvR3geCh5rZm9v8CAy\",\n    \"name\": \"Pro - Monthly\",\n    \"price\": {\n      \"value\": 4,\n      \"currency\": \"usd\",\n      \"symbol\": \"$\",\n      \"interval\": \"month\"\n    },\n    \"features\": [\n      \"Up to 500,000 requests per month\",\n      \"500GB Bandwidth\",\n      \"Unlimited Projects\",\n      \"No hourly request limit\",\n      \"Up to 6mb per request\"\n    ]\n  },\n  \"pro2\": {\n    \"id\": \"price_1LbnV4AvR3geCh5rMEwJ5Zf1\",\n    \"name\": \"Pro - Annually\",\n    \"desc\": \"Annual billing only\",\n    \"price\": {\n      \"value\": 3,\n      \"currency\": \"usd\",\n      \"symbol\": \"$\",\n      \"interval\": \"month\"\n    },\n    \"features\": [\n      \"Up to 500,000 requests per month\",\n      \"500GB Bandwidth\",\n      \"Unlimited Projects\",\n      \"No hourly request limit\",\n      \"Up to 6mb per request\"\n    ]\n  },\n  \"enterprise\": {\n    \"id\": \"price_1MNTlpAvR3geCh5r3gqzq2s7\",\n    \"name\": \"Enterprise\",\n    \"price\": {\n      \"value\": 499,\n      \"currency\": \"usd\",\n      \"symbol\": \"$\",\n      \"interval\": \"year\"\n    },\n    \"features\": [\n      \"Up to 10,000,000 requests per month\",\n      \"1TB Bandwidth\",\n      \"Unlimited Projects\",\n      \"No hourly request limit\",\n      \"Max 6mb per request\"\n    ]\n  }\n}\n"
  },
  {
    "path": "web/k/plans.test.json",
    "content": "{\n  \"pro\": {\n    \"id\": \"price_1Lda7UAvR3geCh5rVaajCSw6\",\n    \"name\": \"Pro - Monthly\",\n    \"price\": {\n      \"value\": 4,\n      \"currency\": \"usd\",\n      \"symbol\": \"$\",\n      \"interval\": \"month\"\n    },\n    \"features\": [\n      \"Up to 500,000 requests per month\",\n      \"500GB Bandwidth\",\n      \"Unlimited Projects\",\n      \"No hourly request limit\",\n      \"Up to 6mb per request\"\n    ]\n  },\n  \"pro2\": {\n    \"id\": \"price_1MMpznAvR3geCh5ro9O4Gdlt\",\n    \"desc\": \"Annual billing only\",\n    \"name\": \"Pro - Annually\",\n    \"price\": {\n      \"value\": 3,\n      \"currency\": \"usd\",\n      \"symbol\": \"$\",\n      \"interval\": \"month\"\n    },\n    \"features\": [\n      \"Up to 500,000 requests per month\",\n      \"500GB Bandwidth\",\n      \"Unlimited Projects\",\n      \"No hourly request limit\",\n      \"Up to 6mb per request\"\n    ]\n  },\n  \"enterprise\": {\n    \"id\": \"price_1MNTfvAvR3geCh5rV0KueWOk\",\n    \"name\": \"Enterprise\",\n    \"price\": {\n      \"value\": 499,\n      \"currency\": \"usd\",\n      \"symbol\": \"$\",\n      \"interval\": \"year\"\n    },\n    \"features\": [\n      \"Up to 10,000,000 requests per month\",\n      \"1TB Bandwidth\",\n      \"Unlimited Projects\",\n      \"No hourly request limit\",\n      \"Max 6mb per request\"\n    ]\n  }\n}\n"
  },
  {
    "path": "web/k/plans.ts",
    "content": "import plans_live from \"@/k/plans.json\";\nimport plans_test from \"@/k/plans.test.json\";\n\nexport const plans =\n  process.env.NODE_ENV === \"production\" ? plans_live : plans_test;\n\nconst price_pro_monthly = {\n  ...plans.pro2,\n  desc2: \"Pro - Monthly\",\n} as const;\n\nconst price_pro_yearly = {\n  ...plans.pro2,\n  desc2: \"Pro - Save 25% with Annual billing\",\n} as const;\n\n// const enterprise_yearly: Price = {\n//   id: plans.enterprise.id,\n//   name: \"Enterprise\",\n//   price: \"$499\",\n//   unit: \"Year\",\n//   features: [\n//     \"Up to 10,000,000 requests per month\",\n//     \"1TB Bandwidth\",\n//     \"Unlimited Projects\",\n//     \"No hourly request limit\",\n//     \"Max 6mb per request\",\n//   ],\n// };\n"
  },
  {
    "path": "web/layouts/payment-required-page.tsx",
    "content": "import React from \"react\";\nexport function PaymentRequiredPage() {\n  return <></>;\n}\n"
  },
  {
    "path": "web/motions/electron/index.tsx",
    "content": "import React from \"react\";\nimport { motion } from \"framer-motion\";\n\nconst LineAnimation = () => {\n  // Define the motion variant for the moving light effect\n  const motionVariant = {\n    animate: {\n      backgroundPosition: [\"0% 50%\", \"100% 50%\", \"0% 50%\"],\n      transition: {\n        duration: 1.2,\n        ease: \"linear\",\n        repeat: Infinity,\n      },\n    }\n  };\n\n  return (\n    <motion.div\n      variants={motionVariant}\n      initial=\"animate\"\n      animate=\"animate\"\n      style={{\n        height: \"2px\",\n        overflow: \"hidden\",\n        backgroundColor: \"transparent\",\n        position: \"relative\",\n        border: \"none\",\n        // The gradient color effect\n        backgroundImage: `linear-gradient(\n          to right,\n          transparent,\n          rgba(255, 255, 255, 0.75) 50%,\n          transparent 100%\n        )`,\n        backgroundSize: \"200% 100%\",\n      }}\n    />\n  );\n};\n\nexport default LineAnimation;\n"
  },
  {
    "path": "web/next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  transpilePackages: [\"@cors.sh/service-api\", \"@app/ui\", \"@editor-ui/console\"],\n  rewrites() {\n    return [\n      {\n        source: \"/playground\",\n        destination: \"https://playground.cors.sh\",\n      },\n      {\n        source: \"/playground/:path*\",\n        destination: \"https://playground.cors.sh/:path*\",\n      },\n      {\n        source: \"/docs\",\n        destination: \"https://docs.cors.sh/intro\",\n      },\n      {\n        source: \"/docs/:path*\",\n        destination: \"https://docs.cors.sh/:path*\",\n      },\n    ];\n  },\n  redirects() {\n    return [\n      {\n        source: \"/http\\\\://:path*\",\n        destination: \"https://proxy.cors.sh/http\\\\://:path*\",\n        basePath: false,\n        permanent: true,\n      },\n      {\n        source: \"/https\\\\://:path*\",\n        destination: \"https://proxy.cors.sh/https\\\\://:path*\",\n        basePath: false,\n        permanent: true,\n      },\n      {\n        // if pyament is canceled, go back to get started page.\n        source: \"/payments/canceled\",\n        destination: \"/get-started\",\n        permanent: true,\n      },\n      // {\n      //   source: \"/console\",\n      //   destination: \"https://console.grida.co/cors-proxy\",\n      //   permanent: true,\n      // },\n      {\n        source: \"/docs\",\n        destination: \"/docs/intro\",\n        permanent: true,\n      },\n    ];\n  },\n};\n\nmodule.exports = nextConfig;\n"
  },
  {
    "path": "web/package.json",
    "content": "{\n  \"name\": \"homepage\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev -p 8823\",\n    \"build\": \"next build\",\n    \"start\": \"next start -p 8823\",\n    \"lint\": \"next lint\"\n  },\n  \"dependencies\": {\n    \"@emotion/react\": \"^11.11.1\",\n    \"@emotion/styled\": \"^11.11.0\",\n    \"@radix-ui/react-collapsible\": \"^1.0.3\",\n    \"@radix-ui/react-icons\": \"^1.3.0\",\n    \"@radix-ui/themes\": \"^2.0.1\",\n    \"@use-gesture/react\": \"^10.3.0\",\n    \"add\": \"^2.0.6\",\n    \"copy-to-clipboard\": \"^3.3.3\",\n    \"framer-motion\": \"^10.16.4\",\n    \"next\": \"14.0.1\",\n    \"prismjs\": \"^1.29.0\",\n    \"react\": \"^18\",\n    \"react-dom\": \"^18\",\n    \"react-hot-toast\": \"^2.4.1\",\n    \"react-select\": \"^5.7.7\",\n    \"react-syntax-highlighter\": \"^15.5.0\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20\",\n    \"@types/react\": \"^18\",\n    \"@types/react-dom\": \"^18\",\n    \"@types/react-syntax-highlighter\": \"^15.5.9\",\n    \"autoprefixer\": \"^10.0.1\",\n    \"eslint\": \"^8\",\n    \"eslint-config-next\": \"14.0.1\",\n    \"postcss\": \"^8\",\n    \"tailwindcss\": \"^3.3.0\",\n    \"typescript\": \"^5\"\n  }\n}\n"
  },
  {
    "path": "web/pages/onboarding/complete.tsx",
    "content": "import React, { useEffect } from \"react\";\nimport styled from \"@emotion/styled\";\nimport { Client, ApplicationWithApiKey } from \"@cors.sh/service-api\";\nimport Head from \"next/head\";\nimport { FormPageLayout } from \"@app/ui/layouts\";\nimport { CollapsibleInfoCard } from \"@/components/collapsible-info-card\";\nimport { UnderlineButton } from \"@app/ui/components\";\nimport { Prism as SyntaxHighlighter } from \"react-syntax-highlighter\";\nimport { examples } from \"@/k\";\nimport { ApiKeyReveal } from \"@app/ui/components\";\nimport Link from \"next/link\";\n\nexport default function InitialOnboardingFinalPage({\n  application,\n}: {\n  application: ApplicationWithApiKey;\n}) {\n  const demo_target_url =\n    application.allowedOrigins[0] ?? \"https://example.com\";\n\n  return (\n    <>\n      <Head>\n        <title>CORS.SH - Complete</title>\n      </Head>\n      <FormPageLayout>\n        <>\n          <h1>\n            Extend your api call with <u>proxy.cors.sh</u>\n          </h1>\n          <p className=\"description\">\n            <small\n              style={{\n                opacity: 0.5,\n              }}\n            >\n              We&apos;ve sent you an email with the api key.\n              <br />\n              Please check your inbox :)\n            </small>\n            <br />\n            <br />\n            Let’s get rid of the cors errors with proxy.cors.sh like below.\n          </p>\n          <div style={{ height: 16 }} />\n          <div className=\"body\">\n            {/* <VideoDemo /> */}\n\n            <CodeExamples\n              apikey={application.apikey_test}\n              target={demo_target_url}\n            />\n            <ApiKeyReveal\n              keys={{\n                test: application.apikey_test,\n                prod: application.apikey_live,\n              }}\n            />\n            <div\n              style={{\n                display: \"flex\",\n                flexDirection: \"column\",\n                gap: 8,\n              }}\n            >\n              <CollapsibleInfoCard title=\"View more examples\">\n                <MoreCodeExamples\n                  apikey={application.apikey_test}\n                  target={demo_target_url}\n                />\n              </CollapsibleInfoCard>\n              <CollapsibleInfoCard title=\"What's Next?\">\n                <h5>Useful resources</h5>\n                <ul>\n                  <li>\n                    <a href=\"https://cors.sh/docs\">\n                      <u>Learn how to secure your api key</u>\n                    </a>\n                  </li>\n                  <li>\n                    <a href=\"https://cors.sh/docs\">\n                      <u>Before publishing your website to production</u>\n                    </a>\n                  </li>\n                  <li>\n                    <a href=\"https://cors.sh/docs\">\n                      <u>Create new application on console</u>\n                    </a>\n                  </li>\n                </ul>\n              </CollapsibleInfoCard>\n            </div>\n          </div>\n          <div style={{ height: 30 }} />\n          <div\n            style={{\n              display: \"flex\",\n              flexDirection: \"column\",\n              gap: 8,\n            }}\n          >\n            <i style={{ opacity: 0.5 }}>Thank you for using cors.sh 🙏</i>\n\n            {/* <UnderlineButton>Move to dashboard</UnderlineButton> */}\n            <Link href=\"/contact\">\n              <UnderlineButton>\n                I need help\n              </UnderlineButton>\n            </Link>\n          </div>\n        </>\n      </FormPageLayout>\n    </>\n  );\n}\n\nfunction CodeExamples({ target, apikey }: { target: string; apikey: string }) {\n  return (\n    <CodeBlock language=\"js\">{examples.simplest(target, apikey)}</CodeBlock>\n  );\n}\n\nfunction MoreCodeExamples({\n  target,\n  apikey,\n}: {\n  target: string;\n  apikey: string;\n}) {\n  return <CodeBlock language=\"js\">{examples.axios(target, apikey)}</CodeBlock>;\n}\n\nconst CodeBlock = styled(SyntaxHighlighter as any)`\n  max-height: 240px;\n  font-size: 12px !important;\n`;\n\n// <CodeBlock>\n//   <pre>\n//     GET https://proxy.corsh.sh/https://instragram.com/posts/123\n//     <br />\n//     -h x-cors-api-key {apikey}\n//   </pre>\n// </CodeBlock>\n// const CodeBlock = styled.code`\n//   background: black;\n//   color: white;\n//   border-radius: 4px;\n//   padding: 20px;\n//   display: block;\n//   font-size: 12px;\n//   line-height: 1.5;\n//   font-family: monospace;\n//   overflow: scroll;\n\n//   pre {\n//     margin: 0;\n//   }\n// `;\n\nfunction VideoDemo() {\n  return (\n    <div className=\"video-demo\">\n      <video\n        style={{\n          borderRadius: \"4px\",\n          overflow: \"hidden\",\n        }}\n        autoPlay\n        loop\n        muted\n        playsInline\n        width=\"100%\"\n        height=\"100%\"\n      >\n        <source\n          src=\"/console/demo-of-initial-api-key-configuration.mp4\"\n          type=\"video/mp4\"\n        />\n      </video>\n    </div>\n  );\n}\n\nexport async function getServerSideProps(context: any) {\n  const { app, checkout_session_id } = context.query;\n\n  if (!app) {\n    return {\n      redirect: {\n        destination: \"/\",\n        permanent: false,\n      },\n    };\n  }\n\n  try {\n    const client = new Client({\n      \"x-cors-service-checkout-session-id\": checkout_session_id,\n    });\n\n    const application = await client.getApplication(app);\n\n    return {\n      props: {\n        application,\n      },\n    };\n  } catch (e) {\n    console.error(e);\n    // 404\n    return {\n      notFound: true,\n    };\n  }\n}\n"
  },
  {
    "path": "web/pages/onboarding/payment-success-with-issue.tsx",
    "content": "import React from \"react\";\nimport type { Metadata } from 'next'\nimport client from \"@cors.sh/service-api\";\nimport { Button } from \"@editor-ui/console\";\nimport Link from \"next/link\";\n\nexport const metadata: Metadata = {\n  title: \"CORS.SH - Problem with your subscription\"\n}\n\nexport default function PaymentSuccessButThereWasAProblem({\n  error,\n  message,\n  session,\n  application,\n}: {\n  error: \"identity_conflict\" | string;\n  message: string;\n  session: string;\n  application: {\n    id: string;\n    name: string;\n  };\n}) {\n  return (\n    <main style={{\n      fontFamily: \"Inter, sans-serif\",\n    }}>\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          height: \"100vh\",\n          padding: 80,\n          textAlign: \"center\",\n        }}\n      >\n        <h1>There was a problem with your subscription - &quot;{error}&quot;</h1>\n\n        <div style={{ height: 40 }} />\n        <p>{message}</p>\n\n        <h5>Your Information</h5>\n        <p>Copy the data below when you contact customer support</p>\n        <div style={{ height: 40 }} />\n        <code>\n          <pre>session: {session}</pre>\n          <pre>\n            application: {application.id} ({application.name})\n          </pre>\n        </code>\n\n        <div style={{ height: 40 }} />\n        <Link href={`/contact`}>\n          <Button>Contact Support</Button>\n        </Link>\n      </div>\n    </main>\n  );\n}\n\nexport async function getServerSideProps(context: any) {\n  const { error, message, session_id, application_id, onboarding_id } =\n    context.query;\n  if (!session_id || !application_id || !onboarding_id) {\n    // invalid entry\n    return {\n      redirect: {\n        destination: \"/\",\n        permanent: false,\n      },\n    };\n  }\n\n  try {\n    const application = await client.getOnboardingApplication(onboarding_id);\n    return {\n      props: {\n        error,\n        message,\n        session: session_id || null,\n        application,\n      },\n    };\n  } catch (e) {\n    // 404\n    return {\n      notFound: true,\n    };\n  }\n}\n"
  },
  {
    "path": "web/pages/onboarding/payment-success.tsx",
    "content": "import React, { useEffect } from \"react\";\nimport client from \"@cors.sh/service-api\";\nimport { useRouter } from \"next/router\";\nimport { Button, TextFormField } from \"@editor-ui/console\";\nimport { FormPageLayout, PageCloseButton } from \"@app/ui/layouts\";\nimport { toast } from \"react-hot-toast\";\nimport type { Metadata } from 'next'\n\nexport const metadata: Metadata = {\n  title: \"CORS.SH - Complete\"\n}\n\n// page redirected from stripe once the payment is successful\nexport default function PaymentSuccessPage({\n  application,\n  session,\n  isOnboarding,\n}: {\n  session: string;\n  isOnboarding: boolean;\n  application: {\n    id: string;\n    name: string;\n    allowedOrigins: string[];\n  };\n}) {\n  const [isBusy, setBusy] = React.useState(false);\n  const router = useRouter();\n\n  useEffect(() => {\n    // GA4 conversion - Purchase\n    // @ts-ignore\n    window.gtag?.(\"event\", \"purchase\", {\n      transaction_id: session,\n      value: 4,\n      currency: \"USD\",\n      //\n    });\n  }, []);\n\n  const onNext = () => {\n    setBusy(true);\n    // convert to application.\n    client\n      .convertApplication(application.id, session)\n      .then((d) => {\n        // move to complete\n        router.push({\n          pathname: \"/onboarding/complete\",\n          query: {\n            app: d.id,\n            checkout_session_id: session,\n          },\n        });\n      })\n      .catch((e) => {\n        toast.error(\"Something went wrong. Please try again later.\");\n      })\n      .finally(() => {\n        setBusy(false);\n      });\n  };\n\n  return (\n    <>\n\n      <FormPageLayout>\n        <>\n          <h1>Thank you for your subscription</h1>\n          <p className=\"description\">\n            You can now create as many project you want without unlimited hourly\n            rate :)\n            <br />\n            <br />\n            <b>Let’s finish up your first project.</b>\n          </p>\n          <div className=\"form\">\n            <TextFormField\n              label=\"Application name\"\n              placeholder=\"my-portfolio-website\"\n              value={application.name}\n              readonly\n            />\n            <TextFormField\n              label=\"Application origin URL\"\n              placeholder=\"http://localhost:3000, https://my-site.com\"\n              readonly\n              value={application.allowedOrigins.join(\", \")}\n              helpText=\"You can add up to 3 urls of your site\"\n            />\n            <div style={{ height: 16 }} />\n            <Button onClick={onNext} height={\"32px\"} disabled={isBusy}>\n              Continue\n            </Button>\n          </div>\n        </>\n      </FormPageLayout>\n    </>\n  );\n}\n\nexport async function getServerSideProps(context: any) {\n  const { session_id, application_id, customer_id, onboarding_id } =\n    context.query;\n  if (!session_id || !onboarding_id) {\n    // invalid entry\n    return {\n      redirect: {\n        destination: \"/\",\n        permanent: false,\n      },\n    };\n  }\n\n  try {\n    const application = await client.getOnboardingApplication(onboarding_id);\n    return {\n      props: {\n        session: session_id || null,\n        application,\n        customer_id,\n      },\n    };\n  } catch (e) {\n    // 404\n    return {\n      notFound: true,\n    };\n  }\n}\n"
  },
  {
    "path": "web/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "web/public/robots.txt",
    "content": "# Block all crawlers for /console\nUser-agent: *\nDisallow: /console\n\n# Allow all crawlers\nUser-agent: *\nAllow: /"
  },
  {
    "path": "web/tailwind.config.ts",
    "content": "import type { Config } from 'tailwindcss'\n\nconst config: Config = {\n  content: [\n    './pages/**/*.{js,ts,jsx,tsx,mdx}',\n    './components/**/*.{js,ts,jsx,tsx,mdx}',\n    './app/**/*.{js,ts,jsx,tsx,mdx}',\n  ],\n  theme: {\n    extend: {\n      backgroundImage: {\n        'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',\n        'gradient-conic':\n          'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',\n      },\n    },\n  },\n  plugins: [],\n}\nexport default config\n"
  },
  {
    "path": "web/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "web/utils/email-validation.ts",
    "content": "const blacklist = [\n  \"nqmo.com\",\n  \"qabq.com\",\n  \"mailinator.com\",\n  \"10minutemail.com\",\n  \"guerrillamail.com\",\n  \"temp-mail.org\",\n  \"yopmail.com\",\n  \"throwawaymail.com\",\n  \"dispostable.com\",\n  \"getnada.com\",\n  \"maildrop.cc\",\n  \"pastryofistanbul.com\",\n  \"litepax.com\",\n  \"raleigh-construction.com\",\n  \"questtechsystems.com\",\n  \"teihu.com\",\n  \"oakon.com\",\n  \"namestal.com\"\n];\n\nconst pattern =\n  /^(([^<>()[\\]\\\\.,;:\\s@\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n\nexport const validateEmail = (email: string) => {\n  const emailLower = email.toLowerCase();\n  const domain = emailLower.split(\"@\")[1];\n\n  if (!pattern.test(emailLower)) return false;\n  if (blacklist.includes(domain)) return false;\n\n  return true;\n};\n"
  }
]