[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 2\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nquote_type = single\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\non:\n  - push\n  - pull_request\npermissions:\n  contents: read\njobs:\n  test:\n    name: Node.js ${{ matrix.node-version }}\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-version:\n          - 18\n          - '*'\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: ${{ matrix.node-version }}\n      - run: npm install\n      - run: npm test\n      - uses: codecov/codecov-action@v5\n        with:\n          name: Node.js ${{ matrix.node-version }}\n          token: ${{ secrets.CODECOV_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: 'CodeQL'\n\non:\n  push:\n    branches: ['master']\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: ['master']\n  schedule:\n    - cron: '0 0 * * 1'\n\npermissions:\n  contents: read\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v4\n        with:\n          languages: javascript\n          # If you wish to specify custom queries, you can do so here or in a config file.\n          # By default, queries listed here will override any specified in a config file.\n          # Prefix the list here with \"+\" to use these queries and those in the config file.\n\n      # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n      # If this step fails, then you should remove it and run the build manually (see below)\n      # - name: Autobuild\n      #   uses: github/codeql-action/autobuild@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13\n\n      # ℹ️ Command-line programs to run using the OS shell.\n      # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun\n\n      #   If the Autobuild fails above, remove it and uncomment the following three lines.\n      #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.\n\n      # - run: |\n      #   echo \"Run, Build Application using script\"\n      #   ./location_of_script_within_repo/buildscript.sh\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v4\n        with:\n          category: '/language:javascript'\n"
  },
  {
    "path": ".github/workflows/scorecard.yml",
    "content": "# This workflow uses actions that are not certified by GitHub. They are provided\n# by a third-party and are governed by separate terms of service, privacy\n# policy, and support documentation.\n\nname: Scorecard supply-chain security\non:\n  # For Branch-Protection check. Only the default branch is supported. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection\n  branch_protection_rule:\n  # To guarantee Maintained check is occasionally updated. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained\n  schedule:\n    - cron: '16 21 * * 1'\n  push:\n    branches: ['master']\n\n# Declare default permissions as read only.\npermissions: read-all\n\njobs:\n  analysis:\n    name: Scorecard analysis\n    runs-on: ubuntu-latest\n    permissions:\n      # Needed to upload the results to code-scanning dashboard.\n      security-events: write\n      # Needed to publish results and get a badge (see publish_results below).\n      id-token: write\n      # Uncomment the permissions below if installing in a private repository.\n      # contents: read\n      # actions: read\n\n    steps:\n      - name: 'Checkout code'\n        uses: actions/checkout@v6\n        with:\n          persist-credentials: false\n\n      - name: 'Run analysis'\n        uses: ossf/scorecard-action@v2.4.3\n        with:\n          results_file: results.sarif\n          results_format: sarif\n          # (Optional) \"write\" PAT token. Uncomment the `repo_token` line below if:\n          # - you want to enable the Branch-Protection check on a *public* repository, or\n          # - you are installing Scorecard on a *private* repository\n          # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.\n          # repo_token: ${{ secrets.SCORECARD_TOKEN }}\n\n          # Public repositories:\n          #   - Publish results to OpenSSF REST API for easy access by consumers\n          #   - Allows the repository to include the Scorecard badge.\n          #   - See https://github.com/ossf/scorecard-action#publishing-results.\n          # For private repositories:\n          #   - `publish_results` will always be set to `false`, regardless\n          #     of the value entered here.\n          publish_results: true\n\n      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF\n      # format to the repository Actions tab.\n      - name: 'Upload artifact'\n        uses: actions/upload-artifact@v6\n        with:\n          name: SARIF file\n          path: results.sarif\n          retention-days: 5\n\n      # Upload the results to GitHub's code scanning dashboard.\n      - name: 'Upload to code-scanning'\n        uses: github/codeql-action/upload-sarif@v4\n        with:\n          sarif_file: results.sarif\n"
  },
  {
    "path": ".gitignore",
    "content": "coverage/\nnode_modules/\nnpm-debug.log\npackage-lock.json\ndist/\n*.tsbuildinfo"
  },
  {
    "path": "LICENSE",
    "content": "(The MIT License)\n\nCopyright (c) 2014-2017 Douglas Christopher Wilson\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# content-disposition\n\n[![NPM Version][npm-image]][npm-url]\n[![NPM Downloads][downloads-image]][downloads-url]\n[![Node.js Version][node-version-image]][node-version-url]\n[![Build Status][github-actions-ci-image]][github-actions-ci-url]\n[![Test Coverage][coveralls-image]][coveralls-url]\n\nCreate and parse HTTP `Content-Disposition` header\n\n## Installation\n\n```sh\n$ npm install content-disposition\n```\n\n## API\n\n```js\nimport { create, parse } from 'content-disposition';\n```\n\n### create(filename, options)\n\nCreate an attachment `Content-Disposition` header value using the given file name,\nif supplied. The `filename` is optional and if no file name is desired, but you\nwant to specify `options`, set `filename` to `undefined`.\n\n```js\nres.setHeader('Content-Disposition', create('∫ maths.pdf'));\n```\n\n**note** HTTP headers are of the ISO-8859-1 character set. If you are writing this\nheader through a means different from `setHeader` in Node.js, you'll want to specify\nthe `'binary'` encoding in Node.js.\n\n#### Options\n\n`contentDisposition` accepts these properties in the options object.\n\n##### fallback\n\nIf the `filename` option is outside ISO-8859-1, then the file name is actually\nstored in a supplemental field for clients that support Unicode file names and\na ISO-8859-1 version of the file name is automatically generated.\n\nThis specifies the ISO-8859-1 file name to override the automatic generation or\ndisables the generation all together, defaults to `true`.\n\n- A string will specify the ISO-8859-1 file name to use in place of automatic\n  generation.\n- `false` will disable including a ISO-8859-1 file name and only include the\n  Unicode version (unless the file name is already ISO-8859-1).\n- `true` will enable automatic generation if the file name is outside ISO-8859-1.\n\nIf the `filename` option is ISO-8859-1 and this option is specified and has a\ndifferent value, then the `filename` option is encoded in the extended field\nand this set as the fallback field, even though they are both ISO-8859-1.\n\n##### type\n\nSpecifies the disposition type, defaults to `\"attachment\"`. This can also be\n`\"inline\"`, or any other value (all values except inline are treated like\n`attachment`, but can convey additional information if both parties agree to\nit). The type is normalized to lower-case.\n\n### parse(string)\n\n```js\nconst disposition = contentDisposition.parse(\n  'attachment; filename=\"EURO rates.txt\"; filename*=UTF-8\\'\\'%e2%82%ac%20rates.txt',\n);\n```\n\nParse a `Content-Disposition` header string. This automatically handles extended\n(\"Unicode\") parameters by decoding them and providing them under the standard\nparameter name. This will return an object with the following properties (examples\nare shown for the string `'attachment; filename=\"EURO rates.txt\"; filename*=UTF-8\\'\\'%e2%82%ac%20rates.txt'`):\n\n- `type`: The disposition type (always lower case). Example: `'attachment'`\n\n- `parameters`: An object of the parameters in the disposition (name of parameter\n  always lower case and extended versions replace non-extended versions). Example:\n  `{filename: \"€ rates.txt\"}`\n\n## Examples\n\n### Send a file for download\n\n```js\nconst contentDisposition = require('content-disposition');\nconst fs = require('fs');\nconst http = require('http');\nconst onFinished = require('on-finished');\n\nconst filePath = '/path/to/public/plans.pdf';\n\nhttp.createServer(function onRequest(req, res) {\n  // set headers\n  res.setHeader('Content-Type', 'application/pdf');\n  res.setHeader('Content-Disposition', contentDisposition(filePath));\n\n  // send file\n  const stream = fs.createReadStream(filePath);\n  stream.pipe(res);\n  onFinished(res, function () {\n    stream.destroy();\n  });\n});\n```\n\n## Testing\n\n```sh\n$ npm test\n```\n\n## References\n\n- [RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1][rfc-2616]\n- [RFC 5987: Character Set and Language Encoding for Hypertext Transfer Protocol (HTTP) Header Field Parameters][rfc-5987]\n- [RFC 6266: Use of the Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)][rfc-6266]\n- [Test Cases for HTTP Content-Disposition header field (RFC 6266) and the Encodings defined in RFCs 2047, 2231 and 5987][tc-2231]\n\n[rfc-2616]: https://datatracker.ietf.org/doc/html/rfc2616\n[rfc-5987]: https://datatracker.ietf.org/doc/html/rfc5987\n[rfc-6266]: https://datatracker.ietf.org/doc/html/rfc6266\n[tc-2231]: http://greenbytes.de/tech/tc2231/\n\n## License\n\n[MIT](LICENSE)\n\n[npm-image]: https://img.shields.io/npm/v/content-disposition\n[npm-url]: https://www.npmjs.com/package/content-disposition\n[node-version-image]: https://img.shields.io/node/v/content-disposition\n[node-version-url]: https://nodejs.org/en/download\n[coveralls-image]: https://img.shields.io/coverallsCoverage/github/jshttp/content-disposition\n[coveralls-url]: https://coveralls.io/github/jshttp/content-disposition?branch=master\n[downloads-image]: https://img.shields.io/npm/dm/content-disposition\n[downloads-url]: https://www.npmjs.com/package/content-disposition\n[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/jshttp/content-disposition/ci.yml\n[github-actions-ci-url]: https://github.com/jshttp/content-disposition/actions/workflows/ci.yml\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"content-disposition\",\n  \"version\": \"1.1.0\",\n  \"description\": \"Create and parse Content-Disposition header\",\n  \"keywords\": [\n    \"content-disposition\",\n    \"http\",\n    \"rfc6266\",\n    \"res\"\n  ],\n  \"repository\": \"jshttp/content-disposition\",\n  \"funding\": {\n    \"type\": \"opencollective\",\n    \"url\": \"https://opencollective.com/express\"\n  },\n  \"license\": \"MIT\",\n  \"author\": \"Douglas Christopher Wilson <doug@somethingdoug.com>\",\n  \"type\": \"commonjs\",\n  \"exports\": \"./dist/index.js\",\n  \"main\": \"./dist/index.js\",\n  \"types\": \"./dist/index.d.ts\",\n  \"files\": [\n    \"dist/\"\n  ],\n  \"scripts\": {\n    \"bench\": \"vitest bench\",\n    \"build\": \"ts-scripts build\",\n    \"format\": \"ts-scripts format\",\n    \"lint\": \"ts-scripts lint\",\n    \"prepare\": \"ts-scripts install && npm run build\",\n    \"specs\": \"ts-scripts specs\",\n    \"test\": \"ts-scripts test\"\n  },\n  \"devDependencies\": {\n    \"@borderless/ts-scripts\": \"^0.15.0\",\n    \"@vitest/coverage-v8\": \"^3.2.4\",\n    \"typescript\": \"^5.9.3\",\n    \"vitest\": \"^3.2.4\"\n  },\n  \"engines\": {\n    \"node\": \">=18\"\n  },\n  \"ts-scripts\": {\n    \"dist\": [\n      \"dist\"\n    ],\n    \"project\": [\n      \"tsconfig.build.json\"\n    ]\n  }\n}\n"
  },
  {
    "path": "src/create.spec.ts",
    "content": "import { describe, it, assert } from 'vitest';\nimport { create } from './index';\n\ndescribe('create()', function () {\n  it('should create an attachment header', function () {\n    assert.strictEqual(create(), 'attachment');\n  });\n});\n\ndescribe('create(filename)', function () {\n  it('should require a string', function () {\n    assert.throws(create.bind(null, 42 as any), /filename.*string/);\n  });\n\n  it('should create a header with file name', function () {\n    assert.strictEqual(create('plans.pdf'), 'attachment; filename=\"plans.pdf\"');\n  });\n\n  it('should use the basename of a posix path', function () {\n    assert.strictEqual(\n      create('/path/to/plans.pdf'),\n      'attachment; filename=\"plans.pdf\"',\n    );\n  });\n\n  it('should use the basename of a windows path', function () {\n    assert.strictEqual(\n      create('\\\\path\\\\to\\\\plans.pdf'),\n      'attachment; filename=\"plans.pdf\"',\n    );\n  });\n\n  it('should use the basename of a windows path with drive letter', function () {\n    assert.strictEqual(\n      create('C:\\\\path\\\\to\\\\plans.pdf'),\n      'attachment; filename=\"plans.pdf\"',\n    );\n  });\n\n  it('should use the basename of a posix path with trailing slash', function () {\n    assert.strictEqual(\n      create('/path/to/plans.pdf/'),\n      'attachment; filename=\"plans.pdf\"',\n    );\n  });\n\n  it('should use the basename of a windows path with trailing slash', function () {\n    assert.strictEqual(\n      create('\\\\path\\\\to\\\\plans.pdf\\\\'),\n      'attachment; filename=\"plans.pdf\"',\n    );\n  });\n\n  it('should use the basename of a windows path with drive letter and trailing slash', function () {\n    assert.strictEqual(\n      create('C:\\\\path\\\\to\\\\plans.pdf\\\\'),\n      'attachment; filename=\"plans.pdf\"',\n    );\n  });\n\n  it('should use the basename of a posix path with trailing slashes', function () {\n    assert.strictEqual(\n      create('/path/to/plans.pdf///'),\n      'attachment; filename=\"plans.pdf\"',\n    );\n  });\n\n  it('should use the basename of a windows path with trailing slashes', function () {\n    assert.strictEqual(\n      create('\\\\path\\\\to\\\\plans.pdf\\\\\\\\\\\\'),\n      'attachment; filename=\"plans.pdf\"',\n    );\n  });\n\n  it('should use the basename of a windows path with drive letter and trailing slashes', function () {\n    assert.strictEqual(\n      create('C:\\\\path\\\\to\\\\plans.pdf\\\\\\\\\\\\'),\n      'attachment; filename=\"plans.pdf\"',\n    );\n  });\n\n  describe('when \"filename\" is US-ASCII', function () {\n    it('should only include filename parameter', function () {\n      assert.strictEqual(\n        create('plans.pdf'),\n        'attachment; filename=\"plans.pdf\"',\n      );\n    });\n\n    it('should escape quotes', function () {\n      assert.strictEqual(\n        create('the \"plans\".pdf'),\n        'attachment; filename=\"the \\\\\"plans\\\\\".pdf\"',\n      );\n    });\n  });\n\n  describe('when \"filename\" is ISO-8859-1', function () {\n    it('should only include filename parameter', function () {\n      assert.strictEqual(\n        create('«plans».pdf'),\n        'attachment; filename=\"«plans».pdf\"',\n      );\n    });\n\n    it('should escape quotes', function () {\n      assert.strictEqual(\n        create('the \"plans\" (1µ).pdf'),\n        'attachment; filename=\"the \\\\\"plans\\\\\" (1µ).pdf\"',\n      );\n    });\n  });\n\n  describe('when \"filename\" is Unicode', function () {\n    it('should include filename* parameter', function () {\n      assert.strictEqual(\n        create('планы.pdf'),\n        'attachment; filename=\"?????.pdf\"; filename*=UTF-8\\'\\'%D0%BF%D0%BB%D0%B0%D0%BD%D1%8B.pdf',\n      );\n    });\n\n    it('should include filename fallback', function () {\n      assert.strictEqual(\n        create('£ and € rates.pdf'),\n        'attachment; filename=\"£ and ? rates.pdf\"; filename*=UTF-8\\'\\'%C2%A3%20and%20%E2%82%AC%20rates.pdf',\n      );\n      assert.strictEqual(\n        create('€ rates.pdf'),\n        'attachment; filename=\"? rates.pdf\"; filename*=UTF-8\\'\\'%E2%82%AC%20rates.pdf',\n      );\n    });\n\n    it('should encode special characters', function () {\n      assert.strictEqual(\n        create(\"€'*%().pdf\"),\n        \"attachment; filename=\\\"?'*%().pdf\\\"; filename*=UTF-8''%E2%82%AC%27%2A%25%28%29.pdf\",\n      );\n    });\n  });\n\n  describe('when \"filename\" contains hex escape', function () {\n    it('should include filename* parameter', function () {\n      assert.strictEqual(\n        create('the%20plans.pdf'),\n        'attachment; filename=\"the%20plans.pdf\"; filename*=UTF-8\\'\\'the%2520plans.pdf',\n      );\n    });\n\n    it('should handle Unicode', function () {\n      assert.strictEqual(\n        create('€%20£.pdf'),\n        'attachment; filename=\"?%20£.pdf\"; filename*=UTF-8\\'\\'%E2%82%AC%2520%C2%A3.pdf',\n      );\n    });\n  });\n});\n\ndescribe('create(filename, options)', function () {\n  describe('with \"fallback\" option', function () {\n    it('should require a string or Boolean', function () {\n      assert.throws(\n        create.bind(null, 'plans.pdf', { fallback: 42 } as any),\n        /fallback.*string/,\n      );\n    });\n\n    it('should default to true', function () {\n      assert.strictEqual(\n        create('€ rates.pdf'),\n        'attachment; filename=\"? rates.pdf\"; filename*=UTF-8\\'\\'%E2%82%AC%20rates.pdf',\n      );\n    });\n\n    describe('when \"false\"', function () {\n      it('should not generate ISO-8859-1 fallback', function () {\n        assert.strictEqual(\n          create('£ and € rates.pdf', { fallback: false }),\n          \"attachment; filename*=UTF-8''%C2%A3%20and%20%E2%82%AC%20rates.pdf\",\n        );\n      });\n\n      it('should keep ISO-8859-1 filename', function () {\n        assert.strictEqual(\n          create('£ rates.pdf', { fallback: false }),\n          'attachment; filename=\"£ rates.pdf\"',\n        );\n      });\n    });\n\n    describe('when \"true\"', function () {\n      it('should generate ISO-8859-1 fallback', function () {\n        assert.strictEqual(\n          create('£ and € rates.pdf', { fallback: true }),\n          'attachment; filename=\"£ and ? rates.pdf\"; filename*=UTF-8\\'\\'%C2%A3%20and%20%E2%82%AC%20rates.pdf',\n        );\n      });\n\n      it('should pass through ISO-8859-1 filename', function () {\n        assert.strictEqual(\n          create('£ rates.pdf', { fallback: true }),\n          'attachment; filename=\"£ rates.pdf\"',\n        );\n      });\n    });\n\n    describe('when a string', function () {\n      it('should require an ISO-8859-1 string', function () {\n        assert.throws(\n          create.bind(null, '€ rates.pdf', {\n            fallback: '€ rates.pdf',\n          }),\n          /fallback.*iso-8859-1/i,\n        );\n      });\n\n      it('should use as ISO-8859-1 fallback', function () {\n        assert.strictEqual(\n          create('£ and € rates.pdf', {\n            fallback: '£ and EURO rates.pdf',\n          }),\n          'attachment; filename=\"£ and EURO rates.pdf\"; filename*=UTF-8\\'\\'%C2%A3%20and%20%E2%82%AC%20rates.pdf',\n        );\n      });\n\n      it('should use as fallback even when filename is ISO-8859-1', function () {\n        assert.strictEqual(\n          create('\"£ rates\".pdf', { fallback: '£ rates.pdf' }),\n          'attachment; filename=\"£ rates.pdf\"; filename*=UTF-8\\'\\'%22%C2%A3%20rates%22.pdf',\n        );\n      });\n\n      it('should do nothing if equal to filename', function () {\n        assert.strictEqual(\n          create('plans.pdf', { fallback: 'plans.pdf' }),\n          'attachment; filename=\"plans.pdf\"',\n        );\n      });\n\n      it('should use the basename of a posix path', function () {\n        assert.strictEqual(\n          create('€ rates.pdf', {\n            fallback: '/path/to/EURO rates.pdf',\n          }),\n          'attachment; filename=\"EURO rates.pdf\"; filename*=UTF-8\\'\\'%E2%82%AC%20rates.pdf',\n        );\n      });\n\n      it('should use the basename of a windows path', function () {\n        assert.strictEqual(\n          create('€ rates.pdf', {\n            fallback: '\\\\path\\\\to\\\\EURO rates.pdf',\n          }),\n          'attachment; filename=\"EURO rates.pdf\"; filename*=UTF-8\\'\\'%E2%82%AC%20rates.pdf',\n        );\n      });\n\n      it('should use the basename of a windows path with drive letter', function () {\n        assert.strictEqual(\n          create('€ rates.pdf', {\n            fallback: 'C:\\\\path\\\\to\\\\EURO rates.pdf',\n          }),\n          'attachment; filename=\"EURO rates.pdf\"; filename*=UTF-8\\'\\'%E2%82%AC%20rates.pdf',\n        );\n      });\n\n      it('should do nothing without filename option', function () {\n        assert.strictEqual(\n          create(undefined, { fallback: 'plans.pdf' }),\n          'attachment',\n        );\n      });\n    });\n  });\n\n  describe('with \"type\" option', function () {\n    it('should default to attachment', function () {\n      assert.strictEqual(create(), 'attachment');\n    });\n\n    it('should require a string', function () {\n      assert.throws(\n        create.bind(null, undefined, { type: 42 } as any),\n        /invalid type/,\n      );\n    });\n\n    it('should require a valid type', function () {\n      assert.throws(\n        create.bind(null, undefined, { type: 'invalid;type' }),\n        /invalid type/,\n      );\n    });\n\n    it('should create a header with inline type', function () {\n      assert.strictEqual(create(undefined, { type: 'inline' }), 'inline');\n    });\n\n    it('should create a header with inline type & filename', function () {\n      assert.strictEqual(\n        create('plans.pdf', { type: 'inline' }),\n        'inline; filename=\"plans.pdf\"',\n      );\n    });\n\n    it('should normalize type', function () {\n      assert.strictEqual(create(undefined, { type: 'INLINE' }), 'inline');\n    });\n  });\n});\n"
  },
  {
    "path": "src/index.bench.ts",
    "content": "import { bench, describe } from 'vitest';\nimport { create, parse } from './index.js';\n\ndescribe('create', () => {\n  bench('create()', () => {\n    create();\n  });\n\n  bench('create(filename)', () => {\n    create('plans.pdf');\n  });\n});\n\ndescribe('parse', () => {\n  bench('parse(header)', () => {\n    parse('attachment; filename=\"plans.pdf\"');\n  });\n\n  bench('parse(header) with UTF-8 extended parameter', () => {\n    parse(\"attachment; filename*=UTF-8''%E2%82%AC%20rates.pdf\");\n  });\n});\n"
  },
  {
    "path": "src/index.ts",
    "content": "/*!\n * content-disposition\n * Copyright(c) 2014-2017 Douglas Christopher Wilson\n * MIT Licensed\n */\n\nexport interface ContentDisposition {\n  /**\n   * Content-Disposition type, such as \"attachment\" or \"inline\"\n   */\n  type: string;\n  /**\n   * Content-Disposition parameters, such as \"filename\"\n   */\n  parameters: Record<string, string>;\n}\n\nexport interface CreateOptions {\n  /**\n   * Content-Disposition type, defaults to \"attachment\"\n   * @default \"attachment\"\n   */\n  type?: string;\n  /**\n   * Fallback filename for non-ISO-8859-1 strings. If true, a fallback will be generated by replacing non-latin1 characters with \"?\". If false, no fallback will be generated.\n   * @default true\n   */\n  fallback?: string | boolean;\n}\n\n/**\n * Null object perf optimization. Faster than `Object.create(null)` and `{ __proto__: null }`.\n */\nconst NullObject = /* @__PURE__ */ (() => {\n  const C = function () {};\n  C.prototype = Object.create(null);\n  return C;\n})() as unknown as { new (): any };\n\n/**\n * Create an attachment Content-Disposition header.\n */\nexport function create(filename?: string, options?: CreateOptions): string {\n  const type = options?.type || 'attachment';\n  const parameters = createparams(filename, options?.fallback);\n\n  return format({ type, parameters });\n}\n\nconst SP = 32; // \" \"\nconst HTAB = 9; // \"\\t\"\nconst SEMI = 59; // \";\"\nconst EQ = 61; // \"=\"\nconst DQUOTE = 34; // '\"'\nconst BSLASH = 92; // \"\\\\\"\n\n/**\n * Parse Content-Disposition header string.\n */\nexport function parse(header: string): ContentDisposition {\n  const len = header.length;\n  let index = skipOWS(header, 0, header.length);\n\n  const typeStart = index;\n  index = parseToken(header, index, len);\n  const typeEnd = trailingOWS(header, typeStart, index);\n  const type = header.slice(typeStart, typeEnd).toLowerCase();\n\n  const parameters: Record<string, string> = new NullObject();\n\n  parameter: while (index < len) {\n    index = skipOWS(header, index + 1, len); // Skip over semicolon.\n\n    const keyStart = index;\n\n    while (index < len) {\n      const char = header.charCodeAt(index);\n      if (char === SEMI) continue parameter;\n\n      if (char === EQ) {\n        const keyEnd = trailingOWS(header, keyStart, index);\n        const key = header.slice(keyStart, keyEnd).toLowerCase();\n\n        index = skipOWS(header, index + 1, len);\n\n        if (index < len && header.charCodeAt(index) === DQUOTE) {\n          index++;\n\n          let value = '';\n          while (index < len) {\n            const code = header.charCodeAt(index++);\n            if (code === DQUOTE) {\n              index = parseToken(header, index, len);\n              if (parameters[key] === undefined) parameters[key] = value;\n              continue parameter;\n            }\n\n            if (code === BSLASH && index < len) {\n              value += header[index++];\n              continue;\n            }\n\n            value += String.fromCharCode(code);\n          }\n        }\n\n        const valueStart = index;\n        index = parseToken(header, index, len);\n        const valueEnd = trailingOWS(header, valueStart, index);\n        const value = header.slice(valueStart, valueEnd);\n\n        if (key.charCodeAt(key.length - 1) === 42 /* \"*\" */) {\n          const normalizedKey = key.slice(0, -1);\n          const decoded = decodeRFC8187(value);\n          if (decoded !== undefined) {\n            parameters[normalizedKey] = decoded;\n            continue parameter;\n          }\n        }\n\n        if (parameters[key] === undefined) parameters[key] = value;\n        continue parameter;\n      }\n\n      index++;\n    }\n  }\n\n  return { type, parameters };\n}\n\n/**\n * RegExp to match non attr-char, *after* encodeURIComponent (i.e. not including \"%\")\n */\nconst ENCODE_URL_ATTR_CHAR_REGEXP = /[\\x00-\\x20\"'()*,/:;<=>?@[\\\\\\]{}\\x7f]/g; // eslint-disable-line no-control-regex\n\n/**\n * RegExp to match non-latin1 characters.\n */\nconst NON_LATIN1_REGEXP = /[^\\x20-\\x7e\\xa0-\\xff]/g;\n\n/**\n * RegExp to match chars that must be quoted-pair in RFC 2616\n */\nconst QUOTE_REGEXP = /([\\\\\"])/g;\n\n/**\n * RegExp for various RFC 2616 grammar\n *\n * parameter     = token \"=\" ( token | quoted-string )\n * token         = 1*<any CHAR except CTLs or separators>\n * separators    = \"(\" | \")\" | \"<\" | \">\" | \"@\"\n *               | \",\" | \";\" | \":\" | \"\\\" | <\">\n *               | \"/\" | \"[\" | \"]\" | \"?\" | \"=\"\n *               | \"{\" | \"}\" | SP | HT\n * quoted-string = ( <\"> *(qdtext | quoted-pair ) <\"> )\n * qdtext        = <any TEXT except <\">>\n * quoted-pair   = \"\\\" CHAR\n * CHAR          = <any US-ASCII character (octets 0 - 127)>\n * TEXT          = <any OCTET except CTLs, but including LWS>\n * LWS           = [CRLF] 1*( SP | HT )\n * CRLF          = CR LF\n * CR            = <US-ASCII CR, carriage return (13)>\n * LF            = <US-ASCII LF, linefeed (10)>\n * SP            = <US-ASCII SP, space (32)>\n * HT            = <US-ASCII HT, horizontal-tab (9)>\n * CTL           = <any US-ASCII control character (octets 0 - 31) and DEL (127)>\n * OCTET         = <any 8-bit sequence of data>\n */\nconst TEXT_REGEXP = /^[\\x20-\\x7e\\x80-\\xff]+$/;\nconst TOKEN_REGEXP = /^[!#$%&'*+.0-9A-Z^_`a-z|~-]+$/;\n\n/**\n * Create parameters object from filename and fallback.\n */\nfunction createparams(\n  filename?: string,\n  fallback: string | boolean = true,\n): Record<string, string> | undefined {\n  if (filename === undefined) {\n    return;\n  }\n\n  if (typeof filename !== 'string') {\n    throw new TypeError('filename must be a string');\n  }\n\n  if (typeof fallback !== 'string' && typeof fallback !== 'boolean') {\n    throw new TypeError('fallback must be a string or boolean');\n  }\n\n  if (typeof fallback === 'string' && NON_LATIN1_REGEXP.test(fallback)) {\n    throw new TypeError('fallback must be ISO-8859-1 string');\n  }\n\n  const params: Record<string, string> = new NullObject();\n\n  // restrict to file base name\n  const name = basename(filename);\n\n  // determine if name is suitable for quoted string\n  const isQuotedString = TEXT_REGEXP.test(name);\n\n  // generate fallback name\n  const fallbackName =\n    typeof fallback !== 'string'\n      ? fallback && getlatin1(name)\n      : basename(fallback);\n  const hasFallback = typeof fallbackName === 'string' && fallbackName !== name;\n\n  // set extended filename parameter\n  if (hasFallback || !isQuotedString || hasHexEscape(name)) {\n    params['filename*'] = name;\n  }\n\n  // set filename parameter\n  if (isQuotedString || hasFallback) {\n    params.filename = hasFallback ? fallbackName : name;\n  }\n\n  return params;\n}\n\n/**\n * Decode a RFC 8187 field value (gracefully).\n */\nfunction decodeRFC8187(str: string): string | undefined {\n  const charsetEnd = str.indexOf(\"'\");\n  if (charsetEnd <= 0) {\n    return undefined;\n  }\n\n  const languageEnd = str.indexOf(\"'\", charsetEnd + 1);\n  if (languageEnd === -1) {\n    return undefined;\n  }\n\n  const charset = str.slice(0, charsetEnd).toLowerCase();\n  const encoded = str.slice(languageEnd + 1);\n\n  switch (charset) {\n    case 'iso-8859-1': {\n      return decodeHexEscapes(encoded);\n    }\n    case 'utf-8':\n    case 'utf8': {\n      return tryDecodeURIComponent(encoded);\n    }\n  }\n\n  return undefined;\n}\n\n/**\n * Decode URI component but return `undefined` on error.\n */\nfunction tryDecodeURIComponent(str: string): string | undefined {\n  try {\n    return decodeURIComponent(str);\n  } catch {\n    return undefined;\n  }\n}\n\n/**\n * Parse a token starting at the provided index.\n */\nfunction parseToken(str: string, index: number, len: number): number {\n  while (index < len) {\n    const char = str.charCodeAt(index);\n    if (char === SEMI) break;\n    index++;\n  }\n  return index;\n}\n\n/**\n * Skip RFC 2616 linear whitespace (space / tab).\n */\nfunction skipOWS(str: string, index: number, len: number): number {\n  while (index < len) {\n    const char = str.charCodeAt(index);\n    if (char !== SP && char !== HTAB) break;\n    index++;\n  }\n  return index;\n}\n\n/**\n * Skip RFC 2616 linear whitespace (space / tab) from the end of a string.\n */\nfunction trailingOWS(str: string, start: number, end: number): number {\n  while (end > start) {\n    const char = str.charCodeAt(end - 1);\n    if (char !== SP && char !== HTAB) break;\n    end--;\n  }\n  return end;\n}\n\n/**\n * Format object to Content-Disposition header.\n */\nfunction format(obj: Partial<ContentDisposition>): string {\n  if (!obj || typeof obj !== 'object') {\n    throw new TypeError('argument obj is required');\n  }\n\n  if (\n    !obj.type ||\n    typeof obj.type !== 'string' ||\n    !TOKEN_REGEXP.test(obj.type)\n  ) {\n    throw new TypeError('invalid type');\n  }\n\n  // start with normalized type\n  let string = obj.type.toLowerCase();\n\n  // append parameters\n  if (obj.parameters && typeof obj.parameters === 'object') {\n    const params = Object.keys(obj.parameters).sort();\n\n    for (let i = 0; i < params.length; i++) {\n      const param = params[i];\n\n      const val =\n        param.slice(-1) === '*'\n          ? ustring(obj.parameters[param])\n          : qstring(obj.parameters[param]);\n\n      string += `; ${param}=${val}`;\n    }\n  }\n\n  return string;\n}\n\n/**\n * Get ISO-8859-1 version of string.\n */\nfunction getlatin1(val: string): string {\n  // simple Unicode -> ISO-8859-1 transformation\n  return val.replace(NON_LATIN1_REGEXP, '?');\n}\n\n/**\n * Percent encode a single character.\n */\nfunction pencode(char: string): string {\n  return '%' + char.charCodeAt(0).toString(16).toUpperCase();\n}\n\n/**\n * Quote a string for HTTP.\n */\nfunction qstring(val: unknown): string {\n  const str = String(val);\n\n  return '\"' + str.replace(QUOTE_REGEXP, '\\\\$1') + '\"';\n}\n\n/**\n * Encode a Unicode string for HTTP (RFC 5987).\n */\nfunction ustring(val: unknown): string {\n  const str = String(val);\n\n  // percent encode as UTF-8\n  const encoded = encodeURIComponent(str).replace(\n    ENCODE_URL_ATTR_CHAR_REGEXP,\n    pencode,\n  );\n\n  return \"UTF-8''\" + encoded;\n}\n\n/**\n * Return the last portion of a path\n */\nfunction basename(path: string): string {\n  const normalized = path.replaceAll('\\\\', '/');\n\n  let end = normalized.length;\n  while (end > 0 && normalized[end - 1] === '/') {\n    end--;\n  }\n\n  if (end === 0) {\n    return '';\n  }\n\n  let start = end - 1;\n  while (start >= 0 && normalized[start] !== '/') {\n    start--;\n  }\n\n  return normalized.slice(start + 1, end);\n}\n\n/**\n * Check if a character is a hex digit [0-9A-Fa-f]\n */\nfunction isHexDigit(char: string): boolean {\n  const code = char.charCodeAt(0);\n  return (\n    (code >= 48 && code <= 57) || // 0-9\n    (code >= 65 && code <= 70) || // A-F\n    (code >= 97 && code <= 102) // a-f\n  );\n}\n\n/**\n * Check if a string contains percent encoding escapes.\n */\nfunction hasHexEscape(str: string): boolean {\n  const maxIndex = str.length - 3;\n  let lastIndex = -1;\n\n  while (\n    (lastIndex = str.indexOf('%', lastIndex + 1)) !== -1 &&\n    lastIndex <= maxIndex\n  ) {\n    if (isHexDigit(str[lastIndex + 1]) && isHexDigit(str[lastIndex + 2])) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\n/**\n * Decode hex escapes in a string (e.g., %20 -> space)\n */\nfunction decodeHexEscapes(str: string): string {\n  const firstEscape = str.indexOf('%');\n  if (firstEscape === -1) return str;\n\n  let result = str.slice(0, firstEscape);\n  for (let idx = firstEscape; idx < str.length; idx++) {\n    if (\n      str[idx] === '%' &&\n      idx + 2 < str.length &&\n      isHexDigit(str[idx + 1]) &&\n      isHexDigit(str[idx + 2])\n    ) {\n      result += String.fromCharCode(\n        Number.parseInt(str[idx + 1] + str[idx + 2], 16),\n      );\n      idx += 2;\n    } else {\n      result += str[idx];\n    }\n  }\n  return result;\n}\n"
  },
  {
    "path": "src/parse.spec.ts",
    "content": "import { describe, it, assert } from 'vitest';\nimport { parse } from './index';\n\ndescribe('parse(string)', function () {\n  describe('with only type', function () {\n    it('should parse quoted value leniently', function () {\n      assert.deepEqual(parse('\"attachment\"'), {\n        type: '\"attachment\"',\n        parameters: {},\n      });\n    });\n\n    it('should ignore trailing semicolon', function () {\n      assert.deepEqual(parse('attachment;'), {\n        type: 'attachment',\n        parameters: {},\n      });\n    });\n\n    it('should parse \"attachment\"', function () {\n      assert.deepEqual(parse('attachment'), {\n        type: 'attachment',\n        parameters: {},\n      });\n    });\n\n    it('should parse \"inline\"', function () {\n      assert.deepEqual(parse('inline'), {\n        type: 'inline',\n        parameters: {},\n      });\n    });\n\n    it('should parse \"form-data\"', function () {\n      assert.deepEqual(parse('form-data'), {\n        type: 'form-data',\n        parameters: {},\n      });\n    });\n\n    it('should parse with trailing LWS', function () {\n      assert.deepEqual(parse('attachment \\t '), {\n        type: 'attachment',\n        parameters: {},\n      });\n    });\n\n    it('should normalize to lower-case', function () {\n      assert.deepEqual(parse('ATTACHMENT'), {\n        type: 'attachment',\n        parameters: {},\n      });\n    });\n  });\n\n  describe('with parameters', function () {\n    it('should ignore trailing semicolon', function () {\n      assert.deepEqual(parse('attachment; filename=\"rates.pdf\";'), {\n        type: 'attachment',\n        parameters: { filename: 'rates.pdf' },\n      });\n    });\n\n    it('should preserve invalid parameter name', function () {\n      assert.deepEqual(parse('attachment; filename@=\"rates.pdf\"'), {\n        type: 'attachment',\n        parameters: { 'filename@': 'rates.pdf' },\n      });\n    });\n\n    it('should treat missing parameter value as empty', function () {\n      assert.deepEqual(parse('attachment; filename='), {\n        type: 'attachment',\n        parameters: { filename: '' },\n      });\n    });\n\n    it('should preserve invalid parameter value', function () {\n      assert.deepEqual(parse('attachment; filename=trolly,trains'), {\n        type: 'attachment',\n        parameters: { filename: 'trolly,trains' },\n      });\n    });\n\n    it('should preserve otherwise invalid parameters', function () {\n      assert.deepEqual(parse('attachment; filename=total/; foo=bar'), {\n        type: 'attachment',\n        parameters: { filename: 'total/', foo: 'bar' },\n      });\n    });\n\n    it('should keep the first duplicate parameter', function () {\n      assert.deepEqual(parse('attachment; filename=foo; filename=bar'), {\n        type: 'attachment',\n        parameters: { filename: 'foo' },\n      });\n    });\n\n    it('should parse missing type leniently', function () {\n      assert.deepEqual(parse('filename=\"plans.pdf\"'), {\n        type: 'filename=\"plans.pdf\"',\n        parameters: {},\n      });\n      assert.deepEqual(parse('; filename=\"plans.pdf\"'), {\n        type: '',\n        parameters: { filename: 'plans.pdf' },\n      });\n    });\n\n    it('should lower-case parameter name', function () {\n      assert.deepEqual(parse('attachment; FILENAME=\"plans.pdf\"'), {\n        type: 'attachment',\n        parameters: { filename: 'plans.pdf' },\n      });\n    });\n\n    it('should parse quoted parameter value', function () {\n      assert.deepEqual(parse('attachment; filename=\"plans.pdf\"'), {\n        type: 'attachment',\n        parameters: { filename: 'plans.pdf' },\n      });\n    });\n\n    it('should parse & unescape quoted value', function () {\n      assert.deepEqual(parse('attachment; filename=\"the \\\\\"plans\\\\\".pdf\"'), {\n        type: 'attachment',\n        parameters: { filename: 'the \"plans\".pdf' },\n      });\n    });\n\n    it('should include all parameters', function () {\n      assert.deepEqual(parse('attachment; filename=\"plans.pdf\"; foo=bar'), {\n        type: 'attachment',\n        parameters: { filename: 'plans.pdf', foo: 'bar' },\n      });\n    });\n\n    it('should parse parameters separated with any LWS', function () {\n      assert.deepEqual(\n        parse('attachment;filename=\"plans.pdf\" \\t;    \\t\\t foo=bar'),\n        {\n          type: 'attachment',\n          parameters: { filename: 'plans.pdf', foo: 'bar' },\n        },\n      );\n    });\n\n    it('should parse token filename', function () {\n      assert.deepEqual(parse('attachment; filename=plans.pdf'), {\n        type: 'attachment',\n        parameters: { filename: 'plans.pdf' },\n      });\n    });\n\n    it('should parse ISO-8859-1 filename', function () {\n      assert.deepEqual(parse('attachment; filename=\"£ rates.pdf\"'), {\n        type: 'attachment',\n        parameters: { filename: '£ rates.pdf' },\n      });\n    });\n  });\n\n  describe('with extended parameters', function () {\n    it('should preserve quoted extended parameter value', function () {\n      assert.deepEqual(\n        parse('attachment; filename*=\"UTF-8\\'\\'%E2%82%AC%20rates.pdf\"'),\n        {\n          type: 'attachment',\n          parameters: { 'filename*': \"UTF-8''%E2%82%AC%20rates.pdf\" },\n        },\n      );\n    });\n\n    it('should parse UTF-8 extended parameter value', function () {\n      assert.deepEqual(\n        parse(\"attachment; filename*=UTF-8''%E2%82%AC%20rates.pdf\"),\n        {\n          type: 'attachment',\n          parameters: { filename: '€ rates.pdf' },\n        },\n      );\n    });\n\n    it('should parse UTF8 extended parameter value', function () {\n      assert.deepEqual(\n        parse(\"attachment; filename*=utf8''%E2%82%AC%20rates.pdf\"),\n        {\n          type: 'attachment',\n          parameters: { filename: '€ rates.pdf' },\n        },\n      );\n    });\n\n    it('should parse UTF-8 extended parameter value', function () {\n      assert.deepEqual(\n        parse(\"attachment; filename*=UTF-8''%E2%82%AC%20rates.pdf\"),\n        {\n          type: 'attachment',\n          parameters: { filename: '€ rates.pdf' },\n        },\n      );\n    });\n\n    it('should ignore invalid percent-encodings in UTF-8 extended parameter value', function () {\n      assert.deepEqual(parse(\"attachment; filename*=UTF-8''%E4%20rates.pdf\"), {\n        type: 'attachment',\n        parameters: { 'filename*': \"UTF-8''%E4%20rates.pdf\" },\n      });\n    });\n\n    it('should parse ISO-8859-1 extended parameter value', function () {\n      assert.deepEqual(\n        parse(\"attachment; filename*=ISO-8859-1''%A3%20rates.pdf\"),\n        {\n          type: 'attachment',\n          parameters: { filename: '£ rates.pdf' },\n        },\n      );\n    });\n\n    it('should retain invalid latin1 bytes in ISO-8859-1 extended parameter value', function () {\n      assert.deepEqual(\n        parse(\"attachment; filename*=ISO-8859-1''%82%20rates.pdf\"),\n        {\n          type: 'attachment',\n          parameters: { filename: '\\x82 rates.pdf' },\n        },\n      );\n    });\n\n    it('should not be case-sensitive for charset', function () {\n      assert.deepEqual(\n        parse(\"attachment; filename*=utf-8''%E2%82%AC%20rates.pdf\"),\n        {\n          type: 'attachment',\n          parameters: { filename: '€ rates.pdf' },\n        },\n      );\n    });\n\n    it('should preserve unsupported charset as the original parameter', function () {\n      assert.deepEqual(\n        parse(\"attachment; filename*=ISO-8859-2''%A4%20rates.pdf\"),\n        {\n          type: 'attachment',\n          parameters: { 'filename*': \"ISO-8859-2''%A4%20rates.pdf\" },\n        },\n      );\n    });\n\n    it('should parse with embedded language', function () {\n      assert.deepEqual(\n        parse(\"attachment; filename*=UTF-8'en'%E2%82%AC%20rates.pdf\"),\n        {\n          type: 'attachment',\n          parameters: { filename: '€ rates.pdf' },\n        },\n      );\n    });\n\n    it('should prefer extended parameter value', function () {\n      assert.deepEqual(\n        parse(\n          'attachment; filename=\"EURO rates.pdf\"; filename*=UTF-8\\'\\'%E2%82%AC%20rates.pdf',\n        ),\n        {\n          type: 'attachment',\n          parameters: { filename: '€ rates.pdf' },\n        },\n      );\n      assert.deepEqual(\n        parse(\n          'attachment; filename*=UTF-8\\'\\'%E2%82%AC%20rates.pdf; filename=\"EURO rates.pdf\"',\n        ),\n        {\n          type: 'attachment',\n          parameters: { filename: '€ rates.pdf' },\n        },\n      );\n    });\n\n    it('should keep fallback filename when extended parameter cannot be decoded', function () {\n      assert.deepEqual(\n        parse(\n          'attachment; filename=\"EURO rates.pdf\"; filename*=ISO-8859-2\\'\\'%A4%20rates.pdf',\n        ),\n        {\n          type: 'attachment',\n          parameters: {\n            filename: 'EURO rates.pdf',\n            'filename*': \"ISO-8859-2''%A4%20rates.pdf\",\n          },\n        },\n      );\n    });\n  });\n\n  describe('from TC 2231', function () {\n    describe('Disposition-Type Inline', function () {\n      it('should parse \"inline\"', function () {\n        assert.deepEqual(parse('inline'), {\n          type: 'inline',\n          parameters: {},\n        });\n      });\n\n      it('should parse \"\"inline\"\" leniently', function () {\n        assert.deepEqual(parse('\"inline\"'), {\n          type: '\"inline\"',\n          parameters: {},\n        });\n      });\n\n      it('should parse \"inline; filename=\"foo.html\"\"', function () {\n        assert.deepEqual(parse('inline; filename=\"foo.html\"'), {\n          type: 'inline',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should parse \"inline; filename=\"Not an attachment!\"\"', function () {\n        assert.deepEqual(parse('inline; filename=\"Not an attachment!\"'), {\n          type: 'inline',\n          parameters: { filename: 'Not an attachment!' },\n        });\n      });\n\n      it('should parse \"inline; filename=\"foo.pdf\"\"', function () {\n        assert.deepEqual(parse('inline; filename=\"foo.pdf\"'), {\n          type: 'inline',\n          parameters: { filename: 'foo.pdf' },\n        });\n      });\n    });\n\n    describe('Disposition-Type Attachment', function () {\n      it('should parse \"attachment\"', function () {\n        assert.deepEqual(parse('attachment'), {\n          type: 'attachment',\n          parameters: {},\n        });\n      });\n\n      it('should parse \"\"attachment\"\" leniently', function () {\n        assert.deepEqual(parse('\"attachment\"'), {\n          type: '\"attachment\"',\n          parameters: {},\n        });\n      });\n\n      it('should parse \"ATTACHMENT\"', function () {\n        assert.deepEqual(parse('ATTACHMENT'), {\n          type: 'attachment',\n          parameters: {},\n        });\n      });\n\n      it('should parse \"attachment; filename=\"foo.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename=\"foo.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"0000000000111111111122222\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename=\"0000000000111111111122222\"'),\n          {\n            type: 'attachment',\n            parameters: { filename: '0000000000111111111122222' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename=\"00000000001111111111222222222233333\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename=\"00000000001111111111222222222233333\"'),\n          {\n            type: 'attachment',\n            parameters: { filename: '00000000001111111111222222222233333' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename=\"f\\\\oo.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename=\"f\\\\oo.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"\\\\\"quoting\\\\\" tested.html\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename=\"\\\\\"quoting\\\\\" tested.html\"'),\n          {\n            type: 'attachment',\n            parameters: { filename: '\"quoting\" tested.html' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename=\"Here\\'s a semicolon;.html\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename=\"Here\\'s a semicolon;.html\"'),\n          {\n            type: 'attachment',\n            parameters: { filename: \"Here's a semicolon;.html\" },\n          },\n        );\n      });\n\n      it('should parse \"attachment; foo=\"bar\"; filename=\"foo.html\"\"', function () {\n        assert.deepEqual(parse('attachment; foo=\"bar\"; filename=\"foo.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'foo.html', foo: 'bar' },\n        });\n      });\n\n      it('should parse \"attachment; foo=\"\\\\\"\\\\\\\\\";filename=\"foo.html\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; foo=\"\\\\\"\\\\\\\\\";filename=\"foo.html\"'),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo.html', foo: '\"\\\\' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; FILENAME=\"foo.html\"\"', function () {\n        assert.deepEqual(parse('attachment; FILENAME=\"foo.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=foo.html\"', function () {\n        assert.deepEqual(parse('attachment; filename=foo.html'), {\n          type: 'attachment',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should preserve commas in token values', function () {\n        assert.deepEqual(parse('attachment; filename=foo,bar.html'), {\n          type: 'attachment',\n          parameters: { filename: 'foo,bar.html' },\n        });\n      });\n\n      it('should ignore trailing semicolon after value', function () {\n        assert.deepEqual(parse('attachment; filename=foo.html ;'), {\n          type: 'attachment',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should skip empty parameter slots', function () {\n        assert.deepEqual(parse('attachment; ;filename=foo'), {\n          type: 'attachment',\n          parameters: { filename: 'foo' },\n        });\n      });\n\n      it('should preserve spaces in token values', function () {\n        assert.deepEqual(parse('attachment; filename=foo bar.html'), {\n          type: 'attachment',\n          parameters: { filename: 'foo bar.html' },\n        });\n      });\n\n      it(\"should parse \\\"attachment; filename='foo.bar'\", function () {\n        assert.deepEqual(parse(\"attachment; filename='foo.bar'\"), {\n          type: 'attachment',\n          parameters: { filename: \"'foo.bar'\" },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"foo-ä.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename=\"foo-ä.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'foo-ä.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"foo-Ã¤.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename=\"foo-Ã¤.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'foo-Ã¤.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"foo-%41.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename=\"foo-%41.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'foo-%41.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"50%.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename=\"50%.html\"'), {\n          type: 'attachment',\n          parameters: { filename: '50%.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"foo-%\\\\41.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename=\"foo-%\\\\41.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'foo-%41.html' },\n        });\n      });\n\n      it('should parse \"attachment; name=\"foo-%41.html\"\"', function () {\n        assert.deepEqual(parse('attachment; name=\"foo-%41.html\"'), {\n          type: 'attachment',\n          parameters: { name: 'foo-%41.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"ä-%41.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename=\"ä-%41.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'ä-%41.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"foo-%c3%a4-%e2%82%ac.html\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename=\"foo-%c3%a4-%e2%82%ac.html\"'),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo-%c3%a4-%e2%82%ac.html' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename =\"foo.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename =\"foo.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should keep the first duplicate quoted filename', function () {\n        assert.deepEqual(\n          parse('attachment; filename=\"foo.html\"; filename=\"bar.html\"'),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo.html' },\n          },\n        );\n      });\n\n      it('should preserve bracket characters in token values', function () {\n        assert.deepEqual(parse('attachment; filename=foo[1](2).html'), {\n          type: 'attachment',\n          parameters: { filename: 'foo[1](2).html' },\n        });\n      });\n\n      it('should preserve latin1 token values', function () {\n        assert.deepEqual(parse('attachment; filename=foo-ä.html'), {\n          type: 'attachment',\n          parameters: { filename: 'foo-ä.html' },\n        });\n      });\n\n      it('should preserve mojibake token values', function () {\n        assert.deepEqual(parse('attachment; filename=foo-Ã¤.html'), {\n          type: 'attachment',\n          parameters: { filename: 'foo-Ã¤.html' },\n        });\n      });\n\n      it('should treat a bare parameter as the type', function () {\n        assert.deepEqual(parse('filename=foo.html'), {\n          type: 'filename=foo.html',\n          parameters: {},\n        });\n      });\n\n      it('should preserve invalid type token with parameters', function () {\n        assert.deepEqual(parse('x=y; filename=foo.html'), {\n          type: 'x=y',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should stop at the first recoverable parameter after a quoted type', function () {\n        assert.deepEqual(parse('\"foo; filename=bar;baz\"; filename=qux'), {\n          type: '\"foo',\n          parameters: { filename: 'bar' },\n        });\n      });\n\n      it('should preserve commas in a malformed type token', function () {\n        assert.deepEqual(parse('filename=foo.html, filename=bar.html'), {\n          type: 'filename=foo.html, filename=bar.html',\n          parameters: {},\n        });\n      });\n\n      it('should allow an empty type when parameters follow', function () {\n        assert.deepEqual(parse('; filename=foo.html'), {\n          type: '',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should preserve leading punctuation in the type', function () {\n        assert.deepEqual(parse(': inline; attachment; filename=foo.html'), {\n          type: ': inline',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should skip bare parameters without values', function () {\n        assert.deepEqual(parse('inline; attachment; filename=foo.html'), {\n          type: 'inline',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should skip bare attachment parameters without values', function () {\n        assert.deepEqual(parse('attachment; inline; filename=foo.html'), {\n          type: 'attachment',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should ignore a suffix after a quoted filename', function () {\n        assert.deepEqual(parse('attachment; filename=\"foo.html\".txt'), {\n          type: 'attachment',\n          parameters: { filename: 'foo.html' },\n        });\n      });\n\n      it('should treat an unterminated quoted filename as empty', function () {\n        assert.deepEqual(parse('attachment; filename=\"bar'), {\n          type: 'attachment',\n          parameters: { filename: '' },\n        });\n      });\n\n      it('should stop a token value at the next semicolon', function () {\n        assert.deepEqual(parse('attachment; filename=foo\"bar;baz\"qux'), {\n          type: 'attachment',\n          parameters: { filename: 'foo\"bar' },\n        });\n      });\n\n      it('should preserve a comma-separated header fragment in the first value', function () {\n        assert.deepEqual(\n          parse('attachment; filename=foo.html, attachment; filename=bar.html'),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo.html, attachment' },\n          },\n        );\n      });\n\n      it('should keep an unseparated parameter assignment inside the value', function () {\n        assert.deepEqual(parse('attachment; foo=foo filename=bar'), {\n          type: 'attachment',\n          parameters: { foo: 'foo filename=bar' },\n        });\n      });\n\n      it('should keep trailing assignments inside the filename value', function () {\n        assert.deepEqual(parse('attachment; filename=bar foo=foo'), {\n          type: 'attachment',\n          parameters: { filename: 'bar foo=foo' },\n        });\n      });\n\n      it('should treat missing semicolon after the type as part of the type', function () {\n        assert.deepEqual(parse('attachment filename=bar'), {\n          type: 'attachment filename=bar',\n          parameters: {},\n        });\n      });\n\n      it('should keep the first malformed type segment', function () {\n        assert.deepEqual(parse('filename=foo.html; attachment'), {\n          type: 'filename=foo.html',\n          parameters: {},\n        });\n      });\n\n      it('should parse \"attachment; xfilename=foo.html\"', function () {\n        assert.deepEqual(parse('attachment; xfilename=foo.html'), {\n          type: 'attachment',\n          parameters: { xfilename: 'foo.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"/foo.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename=\"/foo.html\"'), {\n          type: 'attachment',\n          parameters: { filename: '/foo.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename=\"\\\\\\\\foo.html\"\"', function () {\n        assert.deepEqual(parse('attachment; filename=\"\\\\\\\\foo.html\"'), {\n          type: 'attachment',\n          parameters: { filename: '\\\\foo.html' },\n        });\n      });\n    });\n\n    describe('Additional Parameters', function () {\n      it('should parse \"attachment; creation-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; creation-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"'),\n          {\n            type: 'attachment',\n            parameters: { 'creation-date': 'Wed, 12 Feb 1997 16:29:51 -0500' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"\"', function () {\n        assert.deepEqual(\n          parse(\n            'attachment; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"',\n          ),\n          {\n            type: 'attachment',\n            parameters: {\n              'modification-date': 'Wed, 12 Feb 1997 16:29:51 -0500',\n            },\n          },\n        );\n      });\n    });\n\n    describe('Disposition-Type Extension', function () {\n      it('should parse \"foobar\"', function () {\n        assert.deepEqual(parse('foobar'), {\n          type: 'foobar',\n          parameters: {},\n        });\n      });\n\n      it('should parse \"attachment; example=\"filename=example.txt\"\"', function () {\n        assert.deepEqual(parse('attachment; example=\"filename=example.txt\"'), {\n          type: 'attachment',\n          parameters: { example: 'filename=example.txt' },\n        });\n      });\n    });\n\n    describe('RFC 2231/5987 Encoding: Character Sets', function () {\n      it('should parse \"attachment; filename*=iso-8859-1\\'\\'foo-%E4.html\"', function () {\n        assert.deepEqual(\n          parse(\"attachment; filename*=iso-8859-1''foo-%E4.html\"),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo-ä.html' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename*=UTF-8\\'\\'foo-%c3%a4-%e2%82%ac.html\"', function () {\n        assert.deepEqual(\n          parse(\"attachment; filename*=UTF-8''foo-%c3%a4-%e2%82%ac.html\"),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo-ä-€.html' },\n          },\n        );\n      });\n\n      it('should preserve extended values without a charset', function () {\n        assert.deepEqual(\n          parse(\"attachment; filename*=''foo-%c3%a4-%e2%82%ac.html\"),\n          {\n            type: 'attachment',\n            parameters: { 'filename*': \"''foo-%c3%a4-%e2%82%ac.html\" },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename*=UTF-8\\'\\'foo-a%cc%88.html\"', function () {\n        assert.deepEqual(\n          parse(\"attachment; filename*=UTF-8''foo-a%cc%88.html\"),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo-ä.html' },\n          },\n        );\n      });\n\n      it('should parse iso-8859-1 extended parameter value with invalid bytes', function () {\n        assert.deepEqual(\n          parse(\"attachment; filename*=iso-8859-1''foo-%c3%a4-%e2%82%ac.html\"),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo-Ã¤-â\\x82¬.html' },\n          },\n        );\n      });\n\n      it('should preserve spaces before the star in the parameter name', function () {\n        assert.deepEqual(\n          parse(\"attachment; filename *=UTF-8''foo-%c3%a4.html\"),\n          {\n            type: 'attachment',\n            parameters: { 'filename ': 'foo-ä.html' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename*= UTF-8\\'\\'foo-%c3%a4.html\"', function () {\n        assert.deepEqual(\n          parse(\"attachment; filename*= UTF-8''foo-%c3%a4.html\"),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo-ä.html' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename* =UTF-8\\'\\'foo-%c3%a4.html\"', function () {\n        assert.deepEqual(\n          parse(\"attachment; filename* =UTF-8''foo-%c3%a4.html\"),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo-ä.html' },\n          },\n        );\n      });\n\n      it('should preserve quoted UTF-8 extended values verbatim', function () {\n        assert.deepEqual(\n          parse('attachment; filename*=\"UTF-8\\'\\'foo-%c3%a4.html\"'),\n          {\n            type: 'attachment',\n            parameters: { 'filename*': \"UTF-8''foo-%c3%a4.html\" },\n          },\n        );\n      });\n\n      it('should preserve quoted extended values without charset verbatim', function () {\n        assert.deepEqual(parse('attachment; filename*=\"foo%20bar.html\"'), {\n          type: 'attachment',\n          parameters: { 'filename*': 'foo%20bar.html' },\n        });\n      });\n\n      it('should preserve extended values without both apostrophes', function () {\n        assert.deepEqual(parse(\"attachment; filename*=UTF-8'foo-%c3%a4.html\"), {\n          type: 'attachment',\n          parameters: { 'filename*': \"UTF-8'foo-%c3%a4.html\" },\n        });\n      });\n\n      it('should ignore malformed trailing percent escapes', function () {\n        assert.deepEqual(parse(\"attachment; filename*=UTF-8''foo%\"), {\n          type: 'attachment',\n          parameters: { 'filename*': \"UTF-8''foo%\" },\n        });\n      });\n\n      it('should ignore malformed percent escapes inside the value', function () {\n        assert.deepEqual(parse(\"attachment; filename*=UTF-8''f%oo.html\"), {\n          type: 'attachment',\n          parameters: { 'filename*': \"UTF-8''f%oo.html\" },\n        });\n      });\n\n      it('should parse \"attachment; filename*=UTF-8\\'\\'A-%2541.html\"', function () {\n        assert.deepEqual(parse(\"attachment; filename*=UTF-8''A-%2541.html\"), {\n          type: 'attachment',\n          parameters: { filename: 'A-%41.html' },\n        });\n      });\n\n      it('should parse \"attachment; filename*=UTF-8\\'\\'%5cfoo.html\"', function () {\n        assert.deepEqual(parse(\"attachment; filename*=UTF-8''%5cfoo.html\"), {\n          type: 'attachment',\n          parameters: { filename: '\\\\foo.html' },\n        });\n      });\n    });\n\n    describe('RFC2231 Encoding: Continuations', function () {\n      it('should parse \"attachment; filename*0=\"foo.\"; filename*1=\"html\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename*0=\"foo.\"; filename*1=\"html\"'),\n          {\n            type: 'attachment',\n            parameters: { 'filename*0': 'foo.', 'filename*1': 'html' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename*0=\"foo\"; filename*1=\"\\\\b\\\\a\\\\r.html\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename*0=\"foo\"; filename*1=\"\\\\b\\\\a\\\\r.html\"'),\n          {\n            type: 'attachment',\n            parameters: { 'filename*0': 'foo', 'filename*1': 'bar.html' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename*0*=UTF-8\\'\\'foo-%c3%a4; filename*1=\".html\"\"', function () {\n        assert.deepEqual(\n          parse(\n            'attachment; filename*0*=UTF-8\\'\\'foo-%c3%a4; filename*1=\".html\"',\n          ),\n          {\n            type: 'attachment',\n            parameters: {\n              'filename*0': 'foo-ä',\n              'filename*1': '.html',\n            },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename*0=\"foo\"; filename*01=\"bar\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename*0=\"foo\"; filename*01=\"bar\"'),\n          {\n            type: 'attachment',\n            parameters: { 'filename*0': 'foo', 'filename*01': 'bar' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename*0=\"foo\"; filename*2=\"bar\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename*0=\"foo\"; filename*2=\"bar\"'),\n          {\n            type: 'attachment',\n            parameters: { 'filename*0': 'foo', 'filename*2': 'bar' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename*1=\"foo.\"; filename*2=\"html\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename*1=\"foo.\"; filename*2=\"html\"'),\n          {\n            type: 'attachment',\n            parameters: { 'filename*1': 'foo.', 'filename*2': 'html' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename*1=\"bar\"; filename*0=\"foo\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename*1=\"bar\"; filename*0=\"foo\"'),\n          {\n            type: 'attachment',\n            parameters: { 'filename*1': 'bar', 'filename*0': 'foo' },\n          },\n        );\n      });\n    });\n\n    describe('RFC2231 Encoding: Fallback Behaviour', function () {\n      it('should parse \"attachment; filename=\"foo-ae.html\"; filename*=UTF-8\\'\\'foo-%c3%a4.html\"', function () {\n        assert.deepEqual(\n          parse(\n            'attachment; filename=\"foo-ae.html\"; filename*=UTF-8\\'\\'foo-%c3%a4.html',\n          ),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo-ä.html' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename*=UTF-8\\'\\'foo-%c3%a4.html; filename=\"foo-ae.html\"', function () {\n        assert.deepEqual(\n          parse(\n            'attachment; filename*=UTF-8\\'\\'foo-%c3%a4.html; filename=\"foo-ae.html\"',\n          ),\n          {\n            type: 'attachment',\n            parameters: { filename: 'foo-ä.html' },\n          },\n        );\n      });\n\n      it(\"should parse \\\"attachment; filename*0*=ISO-8859-15''euro-sign%3d%a4; filename*=ISO-8859-1''currency-sign%3d%a4\", function () {\n        assert.deepEqual(\n          parse(\n            \"attachment; filename*0*=ISO-8859-15''euro-sign%3d%a4; filename*=ISO-8859-1''currency-sign%3d%a4\",\n          ),\n          {\n            type: 'attachment',\n            parameters: {\n              filename: 'currency-sign=¤',\n              'filename*0*': \"ISO-8859-15''euro-sign%3d%a4\",\n            },\n          },\n        );\n      });\n\n      it('should parse \"attachment; foobar=x; filename=\"foo.html\"', function () {\n        assert.deepEqual(parse('attachment; foobar=x; filename=\"foo.html\"'), {\n          type: 'attachment',\n          parameters: { filename: 'foo.html', foobar: 'x' },\n        });\n      });\n    });\n\n    describe('RFC2047 Encoding', function () {\n      it('should preserve RFC2047-looking token values', function () {\n        assert.deepEqual(\n          parse('attachment; filename==?ISO-8859-1?Q?foo-=E4.html?='),\n          {\n            type: 'attachment',\n            parameters: { filename: '=?ISO-8859-1?Q?foo-=E4.html?=' },\n          },\n        );\n      });\n\n      it('should parse \"attachment; filename=\"=?ISO-8859-1?Q?foo-=E4.html?=\"\"', function () {\n        assert.deepEqual(\n          parse('attachment; filename=\"=?ISO-8859-1?Q?foo-=E4.html?=\"'),\n          {\n            type: 'attachment',\n            parameters: { filename: '=?ISO-8859-1?Q?foo-=E4.html?=' },\n          },\n        );\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "tsconfig.build.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {},\n  \"exclude\": [\"src/**/*.spec.ts\", \"src/**/*.bench.ts\"]\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"extends\": \"@borderless/ts-scripts/configs/tsconfig.json\",\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2023\"],\n    \"rootDir\": \"src\",\n    \"outDir\": \"dist\",\n    \"module\": \"nodenext\",\n    \"moduleResolution\": \"nodenext\",\n    \"types\": []\n  },\n  \"include\": [\"src/**/*\"]\n}\n"
  }
]