[
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n    \"env\": {\n        \"browser\": true,\n        \"es2020\": true\n    },\n    \"extends\": [\n        \"eslint:recommended\",\n        \"plugin:@typescript-eslint/recommended\"\n    ],\n    \"parser\": \"@typescript-eslint/parser\",\n    \"parserOptions\": {\n        \"ecmaVersion\": 11,\n        \"sourceType\": \"module\"\n    },\n    \"plugins\": [\n        \"@typescript-eslint\"\n    ],\n    \"rules\": {\n        \"indent\": [\n            \"error\",\n            2\n        ],\n        \"linebreak-style\": [\n            \"error\",\n            \"unix\"\n        ],\n        \"quotes\": [\n            \"error\",\n            \"double\"\n        ],\n        \"semi\": [\n            \"error\",\n            \"always\"\n        ]\n    }\n};\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [jOlv3r4] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".gitignore",
    "content": "#### joe made this: http://goel.io/joe\n\n#### linux ####\n*~\n\n# temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# Linux trash folder which might appear on any partition or disk\n.Trash-*\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n\n#### macos ####\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n\n#### node ####\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\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\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@types\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\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\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n\n# next.js build output\n.next\n\n# nuxt.js build output\n.nuxt\n\n# vuepress build output\n.vuepress/dist\n\n\n# Serverless directories\n.serverless\n\n\n#### vim ####\n# Swap\n[._]*.s[a-v][a-z]\n[._]*.sw[a-p]\n[._]s[a-rt-v][a-z]\n[._]ss[a-gi-z]\n[._]sw[a-p]\n\n# Session\nSession.vim\n\n# Temporary\n.netrwhist\n*~\n# Auto-generated tag files\ntags\n# Persistent undo\n[._]*.un~\n\n\n#### visualstudiocode ####\n.vscode/*\n\ndist\n.idea\n\n.now\nexample/.vercel\n\n#### yalc ####\nexample/.yalc\n**/.yalc/**/*.md\n"
  },
  {
    "path": ".npmignore",
    "content": "rollup.config.js\nsrc\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2022 Juan Olvera\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."
  },
  {
    "path": "README.md",
    "content": "# next-csrf\n\n![Discord](https://discord.com/api/guilds/967474884378763314/widget.png)\n\nCSRF mitigation for Next.js.\n\n## Features\n\nMitigation patterns that `next-csrf` implements:\n\n* [Synchronizer Token Pattern](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#synchronizer-token-pattern) using [`csrf`](https://github.com/pillarjs/csrf) (Also [read Understanding CSRF](https://github.com/pillarjs/understanding-csrf#csrf-tokens))\n\n## Installation\n\nWith yarn:\n\n```bash\nyarn add next-csrf\n```\n\nWith npm:\n\n```bash\nnpm i next-csrf --save\n```\n\n## Usage\n\nCreate an initialization file to add options:\n\n```js\n// file: lib/csrf.js\n\nimport { nextCsrf } from \"next-csrf\";\n\nconst { csrf, setup } = nextCsrf({\n    // eslint-disable-next-line no-undef\n    secret: process.env.CSRF_SECRET,\n});\n\nexport { csrf, setup };\n\n```\n\nProtect an API endpoint:\n\n```js\n// file: pages/api/protected.js\n\nimport { csrf } from '../lib/csrf';\n\nconst handler = (req, res) => {\n    return res.status(200).json({ message: \"This API route is protected.\"})\n}\n\nexport default csrf(handler);\n```\n\nTest the protected API route by sending a POST request from your terminal. Since this request doesn't have the proper token setup, it wil fail.\n\n```shell\ncurl -X POST http://localhost:3000/api/protected\n>> {\"message\": \"Invalid CSRF token\"}\n```\n\nUse an [SSG page](https://nextjs.org/docs/basic-features/pages#server-side-rendering) to set up the token. Usually, you use CSRF mitigation to harden your requests from authenticated users, if this is the case then you should use the login page.\n\n```js\n// file: pages/login.js\n\nimport { setup } from '../lib/csrf';\n\nfunction Login() {\n    const loginRequest = async (event) => {\n        event.preventDefault();\n        \n        // The secret and token are sent with the request by default, so no extra\n        // configuration is needed in the request.\n        const response = await fetch('/api/protected', {\n            method: 'post'\n        });\n        \n        if (response.ok) {\n            console.log('protected response ok');\n        }\n    }\n    \n    return (\n        <form onSubmit={loginRequest}>\n            <label>\n                Username\n                <input type=\"text\" required />\n            </label>\n            \n            <label>\n                Password\n                <input type=\"password\" required />\n            </label>\n            \n            <button>Submit</button>\n        </form>\n    )\n}\n\n// Here's the important part. `setup` saves the necesary secret and token.\nexport const getServerSideProps = setup(async ({req, res}) => {\n    return { props: {}}\n});\n\nexport default Login;\n```\n\n## API\n\n### `nextCsrf(options);`\n\nReturns two functions: \n\n* `setup` Setups two cookies, one for the secret and other one for the token. Only works on SSG pages.\n* `csrf` Protects API routes from requests without the token. Validates and verify signatures on the cookies.\n\n#### `options`\n\n* `tokenKey` (`string`) The name of the cookie to store the CSRF token. Default is `\"XSRF-TOKEN\"`.\n* `csrfErrorMessage` (`string`) Error message to return for unauthorized requests. Default is `\"Invalid CSRF token\"`.\n* `ignoredMethods`: (`string[]`) Methods to ignore, i.e. let pass all requests with these methods. Default is `[\"GET\", \"HEAD\", \"OPTIONS\"]`.\n* `cookieOptions`: Same options as https://www.npmjs.com/package/cookie\n"
  },
  {
    "path": "example/.gitignore",
    "content": ".vercel\n.yalc"
  },
  {
    "path": "example/components/layout.js",
    "content": "import Head from \"next/head\";\nimport Link from \"next/link\";\nimport styles from \"../styles/Home.module.css\";\n\nfunction Layout({ children }) {\n  return (\n    <div className={styles.container}>\n      <Head>\n        <title>Next CSRF</title>\n        <link rel=\"icon\" href=\"/favicon.ico\" />\n      </Head>\n\n      <main className={styles.main}>\n        <header>\n          <h1 className={styles.title}>Next CSRF</h1>\n\n          <nav className={styles.nav}>\n            <Link href=\"/\">Home</Link>\n            <Link href=\"login\">Login</Link>\n          </nav>\n        </header>\n\n        <div>{children}</div>\n      </main>\n\n      <footer className={styles.footer}>\n        <a\n          href=\"https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          Powered by{\" \"}\n          <img src=\"/vercel.svg\" alt=\"Vercel Logo\" className={styles.logo} />\n        </a>\n      </footer>\n    </div>\n  );\n}\n\nexport default Layout;\n"
  },
  {
    "path": "example/lib/csrf.js",
    "content": "import { nextCsrf } from \"next-csrf\";\n\nconst { csrf, setup } = nextCsrf({\n  // eslint-disable-next-line no-undef\n  secret: process.env.CSRF_SECRET,\n});\n\nexport { csrf, setup };\n"
  },
  {
    "path": "example/package.json",
    "content": "{\n  \"name\": \"next-csrf-example\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\"\n  },\n  \"dependencies\": {\n    \"next\": \"9.5.3\",\n    \"next-csrf\": \"file:.yalc/next-csrf\",\n    \"react\": \"16.13.1\",\n    \"react-dom\": \"16.13.1\"\n  },\n  \"devDependencies\": {\n    \"prettier\": \"^2.1.1\"\n  }\n}\n"
  },
  {
    "path": "example/pages/_app.js",
    "content": "import Layout from \"../components/layout\";\nimport \"../styles/globals.css\";\n\nfunction MyApp({ Component, pageProps }) {\n  return (\n    <Layout>\n      <Component {...pageProps} />\n    </Layout>\n  );\n}\n\nexport default MyApp;\n"
  },
  {
    "path": "example/pages/api/csrf/setup.js",
    "content": "import { setup } from \"../../../lib/csrf\";\n\nconst handler = (req, res) => {\n  res.statusCode = 200;\n  res.json({ message: \"CSRF token added to cookies\" });\n};\n\nexport default setup(handler);\n"
  },
  {
    "path": "example/pages/api/hello.js",
    "content": "// Next.js API route support: https://nextjs.org/docs/api-routes/introduction\n\nexport default (req, res) => {\n  res.statusCode = 200;\n  res.json({ name: \"John Doe\" });\n};\n"
  },
  {
    "path": "example/pages/api/protected.js",
    "content": "// Next.js API route support: https://nextjs.org/docs/api-routes/introduction\nimport { csrf } from \"../../lib/csrf\";\n\nconst handler = (req, res) => {\n  res.statusCode = 200;\n  res.json({ message: \"Request successful\" });\n};\n\nexport default csrf(handler);\n"
  },
  {
    "path": "example/pages/index.js",
    "content": "import styles from \"../styles/Home.module.css\";\nimport Link from \"next/link\";\n\nexport default function Home() {\n  return (\n    <>\n      <p className={styles.description}>\n        Get started by editing{\" \"}\n        <code className={styles.code}>pages/index.js</code>\n      </p>\n\n      <div>\n        <div className={styles.card}>\n          <h3>Send a request without the CSRF token</h3>\n\n          <p>\n            Because any request we send from the browser will have a cookie with\n            the token attached, try to send a request from a terminal and see\n            what happens with a missing or an invalid CSRF token.\n          </p>\n\n          <pre>\n            <code className={styles.code}>\n              $ curl -X POST http://localhost:3000/api/protected\n            </code>\n          </pre>\n\n          <pre>\n            <code className={styles.code}>\n              {`>> {\"message\": \"Invalid CSRF token\"}`}\n            </code>\n          </pre>\n        </div>\n\n        <div className={styles.card}>\n          <h3>Send a request with the proper CSRF token setup</h3>\n\n          <ol>\n            <li>\n              Go to the <Link href=\"login\">Login</Link> page\n            </li>\n            <li>Open the Console</li>\n            <li>\n              Fill the form with \"demo\" for username and \"demo\" for password\n            </li>\n            <li>Submit the form, inspect the Console and the Network</li>\n            <li>\n              Try to modify the cookie values, try again, and see the request\n              being blocked.\n            </li>\n          </ol>\n        </div>\n      </div>\n      <style jsx>{`\n        ol li {\n          margin-bottom: 0.8rem;\n        }\n      `}</style>\n    </>\n  );\n}\n"
  },
  {
    "path": "example/pages/login.js",
    "content": "import styles from \"../styles/Home.module.css\";\nimport { setup } from \"../lib/csrf\";\n\nfunction Login() {\n  const requestWithToken = async (event) => {\n    event.preventDefault();\n\n    const response = await fetch(\"/api/protected\", {\n      method: \"post\",\n    });\n\n    if (response.ok) {\n      console.log(\"protected response ok\");\n      console.log(response);\n    }\n  };\n\n  return (\n    <>\n      <p>Use \"demo\" for both username and password</p>\n      <form className={styles.form} onSubmit={requestWithToken}>\n        <label>\n          Username\n          <input type=\"text\" required />\n        </label>\n\n        <label htmlFor=\"password\">\n          Password\n          <input type=\"password\" required />\n        </label>\n\n        <button>Submit</button>\n      </form>\n    </>\n  );\n}\n\nexport const getServerSideProps = setup(async ({ req, res }) => {\n  return { props: {} };\n});\n\nexport default Login;\n"
  },
  {
    "path": "example/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}\n\n.form .info {\n  color: #333;\n}\n\n.form {\n  border: 1px solid #ccc;\n  padding: 1rem;\n}\n\n.form label {\n  padding: .5rem 0;\n  display: flex;\n  flex-direction: column;\n}\n\n.form input {\n  margin-top: .5rem;\n}\n\n.form button {\n  margin-top: .5rem;\n}\n\n.nav {\n  display: flex;\n  justify-content: center;\n  padding-top: 1rem;\n  padding-bottom: 1rem;\n  margin-top: 1rem;\n  margin-bottom: 1rem;\n}\n\n.nav a {\n  text-decoration: underline;\n  color: blue;\n  margin-right: .5rem;\n}\n\n.main {\n  max-width: 45rem;\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 img {\n  margin-left: 0.5rem;\n}\n\n.footer a {\n  display: flex;\n  justify-content: center;\n  align-items: center;\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\n  max-width: 800px;\n  margin-top: 3rem;\n}\n\n.button {\n  padding: .5rem 1rem;\n  border: 1px solid black;\n  background: black;\n  color: white;\n  font-size: 1.2rem;\n}\n\n.card {\n  margin: 1rem;\n  flex-basis: 45%;\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}\n\n.card:hover,\n.card:focus,\n.card:active {\n  color: #0070f3;\n  border-color: #0070f3;\n}\n\n.card h3 {\n  margin: 0 0 1rem 0;\n  font-size: 1.5rem;\n}\n\n.card p {\n  margin-top: 0;\n  font-size: 1.25rem;\n  line-height: 1.5;\n}\n\n.logo {\n  height: 1em;\n}\n\n@media (max-width: 600px) {\n  .grid {\n    width: 100%;\n    flex-direction: column;\n  }\n}\n"
  },
  {
    "path": "example/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": "jest.config.js",
    "content": "// For a detailed explanation regarding each configuration property, visit:\n// https://jestjs.io/docs/en/configuration.html\n\nmodule.exports = {\n    // All imported modules in your tests should be mocked automatically\n    // automock: false,\n\n    // Stop running tests after `n` failures\n    // bail: 0,\n\n    // Respect \"browser\" field in package.json when resolving modules\n    // browser: false,\n\n    // The directory where Jest should store its cached dependency information\n    // cacheDirectory: \"/tmp/jest_rs\",\n\n    // Automatically clear mock calls and instances between every test\n    clearMocks: true,\n\n    // Indicates whether the coverage information should be collected while executing the test\n    // collectCoverage: false,\n\n    // An array of glob patterns indicating a set of files for which coverage information should be collected\n    // collectCoverageFrom: undefined,\n\n    // The directory where Jest should output its coverage files\n    // coverageDirectory: undefined,\n\n    // An array of regexp pattern strings used to skip coverage collection\n    // coveragePathIgnorePatterns: [\n    //   \"/node_modules/\"\n    // ],\n\n    // A list of reporter names that Jest uses when writing coverage reports\n    // coverageReporters: [\n    //   \"json\",\n    //   \"text\",\n    //   \"lcov\",\n    //   \"clover\"\n    // ],\n\n    // An object that configures minimum threshold enforcement for coverage results\n    // coverageThreshold: undefined,\n\n    // A path to a custom dependency extractor\n    // dependencyExtractor: undefined,\n\n    // Make calling deprecated APIs throw helpful error messages\n    // errorOnDeprecated: false,\n\n    // Force coverage collection from ignored files using an array of glob patterns\n    // forceCoverageMatch: [],\n\n    // A path to a module which exports an async function that is triggered once before all test suites\n    // globalSetup: undefined,\n\n    // A path to a module which exports an async function that is triggered once after all test suites\n    // globalTeardown: undefined,\n\n    // A set of global variables that need to be available in all test environments\n    // globals: {},\n\n    // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.\n    // maxWorkers: \"50%\",\n\n    // An array of directory names to be searched recursively up from the requiring module's location\n    // moduleDirectories: [\n    //   \"node_modules\"\n    // ],\n\n    // An array of file extensions your modules use\n    moduleFileExtensions: [\n        \"js\",\n        // \"json\",\n        // \"jsx\",\n        \"ts\",\n        \"tsx\",\n        // \"node\"\n    ],\n\n    // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module\n    // moduleNameMapper: {},\n\n    // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader\n    // modulePathIgnorePatterns: [],\n\n    // Activates notifications for test results\n    // notify: false,\n\n    // An enum that specifies notification mode. Requires { notify: true }\n    // notifyMode: \"failure-change\",\n\n    // A preset that is used as a base for Jest's configuration\n    // preset: undefined,\n\n    // Run tests from one or more projects\n    // projects: undefined,\n\n    // Use this configuration option to add custom reporters to Jest\n    // reporters: undefined,\n\n    // Automatically reset mock state between every test\n    // resetMocks: false,\n\n    // Reset the module registry before running each individual test\n    // resetModules: false,\n\n    // A path to a custom resolver\n    // resolver: undefined,\n\n    // Automatically restore mock state between every test\n    // restoreMocks: false,\n\n    // The root directory that Jest should scan for tests and modules within\n    // rootDir: undefined,\n\n    // A list of paths to directories that Jest should use to search for files in\n    // roots: [\n    //   \"<rootDir>\"\n    // ],\n\n    // Allows you to use a custom runner instead of Jest's default test runner\n    // runner: \"jest-runner\",\n\n    // The paths to modules that run some code to configure or set up the testing environment before each test\n    // setupFiles: [],\n\n    // A list of paths to modules that run some code to configure or set up the testing framework before each test\n    // setupFilesAfterEnv: [],\n\n    // A list of paths to snapshot serializer modules Jest should use for snapshot testing\n    // snapshotSerializers: [],\n\n    // The test environment that will be used for testing\n    testEnvironment: \"node\",\n\n    // Options that will be passed to the testEnvironment\n    // testEnvironmentOptions: {},\n\n    // Adds a location field to test results\n    // testLocationInResults: false,\n\n    // The glob patterns Jest uses to detect test files\n    // testMatch: [\n    //   \"**/__tests__/**/*.[jt]s?(x)\",\n    //   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n    // ],\n\n    // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n    // testPathIgnorePatterns: [\n    //   \"/node_modules/\"\n    // ],\n\n    // The regexp pattern or array of patterns that Jest uses to detect test files\n    // testRegex: [],\n\n    // This option allows the use of a custom results processor\n    // testResultsProcessor: undefined,\n\n    // This option allows use of a custom test runner\n    // testRunner: \"jasmine2\",\n\n    // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href\n    // testURL: \"http://localhost\",\n\n    // Setting this value to \"fake\" allows the use of fake timers for functions such as \"setTimeout\"\n    // timers: \"real\",\n\n    // A map from regular expressions to paths to transformers\n    transform: {\n        \".(ts|tsx)\": \"ts-jest\",\n    },\n\n    // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n    // transformIgnorePatterns: [\n    //   \"/node_modules/\"\n    // ],\n\n    // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n    // unmockedModulePathPatterns: undefined,\n\n    // Indicates whether each individual test should be reported during the run\n    // verbose: undefined,\n\n    // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n    // watchPathIgnorePatterns: [],\n\n    // Whether to use watchman for file crawling\n    // watchman: true,\n};"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"next-csrf\",\n  \"version\": \"0.2.1\",\n  \"description\": \"CSRF mitigation library for Next.js\",\n  \"main\": \"dist/next-csrf.js\",\n  \"module\": \"dist/next-csrf.esm.js\",\n  \"types\": \"@types/\",\n  \"source\": \"src/index.ts\",\n  \"repository\": \"https://github.com/j0lv3r4/next-csrf\",\n  \"author\": \"Juan Olvera\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"build\": \"npm run types && rollup -c\",\n    \"dev\": \"rollup -w\",\n    \"test\": \"jest\",\n    \"test:e2e\": \"playwright test\",\n    \"types\": \"tsc --emitDeclarationOnly --declaration --outDir @types\",\n    \"release\": \"cross-var npm run build && cross-var git commit -am $npm_package_version && cross-var git tag $npm_package_version && git push && git push --tags && npm publish\"\n  },\n  \"devDependencies\": {\n    \"@playwright/test\": \"^1.20.0\",\n    \"@rollup/plugin-commonjs\": \"^13.0.2\",\n    \"@rollup/plugin-json\": \"^4.1.0\",\n    \"@rollup/plugin-node-resolve\": \"^8.4.0\",\n    \"@rollup/plugin-typescript\": \"^5.0.2\",\n    \"@types/cookie\": \"^0.4.0\",\n    \"@types/cookie-signature\": \"^1.0.3\",\n    \"@types/csrf\": \"^1.3.2\",\n    \"@types/jest\": \"^26.0.15\",\n    \"@types/supertest\": \"^2.0.10\",\n    \"@typescript-eslint/eslint-plugin\": \"^3.10.1\",\n    \"@typescript-eslint/parser\": \"^3.10.1\",\n    \"axios\": \"^0.26.1\",\n    \"eslint\": \"^7.12.1\",\n    \"eslint-config-airbnb-base\": \"^14.2.0\",\n    \"eslint-plugin-import\": \"^2.22.1\",\n    \"jest\": \"^26.6.1\",\n    \"next\": \"^9.5.5\",\n    \"np\": \"^6.5.0\",\n    \"prettier\": \"^2.1.2\",\n    \"rollup\": \"^2.32.1\",\n    \"rollup-plugin-analyzer\": \"^3.3.0\",\n    \"supertest\": \"^4.0.2\",\n    \"ts-jest\": \"^26.4.3\",\n    \"typescript\": \"^3.9.7\"\n  },\n  \"dependencies\": {\n    \"@types/http-errors\": \"^1.8.0\",\n    \"cookie\": \"^0.4.1\",\n    \"cookie-signature\": \"^1.1.0\",\n    \"csrf\": \"^3.1.0\",\n    \"querystring\": \"^0.2.0\"\n  }\n}\n"
  },
  {
    "path": "playwright.config.ts",
    "content": "import { PlaywrightTestConfig } from \"@playwright/test\";\n\nconst config: PlaywrightTestConfig = {\n  testMatch: \"**/*.e2e.ts\",\n  webServer: {\n    command: \"npm --prefix example run dev\",\n    port: 3000,\n    timeout: 120 * 1000,\n    reuseExistingServer: !process.env.CI,\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import resolve from \"@rollup/plugin-node-resolve\";\nimport commonjs from \"@rollup/plugin-commonjs\";\nimport typescript from \"@rollup/plugin-typescript\";\nimport analyze from \"rollup-plugin-analyzer\";\n\nimport pkg from \"./package.json\";\n\nexport default [\n  {\n    input: \"src/index.ts\",\n    output: [\n      { file: pkg.main, format: \"cjs\" },\n      { file: pkg.module, format: \"es\" },\n    ],\n    plugins: [typescript(), commonjs(), resolve(), analyze()],\n    external: [\"crypto\", \"util\"],\n  },\n];\n"
  },
  {
    "path": "src/index.e2e.ts",
    "content": "import { test, expect, request } from \"@playwright/test\";\nimport axios from \"axios\";\n\n// Go to login page\n// Check for secret and token\n// Submit the form\n// Expect 200\n\n// Go to login page\n// Check for secret and token\n// Modify token\n// Expect 403\n\n// Go to login page\n// Check for secret and token\n// Modify token\n// Expect 403\n\n// Send a GET request to /login\n// Grab the cookies\n// Send a POST request to /api/protected with the same cookies\n// Expect 200\n\n// Send a GET request to /login\n// Grab the cookies\n// Modify the cookies\n// Send a POST request to /api/protected with the same cookies\n// Expect 403\n\ntest(\"config is setup correctly in the example\", async ({ page, baseURL }) => {\n  if (baseURL != null) {\n    await page.goto(baseURL);\n\n    const [response] = await Promise.all([\n      page.waitForResponse((res) => res.status() === 200),\n    ]);\n\n    expect(response.status()).toBe(200);\n  }\n});\n"
  },
  {
    "path": "src/index.ts",
    "content": "import { NextApiHandler } from \"next\";\nimport { csrf, setup } from \"./middleware\";\nimport { NextCsrfOptions } from \"./types\";\nimport { CookieSerializeOptions } from \"cookie\";\n\nconst cookieDefaultOptions: CookieSerializeOptions = {\n  httpOnly: true,\n  path: \"/\",\n  sameSite: \"lax\",\n  secure: process.env.NODE_ENV === \"production\",\n};\n\nconst defaultOptions = {\n  tokenKey: \"XSRF-TOKEN\",\n  csrfErrorMessage: \"Invalid CSRF token\",\n  ignoredMethods: [\"GET\", \"HEAD\", \"OPTIONS\"],\n  cookieOptions: cookieDefaultOptions,\n};\n\ntype Middleware = (handler: NextApiHandler) => void;\n\ntype NextCSRF = {\n  setup: Middleware;\n  csrf: Middleware;\n};\n\nfunction nextCsrf(userOptions: NextCsrfOptions): NextCSRF {\n  const options = {\n    ...defaultOptions,\n    ...userOptions,\n  };\n\n  // generate middleware\n  return {\n    setup: (handler: NextApiHandler) =>\n      setup(handler, {\n        tokenKey: options.tokenKey,\n        cookieOptions: options.cookieOptions,\n        secret: options.secret,\n      }),\n    csrf: (handler: NextApiHandler) => csrf(handler, options),\n  };\n}\n\nexport { nextCsrf };\n"
  },
  {
    "path": "src/middleware/csrf.test.ts",
    "content": "import request from \"supertest\";\nimport http, { IncomingMessage, ServerResponse } from \"http\";\nimport { apiResolver } from \"next/dist/next-server/server/api-utils\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\nimport { nextCsrf } from \"../index\";\n\ndescribe(\"setup middleware\", () => {\n  const secret = \"yoMqR8xtQUhbmLwM*kRK\";\n  const tokenKey = \"XSRF-TOKEN\";\n  const userOptions = {\n    secret,\n    tokenKey,\n    cookieOptions: { httpOnly: true, path: \"/\" },\n  };\n\n  const apiPreviewPropsMock = {\n    previewModeId: \"id\",\n    previewModeEncryptionKey: \"key\",\n    previewModeSigningKey: \"key\",\n  };\n\n  const { setup } = nextCsrf(userOptions);\n\n  const requestListener = (req: IncomingMessage, res: ServerResponse) => {\n    apiResolver(\n      req,\n      res,\n      undefined,\n      setup((req: NextApiRequest, res: NextApiResponse) => {\n        return res.status(200).json({ message: \"Hello, world.\" });\n      }),\n      apiPreviewPropsMock,\n      true\n    );\n  };\n\n  it.only(\"Should setup cookies with csrfSecret and token\", async () => {\n    const server = http.createServer(requestListener);\n    const agent = await request.agent(server).post(\"/\");\n\n    expect(agent.header[\"set-cookie\"][0]).toEqual(\n      expect.stringMatching(/csrfSecret=(.+); Path=\\/; HttpOnly/g)\n    );\n\n    expect(agent.header[\"set-cookie\"][1]).toEqual(\n      expect.stringMatching(/XSRF-TOKEN=(.+); Path=\\/; HttpOnly/g)\n    );\n\n    expect(agent.text).toBe(JSON.stringify({ message: \"Hello, world.\" }));\n  });\n});\n\ndescribe(\"csrf middleware\", () => {\n  const secret = \"yoMqR8xtQUhbmLwM*kRK\";\n  const tokenKey = \"XSRF-TOKEN\";\n  const userOptions = {\n    secret,\n    tokenKey,\n    cookieOptions: { httpOnly: true, path: \"/\" },\n  };\n\n  // mock for `apiResolver`'s 5th parameter to please TS\n  const apiPreviewPropsMock = {\n    previewModeId: \"id\",\n    previewModeEncryptionKey: \"key\",\n    previewModeSigningKey: \"key\",\n  };\n\n  const { csrf } = nextCsrf(userOptions);\n\n  const requestListener = (req: IncomingMessage, res: ServerResponse) => {\n    apiResolver(\n      req,\n      res,\n      undefined,\n      csrf((req: NextApiRequest, res: NextApiResponse) => {\n        return res.status(200).json({ message: \"Hello, world.\" });\n      }),\n      apiPreviewPropsMock,\n      true\n    );\n  };\n\n  it(\"should setup a CSRF token on the first request, i.e. when there's no token already in a cookie\", async () => {\n    // If we receive a request without secret in a cookie we assume it's the first request to an API route\n\n    const server = http.createServer(requestListener);\n    const agent = await request.agent(server).post(\"/\");\n\n    console.log(agent.status);\n\n    expect(agent.header[\"set-cookie\"][0]).toEqual(\n      expect.stringMatching(/XSRF-TOKEN=(.+); Path=\\/; HttpOnly/g)\n    );\n\n    expect(agent.text).toBe(JSON.stringify({ message: \"Hello, world.\" }));\n  });\n\n  it(\"should validate a token with a secret and send 200 if everything is okay after the first request\", async () => {\n    const server = http.createServer(requestListener);\n    const firstRequest = await request.agent(server).get(\"/\");\n\n    // Grab the token and secret from the response\n    const [reqCsrfToken] = firstRequest.header[\"set-cookie\"];\n\n    // Send back the token in a header and a cookie\n    const secondRequest = await request\n      .agent(server)\n      .get(\"/\")\n      .set(\"Cookie\", reqCsrfToken)\n      .set(tokenKey, reqCsrfToken);\n\n    expect(secondRequest.status).toBe(200);\n  });\n\n  it(\"should validate a token with a secret and send 200 even if the cookie exists but is different from the XSRF token\", async () => {\n    const server = http.createServer(requestListener);\n    const firstRequest = await request.agent(server).get(\"/\");\n\n    // Grab the token and secret from the response\n    const [reqCsrfToken] = firstRequest.header[\"set-cookie\"];\n\n    // Send back the token in a header and a cookie\n    const secondRequest = await request\n      .agent(server)\n      .get(\"/\")\n      .set(\"Cookie\", \"anotherCookieThanXSRF\")\n      .set(tokenKey, reqCsrfToken);\n\n    expect(secondRequest.status).toBe(200);\n  });\n\n  it(\"should return 403 if we don't send a valid token in a custom header and a cookie\", async () => {\n    const server = http.createServer(requestListener);\n    const firstRequest = await request.agent(server).post(\"/\");\n\n    // Grab the token and secret from the response\n    const [reqCsrfToken] = firstRequest.header[\"set-cookie\"];\n\n    const [token, signature] = reqCsrfToken.split(\".\");\n    const tamperedToken = `${token}.invalidSignature`;\n\n    // Request without token in a custom header\n    const secondRequest = await request\n      .agent(server)\n      .post(\"/\")\n      .set(\"Cookie\", reqCsrfToken);\n\n    // Request with an invalid token in a custom header\n    const thirdRequest = await request\n      .agent(server)\n      .post(\"/\")\n      .set(\"Cookie\", reqCsrfToken)\n      .set(tokenKey, tamperedToken);\n\n    // Request with an invalid token in the cookie\n    const fourthRequest = await request\n      .agent(server)\n      .post(\"/\")\n      .set(\"Cookie\", tamperedToken)\n      .set(tokenKey, reqCsrfToken);\n\n    expect(secondRequest.status).toBe(403);\n    expect(thirdRequest.status).toBe(403);\n    expect(fourthRequest.status).toBe(403);\n  });\n});\n"
  },
  {
    "path": "src/middleware/csrf.ts",
    "content": "import { HttpError } from \"../utils\";\nimport { serialize, parse } from \"cookie\";\nimport { sign, unsign } from \"cookie-signature\";\nimport { createToken } from \"../utils/create-token\";\nimport { NextApiHandler, NextApiRequest, NextApiResponse } from \"next\";\nimport { MiddlewareArgs } from \"../types\";\n\nconst csrf = (\n  handler: NextApiHandler,\n  {\n    ignoredMethods,\n    csrfErrorMessage,\n    tokenKey,\n    cookieOptions,\n    secret,\n  }: MiddlewareArgs\n) => async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {\n  try {\n    if (typeof req.method !== \"string\") {\n      throw new HttpError(403, csrfErrorMessage);\n    }\n\n    // Do nothing on if method is in `ignoreMethods`\n    if (ignoredMethods.includes(req.method)) {\n      return handler(req, res);\n    }\n\n    // Fail if no cookie is present\n    if (req.headers?.cookie === undefined) {\n      throw new HttpError(403, csrfErrorMessage);\n    }\n\n    const cookie = parse(req.headers?.cookie);\n    // Extract secret and token from their cookies\n    let token = cookie[tokenKey];\n    const csrfSecret = cookie[\"csrfSecret\"];\n\n    // Check token is in the cookie\n    if (!token) {\n      throw new HttpError(403, csrfErrorMessage);\n    }\n\n    // If user provided a secret, then the cookie is signed.\n    // Unsign and verify aka Synchronizer token pattern.\n    // https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#synchronizer-token-pattern\n    if (secret != null) {\n      // unsign cookie\n      const unsignedToken = unsign(token, secret);\n\n      // validate signature\n      if (!unsignedToken) {\n        throw new HttpError(403, csrfErrorMessage);\n      }\n\n      token = unsignedToken;\n    }\n\n    // 5. verify CSRF token\n    if (!createToken.verify(csrfSecret, token)) {\n      throw new HttpError(403, csrfErrorMessage);\n    }\n\n    // If token is verified, generate a new one and save it in the cookie\n    let newToken;\n    if (secret != null) {\n      // Sign if `secret` is present\n      newToken = sign(createToken.create(csrfSecret), secret);\n    } else {\n      newToken = createToken.create(csrfSecret);\n    }\n\n    res.setHeader(\"Set-Cookie\", serialize(tokenKey, newToken, cookieOptions));\n\n    return handler(req, res);\n  } catch (error) {\n    return res.status(error.status ?? 500).json({ message: error.message });\n  }\n};\n\nexport { csrf };\n"
  },
  {
    "path": "src/middleware/index.ts",
    "content": "export * from \"./csrf\";\nexport * from \"./setup\";\n"
  },
  {
    "path": "src/middleware/setup.ts",
    "content": "import {\n  GetServerSidePropsContext,\n  NextApiHandler,\n  NextApiRequest,\n  NextApiResponse,\n} from \"next\";\nimport { SetupMiddlewareArgs } from \"../types\";\nimport { createToken } from \"../utils/create-token\";\nimport { sign } from \"cookie-signature\";\nimport { serialize } from \"cookie\";\nimport { getSecret } from \"../utils/get-secret\";\n\ntype SetupArgs =\n  | NextApiRequest[]\n  | NextApiResponse[]\n  | GetServerSidePropsContext[];\n\nconst setup = (\n  handler: NextApiHandler,\n  { secret, tokenKey, cookieOptions }: SetupMiddlewareArgs\n) => async (...args: SetupArgs): Promise<void> => {\n  const isApi = args.length > 1;\n\n  const req = isApi\n    ? (args[0] as NextApiRequest) // (*req*, res)\n    : (args[0] as GetServerSidePropsContext).req; // (context).req\n  const res = isApi\n    ? (args[1] as NextApiResponse) // (req, *res*)\n    : (args[0] as GetServerSidePropsContext).res; // (context).res\n\n  const csrfSecret = getSecret(req, \"csrfSecret\") || createToken.secretSync();\n  const unsignedToken = createToken.create(csrfSecret);\n\n  // TODO:\n  // Make a note that if the user changes the secret in the backend all the sessions\n  // will invalidate\n  let token;\n  if (secret != null) {\n    token = sign(unsignedToken, secret);\n  } else {\n    token = unsignedToken;\n  }\n\n  res.setHeader(\"Set-Cookie\", [\n    serialize(\"csrfSecret\", csrfSecret, cookieOptions),\n    serialize(tokenKey, token, cookieOptions),\n  ]);\n\n  return handler(req as NextApiRequest, res as NextApiResponse);\n};\n\nexport { setup };\n"
  },
  {
    "path": "src/package.json",
    "content": "{\n  \"name\": \"src\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n  }\n}\n"
  },
  {
    "path": "src/types.ts",
    "content": "import { CookieSerializeOptions } from \"cookie\";\n\ninterface NextCsrfOptions {\n  ignoredMethods?: string[];\n  csrfErrorMessage?: string;\n  tokenKey?: string;\n  cookieOptions?: CookieSerializeOptions;\n  secret?: string;\n}\n\n// Make the optional parameters in `nextCsrf` required in the `csrf` middleware\ninterface MiddlewareArgs extends NextCsrfOptions {\n  csrfErrorMessage: string;\n  ignoredMethods: string[];\n  tokenKey: string;\n  cookieOptions: CookieSerializeOptions;\n}\n\ninterface SetupMiddlewareArgs {\n  tokenKey: string;\n  cookieOptions: CookieSerializeOptions;\n  secret?: string;\n}\n\nexport { NextCsrfOptions, MiddlewareArgs, SetupMiddlewareArgs };\n"
  },
  {
    "path": "src/utils/create-token.ts",
    "content": "import Tokens from \"csrf\";\n\nconst createToken = new Tokens();\n\nexport { createToken };\n"
  },
  {
    "path": "src/utils/get-cookie.ts",
    "content": "import { parse } from \"cookie\";\nimport { NextApiRequest } from \"next\";\nimport { IncomingMessage } from \"http\";\n\nfunction getCookie(req: IncomingMessage, name: string): string {\n  if (req.headers.cookie != null) {\n    const parsedCookie = parse(req.headers.cookie);\n    return parsedCookie[name];\n  }\n\n  return \"\";\n}\n\nexport { getCookie };\n"
  },
  {
    "path": "src/utils/get-secret.ts",
    "content": "import { getCookie } from \"./get-cookie\";\nimport { IncomingMessage } from \"http\";\n\nconst getSecret = (req: IncomingMessage, tokenKey: string): string => {\n  return getCookie(req, tokenKey.toLowerCase());\n};\n\nexport { getSecret };\n"
  },
  {
    "path": "src/utils/httpError.ts",
    "content": "class HttpError extends Error {\n  private status: number;\n\n  constructor(status = 403, message: string, ...params: undefined[]) {\n    super(...params);\n\n    if (Error.captureStackTrace) {\n      Error.captureStackTrace(this, HttpError);\n    }\n\n    this.name = \"HttpError\";\n    this.status = status;\n    this.message = message;\n  }\n}\n\nexport { HttpError };\n"
  },
  {
    "path": "src/utils/index.ts",
    "content": "export * from \"./httpError\";\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    /* Visit https://aka.ms/tsconfig.json to read more about this file */\n\n    /* Basic Options */\n    // \"incremental\": true,                   /* Enable incremental compilation */\n    \"target\": \"es5\",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */\n    \"module\": \"es2015\",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */\n    // \"lib\": [],                             /* Specify library files to be included in the compilation. */\n    // \"allowJs\": true,                       /* Allow javascript files to be compiled. */\n    // \"checkJs\": true,                       /* Report errors in .js files. */\n    // \"jsx\": \"preserve\",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */\n    // \"declaration\": true,                   /* Generates corresponding '.d.ts' file. */\n    // \"declarationMap\": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */\n    // \"sourceMap\": true,                     /* Generates corresponding '.map' file. */\n    // \"outFile\": \"./\",                       /* Concatenate and emit output to single file. */\n    // \"outDir\": \"./\",                        /* Redirect output structure to the directory. */\n    // \"rootDir\": \"./\",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */\n    // \"composite\": true,                     /* Enable project compilation */\n    // \"tsBuildInfoFile\": \"./\",               /* Specify file to store incremental compilation information */\n    // \"removeComments\": true,                /* Do not emit comments to output. */\n    // \"noEmit\": true,                        /* Do not emit outputs. */\n    // \"importHelpers\": true,                 /* Import emit helpers from 'tslib'. */\n    // \"downlevelIteration\": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */\n    // \"isolatedModules\": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */\n\n    /* Strict Type-Checking Options */\n    \"strict\": true,                           /* Enable all strict type-checking options. */\n    // \"noImplicitAny\": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */\n    // \"strictNullChecks\": true,              /* Enable strict null checks. */\n    // \"strictFunctionTypes\": true,           /* Enable strict checking of function types. */\n    // \"strictBindCallApply\": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */\n    // \"strictPropertyInitialization\": true,  /* Enable strict checking of property initialization in classes. */\n    // \"noImplicitThis\": true,                /* Raise error on 'this' expressions with an implied 'any' type. */\n    // \"alwaysStrict\": true,                  /* Parse in strict mode and emit \"use strict\" for each source file. */\n\n    /* Additional Checks */\n    // \"noUnusedLocals\": true,                /* Report errors on unused locals. */\n    // \"noUnusedParameters\": true,            /* Report errors on unused parameters. */\n    // \"noImplicitReturns\": true,             /* Report error when not all code paths in function return a value. */\n    // \"noFallthroughCasesInSwitch\": true,    /* Report errors for fallthrough cases in switch statement. */\n\n    /* Module Resolution Options */\n    \"moduleResolution\": \"node\",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */\n    // \"baseUrl\": \"./\",                       /* Base directory to resolve non-absolute module names. */\n    // \"paths\": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */\n    // \"rootDirs\": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */\n    // \"typeRoots\": [],                       /* List of folders to include type definitions from. */\n    // \"types\": [],                           /* Type declaration files to be included in compilation. */\n    // \"allowSyntheticDefaultImports\": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */\n    \"esModuleInterop\": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */\n    // \"preserveSymlinks\": true,              /* Do not resolve the real path of symlinks. */\n    // \"allowUmdGlobalAccess\": true,          /* Allow accessing UMD globals from modules. */\n\n    /* Source Map Options */\n    // \"sourceRoot\": \"\",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */\n    // \"mapRoot\": \"\",                         /* Specify the location where debugger should locate map files instead of generated locations. */\n    // \"inlineSourceMap\": true,               /* Emit a single file with source maps instead of having a separate file. */\n    // \"inlineSources\": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */\n\n    /* Experimental Options */\n    // \"experimentalDecorators\": true,        /* Enables experimental support for ES7 decorators. */\n    // \"emitDecoratorMetadata\": true,         /* Enables experimental support for emitting type metadata for decorators. */\n\n    /* Advanced Options */\n    \"skipLibCheck\": true,                     /* Skip type checking of declaration files. */\n    \"forceConsistentCasingInFileNames\": true  /* Disallow inconsistently-cased references to the same file. */\n  },\n  \"include\": [\n    \"src\"\n  ]\n}"
  }
]